From ec6e70725c95097b7e415f8d3bee093d1564c7cd Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 28 May 2022 20:01:20 -0500 Subject: [PATCH] Fix:Include Watcher as lib with no dependencies and fix tiny-readdir bug #610 --- package-lock.json | 410 ++++++------------ package.json | 1 - server/Watcher.js | 3 +- server/libs/watcher/aborter/controller.js | 19 + server/libs/watcher/aborter/signal.js | 39 ++ server/libs/watcher/are-shallow-equal.js | 24 + server/libs/watcher/atomically/consts.js | 28 ++ server/libs/watcher/atomically/index.js | 177 ++++++++ .../watcher/atomically/utils/attemptify.js | 25 ++ server/libs/watcher/atomically/utils/fs.js | 42 ++ .../watcher/atomically/utils/fs_handlers.js | 28 ++ server/libs/watcher/atomically/utils/lang.js | 16 + .../libs/watcher/atomically/utils/retryify.js | 45 ++ .../atomically/utils/retryify_queue.js | 58 +++ .../watcher/atomically/utils/scheduler.js | 35 ++ server/libs/watcher/atomically/utils/temp.js | 56 +++ server/libs/watcher/constants.js | 30 ++ server/libs/watcher/debounce.js | 70 +++ server/libs/watcher/enums.js | 4 + server/libs/watcher/is-primitive.js | 15 + .../watcher/promise-concurrency-limiter.js | 41 ++ server/libs/watcher/ripstat/consts.js | 19 + server/libs/watcher/ripstat/index.js | 39 ++ server/libs/watcher/ripstat/stats.js | 55 +++ server/libs/watcher/string-indexes.js | 17 + server/libs/watcher/tiny-readdir.js | 104 +++++ server/libs/watcher/types.js | 12 + server/libs/watcher/utils.js | 88 ++++ server/libs/watcher/watcher.js | 397 +++++++++++++++++ server/libs/watcher/watcher_handler.js | 245 +++++++++++ server/libs/watcher/watcher_locker.js | 136 ++++++ server/libs/watcher/watcher_locks_resolver.js | 45 ++ server/libs/watcher/watcher_poller.js | 113 +++++ server/libs/watcher/watcher_stats.js | 32 ++ 34 files changed, 2187 insertions(+), 281 deletions(-) create mode 100644 server/libs/watcher/aborter/controller.js create mode 100644 server/libs/watcher/aborter/signal.js create mode 100644 server/libs/watcher/are-shallow-equal.js create mode 100644 server/libs/watcher/atomically/consts.js create mode 100644 server/libs/watcher/atomically/index.js create mode 100644 server/libs/watcher/atomically/utils/attemptify.js create mode 100644 server/libs/watcher/atomically/utils/fs.js create mode 100644 server/libs/watcher/atomically/utils/fs_handlers.js create mode 100644 server/libs/watcher/atomically/utils/lang.js create mode 100644 server/libs/watcher/atomically/utils/retryify.js create mode 100644 server/libs/watcher/atomically/utils/retryify_queue.js create mode 100644 server/libs/watcher/atomically/utils/scheduler.js create mode 100644 server/libs/watcher/atomically/utils/temp.js create mode 100644 server/libs/watcher/constants.js create mode 100644 server/libs/watcher/debounce.js create mode 100644 server/libs/watcher/enums.js create mode 100644 server/libs/watcher/is-primitive.js create mode 100644 server/libs/watcher/promise-concurrency-limiter.js create mode 100644 server/libs/watcher/ripstat/consts.js create mode 100644 server/libs/watcher/ripstat/index.js create mode 100644 server/libs/watcher/ripstat/stats.js create mode 100644 server/libs/watcher/string-indexes.js create mode 100644 server/libs/watcher/tiny-readdir.js create mode 100644 server/libs/watcher/types.js create mode 100644 server/libs/watcher/utils.js create mode 100644 server/libs/watcher/watcher.js create mode 100644 server/libs/watcher/watcher_handler.js create mode 100644 server/libs/watcher/watcher_locker.js create mode 100644 server/libs/watcher/watcher_locks_resolver.js create mode 100644 server/libs/watcher/watcher_poller.js create mode 100644 server/libs/watcher/watcher_stats.js diff --git a/package-lock.json b/package-lock.json index 86ebcc86..04762291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,6 @@ "read-chunk": "^3.1.0", "recursive-readdir-async": "^1.1.8", "socket.io": "^4.4.1", - "watcher": "^1.2.0", "xml2js": "^0.4.23" }, "bin": { @@ -105,9 +104,9 @@ } }, "node_modules/@types/node": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.32.tgz", - "integrity": "sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw==" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "node_modules/@types/responselike": { "version": "1.0.0", @@ -117,11 +116,6 @@ "@types/node": "*" } }, - "node_modules/aborter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/aborter/-/aborter-1.1.0.tgz", - "integrity": "sha512-9rHWMcWTEYsMB4l+ttgPujR7OiXH9NQbP0ej+SSVaK1e2yU/tePbYm8g/g9cQhJkgczp6lpEB2fdJYLKT/T0mg==" - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -190,12 +184,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/are-shallow-equal": { + "node_modules/archiver-utils/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/are-shallow-equal/-/are-shallow-equal-1.1.1.tgz", - "integrity": "sha512-Y0MC/7IP+WZSo0NgYDwww7euKssEodUJxjby3fmNurEDcbq8htqSgyI7a7HELJzkzNv26dOH5vKQFlzCt1H9Ag==", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dependencies": { - "is-primitive": "^3.0.1" + "safe-buffer": "~5.1.0" } }, "node_modules/array-back": { @@ -209,21 +203,13 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, - "node_modules/atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/axios": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", @@ -267,7 +253,7 @@ "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "node_modules/bl": { "version": "4.1.0", @@ -337,7 +323,7 @@ "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "engines": { "node": "*" } @@ -345,17 +331,17 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "node_modules/busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { - "dicer": "0.3.0" + "streamsearch": "^1.1.0" }, "engines": { - "node": ">=4.5.0" + "node": ">=10.16.0" } }, "node_modules/bytes": { @@ -406,7 +392,7 @@ "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "dependencies": { "mimic-response": "^1.0.0" } @@ -459,7 +445,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -491,7 +477,7 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-util-is": { "version": "1.0.3", @@ -538,11 +524,6 @@ "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.3.1.tgz", "integrity": "sha512-OaIRmSJXifwEN21rMVVDs0Kz8uhJ3wWPYd86atkRiqN54liaMQYEbbrgjZQea75YXOBWL4ZFb3rG/waenw1TEg==" }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -601,17 +582,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "dependencies": { - "streamsearch": "0.1.2" - }, - "engines": { - "node": ">=4.5.0" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -674,12 +644,12 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -763,12 +733,12 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -815,11 +785,11 @@ } }, "node_modules/express-fileupload": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.3.1.tgz", - "integrity": "sha512-LD1yabD3exmWIFujKGDnT1rmxSomaqQSlUvzIsrA1ZgwCJ6ci7lg2YHFGM3Q6DfK+Yk0gAVU7GWLE7qDMwZLkw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.0.tgz", + "integrity": "sha512-RjzLCHxkv3umDeZKeFeMg8w7qe0V09w3B7oGZprr/oO2H/ISCgNzuqzn7gV3HRWb37GjRk429CCpSLS2KNTqMQ==", "dependencies": { - "busboy": "^0.3.1" + "busboy": "^1.6.0" }, "engines": { "node": ">=12.0.0" @@ -874,7 +844,7 @@ "node_modules/fluent-ffmpeg": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", - "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=", + "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", "dependencies": { "async": ">=0.2.9", "which": "^1.1.1" @@ -884,9 +854,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "funding": [ { "type": "individual", @@ -913,7 +883,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -939,7 +909,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/function-bind": { "version": "1.1.1", @@ -974,14 +944,14 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -993,9 +963,9 @@ } }, "node_modules/got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -1156,14 +1126,6 @@ "node": ">= 0.10" } }, - "node_modules/is-primitive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", - "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1236,9 +1198,9 @@ } }, "node_modules/keyv": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.8.tgz", - "integrity": "sha512-IZZo6krhHWPhgsP5mBkEdPopVPN/stgCnBVuqi6dda/Nm5mDTOSVTrFMkWqlJsDum+B0YSe887tNxdjDWkO7aQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.0.tgz", + "integrity": "sha512-C30Un9+63J0CsR7Wka5quXKqYZsT6dcRQ2aOwGcSc3RiQ4HGWpTAHlCA+puNfw2jA/s11EsxA1nCXgZRuRKMQQ==", "dependencies": { "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" @@ -1274,6 +1236,14 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/libgen": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/libgen/-/libgen-2.1.2.tgz", @@ -1511,9 +1481,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1603,11 +1573,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/promise-concurrency-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/promise-concurrency-limiter/-/promise-concurrency-limiter-1.0.0.tgz", - "integrity": "sha512-OI96yL5DUck9KCLee5H6DnRfVsHIstQspXk8xsYrWr9ur9IlFuzKvoU70HwQb99MqHg2mpdkuGa92NuoXue3cw==" - }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -1748,14 +1713,6 @@ "node": ">= 4" } }, - "node_modules/ripstat": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ripstat/-/ripstat-1.1.1.tgz", - "integrity": "sha512-O+KrJUwY3Q8cArNraH136svsDlNmRh6mnJ9TogkpcGWBvd2Kks5d5HGsZRnWt9h3kh8D4uq62kdlYihONjgj5w==", - "dependencies": { - "atomically": "^1.7.0" - } - }, "node_modules/rss": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", @@ -1887,9 +1844,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/socket.io": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.0.tgz", - "integrity": "sha512-slTYqU2jCgMjXwresG8grhUi/cC6GjzmcfqArzaH3BN/9I/42eZk9yamNvZJdBfTubkjEdKAKs12NEztId+bUA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", + "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -1971,31 +1928,21 @@ } }, "node_modules/streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { - "node": ">=0.8.0" + "node": ">=10.0.0" } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/string-indexes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz", - "integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw==" - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -2011,17 +1958,6 @@ "node": ">=6" } }, - "node_modules/tiny-readdir": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-1.5.0.tgz", - "integrity": "sha512-Nep9qu34bOZApNkEnJu4V1WcgxW1kCGlYN8SYwMzZfqpv6f2E1n5vPHLczJqy2vOQ1rQG/m9fI3DQbIFXAQNGw==", - "dependencies": { - "promise-concurrency-limiter": "^1.0.0" - }, - "engines": { - "node": ">= 10.12.0" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2087,22 +2023,6 @@ "node": ">= 0.8" } }, - "node_modules/watcher": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/watcher/-/watcher-1.2.0.tgz", - "integrity": "sha512-f2KFU8ZRSDfTXdI2Y6Ge7DXVnCx9QnlGScwUHPh+YPkbFWZS983KfgHddj+KBWZGrCCuRanYvDz91p65d9/h4w==", - "dependencies": { - "aborter": "^1.0.0", - "are-shallow-equal": "^1.1.1", - "debounce": "^1.2.0", - "ripstat": "^1.1.1", - "string-indexes": "^1.0.0", - "tiny-readdir": "^1.5.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2250,9 +2170,9 @@ } }, "@types/node": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.32.tgz", - "integrity": "sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw==" + "version": "17.0.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz", + "integrity": "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" }, "@types/responselike": { "version": "1.0.0", @@ -2262,11 +2182,6 @@ "@types/node": "*" } }, - "aborter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/aborter/-/aborter-1.1.0.tgz", - "integrity": "sha512-9rHWMcWTEYsMB4l+ttgPujR7OiXH9NQbP0ej+SSVaK1e2yU/tePbYm8g/g9cQhJkgczp6lpEB2fdJYLKT/T0mg==" - }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2325,17 +2240,17 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, - "are-shallow-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/are-shallow-equal/-/are-shallow-equal-1.1.1.tgz", - "integrity": "sha512-Y0MC/7IP+WZSo0NgYDwww7euKssEodUJxjby3fmNurEDcbq8htqSgyI7a7HELJzkzNv26dOH5vKQFlzCt1H9Ag==", - "requires": { - "is-primitive": "^3.0.1" - } - }, "array-back": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", @@ -2344,18 +2259,13 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, - "atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" - }, "axios": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", @@ -2382,7 +2292,7 @@ "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "bl": { "version": "4.1.0", @@ -2434,19 +2344,19 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "requires": { - "dicer": "0.3.0" + "streamsearch": "^1.1.0" } }, "bytes": { @@ -2485,7 +2395,7 @@ "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "requires": { "mimic-response": "^1.0.0" } @@ -2529,7 +2439,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "content-disposition": { "version": "0.5.4", @@ -2552,7 +2462,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-util-is": { "version": "1.0.3", @@ -2587,11 +2497,6 @@ "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.3.1.tgz", "integrity": "sha512-OaIRmSJXifwEN21rMVVDs0Kz8uhJ3wWPYd86atkRiqN54liaMQYEbbrgjZQea75YXOBWL4ZFb3rG/waenw1TEg==" }, - "debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2630,14 +2535,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, - "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "requires": { - "streamsearch": "0.1.2" - } - }, "dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2682,12 +2579,12 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -2747,12 +2644,12 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "express": { "version": "4.18.1", @@ -2793,11 +2690,11 @@ } }, "express-fileupload": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.3.1.tgz", - "integrity": "sha512-LD1yabD3exmWIFujKGDnT1rmxSomaqQSlUvzIsrA1ZgwCJ6ci7lg2YHFGM3Q6DfK+Yk0gAVU7GWLE7qDMwZLkw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.0.tgz", + "integrity": "sha512-RjzLCHxkv3umDeZKeFeMg8w7qe0V09w3B7oGZprr/oO2H/ISCgNzuqzn7gV3HRWb37GjRk429CCpSLS2KNTqMQ==", "requires": { - "busboy": "^0.3.1" + "busboy": "^1.6.0" } }, "express-rate-limit": { @@ -2840,16 +2737,16 @@ "fluent-ffmpeg": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", - "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=", + "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", "requires": { "async": ">=0.2.9", "which": "^1.1.1" } }, "follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==" + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, "forwarded": { "version": "0.2.0", @@ -2859,7 +2756,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-constants": { "version": "1.0.0", @@ -2879,7 +2776,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { "version": "1.1.1", @@ -2905,22 +2802,22 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", "requires": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -3030,11 +2927,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-primitive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", - "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==" - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3103,9 +2995,9 @@ } }, "keyv": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.8.tgz", - "integrity": "sha512-IZZo6krhHWPhgsP5mBkEdPopVPN/stgCnBVuqi6dda/Nm5mDTOSVTrFMkWqlJsDum+B0YSe887tNxdjDWkO7aQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.0.tgz", + "integrity": "sha512-C30Un9+63J0CsR7Wka5quXKqYZsT6dcRQ2aOwGcSc3RiQ4HGWpTAHlCA+puNfw2jA/s11EsxA1nCXgZRuRKMQQ==", "requires": { "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" @@ -3137,6 +3029,14 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -3316,9 +3216,9 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "on-finished": { "version": "2.4.1", @@ -3384,11 +3284,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "promise-concurrency-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/promise-concurrency-limiter/-/promise-concurrency-limiter-1.0.0.tgz", - "integrity": "sha512-OI96yL5DUck9KCLee5H6DnRfVsHIstQspXk8xsYrWr9ur9IlFuzKvoU70HwQb99MqHg2mpdkuGa92NuoXue3cw==" - }, "proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -3496,14 +3391,6 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, - "ripstat": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ripstat/-/ripstat-1.1.1.tgz", - "integrity": "sha512-O+KrJUwY3Q8cArNraH136svsDlNmRh6mnJ9TogkpcGWBvd2Kks5d5HGsZRnWt9h3kh8D4uq62kdlYihONjgj5w==", - "requires": { - "atomically": "^1.7.0" - } - }, "rss": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", @@ -3607,9 +3494,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "socket.io": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.0.tgz", - "integrity": "sha512-slTYqU2jCgMjXwresG8grhUi/cC6GjzmcfqArzaH3BN/9I/42eZk9yamNvZJdBfTubkjEdKAKs12NEztId+bUA==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", + "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -3670,30 +3557,18 @@ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "~5.2.0" } }, - "string-indexes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz", - "integrity": "sha512-RUlx+2YydZJNlRAvoh1siPYWj/Xfk6t1sQLkA5n1tMGRCKkRLzkRtJhHk4qRmKergEBh8R3pWhsUsDqia/bolw==" - }, "tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -3706,14 +3581,6 @@ "readable-stream": "^3.1.1" } }, - "tiny-readdir": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-1.5.0.tgz", - "integrity": "sha512-Nep9qu34bOZApNkEnJu4V1WcgxW1kCGlYN8SYwMzZfqpv6f2E1n5vPHLczJqy2vOQ1rQG/m9fI3DQbIFXAQNGw==", - "requires": { - "promise-concurrency-limiter": "^1.0.0" - } - }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3758,19 +3625,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "watcher": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/watcher/-/watcher-1.2.0.tgz", - "integrity": "sha512-f2KFU8ZRSDfTXdI2Y6Ge7DXVnCx9QnlGScwUHPh+YPkbFWZS983KfgHddj+KBWZGrCCuRanYvDz91p65d9/h4w==", - "requires": { - "aborter": "^1.0.0", - "are-shallow-equal": "^1.1.1", - "debounce": "^1.2.0", - "ripstat": "^1.1.1", - "string-indexes": "^1.0.0", - "tiny-readdir": "^1.5.0" - } - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index fab42a18..c520d518 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "read-chunk": "^3.1.0", "recursive-readdir-async": "^1.1.8", "socket.io": "^4.4.1", - "watcher": "^1.2.0", "xml2js": "^0.4.23" } } diff --git a/server/Watcher.js b/server/Watcher.js index d2166b6e..341c9c10 100644 --- a/server/Watcher.js +++ b/server/Watcher.js @@ -1,6 +1,5 @@ -const Path = require('path') const EventEmitter = require('events') -const Watcher = require('watcher') +const Watcher = require('./libs/watcher/watcher') const Logger = require('./Logger') class FolderWatcher extends EventEmitter { diff --git a/server/libs/watcher/aborter/controller.js b/server/libs/watcher/aborter/controller.js new file mode 100644 index 00000000..8acbe00d --- /dev/null +++ b/server/libs/watcher/aborter/controller.js @@ -0,0 +1,19 @@ +"use strict"; +/* IMPORT */ +var signal_1 = require("./signal"); +/* ABORT CONTROLLER */ +var AbortController = /** @class */ (function () { + function AbortController() { + /* VARIABLES */ + this.signal = new signal_1.default(); + } + /* API */ + AbortController.prototype.abort = function () { + return this.signal.abort(); + }; + return AbortController; +}()); +/* EXPORT */ +module.exports = AbortController; +module.exports.default = AbortController; +Object.defineProperty(module.exports, "__esModule", { value: true }); diff --git a/server/libs/watcher/aborter/signal.js b/server/libs/watcher/aborter/signal.js new file mode 100644 index 00000000..2a730568 --- /dev/null +++ b/server/libs/watcher/aborter/signal.js @@ -0,0 +1,39 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +/* ABORT SIGNAL */ +var AbortSignal = /** @class */ (function () { + function AbortSignal() { + /* VARIABLES */ + this.aborted = false; + this.listeners = {}; + } + /* EVENTS API */ + AbortSignal.prototype.addEventListener = function (event, listener) { + var listeners = this.listeners[event] || (this.listeners[event] = []); + listeners.push(listener); + }; + AbortSignal.prototype.removeEventListener = function (event, listener) { + var listeners = this.listeners[event]; + if (!listeners) + return; + listeners.splice(listeners.indexOf(listener), 1); + }; + AbortSignal.prototype.dispatchEvent = function (event) { + var listeners = this.listeners[event]; + if (!listeners) + return true; + listeners.slice().forEach(function (listener) { return listener(); }); + return true; + }; + /* API */ + AbortSignal.prototype.abort = function () { + if (this.aborted) + return; + this.aborted = true; + this.dispatchEvent('abort'); + }; + return AbortSignal; +}()); +/* EXPORT */ +exports.default = AbortSignal; diff --git a/server/libs/watcher/are-shallow-equal.js b/server/libs/watcher/are-shallow-equal.js new file mode 100644 index 00000000..57233de0 --- /dev/null +++ b/server/libs/watcher/are-shallow-equal.js @@ -0,0 +1,24 @@ +"use strict"; +/* IMPORT */ +var isPrimitive = require("./is-primitive"); +/* ARE SHALLOW EQUAL */ +var isNaN = Number.isNaN; +function areShallowEqual(x, y) { + if (x === y) + return true; + if (isNaN(x)) + return isNaN(y); + if (isPrimitive(x) || isPrimitive(y)) + return x === y; + for (var i in x) + if (!(i in y)) + return false; + for (var i in y) + if (x[i] !== y[i]) + return false; + return true; +} +/* EXPORT */ +module.exports = areShallowEqual; +module.exports.default = areShallowEqual; +Object.defineProperty(module.exports, "__esModule", { value: true }); diff --git a/server/libs/watcher/atomically/consts.js b/server/libs/watcher/atomically/consts.js new file mode 100644 index 00000000..4f19bae3 --- /dev/null +++ b/server/libs/watcher/atomically/consts.js @@ -0,0 +1,28 @@ +"use strict"; +/* CONSTS */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NOOP = exports.LIMIT_FILES_DESCRIPTORS = exports.LIMIT_BASENAME_LENGTH = exports.IS_USER_ROOT = exports.IS_POSIX = exports.DEFAULT_TIMEOUT_SYNC = exports.DEFAULT_TIMEOUT_ASYNC = exports.DEFAULT_WRITE_OPTIONS = exports.DEFAULT_READ_OPTIONS = exports.DEFAULT_FOLDER_MODE = exports.DEFAULT_FILE_MODE = exports.DEFAULT_ENCODING = void 0; +const DEFAULT_ENCODING = 'utf8'; +exports.DEFAULT_ENCODING = DEFAULT_ENCODING; +const DEFAULT_FILE_MODE = 0o666; +exports.DEFAULT_FILE_MODE = DEFAULT_FILE_MODE; +const DEFAULT_FOLDER_MODE = 0o777; +exports.DEFAULT_FOLDER_MODE = DEFAULT_FOLDER_MODE; +const DEFAULT_READ_OPTIONS = {}; +exports.DEFAULT_READ_OPTIONS = DEFAULT_READ_OPTIONS; +const DEFAULT_WRITE_OPTIONS = {}; +exports.DEFAULT_WRITE_OPTIONS = DEFAULT_WRITE_OPTIONS; +const DEFAULT_TIMEOUT_ASYNC = 5000; +exports.DEFAULT_TIMEOUT_ASYNC = DEFAULT_TIMEOUT_ASYNC; +const DEFAULT_TIMEOUT_SYNC = 100; +exports.DEFAULT_TIMEOUT_SYNC = DEFAULT_TIMEOUT_SYNC; +const IS_POSIX = !!process.getuid; +exports.IS_POSIX = IS_POSIX; +const IS_USER_ROOT = process.getuid ? !process.getuid() : false; +exports.IS_USER_ROOT = IS_USER_ROOT; +const LIMIT_BASENAME_LENGTH = 128; //TODO: fetch the real limit from the filesystem //TODO: fetch the whole-path length limit too +exports.LIMIT_BASENAME_LENGTH = LIMIT_BASENAME_LENGTH; +const LIMIT_FILES_DESCRIPTORS = 10000; //TODO: fetch the real limit from the filesystem +exports.LIMIT_FILES_DESCRIPTORS = LIMIT_FILES_DESCRIPTORS; +const NOOP = () => { }; +exports.NOOP = NOOP; diff --git a/server/libs/watcher/atomically/index.js b/server/libs/watcher/atomically/index.js new file mode 100644 index 00000000..13285838 --- /dev/null +++ b/server/libs/watcher/atomically/index.js @@ -0,0 +1,177 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.writeFileSync = exports.writeFile = exports.readFileSync = exports.readFile = void 0; +const path = require("path"); +const consts_1 = require("./consts"); +const fs_1 = require("./utils/fs"); +const lang_1 = require("./utils/lang"); +const scheduler_1 = require("./utils/scheduler"); +const temp_1 = require("./utils/temp"); +function readFile(filePath, options = consts_1.DEFAULT_READ_OPTIONS) { + var _a; + if (lang_1.default.isString(options)) + return readFile(filePath, { encoding: options }); + const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_ASYNC); + return fs_1.default.readFileRetry(timeout)(filePath, options); +} +exports.readFile = readFile; +; +function readFileSync(filePath, options = consts_1.DEFAULT_READ_OPTIONS) { + var _a; + if (lang_1.default.isString(options)) + return readFileSync(filePath, { encoding: options }); + const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_SYNC); + return fs_1.default.readFileSyncRetry(timeout)(filePath, options); +} +exports.readFileSync = readFileSync; +; +const writeFile = (filePath, data, options, callback) => { + if (lang_1.default.isFunction(options)) + return writeFile(filePath, data, consts_1.DEFAULT_WRITE_OPTIONS, options); + const promise = writeFileAsync(filePath, data, options); + if (callback) + promise.then(callback, callback); + return promise; +}; +exports.writeFile = writeFile; +const writeFileAsync = async (filePath, data, options = consts_1.DEFAULT_WRITE_OPTIONS) => { + var _a; + if (lang_1.default.isString(options)) + return writeFileAsync(filePath, data, { encoding: options }); + const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_ASYNC); + let schedulerCustomDisposer = null, schedulerDisposer = null, tempDisposer = null, tempPath = null, fd = null; + try { + if (options.schedule) + schedulerCustomDisposer = await options.schedule(filePath); + schedulerDisposer = await scheduler_1.default.schedule(filePath); + filePath = await fs_1.default.realpathAttempt(filePath) || filePath; + [tempPath, tempDisposer] = temp_1.default.get(filePath, options.tmpCreate || temp_1.default.create, !(options.tmpPurge === false)); + const useStatChown = consts_1.IS_POSIX && lang_1.default.isUndefined(options.chown), useStatMode = lang_1.default.isUndefined(options.mode); + if (useStatChown || useStatMode) { + const stat = await fs_1.default.statAttempt(filePath); + if (stat) { + options = { ...options }; + if (useStatChown) + options.chown = { uid: stat.uid, gid: stat.gid }; + if (useStatMode) + options.mode = stat.mode; + } + } + const parentPath = path.dirname(filePath); + await fs_1.default.mkdirAttempt(parentPath, { + mode: consts_1.DEFAULT_FOLDER_MODE, + recursive: true + }); + fd = await fs_1.default.openRetry(timeout)(tempPath, 'w', options.mode || consts_1.DEFAULT_FILE_MODE); + if (options.tmpCreated) + options.tmpCreated(tempPath); + if (lang_1.default.isString(data)) { + await fs_1.default.writeRetry(timeout)(fd, data, 0, options.encoding || consts_1.DEFAULT_ENCODING); + } + else if (!lang_1.default.isUndefined(data)) { + await fs_1.default.writeRetry(timeout)(fd, data, 0, data.length, 0); + } + if (options.fsync !== false) { + if (options.fsyncWait !== false) { + await fs_1.default.fsyncRetry(timeout)(fd); + } + else { + fs_1.default.fsyncAttempt(fd); + } + } + await fs_1.default.closeRetry(timeout)(fd); + fd = null; + if (options.chown) + await fs_1.default.chownAttempt(tempPath, options.chown.uid, options.chown.gid); + if (options.mode) + await fs_1.default.chmodAttempt(tempPath, options.mode); + try { + await fs_1.default.renameRetry(timeout)(tempPath, filePath); + } + catch (error) { + if (error.code !== 'ENAMETOOLONG') + throw error; + await fs_1.default.renameRetry(timeout)(tempPath, temp_1.default.truncate(filePath)); + } + tempDisposer(); + tempPath = null; + } + finally { + if (fd) + await fs_1.default.closeAttempt(fd); + if (tempPath) + temp_1.default.purge(tempPath); + if (schedulerCustomDisposer) + schedulerCustomDisposer(); + if (schedulerDisposer) + schedulerDisposer(); + } +}; +const writeFileSync = (filePath, data, options = consts_1.DEFAULT_WRITE_OPTIONS) => { + var _a; + if (lang_1.default.isString(options)) + return writeFileSync(filePath, data, { encoding: options }); + const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_SYNC); + let tempDisposer = null, tempPath = null, fd = null; + try { + filePath = fs_1.default.realpathSyncAttempt(filePath) || filePath; + [tempPath, tempDisposer] = temp_1.default.get(filePath, options.tmpCreate || temp_1.default.create, !(options.tmpPurge === false)); + const useStatChown = consts_1.IS_POSIX && lang_1.default.isUndefined(options.chown), useStatMode = lang_1.default.isUndefined(options.mode); + if (useStatChown || useStatMode) { + const stat = fs_1.default.statSyncAttempt(filePath); + if (stat) { + options = { ...options }; + if (useStatChown) + options.chown = { uid: stat.uid, gid: stat.gid }; + if (useStatMode) + options.mode = stat.mode; + } + } + const parentPath = path.dirname(filePath); + fs_1.default.mkdirSyncAttempt(parentPath, { + mode: consts_1.DEFAULT_FOLDER_MODE, + recursive: true + }); + fd = fs_1.default.openSyncRetry(timeout)(tempPath, 'w', options.mode || consts_1.DEFAULT_FILE_MODE); + if (options.tmpCreated) + options.tmpCreated(tempPath); + if (lang_1.default.isString(data)) { + fs_1.default.writeSyncRetry(timeout)(fd, data, 0, options.encoding || consts_1.DEFAULT_ENCODING); + } + else if (!lang_1.default.isUndefined(data)) { + fs_1.default.writeSyncRetry(timeout)(fd, data, 0, data.length, 0); + } + if (options.fsync !== false) { + if (options.fsyncWait !== false) { + fs_1.default.fsyncSyncRetry(timeout)(fd); + } + else { + fs_1.default.fsyncAttempt(fd); + } + } + fs_1.default.closeSyncRetry(timeout)(fd); + fd = null; + if (options.chown) + fs_1.default.chownSyncAttempt(tempPath, options.chown.uid, options.chown.gid); + if (options.mode) + fs_1.default.chmodSyncAttempt(tempPath, options.mode); + try { + fs_1.default.renameSyncRetry(timeout)(tempPath, filePath); + } + catch (error) { + if (error.code !== 'ENAMETOOLONG') + throw error; + fs_1.default.renameSyncRetry(timeout)(tempPath, temp_1.default.truncate(filePath)); + } + tempDisposer(); + tempPath = null; + } + finally { + if (fd) + fs_1.default.closeSyncAttempt(fd); + if (tempPath) + temp_1.default.purge(tempPath); + } +}; +exports.writeFileSync = writeFileSync; diff --git a/server/libs/watcher/atomically/utils/attemptify.js b/server/libs/watcher/atomically/utils/attemptify.js new file mode 100644 index 00000000..94a457d1 --- /dev/null +++ b/server/libs/watcher/atomically/utils/attemptify.js @@ -0,0 +1,25 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.attemptifySync = exports.attemptifyAsync = void 0; +const consts_1 = require("../consts"); +/* ATTEMPTIFY */ +//TODO: Maybe publish this as a standalone package +//FIXME: The type castings here aren't exactly correct +const attemptifyAsync = (fn, onError = consts_1.NOOP) => { + return function () { + return fn.apply(undefined, arguments).catch(onError); + }; +}; +exports.attemptifyAsync = attemptifyAsync; +const attemptifySync = (fn, onError = consts_1.NOOP) => { + return function () { + try { + return fn.apply(undefined, arguments); + } + catch (error) { + return onError(error); + } + }; +}; +exports.attemptifySync = attemptifySync; diff --git a/server/libs/watcher/atomically/utils/fs.js b/server/libs/watcher/atomically/utils/fs.js new file mode 100644 index 00000000..35783d1e --- /dev/null +++ b/server/libs/watcher/atomically/utils/fs.js @@ -0,0 +1,42 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = require("fs"); +const util_1 = require("util"); +const attemptify_1 = require("./attemptify"); +const fs_handlers_1 = require("./fs_handlers"); +const retryify_1 = require("./retryify"); +/* FS */ +const FS = { + chmodAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.chmod), fs_handlers_1.default.onChangeError), + chownAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.chown), fs_handlers_1.default.onChangeError), + closeAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.close)), + fsyncAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.fsync)), + mkdirAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.mkdir)), + realpathAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.realpath)), + statAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.stat)), + unlinkAttempt: attemptify_1.attemptifyAsync(util_1.promisify(fs.unlink)), + closeRetry: retryify_1.retryifyAsync(util_1.promisify(fs.close), fs_handlers_1.default.isRetriableError), + fsyncRetry: retryify_1.retryifyAsync(util_1.promisify(fs.fsync), fs_handlers_1.default.isRetriableError), + openRetry: retryify_1.retryifyAsync(util_1.promisify(fs.open), fs_handlers_1.default.isRetriableError), + readFileRetry: retryify_1.retryifyAsync(util_1.promisify(fs.readFile), fs_handlers_1.default.isRetriableError), + renameRetry: retryify_1.retryifyAsync(util_1.promisify(fs.rename), fs_handlers_1.default.isRetriableError), + statRetry: retryify_1.retryifyAsync(util_1.promisify(fs.stat), fs_handlers_1.default.isRetriableError), + writeRetry: retryify_1.retryifyAsync(util_1.promisify(fs.write), fs_handlers_1.default.isRetriableError), + chmodSyncAttempt: attemptify_1.attemptifySync(fs.chmodSync, fs_handlers_1.default.onChangeError), + chownSyncAttempt: attemptify_1.attemptifySync(fs.chownSync, fs_handlers_1.default.onChangeError), + closeSyncAttempt: attemptify_1.attemptifySync(fs.closeSync), + mkdirSyncAttempt: attemptify_1.attemptifySync(fs.mkdirSync), + realpathSyncAttempt: attemptify_1.attemptifySync(fs.realpathSync), + statSyncAttempt: attemptify_1.attemptifySync(fs.statSync), + unlinkSyncAttempt: attemptify_1.attemptifySync(fs.unlinkSync), + closeSyncRetry: retryify_1.retryifySync(fs.closeSync, fs_handlers_1.default.isRetriableError), + fsyncSyncRetry: retryify_1.retryifySync(fs.fsyncSync, fs_handlers_1.default.isRetriableError), + openSyncRetry: retryify_1.retryifySync(fs.openSync, fs_handlers_1.default.isRetriableError), + readFileSyncRetry: retryify_1.retryifySync(fs.readFileSync, fs_handlers_1.default.isRetriableError), + renameSyncRetry: retryify_1.retryifySync(fs.renameSync, fs_handlers_1.default.isRetriableError), + statSyncRetry: retryify_1.retryifySync(fs.statSync, fs_handlers_1.default.isRetriableError), + writeSyncRetry: retryify_1.retryifySync(fs.writeSync, fs_handlers_1.default.isRetriableError) +}; +/* EXPORT */ +exports.default = FS; diff --git a/server/libs/watcher/atomically/utils/fs_handlers.js b/server/libs/watcher/atomically/utils/fs_handlers.js new file mode 100644 index 00000000..08c8b2ab --- /dev/null +++ b/server/libs/watcher/atomically/utils/fs_handlers.js @@ -0,0 +1,28 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +const consts_1 = require("../consts"); +/* FS HANDLERS */ +const Handlers = { + isChangeErrorOk: (error) => { + const { code } = error; + if (code === 'ENOSYS') + return true; + if (!consts_1.IS_USER_ROOT && (code === 'EINVAL' || code === 'EPERM')) + return true; + return false; + }, + isRetriableError: (error) => { + const { code } = error; + if (code === 'EMFILE' || code === 'ENFILE' || code === 'EAGAIN' || code === 'EBUSY' || code === 'EACCESS' || code === 'EACCS' || code === 'EPERM') + return true; + return false; + }, + onChangeError: (error) => { + if (Handlers.isChangeErrorOk(error)) + return; + throw error; + } +}; +/* EXPORT */ +exports.default = Handlers; diff --git a/server/libs/watcher/atomically/utils/lang.js b/server/libs/watcher/atomically/utils/lang.js new file mode 100644 index 00000000..27065400 --- /dev/null +++ b/server/libs/watcher/atomically/utils/lang.js @@ -0,0 +1,16 @@ +"use strict"; +/* LANG */ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lang = { + isFunction: (x) => { + return typeof x === 'function'; + }, + isString: (x) => { + return typeof x === 'string'; + }, + isUndefined: (x) => { + return typeof x === 'undefined'; + } +}; +/* EXPORT */ +exports.default = Lang; diff --git a/server/libs/watcher/atomically/utils/retryify.js b/server/libs/watcher/atomically/utils/retryify.js new file mode 100644 index 00000000..e2f759b3 --- /dev/null +++ b/server/libs/watcher/atomically/utils/retryify.js @@ -0,0 +1,45 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.retryifySync = exports.retryifyAsync = void 0; +const retryify_queue_1 = require("./retryify_queue"); +/* RETRYIFY */ +const retryifyAsync = (fn, isRetriableError) => { + return function (timestamp) { + return function attempt() { + return retryify_queue_1.default.schedule().then(cleanup => { + return fn.apply(undefined, arguments).then(result => { + cleanup(); + return result; + }, error => { + cleanup(); + if (Date.now() >= timestamp) + throw error; + if (isRetriableError(error)) { + const delay = Math.round(100 + (400 * Math.random())), delayPromise = new Promise(resolve => setTimeout(resolve, delay)); + return delayPromise.then(() => attempt.apply(undefined, arguments)); + } + throw error; + }); + }); + }; + }; +}; +exports.retryifyAsync = retryifyAsync; +const retryifySync = (fn, isRetriableError) => { + return function (timestamp) { + return function attempt() { + try { + return fn.apply(undefined, arguments); + } + catch (error) { + if (Date.now() > timestamp) + throw error; + if (isRetriableError(error)) + return attempt.apply(undefined, arguments); + throw error; + } + }; + }; +}; +exports.retryifySync = retryifySync; diff --git a/server/libs/watcher/atomically/utils/retryify_queue.js b/server/libs/watcher/atomically/utils/retryify_queue.js new file mode 100644 index 00000000..b67e76e7 --- /dev/null +++ b/server/libs/watcher/atomically/utils/retryify_queue.js @@ -0,0 +1,58 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +const consts_1 = require("../consts"); +/* RETRYIFY QUEUE */ +const RetryfyQueue = { + interval: 25, + intervalId: undefined, + limit: consts_1.LIMIT_FILES_DESCRIPTORS, + queueActive: new Set(), + queueWaiting: new Set(), + init: () => { + if (RetryfyQueue.intervalId) + return; + RetryfyQueue.intervalId = setInterval(RetryfyQueue.tick, RetryfyQueue.interval); + }, + reset: () => { + if (!RetryfyQueue.intervalId) + return; + clearInterval(RetryfyQueue.intervalId); + delete RetryfyQueue.intervalId; + }, + add: (fn) => { + RetryfyQueue.queueWaiting.add(fn); + if (RetryfyQueue.queueActive.size < (RetryfyQueue.limit / 2)) { // Active queue not under preassure, executing immediately + RetryfyQueue.tick(); + } + else { + RetryfyQueue.init(); + } + }, + remove: (fn) => { + RetryfyQueue.queueWaiting.delete(fn); + RetryfyQueue.queueActive.delete(fn); + }, + schedule: () => { + return new Promise(resolve => { + const cleanup = () => RetryfyQueue.remove(resolver); + const resolver = () => resolve(cleanup); + RetryfyQueue.add(resolver); + }); + }, + tick: () => { + if (RetryfyQueue.queueActive.size >= RetryfyQueue.limit) + return; + if (!RetryfyQueue.queueWaiting.size) + return RetryfyQueue.reset(); + for (const fn of RetryfyQueue.queueWaiting) { + if (RetryfyQueue.queueActive.size >= RetryfyQueue.limit) + break; + RetryfyQueue.queueWaiting.delete(fn); + RetryfyQueue.queueActive.add(fn); + fn(); + } + } +}; +/* EXPORT */ +exports.default = RetryfyQueue; diff --git a/server/libs/watcher/atomically/utils/scheduler.js b/server/libs/watcher/atomically/utils/scheduler.js new file mode 100644 index 00000000..759e7e5f --- /dev/null +++ b/server/libs/watcher/atomically/utils/scheduler.js @@ -0,0 +1,35 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +/* VARIABLES */ +const Queues = {}; +/* SCHEDULER */ +//TODO: Maybe publish this as a standalone package +const Scheduler = { + next: (id) => { + const queue = Queues[id]; + if (!queue) + return; + queue.shift(); + const job = queue[0]; + if (job) { + job(() => Scheduler.next(id)); + } + else { + delete Queues[id]; + } + }, + schedule: (id) => { + return new Promise(resolve => { + let queue = Queues[id]; + if (!queue) + queue = Queues[id] = []; + queue.push(resolve); + if (queue.length > 1) + return; + resolve(() => Scheduler.next(id)); + }); + } +}; +/* EXPORT */ +exports.default = Scheduler; diff --git a/server/libs/watcher/atomically/utils/temp.js b/server/libs/watcher/atomically/utils/temp.js new file mode 100644 index 00000000..c0844f64 --- /dev/null +++ b/server/libs/watcher/atomically/utils/temp.js @@ -0,0 +1,56 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +const path = require("path"); +const consts_1 = require("../consts"); +const fs_1 = require("./fs"); +/* TEMP */ +//TODO: Maybe publish this as a standalone package +const Temp = { + store: {}, + create: (filePath) => { + const randomness = `000000${Math.floor(Math.random() * 16777215).toString(16)}`.slice(-6), // 6 random-enough hex characters + timestamp = Date.now().toString().slice(-10), // 10 precise timestamp digits + prefix = 'tmp-', suffix = `.${prefix}${timestamp}${randomness}`, tempPath = `${filePath}${suffix}`; + return tempPath; + }, + get: (filePath, creator, purge = true) => { + const tempPath = Temp.truncate(creator(filePath)); + if (tempPath in Temp.store) + return Temp.get(filePath, creator, purge); // Collision found, try again + Temp.store[tempPath] = purge; + const disposer = () => delete Temp.store[tempPath]; + return [tempPath, disposer]; + }, + purge: (filePath) => { + if (!Temp.store[filePath]) + return; + delete Temp.store[filePath]; + fs_1.default.unlinkAttempt(filePath); + }, + purgeSync: (filePath) => { + if (!Temp.store[filePath]) + return; + delete Temp.store[filePath]; + fs_1.default.unlinkSyncAttempt(filePath); + }, + purgeSyncAll: () => { + for (const filePath in Temp.store) { + Temp.purgeSync(filePath); + } + }, + truncate: (filePath) => { + const basename = path.basename(filePath); + if (basename.length <= consts_1.LIMIT_BASENAME_LENGTH) + return filePath; //FIXME: Rough and quick attempt at detecting ok lengths + const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename); + if (!truncable) + return filePath; //FIXME: No truncable part detected, can't really do much without also changing the parent path, which is unsafe, hoping for the best here + const truncationLength = basename.length - consts_1.LIMIT_BASENAME_LENGTH; + return `${filePath.slice(0, -basename.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`; //FIXME: The truncable part might be shorter than needed here + } +}; +/* INIT */ +process.on('exit', Temp.purgeSyncAll); // Ensuring purgeable temp files are purged on exit +/* EXPORT */ +exports.default = Temp; diff --git a/server/libs/watcher/constants.js b/server/libs/watcher/constants.js new file mode 100644 index 00000000..1d3a79e7 --- /dev/null +++ b/server/libs/watcher/constants.js @@ -0,0 +1,30 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RENAME_TIMEOUT = exports.POLLING_TIMEOUT = exports.POLLING_INTERVAL = exports.PLATFORM = exports.IS_WINDOWS = exports.IS_MAC = exports.IS_LINUX = exports.HAS_NATIVE_RECURSION = exports.DEPTH = exports.DEBOUNCE = void 0; +const os_1 = __importDefault(require("os")); +/* CONSTANTS */ +const DEBOUNCE = 300; +exports.DEBOUNCE = DEBOUNCE; +const DEPTH = 20; +exports.DEPTH = DEPTH; +const PLATFORM = os_1.default.platform(); +exports.PLATFORM = PLATFORM; +const IS_LINUX = (PLATFORM === 'linux'); +exports.IS_LINUX = IS_LINUX; +const IS_MAC = (PLATFORM === 'darwin'); +exports.IS_MAC = IS_MAC; +const IS_WINDOWS = (PLATFORM === 'win32'); +exports.IS_WINDOWS = IS_WINDOWS; +const HAS_NATIVE_RECURSION = IS_MAC || IS_WINDOWS; +exports.HAS_NATIVE_RECURSION = HAS_NATIVE_RECURSION; +const POLLING_INTERVAL = 3000; +exports.POLLING_INTERVAL = POLLING_INTERVAL; +const POLLING_TIMEOUT = 20000; +exports.POLLING_TIMEOUT = POLLING_TIMEOUT; +const RENAME_TIMEOUT = 1250; +exports.RENAME_TIMEOUT = RENAME_TIMEOUT; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQ0EsWUFBWTs7Ozs7O0FBRVosNENBQW9CO0FBRXBCLGVBQWU7QUFFZixNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUM7QUFzQmIsNEJBQVE7QUFwQmhCLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQztBQW9CQyxzQkFBSztBQWxCdkIsTUFBTSxRQUFRLEdBQUcsWUFBRSxDQUFDLFFBQVEsRUFBRyxDQUFDO0FBa0I2Qyw0QkFBUTtBQWhCckYsTUFBTSxRQUFRLEdBQUcsQ0FBRSxRQUFRLEtBQUssT0FBTyxDQUFFLENBQUM7QUFnQkssNEJBQVE7QUFkdkQsTUFBTSxNQUFNLEdBQUcsQ0FBRSxRQUFRLEtBQUssUUFBUSxDQUFFLENBQUM7QUFjZ0Isd0JBQU07QUFaL0QsTUFBTSxVQUFVLEdBQUcsQ0FBRSxRQUFRLEtBQUssT0FBTyxDQUFFLENBQUM7QUFZcUIsZ0NBQVU7QUFWM0UsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLElBQUksVUFBVSxDQUFDO0FBVXpCLG9EQUFvQjtBQVI3QyxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQztBQVF5RCw0Q0FBZ0I7QUFOdkcsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDO0FBTTJFLDBDQUFlO0FBSnhILE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQztBQUk4Rix3Q0FBYyJ9 \ No newline at end of file diff --git a/server/libs/watcher/debounce.js b/server/libs/watcher/debounce.js new file mode 100644 index 00000000..aa5ec03e --- /dev/null +++ b/server/libs/watcher/debounce.js @@ -0,0 +1,70 @@ +/** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. If `immediate` is passed, trigger the function on the + * leading edge, instead of the trailing. The function also has a property 'clear' + * that is a function which will clear the timer to prevent previously scheduled executions. + * + * @source underscore.js + * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ + * @param {Function} function to wrap + * @param {Number} timeout in ms (`100`) + * @param {Boolean} whether to execute at the beginning (`false`) + * @api public + */ +function debounce(func, wait, immediate) { + var timeout, args, context, timestamp, result; + if (null == wait) wait = 100; + + function later() { + var last = Date.now() - timestamp; + + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + var debounced = function () { + context = this; + args = arguments; + timestamp = Date.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + + debounced.clear = function () { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + }; + + debounced.flush = function () { + if (timeout) { + result = func.apply(context, args); + context = args = null; + + clearTimeout(timeout); + timeout = null; + } + }; + + return debounced; +}; + +// Adds compatibility for ES modules +debounce.debounce = debounce; + +module.exports = debounce; diff --git a/server/libs/watcher/enums.js b/server/libs/watcher/enums.js new file mode 100644 index 00000000..2839be0d --- /dev/null +++ b/server/libs/watcher/enums.js @@ -0,0 +1,4 @@ +"use strict"; +/* ENUMS */ +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW51bXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZW51bXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLFdBQVcifQ== \ No newline at end of file diff --git a/server/libs/watcher/is-primitive.js b/server/libs/watcher/is-primitive.js new file mode 100644 index 00000000..ccb0423c --- /dev/null +++ b/server/libs/watcher/is-primitive.js @@ -0,0 +1,15 @@ +/*! + * is-primitive + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + */ + +'use strict'; + +module.exports = function isPrimitive(val) { + if (typeof val === 'object') { + return val === null; + } + return typeof val !== 'function'; +}; diff --git a/server/libs/watcher/promise-concurrency-limiter.js b/server/libs/watcher/promise-concurrency-limiter.js new file mode 100644 index 00000000..944da4e1 --- /dev/null +++ b/server/libs/watcher/promise-concurrency-limiter.js @@ -0,0 +1,41 @@ +"use strict"; +/* IMPORT */ +/* PROMISE CONCURRENCY LIMITER */ +class Limiter { + /* CONSTRUCTOR */ + constructor(options) { + this.concurrency = options.concurrency; + this.count = 0; + this.queue = new Set(); + } + /* API */ + add(fn) { + if (this.count < this.concurrency) + return this.run(fn); + return new Promise(resolve => { + const callback = () => resolve(this.run(fn)); + this.queue.add(callback); + }); + } + flush() { + for (const callback of this.queue) { + if (this.count >= this.concurrency) + break; + this.queue.delete(callback); + callback(); + } + } + run(fn) { + this.count += 1; + const promise = fn(); + const cleanup = () => { + this.count -= 1; + this.flush(); + }; + promise.then(cleanup, cleanup); + return promise; + } +} +module.exports = Limiter; +module.exports.default = Limiter; +Object.defineProperty(module.exports, "__esModule", { value: true }); diff --git a/server/libs/watcher/ripstat/consts.js b/server/libs/watcher/ripstat/consts.js new file mode 100644 index 00000000..524be031 --- /dev/null +++ b/server/libs/watcher/ripstat/consts.js @@ -0,0 +1,19 @@ +"use strict"; +/* CONSTS */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.S_IFSOCK = exports.S_IFREG = exports.S_IFMT = exports.S_IFLNK = exports.S_IFIFO = exports.S_IFDIR = exports.S_IFCHR = exports.S_IFBLK = exports.RETRY_TIMEOUT = exports.MAX_SAFE_INTEGER = exports.IS_WINDOWS = void 0; +const IS_WINDOWS = (process.platform === 'win32'); +exports.IS_WINDOWS = IS_WINDOWS; +const { MAX_SAFE_INTEGER } = Number; +exports.MAX_SAFE_INTEGER = MAX_SAFE_INTEGER; +const RETRY_TIMEOUT = 5000; +exports.RETRY_TIMEOUT = RETRY_TIMEOUT; +const { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } = process['binding']('constants').fs; +exports.S_IFBLK = S_IFBLK; +exports.S_IFCHR = S_IFCHR; +exports.S_IFDIR = S_IFDIR; +exports.S_IFIFO = S_IFIFO; +exports.S_IFLNK = S_IFLNK; +exports.S_IFMT = S_IFMT; +exports.S_IFREG = S_IFREG; +exports.S_IFSOCK = S_IFSOCK; diff --git a/server/libs/watcher/ripstat/index.js b/server/libs/watcher/ripstat/index.js new file mode 100644 index 00000000..8bb739d1 --- /dev/null +++ b/server/libs/watcher/ripstat/index.js @@ -0,0 +1,39 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Stats = void 0; +const fs_1 = require("../atomically/utils/fs"); +const path_1 = require("path"); +const consts_1 = require("./consts"); +const stats_1 = require("./stats"); +exports.Stats = stats_1.default; +/* HELPERS */ +const { stat, FSReqCallback } = process['binding']('fs'); +/* RIPSTAT */ +const ripstat = (filePath, timeout) => { + return new Promise((resolve, reject) => { + const req = new FSReqCallback(true); + req.oncomplete = (error, statsdata) => { + if (error) { + const { code } = error; + if (code === 'EMFILE' || code === 'ENFILE' || code === 'EAGAIN' || code === 'EBUSY' || code === 'EACCESS' || code === 'EACCS' || code === 'EPERM') { // Retriable error + fs_1.default.statRetry(timeout || consts_1.RETRY_TIMEOUT)(filePath, { bigint: true }).then(nstats => { + const statsdata = [nstats.dev, nstats.mode, nstats.nlink, nstats.uid, nstats.gid, nstats.rdev, nstats.blksize, nstats.ino, nstats.size, nstats.blocks, 0n, nstats.atimeNs, 0n, nstats.mtimeNs, 0n, nstats.ctimeNs, 0n, nstats.birthtimeNs]; + const stats = new stats_1.default(statsdata); + resolve(stats); + }, reject); + } + else { + reject(error); + } + } + else { + const stats = new stats_1.default(statsdata); + resolve(stats); + } + }; + stat(path_1.toNamespacedPath(filePath), true, req); + }); +}; +/* EXPORT */ +exports.default = ripstat; diff --git a/server/libs/watcher/ripstat/stats.js b/server/libs/watcher/ripstat/stats.js new file mode 100644 index 00000000..a4dc329c --- /dev/null +++ b/server/libs/watcher/ripstat/stats.js @@ -0,0 +1,55 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +const consts_1 = require("./consts"); +/* HELPERS */ +const { floor } = Math; +const toNumber = Number; +/* STATS */ +class Stats { + /* CONSTRUCTOR */ + constructor(stats) { + this.dev = toNumber(stats[0]); + this.mode = toNumber(stats[1]); + this.nlink = toNumber(stats[2]); + this.uid = toNumber(stats[3]); + this.gid = toNumber(stats[4]); + this.rdev = toNumber(stats[5]); + this.blksize = toNumber(stats[6]); + this.ino = (stats[7] <= consts_1.MAX_SAFE_INTEGER) ? toNumber(stats[7]) : stats[7]; + this.size = toNumber(stats[8]); + this.blocks = toNumber(stats[9]); + this.atimeMs = (toNumber(stats[10]) * 1000) + floor(toNumber(stats[11]) / 1000000); + this.mtimeMs = (toNumber(stats[12]) * 1000) + floor(toNumber(stats[13]) / 1000000); + this.ctimeMs = (toNumber(stats[14]) * 1000) + floor(toNumber(stats[15]) / 1000000); + this.birthtimeMs = (toNumber(stats[16]) * 1000) + floor(toNumber(stats[17]) / 1000000); + } + /* HELPERS */ + _isMode(mode) { + return (this.mode & consts_1.S_IFMT) === mode; + } + /* API */ + isDirectory() { + return this._isMode(consts_1.S_IFDIR); + } + isFile() { + return this._isMode(consts_1.S_IFREG); + } + isBlockDevice() { + return !consts_1.IS_WINDOWS && this._isMode(consts_1.S_IFBLK); + } + isCharacterDevice() { + return this._isMode(consts_1.S_IFCHR); + } + isSymbolicLink() { + return this._isMode(consts_1.S_IFLNK); + } + isFIFO() { + return !consts_1.IS_WINDOWS && this._isMode(consts_1.S_IFIFO); + } + isSocket() { + return !consts_1.IS_WINDOWS && this._isMode(consts_1.S_IFSOCK); + } +} +/* EXPORT */ +exports.default = Stats; diff --git a/server/libs/watcher/string-indexes.js b/server/libs/watcher/string-indexes.js new file mode 100644 index 00000000..c4b70c9d --- /dev/null +++ b/server/libs/watcher/string-indexes.js @@ -0,0 +1,17 @@ +"use strict"; +/* STRING INDEXES */ +function indexes(str, substr) { + var indexes = [], rangeLength = substr.length; + var indexFrom = 0; + while (true) { + var index = str.indexOf(substr, indexFrom); + if (index === -1) + return indexes; + indexes.push(index); + indexFrom = index + rangeLength; + } +} +/* EXPORT */ +module.exports = indexes; +module.exports.default = indexes; +Object.defineProperty(module.exports, "__esModule", { value: true }); diff --git a/server/libs/watcher/tiny-readdir.js b/server/libs/watcher/tiny-readdir.js new file mode 100644 index 00000000..5ba9ce80 --- /dev/null +++ b/server/libs/watcher/tiny-readdir.js @@ -0,0 +1,104 @@ +"use strict"; +/* IMPORT */ +const fs = require("fs"); +const path = require("path"); +const promise_concurrency_limiter_1 = require("./promise-concurrency-limiter"); +/* HELPERS */ +const limiter = new promise_concurrency_limiter_1.default({ concurrency: 500 }); +/* TINY READDIR */ +const readdir = (rootPath, options) => { + var _a, _b, _c, _d; + const followSymlinks = (_a = options === null || options === void 0 ? void 0 : options.followSymlinks) !== null && _a !== void 0 ? _a : false, maxDepth = (_b = options === null || options === void 0 ? void 0 : options.depth) !== null && _b !== void 0 ? _b : Infinity, isIgnored = (_c = options === null || options === void 0 ? void 0 : options.ignore) !== null && _c !== void 0 ? _c : (() => false), signal = (_d = options === null || options === void 0 ? void 0 : options.signal) !== null && _d !== void 0 ? _d : { aborted: false }, directories = [], files = [], symlinks = [], map = {}, resultEmpty = { directories: [], files: [], symlinks: [], map: {} }, result = { directories, files, symlinks, map }; + const handleDirectory = (dirmap, subPath, depth) => { + dirmap.directories.push(subPath); + directories.push(subPath); + if (depth >= maxDepth) + return; + + // if depth > 1 and the limiter is full, then we cannot queue this function or the current promise will never return + if (depth > 1 && limiter.count >= limiter.concurrency) return populateResultFromPath(subPath, depth + 1) + + return limiter.add(() => populateResultFromPath(subPath, depth + 1)); + }; + const handleFile = (dirmap, subPath) => { + dirmap.files.push(subPath); + files.push(subPath); + }; + const handleSymlink = (dirmap, subPath, depth) => { + dirmap.symlinks.push(subPath); + symlinks.push(subPath); + if (!followSymlinks) + return; + if (depth >= maxDepth) + return; + return limiter.add(() => populateResultFromSymlink(subPath, depth + 1)); + }; + const handleStat = (dirmap, rootPath, stat, depth) => { + if (signal.aborted) + return; + if (isIgnored(rootPath)) + return; + if (stat.isDirectory()) { + return handleDirectory(dirmap, rootPath, depth); + } + else if (stat.isFile()) { + return handleFile(dirmap, rootPath); + } + else if (stat.isSymbolicLink()) { + return handleSymlink(dirmap, rootPath, depth); + } + }; + const handleDirent = (dirmap, rootPath, dirent, depth) => { + if (signal.aborted) + return; + const subPath = `${rootPath}${path.sep}${dirent.name}`; + if (isIgnored(subPath)) + return; + if (dirent.isDirectory()) { + return handleDirectory(dirmap, subPath, depth); + } + else if (dirent.isFile()) { + return handleFile(dirmap, subPath); + } + else if (dirent.isSymbolicLink()) { + return handleSymlink(dirmap, subPath, depth); + } + }; + const handleDirents = (dirmap, rootPath, dirents, depth) => { + return Promise.all(dirents.map((dirent) => { + return handleDirent(dirmap, rootPath, dirent, depth); + })); + }; + const populateResultFromPath = async (rootPath, depth) => { + if (signal.aborted) + return; + if (depth > maxDepth) + return; + const dirents = await fs.promises.readdir(rootPath, { withFileTypes: true }).catch(() => []); + if (signal.aborted) + return; + const dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] }; + if (!dirents.length) + return; + await handleDirents(dirmap, rootPath, dirents, depth); + }; + const populateResultFromSymlink = async (rootPath, depth) => { + try { + const realPath = await fs.promises.realpath(rootPath), stat = await fs.promises.stat(realPath), dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] }; + await handleStat(dirmap, realPath, stat, depth); + } + catch (_a) { } + }; + const getResult = async (rootPath, depth = 1) => { + rootPath = path.normalize(rootPath); + await populateResultFromPath(rootPath, depth); + if (signal.aborted) + return resultEmpty; + return result; + }; + return getResult(rootPath); +}; +/* EXPORT */ +module.exports = readdir; +module.exports.default = readdir; +Object.defineProperty(module.exports, "__esModule", { value: true }); diff --git a/server/libs/watcher/types.js b/server/libs/watcher/types.js new file mode 100644 index 00000000..682b3f45 --- /dev/null +++ b/server/libs/watcher/types.js @@ -0,0 +1,12 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WatcherStats = exports.Stats = void 0; +const ripstat_1 = require("ripstat"); +Object.defineProperty(exports, "Stats", { enumerable: true, get: function () { return ripstat_1.Stats; } }); +const watcher_stats_1 = __importDefault(require("./watcher_stats")); +exports.WatcherStats = watcher_stats_1.default; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLFlBQVk7Ozs7OztBQUdaLHFDQUE4QjtBQWlGbUUsc0ZBakZ6RixlQUFLLE9BaUZ5RjtBQTlFdEcsb0VBQTJDO0FBOEUwSyx1QkE5RTlNLHVCQUFZLENBOEU4TSJ9 \ No newline at end of file diff --git a/server/libs/watcher/utils.js b/server/libs/watcher/utils.js new file mode 100644 index 00000000..75a2bdb2 --- /dev/null +++ b/server/libs/watcher/utils.js @@ -0,0 +1,88 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const are_shallow_equal_1 = __importDefault(require("./are-shallow-equal")); +const debounce_1 = __importDefault(require("./debounce")); +const path_1 = __importDefault(require("path")); +const ripstat_1 = __importDefault(require("./ripstat")); +const tiny_readdir_1 = __importDefault(require("./tiny-readdir")); +const constants_1 = require("./constants"); +/* UTILS */ +const Utils = { + lang: { + areShallowEqual: //TODO: Import all these utilities from "nanodash" instead + are_shallow_equal_1.default, + debounce: debounce_1.default, + attempt: (fn) => { + try { + return fn(); + } + catch (error) { + return Utils.lang.castError(error); + } + }, + castArray: (x) => { + return Utils.lang.isArray(x) ? x : [x]; + }, + castError(exception) { + if (Utils.lang.isError(exception)) + return exception; + if (Utils.lang.isString(exception)) + return new Error(exception); + return new Error('Unknown error'); + }, + defer: (callback) => { + return setTimeout(callback, 0); + }, + isArray: (x) => { + return Array.isArray(x); + }, + isError(x) { + return x instanceof Error; + }, + isFunction: (x) => { + return typeof x === 'function'; + }, + isNumber: (x) => { + return typeof x === 'number'; + }, + isString: (x) => { + return typeof x === 'string'; + }, + isUndefined: (x) => { + return x === undefined; + }, + noop: () => { + return; + }, + uniq: (arr) => { + if (arr.length < 2) + return arr; + return Array.from(new Set(arr)); + } + }, + fs: { + isSubPath: (targetPath, subPath) => { + return (subPath.startsWith(targetPath) && subPath[targetPath.length] === path_1.default.sep && (subPath.length - targetPath.length) > path_1.default.sep.length); + }, + poll: (targetPath, timeout = constants_1.POLLING_TIMEOUT) => { + return ripstat_1.default(targetPath, timeout).catch(Utils.lang.noop); + }, + readdir: async (rootPath, ignore, depth = Infinity, signal, readdirMap) => { + if (readdirMap && depth === 1 && rootPath in readdirMap) { // Reusing cached data + const result = readdirMap[rootPath]; + return [result.directories, result.files]; + } + else { // Retrieving fresh data + const result = await tiny_readdir_1.default(rootPath, { depth, ignore, signal }); + return [result.directories, result.files]; + } + } + } +}; +/* EXPORT */ +exports.default = Utils; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLFlBQVk7Ozs7O0FBRVosMEVBQWdEO0FBQ2hELHdEQUFnQztBQUNoQyxnREFBd0I7QUFDeEIsc0RBQThCO0FBQzlCLGdFQUFtQztBQUNuQywyQ0FBNEM7QUFHNUMsV0FBVztBQUVYLE1BQU0sS0FBSyxHQUFHO0lBRVosSUFBSSxFQUFFO1FBRUosZUFBZSxFQUZULDBEQUEwRDtRQUVoRSwyQkFBZTtRQUVmLFFBQVEsRUFBUixrQkFBUTtRQUVSLE9BQU8sRUFBRSxDQUFNLEVBQVcsRUFBYyxFQUFFO1lBRXhDLElBQUk7Z0JBRUYsT0FBTyxFQUFFLEVBQUcsQ0FBQzthQUVkO1lBQUMsT0FBUSxLQUFjLEVBQUc7Z0JBRXpCLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUcsS0FBSyxDQUFFLENBQUM7YUFFdkM7UUFFSCxDQUFDO1FBRUQsU0FBUyxFQUFFLENBQU0sQ0FBVSxFQUFRLEVBQUU7WUFFbkMsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBRyxDQUFDLENBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTVDLENBQUM7UUFFRCxTQUFTLENBQUcsU0FBa0I7WUFFNUIsSUFBSyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBRyxTQUFTLENBQUU7Z0JBQUcsT0FBTyxTQUFTLENBQUM7WUFFekQsSUFBSyxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBRyxTQUFTLENBQUU7Z0JBQUcsT0FBTyxJQUFJLEtBQUssQ0FBRyxTQUFTLENBQUUsQ0FBQztZQUV4RSxPQUFPLElBQUksS0FBSyxDQUFHLGVBQWUsQ0FBRSxDQUFDO1FBRXZDLENBQUM7UUFFRCxLQUFLLEVBQUUsQ0FBRSxRQUFrQixFQUFtQixFQUFFO1lBRTlDLE9BQU8s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 \ No newline at end of file diff --git a/server/libs/watcher/watcher.js b/server/libs/watcher/watcher.js new file mode 100644 index 00000000..4e3af1f2 --- /dev/null +++ b/server/libs/watcher/watcher.js @@ -0,0 +1,397 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +const aborter_1 = __importDefault(require("./aborter/controller")); +const events_1 = require("events"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const string_indexes_1 = __importDefault(require("./string-indexes")); +const constants_1 = require("./constants"); +const watcher_handler_1 = __importDefault(require("./watcher_handler")); +const watcher_locker_1 = __importDefault(require("./watcher_locker")); +const watcher_poller_1 = __importDefault(require("./watcher_poller")); +const utils_1 = __importDefault(require("./utils")); +/* WATCHER */ +class Watcher extends events_1.EventEmitter { + /* CONSTRUCTOR */ + constructor(target, options, handler) { + super(); + this._closed = false; + this._ready = false; + this._closeAborter = new aborter_1.default(); + this._closeSignal = this._closeAborter.signal; + this.on("close" /* CLOSE */, () => this._closeAborter.abort()); + this._closeWait = new Promise(resolve => this.on("close" /* CLOSE */, resolve)); + this._readyWait = new Promise(resolve => this.on("ready" /* READY */, resolve)); + this._locker = new watcher_locker_1.default(this); + this._roots = new Set(); + this._poller = new watcher_poller_1.default(); + this._pollers = new Set(); + this._subwatchers = new Set(); + this._watchers = {}; + this._watchersLock = Promise.resolve(); + this._watchersRestorable = {}; + this.watch(target, options, handler); + } + /* API */ + isClosed() { + return this._closed; + } + isIgnored(targetPath, ignore) { + return !!ignore && !!ignore(targetPath); + } + isReady() { + return this._ready; + } + close() { + this._locker.reset(); + this._poller.reset(); + this._roots.clear(); + this.watchersClose(); + if (this.isClosed()) + return false; + this._closed = true; + return this.emit("close" /* CLOSE */); + } + error(exception) { + if (this.isClosed()) + return false; + const error = utils_1.default.lang.castError(exception); + return this.emit("error" /* ERROR */, error); + } + event(event, targetPath, targetPathNext) { + if (this.isClosed()) + return false; + this.emit("all" /* ALL */, event, targetPath, targetPathNext); + return this.emit(event, targetPath, targetPathNext); + } + ready() { + if (this.isClosed() || this.isReady()) + return false; + this._ready = true; + return this.emit("ready" /* READY */); + } + pollerExists(targetPath, options) { + for (const poller of this._pollers) { + if (poller.targetPath !== targetPath) + continue; + if (!utils_1.default.lang.areShallowEqual(poller.options, options)) + continue; + return true; + } + return false; + } + subwatcherExists(targetPath, options) { + for (const subwatcher of this._subwatchers) { + if (subwatcher.targetPath !== targetPath) + continue; + if (!utils_1.default.lang.areShallowEqual(subwatcher.options, options)) + continue; + return true; + } + return false; + } + watchersClose(folderPath, filePath, recursive = true) { + if (!folderPath) { + for (const folderPath in this._watchers) { + this.watchersClose(folderPath, filePath, false); + } + } + else { + const configs = this._watchers[folderPath]; + if (configs) { + for (const config of configs) { + if (filePath && config.filePath !== filePath) + continue; + this.watcherClose(config); + } + } + if (recursive) { + for (const folderPathOther in this._watchers) { + if (!utils_1.default.fs.isSubPath(folderPath, folderPathOther)) + continue; + this.watchersClose(folderPathOther, filePath, false); + } + } + } + } + watchersLock(callback) { + return this._watchersLock.then(() => { + return this._watchersLock = new Promise(async (resolve) => { + await callback(); + resolve(); + }); + }); + } + watchersRestore() { + delete this._watchersRestoreTimeout; + const watchers = Object.entries(this._watchersRestorable); + this._watchersRestorable = {}; + for (const [targetPath, config] of watchers) { + this.watchPath(targetPath, config.options, config.handler); + } + } + async watcherAdd(config, baseWatcherHandler) { + const { folderPath } = config; + const configs = this._watchers[folderPath] = (this._watchers[folderPath] || []); + configs.push(config); + const watcherHandler = new watcher_handler_1.default(this, config, baseWatcherHandler); + await watcherHandler.init(); + return watcherHandler; + } + watcherClose(config) { + config.watcher.close(); + const configs = this._watchers[config.folderPath]; + if (configs) { + const index = configs.indexOf(config); + configs.splice(index, 1); + if (!configs.length) { + delete this._watchers[config.folderPath]; + } + } + const rootPath = config.filePath || config.folderPath, isRoot = this._roots.has(rootPath); + if (isRoot) { + this._watchersRestorable[rootPath] = config; + if (!this._watchersRestoreTimeout) { + this._watchersRestoreTimeout = utils_1.default.lang.defer(() => this.watchersRestore()); + } + } + } + watcherExists(folderPath, options, handler, filePath) { + const configsSibling = this._watchers[folderPath]; + if (!!(configsSibling === null || configsSibling === void 0 ? void 0 : configsSibling.find(config => config.handler === handler && (!config.filePath || config.filePath === filePath) && config.options.ignore === options.ignore && !!config.options.native === !!options.native && (!options.recursive || config.options.recursive)))) + return true; + let folderAncestorPath = path_1.default.dirname(folderPath); + for (let depth = 1; depth < Infinity; depth++) { + const configsAncestor = this._watchers[folderAncestorPath]; + if (!!(configsAncestor === null || configsAncestor === void 0 ? void 0 : configsAncestor.find(config => { var _a; return (depth === 1 || (config.options.recursive && depth <= ((_a = config.options.depth) !== null && _a !== void 0 ? _a : constants_1.DEPTH))) && config.handler === handler && (!config.filePath || config.filePath === filePath) && config.options.ignore === options.ignore && !!config.options.native === !!options.native && (!options.recursive || (config.options.recursive && (constants_1.HAS_NATIVE_RECURSION && config.options.native !== false))); }))) + return true; + if (!constants_1.HAS_NATIVE_RECURSION) + break; // No other ancestor will possibly be found + const folderAncestorPathNext = path_1.default.dirname(folderPath); + if (folderAncestorPath === folderAncestorPathNext) + break; + folderAncestorPath = folderAncestorPathNext; + } + return false; + } + async watchDirectories(foldersPaths, options, handler, filePath, baseWatcherHandler) { + if (this.isClosed()) + return; + foldersPaths = utils_1.default.lang.uniq(foldersPaths).sort(); + let watcherHandlerLast; + for (const folderPath of foldersPaths) { + if (this.isIgnored(folderPath, options.ignore)) + continue; + if (this.watcherExists(folderPath, options, handler, filePath)) + continue; + try { + const watcherOptions = (!options.recursive || (constants_1.HAS_NATIVE_RECURSION && options.native !== false)) ? options : { ...options, recursive: false }, // Ensuring recursion is explicitly disabled if not available + watcher = fs_1.default.watch(folderPath, watcherOptions), watcherConfig = { watcher, handler, options, folderPath, filePath }, watcherHandler = watcherHandlerLast = await this.watcherAdd(watcherConfig, baseWatcherHandler); + const isRoot = this._roots.has(filePath || folderPath); + if (isRoot) { + const parentOptions = { ...options, ignoreInitial: true, recursive: false }, // Ensuring only the parent folder is being watched + parentFolderPath = path_1.default.dirname(folderPath), parentFilePath = folderPath; + await this.watchDirectories([parentFolderPath], parentOptions, handler, parentFilePath, watcherHandler); + //TODO: Watch parents recursively with the following code, which requires other things to be changed too though + // while ( true ) { + // await this.watchDirectories ( [parentFolderPath], parentOptions, handler, parentFilePath, watcherHandler ); + // const parentFolderPathNext = path.dirname ( parentFolderPath ); + // if ( parentFolderPath === parentFolderPathNext ) break; + // parentFilePath = parentFolderPath; + // parentFolderPath = parentFolderPathNext; + // } + } + } + catch (error) { + this.error(error); + } + } + return watcherHandlerLast; + } + async watchDirectory(folderPath, options, handler, filePath, baseWatcherHandler) { + var _a; + if (this.isClosed()) + return; + if (this.isIgnored(folderPath, options.ignore)) + return; + if (!options.recursive || (constants_1.HAS_NATIVE_RECURSION && options.native !== false)) { + return this.watchersLock(() => { + return this.watchDirectories([folderPath], options, handler, filePath, baseWatcherHandler); + }); + } + else { + options = { ...options, recursive: true }; // Ensuring recursion is explicitly enabled + const depth = (_a = options.depth) !== null && _a !== void 0 ? _a : constants_1.DEPTH, [folderSubPaths] = await utils_1.default.fs.readdir(folderPath, options.ignore, depth, this._closeSignal, options.readdirMap); + return this.watchersLock(async () => { + const watcherHandler = await this.watchDirectories([folderPath], options, handler, filePath, baseWatcherHandler); + if (folderSubPaths.length) { + const folderPathDepth = string_indexes_1.default(folderPath, path_1.default.sep).length; + for (const folderSubPath of folderSubPaths) { + const folderSubPathDepth = string_indexes_1.default(folderSubPath, path_1.default.sep).length, subDepth = Math.max(0, depth - (folderSubPathDepth - folderPathDepth)), subOptions = { ...options, depth: subDepth }; // Updating the maximum depth to account for depth of the sub path + await this.watchDirectories([folderSubPath], subOptions, handler, filePath, baseWatcherHandler || watcherHandler); + } + } + }); + } + } + async watchFileOnce(filePath, options, callback) { + if (this.isClosed()) + return; + options = { ...options, ignoreInitial: false }; // Ensuring initial events are detected too + if (this.subwatcherExists(filePath, options)) + return; + const config = { targetPath: filePath, options }; + const handler = (event, targetPath) => { + if (targetPath !== filePath) + return; + stop(); + callback(); + }; + const watcher = new Watcher(handler); + const start = () => { + this._subwatchers.add(config); + this.on("close" /* CLOSE */, stop); // Ensuring the subwatcher is stopped on close + watcher.watchFile(filePath, options, handler); + }; + const stop = () => { + this._subwatchers.delete(config); + this.removeListener("close" /* CLOSE */, stop); // Ensuring there are no leftover listeners + watcher.close(); + }; + return start(); + } + async watchFile(filePath, options, handler) { + if (this.isClosed()) + return; + if (this.isIgnored(filePath, options.ignore)) + return; + options = { ...options, recursive: false }; // Ensuring recursion is explicitly disabled + const folderPath = path_1.default.dirname(filePath); + return this.watchDirectory(folderPath, options, handler, filePath); + } + async watchPollingOnce(targetPath, options, callback) { + if (this.isClosed()) + return; + let isDone = false; + const poller = new watcher_poller_1.default(); + const disposer = await this.watchPolling(targetPath, options, async () => { + if (isDone) + return; + const events = await poller.update(targetPath, options.pollingTimeout); + if (!events.length) + return; // Nothing actually changed, skipping + if (isDone) + return; // Another async callback has done the work already, skipping + isDone = true; + disposer(); + callback(); + }); + } + async watchPolling(targetPath, options, callback) { + var _a; + if (this.isClosed()) + return utils_1.default.lang.noop; + if (this.pollerExists(targetPath, options)) + return utils_1.default.lang.noop; + const watcherOptions = { ...options, interval: (_a = options.pollingInterval) !== null && _a !== void 0 ? _a : constants_1.POLLING_INTERVAL }; // Ensuring a default interval is set + const config = { targetPath, options }; + const start = () => { + this._pollers.add(config); + this.on("close" /* CLOSE */, stop); // Ensuring polling is stopped on close + fs_1.default.watchFile(targetPath, watcherOptions, callback); + }; + const stop = () => { + this._pollers.delete(config); + this.removeListener("close" /* CLOSE */, stop); // Ensuring there are no leftover listeners + fs_1.default.unwatchFile(targetPath, callback); + }; + utils_1.default.lang.attempt(start); + return () => utils_1.default.lang.attempt(stop); + } + async watchUnknownChild(targetPath, options, handler) { + if (this.isClosed()) + return; + const watch = () => this.watchPath(targetPath, options, handler); + return this.watchFileOnce(targetPath, options, watch); + } + async watchUnknownTarget(targetPath, options, handler) { + if (this.isClosed()) + return; + const watch = () => this.watchPath(targetPath, options, handler); + return this.watchPollingOnce(targetPath, options, watch); + } + async watchPaths(targetPaths, options, handler) { + if (this.isClosed()) + return; + targetPaths = utils_1.default.lang.uniq(targetPaths).sort(); + const isParallelizable = targetPaths.every((targetPath, index) => targetPaths.every((t, i) => i === index || !utils_1.default.fs.isSubPath(targetPath, t))); // All paths are about separate subtrees, so we can start watching in parallel safely //TODO: Find parallelizable chunks rather than using an all or nothing approach + if (isParallelizable) { // Watching in parallel + await Promise.all(targetPaths.map(targetPath => { + return this.watchPath(targetPath, options, handler); + })); + } + else { // Watching serially + for (const targetPath of targetPaths) { + await this.watchPath(targetPath, options, handler); + } + } + } + async watchPath(targetPath, options, handler) { + if (this.isClosed()) + return; + targetPath = path_1.default.normalize(targetPath); + if (this.isIgnored(targetPath, options.ignore)) + return; + const stats = await utils_1.default.fs.poll(targetPath, options.pollingTimeout); + if (!stats) { + const parentPath = path_1.default.dirname(targetPath), parentStats = await utils_1.default.fs.poll(parentPath, options.pollingTimeout); + if (parentStats === null || parentStats === void 0 ? void 0 : parentStats.isDirectory()) { + return this.watchUnknownChild(targetPath, options, handler); + } + else { + return this.watchUnknownTarget(targetPath, options, handler); + } + } + else if (stats.isFile()) { + return this.watchFile(targetPath, options, handler); + } + else if (stats.isDirectory()) { + return this.watchDirectory(targetPath, options, handler); + } + else { + this.error(`"${targetPath}" is not supported`); + } + } + async watch(target, options, handler = utils_1.default.lang.noop) { + if (utils_1.default.lang.isFunction(target)) + return this.watch([], {}, target); + if (utils_1.default.lang.isUndefined(target)) + return this.watch([], options, handler); + if (utils_1.default.lang.isFunction(options)) + return this.watch(target, {}, options); + if (utils_1.default.lang.isUndefined(options)) + return this.watch(target, {}, handler); + if (this.isClosed()) + return; + if (this.isReady()) + options.readdirMap = undefined; // Only usable before initialization + const targetPaths = utils_1.default.lang.castArray(target); + targetPaths.forEach(targetPath => this._roots.add(targetPath)); + await this.watchPaths(targetPaths, options, handler); + if (this.isClosed()) + return; + if (handler !== utils_1.default.lang.noop) { + this.on("all" /* ALL */, handler); + } + options.readdirMap = undefined; // Only usable before initialization + this.ready(); + } +} +/* EXPORT */ +module.exports = Watcher; +module.exports.default = Watcher; +Object.defineProperty(module.exports, "__esModule", { value: true }); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy93YXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxZQUFZOzs7OztBQUVaLHNEQUE4QjtBQUM5QixtQ0FBb0M7QUFDcEMsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QixvRUFBMkM7QUFDM0MsMkNBQTBFO0FBRTFFLHdFQUErQztBQUMvQyxzRUFBNkM7QUFDN0Msc0VBQTZDO0FBQzdDLG9EQUE0QjtBQUc1QixhQUFhO0FBRWIsTUFBTSxPQUFRLFNBQVEscUJBQVk7SUFvQmhDLGlCQUFpQjtJQUVqQixZQUFjLE1BQWdDLEVBQUUsT0FBa0MsRUFBRSxPQUFpQjtRQUVuRyxLQUFLLEVBQUcsQ0FBQztRQUVULElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxpQkFBTyxFQUFHLENBQUM7UUFDcEMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQztRQUM5QyxJQUFJLENBQUMsRUFBRSxzQkFBdUIsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUcsQ0FBRSxDQUFDO1FBQ2xFLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxPQUFPLENBQUcsT0FBTyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxzQkFBdUIsT0FBTyxDQUFFLENBQUUsQ0FBQztRQUNyRixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksT0FBTyxDQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsc0JBQXVCLE9BQU8sQ0FBRSxDQUFFLENBQUM7UUFDckYsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLHdCQUFhLENBQUcsSUFBSSxDQUFFLENBQUM7UUFDMUMsSUFBSSxDQUFDLE1BQU0s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 \ No newline at end of file diff --git a/server/libs/watcher/watcher_handler.js b/server/libs/watcher/watcher_handler.js new file mode 100644 index 00000000..411e65b0 --- /dev/null +++ b/server/libs/watcher/watcher_handler.js @@ -0,0 +1,245 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const path_1 = __importDefault(require("path")); +const constants_1 = require("./constants"); +const utils_1 = __importDefault(require("./utils")); +/* WATCHER HANDLER */ +class WatcherHandler { + /* CONSTRUCTOR */ + constructor(watcher, config, base) { + this.base = base; + this.watcher = watcher; + this.handler = config.handler; + this.fswatcher = config.watcher; + this.options = config.options; + this.folderPath = config.folderPath; + this.filePath = config.filePath; + this['handlerBatched'] = this.base ? this.base.onWatcherEvent.bind(this.base) : this._makeHandlerBatched(this.options.debounce); //UGLY + } + /* HELPERS */ + _isSubRoot(targetPath) { + if (this.filePath) { + return targetPath === this.filePath; + } + else { + return targetPath === this.folderPath || utils_1.default.fs.isSubPath(this.folderPath, targetPath); + } + } + _makeHandlerBatched(delay = constants_1.DEBOUNCE) { + return (() => { + let lock = this.watcher._readyWait, // ~Ensuring no two flushes are active in parallel, or before the watcher is ready + initials = [], regulars = new Set(); + const flush = async (initials, regulars) => { + const initialEvents = this.options.ignoreInitial ? [] : initials, regularEvents = await this.eventsPopulate([...regulars]), events = this.eventsDeduplicate([...initialEvents, ...regularEvents]); + this.onTargetEvents(events); + }; + const flushDebounced = utils_1.default.lang.debounce(() => { + if (this.watcher.isClosed()) + return; + lock = flush(initials, regulars); + initials = []; + regulars = new Set(); + }, delay); + return async (event, targetPath = '', isInitial = false) => { + if (isInitial) { // Poll immediately + await this.eventsPopulate([targetPath], initials, true); + } + else { // Poll later + regulars.add(targetPath); + } + lock.then(flushDebounced); + }; + })(); + } + /* EVENT HELPERS */ + eventsDeduplicate(events) { + if (events.length < 2) + return events; + const targetsEventPrev = {}; + return events.reduce((acc, event) => { + const [targetEvent, targetPath] = event, targetEventPrev = targetsEventPrev[targetPath]; + if (targetEvent === targetEventPrev) + return acc; // Same event, ignoring + if (targetEvent === "change" /* CHANGE */ && targetEventPrev === "add" /* ADD */) + return acc; // "change" after "add", ignoring + targetsEventPrev[targetPath] = targetEvent; + acc.push(event); + return acc; + }, []); + } + async eventsPopulate(targetPaths, events = [], isInitial = false) { + await Promise.all(targetPaths.map(async (targetPath) => { + const targetEvents = await this.watcher._poller.update(targetPath, this.options.pollingTimeout); + await Promise.all(targetEvents.map(async (event) => { + events.push([event, targetPath]); + if (event === "addDir" /* ADD_DIR */) { + await this.eventsPopulateAddDir(targetPaths, targetPath, events, isInitial); + } + else if (event === "unlinkDir" /* UNLINK_DIR */) { + await this.eventsPopulateUnlinkDir(targetPaths, targetPath, events, isInitial); + } + })); + })); + return events; + } + ; + async eventsPopulateAddDir(targetPaths, targetPath, events = [], isInitial = false) { + var _a, _b; + if (isInitial) + return events; + const depth = this.options.recursive ? (_a = this.options.depth) !== null && _a !== void 0 ? _a : constants_1.DEPTH : Math.min(1, (_b = this.options.depth) !== null && _b !== void 0 ? _b : constants_1.DEPTH), [directories, files] = await utils_1.default.fs.readdir(targetPath, this.options.ignore, depth, this.watcher._closeSignal), targetSubPaths = [...directories, ...files]; + await Promise.all(targetSubPaths.map(targetSubPath => { + if (this.watcher.isIgnored(targetSubPath, this.options.ignore)) + return; + if (targetPaths.includes(targetSubPath)) + return; + return this.eventsPopulate([targetSubPath], events, true); + })); + return events; + } + async eventsPopulateUnlinkDir(targetPaths, targetPath, events = [], isInitial = false) { + if (isInitial) + return events; + for (const folderPathOther of this.watcher._poller.stats.keys()) { + if (!utils_1.default.fs.isSubPath(targetPath, folderPathOther)) + continue; + if (targetPaths.includes(folderPathOther)) + continue; + await this.eventsPopulate([folderPathOther], events, true); + } + return events; + } + /* EVENT HANDLERS */ + onTargetAdd(targetPath) { + if (this._isSubRoot(targetPath)) { + if (this.options.renameDetection) { + this.watcher._locker.getLockTargetAdd(targetPath, this.options.renameTimeout); + } + else { + this.watcher.event("add" /* ADD */, targetPath); + } + } + } + onTargetAddDir(targetPath) { + if (targetPath !== this.folderPath && this.options.recursive && (!constants_1.HAS_NATIVE_RECURSION && this.options.native !== false)) { + this.watcher.watchDirectory(targetPath, this.options, this.handler, undefined, this.base || this); + } + if (this._isSubRoot(targetPath)) { + if (this.options.renameDetection) { + this.watcher._locker.getLockTargetAddDir(targetPath, this.options.renameTimeout); + } + else { + this.watcher.event("addDir" /* ADD_DIR */, targetPath); + } + } + } + onTargetChange(targetPath) { + if (this._isSubRoot(targetPath)) { + this.watcher.event("change" /* CHANGE */, targetPath); + } + } + onTargetUnlink(targetPath) { + this.watcher.watchersClose(path_1.default.dirname(targetPath), targetPath, false); + if (this._isSubRoot(targetPath)) { + if (this.options.renameDetection) { + this.watcher._locker.getLockTargetUnlink(targetPath, this.options.renameTimeout); + } + else { + this.watcher.event("unlink" /* UNLINK */, targetPath); + } + } + } + onTargetUnlinkDir(targetPath) { + this.watcher.watchersClose(path_1.default.dirname(targetPath), targetPath, false); + this.watcher.watchersClose(targetPath); + if (this._isSubRoot(targetPath)) { + if (this.options.renameDetection) { + this.watcher._locker.getLockTargetUnlinkDir(targetPath, this.options.renameTimeout); + } + else { + this.watcher.event("unlinkDir" /* UNLINK_DIR */, targetPath); + } + } + } + onTargetEvent(event) { + const [targetEvent, targetPath] = event; + if (targetEvent === "add" /* ADD */) { + this.onTargetAdd(targetPath); + } + else if (targetEvent === "addDir" /* ADD_DIR */) { + this.onTargetAddDir(targetPath); + } + else if (targetEvent === "change" /* CHANGE */) { + this.onTargetChange(targetPath); + } + else if (targetEvent === "unlink" /* UNLINK */) { + this.onTargetUnlink(targetPath); + } + else if (targetEvent === "unlinkDir" /* UNLINK_DIR */) { + this.onTargetUnlinkDir(targetPath); + } + } + onTargetEvents(events) { + for (const event of events) { + this.onTargetEvent(event); + } + } + onWatcherEvent(event, targetPath, isInitial = false) { + return this['handlerBatched'](event, targetPath, isInitial); + } + onWatcherChange(event = "change" /* CHANGE */, targetName) { + if (this.watcher.isClosed()) + return; + const targetPath = path_1.default.resolve(this.folderPath, targetName || ''); + if (this.filePath && targetPath !== this.folderPath && targetPath !== this.filePath) + return; + if (this.watcher.isIgnored(targetPath, this.options.ignore)) + return; + this.onWatcherEvent(event, targetPath); + } + onWatcherError(error) { + if (constants_1.IS_WINDOWS && error.code === 'EPERM') { // This may happen when a folder is deleted + this.onWatcherChange("change" /* CHANGE */, ''); + } + else { + this.watcher.error(error); + } + } + /* API */ + async init() { + await this.initWatcherEvents(); + await this.initInitialEvents(); + } + async initWatcherEvents() { + const onChange = this.onWatcherChange.bind(this); + this.fswatcher.on("change" /* CHANGE */, onChange); + const onError = this.onWatcherError.bind(this); + this.fswatcher.on("error" /* ERROR */, onError); + } + async initInitialEvents() { + var _a, _b; + const isInitial = !this.watcher.isReady(); // "isInitial" => is ignorable via the "ignoreInitial" option + if (this.filePath) { // Single initial path + if (this.watcher._poller.stats.has(this.filePath)) + return; // Already polled + await this.onWatcherEvent("change" /* CHANGE */, this.filePath, isInitial); + } + else { // Multiple initial paths + const depth = this.options.recursive && (constants_1.HAS_NATIVE_RECURSION && this.options.native !== false) ? (_a = this.options.depth) !== null && _a !== void 0 ? _a : constants_1.DEPTH : Math.min(1, (_b = this.options.depth) !== null && _b !== void 0 ? _b : constants_1.DEPTH), [directories, files] = await utils_1.default.fs.readdir(this.folderPath, this.options.ignore, depth, this.watcher._closeSignal, this.options.readdirMap), targetPaths = [this.folderPath, ...directories, ...files]; + await Promise.all(targetPaths.map(targetPath => { + if (this.watcher._poller.stats.has(targetPath)) + return; // Already polled + if (this.watcher.isIgnored(targetPath, this.options.ignore)) + return; + return this.onWatcherEvent("change" /* CHANGE */, targetPath, isInitial); + })); + } + } +} +/* EXPORT */ +exports.default = WatcherHandler; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlcl9oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dhdGNoZXJfaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQ0EsWUFBWTs7Ozs7QUFFWixnREFBd0I7QUFDeEIsMkNBQThFO0FBRTlFLG9EQUE0QjtBQUk1QixxQkFBcUI7QUFFckIsTUFBTSxjQUFjO0lBWWxCLGlCQUFpQjtJQUVqQixZQUFjLE9BQWdCLEVBQUUsTUFBcUIsRUFBRSxJQUFxQjtRQUUxRSxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUM7UUFDOUIsSUFBSSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztRQUM5QixJQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUM7UUFDcEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDO1FBRWhDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBRyxJQUFJLENBQUMsSUFBSSxDQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBRSxDQUFDLENBQUMsTUFBTTtJQUUvSSxDQUFDO0lBRUQsYUFBYTtJQUViLFVBQVUsQ0FBRyxVQUFnQjtRQUUzQixJQUFLLElBQUksQ0FBQyxRQUFRLEVBQUc7WUFFbkIsT0FBTyxVQUFVLEtBQUssSUFBSSxDQUFDLFFBQVEsQ0FBQztTQUVyQzthQUFNO1lBRUwsT0FBTyxVQUFV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 \ No newline at end of file diff --git a/server/libs/watcher/watcher_locker.js b/server/libs/watcher/watcher_locker.js new file mode 100644 index 00000000..c5377204 --- /dev/null +++ b/server/libs/watcher/watcher_locker.js @@ -0,0 +1,136 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("./constants"); +const watcher_locks_resolver_1 = __importDefault(require("./watcher_locks_resolver")); +/* WATCHER LOCKER */ +//TODO: Use a better name for this thing, maybe "RenameDetector" +class WatcherLocker { + /* CONSTRUCTOR */ + constructor(watcher) { + this._watcher = watcher; + this.reset(); + } + /* API */ + getLockAdd(config, timeout = constants_1.RENAME_TIMEOUT) { + const { ino, targetPath, events, locks } = config; + const emit = () => { + this._watcher.event(events.add, targetPath); + }; + if (!ino) + return emit(); + const cleanup = () => { + locks.add.delete(ino); + watcher_locks_resolver_1.default.remove(free); + }; + const free = () => { + cleanup(); + emit(); + }; + watcher_locks_resolver_1.default.add(free, timeout); + const resolve = () => { + const unlink = locks.unlink.get(ino); + if (!unlink) + return; // No matching "unlink" lock found, skipping + cleanup(); + const targetPathPrev = unlink(); + if (targetPath === targetPathPrev) { + if (events.change) { + if (this._watcher._poller.stats.has(targetPath)) { + this._watcher.event(events.change, targetPath); + } + } + } + else { + this._watcher.event(events.rename, targetPathPrev, targetPath); + } + }; + locks.add.set(ino, resolve); + resolve(); + } + getLockUnlink(config, timeout = constants_1.RENAME_TIMEOUT) { + var _a; + const { ino, targetPath, events, locks } = config; + const emit = () => { + this._watcher.event(events.unlink, targetPath); + }; + if (!ino) + return emit(); + const cleanup = () => { + locks.unlink.delete(ino); + watcher_locks_resolver_1.default.remove(free); + }; + const free = () => { + cleanup(); + emit(); + }; + watcher_locks_resolver_1.default.add(free, timeout); + const overridden = () => { + cleanup(); + return targetPath; + }; + locks.unlink.set(ino, overridden); + (_a = locks.add.get(ino)) === null || _a === void 0 ? void 0 : _a(); + } + getLockTargetAdd(targetPath, timeout) { + const ino = this._watcher._poller.getIno(targetPath, "add" /* ADD */, 2 /* FILE */); + return this.getLockAdd({ + ino, + targetPath, + events: WatcherLocker.FILE_EVENTS, + locks: this._locksFile + }, timeout); + } + getLockTargetAddDir(targetPath, timeout) { + const ino = this._watcher._poller.getIno(targetPath, "addDir" /* ADD_DIR */, 1 /* DIR */); + return this.getLockAdd({ + ino, + targetPath, + events: WatcherLocker.DIR_EVENTS, + locks: this._locksDir + }, timeout); + } + getLockTargetUnlink(targetPath, timeout) { + const ino = this._watcher._poller.getIno(targetPath, "unlink" /* UNLINK */, 2 /* FILE */); + return this.getLockUnlink({ + ino, + targetPath, + events: WatcherLocker.FILE_EVENTS, + locks: this._locksFile + }, timeout); + } + getLockTargetUnlinkDir(targetPath, timeout) { + const ino = this._watcher._poller.getIno(targetPath, "unlinkDir" /* UNLINK_DIR */, 1 /* DIR */); + return this.getLockUnlink({ + ino, + targetPath, + events: WatcherLocker.DIR_EVENTS, + locks: this._locksDir + }, timeout); + } + reset() { + this._locksAdd = new Map(); + this._locksAddDir = new Map(); + this._locksUnlink = new Map(); + this._locksUnlinkDir = new Map(); + this._locksDir = { add: this._locksAddDir, unlink: this._locksUnlinkDir }; + this._locksFile = { add: this._locksAdd, unlink: this._locksUnlink }; + } +} +WatcherLocker.DIR_EVENTS = { + add: "addDir" /* ADD_DIR */, + rename: "renameDir" /* RENAME_DIR */, + unlink: "unlinkDir" /* UNLINK_DIR */ +}; +WatcherLocker.FILE_EVENTS = { + add: "add" /* ADD */, + change: "change" /* CHANGE */, + rename: "rename" /* RENAME */, + unlink: "unlink" /* UNLINK */ +}; +/* EXPORT */ +exports.default = WatcherLocker; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlcl9sb2NrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2F0Y2hlcl9sb2NrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLFlBQVk7Ozs7O0FBRVosMkNBQTJDO0FBRzNDLHNGQUE0RDtBQUc1RCxvQkFBb0I7QUFFcEIsZ0VBQWdFO0FBRWhFLE1BQU0sYUFBYTtJQXlCakIsaUJBQWlCO0lBRWpCLFlBQWMsT0FBZ0I7UUFFNUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUM7UUFFeEIsSUFBSSxDQUFDLEtBQUssRUFBRyxDQUFDO0lBRWhCLENBQUM7SUFFRCxTQUFTO0lBRVQsVUFBVSxDQUFHLE1BQWtCLEVBQUUsVUFBa0IsMEJBQWM7UUFFL0QsTUFBTSxFQUFDLEdBQUcsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBQyxHQUFHLE1BQU0sQ0FBQztRQUVoRCxNQUFNLElBQUksR0FBRyxHQUFTLEVBQUU7WUFDdEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUcsTUFBTSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUUsQ0FBQztRQUNqRCxDQUFDLENBQUM7UUFFRixJQUFLLENBQUMsR0FBRztZQUFHLE9BQU8sSUFBSSxFQUFHLENBQUM7UUFFM0IsTUFBTSxPQUFPLEdBQUcsR0FBUyxFQUFFO1lBQ3pCLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFHLEdBQUcsQ0FBRSxDQUFDO1lBQ3pCLGdDQUFvQixDQUFDLE1BQU0sQ0FBRyxJQUFJLENBQUUsQ0FBQztRQUN2QyxDQUFDLENBQUM7UUFFRixNQUFNLElBQUksR0FBRyxHQUFTLEVBQUU7WUFDdEIsT0FBTyxFQUFHLENBQUM7WUFDWCxJQUFJLEVBQUcsQ0FBQztRQUNWLENBQUMsQ0FBQztRQUVGLGdDQUFvQixDQUFDLEdBQUcsQ0FBRyxJQUFJLEVBQUUsT0FBTyxDQUFFLENBQUM7UUFFM0MsTUFBTSxPQUFPLEdBQUcsR0FBUyxFQUFF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 \ No newline at end of file diff --git a/server/libs/watcher/watcher_locks_resolver.js b/server/libs/watcher/watcher_locks_resolver.js new file mode 100644 index 00000000..2e23c7b6 --- /dev/null +++ b/server/libs/watcher/watcher_locks_resolver.js @@ -0,0 +1,45 @@ +"use strict"; +/* WATCHER LOCKS RESOLVER */ +Object.defineProperty(exports, "__esModule", { value: true }); +// Registering a single interval scales much better than registering N timeouts +// Timeouts are respected within the interval margin +const WatcherLocksResolver = { + /* VARIABLES */ + interval: 100, + intervalId: undefined, + fns: new Map(), + /* LIFECYCLE */ + init: () => { + if (WatcherLocksResolver.intervalId) + return; + WatcherLocksResolver.intervalId = setInterval(WatcherLocksResolver.resolve, WatcherLocksResolver.interval); + }, + reset: () => { + if (!WatcherLocksResolver.intervalId) + return; + clearInterval(WatcherLocksResolver.intervalId); + delete WatcherLocksResolver.intervalId; + }, + /* API */ + add: (fn, timeout) => { + WatcherLocksResolver.fns.set(fn, Date.now() + timeout); + WatcherLocksResolver.init(); + }, + remove: (fn) => { + WatcherLocksResolver.fns.delete(fn); + }, + resolve: () => { + if (!WatcherLocksResolver.fns.size) + return WatcherLocksResolver.reset(); + const now = Date.now(); + for (const [fn, timestamp] of WatcherLocksResolver.fns) { + if (timestamp >= now) + continue; // We should still wait some more for this + WatcherLocksResolver.remove(fn); + fn(); + } + } +}; +/* EXPORT */ +exports.default = WatcherLocksResolver; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlcl9sb2Nrc19yZXNvbHZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy93YXRjaGVyX2xvY2tzX3Jlc29sdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSw0QkFBNEI7O0FBRTVCLCtFQUErRTtBQUMvRSxvREFBb0Q7QUFFcEQsTUFBTSxvQkFBb0IsR0FBRztJQUUzQixlQUFlO0lBRWYsUUFBUSxFQUFFLEdBQUc7SUFDYixVQUFVLEVBQUUsU0FBdUM7SUFDbkQsR0FBRyxFQUFFLElBQUksR0FBRyxFQUFxQjtJQUVqQyxlQUFlO0lBRWYsSUFBSSxFQUFFLEdBQVMsRUFBRTtRQUVmLElBQUssb0JBQW9CLENBQUMsVUFBVTtZQUFHLE9BQU87UUFFOUMsb0JBQW9CLENBQUMsVUFBVSxHQUFHLFdBQVcsQ0FBRyxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsb0JBQW9CLENBQUMsUUFBUSxDQUFFLENBQUM7SUFFaEgsQ0FBQztJQUVELEtBQUssRUFBRSxHQUFTLEVBQUU7UUFFaEIsSUFBSyxDQUFDLG9CQUFvQixDQUFDLFVBQVU7WUFBRyxPQUFPO1FBRS9DLGFBQWEsQ0FBRyxvQkFBb0IsQ0FBQyxVQUFVLENBQUUsQ0FBQztRQUVsRCxPQUFPLG9CQUFvQixDQUFDLFVBQVUsQ0FBQztJQUV6QyxDQUFDO0lBRUQsU0FBUztJQUVULEdBQUcsRUFBRSxDQUFFLEVBQVksRUFBRSxPQUFlLEVBQVMsRUFBRTtRQUU3QyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFHLEVBQUUsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFHLEdBQUcsT0FBTyxDQUFFLENBQUM7UUFFM0Qsb0JBQW9CLENBQUMsSUFBSSxFQUFHLENBQUM7SUFFL0IsQ0FBQztJQUVELE1BQU0sRUFBRSxDQUFFLEVBQVksRUFBUyxFQUFFO1FBRS9CLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUcsRUFBRSxDQUFFLENBQUM7SUFFekMsQ0FBQztJQUVELE9BQU8sRUFBRSxHQUFTLEVBQUU7UUFFbEIsSUFBSyxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxJQUFJO1lBQUcsT0FBTyxvQkFBb0IsQ0FBQyxLQUFLLEVBQUcsQ0FBQztRQUUzRSxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFHLENBQUM7UUFFeEIsS0FBTSxNQUFNLENBQUMsRUFBRSxFQUFFLFNBQVMsQ0FBQyxJQUFJLG9CQUFvQixDQUFDLEdBQUcsRUFBRztZQUV4RCxJQUFLLFNBQVMsSUFBSSxHQUFHO2dCQUFHLFNBQVMsQ0FBQywwQ0FBMEM7WUFFNUUsb0JBQW9CLENBQUMsTUFBTSxDQUFHLEVBQUUsQ0FBRSxDQUFDO1lBRW5DLEVBQUUsRUFBRyxDQUFDO1NBRVA7SUFFSCxDQUFDO0NBRUYsQ0FBQztBQUVGLFlBQVk7QUFFWixrQkFBZSxvQkFBb0IsQ0FBQyJ9 \ No newline at end of file diff --git a/server/libs/watcher/watcher_poller.js b/server/libs/watcher/watcher_poller.js new file mode 100644 index 00000000..79c2312e --- /dev/null +++ b/server/libs/watcher/watcher_poller.js @@ -0,0 +1,113 @@ +"use strict"; +/* IMPORT */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = __importDefault(require("./utils")); +const watcher_stats_1 = __importDefault(require("./watcher_stats")); +/* WATCHER POLLER */ +class WatcherPoller { + constructor() { + /* VARIABLES */ + this.inos = {}; + this.stats = new Map(); + } + /* API */ + getIno(targetPath, event, type) { + const inos = this.inos[event]; + if (!inos) + return; + const ino = inos[targetPath]; + if (!ino) + return; + if (type && ino[1] !== type) + return; + return ino[0]; + } + getStats(targetPath) { + return this.stats.get(targetPath); + } + async poll(targetPath, timeout) { + const stats = await utils_1.default.fs.poll(targetPath, timeout); + if (!stats) + return; + const isSupported = stats.isFile() || stats.isDirectory(); + if (!isSupported) + return; + return new watcher_stats_1.default(stats); + } + reset() { + this.inos = {}; + this.stats = new Map(); + } + async update(targetPath, timeout) { + const prev = this.getStats(targetPath), next = await this.poll(targetPath, timeout); + this.updateStats(targetPath, next); + if (!prev && next) { + if (next.isFile()) { + this.updateIno(targetPath, "add" /* ADD */, next); + return ["add" /* ADD */]; + } + if (next.isDirectory()) { + this.updateIno(targetPath, "addDir" /* ADD_DIR */, next); + return ["addDir" /* ADD_DIR */]; + } + } + else if (prev && !next) { + if (prev.isFile()) { + this.updateIno(targetPath, "unlink" /* UNLINK */, prev); + return ["unlink" /* UNLINK */]; + } + if (prev.isDirectory()) { + this.updateIno(targetPath, "unlinkDir" /* UNLINK_DIR */, prev); + return ["unlinkDir" /* UNLINK_DIR */]; + } + } + else if (prev && next) { + if (prev.isFile()) { + if (next.isFile()) { + if (prev.ino === next.ino && !prev.size && !next.size) + return []; // Same path, same content and same file, nothing actually changed + this.updateIno(targetPath, "change" /* CHANGE */, next); + return ["change" /* CHANGE */]; + } + if (next.isDirectory()) { + this.updateIno(targetPath, "unlink" /* UNLINK */, prev); + this.updateIno(targetPath, "addDir" /* ADD_DIR */, next); + return ["unlink" /* UNLINK */, "addDir" /* ADD_DIR */]; + } + } + else if (prev.isDirectory()) { + if (next.isFile()) { + this.updateIno(targetPath, "unlinkDir" /* UNLINK_DIR */, prev); + this.updateIno(targetPath, "add" /* ADD */, next); + return ["unlinkDir" /* UNLINK_DIR */, "add" /* ADD */]; + } + if (next.isDirectory()) { + if (prev.ino === next.ino) + return []; // Same path and same directory, nothing actually changed + this.updateIno(targetPath, "unlinkDir" /* UNLINK_DIR */, prev); + this.updateIno(targetPath, "addDir" /* ADD_DIR */, next); + return ["unlinkDir" /* UNLINK_DIR */, "addDir" /* ADD_DIR */]; + } + } + } + return []; + } + updateIno(targetPath, event, stats) { + const inos = this.inos[event] = this.inos[event] || (this.inos[event] = {}), type = stats.isFile() ? 2 /* FILE */ : 1 /* DIR */; + inos[targetPath] = [stats.ino, type]; + } + updateStats(targetPath, stats) { + if (stats) { + this.stats.set(targetPath, stats); + } + else { + this.stats.delete(targetPath); + } + } +} +/* EXPORT */ +exports.default = WatcherPoller; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlcl9wb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2F0Y2hlcl9wb2xsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLFlBQVk7Ozs7O0FBR1osb0RBQTRCO0FBQzVCLG9FQUEyQztBQUczQyxvQkFBb0I7QUFFcEIsTUFBTSxhQUFhO0lBQW5CO1FBRUUsZUFBZTtRQUVmLFNBQUksR0FBZ0UsRUFBRSxDQUFDO1FBQ3ZFLFVBQUssR0FBNEIsSUFBSSxHQUFHLEVBQUcsQ0FBQztJQXNLOUMsQ0FBQztJQXBLQyxTQUFTO0lBRVQsTUFBTSxDQUFHLFVBQWdCLEVBQUUsS0FBa0IsRUFBRSxJQUFlO1FBRTVELE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFOUIsSUFBSyxDQUFDLElBQUk7WUFBRyxPQUFPO1FBRXBCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUU3QixJQUFLLENBQUMsR0FBRztZQUFHLE9BQU87UUFFbkIsSUFBSyxJQUFJLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUk7WUFBRyxPQUFPO1FBRXRDLE9BQU8sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRWhCLENBQUM7SUFFRCxRQUFRLENBQUcsVUFBZ0I7UUFFekIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBRyxVQUFVLENBQUUsQ0FBQztJQUV2QyxDQUFDO0lBRUQsS0FBSyxDQUFDLElBQUksQ0FBRyxVQUFnQixFQUFFLE9BQWdCO1FBRTdDLE1BQU0sS0FBSyxHQUFHLE1BQU0sZUFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUcsVUFBVSxFQUFFLE9BQU8sQ0FBRSxDQUFDO1FBRTFELElBQUssQ0FBQyxLQUFLO1lBQUcsT0FBTztRQUVyQixNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFHLElBQUksS0FBSyxDQUFD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 \ No newline at end of file diff --git a/server/libs/watcher/watcher_stats.js b/server/libs/watcher/watcher_stats.js new file mode 100644 index 00000000..b15e9165 --- /dev/null +++ b/server/libs/watcher/watcher_stats.js @@ -0,0 +1,32 @@ +"use strict"; +/* IMPORT */ +Object.defineProperty(exports, "__esModule", { value: true }); +/* WATCHER STATS */ +// An even more memory-efficient representation of the useful subset of stats objects +class WatcherStats { + /* CONSTRUCTOR */ + constructor(stats) { + this.ino = stats.ino; + this.size = stats.size; + this.atimeMs = stats.atimeMs; + this.mtimeMs = stats.mtimeMs; + this.ctimeMs = stats.ctimeMs; + this.birthtimeMs = stats.birthtimeMs; + this._isFile = stats.isFile(); + this._isDirectory = stats.isDirectory(); + this._isSymbolicLink = stats.isSymbolicLink(); + } + /* API */ + isFile() { + return this._isFile; + } + isDirectory() { + return this._isDirectory; + } + isSymbolicLink() { + return this._isSymbolicLink; + } +} +/* EXPORT */ +exports.default = WatcherStats; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlcl9zdGF0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy93YXRjaGVyX3N0YXRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxZQUFZOztBQUlaLG1CQUFtQjtBQUVuQixxRkFBcUY7QUFFckYsTUFBTSxZQUFZO0lBY2hCLGlCQUFpQjtJQUVqQixZQUFjLEtBQVk7UUFFeEIsSUFBSSxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQztRQUN2QixJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUM7UUFDN0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDO1FBQzdCLElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQztRQUM3QixJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQyxXQUFXLENBQUM7UUFDckMsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFHLENBQUM7UUFDL0IsSUFBSSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUMsV0FBVyxFQUFHLENBQUM7UUFDekMsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUMsY0FBYyxFQUFHLENBQUM7SUFFakQsQ0FBQztJQUVELFNBQVM7SUFFVCxNQUFNO1FBRUosT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBRXRCLENBQUM7SUFFRCxXQUFXO1FBRVQsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO0lBRTNCLENBQUM7SUFFRCxjQUFjO1FBRVosT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDO0lBRTlCLENBQUM7Q0FFRjtBQUVELFlBQVk7QUFFWixrQkFBZSxZQUFZLENBQUMifQ== \ No newline at end of file