diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 4b89e49e65..0000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - "env": { - "es2021": true, - "node": true - }, - "extends": "eslint:recommended", - "overrides": [ - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "rules": { - } -} diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index ebcafb81c7..05ce8664f8 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -11,5 +11,5 @@ jobs: steps: - uses: actions/dependency-review-action@v2.2.0 with: - allow-licenses: Apache-2.0, MIT, BSD-3-Clause, ISC, BSD-2-Clause, MIT OR (CC0-1.0 AND MIT), CC0-1.0 OR MIT OR (CC0-1.0 AND MIT), CC-BY-3.0, CC0-1.0, MIT OR Apache-2.0, MIT AND Apache-2.0, MIT OR WTFPL, BSD-2-Clause OR (MIT OR Apache-2.0), Python-2.0, ISC AND MIT, Apache-2.0 AND MIT, MIT/Apache-2.0, Apache-2.0 OR MIT, (Apache-2.0 OR MIT) AND BSD-3-Clause, Zlib OR Apache-2.0 OR MIT, MIT OR Apache-2.0 OR Zlib, MIT OR (Apache-2.0 OR Zlib), (Apache-2.0 WITH LLVM-exception), 0BSD, CC-BY-4.0, Unlicense, MPL-1.1 + allow-licenses: Apache-2.0, MIT, BSD-3-Clause, ISC, BSD-2-Clause, MIT OR (CC0-1.0 AND MIT), CC0-1.0 OR MIT OR (CC0-1.0 AND MIT), CC-BY-3.0, CC0-1.0, MIT OR Apache-2.0, MIT AND Apache-2.0, MIT OR WTFPL, BSD-2-Clause OR (MIT OR Apache-2.0), Python-2.0, ISC AND MIT, Apache-2.0 AND MIT, MIT/Apache-2.0, Apache-2.0 OR MIT, (Apache-2.0 OR MIT) AND BSD-3-Clause, Zlib OR Apache-2.0 OR MIT, MIT OR Apache-2.0 OR Zlib, MIT OR (Apache-2.0 OR Zlib), (Apache-2.0 WITH LLVM-exception), 0BSD, CC-BY-4.0, Unlicense, MPL-1.1, LicenseRef-scancode-unicode AND MIT fail-on-scopes: runtime diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 398f008451..e457d1b0d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -154,6 +154,8 @@ jobs: with: name: fastly-weval-ic-cache - run: npm install + - name: Build CLI + run: npm run build:cli - run: npm test build-debug: @@ -164,6 +166,11 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + - name: Set up Node LTS + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + - run: npm ci - name: Install Rust 1.81.0 run: | rustup toolchain install 1.81.0 @@ -174,6 +181,8 @@ jobs: with: path: "/home/runner/.cargo/bin/wasm-tools" key: crate-cache-wasm-tools-${{ env.wasm-tools_version }} + - name: Build CLI + run: npm run build:cli - name: Build with full debug info run: npm run build:debug:info - uses: actions/upload-artifact@v4 @@ -193,6 +202,11 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + - name: Set up Node LTS + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + - run: npm ci - name: Install Rust 1.81.0 run: | rustup toolchain install 1.81.0 @@ -203,6 +217,8 @@ jobs: with: path: "/home/runner/.cargo/bin/wasm-tools" key: crate-cache-wasm-tools-${{ env.wasm-tools_version }} + - name: Build CLI + run: npm run build:cli - name: Build if: matrix.profile == 'release' run: npm run build:release @@ -229,9 +245,11 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - - uses: actions/setup-node@v3 + - name: Set up Node LTS + uses: actions/setup-node@v3 with: node-version: 'lts/*' + - run: npm ci - name: Download Engine uses: actions/download-artifact@v4 @@ -250,8 +268,9 @@ jobs: with: path: "/home/runner/.cargo/bin/wasm-tools" key: crate-cache-wasm-tools-${{ env.wasm-tools_version }} - - - run: npm install + + - name: Build CLI + run: npm run build:cli - name: Build WPT runtime run: tests/wpt-harness/build-wpt-runtime.sh --debug-build @@ -277,9 +296,11 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - - uses: actions/setup-node@v3 + - name: Set up Node LTS + uses: actions/setup-node@v3 with: node-version: 'lts/*' + - run: npm ci - name: Download Engine uses: actions/download-artifact@v4 @@ -305,7 +326,8 @@ jobs: path: "/home/runner/.cargo/bin/wasm-tools" key: crate-cache-wasm-tools-${{ env.wasm-tools_version }} - - run: npm install + - name: Build CLI + run: npm run build:cli - name: Build WPT runtime run: tests/wpt-harness/build-wpt-runtime.sh ${{matrix.profile == 'weval' && '--enable-experimental-aot' || matrix.profile == 'debug' && '--debug-build' || ''}} @@ -378,6 +400,9 @@ jobs: - name: Npm install run: npm install && cd ./integration-tests/js-compute && npm install + - name: Build CLI + run: npm run build:cli + - name: Run Tests run: SUFFIX_STRING=${{matrix.profile}} node integration-tests/js-compute/test.js --ci --skip-teardown${{ matrix.platform == 'viceroy' && ' --local' || '' }}${{ matrix.profile == 'weval' && ' --aot' || '' }}${{ matrix.features == 'http-cache' && ' --http-cache' || '' }} env: @@ -445,6 +470,9 @@ jobs: - name: Npm install run: npm install && cd ./integration-tests/js-compute && npm install + - name: Build CLI + run: npm run build:cli + - name: Run Tests run: SUFFIX_STRING=debug node integration-tests/js-compute/test.js --ci --skip-teardown --debug-build${{ matrix.platform == 'viceroy' && ' --local' || '' }}${{ matrix.features == 'http-cache' && ' --http-cache' || '' }} env: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 1298379cad..44041d6404 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -89,6 +89,11 @@ jobs: - uses: actions/checkout@v3 with: submodules: true + - name: Set up Node LTS + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + - run: npm ci - name: Install Rust 1.81.0 run: | rustup toolchain install 1.81.0 @@ -99,6 +104,8 @@ jobs: with: path: "/home/runner/.cargo/bin/wasm-tools" key: crate-cache-wasm-tools-${{ env.wasm-tools_version }} + - name: Build CLI + run: npm run build:cli - name: Build if: ${{ matrix.profile == 'release' }} run: npm run build:release diff --git a/.gitignore b/.gitignore index 85ed93951d..7039731060 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# TypeScript build output +dist/ + # Integration Tests build output /integration-tests/**/fixtures/**/*.tar.gz /integration-tests/**/fixtures/**/fastly.toml diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 567a2ca5f6..d51121607a 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -33,9 +33,9 @@ To build from source, you need to have the following tools installed to successf ```sh sudo apt install binaryen ``` -- rust target wasm32-wasi +- rust target wasm32-wasip1 ```sh - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 ``` - [cbindgen](https://github.com/eqrion/cbindgen#quick-start) ```sh @@ -87,9 +87,9 @@ npm run build # then, restart shell or run: source $HOME/.cargo/env ``` -- rust target wasm32-wasi +- rust target wasm32-wasip1 ```sh - rustup target add wasm32-wasi + rustup target add wasm32-wasip1 ``` - [cbindgen](https://github.com/eqrion/cbindgen#quick-start) ```sh @@ -180,10 +180,10 @@ In addition the following flags can be added after the command (passed via `npm A typical development test command is therefore something like: ``` -npm run build:debug && npm run test:integration -- --debug-build --debug-log --local --bail /crypto +npm run build:cli && npm run build:debug && npm run test:integration -- --debug-build --debug-log --local --bail /crypto ``` -Which would run a debug build, enable debugging logging, and then that build against all the crypto tests locally on Viceroy, throwing an error as soon as one is found. +Which would build the CLI TypeScript to JavaScript, run a debug build, enable debugging logging, and then that build against all the crypto tests locally on Viceroy, throwing an error as soon as one is found. Some tests can only be run on Compute and not Viceroy and will be automatically skipped. A green tick is always shown for a test that ran successfully - if it is missing that means it did not run. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000..4eae7c8886 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,37 @@ +// @ts-check + +import eslint from '@eslint/js'; +import { defineConfig } from 'eslint/config'; +import tseslint from 'typescript-eslint'; + +export default defineConfig([ + { + files: ['src/**/*.{ts,tsx,js}'], + languageOptions: { + parserOptions: { + projectService: true, + }, + }, + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + ], + rules: { + 'no-fallthrough': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', + }, + }, + { + ignores: [ + 'ci/*', + 'dist/*', + 'documentation/*', + 'integration-tests/*', + 'runtime/*', + 'tests/*', + 'test-d/*', + ] + }, +]); diff --git a/integration-tests/cli/env.test.js b/integration-tests/cli/env.test.js index 50ee75fd5b..ee93f2afab 100644 --- a/integration-tests/cli/env.test.js +++ b/integration-tests/cli/env.test.js @@ -1,5 +1,5 @@ import test from 'brittle'; -import { EnvParser } from '../../src/env.js'; +import { EnvParser } from '../../dist/env.js'; test('EnvParser should parse single key-value pair', function (t) { const parser = new EnvParser(); diff --git a/integration-tests/cli/output-path-is-not-a-file.test.js b/integration-tests/cli/output-path-is-not-a-file.test.js index d1cae1129c..fae5a54387 100644 --- a/integration-tests/cli/output-path-is-not-a-file.test.js +++ b/integration-tests/cli/output-path-is-not-a-file.test.js @@ -18,7 +18,7 @@ test('should return non-zero exit code', async function (t) { ok( stderr .toString() - .startsWith('Error: The `output` path does not point to a file:'), + .startsWith('Error: The `output` path points to a directory:'), ); ok(stderr.toString().endsWith('main.wasm')); t.is(code, 1); diff --git a/integration-tests/js-compute/fixtures/app/fastly.toml.in b/integration-tests/js-compute/fixtures/app/fastly.toml.in index 94e8840e74..0a2739aaec 100644 --- a/integration-tests/js-compute/fixtures/app/fastly.toml.in +++ b/integration-tests/js-compute/fixtures/app/fastly.toml.in @@ -9,7 +9,7 @@ name = "js-test-app" service_id = "" [scripts] - build = "node ../../../../js-compute-runtime-cli.js --env FASTLY_DEBUG_LOGGING,ACL_NAME,CONFIG_STORE_NAME,DICTIONARY_NAME,KV_STORE_NAME,SECRET_STORE_NAME,LOCAL_TEST,TEST=\"foo\" --enable-experimental-high-resolution-time-methods src/index.js" + build = "node ../../../../dist/cli/js-compute-runtime-cli.js --env FASTLY_DEBUG_LOGGING,ACL_NAME,CONFIG_STORE_NAME,DICTIONARY_NAME,KV_STORE_NAME,SECRET_STORE_NAME,LOCAL_TEST,TEST=\"foo\" --enable-experimental-high-resolution-time-methods src/index.js" [local_server] diff --git a/integration-tests/js-compute/fixtures/module-mode/fastly.toml.in b/integration-tests/js-compute/fixtures/module-mode/fastly.toml.in index 14f731bb2d..dbb2d26deb 100644 --- a/integration-tests/js-compute/fixtures/module-mode/fastly.toml.in +++ b/integration-tests/js-compute/fixtures/module-mode/fastly.toml.in @@ -9,7 +9,7 @@ name = "js-test-app" service_id = "" [scripts] - build = "node ../../../../js-compute-runtime-cli.js --env FASTLY_DEBUG_LOGGING,ACL_NAME,CONFIG_STORE_NAME,DICTIONARY_NAME,KV_STORE_NAME,SECRET_STORE_NAME,LOCAL_TEST,TEST=\"foo\" --enable-experimental-high-resolution-time-methods --module-mode src/index.js" + build = "node ../../../../dist/cli/js-compute-runtime-cli.js --env FASTLY_DEBUG_LOGGING,ACL_NAME,CONFIG_STORE_NAME,DICTIONARY_NAME,KV_STORE_NAME,SECRET_STORE_NAME,LOCAL_TEST,TEST=\"foo\" --enable-experimental-high-resolution-time-methods --module-mode src/index.js" [local_server] diff --git a/package-lock.json b/package-lock.json index c162a992c4..9192449062 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,22 +19,26 @@ "esbuild": "^0.25.0", "magic-string": "^0.30.12", "picomatch": "^4.0.3", - "regexpu-core": "^6.1.1" + "regexpu-core": "^6.4.0" }, "bin": { - "js-compute": "js-compute-runtime-cli.js", - "js-compute-runtime": "js-compute-runtime-cli.js" + "js-compute": "dist/cli/js-compute-runtime-cli.js", + "js-compute-runtime": "dist/cli/js-compute-runtime-cli.js" }, "devDependencies": { + "@eslint/js": "^9.39.1", "@jakechampion/cli-testing-library": "^1.0.0", + "@types/node": "^24.10.1", + "@types/picomatch": "^4.0.2", "brittle": "^3.5.2", - "eslint": "^9.6.0", + "eslint": "^9.39.1", "get-bin-path": "^11.0.0", "prettier": "^3.3.3", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "tsd": "^0.33.0", - "typescript": "^5.9.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.48.1", "unified": "^11.0.5" }, "peerDependencies": { @@ -752,17 +756,20 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -781,9 +788,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -791,13 +798,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -805,20 +812,36 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", - "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -828,7 +851,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -840,19 +863,22 @@ } }, "node_modules/@eslint/js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", - "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -860,12 +886,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -873,9 +900,9 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -883,14 +910,14 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.0", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" @@ -911,9 +938,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1472,6 +1499,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -1479,6 +1516,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1486,12 +1530,268 @@ "dev": true, "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz", + "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.48.1", + "@typescript-eslint/type-utils": "8.48.1", + "@typescript-eslint/utils": "8.48.1", + "@typescript-eslint/visitor-keys": "8.48.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.48.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz", + "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.48.1", + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/typescript-estree": "8.48.1", + "@typescript-eslint/visitor-keys": "8.48.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz", + "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.48.1", + "@typescript-eslint/types": "^8.48.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz", + "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/visitor-keys": "8.48.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz", + "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz", + "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/typescript-estree": "8.48.1", + "@typescript-eslint/utils": "8.48.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz", + "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz", + "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.48.1", + "@typescript-eslint/tsconfig-utils": "8.48.1", + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/visitor-keys": "8.48.1", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz", + "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.48.1", + "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/typescript-estree": "8.48.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz", + "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.48.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1694,9 +1994,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1947,9 +2247,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2285,33 +2585,32 @@ } }, "node_modules/eslint": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", - "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.6.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.12.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2325,8 +2624,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -2505,9 +2803,9 @@ "license": "MIT" }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2522,9 +2820,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2588,15 +2886,15 @@ "license": "MIT" }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2728,6 +3026,24 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2919,6 +3235,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -3003,9 +3326,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3325,9 +3648,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -3338,9 +3661,9 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -4796,9 +5119,9 @@ "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "license": "MIT", "dependencies": { "regenerate": "^1.4.2" @@ -4808,17 +5131,17 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "license": "MIT", "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" @@ -4831,12 +5154,12 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", - "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" @@ -5296,19 +5619,29 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tmatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-5.0.0.tgz", @@ -5359,6 +5692,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tsd": { "version": "0.33.0", "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.33.0.tgz", @@ -5428,6 +5774,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.48.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz", + "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.48.1", + "@typescript-eslint/parser": "8.48.1", + "@typescript-eslint/typescript-estree": "8.48.1", + "@typescript-eslint/utils": "8.48.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -5438,6 +5808,13 @@ "through": "^2.3.8" } }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -5461,9 +5838,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "license": "MIT", "engines": { "node": ">=4" diff --git a/package.json b/package.json index d3b18e540c..6bf658a772 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@fastly/js-compute", "version": "3.37.0", "license": "Apache-2.0", - "main": "js-compute-runtime-cli.js", + "main": "dist/cli/js-compute-runtime-cli.js", "types": "types/index.d.ts", "type": "module", "repository": { @@ -10,18 +10,17 @@ "url": "https://github.com/fastly/js-compute-runtime" }, "bin": { - "js-compute": "js-compute-runtime-cli.js", - "js-compute-runtime": "js-compute-runtime-cli.js" + "js-compute": "dist/cli/js-compute-runtime-cli.js", + "js-compute-runtime": "dist/cli/js-compute-runtime-cli.js" }, "files": [ "types", - "js-compute-runtime-cli.js", "fastly.wasm", "fastly.debug.wasm", "fastly-weval.wasm", "fastly-ics.wevalcache", - "src", - "index.d.ts", + "dist", + "rsrc", "package.json", "README.md", "CHANGELOG.md" @@ -34,26 +33,15 @@ "test:wpt:debug": "tests/wpt-harness/build-wpt-runtime.sh --debug-build && node ./tests/wpt-harness/run-wpt.mjs -vv", "test:types": "tsd", "clean": "rm -f starling.wasm fastly.wasm fastly.debug.wasm fastly-weval.wasm fastly-ics.wevalcache fastly-js-compute-*.tgz", - "build": "npm run clean && npm run build:debug && npm run build:release && npm run build:weval", + "build": "npm run clean && npm run build:cli && npm run build:debug && npm run build:release && npm run build:weval", + "build:cli": "tsc", "build:release": "./runtime/fastly/build-release.sh", "build:debug": "./runtime/fastly/build-debug.sh", "build:weval": "./runtime/fastly/build-release-weval.sh", "build:debug:info": "./runtime/fastly/build-debug.sh --keep-debug-info", "format-changelog": "node ci/format-changelog.js CHANGELOG.md", - "format": "prettier --write *.js src/*.js integration-tests types test-d", - "format:check": "prettier --check *.js src/*.js integration-tests" - }, - "devDependencies": { - "@jakechampion/cli-testing-library": "^1.0.0", - "brittle": "^3.5.2", - "eslint": "^9.6.0", - "get-bin-path": "^11.0.0", - "prettier": "^3.3.3", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "tsd": "^0.33.0", - "typescript": "^5.9.0", - "unified": "^11.0.5" + "format": "prettier --write 'src/**/*.ts' integration-tests types test-d", + "format:check": "prettier --check 'src/**/*.ts' integration-tests" }, "dependencies": { "@bytecodealliance/jco": "^1.7.0", @@ -66,7 +54,23 @@ "esbuild": "^0.25.0", "magic-string": "^0.30.12", "picomatch": "^4.0.3", - "regexpu-core": "^6.1.1" + "regexpu-core": "^6.4.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@jakechampion/cli-testing-library": "^1.0.0", + "@types/node": "^24.10.1", + "@types/picomatch": "^4.0.2", + "brittle": "^3.5.2", + "eslint": "^9.39.1", + "get-bin-path": "^11.0.0", + "prettier": "^3.3.3", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "tsd": "^0.33.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.48.1", + "unified": "^11.0.5" }, "peerDependencies": { "typescript": "^5.9.0" @@ -75,5 +79,10 @@ "typescript": { "optional": true } + }, + "tsd": { + "compilerOptions": { + "types": [] + } } -} \ No newline at end of file +} diff --git a/src/rsrc/trace-mapping.inject.js b/rsrc/trace-mapping.inject.js similarity index 98% rename from src/rsrc/trace-mapping.inject.js rename to rsrc/trace-mapping.inject.js index a11e9d6ddc..fafc7e8ada 100644 --- a/src/rsrc/trace-mapping.inject.js +++ b/rsrc/trace-mapping.inject.js @@ -72,7 +72,7 @@ function mapStack(raw) { const lines = String(raw).split(/\r?\n/); for (const line of lines) { - if (line.startsWith('node_modules/@fastly/js-compute/src/rsrc/trace-mapping.inject.js')) { + if (line.startsWith('node_modules/@fastly/js-compute/rsrc/trace-mapping.inject.js')) { // If the line comes from this file, skip it continue; } diff --git a/src/addSdkMetadataField.js b/src/addSdkMetadataField.js deleted file mode 100644 index f16ef00757..0000000000 --- a/src/addSdkMetadataField.js +++ /dev/null @@ -1,24 +0,0 @@ -import { metadataAdd } from '@bytecodealliance/jco'; -import { readFile, writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export async function addSdkMetadataField(wasmPath, usingAOT) { - const packageJson = await readFile(join(__dirname, '../package.json'), { - encoding: 'utf-8', - }); - - let { name, version } = JSON.parse(packageJson); - - if (usingAOT) { - name += ' (StarlingMonkey with Weval)'; - } else { - name += ' (StarlingMonkey)'; - } - - const metadata = [['sdk', [[`${name}`, version]]]]; - const wasm = await readFile(wasmPath); - const newWasm = await metadataAdd(wasm, metadata); - await writeFile(wasmPath, newWasm); -} diff --git a/src/addSdkMetadataField.ts b/src/addSdkMetadataField.ts new file mode 100644 index 0000000000..91ac5f4afa --- /dev/null +++ b/src/addSdkMetadataField.ts @@ -0,0 +1,29 @@ +import { metadataAdd } from '@bytecodealliance/jco'; +import { readFile, writeFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export async function addSdkMetadataField(wasmPath: string, usingAOT: boolean) { + const packageJson = await readFile(join(__dirname, '../package.json'), { + encoding: 'utf-8', + }); + + const { name, version } = JSON.parse(packageJson) as { + name: string; + version: string; + }; + + let sdkName: string; + if (usingAOT) { + sdkName = name + ' (StarlingMonkey with Weval)'; + } else { + sdkName = name + ' (StarlingMonkey)'; + } + + const metadata = [['sdk', [[sdkName, version]]]]; + const wasm = await readFile(wasmPath); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const newWasm: Uint8Array = await metadataAdd(wasm, metadata); + await writeFile(wasmPath, newWasm); +} diff --git a/src/bundle.js b/src/bundle.ts similarity index 97% rename from src/bundle.js rename to src/bundle.ts index c9cb67207a..88101fcb14 100644 --- a/src/bundle.js +++ b/src/bundle.ts @@ -1,14 +1,14 @@ import { rename } from 'node:fs/promises'; import { dirname, basename, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { build } from 'esbuild'; +import { build, type Plugin } from 'esbuild'; import { swallowTopLevelExportsPlugin } from './swallowTopLevelExportsPlugin.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const fastlyPlugin = { +const fastlyPlugin: Plugin = { name: 'fastly', setup(build) { build.onResolve({ filter: /^fastly:.*/ }, (args) => { @@ -161,8 +161,8 @@ export const TransactionCacheEntry = globalThis.TransactionCacheEntry; }; export async function bundle( - input, - outfile, + input: string, + outfile: string, { moduleMode = false, enableStackTraces = false }, ) { // Build output file in cwd first to build sourcemap with correct paths @@ -175,7 +175,7 @@ export async function bundle( const inject = []; if (enableStackTraces) { - inject.push(resolve(__dirname, './rsrc/trace-mapping.inject.js')); + inject.push(resolve(__dirname, '../rsrc/trace-mapping.inject.js')); } await build({ diff --git a/js-compute-runtime-cli.js b/src/cli/js-compute-runtime-cli.ts similarity index 59% rename from js-compute-runtime-cli.js rename to src/cli/js-compute-runtime-cli.ts index c05f48b040..5f9d65287f 100755 --- a/js-compute-runtime-cli.js +++ b/src/cli/js-compute-runtime-cli.ts @@ -1,33 +1,32 @@ #!/usr/bin/env node -import { parseInputs } from './src/parseInputs.js'; -import { printVersion } from './src/printVersion.js'; -import { printHelp } from './src/printHelp.js'; -import { addSdkMetadataField } from './src/addSdkMetadataField.js'; +import { parseInputs } from '../parseInputs.js'; +import { printHelp, printVersion } from '../printHelp.js'; +import { addSdkMetadataField } from '../addSdkMetadataField.js'; -const { - enableAOT, - aotCache, - enableHttpCache, - enableExperimentalHighResolutionTimeMethods, - moduleMode, - bundle, - enableStackTraces, - excludeSources, - debugIntermediateFilesDir, - wasmEngine, - input, - output, - version, - help, - env, -} = await parseInputs(process.argv.slice(2)); +const parsedInputs = await parseInputs(process.argv.slice(2)); -if (version) { +if (parsedInputs === 'version') { await printVersion(); -} else if (help) { +} else if (parsedInputs === 'help') { await printHelp(); } else { + const { + enableAOT, + aotCache, + enableHttpCache, + enableExperimentalHighResolutionTimeMethods, + moduleMode, + bundle, + enableStackTraces, + excludeSources, + debugIntermediateFilesDir, + wasmEngine, + input, + output, + env, + } = parsedInputs; + // This is a dynamic import because this import will throw an error // if it does not have a pre-compiled version of Wizer available in the platform // running the CLI. In that situation, we would still like the @@ -35,7 +34,7 @@ if (version) { // it could be that the user is using an older version of js-compute-runtime // and a newer version does not support the platform they are using. const { compileApplicationToWasm } = await import( - './src/compileApplicationToWasm.js' + '../compileApplicationToWasm.js' ); await compileApplicationToWasm({ input, diff --git a/src/compileApplicationToWasm.js b/src/compileApplicationToWasm.ts similarity index 75% rename from src/compileApplicationToWasm.js rename to src/compileApplicationToWasm.ts index ea7e77a45d..04622018f5 100644 --- a/src/compileApplicationToWasm.js +++ b/src/compileApplicationToWasm.ts @@ -1,6 +1,9 @@ import { dirname, resolve, sep, normalize } from 'node:path'; import { tmpdir, freemem } from 'node:os'; -import { spawnSync } from 'node:child_process'; +import { + spawnSync, + type SpawnSyncOptionsWithStringEncoding, +} from 'node:child_process'; import { mkdir, readFile, @@ -9,41 +12,61 @@ import { copyFile, } from 'node:fs/promises'; import { rmSync } from 'node:fs'; -import wizer from '@bytecodealliance/wizer'; import weval from '@bytecodealliance/weval'; +import wizer from '@bytecodealliance/wizer'; -import { isFile } from './isFile.js'; -import { isFileOrDoesNotExist } from './isFileOrDoesNotExist.js'; +import { isDirectory, isFile } from './isFile.js'; import { postbundle } from './postbundle.js'; import { bundle } from './bundle.js'; -import { composeSourcemaps } from './composeSourcemaps.js'; +import { composeSourcemaps, ExcludePattern } from './composeSourcemaps.js'; const maybeWindowsPath = process.platform === 'win32' - ? (path) => { + ? (path: string) => { return '//?/' + path.replace(/\\/g, '/'); } - : (path) => path; + : (path: string) => path; async function getTmpDir() { return await mkdtemp(normalize(tmpdir() + sep)); } -export async function compileApplicationToWasm({ - input, - output, - wasmEngine, - enableHttpCache = false, - enableExperimentalHighResolutionTimeMethods = false, - enableAOT = false, - aotCache = '', - enableStackTraces, - excludeSources, - debugIntermediateFilesDir, - moduleMode = false, - doBundle = false, - env, -}) { +export type CompileApplicationToWasmParams = { + input: string; + output: string; + wasmEngine: string; + enableHttpCache: boolean; + enableExperimentalHighResolutionTimeMethods: boolean; + enableAOT: boolean; + aotCache: string; + enableStackTraces: boolean; + excludeSources: boolean; + debugIntermediateFilesDir: string | undefined; + moduleMode: boolean; + doBundle: boolean; + env: Record; +}; + +export async function compileApplicationToWasm( + params: CompileApplicationToWasmParams, +) { + const { + output, + wasmEngine, + enableHttpCache = false, + enableExperimentalHighResolutionTimeMethods = false, + enableAOT = false, + aotCache = '', + enableStackTraces, + excludeSources, + debugIntermediateFilesDir, + moduleMode = false, + doBundle = false, + env, + } = params; + + let input = params.input; + try { if (!(await isFile(input))) { console.error( @@ -60,7 +83,9 @@ export async function compileApplicationToWasm({ try { await readFile(input, { encoding: 'utf-8' }); - } catch (error) { + } catch (maybeError: unknown) { + const error = + maybeError instanceof Error ? maybeError : new Error(String(maybeError)); console.error( `Error: Failed to open the \`input\` (${input})`, error.message, @@ -81,28 +106,29 @@ export async function compileApplicationToWasm({ ); process.exit(1); } - try { - await mkdir(dirname(output), { - recursive: true, - }); - } catch (error) { - console.error( - `Error: Failed to create the \`output\` (${output}) directory`, - error.message, - ); - process.exit(1); - } + // If output exists already, make sure it's not a directory + // (we'll try to overwrite it if it's a file) try { - if (!(await isFileOrDoesNotExist(output))) { + if (await isDirectory(output)) { console.error( - `Error: The \`output\` path does not point to a file: ${output}`, + `Error: The \`output\` path points to a directory: ${output}`, ); process.exit(1); } - } catch (error) { + } catch { + // Output doesn't exist + } + + try { + await mkdir(dirname(output), { + recursive: true, + }); + } catch (maybeError: unknown) { + const error = + maybeError instanceof Error ? maybeError : new Error(String(maybeError)); console.error( - `Error: Failed to check whether the \`output\` (${output}) is a file path`, + `Error: Failed to create the \`output\` (${dirname(output)}) directory: ${output}`, error.message, ); process.exit(1); @@ -116,7 +142,11 @@ export async function compileApplicationToWasm({ await mkdir(debugIntermediateFilesDir, { recursive: true, }); - } catch (error) { + } catch (maybeError: unknown) { + const error = + maybeError instanceof Error + ? maybeError + : new Error(String(maybeError)); console.error( `Error: Failed to create the \`debug-intermediate-files\` (${debugIntermediateFilesDir}) directory`, error.message, @@ -141,7 +171,11 @@ export async function compileApplicationToWasm({ moduleMode, enableStackTraces, }); - } catch (error) { + } catch (maybeError: unknown) { + const error = + maybeError instanceof Error + ? maybeError + : new Error(String(maybeError)); console.error(`Error:`, error.message); process.exit(1); } @@ -192,7 +226,10 @@ export async function compileApplicationToWasm({ if (enableStackTraces) { // Compose source maps const replaceSourceMapToken = '__FINAL_SOURCE_MAP__'; - let excludePatterns = ['forbid-entry:/**', 'node_modules/**']; + let excludePatterns: ExcludePattern[] = [ + 'forbid-entry:/**', + 'node_modules/**', + ]; if (excludeSources) { excludePatterns = [() => true]; } @@ -251,17 +288,18 @@ export async function compileApplicationToWasm({ ENABLE_EXPERIMENTAL_HIGH_RESOLUTION_TIME_METHODS: enableExperimentalHighResolutionTimeMethods ? '1' : '0', ENABLE_EXPERIMENTAL_HTTP_CACHE: enableHttpCache ? '1' : '0', - RUST_MIN_STACK: Math.max(8 * 1024 * 1024, Math.floor(freemem() * 0.1)), + RUST_MIN_STACK: String( + Math.max(8 * 1024 * 1024, Math.floor(freemem() * 0.1)), + ), }, - }; + } satisfies SpawnSyncOptionsWithStringEncoding; try { if (!doBundle) { - // assert(moduleMode); if (enableAOT) { const wevalBin = await weval(); - let wevalProcess = spawnSync( + const wevalProcess = spawnSync( `"${wevalBin}"`, [ 'weval', @@ -279,7 +317,7 @@ export async function compileApplicationToWasm({ } process.exitCode = wevalProcess.status; } else { - let wizerProcess = spawnSync( + const wizerProcess = spawnSync( `"${wizer}"`, [ '--allow-wasi', @@ -302,7 +340,7 @@ export async function compileApplicationToWasm({ if (enableAOT) { const wevalBin = await weval(); - let wevalProcess = spawnSync( + const wevalProcess = spawnSync( `"${wevalBin}"`, [ 'weval', @@ -321,7 +359,7 @@ export async function compileApplicationToWasm({ } process.exitCode = wevalProcess.status; } else { - let wizerProcess = spawnSync( + const wizerProcess = spawnSync( `"${wizer}"`, [ '--inherit-env=true', @@ -341,12 +379,14 @@ export async function compileApplicationToWasm({ process.exitCode = wizerProcess.status; } } - } catch (error) { + } catch (maybeError: unknown) { + const error = + maybeError instanceof Error ? maybeError : new Error(String(maybeError)); throw new Error( `Error: Failed to compile JavaScript to Wasm:\n${error.message}`, ); } finally { - if (doBundle) { + if (doBundle && tmpDir != null) { rmSync(tmpDir, { recursive: true }); } } diff --git a/src/composeSourcemaps.js b/src/composeSourcemaps.js deleted file mode 100644 index ee3f7895df..0000000000 --- a/src/composeSourcemaps.js +++ /dev/null @@ -1,48 +0,0 @@ -import { readFile } from 'node:fs/promises'; -import remapping from '@jridgewell/remapping'; -import { TraceMap } from '@jridgewell/trace-mapping'; -import picomatch from 'picomatch'; - -async function readSourcemap(e) { - const sourceMapJson = await readFile(e.s, { encoding: 'utf-8' }); - return JSON.parse(sourceMapJson); -} - -export async function composeSourcemaps(sourceMaps, excludePatterns = []) { - const top = new TraceMap(await readSourcemap(sourceMaps.pop())); - - const priors = {}; - for (const sourceMap of sourceMaps) { - priors[sourceMap.f] = await readSourcemap(sourceMap); - } - - // Loader: given a source name from mapXZ, return a TraceMap for that source (if any). - const loader = (source) => { - const m = priors[source]; - if (!m) return null; // no earlier map for this source - return new TraceMap(m); - }; - - const raw = JSON.parse(remapping(top, loader, false).toString()); - - return JSON.stringify(stripSourcesContent(raw, excludePatterns)); -} - -function stripSourcesContent(map, excludes) { - const matchers = excludes.map((p) => - typeof p === 'string' ? picomatch(p) : p, - ); - - for (let i = 0; i < map.sources.length; i++) { - const src = map.sources[i]; - - // [Windows] normalize slashes - const normalized = src.replace(/\\/g, '/'); - - if (matchers.some((fn) => fn(normalized))) { - map.sourcesContent[i] = null; - } - } - - return map; -} diff --git a/src/composeSourcemaps.ts b/src/composeSourcemaps.ts new file mode 100644 index 0000000000..8910d4f1e8 --- /dev/null +++ b/src/composeSourcemaps.ts @@ -0,0 +1,73 @@ +import { readFile } from 'node:fs/promises'; +import remapping, { + SourceMap, + type SourceMapInput, + type SourceMapLoader, +} from '@jridgewell/remapping'; +import { TraceMap } from '@jridgewell/trace-mapping'; +import picomatch from 'picomatch'; + +export type ExcludePattern = string | ((file: string) => boolean); + +export type SourceMapInfo = { + f: string; // Filename + s: string; // Sourcemap filename +}; + +async function readSourcemap(e: SourceMapInfo) { + const sourceMapJson = await readFile(e.s, { encoding: 'utf-8' }); + return JSON.parse(sourceMapJson) as SourceMapInput; +} + +export async function composeSourcemaps( + sourceMaps: SourceMapInfo[], + excludePatterns: ExcludePattern[] = [], +) { + const topSourceMap = sourceMaps.pop(); + if (topSourceMap == null) { + throw new Error( + 'Unexpected: composeSourcemaps received empty sourceMaps array.', + ); + } + + const top = new TraceMap(await readSourcemap(topSourceMap)); + + const priors: Record = {}; + for (const sourceMap of sourceMaps) { + priors[sourceMap.f] = await readSourcemap(sourceMap); + } + + // Loader: given a source name from mapXZ, return a TraceMap for that source (if any). + const loader: SourceMapLoader = (source) => { + const m = priors[source]; + if (!m) return null; // no earlier map for this source + return new TraceMap(m); + }; + + const raw = JSON.parse(remapping(top, loader, false).toString()) as SourceMap; + + return JSON.stringify(stripSourcesContent(raw, excludePatterns)); +} + +function stripSourcesContent(map: SourceMap, excludes: ExcludePattern[]) { + if (map.sourcesContent == null) { + return map; + } + + const matchers = excludes.map((p) => + typeof p === 'string' ? picomatch(p) : p, + ); + + for (let i = 0; i < map.sources.length; i++) { + const src = map.sources[i]; + + // [Windows] normalize slashes + const normalized = src?.replace(/\\/g, '/'); + + if (normalized == null || matchers.some((fn) => fn(normalized))) { + map.sourcesContent[i] = null; + } + } + + return map; +} diff --git a/src/env.js b/src/env.ts similarity index 89% rename from src/env.js rename to src/env.ts index 3e4fe6f232..ec66e8d1c0 100644 --- a/src/env.js +++ b/src/env.ts @@ -1,5 +1,5 @@ // env.js -function parseEnvPair(pair) { +function parseEnvPair(pair: string) { const trimmedPair = pair.trim(); // If no '=', treat as a variable to inherit @@ -14,7 +14,7 @@ function parseEnvPair(pair) { return [trimmedPair, value]; } - const matches = trimmedPair.match(/^([^=]+)=(.*)$/); + const matches = /^([^=]+)=(.*)$/.exec(trimmedPair); if (!matches) { throw new Error( `Invalid environment variable format: ${trimmedPair}\nMust be in format KEY=VALUE or an existing environment variable name`, @@ -33,8 +33,8 @@ function parseEnvPair(pair) { return [key, value]; } -function parseEnvString(envString) { - const result = {}; +function parseEnvString(envString: string) { + const result: Record = {}; // Split on unescaped commas and filter out empty strings const pairs = envString @@ -56,6 +56,8 @@ function parseEnvString(envString) { } export class EnvParser { + env: Record; + constructor() { this.env = {}; } @@ -65,7 +67,7 @@ export class EnvParser { * or names of environment variables to inherit * @param {string} value - The environment variable string to parse */ - parse(value) { + parse(value: string) { if (!value) { throw new Error( 'Invalid environment variable format: \nMust be in format KEY=VALUE or an existing environment variable name', diff --git a/src/isFile.js b/src/isFile.js deleted file mode 100644 index ffbe2dc655..0000000000 --- a/src/isFile.js +++ /dev/null @@ -1,6 +0,0 @@ -import { stat } from 'node:fs/promises'; - -export async function isFile(path) { - const stats = await stat(path); - return stats.isFile(); -} diff --git a/src/isFile.ts b/src/isFile.ts new file mode 100644 index 0000000000..207be7dac7 --- /dev/null +++ b/src/isFile.ts @@ -0,0 +1,11 @@ +import { stat } from 'node:fs/promises'; + +export async function isFile(path: string) { + const stats = await stat(path); + return stats.isFile(); +} + +export async function isDirectory(path: string) { + const stats = await stat(path); + return stats.isDirectory(); +} diff --git a/src/isFileOrDoesNotExist.js b/src/isFileOrDoesNotExist.js deleted file mode 100644 index f26afef137..0000000000 --- a/src/isFileOrDoesNotExist.js +++ /dev/null @@ -1,13 +0,0 @@ -import { stat } from 'node:fs/promises'; - -export async function isFileOrDoesNotExist(path) { - try { - const stats = await stat(path); - return stats.isFile(); - } catch (error) { - if (error instanceof Error && error.code === 'ENOENT') { - return true; - } - throw error; - } -} diff --git a/src/parseInputs.js b/src/parseInputs.ts similarity index 83% rename from src/parseInputs.js rename to src/parseInputs.ts index 1f85670c90..61e80286b9 100644 --- a/src/parseInputs.js +++ b/src/parseInputs.ts @@ -1,10 +1,28 @@ import { fileURLToPath } from 'node:url'; import { dirname, join, isAbsolute } from 'node:path'; -import { unknownArgument } from './unknownArgument.js'; -import { tooManyEngines } from './tooManyEngines.js'; +import { tooManyEngines, unknownArgument } from './printHelp.js'; import { EnvParser } from './env.js'; -export async function parseInputs(cliInputs) { +export type ParsedInputs = + | 'help' + | 'version' + | { + enableAOT: boolean; + aotCache: string; + enableHttpCache: boolean; + enableExperimentalHighResolutionTimeMethods: boolean; + moduleMode: boolean; + bundle: boolean; + enableStackTraces: boolean; + excludeSources: boolean; + debugIntermediateFilesDir: string | undefined; + wasmEngine: string; + input: string; + output: string; + env: Record; + }; + +export async function parseInputs(cliInputs: string[]): Promise { const __dirname = dirname(fileURLToPath(import.meta.url)); let enableHttpCache = false; @@ -26,14 +44,13 @@ export async function parseInputs(cliInputs) { const envParser = new EnvParser(); - // eslint-disable-next-line no-cond-assign loop: while ((cliInput = cliInputs.shift())) { switch (cliInput) { case '--': { break loop; } case '--env': { - const value = cliInputs.shift(); + let value = cliInputs.shift(); if (!value) { console.error('Error: --env requires a KEY=VALUE pair'); process.exit(1); @@ -81,11 +98,11 @@ export async function parseInputs(cliInputs) { } case '-V': case '--version': { - return { version: true }; + return 'version'; } case '-h': case '--help': { - return { help: true }; + return 'help'; } case '--starlingmonkey': { break; @@ -106,6 +123,10 @@ export async function parseInputs(cliInputs) { tooManyEngines(); } const value = cliInputs.shift(); + if (value == null) { + console.error('Error: --engine-wasm requires a value'); + process.exit(1); + } customEngineSet = true; if (isAbsolute(value)) { wasmEngine = value; @@ -116,6 +137,10 @@ export async function parseInputs(cliInputs) { } case '--aot-cache': { const value = cliInputs.shift(); + if (value == null) { + console.error('Error: --aot-cache requires a value'); + process.exit(1); + } if (isAbsolute(value)) { aotCache = value; } else { @@ -133,6 +158,10 @@ export async function parseInputs(cliInputs) { } case '--debug-intermediate-files': { const value = cliInputs.shift(); + if (value == null) { + console.error('Error: --aot-cache requires a value'); + process.exit(1); + } if (isAbsolute(value)) { debugIntermediateFilesDir = value; } else { diff --git a/src/postbundle.js b/src/postbundle.ts similarity index 98% rename from src/postbundle.js rename to src/postbundle.ts index 86521129e7..790ddd95fd 100644 --- a/src/postbundle.js +++ b/src/postbundle.ts @@ -6,8 +6,8 @@ import MagicString from 'magic-string'; import { simple as simpleWalk } from 'acorn-walk'; export async function postbundle( - input, - outfile, + input: string, + outfile: string, { moduleMode = false, enableStackTraces = false }, ) { const source = await readFile(input, { encoding: 'utf8' }); @@ -28,7 +28,7 @@ export async function postbundle( sourceType: moduleMode ? 'module' : 'script', }); - const precompileCalls = []; + const precompileCalls: string[] = []; simpleWalk(ast, { Literal(node) { if (!node.regex) return; diff --git a/src/printHelp.js b/src/printHelp.ts similarity index 65% rename from src/printHelp.js rename to src/printHelp.ts index 52bfdf492d..c1c85d0592 100644 --- a/src/printHelp.js +++ b/src/printHelp.ts @@ -1,6 +1,8 @@ -import { basename } from 'node:path'; +import { readFile } from 'node:fs/promises'; +import { basename, dirname, join } from 'node:path'; import { argv } from 'node:process'; -import { printVersion } from './printVersion.js'; +import { fileURLToPath } from 'node:url'; +const __dirname = dirname(fileURLToPath(import.meta.url)); export async function printHelp() { await printVersion(); @@ -34,3 +36,31 @@ ARGS: The file path to write the output Wasm module to [default: bin/main.wasm] `); } + +export async function printVersion() { + const packageJson = await readFile(join(__dirname, '../package.json'), { + encoding: 'utf-8', + }); + const version = (JSON.parse(packageJson) as { version: string }).version; + console.log(`${basename(argv[1])} ${version}`); +} + +export function tooManyEngines() { + console.error(`error: The argument '--engine-wasm ' was provided more than once, but cannot be used multiple times + +USAGE: + js-compute-runtime --engine-wasm + +For more information try --help`); + process.exit(1); +} + +export function unknownArgument(cliInput: string) { + console.error(`error: Found argument '${cliInput}' which wasn't expected, or isn't valid in this context + +USAGE: + js-compute-runtime [FLAGS] [OPTIONS] [ARGS] + +For more information try --help`); + process.exit(1); +} diff --git a/src/printVersion.js b/src/printVersion.js deleted file mode 100644 index de9e72fb46..0000000000 --- a/src/printVersion.js +++ /dev/null @@ -1,13 +0,0 @@ -import { readFile } from 'node:fs/promises'; -import { basename, dirname, join } from 'node:path'; -import { argv } from 'node:process'; -import { fileURLToPath } from 'node:url'; -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export async function printVersion() { - const packageJson = await readFile(join(__dirname, '../package.json'), { - encoding: 'utf-8', - }); - const version = JSON.parse(packageJson).version; - console.log(`${basename(argv[1])} ${version}`); -} diff --git a/src/swallowTopLevelExportsPlugin.js b/src/swallowTopLevelExportsPlugin.ts similarity index 81% rename from src/swallowTopLevelExportsPlugin.js rename to src/swallowTopLevelExportsPlugin.ts index fbe7a1f541..20157bc532 100644 --- a/src/swallowTopLevelExportsPlugin.js +++ b/src/swallowTopLevelExportsPlugin.ts @@ -1,7 +1,14 @@ import { dirname, isAbsolute, resolve } from 'node:path'; +import { type Plugin } from 'esbuild'; -export function swallowTopLevelExportsPlugin(opts) { - const { entry } = opts; +export type SwallowTopLevelExportsPluginParams = { + entry?: string; +}; + +export function swallowTopLevelExportsPlugin( + opts?: SwallowTopLevelExportsPluginParams, +) { + const { entry } = opts ?? {}; const name = 'swallow-top-level-exports'; const namespace = 'swallow-top-level'; @@ -33,5 +40,5 @@ export function swallowTopLevelExportsPlugin(opts) { }; }); }, - }; + } satisfies Plugin; } diff --git a/src/tooManyEngines.js b/src/tooManyEngines.js deleted file mode 100644 index 34e1d0324e..0000000000 --- a/src/tooManyEngines.js +++ /dev/null @@ -1,9 +0,0 @@ -export function tooManyEngines() { - console.error(`error: The argument '--engine-wasm ' was provided more than once, but cannot be used multiple times - -USAGE: - js-compute-runtime --engine-wasm - -For more information try --help`); - process.exit(1); -} diff --git a/src/types/modules.d.ts b/src/types/modules.d.ts new file mode 100644 index 0000000000..e9b6174a11 --- /dev/null +++ b/src/types/modules.d.ts @@ -0,0 +1,9 @@ +declare module '@bytecodealliance/wizer' { + const wizer: string; + export default wizer; +} + +declare module '@bytecodealliance/weval' { + function getWeval(): Promise; + export default getWeval; +} diff --git a/src/unknownArgument.js b/src/unknownArgument.js deleted file mode 100644 index 68d5071a2e..0000000000 --- a/src/unknownArgument.js +++ /dev/null @@ -1,9 +0,0 @@ -export function unknownArgument(cliInput) { - console.error(`error: Found argument '${cliInput}' which wasn't expected, or isn't valid in this context - -USAGE: - js-compute-runtime [FLAGS] [OPTIONS] [ARGS] - -For more information try --help`); - process.exit(1); -} diff --git a/tests/wpt-harness/build-wpt-runtime.sh b/tests/wpt-harness/build-wpt-runtime.sh index 28fe128010..0f268a7768 100755 --- a/tests/wpt-harness/build-wpt-runtime.sh +++ b/tests/wpt-harness/build-wpt-runtime.sh @@ -10,4 +10,4 @@ inputs=( ) cat "${inputs[@]}" > "${script_dir}/wpt-test-runner.js" -node "${script_dir}/../../js-compute-runtime-cli.js" "${script_dir}/wpt-test-runner.js" "$@" wpt-runtime.wasm +node "${script_dir}/../../dist/cli/js-compute-runtime-cli.js" "${script_dir}/wpt-test-runner.js" "$@" wpt-runtime.wasm diff --git a/tsconfig.json b/tsconfig.json index 8056264786..6cfce1b4cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,20 @@ { "compilerOptions": { "target": "es2022", - "module": "es2022", + "module": "nodenext", "lib": ["es2022"], - "outDir": "./build", + "outDir": "./dist", + "rootDir": "./src", "strict": true, - "moduleResolution": "node", - "skipLibCheck": false, + "moduleResolution": "nodenext", + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "types": [] + "allowJs": true }, "include": [ - "types/index.d.ts" + "src/**/*.ts", + ], + "exclude": [ + "node_modules" ] }