diff --git a/.eslintrc.json b/.eslintrc.json index 99f173d..5b53dbd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -29,15 +29,10 @@ "mocha": true }, "rules": { - "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-explicit-any": "error", "quote-props": ["error", "consistent"], - "mocha/no-skipped-tests": "error", + "mocha/no-skipped-tests": "warn", "mocha/no-exclusive-tests": "error", - "node/no-unpublished-import": [ - "error", { - "allowModules": ["slow-stream"] - } - ], "node/no-unsupported-features/es-syntax": [ "error", { @@ -45,10 +40,13 @@ "ignores": ["modules"] } ], + "node/no-unpublished-import": [ + "error", { + "allowModules": ["slow-stream"] + } + ], "node/no-missing-import": [ "error", { - "allowModules": [], - "resolvePaths": ["/path/to/a/modules/directory"], "tryExtensions": [".js", ".ts"] } ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 500e2cd..5f1b8ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 21.x cache: npm - run: npm ci - run: npm test @@ -24,7 +24,7 @@ jobs: needs: test strategy: matrix: - node-version: [ 10.x, 12.x, 14.x, 15.x, 16.x, 17.x ] + node-version: [ 10.x, 11.x, 12.x, 13.x, 14.x, 15.x, 16.x, 17.x, 18.x, 19.x, 20.x ] steps: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index eef210f..964c6a5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ .idea/ build/ dist/ +.DS_Store diff --git a/.mocharc.json b/.mocharc.json index 8be2655..6c9ad80 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -1,3 +1,10 @@ { - "extension": ["ts"] + "timeout": 5000, + "exit": true, + "extension": ["ts", "js"], + "recursive": true, + "node-option": [ + "experimental-specifier-resolution=node", + "import=tsx" + ] } diff --git a/.tools/build.sh b/.tools/build.sh index 14feb9c..ec9f647 100644 --- a/.tools/build.sh +++ b/.tools/build.sh @@ -11,7 +11,7 @@ mkdir -p "${DIST_DIR}" "${BUILD_DIR}" cd "${PROJECT_DIR}" echo "Compiling oleoduc (esm version)..." -tsc -p tsconfig.json +npm run tsc -- -p tsconfig.json cat >"${DIST_DIR}/mjs/package.json" <"${DIST_DIR}/mjs/package.json" <"${DIST_DIR}/cjs/package.json" <"${DIST_DIR}/cjs/package.json" <"${BUILD_DIR}/.mocharc.json" <= 10" @@ -521,26 +523,388 @@ "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -599,6 +963,17 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.24.4", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.24.4.tgz", + "integrity": "sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==", + "dev": true, + "dependencies": { + "@shikijs/engine-oniguruma": "^1.24.2", + "@shikijs/types": "^1.24.2", + "@shikijs/vscode-textmate": "^9.3.1" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -843,29 +1218,40 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.24.4", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.4.tgz", + "integrity": "sha512-Do2ry6flp2HWdvpj2XOwwa0ljZBRy15HKZITzPcNIBOGSeprnA8gOooA/bLsSPuy8aJBa+Q/r34dMmC3KNL/zw==", + "dev": true, + "dependencies": { + "@shikijs/types": "1.24.4", + "@shikijs/vscode-textmate": "^9.3.1" + } }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "node_modules/@shikijs/types": { + "version": "1.24.4", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.4.tgz", + "integrity": "sha512-0r0XU7Eaow0PuDxuWC1bVqmWCgm3XqizIaT7SM42K03vc69LGooT0U8ccSR44xP/hGlNx4FKhtYpV+BU6aaKAA==", + "dev": true, + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.1", + "@types/hast": "^3.0.4" + } }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.1.tgz", + "integrity": "sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==", "dev": true }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -894,12 +1280,27 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parallel-transform": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/parallel-transform/-/parallel-transform-1.1.4.tgz", + "integrity": "sha512-Dlw8uStux7ZL3FuKgw3h+zeBPv1gmJySEvyCuACjKzr6Aco43hW4LHHkmwsL6w8Q9GldlhdJwyRsFxyz/uwlqA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.13.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", @@ -1116,15 +1517,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", - "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -1154,16 +1546,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1188,20 +1570,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "peer": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -1220,12 +1588,6 @@ "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1349,15 +1711,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -1398,16 +1751,6 @@ "node": ">=0.6" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -1442,13 +1785,6 @@ "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "peer": true - }, "node_modules/browserslist": { "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", @@ -1481,12 +1817,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -1585,47 +1915,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "peer": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -1635,18 +1924,6 @@ "node": ">=6" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1688,12 +1965,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1837,16 +2108,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1896,6 +2157,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -1995,6 +2268,45 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2636,16 +2948,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "peer": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -2724,7 +3026,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -2836,9 +3137,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -3059,16 +3360,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "peer": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3198,19 +3489,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "peer": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -3405,16 +3683,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3506,19 +3774,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3778,6 +4033,15 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3803,24 +4067,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "dev": true }, "node_modules/lru-cache": { "version": "5.1.1", @@ -3831,6 +4078,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3855,10 +4108,27 @@ "semver": "bin/semver.js" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, "node_modules/merge-stream": { @@ -3922,124 +4192,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "peer": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "peer": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4064,16 +4222,6 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -4700,6 +4848,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4726,16 +4883,6 @@ "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", "dev": true }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -4750,19 +4897,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "peer": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -5070,16 +5204,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -5181,16 +5305,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -5440,119 +5554,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", - "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", - "dev": true, - "dependencies": { - "ts-node": "7.0.1" - }, - "bin": { - "ts-mocha": "bin/ts-mocha" - }, - "engines": { - "node": ">= 6.X.X" - }, - "optionalDependencies": { - "tsconfig-paths": "^3.5.0" - }, - "peerDependencies": { - "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" - } - }, - "node_modules/ts-mocha/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ts-mocha/node_modules/ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", - "dev": true, - "dependencies": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ts-mocha/node_modules/yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -5592,6 +5593,25 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5690,10 +5710,68 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typedoc": { + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.6.tgz", + "integrity": "sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==", + "dev": true, + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.4.0.tgz", + "integrity": "sha512-4207DYcxJGWQRrEVTza7XkIo7ldhuEzJBaZO6dX5ogUGlvWeRTo4uiN+R1F11ttJGh4toLtxrpoi2wTTP+nEwA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.27.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5703,6 +5781,12 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -5799,12 +5883,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5861,31 +5939,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true, - "peer": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -5904,100 +5957,22 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "peer": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "peer": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" + "bin": { + "yaml": "bin.mjs" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, "engines": { - "node": ">=6" + "node": ">= 14" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index 67f9f3a..9f05c36 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,12 @@ "scripts": { "build": "bash .tools/build.sh", "hooks": "bash .tools/hooks.sh", - "test": "TS_NODE_PROJECT=tsconfig.test.json ts-mocha test/", + "test": "mocha test/", "coverage": "nyc --temp-dir .coverage/.nyc_output --report-dir .coverage --reporter=lcov --reporter=html npm test", "lint": "eslint src/ test/", "release": "bash .tools/release.sh master", - "ts": "tsc -p tsconfig.test.json --showConfig" + "tsc": "tsc", + "doc": "npx typedoc --out docs --plugin typedoc-plugin-markdown src/accumulateData.ts" }, "repository": { "type": "git", @@ -47,6 +48,7 @@ "devDependencies": { "@types/mocha": "10.0.6", "@types/node": "20.10.4", + "@types/parallel-transform": "1.1.4", "@typescript-eslint/eslint-plugin": "6.13.2", "@typescript-eslint/parser": "6.13.2", "assert": "2.1.0", @@ -60,9 +62,10 @@ "nyc": "15.1.0", "prettier": "3.1.0", "slow-stream": "0.0.4", - "ts-mocha": "10.0.0", - "ts-node": "10.9.2", - "typescript": "5.3.3" + "tsx": "4.19.2", + "typedoc": "0.27.6", + "typedoc-plugin-markdown": "4.4.0", + "typescript": "5.7.2" }, "prettier": { "printWidth": 120 diff --git a/src/@types/cyclist.d.ts b/src/@types/cyclist.d.ts new file mode 100644 index 0000000..44c950c --- /dev/null +++ b/src/@types/cyclist.d.ts @@ -0,0 +1,13 @@ +declare module "cyclist" { + function Cyclist(size: number): Cyclist; + + export class Cyclist { + constructor(size: number); + + put(index: number, val: unknown): number; + get(index: number): unknown; + del(index: number): unknown; + } + + export = Cyclist; +} diff --git a/src/accumulateData.ts b/src/accumulateData.ts index 3784e63..82ed162 100644 --- a/src/accumulateData.ts +++ b/src/accumulateData.ts @@ -1,12 +1,18 @@ import { Transform, TransformOptions } from "stream"; -type AccumulateDateOptions = { - accumulator?: unknown; -} & TransformOptions; +export type AccumulateDataOptions = TransformOptions & { accumulator?: TAcc }; +export type AccumulateDataCallback = ( + acc: TAcc, + data: TInput, + flush: (data: TOutput) => void, +) => TAcc; -export function accumulateData(accumulate, options: AccumulateDateOptions = {}) { +export function accumulateData( + accumulate: AccumulateDataCallback, + options: AccumulateDataOptions = {}, +): NodeJS.ReadWriteStream { const { accumulator, ...rest } = options; - let acc = accumulator === undefined ? null : accumulator; + let acc = (accumulator === undefined ? null : accumulator) as TAcc; let flushed = false; return new Transform({ @@ -15,16 +21,14 @@ export function accumulateData(accumulate, options: AccumulateDateOptions = {}) transform: async function (chunk, encoding, callback) { try { flushed = false; - const flush = (res) => { + acc = await accumulate(acc, chunk, (data: TOutput) => { flushed = true; - this.push(res); - }; - - acc = await accumulate(acc, chunk, flush); + this.push(data); + }); callback(); } catch (e) { - callback(e); + callback(e as Error); } }, flush(callback) { diff --git a/src/compose.ts b/src/compose.ts index e1068c3..b196278 100644 --- a/src/compose.ts +++ b/src/compose.ts @@ -1,7 +1,35 @@ -import { oleoduc } from "./oleoduc"; import { parseArgs } from "./utils/parseArgs"; +import { wrapStreams } from "./utils/wrapStreams"; +import { isReadableStream } from "./utils/isReadableStream"; +import { decorateWithAsyncIterator } from "./utils/decorateWithAsyncIterator"; +import { pipeStreamsTogether } from "./utils/pipeStreamsTogether"; +import { AnyStream, PipeableStreams } from "./types"; +import { Readable, TransformOptions } from "stream"; -export function compose(...args) { - const { streams, options } = parseArgs(args, { promisify: false }); - return oleoduc(streams, options); +export type ComposeOptions = TransformOptions; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type ComposeReturn = TLast extends Readable + ? NodeJS.ReadWriteStream & Readable + : NodeJS.ReadWriteStream; + +export function compose( + ...args: PipeableStreams +): ComposeReturn { + const { params: streams, options } = parseArgs(args); + + const { first, last, wrapper } = wrapStreams(streams, options); + + if (isReadableStream(last)) { + decorateWithAsyncIterator(wrapper as Readable); + } + + if (wrapper !== last && wrapper !== first) { + last.on("finish", () => wrapper.emit("finish")); // Needed by nodejs 12 and previous + last.on("close", () => wrapper.emit("close")); + } + + pipeStreamsTogether(streams, wrapper); + + return wrapper as ComposeReturn; } diff --git a/src/concatStreams.ts b/src/concatStreams.ts index f42f749..494fee5 100644 --- a/src/concatStreams.ts +++ b/src/concatStreams.ts @@ -1,11 +1,13 @@ -import { PassThrough } from "stream"; +import { PassThrough, TransformOptions } from "stream"; import { parseArgs } from "./utils/parseArgs"; -class ArrayCursor { - private array: any; +type NextStreamCallback = () => Promise | NodeJS.ReadableStream | null; + +class StreamArrayCursor { + private array: Array; private cpt: number; - constructor(array) { + constructor(array: Array) { this.array = array; this.cpt = 0; } @@ -15,13 +17,15 @@ class ArrayCursor { } } -function isFunction(streams) { - return streams.length === 1 && typeof streams[0] === "function"; -} - -export function concatStreams(...args) { - const { streams, options } = parseArgs(args); - const cursor = isFunction(streams) ? { next: streams[0] } : new ArrayCursor(streams); +export function concatStreams( + ...args: + | [...streams: NodeJS.ReadableStream[]] + | [...streams: NodeJS.ReadableStream[], options: TransformOptions] + | [next: NextStreamCallback] + | [next: NextStreamCallback, options: TransformOptions] +): NodeJS.ReadWriteStream { + const { params, options } = parseArgs(args); + const cursor = asCursor(params); const passThrough = new PassThrough({ objectMode: true, ...options }); passThrough.setMaxListeners(0); @@ -31,7 +35,7 @@ export function concatStreams(...args) { return passThrough.end(); } - stream.on("error", (e) => passThrough.emit("error", e)); + stream.on("error", (e: Error) => passThrough.emit("error", e)); stream.on("end", () => concat()); stream.pipe(passThrough, { end: false }); } @@ -40,3 +44,15 @@ export function concatStreams(...args) { return passThrough; } + +function isFunction(streams: (NodeJS.ReadableStream | NextStreamCallback)[]) { + return streams.length === 1 && typeof streams[0] === "function"; +} + +function asCursor(value: (NodeJS.ReadableStream | NextStreamCallback)[]) { + if (isFunction(value)) { + const callbacks = value as NextStreamCallback[]; + return { next: callbacks[0] }; + } + return new StreamArrayCursor(value as NodeJS.ReadableStream[]); +} diff --git a/src/filterData.ts b/src/filterData.ts index d8984b2..46c4d41 100644 --- a/src/filterData.ts +++ b/src/filterData.ts @@ -1,5 +1,9 @@ -import { transformData } from "./transformData"; +import { TransformDataCallback, transformData, TransformDataOptions } from "./transformData"; +import { Transform } from "node:stream"; -export function filterData(filter, options = {}) { +export function filterData( + filter: TransformDataCallback, + options: TransformDataOptions = {}, +): Transform { return transformData((data) => data, { ...options, filter }); } diff --git a/src/flattenArray.ts b/src/flattenArray.ts index 17480ba..85d7f92 100644 --- a/src/flattenArray.ts +++ b/src/flattenArray.ts @@ -1,16 +1,17 @@ -import { Transform } from "stream"; +import { Transform, TransformCallback, TransformOptions } from "stream"; /** * Inspired by https://stackoverflow.com/a/43811543/122975 */ -class TransformArray extends Transform { - private _resumeTransform: any; +class TransformArray extends Transform { + private _resumeTransform: (() => void) | null; - constructor(options) { + constructor(options?: TransformOptions) { super(options); this._resumeTransform = null; } - _transform(chunk, encoding, callback) { + + _transform(chunk: Array | T, encoding: BufferEncoding, callback: TransformCallback) { const array = Array.isArray(chunk) ? chunk : [chunk]; let index = 0; const pushArrayItems = () => { @@ -31,7 +32,7 @@ class TransformArray extends Transform { //Start pushing array items pushArrayItems(); } - _read(size) { + _read(size: number) { if (this._resumeTransform !== null) { //Ensure every items from the previous transform has been pushed this._resumeTransform(); @@ -40,6 +41,6 @@ class TransformArray extends Transform { } } -export function flattenArray(options = {}) { +export function flattenArray(options: TransformOptions = {}): Transform { return new TransformArray({ objectMode: true, ...options }); } diff --git a/src/groupData.ts b/src/groupData.ts index bec5ecf..456c4b5 100644 --- a/src/groupData.ts +++ b/src/groupData.ts @@ -1,31 +1,19 @@ -import { compose } from "./compose"; -import { accumulateData } from "./accumulateData"; +import { accumulateData, AccumulateDataOptions } from "./accumulateData"; -function parseOptionalArgs(...args) { - const options = { - accumulator: [], - ...(typeof args[args.length - 1] === "object" ? args.pop() : {}), - }; - const size = options.size || 1; +export type GroupDataOptions = { size?: number } & AccumulateDataOptions>; - return { - shouldFlush: args.pop() || ((group) => group.length === size), - options, - }; -} - -export function groupData(...args) { - const { shouldFlush, options } = parseOptionalArgs(...args); - - return compose( - accumulateData((acc, data, flush) => { +export function groupData(options: GroupDataOptions = {}): NodeJS.ReadWriteStream { + return accumulateData, Array>( + (acc, data, flush) => { const group = [...acc, data]; - if (shouldFlush(group)) { + const groupSize = options.size || 1; + if (group.length === groupSize) { flush(group); return []; } else { return group; } - }, options), + }, + { ...options, accumulator: [] }, ); } diff --git a/src/index.ts b/src/index.ts index b936aa4..9914ecc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,33 +1,33 @@ -import { oleoduc } from "./oleoduc"; +import { accumulateData } from "./accumulateData"; import { compose } from "./compose"; -import { transformData } from "./transformData"; -import { transformIntoJSON } from "./transformIntoJSON"; -import { transformIntoCSV } from "./transformIntoCSV"; -import { transformStream } from "./transformStream"; +import { concatStreams } from "./concatStreams"; import { filterData } from "./filterData"; -import { accumulateData } from "./accumulateData"; -import { groupData } from "./groupData"; -import { readLineByLine } from "./readLineByLine"; import { flattenArray } from "./flattenArray"; +import { groupData } from "./groupData"; import { mergeStreams } from "./mergeStreams"; -import { concatStreams } from "./concatStreams"; +import { oleoduc } from "./oleoduc"; +import { readLineByLine } from "./readLineByLine"; +import { transformData } from "./transformData"; +import { transformIntoCSV } from "./transformIntoCSV"; +import { transformIntoJSON } from "./transformIntoJSON"; +import { transformStream } from "./transformStream"; import { writeData } from "./writeData"; import { writeToStdout } from "./writeToStdout"; export { - oleoduc, + accumulateData, compose, - transformData, - transformIntoJSON, - transformIntoCSV, - transformStream, + concatStreams, filterData, - accumulateData, - groupData, - readLineByLine, flattenArray, + groupData, mergeStreams, - concatStreams, + oleoduc, + readLineByLine, + transformData, + transformIntoCSV, + transformIntoJSON, + transformStream, writeData, writeToStdout, }; diff --git a/src/mergeStreams.ts b/src/mergeStreams.ts index 6b48cbf..170670b 100644 --- a/src/mergeStreams.ts +++ b/src/mergeStreams.ts @@ -1,9 +1,14 @@ -import { PassThrough } from "stream"; +import { PassThrough, Transform } from "stream"; import { decorateWithAsyncIterator } from "./utils/decorateWithAsyncIterator"; import { parseArgs } from "./utils/parseArgs"; +import { StreamOptions } from "node:stream"; -export function mergeStreams(...args) { - const { streams, options } = parseArgs(args); +export function mergeStreams( + ...args: + | [...streams: NodeJS.ReadableStream[]] + | [...streams: NodeJS.ReadableStream[], options: StreamOptions] +): NodeJS.ReadableStream { + const { params: streams, options } = parseArgs(args); const last = streams[streams.length - 1]; const passThrough = new PassThrough(options); passThrough.setMaxListeners(0); @@ -11,9 +16,9 @@ export function mergeStreams(...args) { streams.forEach((s) => pipeStream(s)); - async function pipeStream(obj) { + async function pipeStream(obj: unknown) { const stream = typeof obj === "function" ? await obj() : obj; - stream.on("error", (e) => passThrough.emit("error", e)); + stream.on("error", (e: Error) => passThrough.emit("error", e)); stream.once("end", () => { if (--cpt === 0) { passThrough.end(); diff --git a/src/oleoduc.ts b/src/oleoduc.ts index db0b4dd..db7112d 100644 --- a/src/oleoduc.ts +++ b/src/oleoduc.ts @@ -1,72 +1,26 @@ -import { PassThrough } from "stream"; -import { decorateWithPromise } from "./utils/decorateWithPromise"; -import { decorateWithAsyncIterator } from "./utils/decorateWithAsyncIterator"; import { parseArgs } from "./utils/parseArgs"; -import { Duplexer } from "./utils/Duplexer"; +import { AnyStream, PipeableStreams } from "./types"; +import { wrapStreams } from "./utils/wrapStreams"; +import { pipeStreamsTogether } from "./utils/pipeStreamsTogether"; +import { TransformOptions } from "stream"; -function pipeStreamsTogether(streams, wrapper) { - for (const [i, stream] of streams.entries()) { - const next = streams[i + 1]; - if (next) { - stream.pipe(next); - } +export type OleoducOptions = TransformOptions; - if (stream !== wrapper) { - stream.on("error", (err) => wrapper.emit("error", err)); - } - } -} - -function getWrapper(first, last, options = {}) { - let wrapper; - if (first === last) { - wrapper = first; - } else if (first.writable && last.readable) { - wrapper = new Duplexer(first, last, options); - } else if (first.writable) { - wrapper = first; - } else if (last.readable) { - wrapper = last; - } else { - wrapper = new PassThrough(options); - } - - return wrapper; -} - -export function oleoduc(...args) { - const { - streams, - options: { promisify, ...rest }, - } = parseArgs(args, { promisify: true }); +export function oleoduc( + ...args: PipeableStreams +): Promise { + const { params: streams, options } = parseArgs(args); if (streams.length === 0) { throw new Error("You must provide at least one stream"); } - const first = streams[0]; - const last = streams[streams.length - 1]; - const wrapper = getWrapper(first, last, rest); - - if (last.readable) { - decorateWithAsyncIterator(wrapper); - } - - if (promisify) { - decorateWithPromise( - wrapper, - new Promise((resolve, reject) => { - wrapper.on("error", (e) => reject(e)); - last.on("finish", resolve); // Needed by nodejs 12 and previous - last.on("close", resolve); - }), - ); - } else if (wrapper !== last && wrapper !== first) { - last.on("finish", () => wrapper.emit("finish")); // Needed by nodejs 12 and previous - last.on("close", () => wrapper.emit("close")); - } - + const { wrapper, last } = wrapStreams(streams, options); pipeStreamsTogether(streams, wrapper); - return wrapper; + return new Promise((resolve, reject) => { + wrapper.on("error", (e) => reject(e)); + last.on("finish", resolve); // Needed by nodejs 12 and previous + last.on("close", resolve); + }); } diff --git a/src/readLineByLine.ts b/src/readLineByLine.ts index ec9db40..3f4d5a6 100644 --- a/src/readLineByLine.ts +++ b/src/readLineByLine.ts @@ -4,10 +4,10 @@ import { compose } from "./compose"; export function readLineByLine() { return compose( - accumulateData( - (acc, data, flush) => { + accumulateData( + (acc, data: string, flush) => { const lines = data.toString().split(/\r?\n/); - const rest = lines.pop(); + const rest = lines.pop() || ""; if (lines.length > 0) { lines[0] = acc + lines[0]; diff --git a/src/transformData.ts b/src/transformData.ts index 9ee8776..c0b2f95 100644 --- a/src/transformData.ts +++ b/src/transformData.ts @@ -1,19 +1,30 @@ import transform from "parallel-transform"; +import { Transform, TransformOptions } from "node:stream"; +import { TransformCallback } from "stream"; -export function transformData(handleChunk, options: any = {}) { +export type TransformDataCallback = (data: TInput) => Promise | TOutput; +export type TransformDataOptions = { + filter?: TransformDataCallback; + parallel?: number; +} & TransformOptions; + +export function transformData( + callback: TransformDataCallback, + options: TransformDataOptions = {}, +): Transform { const { filter, parallel, ...rest } = options; - const filterChunk = (value) => !filter || filter(value); + const filterChunk = (value: TInput) => !filter || filter(value); const maxParallel = parallel || 1; - return transform(maxParallel, { objectMode: true, ...rest }, async (chunk, callback) => { + return transform(maxParallel, { objectMode: true, ...rest }, async (chunk: TInput, cb: TransformCallback) => { try { if (!(await filterChunk(chunk))) { - return callback(null, null); + return cb(null, null); } - const res = await handleChunk(chunk); - callback(null, res); + const res = await callback(chunk); + cb(null, res); } catch (e) { - callback(e); + cb(e as Error); } }); } diff --git a/src/transformIntoCSV.ts b/src/transformIntoCSV.ts index 7f87cad..81116cd 100644 --- a/src/transformIntoCSV.ts +++ b/src/transformIntoCSV.ts @@ -1,20 +1,32 @@ -import { Transform } from "stream"; +import { Transform, TransformOptions } from "stream"; -export function transformIntoCSV(options: any = {}) { +export type TransformIntoCSVOptions = TransformOptions & { + columns?: Record Promise | AllowedCSVType>; + bom?: boolean; + mapper?: ValueMapper; + separator?: string; +}; +type AllowedCSVType = string | number; +type ValueMapper = (value: AllowedCSVType) => AllowedCSVType; + +export function transformIntoCSV>( + options: TransformIntoCSVOptions = {}, +): Transform { const bom = options.bom; const separator = options.separator || ";"; - const mapper = options.mapper || ((v) => v); + const columns = options.columns; + const mapper: ValueMapper = options.mapper || ((v) => v); - function generateColumnNames(chunk, columns) { + function generateColumnNames(chunk: TInput) { return Object.keys(columns || chunk) .map(mapper) .join(separator); } - async function generateColumnValues(chunk, columns) { - let values; + async function generateColumnValues(chunk: TInput) { + let values: AllowedCSVType[]; if (!columns) { - values = Object.values(chunk); + values = Object.values(chunk) as AllowedCSVType[]; } else { const columnNames = Object.keys(columns); values = await Promise.all(columnNames.map((name) => columns[name](chunk))); @@ -26,21 +38,19 @@ export function transformIntoCSV(options: any = {}) { let lines = 0; return new Transform({ objectMode: true, - transform: async function (chunk, encoding, callback) { - const columns = options.columns; - + transform: async function (chunk: TInput, encoding, cb) { try { if (lines++ === 0) { if (bom) { this.push("\ufeff"); } - this.push(`${generateColumnNames(chunk, columns)}\n`); + this.push(`${generateColumnNames(chunk)}\n`); } - this.push(`${await generateColumnValues(chunk, columns)}\n`); - callback(); + this.push(`${await generateColumnValues(chunk)}\n`); + cb(); } catch (e) { - callback(e); + cb(e as Error); } }, }); diff --git a/src/transformIntoJSON.ts b/src/transformIntoJSON.ts index acd1ca2..de7fab6 100644 --- a/src/transformIntoJSON.ts +++ b/src/transformIntoJSON.ts @@ -1,18 +1,24 @@ -import { Transform } from "stream"; +import { Transform, TransformOptions } from "stream"; -export function transformIntoJSON(options: any = {}) { +export type TransformIntoJSONOptions = TransformOptions & { + arrayPropertyName?: string; + arrayWrapper?: Record; +}; +const DEFAULT_ARRAY_PROPERTY_NAME = "items"; + +export function transformIntoJSON(options: TransformIntoJSONOptions = {}): Transform { let chunksSent = 0; return new Transform({ readableObjectMode: false, writableObjectMode: true, - transform: function (chunk, encoding, callback) { + transform: function (chunk: TInput, encoding, callback) { const shouldWrap = options.arrayWrapper || options.arrayPropertyName; if (chunksSent === 0) { if (shouldWrap) { let value = JSON.stringify(options.arrayWrapper || {}); value = value.substring(0, value.length - 1); const comma = options.arrayWrapper ? "," : ""; - value += String(`${comma}"${options.arrayPropertyName}":[`); + value += String(`${comma}"${options.arrayPropertyName || DEFAULT_ARRAY_PROPERTY_NAME}":[`); this.push(value); } else { this.push("["); @@ -33,7 +39,7 @@ export function transformIntoJSON(options: any = {}) { //nothing sent if (shouldWrap) { const value = options.arrayWrapper || {}; - value[options.arrayPropertyName] = []; + value[options.arrayPropertyName || DEFAULT_ARRAY_PROPERTY_NAME] = []; this.push(JSON.stringify(value)); } else { this.push("[]"); diff --git a/src/transformStream.ts b/src/transformStream.ts index 3e16da5..72a2bd7 100644 --- a/src/transformStream.ts +++ b/src/transformStream.ts @@ -1,22 +1,24 @@ -import { Transform } from "stream"; +import { Transform, TransformCallback } from "stream"; +import { TransformOptions } from "node:stream"; -class TransformStream extends Transform { - private handleChunk: any; - private _current: any; +export type TransformStreamCallback = ( + data: TInput, +) => Promise | TOutput; - constructor(handleChunk, options) { +class TransformStream extends Transform { + private callback: TransformStreamCallback; + private _current: NodeJS.ReadableStream | null; + + constructor(callback: TransformStreamCallback, options?: TransformOptions) { super(options); - this.handleChunk = handleChunk; + this.callback = callback; this._current = null; } - _transform(chunk, encoding, callback) { - const res = Promise.resolve(this.handleChunk(chunk)); - const promise = res.then ? res : Promise.resolve(res); //FIXME - - promise + _transform(chunk: TInput, encoding: BufferEncoding, cb: TransformCallback) { + Promise.resolve(this.callback(chunk)) .then((stream) => { - stream.on("error", (err) => this.emit("error", err)); + stream.on("error", (err: Error) => this.emit("error", err)); stream.on("data", (data) => { this._current = stream; const backpressure = !this.push(data); @@ -26,13 +28,13 @@ class TransformStream extends Transform { }); stream.on("end", () => { this._current = null; - callback(); + cb(); }); }) - .catch(callback); + .catch(cb); } - async _read(size) { + async _read(size: number) { if (this._current) { //Ensure every chunks has been consumed from the previous transform this._current.resume(); @@ -41,6 +43,9 @@ class TransformStream extends Transform { } } -export function transformStream(handleChunk, options = {}) { - return new TransformStream(handleChunk, { objectMode: true, ...options }); +export function transformStream( + callback: TransformStreamCallback, + options: TransformOptions = {}, +) { + return new TransformStream(callback, { objectMode: true, ...options }); } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..3e08c8f --- /dev/null +++ b/src/types.ts @@ -0,0 +1,7 @@ +export type AnyStream = NodeJS.ReadableStream | NodeJS.WritableStream | NodeJS.ReadWriteStream; + +export type PipeableStreams = + | [first: TFirstStream] + | [first: TFirstStream, last: TLastStream] + | [first: TFirstStream, ...streams: NodeJS.ReadWriteStream[], last: TLastStream] + | [first: TFirstStream, ...streams: NodeJS.ReadWriteStream[], last: TLastStream, options: TOptions]; diff --git a/src/utils/Duplexer.ts b/src/utils/Duplexer.ts index 7c89832..3530062 100644 --- a/src/utils/Duplexer.ts +++ b/src/utils/Duplexer.ts @@ -1,9 +1,12 @@ -import { Duplex } from "stream"; +import { Duplex, DuplexOptions, Readable, Writable } from "stream"; + +type DuplexerCallback = (error?: Error | null) => void; export class Duplexer extends Duplex { - private input: any; - private output: any; - constructor(input, output, options) { + private input: Writable; + private output: Readable; + + constructor(input: Writable, output: Readable, options?: DuplexOptions) { super(options || { writableObjectMode: true, readableObjectMode: true }); this.input = input; this.output = output; @@ -15,11 +18,11 @@ export class Duplexer extends Duplex { } }); } - _write(chunk, encoding, callback) { + _write(chunk: unknown, encoding: BufferEncoding, callback: DuplexerCallback) { this.input.write(chunk, encoding, callback); } - _final(callback) { - this.input.end(null, null, callback); + _final(callback: DuplexerCallback) { + this.input.end(null, callback); } _read() { this.output.resume(); diff --git a/src/utils/decorateWithAsyncIterator.ts b/src/utils/decorateWithAsyncIterator.ts index 81adc67..1751281 100644 --- a/src/utils/decorateWithAsyncIterator.ts +++ b/src/utils/decorateWithAsyncIterator.ts @@ -1,5 +1,6 @@ import { toAsyncIterator } from "./toAsyncIterator"; +import { Readable } from "stream"; -export function decorateWithAsyncIterator(stream) { +export function decorateWithAsyncIterator(stream: Readable) { stream[Symbol.asyncIterator] = () => toAsyncIterator(stream); } diff --git a/src/utils/decorateWithPromise.ts b/src/utils/decorateWithPromise.ts deleted file mode 100644 index 293fcde..0000000 --- a/src/utils/decorateWithPromise.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function decorateWithPromise(stream, promise) { - const descriptors = ["then", "catch", "finally"].map((property) => { - return { property, descriptor: Reflect.getOwnPropertyDescriptor(Promise.prototype, property) }; - }); - - for (const { property, descriptor } of descriptors) { - const value = (...args) => Reflect.apply(descriptor.value, promise, args); - Reflect.defineProperty(stream, property, { ...descriptor, value }); - } - return stream; -} diff --git a/src/utils/isReadableStream.ts b/src/utils/isReadableStream.ts new file mode 100644 index 0000000..782d7d4 --- /dev/null +++ b/src/utils/isReadableStream.ts @@ -0,0 +1,13 @@ +import { Readable } from "stream"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isReadableStream(stream: any): stream is Readable { + return ( + stream !== null && + typeof stream === "object" && + typeof stream.read === "function" && + typeof stream.readable === "boolean" && + typeof stream.pipe === "function" && + typeof stream.on === "function" + ); +} diff --git a/src/utils/isWriteableStream.ts b/src/utils/isWriteableStream.ts new file mode 100644 index 0000000..3775338 --- /dev/null +++ b/src/utils/isWriteableStream.ts @@ -0,0 +1,12 @@ +import { Writable } from "stream"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isWritableStream(stream: any): stream is Writable { + return ( + stream !== null && + typeof stream === "object" && + typeof stream.write === "function" && + typeof stream.end === "function" && + typeof stream.writable === "boolean" + ); +} diff --git a/src/utils/parseArgs.ts b/src/utils/parseArgs.ts index 74e1440..71df923 100644 --- a/src/utils/parseArgs.ts +++ b/src/utils/parseArgs.ts @@ -1,14 +1,34 @@ -export function parseArgs(args, defaults = {}) { +type Args = { + params: TParams[]; + options: TOptions; +}; + +export function parseArgs( + args: [...params: TParams[]] | [...params: TParams[], options: TOptions], + defaults?: Partial, +): Args { const last = args[args.length - 1]; - const hasOption = typeof last === "object" && !Array.isArray(last) && typeof last.pipe !== "function"; - const options = { - objectMode: true, - ...defaults, - ...(hasOption ? args.pop() : {}), - }; + + let options = {}; + if (isOption(last)) { + options = args.pop() as TOptions; + } return { - streams: Array.isArray(args[0]) ? args[0] : args, - options, + params: (Array.isArray(args[0]) ? args[0] : args) as TParams[], + options: { + objectMode: true, + ...defaults, + ...options, + } as TOptions, }; } + +function isOption(last: unknown): last is TOptions { + return Boolean( + last && + typeof last === "object" && + !Array.isArray(last) && + typeof (last as Record).pipe !== "function", + ); +} diff --git a/src/utils/pipeStreamsTogether.ts b/src/utils/pipeStreamsTogether.ts new file mode 100644 index 0000000..f941562 --- /dev/null +++ b/src/utils/pipeStreamsTogether.ts @@ -0,0 +1,18 @@ +import { AnyStream } from "../types"; +import { Readable, Writable } from "stream"; + +export function pipeStreamsTogether(streams: AnyStream[], wrapper: AnyStream) { + for (const [i, stream] of streams.entries()) { + const next = streams[i + 1]; + if (next) { + // we assume that the oleoduc is correctly configured + const readable = stream as Readable; + const writable = next as Writable; + readable.pipe(writable); + } + + if (stream !== wrapper) { + stream.on("error", (err) => wrapper.emit("error", err)); + } + } +} diff --git a/src/utils/toAsyncIterator.ts b/src/utils/toAsyncIterator.ts index 5d17a8f..3b59e20 100644 --- a/src/utils/toAsyncIterator.ts +++ b/src/utils/toAsyncIterator.ts @@ -12,7 +12,7 @@ type ToAsyncIteratorOptions = { * @param options * @returns {AsyncGenerator<*, void, *>} */ -export async function* toAsyncIterator(stream, options: ToAsyncIteratorOptions = {}) { +export async function* toAsyncIterator(stream: NodeJS.ReadableStream, options: ToAsyncIteratorOptions = {}) { const chunkSize = options.chunkSize || 1; if (typeof stream.read !== "function") { diff --git a/src/utils/wrapStreams.ts b/src/utils/wrapStreams.ts new file mode 100644 index 0000000..adb5c71 --- /dev/null +++ b/src/utils/wrapStreams.ts @@ -0,0 +1,29 @@ +import { Duplexer } from "./Duplexer"; +import { PassThrough, TransformOptions } from "stream"; +import { isReadableStream } from "./isReadableStream"; +import { isWritableStream } from "./isWriteableStream"; +import { AnyStream } from "../types"; + +export function wrapStreams(streams: AnyStream[], options: TransformOptions = {}) { + if (streams.length === 0) { + throw new Error("You must provide at least one stream"); + } + + const first = streams[0]; + const last = streams[streams.length - 1]; + + let wrapper: AnyStream; + if (first === last) { + wrapper = first; + } else if (isWritableStream(first) && isReadableStream(last)) { + wrapper = new Duplexer(first, last, options); + } else if (isWritableStream(first)) { + wrapper = first; + } else if (isReadableStream(first)) { + wrapper = last; + } else { + wrapper = new PassThrough(options); + } + + return { first, last, wrapper }; +} diff --git a/src/writeData.ts b/src/writeData.ts index 9fcccf0..5a6bebf 100644 --- a/src/writeData.ts +++ b/src/writeData.ts @@ -1,22 +1,26 @@ import { Writable } from "stream"; -import cyclist from "cyclist"; +import cyclist, { Cyclist } from "cyclist"; +import { WritableOptions } from "node:stream"; -type ParallelWriteOptions = { - parallel?: number; -}; +export type WriteDataOptions = { parallel?: number }; +export type WriteDataCallback = (data: TInput) => void | unknown; -class ParallelWrite extends Writable { +type ParallelWriteOptions = WritableOptions & { ordered?: boolean }; +type ParallelOnWrite = (chunk: TInput, callback: (e?: Error | null, data?: unknown) => void) => void; +type ParallelOnDrain = () => void; + +class ParallelWrite extends Writable { private _maxParallel: number; - private _onWrite: (chunk: T, callback: (e: Error, data: T) => void) => void; + private _onWrite: ParallelOnWrite; private _destroyed: boolean; private _flushed: boolean; private _ordered: boolean; - private _buffer: cyclist | []; + private _buffer: Cyclist | Array; private _top: number; private _bottom: number; - private _ondrain: () => void; + private _ondrain: ParallelOnDrain | null; - constructor(maxParallel, opts, onWrite) { + constructor(maxParallel: number, opts: ParallelWriteOptions | null, onWrite: ParallelOnWrite) { super(); if (typeof maxParallel === "function") { onWrite = maxParallel; @@ -51,7 +55,7 @@ class ParallelWrite extends Writable { this.emit("close"); } - _write(chunk, enc, callback) { + _write(chunk: TInput, enc: BufferEncoding, callback: ParallelOnDrain) { const pos = this._top++; this._onWrite(chunk, (err, data) => { @@ -61,10 +65,10 @@ class ParallelWrite extends Writable { this.destroy(); return; } - if (this._ordered) { + if (this._isOrdered(this._buffer)) { this._buffer.put(pos, data === undefined || data === null ? null : data); } else { - this._buffer.push(data); + this._buffer.push(data as TInput); } this._drain(); }); @@ -73,14 +77,14 @@ class ParallelWrite extends Writable { this._ondrain = callback; } - _final(callback) { + _final(callback: ParallelOnDrain) { this._flushed = true; this._ondrain = callback; this._drain(); } _drain() { - if (this._ordered) { + if (this._isOrdered(this._buffer)) { while (this._buffer.get(this._bottom) !== undefined) { this._buffer.del(this._bottom++); } @@ -98,19 +102,26 @@ class ParallelWrite extends Writable { ondrain(); } + _isOrdered(buffer: Cyclist | Array): buffer is Cyclist { + return this._ordered; + } + _drained() { const diff = this._top - this._bottom; return this._flushed ? !diff : diff < this._maxParallel; } } -export function writeData(handleChunk, options: ParallelWriteOptions = {}) { - return new ParallelWrite(options.parallel || 1, { objectMode: true }, async (chunk, callback) => { +export function writeData( + callback: WriteDataCallback, + options: WriteDataOptions = {}, +): NodeJS.WritableStream { + return new ParallelWrite(options.parallel || 1, { objectMode: true }, async (chunk, cb) => { try { - const res = await handleChunk(chunk); - callback(null, res); + const res = await callback(chunk); + cb(null, res); } catch (e) { - callback(e); + cb(e as Error); } }); } diff --git a/src/writeToStdout.ts b/src/writeToStdout.ts index 3fbd93d..5241e42 100644 --- a/src/writeToStdout.ts +++ b/src/writeToStdout.ts @@ -1,8 +1,10 @@ import { Writable } from "stream"; +type WriteToStdoutCallback = (error: Error | null | undefined) => void; + export function writeToStdout() { return new Writable({ - write(chunk, encoding, callback) { + write(chunk: Uint8Array | string, encoding: BufferEncoding, callback: WriteToStdoutCallback) { //write to stdout as a side effect and honor finish event //https://stackoverflow.com/questions/33190458/node-pipe-to-stdout-how-do-i-tell-if-drained process.stdout.write(chunk, callback); diff --git a/test/.mocharc.json b/test/.mocharc.json new file mode 100644 index 0000000..eed28b6 --- /dev/null +++ b/test/.mocharc.json @@ -0,0 +1,10 @@ +{ + "timeout": 5000, + "exit": true, + "extension": ["ts", "js"], + "recursive": true, + "node-option": [ + "experimental-specifier-resolution=node", + "import=tsx" + ] +} diff --git a/test/accumulateData-test.ts b/test/accumulateData-test.ts index 3294bc8..d8fbc6b 100644 --- a/test/accumulateData-test.ts +++ b/test/accumulateData-test.ts @@ -4,7 +4,7 @@ import { createStream } from "./testUtils"; describe("accumulateData", () => { it("can accumulateData by grouping them (flush)", (done) => { - const result = []; + const result: string[] = []; const source = createStream(); source.push("John"); source.push("Doe"); @@ -15,7 +15,7 @@ describe("accumulateData", () => { source .pipe( accumulateData( - (acc, data, flush) => { + (acc, data: string, flush) => { acc = [...acc, data]; if (acc.length < 2) { @@ -25,10 +25,10 @@ describe("accumulateData", () => { flush(acc.join(" ")); return []; // Reset accumulator }, - { accumulator: [] }, + { accumulator: [] as string[] }, ), ) - .pipe(writeData((data) => result.push(data))) + .pipe(writeData((data: string) => result.push(data))) .on("finish", () => { deepStrictEqual(result, ["John Doe", "Robert Hue"]); done(); @@ -36,7 +36,7 @@ describe("accumulateData", () => { }); it("can accumulateData into a single chunk (no flush)", (done) => { - const result = []; + const result: string[] = []; const source = createStream(); source.push("j"); source.push("o"); @@ -46,7 +46,7 @@ describe("accumulateData", () => { source .pipe(accumulateData((acc, data) => acc + data, { accumulator: "" })) - .pipe(writeData((data) => result.push(data))) + .pipe(writeData((data: string) => result.push(data))) .on("finish", () => { deepStrictEqual(result, ["john"]); done(); @@ -69,7 +69,7 @@ describe("accumulateData", () => { source .pipe(accumulator) - .pipe(writeData((data) => result.push(data))) + .pipe(writeData((data: string) => result.push(data))) .on("finish", () => { fail(); done(); diff --git a/test/compose-test.ts b/test/compose-test.ts index 7253fb2..c202d00 100644 --- a/test/compose-test.ts +++ b/test/compose-test.ts @@ -1,19 +1,20 @@ import { deepStrictEqual, fail } from "assert"; -import SlowStream from "slow-stream"; -import { createStream } from "./testUtils"; -import { compose, flattenArray, oleoduc, transformData, writeData } from "../src"; +import { createSlowStream, createStream } from "./testUtils"; +import { compose, flattenArray, transformData, writeData } from "../src"; describe("compose", () => { it("can compose streams", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("first"); source.push(null); compose( source, - transformData((d) => d.substring(0, 1)), - writeData((data) => chunks.push(data)), + transformData((data: string) => data.substring(0, 1)), + writeData((data: string) => { + chunks.push(data); + }), ) .on("finish", () => { deepStrictEqual(chunks, ["f"]); @@ -26,7 +27,7 @@ describe("compose", () => { }); it("can iterate over a composed stream", async () => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -35,18 +36,18 @@ describe("compose", () => { const stream = compose( source, - transformData((data) => data.substring(0, 1)), + transformData((data: string) => data.substring(0, 1)), ); for await (const chunk of stream) { - chunks.push(chunk); + chunks.push(chunk.toString()); } deepStrictEqual(chunks, ["a", "b", "r"]); }); it("can pipe a compose stream", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -54,8 +55,12 @@ describe("compose", () => { source.push(null); compose(source) - .pipe(transformData((data) => data.substring(0, 1))) - .pipe(writeData((data) => chunks.push(data))) + .pipe(transformData((data: string) => data.substring(0, 1))) + .pipe( + writeData((data: string) => { + chunks.push(data); + }), + ) .on("finish", () => { deepStrictEqual(chunks, ["a", "b", "r"]); done(); @@ -63,7 +68,7 @@ describe("compose", () => { }); it("can build compose with first writeable and last readable (duplex)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -73,11 +78,15 @@ describe("compose", () => { source .pipe( compose( - transformData((data) => data.substring(0, 1)), + transformData((data: string) => data.substring(0, 1)), transformData((data) => "_" + data), ), ) - .pipe(writeData((data) => chunks.push(data))) + .pipe( + writeData((data: string) => { + chunks.push(data); + }), + ) .on("finish", () => { deepStrictEqual(chunks, ["_a", "_b", "_r"]); done(); @@ -85,11 +94,11 @@ describe("compose", () => { }); it("can compose inside compose", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); const nested = compose( source, - transformData((d) => d.substring(0, 1)), + transformData((d: string) => d.substring(0, 1)), ); source.push("first"); @@ -97,7 +106,9 @@ describe("compose", () => { compose( nested, - writeData((d) => chunks.push(d)), + writeData((data: string) => { + chunks.push(data); + }), ) .on("finish", () => { deepStrictEqual(chunks, ["f"]); @@ -109,31 +120,6 @@ describe("compose", () => { }); }); - it("can use oleoduc inside compose", (done) => { - const chunks = []; - const source = createStream(); - const nested = oleoduc( - source, - transformData((d) => d.substring(0, 1)), - ); - - source.push("first"); - source.push(null); - - compose( - nested, - writeData((d) => chunks.push(d)), - ) - .on("finish", () => { - deepStrictEqual(chunks, ["f"]); - done(); - }) - .on("error", () => { - fail(); - done(); - }); - }); - it("can handle back pressure", (done) => { let result = ""; const source = createStream(); @@ -143,8 +129,8 @@ describe("compose", () => { compose( source, flattenArray({ highWaterMark: 1 }), - new SlowStream({ maxWriteInterval: 10 }), - writeData((data) => { + createSlowStream({ maxWriteInterval: 10 }), + writeData((data: string) => { result += data; }), ).on("finish", () => { @@ -161,8 +147,8 @@ describe("compose", () => { compose( source, - compose(flattenArray({ highWaterMark: 1 }), new SlowStream({ maxWriteInterval: 10 })), - writeData((data) => { + compose(flattenArray({ highWaterMark: 1 }), createSlowStream({ maxWriteInterval: 10 })), + writeData((data: string) => { result += data; }), ).on("finish", () => { @@ -176,7 +162,7 @@ describe("compose", () => { compose( source, - writeData(() => ({})), + writeData(() => {}), ) .on("finish", () => { fail(); diff --git a/test/concatStreams-test.ts b/test/concatStreams-test.ts index 58300a8..a90639e 100644 --- a/test/concatStreams-test.ts +++ b/test/concatStreams-test.ts @@ -1,6 +1,7 @@ import { deepStrictEqual } from "assert"; import { concatStreams, writeData } from "../src/index"; import { delay, streamArray } from "./testUtils"; +import { Readable } from "stream"; describe("concatStreams", () => { it("can concat streams", (done) => { @@ -19,7 +20,7 @@ describe("concatStreams", () => { it("can concat streams (next function)", (done) => { let result = ""; const array = [streamArray(["andré"]), streamArray(["bruno"])]; - const next = () => array.shift(); + const next = () => array.shift() as Readable; concatStreams(next) .pipe(writeData((data) => (result += data))) @@ -32,7 +33,7 @@ describe("concatStreams", () => { it("can concat streams (async next function)", (done) => { let result = ""; const array = [streamArray(["andré"]), streamArray(["bruno"])]; - const next = () => Promise.resolve(array.shift()); + const next = () => Promise.resolve(array.shift() as Readable); concatStreams(next) .pipe(writeData((data) => (result += data))) @@ -45,7 +46,7 @@ describe("concatStreams", () => { it("can concat streams (slow async next)", (done) => { let result = ""; const array = [streamArray(["andré"]), streamArray(["bruno"])]; - const next = () => delay(() => array.shift(), 2); + const next = () => delay(() => array.shift() as Readable, 2); concatStreams(next) .pipe(writeData((data) => (result += data))) diff --git a/test/filterData-tests.ts b/test/filterData-tests.ts index f14d13f..936671c 100644 --- a/test/filterData-tests.ts +++ b/test/filterData-tests.ts @@ -4,7 +4,7 @@ import { filterData } from "../src"; describe("filterData", () => { it("should filter (ignore empty)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("first"); source.push(""); @@ -24,7 +24,7 @@ describe("filterData", () => { }); it("should filter (async)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("first"); source.push(""); @@ -44,7 +44,7 @@ describe("filterData", () => { }); it("should filter (ignore first line)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("first"); source.push("second"); diff --git a/test/flattenArray-test.ts b/test/flattenArray-test.ts index e1ffcda..43c8c33 100644 --- a/test/flattenArray-test.ts +++ b/test/flattenArray-test.ts @@ -1,7 +1,6 @@ import { deepStrictEqual } from "assert"; -import SlowStream from "slow-stream"; import { accumulateData, flattenArray, writeData } from "../src"; -import { streamArray } from "./testUtils"; +import { createSlowStream, streamArray } from "./testUtils"; describe("flattenArray", () => { it("can flat map an array", (done) => { @@ -22,17 +21,24 @@ describe("flattenArray", () => { }); it("can flat map an array inside a pipeline", (done) => { - let result = []; + let result = ""; const source = streamArray(); source.push("andré"); source.push("bruno"); source.push(null); source - .pipe(accumulateData((acc, data) => [...acc, data.substring(0, 1)], { accumulator: [] })) + .pipe( + accumulateData( + (acc, data: string) => { + return [...acc, data.substring(0, 1)]; + }, + { accumulator: [] as string[] }, + ), + ) .pipe(flattenArray()) .pipe( - writeData((data) => { + writeData((data: string) => { result += data.toUpperCase(); }), ) @@ -51,7 +57,7 @@ describe("flattenArray", () => { source .pipe(flattenArray({ objectMode: true, highWaterMark: 1 })) - .pipe(new SlowStream({ maxWriteInterval: 10 })) // Force up streams to be paused + .pipe(createSlowStream({ maxWriteInterval: 10 })) // Force up streams to be paused .pipe( writeData((data) => { result += "_" + data; diff --git a/test/groupData-test.ts b/test/groupData-test.ts index 284b8c7..6357bbb 100644 --- a/test/groupData-test.ts +++ b/test/groupData-test.ts @@ -4,7 +4,7 @@ import { groupData, writeData } from "../src"; describe("groupData", () => { it("can create group of data", (done) => { - const results = []; + const results: string[][] = []; const source = createStream(); source.push("abc"); source.push("def"); @@ -14,7 +14,7 @@ describe("groupData", () => { source .pipe(groupData()) .pipe( - writeData((group) => { + writeData((group: string[]) => { return results.push(group); }), ) @@ -25,7 +25,7 @@ describe("groupData", () => { }); it("can create group of data with custom size", (done) => { - const results = []; + const results: string[][] = []; const source = createStream(); source.push("abc"); source.push("def"); @@ -35,7 +35,7 @@ describe("groupData", () => { source .pipe(groupData({ size: 2 })) .pipe( - writeData((group) => { + writeData((group: string[]) => { return results.push(group); }), ) diff --git a/test/mergeStreams-test.ts b/test/mergeStreams-test.ts index c3f3c41..087888f 100644 --- a/test/mergeStreams-test.ts +++ b/test/mergeStreams-test.ts @@ -9,7 +9,11 @@ describe("mergeStreams", () => { const source2 = streamArray(["bruno"]); mergeStreams(source1, source2) - .pipe(writeData((data) => (result += data))) + .pipe( + writeData((data) => { + result += data; + }), + ) .on("finish", () => { deepStrictEqual(result, "andrébruno"); done(); diff --git a/test/oleoduc-test.ts b/test/oleoduc-test.ts index 61d0959..e1ca316 100644 --- a/test/oleoduc-test.ts +++ b/test/oleoduc-test.ts @@ -1,10 +1,10 @@ -import { deepStrictEqual, fail, strictEqual } from "assert"; -import { createStream, delay } from "./testUtils"; +import { deepStrictEqual, fail } from "assert"; +import { assertErrorMessage, createStream, delay } from "./testUtils"; import { compose, oleoduc, transformData, writeData } from "../src"; describe("oleoduc", () => { it("can create oleoduc", async () => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -13,10 +13,10 @@ describe("oleoduc", () => { await oleoduc( source, - transformData((data) => { + transformData((data: string) => { return delay(() => data.substring(0, 1), 2); }), - writeData((data) => { + writeData((data: string) => { return delay(() => chunks.push(data), 2); }), ); @@ -24,50 +24,8 @@ describe("oleoduc", () => { deepStrictEqual(chunks, ["a", "b", "r"]); }); - it("can iterate over an oleoduc", async () => { - const chunks = []; - const source = createStream(); - source.push("andré"); - source.push("bruno"); - source.push("robert"); - source.push(null); - - const stream = oleoduc( - source, - transformData((data) => data.substring(0, 1)), - ); - - for await (const chunk of stream) { - chunks.push(chunk); - } - - deepStrictEqual(chunks, ["a", "b", "r"]); - }); - - it("can nest oleoduc", async () => { - const chunks = []; - const source = createStream(); - const nested = oleoduc( - source, - transformData((d) => d.substring(0, 1)), - ); - - source.push("first"); - source.push(null); - - try { - await oleoduc( - nested, - writeData((d) => chunks.push(d)), - ); - deepStrictEqual(chunks, ["f"]); - } catch (e) { - fail(e); - } - }); - it("can build oleoduc with first writeable and last readable (duplex)", async () => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -78,23 +36,23 @@ describe("oleoduc", () => { await oleoduc( source, compose( - transformData((data) => data.substring(0, 1)), - transformData((data) => "_" + data), + transformData((data: string) => data.substring(0, 1)), + transformData((data: string) => "_" + data), ), - writeData((d) => chunks.push(d)), + writeData((data: string) => chunks.push(data)), ); deepStrictEqual(chunks, ["_a", "_b", "_r"]); } catch (e) { - fail(e); + fail(e as Error); } }); it("can use compose inside oleoduc", async () => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); const composed = compose( source, - transformData((d) => d.substring(0, 1)), + transformData((d: string) => d.substring(0, 1)), ); source.push("first"); @@ -102,7 +60,7 @@ describe("oleoduc", () => { await oleoduc( composed, - writeData((d) => chunks.push(d)), + writeData((data: string) => chunks.push(data)), ) .then(() => { deepStrictEqual(chunks, ["f"]); @@ -112,22 +70,6 @@ describe("oleoduc", () => { }); }); - it("can pipe and oleoduc", async () => { - const chunks = []; - const source = createStream(); - source.push("andré"); - source.push("bruno"); - source.push("robert"); - source.push(null); - - await oleoduc(source) - .pipe(transformData((data) => data.substring(0, 1))) - .pipe(writeData((data) => chunks.push(data))) - .on("finish", () => { - deepStrictEqual(chunks, ["a", "b", "r"]); - }); - }); - it("should propagate emitted error", (done) => { const source = createStream(); @@ -169,10 +111,11 @@ describe("oleoduc", () => { it("should fail when no stream are provided", async () => { try { + // @ts-expect-error TS2345 await oleoduc(); fail(); } catch (e) { - strictEqual(e.message, "You must provide at least one stream"); + assertErrorMessage(e, "You must provide at least one stream"); } }); }); diff --git a/test/parallel-test.ts b/test/parallel-test.ts index 019209b..f193392 100644 --- a/test/parallel-test.ts +++ b/test/parallel-test.ts @@ -2,9 +2,11 @@ import { deepStrictEqual, fail, ok } from "assert"; import { createStream, delay } from "./testUtils"; import { filterData, transformData, writeData } from "../src"; +type AccParallelData = { number: number; timestamp: number }; + describe("parallel", () => { it("can run parallel task with order preserved", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -14,8 +16,8 @@ describe("parallel", () => { source .pipe( transformData( - async (data) => { - return new Promise((resolve) => { + async (data: string) => { + return new Promise((resolve) => { resolve(data.substring(0, 1)); }); }, @@ -24,7 +26,7 @@ describe("parallel", () => { ) .pipe( writeData( - (data) => { + (data: string) => { return delay(() => chunks.push(data), 10); }, { parallel: 5 }, @@ -39,7 +41,7 @@ describe("parallel", () => { it("can transformData (parallel)", (done) => { const timeoutPerBatch = 10; const nbParallelTasks = 2; - const acc = []; + const acc: AccParallelData[] = []; const source = createStream(); //first batch @@ -58,13 +60,13 @@ describe("parallel", () => { source .pipe( transformData( - (number) => { + (number: number) => { return delay(() => ({ number, timestamp: Date.now() }), timeoutPerBatch); }, { parallel: nbParallelTasks }, ), ) - .pipe(writeData((data) => acc.push(data))) + .pipe(writeData((data: AccParallelData) => acc.push(data))) .on("error", () => { fail(); done(); @@ -86,7 +88,7 @@ describe("parallel", () => { it("can filterData (parallel)", (done) => { const timeoutPerBatch = 10; const nbParallelTasks = 2; - const acc = []; + const acc: AccParallelData[] = []; const source = createStream(); //first batch @@ -102,11 +104,11 @@ describe("parallel", () => { source.push(null); const start = Date.now(); - let last; + let last: number; source .pipe( filterData( - (number) => { + (number: number) => { return delay(() => { last = Date.now(); return number < 5; @@ -120,7 +122,7 @@ describe("parallel", () => { return { number, timestamp: Date.now() }; }), ) - .pipe(writeData((data) => acc.push(data))) + .pipe(writeData((data: AccParallelData) => acc.push(data))) .on("error", () => { fail(); done(); @@ -142,7 +144,7 @@ describe("parallel", () => { it("can writeData (parallel)", (done) => { const timeoutPerBatch = 10; const nbParallelTasks = 2; - const acc = []; + const acc: AccParallelData[] = []; const source = createStream(); //first batch @@ -161,7 +163,7 @@ describe("parallel", () => { source .pipe( writeData( - (number) => { + (number: number) => { return delay(() => acc.push({ number, timestamp: Date.now() }), timeoutPerBatch); }, { parallel: nbParallelTasks }, diff --git a/test/pipeline-test.ts b/test/pipeline-test.ts index 5d44f1b..eefbf50 100644 --- a/test/pipeline-test.ts +++ b/test/pipeline-test.ts @@ -6,7 +6,7 @@ import { transformData, writeData } from "../src"; describe("pipeline", () => { it("can create pipeline from stream", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -15,8 +15,8 @@ describe("pipeline", () => { pipeline( source, - transformData((data) => data.substring(0, 1)), - writeData((data) => chunks.push(data)), + transformData((data: string) => data.substring(0, 1)), + writeData((data: string) => chunks.push(data)), (err) => { if (err) { return done(err); @@ -28,7 +28,7 @@ describe("pipeline", () => { }); it("can create pipeline from stream (async)", async () => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -37,10 +37,10 @@ describe("pipeline", () => { await promisify(pipeline)( source, - transformData((data) => { + transformData((data: string) => { return delay(() => data.substring(0, 1), 2); }), - writeData((data) => { + writeData((data: string) => { return delay(() => chunks.push(data), 2); }), ); diff --git a/test/readLineByLine-test.ts b/test/readLineByLine-test.ts index 8d5cb54..7c06ff6 100644 --- a/test/readLineByLine-test.ts +++ b/test/readLineByLine-test.ts @@ -4,7 +4,7 @@ import { oleoduc, readLineByLine, writeData } from "../src"; describe("readLineByLine", () => { it("can read a stream line by line", (done) => { - const result = []; + const result: string[] = []; const source = createStream(); source.push("ab"); source.push("c\ndef\ng"); @@ -14,8 +14,8 @@ describe("readLineByLine", () => { source .pipe(readLineByLine()) .pipe( - writeData((data) => { - return result.push(data); + writeData((data: string) => { + result.push(data); }), ) .on("finish", () => { @@ -25,7 +25,7 @@ describe("readLineByLine", () => { }); it("can read a stream line by line (CRLF)", (done) => { - const result = []; + const result: string[] = []; const source = createStream(); source.push("ab"); source.push("c\r\ndef\r\ng"); @@ -35,8 +35,8 @@ describe("readLineByLine", () => { source .pipe(readLineByLine()) .pipe( - writeData((data) => { - return result.push(data); + writeData((data: string) => { + result.push(data); }), ) .on("finish", () => { @@ -46,7 +46,7 @@ describe("readLineByLine", () => { }); it("can handle content without carriage return on the last line", (done) => { - const result = []; + const result: string[] = []; const source = createStream(); source.push("ab\n"); source.push("hi"); @@ -55,8 +55,8 @@ describe("readLineByLine", () => { source .pipe(readLineByLine()) .pipe( - writeData((data) => { - return result.push(data); + writeData((data: string) => { + result.push(data); }), ) .on("finish", () => { @@ -66,7 +66,7 @@ describe("readLineByLine", () => { }); it("can read multiple lines with backpressure", async () => { - const array = []; + const array: string[] = []; const source = createStream(); const manyLines = Array(250) .fill("line") @@ -79,7 +79,7 @@ describe("readLineByLine", () => { await oleoduc( source, readLineByLine(), - writeData((opco) => { + writeData((opco: string) => { array.push(opco); }), ); diff --git a/test/testUtils.ts b/test/testUtils.ts index 6ea641e..7d57425 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -1,6 +1,9 @@ import { Readable } from "stream"; +import { deepStrictEqual } from "assert"; +// @ts-expect-error TS7016 +import SlowStream from "slow-stream"; -export function delay(callback, delay) { +export function delay(callback: () => T, delay: number): Promise { return new Promise((resolve) => { return setTimeout(async () => { resolve(callback()); @@ -8,7 +11,7 @@ export function delay(callback, delay) { }); } -export function streamArray(items = []) { +export function streamArray(items: Array = []): Readable { return new Readable({ objectMode: true, read() { @@ -23,3 +26,11 @@ export function createStream() { read() {}, }); } + +export function assertErrorMessage(e: unknown, message: string) { + deepStrictEqual((e as Error).message, message); +} + +export function createSlowStream(options: { maxWriteInterval?: number } = {}) { + return new SlowStream(options); +} diff --git a/test/toAsyncIterator-test.ts b/test/toAsyncIterator-test.ts index 5c9d7b2..0829c71 100644 --- a/test/toAsyncIterator-test.ts +++ b/test/toAsyncIterator-test.ts @@ -1,7 +1,7 @@ import { deepStrictEqual, fail } from "assert"; -import { createStream } from "./testUtils"; +import { assertErrorMessage, createStream } from "./testUtils"; import { toAsyncIterator } from "../src/utils/toAsyncIterator"; -import { oleoduc, transformData } from "../src"; +import { compose, transformData } from "../src"; describe("toAsyncIterator", () => { it("can convert a readable stream into an iterator", async () => { @@ -21,12 +21,11 @@ describe("toAsyncIterator", () => { it("iterator should honor error", async () => { const readable = createStream(); - const failingStream = oleoduc( + const failingStream = compose( readable, transformData(() => { throw new Error("This is a stream error"); }), - { promisify: false }, ); readable.push("a"); readable.push("b"); @@ -36,7 +35,7 @@ describe("toAsyncIterator", () => { await iterator.next(); fail(); } catch (e) { - deepStrictEqual(e.message, "This is a stream error"); + assertErrorMessage(e, "This is a stream error"); } readable.push(null); diff --git a/test/transformData-test.ts b/test/transformData-test.ts index eb9f586..725946e 100644 --- a/test/transformData-test.ts +++ b/test/transformData-test.ts @@ -4,15 +4,15 @@ import { transformData, writeData } from "../src"; describe("transformData", () => { it("should transformData", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); source.push(null); source - .pipe(transformData((data) => data.substring(0, 1))) - .pipe(writeData((data) => chunks.push(data))) + .pipe(transformData((data: string) => data.substring(0, 1))) + .pipe(writeData((data: string) => chunks.push(data))) .on("finish", () => { deepStrictEqual(chunks, ["a", "b"]); done(); @@ -20,7 +20,7 @@ describe("transformData", () => { }); it("should transformData (async)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = createStream(); source.push("andré"); source.push("bruno"); @@ -28,14 +28,14 @@ describe("transformData", () => { source .pipe( - transformData(async (data) => { - return new Promise((resolve) => { + transformData(async (data: string) => { + return new Promise((resolve) => { resolve(data.substring(0, 1)); }); }), ) .pipe( - writeData(async (data) => { + writeData(async (data: string) => { return new Promise((resolve) => { chunks.push(data); resolve(); diff --git a/test/transformIntoCSV-test.ts b/test/transformIntoCSV-test.ts index a80132c..d2abddb 100644 --- a/test/transformIntoCSV-test.ts +++ b/test/transformIntoCSV-test.ts @@ -2,17 +2,19 @@ import { deepStrictEqual, fail, strictEqual, ok } from "assert"; import { createStream, delay } from "./testUtils"; import { transformIntoCSV, writeData } from "../src"; +type FullNameParams = { firstName: string; lastName: string }; + describe("transformIntoCSV", () => { it("should transform object into a csv", (done) => { const source = createStream(); source.push({ firstName: "Robert", lastName: "Hue" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe(transformIntoCSV()) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) @@ -27,11 +29,11 @@ describe("transformIntoCSV", () => { source.push({ firstName: "Robert", lastName: "Hue" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe(transformIntoCSV({ mapper: (v) => `"${v}"` })) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) @@ -46,11 +48,11 @@ describe("transformIntoCSV", () => { source.push({ firstName: "Robert", lastName: "Hue" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe(transformIntoCSV({ bom: true })) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) @@ -66,7 +68,7 @@ describe("transformIntoCSV", () => { source.push({ firstName: "John", lastName: "Doe" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe( transformIntoCSV({ @@ -74,7 +76,7 @@ describe("transformIntoCSV", () => { }), ) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) @@ -89,7 +91,7 @@ describe("transformIntoCSV", () => { source.push({ firstName: "Robert", lastName: "Hue" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe( transformIntoCSV({ @@ -99,7 +101,7 @@ describe("transformIntoCSV", () => { }), ) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) @@ -114,18 +116,18 @@ describe("transformIntoCSV", () => { source.push({ firstName: "Robert", lastName: "Hue" }); source.push(null); - const csv = []; + const csv: string[] = []; source .pipe( transformIntoCSV({ columns: { - fullName: (data) => Promise.resolve(`${data.firstName} ${data.lastName}`), - lastName: async (data) => await delay(() => data.lastName, 5), + fullName: (data: FullNameParams) => Promise.resolve(`${data.firstName} ${data.lastName}`), + lastName: async (data: FullNameParams) => await delay(() => data.lastName, 5), }, }), ) .pipe( - writeData((line) => { + writeData((line: string) => { csv.push(line); }), ) diff --git a/test/transformStream-test.ts b/test/transformStream-test.ts index b8d64b7..27cbe5b 100644 --- a/test/transformStream-test.ts +++ b/test/transformStream-test.ts @@ -1,16 +1,15 @@ import { deepStrictEqual } from "assert"; -import SlowStream from "slow-stream"; -import { createStream, delay, streamArray } from "./testUtils"; +import { createSlowStream, createStream, delay, streamArray } from "./testUtils"; import { transformStream, writeData } from "../src"; describe("transformStream", () => { it("should transform data into a stream", (done) => { - const chunks = []; + const chunks: string[] = []; const source = streamArray(["andré", "bruno"]); source .pipe( - transformStream((data) => { + transformStream((data: string) => { const source = createStream(); source.push(data + "_transformed"); source.push(null); @@ -18,8 +17,8 @@ describe("transformStream", () => { }), ) .pipe( - writeData((data) => { - return chunks.push(data); + writeData((data: string) => { + chunks.push(data); }), ) .on("finish", () => { @@ -29,12 +28,12 @@ describe("transformStream", () => { }); it("should transform data into a stream (async)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = streamArray(["andré", "bruno"]); source .pipe( - transformStream((data) => { + transformStream((data: string) => { const source = createStream(); return delay(() => { @@ -45,8 +44,8 @@ describe("transformStream", () => { }), ) .pipe( - writeData((data) => { - return chunks.push(data); + writeData((data: string) => { + chunks.push(data); }), ) .on("finish", () => { @@ -56,13 +55,13 @@ describe("transformStream", () => { }); it("should transform data into a stream (backpressure)", (done) => { - const chunks = []; + const chunks: string[] = []; const source = streamArray(["andré"]); source .pipe( transformStream( - async (data) => { + async (data: string) => { const source = createStream(); for (let i = 0; i < 5; i++) { source.push(data + "_transformed"); @@ -73,10 +72,10 @@ describe("transformStream", () => { { objectMode: true, highWaterMark: 1 }, ), ) - .pipe(new SlowStream({ maxWriteInterval: 10 })) // Force up streams to be paused + .pipe(createSlowStream({ maxWriteInterval: 10 })) // Force up streams to be paused .pipe( - writeData((data) => { - return chunks.push(data); + writeData((data: string) => { + chunks.push(data); }), ) .on("finish", () => { diff --git a/test/writeData-test.ts b/test/writeData-test.ts index 63aefc7..747a001 100644 --- a/test/writeData-test.ts +++ b/test/writeData-test.ts @@ -7,11 +7,11 @@ describe("writeData", () => { const source = createStream(); source.push("andré"); source.push(null); - const acc = []; + const acc: string[] = []; source .pipe( - writeData((data) => { + writeData((data: string) => { acc.push(data); }), ) @@ -72,12 +72,12 @@ describe("writeData", () => { source.push(2); source.push(3); source.push(null); - const acc = []; + const acc: number[] = []; source .pipe( writeData( - (data) => { + (data: number) => { return new Promise((resolve, reject) => { if (data === 2) { reject(new Error("async error")); diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json index adbf054..990eb6c 100644 --- a/tsconfig.cjs.json +++ b/tsconfig.cjs.json @@ -4,6 +4,7 @@ "moduleResolution": "node", "module": "commonjs", "target": "es2015", - "outDir": "./dist/cjs" + "outDir": "./dist/cjs", + "typeRoots": ["./node_modules/@types", "./src/@types"] } } diff --git a/tsconfig.json b/tsconfig.json index 1197ad0..b340522 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,12 +5,13 @@ "target": "esnext", "outDir": "./dist/mjs", "rootDirs": ["src"], - "types": ["node"], + "types": ["node", "mocha"], "lib": ["esnext"], "allowJs": true, - "strict": false, + "strict": true, "sourceMap": true, - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true }, "exclude": ["node_modules", "dist"], "include": ["src"] diff --git a/tsconfig.test.json b/tsconfig.test.json index 1d5c422..b3a01d4 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,9 +1,7 @@ { "extends": "./tsconfig.cjs.json", + "include": ["src", "test"], "compilerOptions": { - "outDir": "./build", - "rootDirs": ["./test"], - "types": ["node", "mocha"] + "outDir": "./build" }, - "include": ["src", "test"] }