diff --git a/.changeset/config.json b/.changeset/config.json index 3244c8d..e66ca0c 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,5 +1,5 @@ { - "$schema": "https://unpkg.com/@changesets/config@2.1.1/schema.json", + "$schema": "https://unpkg.com/@changesets/config@2.31.0/schema.json", "changelog": ["changesets-changelog-clean", { "repo": "duniul/clean-modules" }], "commit": false, "fixed": [], diff --git a/.cleanmodules-default b/.cleanmodules-default index 36bc3c2..65dcbb9 100644 --- a/.cleanmodules-default +++ b/.cleanmodules-default @@ -5,13 +5,9 @@ # test __tests__/ +__mocks__/ test/ tests/ -powered-test/ - -# docs -docs/ -doc/ # IDE .idea/ @@ -33,9 +29,27 @@ coverage/ # git .git/ +# ai +.claude/ +.cursor/ +.copilot/ +.codeium/ +.aider/ +.windsurf/ +.bolt/ +.codex/ +.continue/ + # other website/ .husky/ +.turbo/ +.pnpm-store/ +.kotlin/ +.gradle/ +.changeset/ +.storybook/ +.devcontainer/ # # FILE EXTENSIONS @@ -53,7 +67,6 @@ website/ # docs/text *.@(md|mkd|markdown|mdown) *.todo -*.mustache *.@(adoc|asciidoc) *.DOCS @@ -61,6 +74,7 @@ website/ # include .ts files but not .d.ts \!(*.d).ts *.coffee +*.flow # compressed *.zip @@ -68,11 +82,17 @@ website/ *.rar *.tar *.tgz +*.gz +*.xz +*.bz2 # images *.@(jpg|jpeg) *.png *.gif +*.bmp +*.ico +*.webp # compiled *.h @@ -81,6 +101,8 @@ website/ *.cpp *.o *.mk +*.hh +*.cc # other *.log @@ -94,6 +116,15 @@ website/ *.vcxproj*(.*) *.orig *.rej +*.lockfile +*.lcov +*.bak +*.tmp +*.temp +*.gyp +*.gypi +*.1 +*.un~ # # FILES @@ -116,13 +147,15 @@ circle.yml # Mac OS .DS_Store +._* # Windows Desktop.ini Thumbs.db # linters -.eslintrc*(.*) +*.eslintrc*(.*) +*.eslintcache eslint.config.@(cjs|js|mjs) stylelint.config.js .stylelintrc*(.*) @@ -131,14 +164,19 @@ htmllint.js .jshintrc*(.*) .lint tslint.json -.lintignore -.jshintignore -.eslintignore - -# prettier +biome.json +.oxlintrc.@(json|jsonc) +oxlint.config.ts +.oxfmtrc.@(json|jsonc) +oxfmt.config.ts .prettierrc*(.*) prettier.config.js +# ai +.cursorrules +.clinerules +.windsurfrules + # testing \!(utils)/test.js jest.config.js @@ -146,7 +184,7 @@ karma.conf.js wallaby.js wallaby.conf.js .coveralls.yml -.airtap.yml +vitest.config.* # docs readme*(.*) @@ -158,17 +196,23 @@ contributing*(.*) notice*(.*) copying*(.*) +# docker +docker-compose.@(yml|yaml) + +# editors +*.sublime-project +*.sublime-workspace + # licenses -licen@(s|c)e -licen@(s|c)e-mit -licen@(s|c)e.txt -licen@(s|c)e-mit.txt +licen@(s|c)e* +*.licen@(s|c)e* +LICEN@(S|C)E* +*.LICEN@(S|C)E* +ThirdPartyNotice*(.*) +CopyrightNotice*(.*) # typescript -.tsbuildinfo - -# npm -.npmrc +*.tsbuildinfo # ignore files .*ignore @@ -178,39 +222,47 @@ yarn.lock .yarnclean .yarn-metadata.json +# pnpm +pnpm-lock.yaml +pnpm-workspace.yaml + # git .gitmodules .gitattributes # configs -.npmrc -.babelrc -.editorconfig -.tern-project -.flowconfig -.documentup.json -.yo-rc.json +*.npmrc +*.nvmrc +*.babelrc +*.editorconfig +*.flowconfig +*.yo-rc.json _config.yml bower.json renovate.json .vimrc*(.*) -.zuul.yml .istanbul.yml .nycrc -.jscsrc +jsconfig.json +tsconfig.json +turbo.json +nx.json +deno.@(json|jsonc) # versioning .bmp.yml .release-it.@(cjs|js|json|toml|ts|yaml|yml) +# lint-staged +lint-staged.config.* +.lintstagedrc*(.*) + # other .dir-locals.el -binding.gyp -cakefile -component.json composer.json -contributors -.jamignore .codecov.yml pom.xml -OSSMETADATA +.browserslistrc +.browserslist +.node-version +.tool-versions diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index 0ec5c66..8738416 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -7,13 +7,17 @@ on: paths-ignore: - '.vscode/**' - 'images/**' + - '.gitattributes' + - '.gitignore' - '*.md' + - 'LICENSE' concurrency: ${{ github.workflow }}-${{ github.ref }} permissions: contents: write pull-requests: write + id-token: write jobs: main: @@ -21,17 +25,15 @@ jobs: runs-on: ubuntu-latest steps: - name: ๐Ÿ— Setup repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: ๐Ÿ— Setup pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 + uses: pnpm/action-setup@v6 - name: ๐Ÿ— Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: - node-version: 18.x + node-version: 22.x cache: pnpm - name: ๐Ÿ“ฆ Install dependencies @@ -47,4 +49,3 @@ jobs: publish: pnpm run release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ce541c4..7a9257f 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -6,35 +6,42 @@ on: paths-ignore: - '.vscode/**' - 'images/**' + - '.gitattributes' + - '.gitignore' - '*.md' + - 'LICENSE' permissions: contents: read jobs: build: - name: ๐Ÿงฑ Build + name: ๐Ÿงฑ Check and build runs-on: ubuntu-latest permissions: contents: read steps: - name: ๐Ÿ— Setup repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: ๐Ÿ— Setup pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 + uses: pnpm/action-setup@v6 - name: ๐Ÿ— Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: - node-version: 18.x + node-version: 22.x cache: pnpm - name: ๐Ÿ“ฆ Install dependencies run: pnpm install + - name: ๐Ÿงน Stylecheck project + run: pnpm stylecheck + + - name: ๐Ÿงน Typecheck project + run: pnpm typecheck + - name: ๐Ÿงฑ Build project run: pnpm build @@ -43,23 +50,20 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [14.x, 18.x] runs-on: ${{ matrix.os }} steps: - name: ๐Ÿ— Setup repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: ๐Ÿ— Setup pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 + uses: pnpm/action-setup@v6 - name: ๐Ÿ— Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: - node-version: 18.x + node-version: 22.x cache: pnpm - name: ๐Ÿ“ฆ Install dependencies diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..3fe3b15 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24.13.0 diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..44fee27 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,18 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": ["**/node_modules", "**/dist", "**/.history", "**/tsconfig.tsbuildinfo"], + "printWidth": 110, + "trailingComma": "es5", + "jsxSingleQuote": true, + "singleQuote": true, + "arrowParens": "avoid", + "quoteProps": "consistent", + "sortPackageJson": { + "enabled": true, + "sortScripts": false + }, + "sortImports": { + "enabled": true, + "newlinesBetween": false + } +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..d161d2f --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,118 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "ignorePatterns": ["**/node_modules", "**/dist", "**/.history", "**/tsconfig.tsbuildinfo"], + "plugins": ["eslint", "import", "jsdoc", "node", "oxc", "promise", "typescript", "unicorn"], + "env": { + "builtin": true, + "node": true + }, + "categories": { + "correctness": "error", + "nursery": "error", + "pedantic": "error", + "perf": "error", + "restriction": "error", + "style": "error", + "suspicious": "error" + }, + "rules": { + "capitalized-comments": "off", + "consistent-type-definitions": ["error", "type"], + "explicit-length-check": "off", + "exports-last": "off", + "filename-case": ["error", { "case": "camelCase" }], + "func-style": "off", + "id-length": "off", + "import/consistent-type-specifier-style": "off", + "import/group-exports": "off", + "import/no-named-export": "off", + "import/no-namespace": "off", + "import/no-nodejs-modules": "off", + "import/no-relative-parent-imports": "off", + "import/prefer-default-export": "off", + "init-declarations": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-param": "off", + "jsdoc/require-returns-type": "off", + "jsdoc/require-returns": "off", + "max-lines-per-function": "off", + "max-lines": "off", + "max-params": "off", + "max-statements": "off", + "no-array-callback-reference": "off", + "no-async-await": "off", + "no-continue": "off", + "no-duplicate-imports": ["error", { "allowSeparateTypeImports": true }], + "no-implicit-coercion": "off", + "no-inline-comments": "off", + "no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreDefaultValues": true }], + "no-nested-ternary": "off", + "no-null": "off", + "no-optional-chaining": "off", + "no-plusplus": "off", + "no-rest-spread-properties": "off", + "no-ternary": "off", + "no-undefined": "off", + "operator-assignment": "off", + "prefer-await-to-then": "off", + "prefer-destructuring": "off", + "prefer-template": "off", + "prefer-ternary": "off", + "promise/avoid-new": "off", + "require-unicode-regexp": "off", + "sort-imports": "off", + "sort-keys": "off", + "typescript/explicit-member-accessibility": ["error", { "accessibility": "no-public" }], + "unicorn/no-nested-ternary": "off" + }, + "overrides": [ + { + "files": ["*.config.ts", "*.config.*.ts"], + "rules": { + "import/no-default-export": "off" + } + }, + { + "files": ["src/cli/**/*.ts"], + "rules": { + "new-cap": "off", + "no-console": "off" + } + }, + { + "files": ["__mocks__/fs/**/*"], + "rules": { + "unicorn/prefer-module": "off", + "import/no-commonjs": "off" + } + }, + { + "files": ["bin/**/*"], + "rules": { + "import/no-unassigned-import": "off" + } + }, + { + "files": ["*.test.ts", "__test__/**"], + "plugins": ["vitest"], + "rules": { + "vitest/no-hooks": "off", + "vitest/no-importing-vitest-globals": "off", + "vitest/prefer-to-be-truthy": "off", + "vitest/prefer-to-be-falsy": "off", + "vitest/max-expects": "off", + "vitest/prefer-expect-assertions": [ + "error", + { + "onlyFunctionsWithAsyncKeyword": true, + "onlyFunctionsWithExpectInCallback": true, + "onlyFunctionsWithExpectInLoop": true + } + ], + "vitest/valid-title": "off", + "vitest/prefer-called-once": "off", + "no-magic-numbers": "off" + } + } + ] +} diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 9a759a8..0000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 100, - "arrowParens": "avoid" -} diff --git a/.vscode/settings.json b/.vscode/settings.json index b7f65a6..43cfe58 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { // Make VSCode warn about same commit lengths for everyone - "git.inputValidation": "warn", + "git.inputValidation": true, "git.inputValidationLength": 100, "git.inputValidationSubjectLength": 72, @@ -25,27 +25,27 @@ // Treat .cleanmodules files as ignore files "files.associations": { - ".cleanmodules*": "ignore", + ".cleanmodules*": "ignore" }, // Ensure we use the same formatter for files "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[markdown]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "oxc.oxc-vscode", "files.trimTrailingWhitespace": false // Markdown uses trailing whitespace for line breaks }, // Use same TypeScript version as repo rather than local user version - "typescript.tsdk": "node_modules/typescript/lib", + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 820dbad..a46b69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # clean-modules +## 4.0.0 + +### Major Changes + +- **BREAKING** `analyze()` now returns `{ files, failures }` instead of an array of per-file results. _[`bc65618`](https://github.com/duniul/clean-modules/commit/bc65618725939650805dbee32a1845856a5360c6) [@duniul](https://github.com/duniul)_ +- **BREAKING** Package is now ESM only. CLI usage works the same as before, but breaks CJS usage of the programmatic API. _[`ac6de03`](https://github.com/duniul/clean-modules/commit/ac6de030f64469d64916c7b64fbff5b81721c047) [@duniul](https://github.com/duniul)_ +- **BREAKING** Minimum supported Node version is now v22. _[`6fd8cde`](https://github.com/duniul/clean-modules/commit/6fd8cde1fe1db521062afb0f3c6ffec4a63e6cca) [@duniul](https://github.com/duniul)_ + +### Minor Changes + +- Update `picomatch` dependency to v4. _[`00c3cd6`](https://github.com/duniul/clean-modules/commit/00c3cd60a0dd4072d40aedf0ad52e46b50f1ebdf) [@duniul](https://github.com/duniul)_ +- Surface non-fatal filesystem failures instead of swallowing them silently. _[`bc65618`](https://github.com/duniul/clean-modules/commit/bc65618725939650805dbee32a1845856a5360c6) [@duniul](https://github.com/duniul)_ +- Add new `--fail-on-error` flag which exits with a non-zero status code when any failure is recorded. Exit code stays `0` by default, matching prior behavior. _[`bc65618`](https://github.com/duniul/clean-modules/commit/bc65618725939650805dbee32a1845856a5360c6) [@duniul](https://github.com/duniul)_ + +### Patch Changes + +- Remove potentially unsafe `doc/` and `docs/` from default patterns. _[`293e149`](https://github.com/duniul/clean-modules/commit/293e1492305175e4f2ed54f434d78dc3be4c1d30) [@duniul](https://github.com/duniul)_ +- Remove `pretty-bytes`, `pretty-ms`, `supports-color`, and `clipanion` dependencies. _[`d428e34`](https://github.com/duniul/clean-modules/commit/d428e34f2f758c4ee1e18a93a923c7cc0997cd00) [`61b97e7`](https://github.com/duniul/clean-modules/commit/61b97e7d96e124c641f9c16a0e84f7deb86bbfd2) [`dfb87bc`](https://github.com/duniul/clean-modules/commit/dfb87bcea37ae6916ac2404dcaf73e13583d058b) [@duniul](https://github.com/duniul)_ +- Replace `clipanion` with Node's built-in `parseArgs` for CLI args parsing. _[`61b97e7`](https://github.com/duniul/clean-modules/commit/61b97e7d96e124c641f9c16a0e84f7deb86bbfd2) [`dfb87bc`](https://github.com/duniul/clean-modules/commit/dfb87bcea37ae6916ac2404dcaf73e13583d058b) [@duniul](https://github.com/duniul)_ +- Clearly indicate which steps were skipped in `--dry-run` output. _[`c3bd4b2`](https://github.com/duniul/clean-modules/commit/c3bd4b21bdbd5ba8a311994a05109da8e6808f10) [@duniul](https://github.com/duniul)_ +- Set `"sideEffects": false` in package.json. _[`f67c2c9`](https://github.com/duniul/clean-modules/commit/f67c2c9fe7b3b4e461b2ea38823f439e4cba5e8e) [@duniul](https://github.com/duniul)_ +- Fix `repository` definition in package.json. _[`47b01ae`](https://github.com/duniul/clean-modules/commit/47b01ae18030e89ea4d6af90bb9020c0556dbf69) [@duniul](https://github.com/duniul)_ +- Fix the default hint shown for the `--directory` option. _[`b44d0ef`](https://github.com/duniul/clean-modules/commit/b44d0effe6679f8be0b74f145ffaa6b275df328d) [@duniul](https://github.com/duniul)_ +- Fix globs not starting with `/` being incorrectly prefixed with an extra `**`. _[`34a3b27`](https://github.com/duniul/clean-modules/commit/34a3b278ded6b9f87a923e6a761295bc4f1356ec) [@duniul](https://github.com/duniul)_ +- Ensure `--keep-empty` is respected by `clean` command. _[`695f83e`](https://github.com/duniul/clean-modules/commit/695f83eda9921a23e886040329f0e546e2df6c36) [@duniul](https://github.com/duniul)_ +- Skip confirmation prompt in CI/without TTY, require --yes or -y. _[`b0a1838`](https://github.com/duniul/clean-modules/commit/b0a183847f98cab08d3da6c3742c1ef114d0214a) [@duniul](https://github.com/duniul)_ +- Avoid parsing default globs twice in `analyze`. _[`6f878df`](https://github.com/duniul/clean-modules/commit/6f878df7290afdc5abd5e2a784de51582d912e5b) [@duniul](https://github.com/duniul)_ +- Avoid redundant `readdir` calls when removing empty parent directories. _[`540c372`](https://github.com/duniul/clean-modules/commit/540c37287b97ada49f7ce3eb1dc72b438ca3178f) [@duniul](https://github.com/duniul)_ +- Update minor dependency versions. _[`7491637`](https://github.com/duniul/clean-modules/commit/749163777904a4e09f88bf4eb09adae05dbd8234) [@duniul](https://github.com/duniul)_ +- Update the default file patterns. _[`9ab2ba3`](https://github.com/duniul/clean-modules/commit/9ab2ba39e384fbf9a9db5d862bf01734afe680e1) [@duniul](https://github.com/duniul)_ + ## 3.1.1 ### Patch Changes @@ -108,11 +139,11 @@ # 2.0.0 -### BREAKING CHANGES +### Major changes -- Node versions lower than 12 are no longer supported. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`d6c66f2`](https://github.com/duniul/clean-modules/commit/d6c66f2ab75ec03a573b848c396d74316fc085d6) [@duniul](https://github.com/duniul)_ -- A path to `node_modules` should now be supplied to the `--directory` option instead of a positional. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ -- The `--analyze` option has been replaced by a separate command. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ +- **BREAKING** Node versions lower than 12 are no longer supported. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`d6c66f2`](https://github.com/duniul/clean-modules/commit/d6c66f2ab75ec03a573b848c396d74316fc085d6) [@duniul](https://github.com/duniul)_ +- **BREAKING** A path to `node_modules` should now be supplied to the `--directory` option instead of a positional. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ +- **BREAKING** The `--analyze` option has been replaced by a separate command. _[`#5`](https://github.com/duniul/clean-modules/pull/5) [`6aa9d55`](https://github.com/duniul/clean-modules/commit/6aa9d556fe0fa42b70966c6e7788442dae7a3426) [@duniul](https://github.com/duniul)_ ### Minor Changes diff --git a/README.md b/README.md index 1687804..8de4c2b 100644 --- a/README.md +++ b/README.md @@ -1,260 +1,170 @@ # clean-modules ๐Ÿงน - - - - - -> Clean up/prune unnecessary files and reduce the size of your `node_modules` directory. Useful for -> CI caches or for reducing the size of serverless functions. - -- ๐Ÿงน Removes directories and files that are unnecessary and **safe to remove in production** -- ๐Ÿ›  Easily customizable through glob patterns (either through [CLI args](#positionals) or a - [configuration file](#glob-patterns-and-configuration-file)) -- ๐Ÿ“ Cleans up empty directories after removing files -- โšก๏ธ Super fast, but still written in Node -- ๐Ÿ” Allows analyzing results, like which pattern excluded which file -- ๐Ÿง‘โ€๐Ÿ’ป Supports both CLI and programmatic usage - -

- Small project - Large project - Dry run -

+[![npm version](https://img.shields.io/npm/v/clean-modules)](https://www.npmjs.com/package/clean-modules) +[![npm downloads](https://img.shields.io/npm/dm/clean-modules)](https://www.npmjs.com/package/clean-modules) +[![node lts](https://img.shields.io/node/v-lts/clean-modules)](https://www.npmjs.com/package/clean-modules) + +Saves space by removing redundant files from your `node_modules`. Useful for CI caches +or reducing the size of serverless functions. + +- Fully customizable through glob patterns, either via [CLI args](#positionals) or + [config file](#glob-patterns-and-configuration-file). +- Pure Node.js, no separate binary or runtime needed. +- Fast. ## Table of contents
Click to open -- [Quick start](#quick-start) - [Installation](#installation) +- [Quick start](#quick-start) - [Commands](#commands) - - [`clean-modules clean` (default command) ๐Ÿงน](#clean-modules-clean-default-command-) - - [Usage](#usage) + - [`clean-modules [clean]`](#clean-modules-clean) - [Positionals](#positionals) - [Options](#options) - - [`--directory | -D`](#--directory---d) - - [`--glob-file | -f`](#--glob-file---f) - - [`--no-defaults | -n`](#--no-defaults---n) - - [`--keep-empty | -k`](#--keep-empty---k) - - [`--dry-run | -d`](#--dry-run---d) - - [`--json | -j`](#--json---j) - - [`--yes | -y`](#--yes---y) - - [`clean-modules analyze` ๐Ÿ”Ž](#clean-modules-analyze-) - - [Usage](#usage-1) + - [`clean-modules analyze`](#clean-modules-analyze) - [Positionals](#positionals-1) - [Options](#options-1) - - [Example output](#example-output) - [Glob patterns and configuration file](#glob-patterns-and-configuration-file) - [Example configuration file](#example-configuration-file) - [Default globs](#default-globs) - [Common extra inclusions](#common-extra-inclusions) - [Common extra exclusions](#common-extra-exclusions) - [Programmatic usage](#programmatic-usage) -- [Alternatives](#alternatives) - - [Comparisons](#comparisons) - - [clean-modules (this project)](#clean-modules-this-project) - - [yarn autoclean](#yarn-autoclean) - - [modclean](#modclean) - - [node-prune](#node-prune) - - [nm-prune](#nm-prune)
-## Quick start - -Simply run the command in the directory where your `node_modules` are: +## Installation ```bash -clean-modules +npm install --save-dev clean-modules ``` -You can also pass any options that you need, like custom globs or a path to a specific -`node_modules` directory. +Project installation is optional. You can also install it globally, or run it directly via `npx clean-modules` or equivalent. + +## Quick start + +Run it in your project root: ```bash -clean-modules --directory "path/to/node_modules" "extra/included/file/glob.txt" "!extra/excluded/glob.ts +npx clean-modules ``` -Check out the [command](#clean-modules-clean-default-command-๐Ÿงน) section for all available options. - -## Installation +Accept the confirmation prompt, and you'll see output like: -`clean-modules` can be installed globally if you only want to use it as a CLI tool. You can also -install it locally if you want to use it in a package command. +``` +clean-modules -```bash -# global -npm install --global clean-modules +Cleaning up node_modules... +Done in 1.15s! -# dev dependency -npm install --save-dev clean-modules +Results: +- size reduced: 314.4 MB +- files matched: 25741 +- files removed: 25741 +- empty dirs removed: 1361 ``` -## Commands +_To include/exclude extra files or set other options, see the [clean command docs](#clean-modules-clean) below._ -### `clean-modules clean` (default command) ๐Ÿงน +## Commands -The default command, cleans up your `node_modules` based on a set of _most likely_ safe glob -patterns and removes empty directories. +### `clean-modules [clean]` -#### Usage +**Default command.** Cleans up your `node_modules` based on a set of _likely_ safe glob +patterns. ```bash -clean-modules [options] <...globs> +clean-modules [options] [globs...] +# or +clean-modules clean [options] [globs...] ``` #### Positionals -Extra glob patterns can be passed as positional arguments. By default they are combined with the globs loaded from the the default globs file and any custom globs file passed through the [`--glob-file`](#glob-file---f) option. - -For information on how the globs are parsed, see the [Glob patterns](#glob-patterns-and-configuration-file) section. - -**Example**: +Glob patterns can be passed as positional arguments to include or exclude extra files. See the [Glob patterns](#glob-patterns-and-configuration-file) section for how the globs are +parsed. ```bash -# include all TypeScript declaration files and @types folders -clean-modules "**/*.d.ts" "**/@types/**" +# include all .d.ts files and @types folders +clean-modules "*.d.ts" "**/@types/**" # exclude all sourcemap files and PNG files -clean-modules "!**/*.map.js" "!**/*.png" +clean-modules "!*.map.js" "!*.png" # include .d.ts files but exclude PNG files -clean-modules "**/*.d.ts" "!**/*.png" +clean-modules "*.d.ts" "!*.png" ``` #### Options -##### `--directory | -D` - -`string` [default: `./node_modules`] - -Accepts a path to a directory to run the script on. - -**Example**: +| Flag | Description | +| -------------------------- | ------------------------------------------------------------------------- | +| `--directory `, `-D` | Path to your `node_modules` directory.
_Default: `$PWD/node_modules`_ | +| `--glob-file `, `-f` | Path to a file with custom globs.
_Default: `$PWD/.cleanmodules`_ | +| `--no-defaults`, `-n` | Skip the default globs, use only globs explicitly set by you. | +| `--keep-empty`, `-k` | Skip removing empty folders after removing files. | +| `--yes`, `-y` | Skip the confirmation prompt at the start. | +| `--dry-run`, `-d` | Run without actually removing any files, just the final summary. | +| `--silent`, `-s` | Suppress all console output. | +| `--json`, `-j` | Only output a single JSON summary at the end. | +| `--fail-on-error`, `-e` | Exit with a non-zero status code if any files failed. | +| `--help`, `-h` | Show help. | +| `--version`, `-v` | Show version. | + +### `clean-modules analyze` + +Compiles a list of all files that would be marked for cleaning and gives a breakdown of: + +- Exact file path. +- What glob pattern(s) included it. +- How the patterns were parsed and passed to `picomatch`. +- Whether the file was included by default. ```bash -clean-modules --directory "path/to/custom/node_modules" +clean-modules analyze [options] [globs...] ``` -##### `--glob-file | -f` - -`string` [default: `./.cleanmodules`] - -Accepts a path to a file from which clean-modules should read any custom globs. See the -[glob patterns](#glob-patterns-and-configuration-file) section for information about how the glob -file works and what patterns work. - -##### `--no-defaults | -n` - -`boolean` - -Excludes all default globs, using only custom globs added through the -[glob file](#glob-patterns-and-configuration-file) or by the include or exclude options. Useful if -you want complete control over what files to include. - -See the [`.cleanmodules-default`](./cleanmodules-default) to see what patterns are included by -default by default. - -##### `--keep-empty | -k` - -`boolean` - -Skips removing empty folders after removing files. - -##### `--dry-run | -d` - -`boolean` - -Runs the script and prints the result without actually removing any files. Does not count the number -of removed empty directories. - -##### `--json | -j` - -`boolean` - -Only logs a final JSON dump at the end of the script (useful for logs or services). - -##### `--yes | -y` - -`boolean` - -Skips the confirmation prompt at the start of the script. - -### `clean-modules analyze` ๐Ÿ”Ž - -Compiles a list of all files that would be included by `clean-modules` and gives a breakdown of: - -- exact file path -- what glob patterns it was included by -- how the patterns were formatted and passed along to `picomatch` -- if the file was included by default - -#### Usage +Because of the amount of data it can be useful to pipe it somewhere: ```bash -clean-modules analyze [options] -``` - -Because of the amount of data it can be useful to pipe it somewhere, like: - -```bash -clean-modules analyze >> clean-modules-result.json +clean-modules analyze > clean-modules-result.json ``` #### Positionals -The `analyze` command accepts the same type of positionals as the [default command](). +Accepts the same positional globs as the [default clean command](#positionals). #### Options -The `analyze` command accepts several of the default command's options and applies them in the same -way: - -- [`--directory`](#directory---d) -- [`--glob-file`](#glob-file---f) -- [`--no-defaults`](#no-defaults---n) - -#### Example output - -```json -[ - { - "filePath": "/Users/me/projects/foo/node_modules/dependency/__tests__/test1.js", - "includedByDefault": true, - "includedByGlobs": [ - { - "original": "__tests__/", - "derived": "/Users/me/projects/foo/node_modules/dependency/**/__tests__/**" - } - ] - } - // ... -] -``` +| Flag | Description | +| -------------------------- | ---------------------------------------------------------------------- | +| `--directory `, `-D` | Path to your `node_modules` directory.
_Default: `./node_modules`_ | +| `--glob-file `, `-f` | Path to a file with custom globs.
_Default: `./.cleanmodules`_ | +| `--no-defaults`, `-n` | Skip the default globs, use only globs explicitly set by you. | +| `--help`, `-h` | Show help. | +| `--version`, `-v` | Show version. | ## Glob patterns and configuration file -clean-modules accepts globs from either a configuration file (`.cleanmodules` next to -`node_modules` by default) or CLI arguments. It uses -[`.gitignore`-like](https://www.atlassian.com/git/tutorials/saving-changes/gitignore) glob patterns -that are converted to valid [`picomatch`](https://github.com/micromatch/picomatch) globs, which is -what is used to match file paths under the hood. +Custom globs can be provided to clean-modules in two ways: + +- Via a configuration file (`.cleanmodules` by default, can be changed via `--glob-file`). +- As positional CLI arguments. + +Both approaches accept the same .gitignore-like syntax, which is later converted to optimized [`picomatch`](https://github.com/micromatch/picomatch) compatible globs under the hood. **Differences from regular gitignore syntax:** -- To include a directory the pattern _must_ end with `/`, `/*` or `/**` - - This is to prevent directories matching common file names from being included by the globs. - Casing is ignored. - -**Like with .gitignore, globs should use forward-slashes as separators on all operative systems (including Windows)!** +- To include a directory the pattern _must_ end with `/`, `/*` or `/**`. This is to prevent + directories matching common file names from being included by the globs. +- Extended glob matching is supported ([extglobs](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html)). ### Example configuration file -```ignore -# this is a comment (starts with a #) - -# to include include directories, end patterns with / or /* or /** +```sh +# to include directories, end patterns with / or /* or /** dep1/ dep1/* dep2/** @@ -279,11 +189,10 @@ dep2/** ### Default globs The default globs can be found in the [`.cleanmodules-default`](./.cleanmodules-default) file. It -only contains inclusions (as exclusions would override custom inclusions) and consists of a large -list of the most common files that are safe to remove. +consists of a large list of the most common files that are safe to remove. -That said, it's impossible to guarantee that none of the files are needed, and you might need to do -custom exclusions depending on what packages you use. +That said, it's impossible to guarantee that all redundant files are removed (or that no removed +files are needed), and you might need to add custom exclusions depending on what packages you use. #### Common extra inclusions @@ -294,23 +203,23 @@ custom exclusions depending on what packages you use. #### Common extra exclusions - `!**/*.map.js`: **If you are running `clean-modules` locally or need source files in production.** - `clean-modules` removes sourcemap files by default since they take up a lot of space and does not + Sourcemap files are removed by default since they take up a lot of space and don't break builds when removed. They can be nice to have though, especially while developing. -## Programmatic usage +- `!*.{jpg,jpeg,png,gif,webp}`: **If you are using dependencies that provide image files.** + Images and media files are removed by default since they take up a lot of space and are usually not + included in libraries, but some dependencies might use them at runtime. -Clean modules can be used programmatically too! - -Simply import the corresponding function from the package: +## Programmatic usage -````ts -import { clean, analyze } from 'clean-modules'; +```ts +import { analyze, clean } from 'clean-modules'; // analyze, all options are optional const analyzeResult = await analyze({ directory: '/path/to/node_modules', globFile: '/path/to/.cleanmodules', - globs: ['**/*.js'], + globs: ['**/*.d.ts'], noDefaults: false, }); @@ -318,63 +227,17 @@ const analyzeResult = await analyze({ const cleanResult = await clean({ directory: '/path/to/node_modules', globFile: '/path/to/.cleanmodules', - globs: ['**/*.js'], + globs: ['**/*.d.ts'], noDefaults: false, keepEmpty: false, dryRun: false, }); -```` - -## Alternatives - -The most common issues I found with available tools are: - -- They only allow inclusion/exclusion of file names, not file paths. This prevents you from e.g. - excluding subdirectories of a specific folder, like `@types/react-native`. -- They include too many, or too few, patterns by default. Lots of them also include patterns like - `*.ts` by default, which breaks TypeScript declaration files on build. -- They are too slow (only relevant when running on large projects) -- They are abandoned or don't respond to issues. - -### Comparisons - -#### clean-modules (this project) - -- โœ… Inclusion/exclusion through file path globs -- โœ… Fast -- โœ… Safe list of files and folders included by default (for production use) -- โœ… Cleans up empty directories - -#### [yarn autoclean](https://classic.yarnpkg.com/en/docs/cli/autoclean/) - -- โœ… Included with Yarn by default -- โœ… Inclusion/exclusion through globs -- โ›”๏ธ Very slow compared to alternatives -- โ›”๏ธ Runs on every install, both locally and in CI -- โ›”๏ธ Small list of files included by default - -#### [modclean](https://github.com/ModClean/modclean) - -- โœ… Cleans up empty directories -- โœ… Safe list of files and folders included by default -- โ›”๏ธ Slow, only slightly faster than `yarn clean` -- โ›”๏ธ Only allows inclusion/exclusion by file name globs, not file path -- โ›”๏ธ Complains about empty folders that doesn't exist -- โ›”๏ธ Abandoned - -#### [node-prune](https://github.com/tj/node-prune) +``` -- โœ… Fastest option available -- โ›”๏ธ Written in Go and might require separate binary download -- โ›”๏ธ Removes some dangerous files by default (like `.d.ts` files and `assets` folder) -- โ›”๏ธ Only allows inclusion/exclusion by file name globs, not file path -- โ›”๏ธ Globs are very limited since it uses Go's `filepath.Match` -- โ›”๏ธ Does not remove empty folders +Both functions return a result object that mirrors the JSON output of the corresponding CLI command: -#### [nm-prune](https://github.com/tuananh/node-prune) +- `clean` resolves to `{ files, removedFilesCount, reducedSize, removedEmptyDirs, failures }`. +- `analyze` resolves to `{ files, failures }`. -- โœ… Fast -- โ›”๏ธ Removes some dangerous files by default (like `.d.ts` files and `assets` folder) -- โ›”๏ธ Only allows inclusion/exclusion by file name, not file paths or globs -- โ›”๏ธ Does not remove empty folders -- โ›”๏ธ Small list of files included by default +`failures` is a list of non-fatal filesystem errors encountered while crawling or removing files +(e.g. permission errors). diff --git a/__mocks__/fs/promises.ts b/__mocks__/fs/promises.ts index c99c64b..3ec2d35 100644 --- a/__mocks__/fs/promises.ts +++ b/__mocks__/fs/promises.ts @@ -1,3 +1,3 @@ import { fs } from 'memfs'; -module.exports = fs +module.exports = fs; diff --git a/biome.json b/biome.json deleted file mode 100644 index 14fc595..0000000 --- a/biome.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "formatter": { - "enabled": false - }, - "organizeImports": { - "enabled": false - }, - "linter": { - "enabled": true, - "rules": { - "suspicious": { - "noExplicitAny": "off" - }, - "style": { - "useTemplate": "off", - "noUnusedTemplateLiteral": "off" - } - } - }, - "files": { - "ignore": ["**/*.json"] - } -} diff --git a/images/dry-run.png b/images/dry-run.png deleted file mode 100644 index 958a255..0000000 Binary files a/images/dry-run.png and /dev/null differ diff --git a/images/large-project.png b/images/large-project.png deleted file mode 100644 index 4c4c1a2..0000000 Binary files a/images/large-project.png and /dev/null differ diff --git a/images/small-project.png b/images/small-project.png deleted file mode 100644 index c8cb261..0000000 Binary files a/images/small-project.png and /dev/null differ diff --git a/package.json b/package.json index b41f586..dab387b 100644 --- a/package.json +++ b/package.json @@ -1,83 +1,75 @@ { "name": "clean-modules", - "author": "Daniel Grefberg ", - "version": "3.1.1", - "description": "Clean up/prune unnecessary files and reduce the size of your `node_modules` directory. Useful for CI caches or for reducing the size of serverless functions.", - "repository": "https://github.com/duniul/clean-modules.git", - "homepage": "https://github.com/duniul/clean-modules#readme", - "bugs": { - "url": "https://github.com/duniul/clean-modules/issues" - }, + "version": "4.0.0", + "description": "Saves space by removing redundant files from your node_modules. Useful for CI caches or reducing the size of serverless functions.", "keywords": [ + "ci", + "cli-tool", "node", "node-modules", "npm", - "yarn", "pnpm", - "cli-tool", - "ci" + "yarn" ], + "homepage": "https://github.com/duniul/clean-modules#readme", + "bugs": { + "url": "https://github.com/duniul/clean-modules/issues" + }, "license": "ISC", - "engines": { - "node": ">=14" + "author": "Daniel Grefberg ", + "repository": { + "type": "git", + "url": "git+https://github.com/duniul/clean-modules.git" }, + "bin": "bin/cli.js", "files": [ "dist", ".cleanmodules-default", "!.tsbuildinfo" ], - "packageManager": "pnpm@9.11.0", "type": "module", + "sideEffects": false, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.cts", - "default": "./dist/index.cjs" - } + "types": "./dist/index.d.ts", + "default": "./dist/index.js" } }, - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "bin": "bin/cli.js", "scripts": { - "cli": "tsx src/cli/cli.ts", - "build": "tsup", - "dev": "tsc --watch", - "test": "vitest", + "cli": "node bin/cli.js", + "build": "tsdown", + "dev": "tsdown --sourcemap true --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "prepublishOnly": "pnpm build", - "lint": "biome check src && prettier --check src", - "lint:fix": "biome check --apply src && prettier --write src", + "stylecheck": "oxfmt --check src && oxlint src", + "typecheck": "tsc --noEmit", + "format": "oxfmt --write .", + "lint": "oxlint .", + "lint:fix": "oxlint --fix .", "version": "changeset version", - "release": "changeset publish" + "release": "pnpm publish -r" }, "dependencies": { - "clipanion": "^3.2.1", - "picomatch": "^2.3.0", - "pretty-bytes": "^6.1.0", - "pretty-ms": "^8.0.0", - "supports-color": "^9.4.0" + "picomatch": "^4.0.2" }, "devDependencies": { - "@biomejs/biome": "^1.9.2", - "@changesets/cli": "^2.27.8", - "@tsconfig/node14": "^14.1.2", - "@types/mock-fs": "4.13.4", - "@types/node": "16", - "@types/picomatch": "2.3.4", - "@types/supports-color": "8.1.3", - "changesets-changelog-clean": "^1.3.0", - "memfs": "^4.12.0", - "prettier": "3.3.3", - "prettier-plugin-organize-imports": "4.1.0", - "tslib": "^2.7.0", - "tsup": "^8.3.0", - "tsx": "^4.19.1", - "typescript": "5.6.2", - "vitest": "^2.1.1" - } + "@changesets/cli": "2.31.0", + "@types/node": "22.19.18", + "@types/picomatch": "4.0.3", + "changesets-changelog-clean": "1.3.0", + "memfs": "4.57.2", + "oxfmt": "0.51.0", + "oxlint": "1.66.0", + "oxlint-tsgolint": "0.23.0", + "tsdown": "0.22.0", + "typescript": "6.0.3", + "vitest": "4.1.7" + }, + "engines": { + "node": ">=22" + }, + "packageManager": "pnpm@11.2.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee7b84e..ed0a3d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,668 +8,838 @@ importers: .: dependencies: - clipanion: - specifier: ^3.2.1 - version: 3.2.1(typanion@3.14.0) picomatch: - specifier: ^2.3.0 - version: 2.3.1 - pretty-bytes: - specifier: ^6.1.0 - version: 6.1.1 - pretty-ms: - specifier: ^8.0.0 - version: 8.0.0 - supports-color: - specifier: ^9.4.0 - version: 9.4.0 + specifier: ^4.0.2 + version: 4.0.4 devDependencies: - '@biomejs/biome': - specifier: ^1.9.2 - version: 1.9.2 '@changesets/cli': - specifier: ^2.27.8 - version: 2.27.8 - '@tsconfig/node14': - specifier: ^14.1.2 - version: 14.1.2 - '@types/mock-fs': - specifier: 4.13.4 - version: 4.13.4 + specifier: 2.31.0 + version: 2.31.0(@types/node@22.19.18) '@types/node': - specifier: '16' - version: 16.18.82 + specifier: 22.19.18 + version: 22.19.18 '@types/picomatch': - specifier: 2.3.4 - version: 2.3.4 - '@types/supports-color': - specifier: 8.1.3 - version: 8.1.3 + specifier: 4.0.3 + version: 4.0.3 changesets-changelog-clean: - specifier: ^1.3.0 + specifier: 1.3.0 version: 1.3.0 memfs: - specifier: ^4.12.0 - version: 4.12.0 - prettier: - specifier: 3.3.3 - version: 3.3.3 - prettier-plugin-organize-imports: - specifier: 4.1.0 - version: 4.1.0(prettier@3.3.3)(typescript@5.6.2) - tslib: - specifier: ^2.7.0 - version: 2.7.0 - tsup: - specifier: ^8.3.0 - version: 8.3.0(postcss@8.4.35)(supports-color@9.4.0)(tsx@4.19.1)(typescript@5.6.2) - tsx: - specifier: ^4.19.1 - version: 4.19.1 + specifier: 4.57.2 + version: 4.57.2(tslib@2.8.1) + oxfmt: + specifier: 0.51.0 + version: 0.51.0 + oxlint: + specifier: 1.66.0 + version: 1.66.0(oxlint-tsgolint@0.23.0) + oxlint-tsgolint: + specifier: 0.23.0 + version: 0.23.0 + tsdown: + specifier: 0.22.0 + version: 0.22.0(typescript@6.0.3) typescript: - specifier: 5.6.2 - version: 5.6.2 + specifier: 6.0.3 + version: 6.0.3 vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@16.18.82)(supports-color@9.4.0) + specifier: 4.1.7 + version: 4.1.7(@types/node@22.19.18)(vite@8.0.11(@types/node@22.19.18)) packages: - '@babel/runtime@7.23.9': - resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} - engines: {node: '>=6.9.0'} - - '@biomejs/biome@1.9.2': - resolution: {integrity: sha512-4j2Gfwft8Jqp1X0qLYvK4TEy4xhTo4o6rlvJPsjPeEame8gsmbGQfOPBkw7ur+7/Z/f0HZmCZKqbMvR7vTXQYQ==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@1.9.2': - resolution: {integrity: sha512-rbs9uJHFmhqB3Td0Ro+1wmeZOHhAPTL3WHr8NtaVczUmDhXkRDWScaxicG9+vhSLj1iLrW47itiK6xiIJy6vaA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] + '@babel/generator@8.0.0-rc.4': + resolution: {integrity: sha512-YZ+FuIgkj7KrIb2a2X1XiY0QYgDxAbVbYP64SjwJzOK3euCsUerzenh2oqdsmKuPSlhzmFOOklnxzHAzXagvpw==} + engines: {node: ^20.19.0 || >=22.12.0} - '@biomejs/cli-darwin-x64@1.9.2': - resolution: {integrity: sha512-BlfULKijNaMigQ9GH9fqJVt+3JTDOSiZeWOQtG/1S1sa8Lp046JHG3wRJVOvekTPL9q/CNFW1NVG8J0JN+L1OA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@1.9.2': - resolution: {integrity: sha512-ZATvbUWhNxegSALUnCKWqetTZqrK72r2RsFD19OK5jXDj/7o1hzI1KzDNG78LloZxftrwr3uI9SqCLh06shSZw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-arm64@1.9.2': - resolution: {integrity: sha512-T8TJuSxuBDeQCQzxZu2o3OU4eyLumTofhCxxFd3+aH2AEWVMnH7Z/c3QP1lHI5RRMBP9xIJeMORqDQ5j+gVZzw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] + '@babel/helper-string-parser@8.0.0-rc.4': + resolution: {integrity: sha512-dluR3v287dp6YPF57kyKKrHPKffUeuxH1zQcF1WD30TeFzWXhDiVi1U6PkqaDB0++H1PeCwRhmYl4DvoerlPIw==} + engines: {node: ^20.19.0 || >=22.12.0} - '@biomejs/cli-linux-x64-musl@1.9.2': - resolution: {integrity: sha512-CjPM6jT1miV5pry9C7qv8YJk0FIZvZd86QRD3atvDgfgeh9WQU0k2Aoo0xUcPdTnoz0WNwRtDicHxwik63MmSg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] + '@babel/helper-validator-identifier@8.0.0-rc.4': + resolution: {integrity: sha512-HTD3bskipk5MSm08twTW6832jzIXUhxMddy4NPPzIMuyMEsrs0ZgwAaMj5ubB5+6hMlUjDu17vNconEmwsmpYg==} + engines: {node: ^20.19.0 || >=22.12.0} - '@biomejs/cli-linux-x64@1.9.2': - resolution: {integrity: sha512-T0cPk3C3Jr2pVlsuQVTBqk2qPjTm8cYcTD9p/wmR9MeVqui1C/xTVfOIwd3miRODFMrJaVQ8MYSXnVIhV9jTjg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] + '@babel/parser@8.0.0-rc.4': + resolution: {integrity: sha512-0S/1yefMa15N4i2v3t8Fw9pgMHhf2gF6Lc1UEXI96Ls6FNAjqvHHZouZ2ZS/deqLhbMFtmfVeFac6iTsvFbLwA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true - '@biomejs/cli-win32-arm64@1.9.2': - resolution: {integrity: sha512-2x7gSty75bNIeD23ZRPXyox6Z/V0M71ObeJtvQBhi1fgrvPdtkEuw7/0wEHg6buNCubzOFuN9WYJm6FKoUHfhg==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} - '@biomejs/cli-win32-x64@1.9.2': - resolution: {integrity: sha512-JC3XvdYcjmu1FmAehVwVV0SebLpeNTnO2ZaMdGCSOdS7f8O9Fq14T2P1gTG1Q29Q8Dt1S03hh0IdVpIZykOL8g==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] + '@babel/types@8.0.0-rc.4': + resolution: {integrity: sha512-bw30DV880P/VYtsjWWdoWmJpb9S2Vn1/PqayyccTELzRQ/HslIO7+BD9rNoZ4AAFOAjC1vrNeBCkAsyh6Ibfww==} + engines: {node: ^20.19.0 || >=22.12.0} - '@changesets/apply-release-plan@7.0.5': - resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} - '@changesets/assemble-release-plan@6.0.4': - resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} - '@changesets/changelog-git@0.2.0': - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.27.8': - resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} + '@changesets/cli@2.31.0': + resolution: {integrity: sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==} hasBin: true - '@changesets/config@3.0.3': - resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - '@changesets/get-dependents-graph@2.1.2': - resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} '@changesets/get-github-info@0.6.0': resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} - '@changesets/get-release-plan@4.0.4': - resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - '@changesets/git@3.0.1': - resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.0': - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} - '@changesets/pre@2.0.1': - resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.1': - resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} - '@changesets/should-skip-package@0.1.1': - resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} '@changesets/types@4.1.0': resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - '@changesets/types@6.0.0': - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} - '@changesets/write@0.3.2': - resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} - cpu: [arm64] - os: [android] + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] + '@jsonjoy.com/fs-core@4.57.2': + resolution: {integrity: sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] + '@jsonjoy.com/fs-fsa@4.57.2': + resolution: {integrity: sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] + '@jsonjoy.com/fs-node-builtins@4.57.2': + resolution: {integrity: sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + '@jsonjoy.com/fs-node-to-fsa@4.57.2': + resolution: {integrity: sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] + '@jsonjoy.com/fs-node-utils@4.57.2': + resolution: {integrity: sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} + '@jsonjoy.com/fs-node@4.57.2': + resolution: {integrity: sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.57.2': + resolution: {integrity: sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.57.2': + resolution: {integrity: sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-project/types@0.128.0': + resolution: {integrity: sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==} + + '@oxc-project/types@0.129.0': + resolution: {integrity: sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==} + + '@oxfmt/binding-android-arm-eabi@0.51.0': + resolution: {integrity: sha512-Ni0sCqg5CIHaLIYFGj+ncbcumylvNC6FE4rfD0KfdmnWHbPJ+zev0qZCXKxy2hFVa0fYRK0yPzf5nzPbkZou7g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] - os: [linux] + os: [android] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] + '@oxfmt/binding-android-arm64@0.51.0': + resolution: {integrity: sha512-eu5lAZjuo0KAkp+M24EhDqfOwA8owQ8d7wyBlOUUGRbDLHpU3IRlDHp8Dif+YqGlxs6jra7yS6WQu/NkPhAxeg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] + '@oxfmt/binding-darwin-arm64@0.51.0': + resolution: {integrity: sha512-6LsUNIdURhhcIfIn8+xsOb61mSTa9msAHTeSGx9Jf4rsP/gN8PGCF+SKWPAQZbND2w/WBkqQ6303jqEEIXzMdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] + '@oxfmt/binding-darwin-x64@0.51.0': + resolution: {integrity: sha512-9aUMGmVxdHjYMsEAW1tNRoieTJXlVNDFkRvIR1J7LttJXWjVYCu2ekclLij2KJtxBxSQOYSHd12ME/adVGVbZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] + '@oxfmt/binding-freebsd-x64@0.51.0': + resolution: {integrity: sha512-mkY1nhZTqYb+NHaAWxOCKISN6FwdrwMNsu17vTUA3wzUV2VJ+Paq15ZokRcsMU/2PUdHO73prxyeJpjXQ3MPpQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.51.0': + resolution: {integrity: sha512-wtFwNwE4+YCNuPaWoGDZeGsKvD6D1YSUNBJNn/rJBh7CrDBThFE+TBI5kY7vRW9rIOQRsbW2IpyyL3Du4Zqwiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] + '@oxfmt/binding-linux-arm-musleabihf@0.51.0': + resolution: {integrity: sha512-rnOaNx86G7iRKM6lsCIQMux0SMGNC/TEbFR+r7lpruJ12bnrIWgxd5w1PLqOvgR9r8ZJbpK/zfRKctJnh8/Jfg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] + '@oxfmt/binding-linux-arm64-gnu@0.51.0': + resolution: {integrity: sha512-jOgDzSqWcICGRjsp4mc08FxKMN8vzP2Kgs4E0d2HUP99F+nJDQKklRV4Zuj+0gcBgjrzx2CbpqaIdUVPepCojA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [linux] + libc: [glibc] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] + '@oxfmt/binding-linux-arm64-musl@0.51.0': + resolution: {integrity: sha512-KBUCdrH5bwVrAvI9gU/1S55oH6fzXjr++J/oVocdu7bYTks1l7DNNT+rLd/1TDdAEjObGwmfWamn7LC1m8A0DQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [linux] + libc: [musl] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} + '@oxfmt/binding-linux-ppc64-gnu@0.51.0': + resolution: {integrity: sha512-NapfjYsABFqTJ1Dn9Efq6sN5esaHconVKwVLbDGNQLrwpOx/g17mkwErHzU72PutL67nf3wNAkbq122H+zLxag==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} + '@oxfmt/binding-linux-riscv64-gnu@0.51.0': + resolution: {integrity: sha512-5dlDt1dUZCVi6elIhiK1PWg9wpTzTcIuj0IZnSurvIoMrhOWqqTcc1dSTxcSkNaBZhfsNqRZdINI1zAgbKkJNQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} + '@oxfmt/binding-linux-riscv64-musl@0.51.0': + resolution: {integrity: sha512-pgdWUJn0S5nulyiVdlFV8DzCUnGXkU99W5PSkkmbaZW+LrZBPxpezun4G0DDHbQaVYuJeCuKsXsGKGo77CkUTQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} + '@oxfmt/binding-linux-s390x-gnu@0.51.0': + resolution: {integrity: sha512-2XTFUe97CbDGAI8vjwDfZ1HdakO0XIADyJ24idEg64SC4/K4in/OisXVnrW4NMK7I6TgC7EqRhC0Ln/nKhAemA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} + '@oxfmt/binding-linux-x64-gnu@0.51.0': + resolution: {integrity: sha512-kQ1OuCqqt/yyf0ZN9VFxW1/JnlgJgii3Dr7pWf9vNBvrX1hv6g39/+mc5oGRHRGJFZtl3zsGDWR9c5N2B/gwBw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} + '@oxfmt/binding-linux-x64-musl@0.51.0': + resolution: {integrity: sha512-ARTYqxHF475o96Gbn41hvSWSSRygPlRDXZZgZ9I2scU1y0qiWpCQyZCoefaQa0mwv+wwtZ+luS4YOzsRzM/izg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] + '@oxfmt/binding-openharmony-arm64@0.51.0': + resolution: {integrity: sha512-QiC1XrCl6a6BmqMzduO8hdIRMf1m44hCkt2Q68KWkTvUB/E7fd2iomyNh6KnnRca5w6eBrRAAtLFqTh+xjsjJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} + '@oxfmt/binding-win32-arm64-msvc@0.51.0': + resolution: {integrity: sha512-NC/hJb9dtU23Zf8L7IVK95xnFjiQ7AfcLO2l5pb69TDEr958qxrtnB2CveeeNSCBFNIkgaTCfd/vHNSoG78l9g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] - os: [openbsd] + os: [win32] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] + '@oxfmt/binding-win32-ia32-msvc@0.51.0': + resolution: {integrity: sha512-2C45za4Rj36n8YIbhRL1PQbxmXJYf81WEcAgvj5I4ptRROG+A+81hREEN5bmCHADE1UfYaN312U6tkILoZZy6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} + '@oxfmt/binding-win32-x64-msvc@0.51.0': + resolution: {integrity: sha512-73RqdAuVKQTkjZIDw08JaDHUM4lav5Qu+CaPwg4QbbA7k8o7LEW0p3UsfZ/F8dsO/pwVYh3RzFcanwLRTTahbQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] - os: [openbsd] + os: [win32] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] + '@oxlint-tsgolint/darwin-arm64@0.23.0': + resolution: {integrity: sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==} + cpu: [arm64] + os: [darwin] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} + '@oxlint-tsgolint/darwin-x64@0.23.0': + resolution: {integrity: sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==} cpu: [x64] - os: [sunos] + os: [darwin] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} + '@oxlint-tsgolint/linux-arm64@0.23.0': + resolution: {integrity: sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==} cpu: [arm64] - os: [win32] + os: [linux] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} + '@oxlint-tsgolint/linux-x64@0.23.0': + resolution: {integrity: sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@0.23.0': + resolution: {integrity: sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] + '@oxlint-tsgolint/win32-x64@0.23.0': + resolution: {integrity: sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==} + cpu: [x64] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] + '@oxlint/binding-android-arm-eabi@1.66.0': + resolution: {integrity: sha512-f7kq8N51T4phpzqfBpA2qaVTI/KrkCmNwaj3t/97I/WLTDI+UhlP5GL9eER+zVxBhtlx5rKXWByJU1/zDAvyaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] + '@oxlint/binding-android-arm64@1.66.0': + resolution: {integrity: sha512-xu6QO71tdDS9mjmLZ3AqhtaVHBvdmsOKkYnReNNDgh+XiwnsipeQOIxbiYOOO0iAXycJ+GK0wdMSZP/2j/AmSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} + '@oxlint/binding-darwin-arm64@1.66.0': + resolution: {integrity: sha512-HZ24VimSOC7mxuEA99e0H2FS0C1yO3+iW13jPRAk+e2njsUs3QeAXsafCDyaIrV/MirdOVez+etQNQsJE43zNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.66.0': + resolution: {integrity: sha512-awhj8ZvJrrRSnXj7V++rpZvTmnl99L6mi0B7gg7Cp7BN6cKpzuI481bHNLvXGA9GB1/oEgA3ponuyoAc6Md12A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] - os: [win32] + os: [darwin] - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@oxlint/binding-freebsd-x64@1.66.0': + resolution: {integrity: sha512-KQF0oVV21/FjIqkRuL8Q1vh8ECsE5+ocdH5tcqTQ4ZnYuDVoYibQUNfqBjQaUsP6UIIda5Y75Wpm5p4RgQWiWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@jridgewell/gen-mapping@0.3.3': - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} + '@oxlint/binding-linux-arm-gnueabihf@1.66.0': + resolution: {integrity: sha512-9u1rgwZSEXWb30vbFZzQ78HVXBo0WCKNwJ3a2InRUTNMRng+PUDIoSFmA+m4HdUfBaIqftShq8J8qHc+eE/Vig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@oxlint/binding-linux-arm-musleabihf@1.66.0': + resolution: {integrity: sha512-Ynot2HR1bHxUaNWoC280MVTDfZuaWuP3XfSMRDhyuZrVjhzoaBCVFlw8h8qeZjWKVUBhPWFIxB7AQTlK8Z2WWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@jridgewell/set-array@1.1.2': - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} + '@oxlint/binding-linux-arm64-gnu@1.66.0': + resolution: {integrity: sha512-xCbgzciGgo+A4aQZEknsNrNiIwY7sU5SfRuMmRjPIvZAgdF34cIHiKvwOsS5XRLjlTVSFwitmq6YclTtHTfU+g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@oxlint/binding-linux-arm64-musl@1.66.0': + resolution: {integrity: sha512-hmo+ZB/lHkR1HdDmnziNpzSLmulnUSu10VEqX2Yex7OwvoBAbjJQLvy4gIBRV3AAwWnCvAxKp5Nv1GE6LU1QMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@oxlint/binding-linux-ppc64-gnu@1.66.0': + resolution: {integrity: sha512-2Invd4Uyy81mVooQC5FBtfxSNrvcX1OxbMlVQ6M2erRrNI2awFYF26YNW2yFxdVFZ4ffNOWKghtMjhnUPsXsVA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] - '@jridgewell/trace-mapping@0.3.22': - resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} + '@oxlint/binding-linux-riscv64-gnu@1.66.0': + resolution: {integrity: sha512-s0iXPDQVdgayE3RGa/N2DZF7tjgg0TwEtD1sGoDxqPDGrIXgo45H0yHknT0f9A0yteASsweYZtDyTuVlM4aSag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] - '@jsonjoy.com/base64@1.1.2': - resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' + '@oxlint/binding-linux-riscv64-musl@1.66.0': + resolution: {integrity: sha512-OekL4XFiu7RPK0JIZi8VeHgtIXPREf42t8Cy/rKEsC+P3gcqDgNAAGiyuUOpdbG4wwbfue1q4CHcCO7spSve6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] - '@jsonjoy.com/json-pack@1.1.0': - resolution: {integrity: sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' + '@oxlint/binding-linux-s390x-gnu@1.66.0': + resolution: {integrity: sha512-Ga1D0kj1SFslm34ThA/BdkUlyAYEnTsXyRC4pF0C5agZSwtGdHYWMTQWemUfBGp4RCG4QWXgdO+HmmmKqOtlBg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] - '@jsonjoy.com/util@1.3.0': - resolution: {integrity: sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' + '@oxlint/binding-linux-x64-gnu@1.66.0': + resolution: {integrity: sha512-p5jfP1wUZe/IC3qpQO84n9DRnf9g3lKRtLBlQq23ykyrDglHcVx7sWmVTlPuU6SBw8mNnPzyOn022G3XZHnlww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@oxlint/binding-linux-x64-musl@1.66.0': + resolution: {integrity: sha512-vUB/sYlYZorDL1ZD+o9mRv7zbsykrrFRtmgS6R8musZqLtrPRQn1gc1eGpuX+sfdccz42STl/AqldY6XRb2upQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@oxlint/binding-openharmony-arm64@1.66.0': + resolution: {integrity: sha512-yde+6p/F59xRkGR9H1HfngWRif1QRJjynZK349l+UI0H6w9hL3G8/AVaTHFyTtLVQ56qtNbX2/5Dc77n1ovnOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@oxlint/binding-win32-arm64-msvc@1.66.0': + resolution: {integrity: sha512-O9GLucgoTdmOrbBX+EjzNe7o/Ze5TFOvXcib6bzUOtBOmj6cV+zw18NgB+cGKAkDw1Pdqs8vGkfHbbsLuDtXWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@oxlint/binding-win32-ia32-msvc@1.66.0': + resolution: {integrity: sha512-m3Pjwc2MfTcom4E4gOv7DyuGyt7OfGNCbmqDHd+N7EzXmP+ppHuudm2NjcA3AjV5TSeGxaguVF4SbTKHe1USYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@oxlint/binding-win32-x64-msvc@1.66.0': + resolution: {integrity: sha512-/DbBvw8UFBhja6PqudUjV4UtfsJr0Oa7jUjWVKB0g86lj/VwnPrkngn0sFql3c9RDA0O16dh7ozsXb6GjNAzBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rollup/rollup-android-arm-eabi@4.23.0': - resolution: {integrity: sha512-8OR+Ok3SGEMsAZispLx8jruuXw0HVF16k+ub2eNXKHDmdxL4cf9NlNpAzhlOhNyXzKDEJuFeq0nZm+XlNb1IFw==} - cpu: [arm] + '@rolldown/binding-android-arm64@1.0.0': + resolution: {integrity: sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.23.0': - resolution: {integrity: sha512-rEFtX1nP8gqmLmPZsXRMoLVNB5JBwOzIAk/XAcEPuKrPa2nPJ+DuGGpfQUR0XjRm8KjHfTZLpWbKXkA5BoFL3w==} + '@rolldown/binding-android-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.23.0': - resolution: {integrity: sha512-ZbqlMkJRMMPeapfaU4drYHns7Q5MIxjM/QeOO62qQZGPh9XWziap+NF9fsqPHT0KzEL6HaPspC7sOwpgyA3J9g==} + '@rolldown/binding-darwin-arm64@1.0.0': + resolution: {integrity: sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.23.0': - resolution: {integrity: sha512-PfmgQp78xx5rBCgn2oYPQ1rQTtOaQCna0kRaBlc5w7RlA3TDGGo7m3XaptgitUZ54US9915i7KeVPHoy3/W8tA==} + '@rolldown/binding-darwin-x64@1.0.0': + resolution: {integrity: sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.23.0': - resolution: {integrity: sha512-WAeZfAAPus56eQgBioezXRRzArAjWJGjNo/M+BHZygUcs9EePIuGI1Wfc6U/Ki+tMW17FFGvhCfYnfcKPh18SA==} + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + resolution: {integrity: sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0': + resolution: {integrity: sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + resolution: {integrity: sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + resolution: {integrity: sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.23.0': - resolution: {integrity: sha512-v7PGcp1O5XKZxKX8phTXtmJDVpE20Ub1eF6w9iMmI3qrrPak6yR9/5eeq7ziLMrMTjppkkskXyxnmm00HdtXjA==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + resolution: {integrity: sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.23.0': - resolution: {integrity: sha512-nAbWsDZ9UkU6xQiXEyXBNHAKbzSAi95H3gTStJq9UGiS1v+YVXwRHcQOQEF/3CHuhX5BVhShKoeOf6Q/1M+Zhg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0': + resolution: {integrity: sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.23.0': - resolution: {integrity: sha512-5QT/Di5FbGNPaVw8hHO1wETunwkPuZBIu6W+5GNArlKHD9fkMHy7vS8zGHJk38oObXfWdsuLMogD4sBySLJ54g==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': - resolution: {integrity: sha512-Sefl6vPyn5axzCsO13r1sHLcmPuiSOrKIImnq34CBurntcJ+lkQgAaTt/9JkgGmaZJ+OkaHmAJl4Bfd0DmdtOQ==} + '@rolldown/binding-linux-arm64-musl@1.0.0': + resolution: {integrity: sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0': + resolution: {integrity: sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.23.0': - resolution: {integrity: sha512-o4QI2KU/QbP7ZExMse6ULotdV3oJUYMrdx3rBZCgUF3ur3gJPfe8Fuasn6tia16c5kZBBw0aTmaUygad6VB/hQ==} - cpu: [riscv64] + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0': + resolution: {integrity: sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.23.0': - resolution: {integrity: sha512-+bxqx+V/D4FGrpXzPGKp/SEZIZ8cIW3K7wOtcJAoCrmXvzRtmdUhYNbgd+RztLzfDEfA2WtKj5F4tcbNPuqgeg==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.23.0': - resolution: {integrity: sha512-I/eXsdVoCKtSgK9OwyQKPAfricWKUMNCwJKtatRYMmDo5N859tbO3UsBw5kT3dU1n6ZcM1JDzPRSGhAUkxfLxw==} + '@rolldown/binding-linux-x64-gnu@1.0.0': + resolution: {integrity: sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.23.0': - resolution: {integrity: sha512-4ZoDZy5ShLbbe1KPSafbFh1vbl0asTVfkABC7eWqIs01+66ncM82YJxV2VtV3YVJTqq2P8HMx3DCoRSWB/N3rw==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-win32-arm64-msvc@4.23.0': - resolution: {integrity: sha512-+5Ky8dhft4STaOEbZu3/NU4QIyYssKO+r1cD3FzuusA0vO5gso15on7qGzKdNXnc1gOrsgCqZjRw1w+zL4y4hQ==} + '@rolldown/binding-linux-x64-musl@1.0.0': + resolution: {integrity: sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0': + resolution: {integrity: sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0': + resolution: {integrity: sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + resolution: {integrity: sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0': + resolution: {integrity: sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.23.0': - resolution: {integrity: sha512-0SPJk4cPZQhq9qA1UhIRumSE3+JJIBBjtlGl5PNC///BoaByckNZd53rOYD0glpTkYFBQSt7AkMeLVPfx65+BQ==} - cpu: [ia32] + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.23.0': - resolution: {integrity: sha512-lqCK5GQC8fNo0+JvTSxcG7YB1UKYp8yrNLhsArlvPWN+16ovSZgoehlVHg6X0sSWPUkpjRBR5TuR12ZugowZ4g==} + '@rolldown/binding-win32-x64-msvc@1.0.0': + resolution: {integrity: sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@tsconfig/node14@14.1.2': - resolution: {integrity: sha512-1vncsbfCZ3TBLPxesRYz02Rn7SNJfbLoDVkcZ7F/ixOV6nwxwgdhD1mdPcc5YQ413qBJ8CvMxXMFfJ7oawjo7Q==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@rolldown/pluginutils@1.0.0': + resolution: {integrity: sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==} - '@types/mock-fs@4.13.4': - resolution: {integrity: sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==} + '@rolldown/pluginutils@1.0.0-rc.18': + resolution: {integrity: sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==} - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/node@16.18.82': - resolution: {integrity: sha512-pcDZtkx9z8XYV+ius2P3Ot2VVrcYOfXffBQUBuiszrlUzKSmoDYqo+mV+IoL8iIiIjjtOMvNSmH1hwJ+Q+f96Q==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/picomatch@2.3.4': - resolution: {integrity: sha512-0so8lU8O5zatZS/2Fi4zrwks+vZv7e0dygrgEZXljODXBig97l4cPQD+9LabXfGJOWwoRkTVz6Q4edZvD12UOA==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/semver@7.5.7': - resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/supports-color@8.1.3': - resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==} + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@22.19.18': + resolution: {integrity: sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==} + + '@types/picomatch@4.0.3': + resolution: {integrity: sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==} - '@vitest/expect@2.1.1': - resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@2.1.1': - resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: - '@vitest/spy': 2.1.1 - msw: ^2.3.5 - vite: ^5.0.0 + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@2.1.1': - resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@2.1.1': - resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@2.1.1': - resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@2.1.1': - resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/utils@2.1.1': - resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -679,28 +849,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -709,129 +867,79 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + ast-kit@3.0.0-beta.1: + resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} + engines: {node: '>=20.19.0'} better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - peerDependencies: - esbuild: '>=0.18' - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} - engines: {node: '>=12'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} changesets-changelog-clean@1.3.0: resolution: {integrity: sha512-3bGEYg+SuzS4TizAVwGxkiV+c/IMQI7+JPnrEApbvGeb5//c2cfcLG9pteNqpySVY6BNkpCFMM4dpZucnCPxtQ==} - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - clipanion@3.2.1: - resolution: {integrity: sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==} - peerDependencies: - typanion: '*' - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} - engines: {node: ^14.18.0 || >=16.10.0} - - cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} - - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -841,44 +949,37 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fdir@6.4.0: - resolution: {integrity: sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} - fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -892,24 +993,19 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -918,37 +1014,33 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} + hasBin: true hyperdyperid@1.2.0: resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} engines: {node: '>=10.18'} - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -957,10 +1049,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -972,98 +1060,125 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} - engines: {node: '>=14'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] - load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - loupe@3.1.1: - resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} - - lru-cache@10.2.0: - resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} - engines: {node: 14 || >=16.14} - - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - - memfs@4.12.0: - resolution: {integrity: sha512-74wDsex5tQDSClVkeK1vtxqYCAgCoXxx+K4NSHzgU/muYVYByFqa+0RnrPO9NM6naWm1+G9JmZ0p6QHhXmeYfA==} - engines: {node: '>= 4.0.0'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + memfs@4.57.2: + resolution: {integrity: sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==} + peerDependencies: + tslib: '2' merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} - engines: {node: '>=16 || 14 >=14.17'} - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1076,28 +1191,35 @@ packages: encoding: optional: true - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + oxfmt@0.51.0: + resolution: {integrity: sha512-l/AoAnaEOV7Q5/Z9kHOMDehVJnCgYN7wRoooWCTUMBMi16BJhLZqd9cmCnwcVFfVlzkt53zK2KLPFNp8vSsoDg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} + oxlint-tsgolint@0.23.0: + resolution: {integrity: sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==} + hasBin: true - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + oxlint@1.66.0: + resolution: {integrity: sha512-N4LLxYLd94KEBqXDMDM5f+2PUpItTjDLreXe2Gn5KhjhCK4Qp2YUXaBi8Yu325ryOgKwt22m45fpD7nPOn69Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} @@ -1119,12 +1241,8 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-manager-detector@0.2.0: - resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} - - parse-ms@3.0.0: - resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} - engines: {node: '>=12'} + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -1134,96 +1252,42 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} - engines: {node: '>= 14.16'} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - - postcss-load-config@6.0.1: - resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} - engines: {node: '>= 18'} - peerDependencies: - jiti: '>=1.21.0' - postcss: '>=8.0.9' - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - jiti: - optional: true - postcss: - optional: true - tsx: - optional: true - yaml: - optional: true - - postcss@8.4.35: - resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} - prettier-plugin-organize-imports@4.1.0: - resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==} - peerDependencies: - prettier: '>=2.0' - typescript: '>=2.9' - vue-tsc: ^2.1.0 - peerDependenciesMeta: - vue-tsc: - optional: true - prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} - engines: {node: '>=14'} - hasBin: true - - pretty-bytes@6.1.1: - resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} - engines: {node: ^14.13.1 || >=16.0.0} - - pretty-ms@8.0.0: - resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} - engines: {node: '>=14.16'} - - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -1232,13 +1296,6 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -1246,13 +1303,37 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.23.0: - resolution: {integrity: sha512-vXB4IT9/KLDrS2WRXmY22sVB2wTsTwkpxjB8Q3mnakTENcYw3FRmfdYDy/acNmls+lHmDazgrRjK/yQ6hQAtwA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + rolldown-plugin-dts@0.25.0: + resolution: {integrity: sha512-GE3uDZgUuA9l6g+1u928TRmadd5IVhaWiwpWast2kCyLv9tYJJCC6E5HHkV0HGmwC5ZL73xh12/PRZI+KZ2vdQ==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20260325.1' + rolldown: ^1.0.0 + typescript: ^6.0.0 + vue-tsc: ~3.2.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0: + resolution: {integrity: sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.0.0-rc.18: + resolution: {integrity: sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true run-parallel@1.2.0: @@ -1261,23 +1342,15 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -1285,9 +1358,6 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1296,16 +1366,12 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - - spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -1313,55 +1379,23 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - supports-color@9.4.0: - resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} - engines: {node: '>=12'} - term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - - thingies@1.21.0: - resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} engines: {node: '>=10.18'} peerDependencies: tslib: ^2 @@ -1369,29 +1403,22 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} - tinyglobby@0.2.9: - resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tinypool@1.0.1: - resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} - engines: {node: ^18.0.0 || >=20.0.0} + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} - engines: {node: '>=14.0.0'} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1399,11 +1426,8 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - - tree-dump@1.0.2: - resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' @@ -1412,98 +1436,134 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - - tsup@8.3.0: - resolution: {integrity: sha512-ALscEeyS03IomcuNdFdc0YWGVIkwH1Ws7nfTbAPuoILvEV2hpGQAY72LIOjglGo4ShWpZfpBqP/jpQVCzqYQag==} - engines: {node: '>=18'} + tsdown@0.22.0: + resolution: {integrity: sha512-FgW0hHb27nGQA/+F3d5+U9wKXkfilk9DVkc5+7x/ZqF03g+Hoz/eeApT32jqxATt9eRoR+1jxk7MUMON+O4CXw==} + engines: {node: ^22.18.0 || >=24.0.0} hasBin: true peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.5.0' + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.0 + '@tsdown/exe': 0.22.0 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' peerDependenciesMeta: - '@microsoft/api-extractor': + '@arethetypeswrong/core': + optional: true + '@tsdown/css': optional: true - '@swc/core': + '@tsdown/exe': optional: true - postcss: + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: optional: true typescript: optional: true + unplugin-unused: + optional: true + unrun: + optional: true - tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} - hasBin: true - - typanion@3.14.0: - resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} - vite-node@2.1.1: - resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite@5.1.3: - resolution: {integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@8.0.11: + resolution: {integrity: sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true - less: + '@vitejs/devtools': + optional: true + esbuild: optional: true - lightningcss: + jiti: + optional: true + less: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - vitest@2.1.1: - resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.1 - '@vitest/ui': 2.1.1 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@opentelemetry/api': + optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true @@ -1515,19 +1575,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1538,68 +1588,39 @@ packages: engines: {node: '>=8'} hasBin: true - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - snapshots: - '@babel/runtime@7.23.9': + '@babel/generator@8.0.0-rc.4': dependencies: - regenerator-runtime: 0.14.1 - - '@biomejs/biome@1.9.2': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.9.2 - '@biomejs/cli-darwin-x64': 1.9.2 - '@biomejs/cli-linux-arm64': 1.9.2 - '@biomejs/cli-linux-arm64-musl': 1.9.2 - '@biomejs/cli-linux-x64': 1.9.2 - '@biomejs/cli-linux-x64-musl': 1.9.2 - '@biomejs/cli-win32-arm64': 1.9.2 - '@biomejs/cli-win32-x64': 1.9.2 - - '@biomejs/cli-darwin-arm64@1.9.2': - optional: true - - '@biomejs/cli-darwin-x64@1.9.2': - optional: true - - '@biomejs/cli-linux-arm64-musl@1.9.2': - optional: true + '@babel/parser': 8.0.0-rc.4 + '@babel/types': 8.0.0-rc.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 - '@biomejs/cli-linux-arm64@1.9.2': - optional: true + '@babel/helper-string-parser@8.0.0-rc.4': {} - '@biomejs/cli-linux-x64-musl@1.9.2': - optional: true + '@babel/helper-validator-identifier@8.0.0-rc.4': {} - '@biomejs/cli-linux-x64@1.9.2': - optional: true + '@babel/parser@8.0.0-rc.4': + dependencies: + '@babel/types': 8.0.0-rc.4 - '@biomejs/cli-win32-arm64@1.9.2': - optional: true + '@babel/runtime@7.29.2': {} - '@biomejs/cli-win32-x64@1.9.2': - optional: true + '@babel/types@8.0.0-rc.4': + dependencies: + '@babel/helper-string-parser': 8.0.0-rc.4 + '@babel/helper-validator-identifier': 8.0.0-rc.4 - '@changesets/apply-release-plan@7.0.5': + '@changesets/apply-release-plan@7.1.1': dependencies: - '@changesets/config': 3.0.3 + '@changesets/config': 3.1.4 '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 @@ -1607,74 +1628,73 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.6.0 + semver: 7.8.0 - '@changesets/assemble-release-plan@6.0.4': + '@changesets/assemble-release-plan@6.0.10': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.6.0 + semver: 7.8.0 - '@changesets/changelog-git@0.2.0': + '@changesets/changelog-git@0.2.1': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 - '@changesets/cli@2.27.8': + '@changesets/cli@2.31.0(@types/node@22.19.18)': dependencies: - '@changesets/apply-release-plan': 7.0.5 - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.3 + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/get-release-plan': 4.0.4 - '@changesets/git': 3.0.1 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.2 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@22.19.18) '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.7 ansi-colors: 4.1.3 - ci-info: 3.9.0 enquirer: 2.4.1 - external-editor: 3.1.0 fs-extra: 7.0.1 mri: 1.2.0 - outdent: 0.5.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.0 - picocolors: 1.1.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.6.0 - spawndamnit: 2.0.0 + semver: 7.8.0 + spawndamnit: 3.0.1 term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' - '@changesets/config@3.0.3': + '@changesets/config@3.1.4': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-dependents-graph': 2.1.4 '@changesets/logger': 0.1.1 - '@changesets/types': 6.0.0 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - micromatch: 4.0.5 + micromatch: 4.0.8 '@changesets/errors@0.2.0': dependencies: extendable-error: 0.1.7 - '@changesets/get-dependents-graph@2.1.2': + '@changesets/get-dependents-graph@2.1.4': dependencies: - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.0 - semver: 7.6.0 + picocolors: 1.1.1 + semver: 7.8.0 '@changesets/get-github-info@0.6.0': dependencies: @@ -1683,493 +1703,623 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/get-release-plan@4.0.4': + '@changesets/get-release-plan@4.0.16': dependencies: - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/config': 3.0.3 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/types': 6.0.0 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 '@changesets/get-version-range-type@0.4.0': {} - '@changesets/git@3.0.1': + '@changesets/git@3.0.4': dependencies: '@changesets/errors': 0.2.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 - micromatch: 4.0.5 - spawndamnit: 2.0.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 '@changesets/logger@0.1.1': dependencies: - picocolors: 1.1.0 + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.1.1 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.1.3 + prettier: 2.8.8 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@inquirer/external-editor@1.0.3(@types/node@22.19.18)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 22.19.18 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@changesets/parse@0.4.0': + '@jsonjoy.com/fs-core@4.57.2(tslib@2.8.1)': dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.57.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.2 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 - '@changesets/pre@2.0.1': + '@manypkg/get-packages@1.1.3': dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 + '@babel/runtime': 7.29.2 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 - '@changesets/read@0.6.1': + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@changesets/git': 3.0.1 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.0 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true - '@changesets/should-skip-package@0.1.1': + '@nodelib/fs.scandir@2.1.5': dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 - '@changesets/types@6.0.0': {} + '@nodelib/fs.stat@2.0.5': {} - '@changesets/write@0.3.2': + '@nodelib/fs.walk@1.2.8': dependencies: - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - - '@esbuild/aix-ppc64@0.19.12': - optional: true + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 - '@esbuild/aix-ppc64@0.23.1': - optional: true + '@oxc-project/types@0.128.0': {} - '@esbuild/android-arm64@0.19.12': - optional: true + '@oxc-project/types@0.129.0': {} - '@esbuild/android-arm64@0.23.1': + '@oxfmt/binding-android-arm-eabi@0.51.0': optional: true - '@esbuild/android-arm@0.19.12': + '@oxfmt/binding-android-arm64@0.51.0': optional: true - '@esbuild/android-arm@0.23.1': + '@oxfmt/binding-darwin-arm64@0.51.0': optional: true - '@esbuild/android-x64@0.19.12': + '@oxfmt/binding-darwin-x64@0.51.0': optional: true - '@esbuild/android-x64@0.23.1': + '@oxfmt/binding-freebsd-x64@0.51.0': optional: true - '@esbuild/darwin-arm64@0.19.12': + '@oxfmt/binding-linux-arm-gnueabihf@0.51.0': optional: true - '@esbuild/darwin-arm64@0.23.1': + '@oxfmt/binding-linux-arm-musleabihf@0.51.0': optional: true - '@esbuild/darwin-x64@0.19.12': + '@oxfmt/binding-linux-arm64-gnu@0.51.0': optional: true - '@esbuild/darwin-x64@0.23.1': + '@oxfmt/binding-linux-arm64-musl@0.51.0': optional: true - '@esbuild/freebsd-arm64@0.19.12': + '@oxfmt/binding-linux-ppc64-gnu@0.51.0': optional: true - '@esbuild/freebsd-arm64@0.23.1': + '@oxfmt/binding-linux-riscv64-gnu@0.51.0': optional: true - '@esbuild/freebsd-x64@0.19.12': + '@oxfmt/binding-linux-riscv64-musl@0.51.0': optional: true - '@esbuild/freebsd-x64@0.23.1': + '@oxfmt/binding-linux-s390x-gnu@0.51.0': optional: true - '@esbuild/linux-arm64@0.19.12': + '@oxfmt/binding-linux-x64-gnu@0.51.0': optional: true - '@esbuild/linux-arm64@0.23.1': + '@oxfmt/binding-linux-x64-musl@0.51.0': optional: true - '@esbuild/linux-arm@0.19.12': + '@oxfmt/binding-openharmony-arm64@0.51.0': optional: true - '@esbuild/linux-arm@0.23.1': + '@oxfmt/binding-win32-arm64-msvc@0.51.0': optional: true - '@esbuild/linux-ia32@0.19.12': + '@oxfmt/binding-win32-ia32-msvc@0.51.0': optional: true - '@esbuild/linux-ia32@0.23.1': + '@oxfmt/binding-win32-x64-msvc@0.51.0': optional: true - '@esbuild/linux-loong64@0.19.12': + '@oxlint-tsgolint/darwin-arm64@0.23.0': optional: true - '@esbuild/linux-loong64@0.23.1': + '@oxlint-tsgolint/darwin-x64@0.23.0': optional: true - '@esbuild/linux-mips64el@0.19.12': + '@oxlint-tsgolint/linux-arm64@0.23.0': optional: true - '@esbuild/linux-mips64el@0.23.1': + '@oxlint-tsgolint/linux-x64@0.23.0': optional: true - '@esbuild/linux-ppc64@0.19.12': + '@oxlint-tsgolint/win32-arm64@0.23.0': optional: true - '@esbuild/linux-ppc64@0.23.1': + '@oxlint-tsgolint/win32-x64@0.23.0': optional: true - '@esbuild/linux-riscv64@0.19.12': + '@oxlint/binding-android-arm-eabi@1.66.0': optional: true - '@esbuild/linux-riscv64@0.23.1': + '@oxlint/binding-android-arm64@1.66.0': optional: true - '@esbuild/linux-s390x@0.19.12': + '@oxlint/binding-darwin-arm64@1.66.0': optional: true - '@esbuild/linux-s390x@0.23.1': + '@oxlint/binding-darwin-x64@1.66.0': optional: true - '@esbuild/linux-x64@0.19.12': + '@oxlint/binding-freebsd-x64@1.66.0': optional: true - '@esbuild/linux-x64@0.23.1': + '@oxlint/binding-linux-arm-gnueabihf@1.66.0': optional: true - '@esbuild/netbsd-x64@0.19.12': + '@oxlint/binding-linux-arm-musleabihf@1.66.0': optional: true - '@esbuild/netbsd-x64@0.23.1': + '@oxlint/binding-linux-arm64-gnu@1.66.0': optional: true - '@esbuild/openbsd-arm64@0.23.1': + '@oxlint/binding-linux-arm64-musl@1.66.0': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@oxlint/binding-linux-ppc64-gnu@1.66.0': optional: true - '@esbuild/openbsd-x64@0.23.1': + '@oxlint/binding-linux-riscv64-gnu@1.66.0': optional: true - '@esbuild/sunos-x64@0.19.12': + '@oxlint/binding-linux-riscv64-musl@1.66.0': optional: true - '@esbuild/sunos-x64@0.23.1': + '@oxlint/binding-linux-s390x-gnu@1.66.0': optional: true - '@esbuild/win32-arm64@0.19.12': + '@oxlint/binding-linux-x64-gnu@1.66.0': optional: true - '@esbuild/win32-arm64@0.23.1': + '@oxlint/binding-linux-x64-musl@1.66.0': optional: true - '@esbuild/win32-ia32@0.19.12': + '@oxlint/binding-openharmony-arm64@1.66.0': optional: true - '@esbuild/win32-ia32@0.23.1': + '@oxlint/binding-win32-arm64-msvc@1.66.0': optional: true - '@esbuild/win32-x64@0.19.12': + '@oxlint/binding-win32-ia32-msvc@1.66.0': optional: true - '@esbuild/win32-x64@0.23.1': + '@oxlint/binding-win32-x64-msvc@1.66.0': optional: true - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jridgewell/gen-mapping@0.3.3': + '@quansync/fs@1.0.0': dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.22 + quansync: 1.0.0 - '@jridgewell/resolve-uri@3.1.2': {} + '@rolldown/binding-android-arm64@1.0.0': + optional: true - '@jridgewell/set-array@1.1.2': {} + '@rolldown/binding-android-arm64@1.0.0-rc.18': + optional: true - '@jridgewell/sourcemap-codec@1.4.15': {} + '@rolldown/binding-darwin-arm64@1.0.0': + optional: true - '@jridgewell/sourcemap-codec@1.5.0': {} + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + optional: true - '@jridgewell/trace-mapping@0.3.22': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@rolldown/binding-darwin-x64@1.0.0': + optional: true - '@jsonjoy.com/base64@1.1.2(tslib@2.7.0)': - dependencies: - tslib: 2.7.0 + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + optional: true - '@jsonjoy.com/json-pack@1.1.0(tslib@2.7.0)': - dependencies: - '@jsonjoy.com/base64': 1.1.2(tslib@2.7.0) - '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) - hyperdyperid: 1.2.0 - thingies: 1.21.0(tslib@2.7.0) - tslib: 2.7.0 + '@rolldown/binding-freebsd-x64@1.0.0': + optional: true - '@jsonjoy.com/util@1.3.0(tslib@2.7.0)': - dependencies: - tslib: 2.7.0 + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + optional: true - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.23.9 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + optional: true - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.23.9 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + optional: true - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@rolldown/binding-linux-arm64-gnu@1.0.0': + optional: true - '@nodelib/fs.stat@2.0.5': {} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + '@rolldown/binding-linux-arm64-musl@1.0.0': + optional: true - '@pkgjs/parseargs@0.11.0': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': optional: true - '@rollup/rollup-android-arm-eabi@4.23.0': + '@rolldown/binding-linux-ppc64-gnu@1.0.0': optional: true - '@rollup/rollup-android-arm64@4.23.0': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': optional: true - '@rollup/rollup-darwin-arm64@4.23.0': + '@rolldown/binding-linux-s390x-gnu@1.0.0': optional: true - '@rollup/rollup-darwin-x64@4.23.0': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.23.0': + '@rolldown/binding-linux-x64-gnu@1.0.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.23.0': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': optional: true - '@rollup/rollup-linux-arm64-gnu@4.23.0': + '@rolldown/binding-linux-x64-musl@1.0.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.23.0': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': + '@rolldown/binding-openharmony-arm64@1.0.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.23.0': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': optional: true - '@rollup/rollup-linux-s390x-gnu@4.23.0': + '@rolldown/binding-wasm32-wasi@1.0.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rollup/rollup-linux-x64-gnu@4.23.0': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rollup/rollup-linux-x64-musl@4.23.0': + '@rolldown/binding-win32-arm64-msvc@1.0.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.23.0': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': optional: true - '@rollup/rollup-win32-ia32-msvc@4.23.0': + '@rolldown/binding-win32-x64-msvc@1.0.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.23.0': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': optional: true - '@tsconfig/node14@14.1.2': {} + '@rolldown/pluginutils@1.0.0': {} + + '@rolldown/pluginutils@1.0.0-rc.18': {} + + '@standard-schema/spec@1.1.0': {} - '@types/estree@1.0.6': {} + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true - '@types/mock-fs@4.13.4': + '@types/chai@5.2.3': dependencies: - '@types/node': 16.18.82 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - '@types/node@12.20.55': {} + '@types/deep-eql@4.0.2': {} - '@types/node@16.18.82': {} + '@types/estree@1.0.9': {} - '@types/picomatch@2.3.4': {} + '@types/jsesc@2.5.1': {} - '@types/semver@7.5.7': {} + '@types/node@12.20.55': {} + + '@types/node@22.19.18': + dependencies: + undici-types: 6.21.0 - '@types/supports-color@8.1.3': {} + '@types/picomatch@4.0.3': {} - '@vitest/expect@2.1.1': + '@vitest/expect@4.1.7': dependencies: - '@vitest/spy': 2.1.1 - '@vitest/utils': 2.1.1 - chai: 5.1.1 - tinyrainbow: 1.2.0 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.1.3(@types/node@16.18.82))': + '@vitest/mocker@4.1.7(vite@8.0.11(@types/node@22.19.18))': dependencies: - '@vitest/spy': 2.1.1 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 - magic-string: 0.30.11 + magic-string: 0.30.21 optionalDependencies: - vite: 5.1.3(@types/node@16.18.82) + vite: 8.0.11(@types/node@22.19.18) - '@vitest/pretty-format@2.1.1': + '@vitest/pretty-format@4.1.7': dependencies: - tinyrainbow: 1.2.0 + tinyrainbow: 3.1.0 - '@vitest/runner@2.1.1': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 2.1.1 - pathe: 1.1.2 + '@vitest/utils': 4.1.7 + pathe: 2.0.3 - '@vitest/snapshot@2.1.1': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 2.1.1 - magic-string: 0.30.11 - pathe: 1.1.2 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 + magic-string: 0.30.21 + pathe: 2.0.3 - '@vitest/spy@2.1.1': - dependencies: - tinyspy: 3.0.2 + '@vitest/spy@4.1.7': {} - '@vitest/utils@2.1.1': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 2.1.1 - loupe: 3.1.1 - tinyrainbow: 1.2.0 + '@vitest/pretty-format': 4.1.7 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} - ansi-regex@6.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 + ansis@4.2.0: {} argparse@1.0.10: dependencies: sprintf-js: 1.0.3 + argparse@2.0.1: {} + array-union@2.1.0: {} assertion-error@2.0.1: {} - balanced-match@1.0.2: {} + ast-kit@3.0.0-beta.1: + dependencies: + '@babel/parser': 8.0.0-rc.4 + estree-walker: 3.0.3 + pathe: 2.0.3 better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - binary-extensions@2.2.0: {} - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.2: - dependencies: - fill-range: 7.0.1 + birpc@4.0.0: {} - bundle-require@5.0.0(esbuild@0.23.1): + braces@3.0.3: dependencies: - esbuild: 0.23.1 - load-tsconfig: 0.2.5 + fill-range: 7.1.1 - cac@6.7.14: {} + cac@7.0.0: {} - chai@5.1.1: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.1 - pathval: 2.0.0 + chai@6.2.2: {} changesets-changelog-clean@1.3.0: dependencies: '@changesets/get-github-info': 0.6.0 - '@changesets/types': 6.0.0 + '@changesets/types': 6.1.0 transitivePeerDependencies: - encoding - chardet@0.7.0: {} - - check-error@2.1.1: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - ci-info@3.9.0: {} - - clipanion@3.2.1(typanion@3.14.0): - dependencies: - typanion: 3.14.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} + chardet@2.1.1: {} - commander@4.1.1: {} + convert-source-map@2.0.0: {} - consola@3.2.3: {} - - cross-spawn@5.1.0: - dependencies: - lru-cache: 4.1.5 - shebang-command: 1.2.0 - which: 1.3.1 - - cross-spawn@7.0.3: + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -2177,127 +2327,54 @@ snapshots: dataloader@1.4.0: {} - debug@4.3.7(supports-color@9.4.0): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 9.4.0 - - deep-eql@5.0.2: {} + defu@6.1.7: {} detect-indent@6.1.0: {} + detect-libc@2.1.2: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - eastasianwidth@0.2.0: {} - - emoji-regex@8.0.0: {} + dts-resolver@3.0.0: {} - emoji-regex@9.2.2: {} + empathic@2.0.1: {} enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - esbuild@0.19.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - esbuild@0.23.1: - 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 + es-module-lexer@2.1.0: {} esprima@4.0.1: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.9 - execa@5.1.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 + expect-type@1.3.0: {} extendable-error@0.1.7: {} - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.8 - fastq@1.17.1: + fastq@1.20.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 - fdir@6.4.0(picomatch@4.0.2): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.4 - fill-range@7.0.1: + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2306,11 +2383,6 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - foreground-child@3.1.1: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -2326,11 +2398,7 @@ snapshots: fsevents@2.3.3: optional: true - get-func-name@2.0.2: {} - - get-stream@6.0.1: {} - - get-tsconfig@4.8.1: + get-tsconfig@5.0.0-beta.5: dependencies: resolve-pkg-maps: 1.0.0 @@ -2338,53 +2406,43 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.3.10: + glob-to-regex.js@1.2.0(tslib@2.8.1): dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 + tslib: 2.8.1 globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 + fast-glob: 3.3.3 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 graceful-fs@4.2.11: {} - human-id@1.0.2: {} + hookable@6.1.1: {} - human-signals@2.1.0: {} + human-id@4.1.3: {} hyperdyperid@1.2.0: {} - iconv-lite@0.4.24: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 - ignore@5.3.1: {} + ignore@5.3.2: {} - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.2.0 + import-without-cache@0.4.0: {} is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} - is-stream@2.0.1: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -2393,111 +2451,171 @@ snapshots: isexe@2.0.0: {} - jackspeak@2.3.6: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - joycon@3.1.1: {} - - js-yaml@3.14.1: + js-yaml@3.14.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - lilconfig@3.1.2: {} + lightningcss-android-arm64@1.32.0: + optional: true - lines-and-columns@1.2.4: {} + lightningcss-darwin-arm64@1.32.0: + optional: true - load-tsconfig@0.2.5: {} + lightningcss-darwin-x64@1.32.0: + optional: true - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 + lightningcss-freebsd-x64@1.32.0: + optional: true - lodash.sortby@4.7.0: {} + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true - lodash.startcase@4.4.0: {} + lightningcss-linux-arm64-gnu@1.32.0: + optional: true - loupe@3.1.1: - dependencies: - get-func-name: 2.0.2 + lightningcss-linux-arm64-musl@1.32.0: + optional: true - lru-cache@10.2.0: {} + lightningcss-linux-x64-gnu@1.32.0: + optional: true - lru-cache@4.1.5: - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 + lightningcss-linux-x64-musl@1.32.0: + optional: true - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true - magic-string@0.30.11: + lightningcss@1.32.0: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 - memfs@4.12.0: + locate-path@5.0.0: dependencies: - '@jsonjoy.com/json-pack': 1.1.0(tslib@2.7.0) - '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) - tree-dump: 1.0.2(tslib@2.7.0) - tslib: 2.7.0 + p-locate: 4.1.0 - merge-stream@2.0.0: {} + lodash.startcase@4.4.0: {} - merge2@1.4.1: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 - micromatch@4.0.5: + memfs@4.57.2(tslib@2.8.1): dependencies: - braces: 3.0.2 - picomatch: 2.3.1 + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 - mimic-fn@2.1.0: {} + merge2@1.4.1: {} - minimatch@9.0.3: + micromatch@4.0.8: dependencies: - brace-expansion: 2.0.1 - - minipass@7.0.4: {} + braces: 3.0.3 + picomatch: 2.3.2 mri@1.2.0: {} - ms@2.1.3: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.7: {} + nanoid@3.3.12: {} node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 + obug@2.1.1: {} - object-assign@4.1.1: {} + outdent@0.5.0: {} - onetime@5.1.2: + oxfmt@0.51.0: dependencies: - mimic-fn: 2.1.0 - - os-tmpdir@1.0.2: {} - - outdent@0.5.0: {} + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.51.0 + '@oxfmt/binding-android-arm64': 0.51.0 + '@oxfmt/binding-darwin-arm64': 0.51.0 + '@oxfmt/binding-darwin-x64': 0.51.0 + '@oxfmt/binding-freebsd-x64': 0.51.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.51.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.51.0 + '@oxfmt/binding-linux-arm64-gnu': 0.51.0 + '@oxfmt/binding-linux-arm64-musl': 0.51.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.51.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.51.0 + '@oxfmt/binding-linux-riscv64-musl': 0.51.0 + '@oxfmt/binding-linux-s390x-gnu': 0.51.0 + '@oxfmt/binding-linux-x64-gnu': 0.51.0 + '@oxfmt/binding-linux-x64-musl': 0.51.0 + '@oxfmt/binding-openharmony-arm64': 0.51.0 + '@oxfmt/binding-win32-arm64-msvc': 0.51.0 + '@oxfmt/binding-win32-ia32-msvc': 0.51.0 + '@oxfmt/binding-win32-x64-msvc': 0.51.0 + + oxlint-tsgolint@0.23.0: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 0.23.0 + '@oxlint-tsgolint/darwin-x64': 0.23.0 + '@oxlint-tsgolint/linux-arm64': 0.23.0 + '@oxlint-tsgolint/linux-x64': 0.23.0 + '@oxlint-tsgolint/win32-arm64': 0.23.0 + '@oxlint-tsgolint/win32-x64': 0.23.0 + + oxlint@1.66.0(oxlint-tsgolint@0.23.0): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.66.0 + '@oxlint/binding-android-arm64': 1.66.0 + '@oxlint/binding-darwin-arm64': 1.66.0 + '@oxlint/binding-darwin-x64': 1.66.0 + '@oxlint/binding-freebsd-x64': 1.66.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.66.0 + '@oxlint/binding-linux-arm-musleabihf': 1.66.0 + '@oxlint/binding-linux-arm64-gnu': 1.66.0 + '@oxlint/binding-linux-arm64-musl': 1.66.0 + '@oxlint/binding-linux-ppc64-gnu': 1.66.0 + '@oxlint/binding-linux-riscv64-gnu': 1.66.0 + '@oxlint/binding-linux-riscv64-musl': 1.66.0 + '@oxlint/binding-linux-s390x-gnu': 1.66.0 + '@oxlint/binding-linux-x64-gnu': 1.66.0 + '@oxlint/binding-linux-x64-musl': 1.66.0 + '@oxlint/binding-openharmony-arm64': 1.66.0 + '@oxlint/binding-win32-arm64-msvc': 1.66.0 + '@oxlint/binding-win32-ia32-msvc': 1.66.0 + '@oxlint/binding-win32-x64-msvc': 1.66.0 + oxlint-tsgolint: 0.23.0 p-filter@2.1.0: dependencies: @@ -2515,109 +2633,110 @@ snapshots: p-try@2.2.0: {} - package-manager-detector@0.2.0: {} - - parse-ms@3.0.0: {} + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 path-exists@4.0.0: {} path-key@3.1.1: {} - path-scurry@1.10.1: - dependencies: - lru-cache: 10.2.0 - minipass: 7.0.4 - path-type@4.0.0: {} - pathe@1.1.2: {} + pathe@2.0.3: {} - pathval@2.0.0: {} + picocolors@1.1.1: {} - picocolors@1.1.0: {} + picomatch@2.3.2: {} - picomatch@2.3.1: {} - - picomatch@4.0.2: {} + picomatch@4.0.4: {} pify@4.0.1: {} - pirates@4.0.6: {} - - postcss-load-config@6.0.1(postcss@8.4.35)(tsx@4.19.1): - dependencies: - lilconfig: 3.1.2 - optionalDependencies: - postcss: 8.4.35 - tsx: 4.19.1 - - postcss@8.4.35: - dependencies: - nanoid: 3.3.7 - picocolors: 1.1.0 - source-map-js: 1.0.2 - - prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.2): + postcss@8.5.14: dependencies: - prettier: 3.3.3 - typescript: 5.6.2 + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 prettier@2.8.8: {} - prettier@3.3.3: {} - - pretty-bytes@6.1.1: {} + quansync@0.2.11: {} - pretty-ms@8.0.0: - dependencies: - parse-ms: 3.0.0 - - pseudomap@1.0.2: {} - - punycode@2.3.1: {} + quansync@1.0.0: {} queue-microtask@1.2.3: {} read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 - js-yaml: 3.14.1 + js-yaml: 3.14.2 pify: 4.0.1 strip-bom: 3.0.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - regenerator-runtime@0.14.1: {} - resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} - reusify@1.0.4: {} + reusify@1.1.0: {} - rollup@4.23.0: + rolldown-plugin-dts@0.25.0(rolldown@1.0.0)(typescript@6.0.3): dependencies: - '@types/estree': 1.0.6 + '@babel/generator': 8.0.0-rc.4 + '@babel/helper-validator-identifier': 8.0.0-rc.4 + '@babel/parser': 8.0.0-rc.4 + ast-kit: 3.0.0-beta.1 + birpc: 4.0.0 + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.1 + rolldown: 1.0.0 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.23.0 - '@rollup/rollup-android-arm64': 4.23.0 - '@rollup/rollup-darwin-arm64': 4.23.0 - '@rollup/rollup-darwin-x64': 4.23.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.23.0 - '@rollup/rollup-linux-arm-musleabihf': 4.23.0 - '@rollup/rollup-linux-arm64-gnu': 4.23.0 - '@rollup/rollup-linux-arm64-musl': 4.23.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.23.0 - '@rollup/rollup-linux-riscv64-gnu': 4.23.0 - '@rollup/rollup-linux-s390x-gnu': 4.23.0 - '@rollup/rollup-linux-x64-gnu': 4.23.0 - '@rollup/rollup-linux-x64-musl': 4.23.0 - '@rollup/rollup-win32-arm64-msvc': 4.23.0 - '@rollup/rollup-win32-ia32-msvc': 4.23.0 - '@rollup/rollup-win32-x64-msvc': 4.23.0 - fsevents: 2.3.3 + typescript: 6.0.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.0.0: + dependencies: + '@oxc-project/types': 0.129.0 + '@rolldown/pluginutils': 1.0.0 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0 + '@rolldown/binding-darwin-arm64': 1.0.0 + '@rolldown/binding-darwin-x64': 1.0.0 + '@rolldown/binding-freebsd-x64': 1.0.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0 + '@rolldown/binding-linux-arm64-gnu': 1.0.0 + '@rolldown/binding-linux-arm64-musl': 1.0.0 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0 + '@rolldown/binding-linux-s390x-gnu': 1.0.0 + '@rolldown/binding-linux-x64-gnu': 1.0.0 + '@rolldown/binding-linux-x64-musl': 1.0.0 + '@rolldown/binding-openharmony-arm64': 1.0.0 + '@rolldown/binding-wasm32-wasi': 1.0.0 + '@rolldown/binding-win32-arm64-msvc': 1.0.0 + '@rolldown/binding-win32-x64-msvc': 1.0.0 + + rolldown@1.0.0-rc.18: + dependencies: + '@oxc-project/types': 0.128.0 + '@rolldown/pluginutils': 1.0.0-rc.18 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-x64': 1.0.0-rc.18 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.18 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.18 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.18 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.18 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.18 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.18 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.18 run-parallel@1.2.0: dependencies: @@ -2625,115 +2744,57 @@ snapshots: safer-buffer@2.1.2: {} - semver@7.6.0: - dependencies: - lru-cache: 6.0.0 - - shebang-command@1.2.0: - dependencies: - shebang-regex: 1.0.0 + semver@7.8.0: {} shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} siginfo@2.0.0: {} - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} slash@3.0.0: {} - source-map-js@1.0.2: {} - - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map-js@1.2.1: {} - spawndamnit@2.0.0: + spawndamnit@3.0.1: dependencies: - cross-spawn: 5.1.0 - signal-exit: 3.0.7 + cross-spawn: 7.0.6 + signal-exit: 4.1.0 sprintf-js@1.0.3: {} stackback@0.0.2: {} - std-env@3.7.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + std-env@4.1.0: {} strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.0.1 - strip-bom@3.0.0: {} - strip-final-newline@2.0.0: {} - - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - commander: 4.1.1 - glob: 10.3.10 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - - supports-color@9.4.0: {} - term-size@2.2.1: {} - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - thingies@1.21.0(tslib@2.7.0): + thingies@2.6.0(tslib@2.8.1): dependencies: - tslib: 2.7.0 + tslib: 2.8.1 tinybench@2.9.0: {} - tinyexec@0.3.0: {} + tinyexec@1.1.2: {} - tinyglobby@0.2.9: + tinyglobby@0.2.16: dependencies: - fdir: 6.4.0(picomatch@4.0.2) - picomatch: 4.0.2 - - tinypool@1.0.1: {} + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 - tinyrainbow@1.2.0: {} + tinypool@2.1.0: {} - tinyspy@3.0.2: {} - - tmp@0.0.33: - dependencies: - os-tmpdir: 1.0.2 + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: @@ -2741,137 +2802,95 @@ snapshots: tr46@0.0.3: {} - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - - tree-dump@1.0.2(tslib@2.7.0): + tree-dump@1.1.0(tslib@2.8.1): dependencies: - tslib: 2.7.0 + tslib: 2.8.1 tree-kill@1.2.2: {} - ts-interface-checker@0.1.13: {} - - tslib@2.7.0: {} - - tsup@8.3.0(postcss@8.4.35)(supports-color@9.4.0)(tsx@4.19.1)(typescript@5.6.2): - dependencies: - bundle-require: 5.0.0(esbuild@0.23.1) - cac: 6.7.14 - chokidar: 3.6.0 - consola: 3.2.3 - debug: 4.3.7(supports-color@9.4.0) - esbuild: 0.23.1 - execa: 5.1.1 - joycon: 3.1.1 - picocolors: 1.1.0 - postcss-load-config: 6.0.1(postcss@8.4.35)(tsx@4.19.1) - resolve-from: 5.0.0 - rollup: 4.23.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tinyglobby: 0.2.9 + tsdown@0.22.0(typescript@6.0.3): + dependencies: + ansis: 4.2.0 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.1 + picomatch: 4.0.4 + rolldown: 1.0.0 + rolldown-plugin-dts: 0.25.0(rolldown@1.0.0)(typescript@6.0.3) + semver: 7.8.0 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 tree-kill: 1.2.2 + unconfig-core: 7.5.0 optionalDependencies: - postcss: 8.4.35 - typescript: 5.6.2 + typescript: 6.0.3 transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - vue-tsc - tsx@4.19.1: - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 - optionalDependencies: - fsevents: 2.3.3 + tslib@2.8.1: {} + + typescript@6.0.3: {} - typanion@3.14.0: {} + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 - typescript@5.6.2: {} + undici-types@6.21.0: {} universalify@0.1.2: {} - vite-node@2.1.1(@types/node@16.18.82)(supports-color@9.4.0): + vite@8.0.11(@types/node@22.19.18): dependencies: - cac: 6.7.14 - debug: 4.3.7(supports-color@9.4.0) - pathe: 1.1.2 - vite: 5.1.3(@types/node@16.18.82) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - - vite@5.1.3(@types/node@16.18.82): - dependencies: - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.23.0 + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.14 + rolldown: 1.0.0-rc.18 + tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 16.18.82 + '@types/node': 22.19.18 fsevents: 2.3.3 - vitest@2.1.1(@types/node@16.18.82)(supports-color@9.4.0): - dependencies: - '@vitest/expect': 2.1.1 - '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.1.3(@types/node@16.18.82)) - '@vitest/pretty-format': 2.1.1 - '@vitest/runner': 2.1.1 - '@vitest/snapshot': 2.1.1 - '@vitest/spy': 2.1.1 - '@vitest/utils': 2.1.1 - chai: 5.1.1 - debug: 4.3.7(supports-color@9.4.0) - magic-string: 0.30.11 - pathe: 1.1.2 - std-env: 3.7.0 + vitest@4.1.7(@types/node@22.19.18)(vite@8.0.11(@types/node@22.19.18)): + dependencies: + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@8.0.11(@types/node@22.19.18)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.0 - tinypool: 1.0.1 - tinyrainbow: 1.2.0 - vite: 5.1.3(@types/node@16.18.82) - vite-node: 2.1.1(@types/node@16.18.82)(supports-color@9.4.0) + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.11(@types/node@22.19.18) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 16.18.82 + '@types/node': 22.19.18 transitivePeerDependencies: - - less - - lightningcss - msw - - sass - - stylus - - sugarss - - supports-color - - terser webidl-conversions@3.0.1: {} - webidl-conversions@4.0.2: {} - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - - which@1.3.1: - dependencies: - isexe: 2.0.0 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -2880,19 +2899,3 @@ snapshots: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - yallist@2.1.2: {} - - yallist@4.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e40dcf1 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +allowBuilds: + esbuild: set this to true or false + +engineStrict: true + +nodeVersion: '24.13.0' diff --git a/src/__snapshots__/analyze.test.ts.snap b/src/__snapshots__/analyze.test.ts.snap new file mode 100644 index 0000000..be12a91 --- /dev/null +++ b/src/__snapshots__/analyze.test.ts.snap @@ -0,0 +1,191 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`analyze > accepts custom globs 1`] = ` +{ + "failures": [], + "files": [ + { + "filePath": "/node_modules/dep1/.npmrc", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.npmrc", + "original": "*.npmrc", + }, + ], + }, + { + "filePath": "/node_modules/dep2/CHANGELOG.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", + "original": "*.@(md|mkd|markdown|mdown)", + }, + { + "derived": "/node_modules/**/changelog*(.*)", + "original": "changelog*(.*)", + }, + ], + }, + { + "filePath": "/node_modules/dep4/nonDefaultFile.ext", + "includedByDefault": false, + "includedByGlobs": [ + { + "derived": "/node_modules/**/nonDefaultFile.ext", + "original": "**/nonDefaultFile.ext", + }, + ], + }, + { + "filePath": "/node_modules/dep1/__tests__/test1.js", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/__tests__/**", + "original": "__tests__/", + }, + ], + }, + { + "filePath": "/node_modules/dep1/__tests__/test2.js", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/__tests__/**", + "original": "__tests__/", + }, + ], + }, + { + "filePath": "/node_modules/dep1/a-dir/doc.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", + "original": "*.@(md|mkd|markdown|mdown)", + }, + ], + }, + ], +} +`; + +exports[`analyze > allows skipping default globs 1`] = ` +{ + "failures": [], + "files": [ + { + "filePath": "/node_modules/dep4/nonDefaultFile.ext", + "includedByDefault": false, + "includedByGlobs": [ + { + "derived": "/node_modules/**/nonDefaultFile.ext", + "original": "**/nonDefaultFile.ext", + }, + ], + }, + ], +} +`; + +exports[`analyze > returns information about the files that would be removed 1`] = ` +{ + "failures": [], + "files": [ + { + "filePath": "/node_modules/dep1/.npmrc", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.npmrc", + "original": "*.npmrc", + }, + ], + }, + { + "filePath": "/node_modules/dep2/CHANGELOG.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", + "original": "*.@(md|mkd|markdown|mdown)", + }, + { + "derived": "/node_modules/**/changelog*(.*)", + "original": "changelog*(.*)", + }, + ], + }, + { + "filePath": "/node_modules/dep1/__tests__/test1.js", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/__tests__/**", + "original": "__tests__/", + }, + ], + }, + { + "filePath": "/node_modules/dep1/__tests__/test2.js", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/__tests__/**", + "original": "__tests__/", + }, + ], + }, + { + "filePath": "/node_modules/dep1/a-dir/doc.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", + "original": "*.@(md|mkd|markdown|mdown)", + }, + ], + }, + ], +} +`; + +exports[`analyze > uses custom glob file if provided 1`] = ` +{ + "failures": [], + "files": [ + { + "filePath": "/node_modules/dep2/CHANGELOG.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/**.md", + "original": "**.md", + }, + ], + }, + { + "filePath": "/node_modules/dep4/nonDefaultFile.ext", + "includedByDefault": false, + "includedByGlobs": [ + { + "derived": "/node_modules/**/nonDefaultFile.ext", + "original": "**/nonDefaultFile.ext", + }, + ], + }, + { + "filePath": "/node_modules/dep1/a-dir/doc.md", + "includedByDefault": true, + "includedByGlobs": [ + { + "derived": "/node_modules/**/**.md", + "original": "**.md", + }, + ], + }, + ], +} +`; diff --git a/src/__test__/getMockedFileStructure.ts b/src/__test__/getMockedFileStructure.ts index f3b6cf3..fdcb6ee 100644 --- a/src/__test__/getMockedFileStructure.ts +++ b/src/__test__/getMockedFileStructure.ts @@ -1,16 +1,17 @@ -import fs from 'fs'; +import type fs from 'node:fs'; +import type { NestedDirectoryJSON } from 'memfs'; import { vi } from 'vitest'; import { DEFAULT_GLOBS_FILE_PATH } from '../shared.js'; -export async function getMockedFileStructure(): Promise> { +export async function getMockedFileStructure(): Promise { const actualFs = await vi.importActual('fs/promises'); - const defaultGlobs = (await actualFs.readFile(DEFAULT_GLOBS_FILE_PATH)).toString(); + const defaultGlobs = await actualFs.readFile(DEFAULT_GLOBS_FILE_PATH, { encoding: 'utf8' }); return { [DEFAULT_GLOBS_FILE_PATH]: defaultGlobs, node_modules: { dep1: { - __tests__: { + '__tests__': { 'test1.js': '.', 'test2.js': '.', }, diff --git a/src/__test__/path.serializer.ts b/src/__test__/path.serializer.ts index 546a55c..6dc87c1 100644 --- a/src/__test__/path.serializer.ts +++ b/src/__test__/path.serializer.ts @@ -1,16 +1,16 @@ -import { expect } from 'vitest'; +import type { expect } from 'vitest'; -function normalizePathPart(str: string) { - return str.replace(/\\/g, '/'); // replace windows backslashes +function normalizePathPart(str: string): string { + return str.replaceAll('\\', '/'); // replace windows backslashes } -function normalizePath(str: string) { +function normalizePath(str: string): string { const normalizedCwd = normalizePathPart(process.cwd()); const normalizedStr = normalizePathPart(str); return normalizedStr.replace(normalizedCwd, ''); } -function shouldNormalizePath(val: any) { +function shouldNormalizePath(val: unknown): val is string { if (typeof val === 'string') { return normalizePath(val) !== val; } diff --git a/src/analyze.test.ts b/src/analyze.test.ts index 18ad454..0765e66 100644 --- a/src/analyze.test.ts +++ b/src/analyze.test.ts @@ -1,213 +1,45 @@ import { vol } from 'memfs'; -import { beforeEach, describe, expect, it } from 'vitest'; +import { vi, beforeEach, describe, expect, it } from 'vitest'; import { getMockedFileStructure } from './__test__/getMockedFileStructure.js'; import { analyze } from './analyze.js'; +vi.setConfig({ testTimeout: 5000 }); + const fileStructure = await getMockedFileStructure(); -beforeEach(async () => { - vol.fromNestedJSON(fileStructure); -}); +describe(analyze, () => { + beforeEach(() => { + vol.fromNestedJSON(fileStructure); + }); -describe(analyze.name, () => { it('returns information about the files that would be removed', async () => { + expect.hasAssertions(); + const result = await analyze(); - expect(result).toMatchInlineSnapshot(` - [ - { - "filePath": "/node_modules/dep1/.npmrc", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/.npmrc", - "original": ".npmrc", - }, - { - "derived": "/node_modules/**/.npmrc", - "original": ".npmrc", - }, - ], - }, - { - "filePath": "/node_modules/dep2/CHANGELOG.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", - "original": "*.@(md|mkd|markdown|mdown)", - }, - { - "derived": "/node_modules/**/changelog*(.*)", - "original": "changelog*(.*)", - }, - ], - }, - { - "filePath": "/node_modules/dep1/__tests__/test1.js", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/__tests__/**", - "original": "__tests__/", - }, - ], - }, - { - "filePath": "/node_modules/dep1/__tests__/test2.js", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/__tests__/**", - "original": "__tests__/", - }, - ], - }, - { - "filePath": "/node_modules/dep1/a-dir/doc.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", - "original": "*.@(md|mkd|markdown|mdown)", - }, - ], - }, - ] - `); + expect(result).toMatchSnapshot(); }); it('accepts custom globs', async () => { + expect.hasAssertions(); + const result = await analyze({ globs: ['**/nonDefaultFile.ext'] }); - expect(result).toMatchInlineSnapshot(` - [ - { - "filePath": "/node_modules/dep1/.npmrc", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/.npmrc", - "original": ".npmrc", - }, - { - "derived": "/node_modules/**/.npmrc", - "original": ".npmrc", - }, - ], - }, - { - "filePath": "/node_modules/dep2/CHANGELOG.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", - "original": "*.@(md|mkd|markdown|mdown)", - }, - { - "derived": "/node_modules/**/changelog*(.*)", - "original": "changelog*(.*)", - }, - ], - }, - { - "filePath": "/node_modules/dep4/nonDefaultFile.ext", - "includedByDefault": false, - "includedByGlobs": [ - { - "derived": "/node_modules/**/**/nonDefaultFile.ext", - "original": "**/nonDefaultFile.ext", - }, - ], - }, - { - "filePath": "/node_modules/dep1/__tests__/test1.js", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/__tests__/**", - "original": "__tests__/", - }, - ], - }, - { - "filePath": "/node_modules/dep1/__tests__/test2.js", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/__tests__/**", - "original": "__tests__/", - }, - ], - }, - { - "filePath": "/node_modules/dep1/a-dir/doc.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/*.@(md|mkd|markdown|mdown)", - "original": "*.@(md|mkd|markdown|mdown)", - }, - ], - }, - ] - `); + expect(result).toMatchSnapshot(); }); it('allows skipping default globs', async () => { + expect.hasAssertions(); + const result = await analyze({ noDefaults: true, globs: ['**/nonDefaultFile.ext'] }); - expect(result).toMatchInlineSnapshot(` - [ - { - "filePath": "/node_modules/dep4/nonDefaultFile.ext", - "includedByDefault": false, - "includedByGlobs": [ - { - "derived": "/node_modules/**/**/nonDefaultFile.ext", - "original": "**/nonDefaultFile.ext", - }, - ], - }, - ] - `); + expect(result).toMatchSnapshot(); }); it('uses custom glob file if provided', async () => { + expect.hasAssertions(); + const customGlobFile = '.custom-glob-file'; vol.fromNestedJSON({ ...fileStructure, [customGlobFile]: '**.md\n**/nonDefaultFile.ext' }); const result = await analyze({ noDefaults: true, globFile: customGlobFile }); - expect(result).toMatchInlineSnapshot(` - [ - { - "filePath": "/node_modules/dep2/CHANGELOG.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/**.md", - "original": "**.md", - }, - ], - }, - { - "filePath": "/node_modules/dep4/nonDefaultFile.ext", - "includedByDefault": false, - "includedByGlobs": [ - { - "derived": "/node_modules/**/**/nonDefaultFile.ext", - "original": "**/nonDefaultFile.ext", - }, - ], - }, - { - "filePath": "/node_modules/dep1/a-dir/doc.md", - "includedByDefault": true, - "includedByGlobs": [ - { - "derived": "/node_modules/**/**.md", - "original": "**.md", - }, - ], - }, - ] - `); + expect(result).toMatchSnapshot(); }); }); diff --git a/src/analyze.ts b/src/analyze.ts index 972dec6..68ed906 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -1,50 +1,58 @@ -import { SharedOptions, sharedDefaultOptions } from './shared.js'; +import type { CleanFailure, SharedOptions } from './shared.js'; +import { sharedDefaultOptions } from './shared.js'; import { findFilesByGlobLists, getGlobLists, makeGlobMatcher, + mergeGlobLists, optimizeGlobLists, parseDefaultGlobsFile, toAbsoluteGlobLists, toPosixPath, } from './utils/glob.js'; -export interface GlobVersions { +export type GlobVersions = { /** The original glob, as provided by the glob file or user. */ original: string; /** The glob as it was derived by clean-modules and passed to picomatch. */ derived: string; -} +}; -export interface AnalyzeResult { +export type AnalyzedFile = { /** The absolute path to the file. */ filePath: string; /** Whether the file was included by clean-modules' default globs. */ includedByDefault: boolean; /** List of globs that included the file. */ includedByGlobs: GlobVersions[]; -} +}; + +export type AnalyzeResult = { + /** Files that would be cleaned up by the corresponding `clean` call. */ + files: AnalyzedFile[]; + /** Non-fatal failures encountered while crawling the directory tree. */ + failures: CleanFailure[]; +}; export type AnalyzeOptions = SharedOptions; /** * Helps determining why a file is included by the `clean` operation without removing any files. Extra globs can be passed as positional args. * @param options analyze options - * @returns list of files that were included by the clean operation and what globs they were included by + * @returns analyzed files alongside any crawl failures */ -export async function analyze(options: AnalyzeOptions = {}): Promise { +export async function analyze(options: AnalyzeOptions = {}): Promise { const mergedOptions = { ...sharedDefaultOptions, ...options }; const { globs, noDefaults, globFile, directory } = mergedOptions; const nodeModulesPath = directory || sharedDefaultOptions.directory; - const globLists = await getGlobLists({ globs, noDefaults, globFile }); - const includedFiles = await findFilesByGlobLists(nodeModulesPath, globLists); + const defaultGlobLists = await parseDefaultGlobsFile(); + const customGlobLists = await getGlobLists({ globs, noDefaults: true, globFile }); + const globLists = noDefaults ? customGlobLists : mergeGlobLists(defaultGlobLists, customGlobLists); - const defaultGlobs = toAbsoluteGlobLists( - optimizeGlobLists(await parseDefaultGlobsFile()), - nodeModulesPath - ); + const { files: includedFiles, failures } = await findFilesByGlobLists(nodeModulesPath, globLists); + const defaultGlobs = toAbsoluteGlobLists(optimizeGlobLists(defaultGlobLists), nodeModulesPath); const includedByDefaultMatcher = makeGlobMatcher(defaultGlobs.included, { ignore: defaultGlobs.excluded, }); @@ -55,18 +63,18 @@ export async function analyze(options: AnalyzeOptions = {}): Promise { + const analyzedFiles: AnalyzedFile[] = includedFiles.map(filePath => { const includedByDefault = includedByDefaultMatcher(filePath); - const includedByGlobs: { original: string; derived: string }[] = []; + const includedByGlobs: GlobVersions[] = []; for (const { original, derived, matcher } of globMatchers) { if (matcher(filePath)) { - includedByGlobs.push({ original, derived }); + includedByGlobs.push({ original: original ?? '', derived }); } } return { filePath, includedByDefault, includedByGlobs }; }); - return analyzedResults; + return { files: analyzedFiles, failures }; } diff --git a/src/clean.test.ts b/src/clean.test.ts index f51c31e..31ae2b6 100644 --- a/src/clean.test.ts +++ b/src/clean.test.ts @@ -1,19 +1,26 @@ +import fs from 'node:fs'; +import path from 'node:path'; import { vol } from 'memfs'; -import { beforeEach, describe, expect, it } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { getMockedFileStructure } from './__test__/getMockedFileStructure.js'; import { clean } from './clean.js'; +vi.setConfig({ testTimeout: 5000 }); + const fileStructure = await getMockedFileStructure(); -beforeEach(async () => { - vol.fromNestedJSON(fileStructure); -}); +describe(clean, () => { + beforeEach(() => { + vol.fromNestedJSON(fileStructure); + }); -describe(clean.name, () => { it('cleans up expected files by default', async () => { + expect.hasAssertions(); + const result = await clean(); expect(result).toMatchInlineSnapshot(` { + "failures": [], "files": [ "/node_modules/dep1/.npmrc", "/node_modules/dep2/CHANGELOG.md", @@ -23,14 +30,18 @@ describe(clean.name, () => { ], "reducedSize": 5, "removedEmptyDirs": 3, + "removedFilesCount": 5, } `); }); it('accepts custom globs', async () => { + expect.hasAssertions(); + const result = await clean({ globs: ['**/nonDefaultFile.ext'] }); expect(result).toMatchInlineSnapshot(` { + "failures": [], "files": [ "/node_modules/dep1/.npmrc", "/node_modules/dep2/CHANGELOG.md", @@ -41,30 +52,38 @@ describe(clean.name, () => { ], "reducedSize": 6, "removedEmptyDirs": 4, + "removedFilesCount": 6, } `); }); it('allows skipping default globs', async () => { + expect.hasAssertions(); + const result = await clean({ noDefaults: true, globs: ['**/nonDefaultFile.ext'] }); expect(result).toMatchInlineSnapshot(` { + "failures": [], "files": [ "/node_modules/dep4/nonDefaultFile.ext", ], "reducedSize": 1, "removedEmptyDirs": 1, + "removedFilesCount": 1, } `); }); it('uses custom glob file if provided', async () => { + expect.hasAssertions(); + const customGlobFile = '.custom-glob-file'; vol.fromNestedJSON({ ...fileStructure, [customGlobFile]: '**.md\n**/nonDefaultFile.ext' }); const result = await clean({ noDefaults: true, globFile: customGlobFile }); expect(result).toMatchInlineSnapshot(` { + "failures": [], "files": [ "/node_modules/dep2/CHANGELOG.md", "/node_modules/dep4/nonDefaultFile.ext", @@ -72,12 +91,70 @@ describe(clean.name, () => { ], "reducedSize": 3, "removedEmptyDirs": 2, + "removedFilesCount": 3, } `); }); it('keeps empty directories if specified', async () => { + expect.hasAssertions(); + const result = await clean({ keepEmpty: true }); expect(result.removedEmptyDirs).toBe(0); }); }); + +describe('symlink handling', () => { + beforeEach(() => { + vol.fromNestedJSON({ + ...fileStructure, + node_modules: { + '.pnpm': { + 'foo@1.0.0': { + node_modules: { + foo: { + '__tests__': { 'a.js': '.' }, + 'index.js': '.', + 'CHANGELOG.md': '.', + }, + }, + }, + }, + }, + }); + + vol.symlinkSync( + path.resolve('node_modules/.pnpm/foo@1.0.0/node_modules/foo'), + path.resolve('node_modules/foo') + ); + }); + + it('matches files exactly once via their real .pnpm path', async () => { + expect.hasAssertions(); + + const result = await clean(); + + const realChangelog = path.resolve('node_modules/.pnpm/foo@1.0.0/node_modules/foo/CHANGELOG.md'); + const linkedChangelog = path.resolve('node_modules/foo/CHANGELOG.md'); + + expect(result.files).toContain(realChangelog); + expect(result.files).not.toContain(linkedChangelog); + + const realTest = path.resolve('node_modules/.pnpm/foo@1.0.0/node_modules/foo/__tests__/a.js'); + expect(result.files).toContain(realTest); + + // No duplicates โ€” every matched path appears exactly once. + expect(new Set(result.files).size).toBe(result.files.length); + expect(result.failures).toStrictEqual([]); + }); + + it('leaves the symlink itself in place when its name does not match any glob', async () => { + expect.hasAssertions(); + + await clean(); + + // The symlink at node_modules/foo doesn't match any default glob, so it survives cleanup. + expect(fs.existsSync(path.resolve('node_modules/foo'))).toBe(true); + expect(fs.lstatSync(path.resolve('node_modules/foo')).isSymbolicLink()).toBe(true); + }); +}); diff --git a/src/clean.ts b/src/clean.ts index 2be4c7c..557afef 100644 --- a/src/clean.ts +++ b/src/clean.ts @@ -1,4 +1,5 @@ -import { SharedOptions, sharedDefaultOptions } from './shared.js'; +import type { CleanFailure, SharedOptions } from './shared.js'; +import { sharedDefaultOptions } from './shared.js'; import { removeEmptyDirs, removeFiles } from './utils/filesystem.js'; import { findFilesByGlobLists, getGlobLists } from './utils/glob.js'; @@ -8,21 +9,25 @@ export const defaultCleanOptions: Required = { keepEmpty: false, }; -export interface CleanResult { - /** List of all files that were cleaned up. */ +export type CleanResult = { + /** List of all files matched for cleanup. */ files: string[]; + /** Number of files that were removed. */ + removedFilesCount: number; /** How many bytes of data that was removed when cleaning. */ reducedSize: number; /** The number of empty directories that were cleaned up after the operation. */ removedEmptyDirs: number; -} + /** Non-fatal failures encountered while crawling or removing files. */ + failures: CleanFailure[]; +}; -export interface CleanOptions extends SharedOptions { +export type CleanOptions = SharedOptions & { /** Whether to skip actually deleting any files and just perform a dry run of the cleaning operation. */ dryRun?: boolean; /** Whether to keep empty directories around after cleaning. */ keepEmpty?: boolean; -} +}; /** * Removes unnecessary files to reduce the size of a node_modules directory. @@ -34,9 +39,15 @@ export async function clean(options: CleanOptions = {}): Promise { const { globs, noDefaults, globFile, directory, dryRun, keepEmpty } = mergedOptions; const globLists = await getGlobLists({ globs, noDefaults, globFile }); - const files = await findFilesByGlobLists(directory, globLists); - const reducedSize = await removeFiles(files, { dryRun }); + const { files, failures: crawlFailures } = await findFilesByGlobLists(directory, globLists); + const { removedFilesCount, reducedSize, failures: removeFailures } = await removeFiles(files, { dryRun }); const removedEmptyDirs = dryRun || keepEmpty ? 0 : await removeEmptyDirs(files); - return { files, reducedSize, removedEmptyDirs }; + return { + files, + removedFilesCount, + reducedSize, + removedEmptyDirs, + failures: [...crawlFailures, ...removeFailures], + }; } diff --git a/src/cli/__snapshots__/cli.test.ts.snap b/src/cli/__snapshots__/cli.test.ts.snap new file mode 100644 index 0000000..33c5255 --- /dev/null +++ b/src/cli/__snapshots__/cli.test.ts.snap @@ -0,0 +1,160 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`analyze command > renders the analyze help 1`] = ` +" +โ”โ”โ” clean-modules vX.Y.Z โ”โ”โ” +https://github.com/duniul/clean-modules + +Analyze which files are cleaned up or not. + +โ”โ”โ” Usage โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + +$ clean-modules analyze [options] [globs...] + +โ”โ”โ” Options โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +-h, --help Show help +-v, --version Show version +-D, --directory Path to node_modules (default: $PWD/node_modules) +-f, --glob-file Path to a custom glob file (default: .cleanmodules) +-n, --no-defaults Skips default glob patterns from being used +" +`; + +exports[`clean command > renders the clean help 1`] = ` +" +โ”โ”โ” clean-modules vX.Y.Z โ”โ”โ” +https://github.com/duniul/clean-modules + +Remove unnecessary files in your node_modules directory. + +โ”โ”โ” Usage โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + +$ clean-modules clean [options] [globs...] + +Extra globs can be passed as positional args. + +โ”โ”โ” Options โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +-h, --help Show help +-v, --version Show version +-D, --directory Path to node_modules (default: $PWD/node_modules) +-f, --glob-file Path to a custom glob file (default: .cleanmodules) +-n, --no-defaults Skips default glob patterns from being used +-k, --keep-empty Skips removing empty folders after cleanup +-d, --dry-run Runs clean without removing any files +-s, --silent Does not log anything to console +-j, --json Output results as JSON +-y, --yes Skips the confirmation prompt +-e, --fail-on-error Exit with a non-zero status code on file errors +" +`; + +exports[`clean output formatting > emits a JSON summary with --json (no human-readable text) 1`] = ` +"{ + "removedFiles": 5, + "reducedSize": 5, + "removedEmptyDirs": 3, + "duration": , + "dryRun": false, + "failures": [] +}" +`; + +exports[`clean output formatting > lists failures (truncated past MAX_PRINTED_FAILURES) below the summary 1`] = ` +"clean-modules + +Cleaning up node_modules... +Done in ! + +Results: +- size reduced: 12.35 kB +- files matched: 8 +- files removed: 3 +- empty dirs removed: 0 +- failures: 8 + +Failed to process 8 files: + EACCES: /node_modules/dep/file-0 (unlink failed: permission denied (0)) + EACCES: /node_modules/dep/file-1 (unlink failed: permission denied (1)) + EACCES: /node_modules/dep/file-2 (unlink failed: permission denied (2)) + EACCES: /node_modules/dep/file-3 (unlink failed: permission denied (3)) + EACCES: /node_modules/dep/file-4 (unlink failed: permission denied (4)) + ...3 more (use --json to see all)" +`; + +exports[`clean output formatting > marks the run as a dry run in the human-readable output 1`] = ` +"clean-modules (dry run) + +Cleaning up node_modules... +Done in ! + +Results: +- size reduced: 5 B +- files matched: 5 +- files removed: 0 (skipped in dry run) +- empty dirs removed: 0 (skipped in dry run)" +`; + +exports[`clean output formatting > renders a human-readable summary after a successful clean 1`] = ` +"clean-modules + +Cleaning up node_modules... +Done in ! + +Results: +- size reduced: 5 B +- files matched: 5 +- files removed: 5 +- empty dirs removed: 3" +`; + +exports[`clean output formatting > uses singular wording and skips truncation when there is a single failure 1`] = ` +"clean-modules + +Cleaning up node_modules... +Done in ! + +Results: +- size reduced: 0 B +- files matched: 1 +- files removed: 0 +- empty dirs removed: 0 +- failures: 1 + +Failed to process 1 file: + /node_modules/dep/only-file (unlink failed: boom)" +`; + +exports[`default command > renders help that lists both subcommands 1`] = ` +" +โ”โ”โ” clean-modules vX.Y.Z โ”โ”โ” +https://github.com/duniul/clean-modules + +Remove unnecessary files in your node_modules directory. + +โ”โ”โ” Usage โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + +$ clean-modules [options] [globs...] + +Extra globs can be passed as positional args. + +โ”โ”โ” Commands โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +clean-modules clean (default) + Remove unnecessary files in your node_modules directory. + +clean-modules analyze + Analyze which files are cleaned up or not. + +โ”โ”โ” Options โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +-h, --help Show help +-v, --version Show version +-D, --directory Path to node_modules (default: $PWD/node_modules) +-f, --glob-file Path to a custom glob file (default: .cleanmodules) +-n, --no-defaults Skips default glob patterns from being used +-k, --keep-empty Skips removing empty folders after cleanup +-d, --dry-run Runs clean without removing any files +-s, --silent Does not log anything to console +-j, --json Output results as JSON +-y, --yes Skips the confirmation prompt +-e, --fail-on-error Exit with a non-zero status code on file errors +" +`; diff --git a/src/cli/cli.test.ts b/src/cli/cli.test.ts new file mode 100644 index 0000000..c1bf6e8 --- /dev/null +++ b/src/cli/cli.test.ts @@ -0,0 +1,579 @@ +import { vol } from 'memfs'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; +import type { analyze, AnalyzeResult } from '../analyze.js'; +import type * as cleanModule from '../clean.js'; +import type { clean, CleanResult } from '../clean.js'; +import type * as terminalModule from './utils/terminal.js'; +import type { yesOrNo } from './utils/terminal.js'; + +vi.setConfig({ testTimeout: 5000 }); + +type CleanFn = typeof clean; +type AnalyzeFn = typeof analyze; + +const mocks = vi.hoisted(() => ({ + clean: vi.fn(), + analyze: vi.fn(), + yesOrNo: vi.fn(), +})); + +vi.mock(import('../clean.js'), () => ({ clean: mocks.clean })); +vi.mock(import('../analyze.js'), () => ({ analyze: mocks.analyze })); +vi.mock(import('./utils/terminal.js'), async () => { + const actual = await vi.importActual('./utils/terminal.js'); + return { ...actual, yesOrNo: mocks.yesOrNo }; +}); + +const DEFAULT_CLEAN_RESULT: CleanResult = { + files: [], + removedFilesCount: 0, + reducedSize: 0, + removedEmptyDirs: 0, + failures: [], +}; + +const DEFAULT_ANALYZE_RESULT: AnalyzeResult = { + files: [], + failures: [], +}; + +class ExitError extends Error { + override name = 'ExitError'; + exitCode: number; + constructor(code: number) { + super(`process.exit(${code})`); + this.exitCode = code; + } +} + +type RunOptions = { + stdinTTY?: boolean; + confirm?: boolean; + cleanResult?: CleanResult; + cleanImpl?: CleanFn; + analyzeResult?: AnalyzeResult; + analyzeImpl?: AnalyzeFn; +}; + +type RunResult = { + exitCode: number; + stdout: string; + stderr: string; +}; + +function formatArg(arg: unknown): string { + return typeof arg === 'string' ? arg : String(arg); +} + +/** + * Runs the CLI as if invoked from a shell with the given args. + */ +async function runCli(argv: string[], opts: RunOptions = {}): Promise { + const originalArgv = process.argv; + const originalIsTTY = process.stdin.isTTY; + + process.argv = ['node', 'cli.js', ...argv]; + (process.stdin as { isTTY?: boolean }).isTTY = opts.stdinTTY ?? true; + + const cleanSetup = mocks.clean.mockReset(); + if (opts.cleanImpl) { + cleanSetup.mockImplementation(opts.cleanImpl); + } else { + cleanSetup.mockResolvedValue(opts.cleanResult ?? DEFAULT_CLEAN_RESULT); + } + + const analyzeSetup = mocks.analyze.mockReset(); + if (opts.analyzeImpl) { + analyzeSetup.mockImplementation(opts.analyzeImpl); + } else { + analyzeSetup.mockResolvedValue(opts.analyzeResult ?? DEFAULT_ANALYZE_RESULT); + } + + mocks.yesOrNo.mockReset().mockResolvedValue(opts.confirm ?? true); + + const stdoutLines: string[] = []; + const stderrLines: string[] = []; + let exitCode = 0; + + const logSpy = vi.spyOn(console, 'log').mockImplementation((...args: unknown[]): void => { + stdoutLines.push(args.map(formatArg).join(' ')); + }); + const errSpy = vi.spyOn(console, 'error').mockImplementation((...args: unknown[]): void => { + stderrLines.push(args.map(formatArg).join(' ')); + }); + const exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code?: number): never => { + throw new ExitError(typeof code === 'number' ? code : 0); + }) as typeof process.exit); + + vi.resetModules(); + + try { + await import('./cli.js'); + } catch (error) { + if (error instanceof ExitError) { + exitCode = error.exitCode; + } else { + throw error; + } + } finally { + process.argv = originalArgv; + (process.stdin as { isTTY?: boolean }).isTTY = originalIsTTY; + logSpy.mockRestore(); + errSpy.mockRestore(); + exitSpy.mockRestore(); + } + + return { + exitCode, + stdout: stdoutLines.join('\n'), + stderr: stderrLines.join('\n'), + }; +} + +/** Normalizes non-deterministic bits (version, durations) so snapshots stay stable. */ +function normalize(text: string): string { + return text + .replaceAll(/v\d+\.\d+\.\d+/g, 'vX.Y.Z') + .replaceAll(/Done in \d+(?:\.\d+)?(?:ms|s|m \d+s|m)!/g, 'Done in !') + .replaceAll(/"duration": \d+/g, '"duration": '); +} + +describe('default command', () => { + it('renders help that lists both subcommands', async () => { + expect.hasAssertions(); + + const result = await runCli(['--help']); + + expect(result.exitCode).toBe(0); + expect(result.stderr).toBe(''); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('renders the package version on --version', async () => { + expect.hasAssertions(); + + const result = await runCli(['--version']); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toMatch(/^\d+\.\d+\.\d+$/); + }); + + it('runs clean when no subcommand is given', async () => { + expect.hasAssertions(); + + const result = await runCli(['--yes']); + + expect(result.exitCode).toBe(0); + expect(mocks.clean).toHaveBeenCalledTimes(1); + expect(mocks.analyze).not.toHaveBeenCalled(); + }); + + it('forwards positional args as globs to clean', async () => { + expect.hasAssertions(); + + await runCli(['--yes', '**/*.foo', '**/*.bar']); + + expect(mocks.clean).toHaveBeenCalledTimes(1); + expect(mocks.clean).toHaveBeenCalledWith( + expect.objectContaining({ globs: expect.arrayContaining(['**/*.foo', '**/*.bar']) }) + ); + }); + + it('forwards clean options when no subcommand is given', async () => { + expect.hasAssertions(); + + await runCli(['--yes', '--dry-run', '--keep-empty', '--directory', '/tmp/nm']); + + expect(mocks.clean).toHaveBeenCalledWith( + expect.objectContaining({ dryRun: true, keepEmpty: true, directory: '/tmp/nm' }) + ); + }); + + it('exits with code 1 when an unknown option is passed', async () => { + expect.hasAssertions(); + + const result = await runCli(['--bogus']); + + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain('Run with --help for usage.'); + expect(mocks.clean).not.toHaveBeenCalled(); + }); +}); + +describe('clean command', () => { + it('renders the clean help', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--help']); + + expect(result.exitCode).toBe(0); + expect(result.stderr).toBe(''); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('calls clean with default options when none are passed', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes']); + + expect(mocks.clean).toHaveBeenCalledTimes(1); + expect(mocks.clean).toHaveBeenCalledWith( + expect.objectContaining({ + dryRun: false, + keepEmpty: false, + noDefaults: false, + globFile: '.cleanmodules', + }) + ); + }); + + it('forwards positional args as globs', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes', '**/foo', '**/bar']); + + expect(mocks.clean).toHaveBeenCalledWith( + expect.objectContaining({ globs: expect.arrayContaining(['**/foo', '**/bar']) }) + ); + }); + + it('forwards --directory / -D', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes', '--directory', '/long/path']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ directory: '/long/path' })); + + await runCli(['clean', '--yes', '-D', '/short/path']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ directory: '/short/path' })); + }); + + it('forwards --glob-file / -f', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes', '--glob-file', '.custom-globs']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ globFile: '.custom-globs' })); + + await runCli(['clean', '--yes', '-f', '.short-globs']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ globFile: '.short-globs' })); + }); + + it('forwards --no-defaults / -n', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes', '--no-defaults']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ noDefaults: true })); + + await runCli(['clean', '--yes', '-n']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ noDefaults: true })); + }); + + it('forwards --keep-empty / -k', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes', '--keep-empty']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ keepEmpty: true })); + + await runCli(['clean', '--yes', '-k']); + expect(mocks.clean).toHaveBeenLastCalledWith(expect.objectContaining({ keepEmpty: true })); + }); + + it('forwards --dry-run / -d and skips confirmation', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--dry-run'], { stdinTTY: false }); + + expect(result.exitCode).toBe(0); + expect(mocks.clean).toHaveBeenCalledWith(expect.objectContaining({ dryRun: true })); + expect(mocks.yesOrNo).not.toHaveBeenCalled(); + expect(result.stdout).toContain('(dry run)'); + }); + + it('-d short flag also enables dry run', async () => { + expect.hasAssertions(); + + await runCli(['clean', '-d'], { stdinTTY: false }); + + expect(mocks.clean).toHaveBeenCalledWith(expect.objectContaining({ dryRun: true })); + }); + + it('--silent / -s suppresses logger output but still calls clean', async () => { + expect.hasAssertions(); + + const longResult = await runCli(['clean', '--yes', '--silent']); + expect(longResult.exitCode).toBe(0); + expect(longResult.stdout).toBe(''); + expect(mocks.clean).toHaveBeenCalledTimes(1); + + const shortResult = await runCli(['clean', '--yes', '-s']); + expect(shortResult.stdout).toBe(''); + }); + + it('--json / -j prints a JSON summary instead of human-readable output', async () => { + expect.hasAssertions(); + + const cleanResult: CleanResult = { + files: ['/x'], + removedFilesCount: 1, + reducedSize: 100, + removedEmptyDirs: 2, + failures: [], + }; + + const result = await runCli(['clean', '--yes', '--json'], { cleanResult }); + + expect(result.exitCode).toBe(0); + const parsed = JSON.parse(result.stdout); + expect(parsed).toStrictEqual( + expect.objectContaining({ + removedFiles: 1, + reducedSize: 100, + removedEmptyDirs: 2, + dryRun: false, + failures: [], + }) + ); + }); + + it('--yes / -y skips the readline confirmation prompt', async () => { + expect.hasAssertions(); + + await runCli(['clean', '--yes']); + expect(mocks.yesOrNo).not.toHaveBeenCalled(); + expect(mocks.clean).toHaveBeenCalledTimes(1); + + await runCli(['clean', '-y']); + expect(mocks.yesOrNo).not.toHaveBeenCalled(); + expect(mocks.clean).toHaveBeenCalledTimes(1); + }); + + it('prompts for confirmation and continues when the user confirms', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean'], { confirm: true }); + + expect(result.exitCode).toBe(0); + expect(mocks.yesOrNo).toHaveBeenCalledTimes(1); + expect(mocks.clean).toHaveBeenCalledTimes(1); + }); + + it('prompts for confirmation and aborts when the user declines', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean'], { confirm: false }); + + expect(result.exitCode).toBe(0); + expect(mocks.yesOrNo).toHaveBeenCalledTimes(1); + expect(mocks.clean).not.toHaveBeenCalled(); + }); + + it('exits 1 when stdin is not a TTY and confirmation is required', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean'], { stdinTTY: false }); + + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain('stdin is not a TTY'); + expect(mocks.clean).not.toHaveBeenCalled(); + }); + + it('--fail-on-error / -e exits 1 when clean() reports failures', async () => { + expect.hasAssertions(); + + const cleanResult: CleanResult = { + files: [], + removedFilesCount: 0, + reducedSize: 0, + removedEmptyDirs: 0, + failures: [{ path: '/x', phase: 'unlink', message: 'boom' }], + }; + + const longResult = await runCli(['clean', '--yes', '--fail-on-error'], { cleanResult }); + expect(longResult.exitCode).toBe(1); + + const shortResult = await runCli(['clean', '--yes', '-e'], { cleanResult }); + expect(shortResult.exitCode).toBe(1); + }); + + it('--fail-on-error exits 0 when there are no failures', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--yes', '--fail-on-error']); + + expect(result.exitCode).toBe(0); + }); +}); + +describe('analyze command', () => { + it('renders the analyze help', async () => { + expect.hasAssertions(); + + const result = await runCli(['analyze', '--help']); + + expect(result.exitCode).toBe(0); + expect(result.stderr).toBe(''); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('calls analyze with default options when none are passed', async () => { + expect.hasAssertions(); + + await runCli(['analyze']); + + expect(mocks.analyze).toHaveBeenCalledTimes(1); + expect(mocks.clean).not.toHaveBeenCalled(); + expect(mocks.analyze).toHaveBeenCalledWith( + expect.objectContaining({ noDefaults: false, globFile: '.cleanmodules' }) + ); + }); + + it('forwards positional args as globs', async () => { + expect.hasAssertions(); + + await runCli(['analyze', '**/foo', '**/bar']); + + expect(mocks.analyze).toHaveBeenCalledWith( + expect.objectContaining({ globs: expect.arrayContaining(['**/foo', '**/bar']) }) + ); + }); + + it('forwards --directory / -D', async () => { + expect.hasAssertions(); + + await runCli(['analyze', '--directory', '/long/path']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ directory: '/long/path' })); + + await runCli(['analyze', '-D', '/short/path']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ directory: '/short/path' })); + }); + + it('forwards --glob-file / -f', async () => { + expect.hasAssertions(); + + await runCli(['analyze', '--glob-file', '.custom-globs']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ globFile: '.custom-globs' })); + + await runCli(['analyze', '-f', '.short-globs']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ globFile: '.short-globs' })); + }); + + it('forwards --no-defaults / -n', async () => { + expect.hasAssertions(); + + await runCli(['analyze', '--no-defaults']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ noDefaults: true })); + + await runCli(['analyze', '-n']); + expect(mocks.analyze).toHaveBeenLastCalledWith(expect.objectContaining({ noDefaults: true })); + }); + + it('prints the analyze result as JSON', async () => { + expect.hasAssertions(); + + const analyzeResult: AnalyzeResult = { + files: [{ filePath: '/x', includedByDefault: true, includedByGlobs: [] }], + failures: [], + }; + + const result = await runCli(['analyze'], { analyzeResult }); + + expect(result.exitCode).toBe(0); + expect(JSON.parse(result.stdout)).toStrictEqual(analyzeResult); + }); + + it('exits 1 on unknown option', async () => { + expect.hasAssertions(); + + const result = await runCli(['analyze', '--bogus']); + + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain('Run with --help for usage.'); + expect(mocks.analyze).not.toHaveBeenCalled(); + }); +}); + +/** + * End-to-end smoke tests for the human-readable output that clean.command.ts + * renders. Runs the real `clean` implementation against a memfs fixture for + * the success paths, and inject mocked results for the failure paths. + */ +describe('clean output formatting', () => { + let realClean: CleanFn; + let fileStructure: Awaited>; + + beforeAll(async () => { + const actualCleanModule = await vi.importActual('../clean.js'); + realClean = actualCleanModule.clean; + fileStructure = await getMockedFileStructure(); + }); + + beforeEach(() => { + vol.fromNestedJSON(fileStructure); + }); + + it('renders a human-readable summary after a successful clean', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--yes'], { cleanImpl: realClean }); + + expect(result.exitCode).toBe(0); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('marks the run as a dry run in the human-readable output', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--dry-run'], { cleanImpl: realClean }); + + expect(result.exitCode).toBe(0); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('emits a JSON summary with --json (no human-readable text)', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--yes', '--json'], { cleanImpl: realClean }); + + expect(result.exitCode).toBe(0); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('lists failures (truncated past MAX_PRINTED_FAILURES) below the summary', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--yes'], { + cleanResult: { + files: Array.from({ length: 8 }, (_, i) => `/node_modules/dep/file-${i}`), + removedFilesCount: 3, + reducedSize: 12_345, + removedEmptyDirs: 0, + failures: Array.from({ length: 8 }, (_, i) => ({ + path: `/node_modules/dep/file-${i}`, + phase: 'unlink', + code: 'EACCES', + message: `permission denied (${i})`, + })), + }, + }); + + expect(result.exitCode).toBe(0); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); + + it('uses singular wording and skips truncation when there is a single failure', async () => { + expect.hasAssertions(); + + const result = await runCli(['clean', '--yes'], { + cleanResult: { + files: ['/node_modules/dep/only-file'], + removedFilesCount: 0, + reducedSize: 0, + removedEmptyDirs: 0, + failures: [{ path: '/node_modules/dep/only-file', phase: 'unlink', message: 'boom' }], + }, + }); + + expect(result.exitCode).toBe(0); + expect(normalize(result.stdout)).toMatchSnapshot(); + }); +}); diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 3500885..722b09a 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,24 +1,38 @@ -import { Builtins, Cli } from 'clipanion'; -import { createRequire } from 'node:module'; -import path from 'node:path'; -import { fileDir } from '../utils/filesystem.js'; -import { AnalyzeCommand } from './commands/analyze.command.js'; -import { CleanCommand } from './commands/clean.command.js'; +import { analyzeCommand } from './commands/analyze.command.js'; +import { cleanCommand } from './commands/clean.command.js'; +import { formatHelp } from './helpers/help.js'; -const [_node, _app, ...args] = process.argv; -const esmRequire = createRequire(import.meta.url); -const cliDir = fileDir(import.meta.url); -const { name, version } = esmRequire(path.resolve(cliDir, '..', '..', 'package.json')); +const FIRST_POSITIONAL_INDEX = 2; -const cli = new Cli({ - binaryLabel: name, - binaryVersion: version, - binaryName: name, -}); +const firstPositional = process.argv + .slice(FIRST_POSITIONAL_INDEX) + .find(positional => !positional.startsWith('-')); -cli.register(Builtins.HelpCommand); -cli.register(Builtins.VersionCommand); -cli.register(CleanCommand); -cli.register(AnalyzeCommand); +switch (firstPositional) { + case cleanCommand.name: { + await cleanCommand.run(); + break; + } -cli.runExit(args); + case analyzeCommand.name: { + await analyzeCommand.run(); + break; + } + + default: { + const rootCommand = { + ...cleanCommand, + renderHelp: (): string => + formatHelp({ + ...cleanCommand, + subcommands: [ + { name: `${cleanCommand.name} (default)`, description: cleanCommand.description }, + analyzeCommand, + ], + }), + }; + + await rootCommand.run(); + break; + } +} diff --git a/src/cli/commands/analyze.command.ts b/src/cli/commands/analyze.command.ts index e795a1e..0262f0f 100644 --- a/src/cli/commands/analyze.command.ts +++ b/src/cli/commands/analyze.command.ts @@ -1,21 +1,32 @@ import { analyze } from '../../analyze.js'; -import { BaseCommand } from '../helpers/base.command.js'; +import { formatJson } from '../../utils/formatting.js'; +import { processArgs, sharedOptions, sharedPositionals } from '../helpers/args.js'; +import { formatHelp } from '../helpers/help.js'; +import type { CommandDefinition } from '../types.js'; -export class AnalyzeCommand extends BaseCommand { - static override paths = [['analyze']]; - static override usage = { - description: - 'Helps determining why a file is included by the clean command without removing any files. Extra globs can be passed as positional args.', - }; +export const analyzeCommand = { + name: 'analyze', + description: 'Analyze which files are cleaned up or not.', - async execute(): Promise { - const analyzeResults = await analyze({ - directory: this.directory, - globs: this.globs, - noDefaults: this.noDefaults, - globFile: this.globFile, + allowPositionals: true, + positionals: sharedPositionals, + options: sharedOptions, + + renderHelp(): string { + return formatHelp(this as typeof analyzeCommand); + }, + + async run(): Promise { + const { values: args, positionals: globs } = processArgs(this as typeof analyzeCommand); + + const analyzeResult = await analyze({ + globs, + directory: args.directory, + noDefaults: args['no-defaults'], + globFile: args['glob-file'], }); - console.log(JSON.stringify(analyzeResults, null, 2)); - } -} + // oxlint-disable-next-line no-console + console.log(formatJson(analyzeResult)); + }, +} satisfies CommandDefinition; diff --git a/src/cli/commands/clean.command.ts b/src/cli/commands/clean.command.ts index c6d53b1..65ab6d3 100644 --- a/src/cli/commands/clean.command.ts +++ b/src/cli/commands/clean.command.ts @@ -1,90 +1,158 @@ -import { Command, Option } from 'clipanion'; -import prettyBytes from 'pretty-bytes'; -import prettyMs from 'pretty-ms'; import { clean } from '../../clean.js'; -import { BaseCommand } from '../helpers/base.command.js'; -import { bold, green, makeLogger, yellow, yesOrNo } from '../utils/terminal.js'; - -export class CleanCommand extends BaseCommand { - static override paths = [['clean'], Command.Default]; - static override usage = { - description: - 'Default command. Removes unnecessary files to reduce the size of your node_modules directory. Extra globs can be passed as positional args.', - }; - - keepEmpty = Option.Boolean('-k,--keep-empty', false, { - description: 'Skips removing empty folders after removing contents', - }); - - dryRun = Option.Boolean('-d,--dry-run', false, { - description: 'Logs files that would be removed without removing any files', - }); - - silent = Option.Boolean('-s,--silent', false, { - description: 'Does not log anything to console (unless --json is enabled)', - }); - - yes = Option.Boolean('-y,--yes', false, { - description: 'Skips the confirmation prompt at the start of the script', - }); - - json = Option.Boolean('-j,--json', false, { - description: 'Output results as JSON', - }); - - async execute(): Promise { - const logger = makeLogger({ disabled: this.json || this.silent }); +import type { CleanFailure } from '../../shared.js'; +import { formatBytes, formatJson, formatMs } from '../../utils/formatting.js'; +import { processArgs, sharedOptions, sharedPositionals } from '../helpers/args.js'; +import { formatHelp, NAME } from '../helpers/help.js'; +import type { CommandDefinition } from '../types.js'; +import { makeSimpleLogger, yesOrNo } from '../utils/terminal.js'; + +const MAX_PRINTED_FAILURES = 5; + +function formatFailureLine(failure: CleanFailure): string { + const prefix = failure.code ? `${failure.code}: ` : ''; + return ` ${prefix}${failure.path} (${failure.phase} failed: ${failure.message})`; +} - logger.log(bold('clean-modules'), this.dryRun ? yellow('(dry run)') : ''); +export const cleanCommand = { + name: 'clean', + description: 'Remove unnecessary files in your node_modules directory.', + extraUsageInfo: 'Extra globs can be passed as positional args.', + + allowPositionals: true, + positionals: sharedPositionals, + + options: { + ...sharedOptions, + 'keep-empty': { + type: 'boolean', + short: 'k', + description: 'Skips removing empty folders after cleanup', + default: false, + }, + 'dry-run': { + type: 'boolean', + short: 'd', + description: 'Runs clean without removing any files', + default: false, + }, + 'silent': { + type: 'boolean', + short: 's', + description: 'Does not log anything to console', + default: false, + }, + 'json': { + type: 'boolean', + short: 'j', + description: 'Output results as JSON', + default: false, + }, + 'yes': { + type: 'boolean', + short: 'y', + description: 'Skips the confirmation prompt', + default: false, + }, + 'fail-on-error': { + type: 'boolean', + short: 'e', + description: 'Exit with a non-zero status code on file errors', + default: false, + }, + }, + + renderHelp(): string { + return formatHelp(this as typeof cleanCommand); + }, + + async run(): Promise { + const { values: args, positionals: globs } = processArgs(this as typeof cleanCommand); + const logger = makeSimpleLogger({ disabled: args.json || args.silent }); + + logger.log(`${NAME}${args['dry-run'] ? ' (dry run)' : ''}`); + + if (!args.yes && !args['dry-run']) { + if (!process.stdin.isTTY) { + console.error( + 'stdin is not a TTY, skipping confirmation prompt. Please use --yes or -y to skip the prompt.' + ); + + // oxlint-disable-next-line unicorn/no-process-exit + process.exit(1); + } - if (!this.yes && !this.dryRun) { - const warning = `\nPreparing to clean node_modules at: ${this.directory}\nAre you sure you want to continue? (Y/N) `; - const confirmed = await yesOrNo(yellow(warning)); + const warning = `\nPreparing to clean node_modules at:\n${args.directory}\n\nAre you sure you want to continue? (Y/N) `; + const confirmed = await yesOrNo(warning); if (!confirmed) { + // oxlint-disable-next-line unicorn/no-process-exit process.exit(0); } } logger.log('\nCleaning up node_modules...'); - const cleanupStart = new Date().getTime(); + const cleanupStart = Date.now(); - const { files, reducedSize, removedEmptyDirs } = await clean({ - globs: this.globs, - noDefaults: this.noDefaults, - globFile: this.globFile, - directory: this.directory, - dryRun: this.dryRun, + const { files, removedFilesCount, reducedSize, removedEmptyDirs, failures } = await clean({ + globs, + dryRun: args['dry-run'], + noDefaults: args['no-defaults'], + globFile: args['glob-file'], + keepEmpty: args['keep-empty'], + directory: args.directory, }); - const cleanupDuration = new Date().getTime() - cleanupStart; - logger.log(green(`Done in ${prettyMs(cleanupDuration)}!`)); + const cleanupDuration = Date.now() - cleanupStart; + logger.log(`Done in ${formatMs(cleanupDuration)}!`); - if (this.json) { + if (args.json) { const output: Record = { - removedFiles: files.length, + removedFiles: removedFilesCount, reducedSize, removedEmptyDirs, duration: cleanupDuration, - dryRun: this.dryRun, + dryRun: args['dry-run'], + failures, }; - console.log(JSON.stringify(output, null, 2)); + // oxlint-disable-next-line no-console + console.log(formatJson(output)); } else { - logger.log(bold('\nResults:')); - logger.log( - '- size reduced:', - green( - prettyBytes(reducedSize || 0, { - space: true, - minimumFractionDigits: 1, - maximumFractionDigits: 2, - }) - ) - ); - logger.log('- files removed:', green(files.length)); - logger.log('- empty dirs removed:', green(removedEmptyDirs || 0)); + const dryRunSuffix = args['dry-run'] ? '(skipped in dry run)' : ''; + logger.log('\nResults:'); + logger.log('- size reduced:', formatBytes(reducedSize)); + logger.log('- files matched:', files.length); + + const filesRemovedLogArgs = ['- files removed:', removedFilesCount]; + const emptyDirLogArgs = ['- empty dirs removed:', removedEmptyDirs]; + if (dryRunSuffix) { + filesRemovedLogArgs.push(dryRunSuffix); + emptyDirLogArgs.push(dryRunSuffix); + } + + logger.log(...filesRemovedLogArgs); + logger.log(...emptyDirLogArgs); + + if (failures.length > 0) { + logger.log('- failures:', failures.length); + + const printed = failures.slice(0, MAX_PRINTED_FAILURES); + const remaining = failures.length - printed.length; + + logger.log(`\nFailed to process ${failures.length} ${failures.length === 1 ? 'file' : 'files'}:`); + for (const failure of printed) { + logger.log(formatFailureLine(failure)); + } + if (remaining > 0) { + logger.log(` ...${remaining} more (use --json to see all)`); + } + } } - } -} + + if (failures.length > 0 && args['fail-on-error']) { + // oxlint-disable-next-line unicorn/no-process-exit + process.exit(1); + } + }, +} satisfies CommandDefinition; diff --git a/src/cli/helpers/args.ts b/src/cli/helpers/args.ts new file mode 100644 index 0000000..d636e3b --- /dev/null +++ b/src/cli/helpers/args.ts @@ -0,0 +1,76 @@ +import { parseArgs } from 'node:util'; +import { sharedDefaultOptions } from '../../shared.js'; +import type { CommandDefinition, OptionDescriptors, ParsedArgs } from '../types.js'; +import { formatHelp, VERSION } from './help.js'; + +/** Shared positionals for all commands. */ +export const sharedPositionals = { + globs: { + multiple: true, + required: false, + }, +}; + +/** Shared options for all commands. */ +export const sharedOptions = { + 'help': { + type: 'boolean', + short: 'h', + description: 'Show help', + }, + 'version': { + type: 'boolean', + short: 'v', + description: 'Show version', + }, + 'directory': { + type: 'string', + short: 'D', + default: sharedDefaultOptions.directory, + defaultHint: '$PWD/node_modules', + description: 'Path to node_modules', + valueHint: 'path', + }, + 'glob-file': { + type: 'string', + short: 'f', + default: sharedDefaultOptions.globFile, + description: 'Path to a custom glob file', + valueHint: 'path', + }, + 'no-defaults': { + type: 'boolean', + short: 'n', + default: sharedDefaultOptions.noDefaults, + description: 'Skips default glob patterns from being used', + }, +} satisfies OptionDescriptors; + +export function processArgs(command: T): ParsedArgs { + let parsed: ParsedArgs; + try { + parsed = parseArgs(command); + } catch (error: unknown) { + const msg = error instanceof Error ? error.message : String(error); + console.error(`${msg}\n\nRun with --help for usage.`); + // oxlint-disable-next-line unicorn/no-process-exit + process.exit(1); + } + + const values = parsed.values as { help?: boolean; version?: boolean }; + + if (values.version) { + console.log(VERSION); + // oxlint-disable-next-line unicorn/no-process-exit + process.exit(0); + } + + if (values.help) { + const helpText = command.renderHelp ? command.renderHelp() : formatHelp(command); + console.log(helpText); + // oxlint-disable-next-line unicorn/no-process-exit + process.exit(0); + } + + return parsed; +} diff --git a/src/cli/helpers/base.command.ts b/src/cli/helpers/base.command.ts deleted file mode 100644 index c9ff882..0000000 --- a/src/cli/helpers/base.command.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Command, Option } from 'clipanion'; -import { sharedDefaultOptions } from '../../shared.js'; - -/** - * Shared options for all commands. - */ -export abstract class BaseCommand extends Command { - directory = Option.String('-D,--directory', sharedDefaultOptions.directory, { - description: 'Path to node_modules', - }); - - globFile = Option.String('-f,--glob-file', sharedDefaultOptions.globFile, { - description: 'Path to a custom glob file', - }); - - noDefaults = Option.Boolean('-n,--no-defaults', sharedDefaultOptions.noDefaults, { - description: 'Only includes/excludes globs specified by a custom glob file or CLI arguments', - }); - - globs = Option.Rest({ name: 'globs' }); -} diff --git a/src/cli/helpers/help.ts b/src/cli/helpers/help.ts new file mode 100644 index 0000000..91c71e4 --- /dev/null +++ b/src/cli/helpers/help.ts @@ -0,0 +1,108 @@ +import pkgJson from '../../../package.json' with { type: 'json' }; +import type { CommandDefinition, OptionDescriptor, OptionDescriptors, PositionalDescriptors } from '../types'; + +export const VERSION = pkgJson.version; +export const NAME = pkgJson.name; +const HOMEPAGE = pkgJson.homepage.replace('#readme', ''); + +function renderOptions(options: OptionDescriptors): string { + let longestArgNameLength = 0; + const optionItems: [string, string][] = Object.entries(options).map(([key, option]) => { + const argNames = (option as OptionDescriptor).short + ? `-${(option as OptionDescriptor).short}, --${key}` + : `--${key}`; + + const defaultText = option.defaultHint ?? option.default; + const argInfo = `${option.description}${defaultText ? ` (default: ${defaultText})` : ''}`; + + if (argNames.length > longestArgNameLength) { + longestArgNameLength = argNames.length; + } + + return [argNames, argInfo]; + }); + + return optionItems + .map(([argNames, argInfo]) => `${argNames.padEnd(longestArgNameLength)} ${argInfo}`) + .join('\n'); +} + +type CommandDescriptor = { name: string; description: string }; +type CommandDescriptors = CommandDescriptor[]; + +function renderCommands(commands: CommandDescriptors): string { + return commands.map(command => `${NAME} ${command.name}\n ${command.description}\n`).join('\n'); +} + +function renderUsage( + command: string, + positionals?: PositionalDescriptors, + hasOptions?: boolean, + extraUsageInfo?: string +): string { + let usage = `\n$ ${command}`; + + if (hasOptions) { + usage += ' [options]'; + } + + if (positionals) { + const positionalFlags = Object.entries(positionals) + .map(([name, { required, multiple }]) => { + const id = multiple ? `${name}...` : name; + return required ? `<${id}>` : `[${id}]`; + }) + .join(' '); + + usage += ` ${positionalFlags}`; + } + + if (extraUsageInfo) { + usage += `\n\n${extraUsageInfo}`; + } + + return usage; +} + +function renderHeading(heading: string, minLength = 20): string { + return `โ”โ”โ” ${heading} โ”โ”โ”${'โ”'.repeat(minLength - heading.length)}`; +} + +type FormatHelpOptions = Pick< + CommandDefinition, + 'name' | 'description' | 'options' | 'positionals' | 'extraUsageInfo' +> & { subcommands?: CommandDescriptors }; + +export function formatHelp({ + name, + description, + subcommands, + options, + positionals, + extraUsageInfo, +}: FormatHelpOptions): string { + const usageName = subcommands ? `${NAME} ` : name === NAME ? NAME : `${NAME} ${name}`; + + let help = ` +${renderHeading(`${NAME} v${VERSION}`)} +${HOMEPAGE} + +${description} + +${renderHeading('Usage')} +${renderUsage(usageName, positionals, !!options, extraUsageInfo)} + +`; + + if (subcommands) { + help += renderHeading('Commands') + '\n'; + help += renderCommands(subcommands) + '\n'; + } + + if (options) { + help += renderHeading('Options') + '\n'; + help += renderOptions(options) + '\n'; + } + + return help; +} diff --git a/src/cli/types.ts b/src/cli/types.ts new file mode 100644 index 0000000..fe26e9d --- /dev/null +++ b/src/cli/types.ts @@ -0,0 +1,31 @@ +import type { parseArgs, ParseArgsConfig, ParseArgsOptionDescriptor } from 'node:util'; + +export type OptionDescriptor = ParseArgsOptionDescriptor & { + description: string; + valueHint?: string; + defaultHint?: string; +}; + +export type OptionDescriptors = Record; + +export type PositionalDescriptor = { + required?: boolean; + multiple?: boolean; +}; + +export type PositionalDescriptors = Record; + +export type CommandDefinition< + TOptions extends OptionDescriptors = OptionDescriptors, + TPositionals extends PositionalDescriptors = PositionalDescriptors, +> = ParseArgsConfig & { + name: string; + description: string; + extraUsageInfo?: string; + options?: TOptions; + positionals?: TPositionals; + renderHelp?: () => string; + run(): Promise; +}; + +export type ParsedArgs = ReturnType>; diff --git a/src/cli/utils/terminal.ts b/src/cli/utils/terminal.ts index adcc2c7..2cf8c6a 100644 --- a/src/cli/utils/terminal.ts +++ b/src/cli/utils/terminal.ts @@ -1,51 +1,46 @@ -import readline from 'readline'; -import supportsColor from 'supports-color'; +import readline from 'node:readline'; -type DisabledConsole = Console; +type SimpleConsole = Pick; + +// oxlint-disable-next-line no-empty-function +const noop = (): void => {}; /** * Creates a conditional console logger. */ -export function makeLogger({ disabled }: { disabled: boolean }): Console | DisabledConsole { +export function makeSimpleLogger({ disabled }: { disabled: boolean }): SimpleConsole { if (disabled) { - const noop = () => undefined; - return Object.keys(console).reduce((disabledConsole, key) => { - disabledConsole[key as keyof Console] = noop as any; - return disabledConsole; - }, {} as DisabledConsole); + return { + log: noop, + }; } - return console; + return { + // oxlint-disable-next-line no-console + log: console.log.bind(console), + }; } /** * Prompts the user with a yes/no question and waits for the answer. */ -export async function yesOrNo(query: string): Promise { +export function yesOrNo(query: string): Promise { + if (!process.stdin.isTTY) { + console.error( + 'stdin is not a TTY, skipping confirmation prompt. Please use --yes or -y to skip the prompt.' + ); + return Promise.resolve(false); + } + const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); - return new Promise(resolve => + return new Promise(resolve => { rl.question(query, (answer: string) => { rl.close(); - resolve(/ye?s?/i.test(answer)); - }) - ); -} - -/** - * Simple string colorizer factory. - */ -function colorizer(colorCode: string) { - if (!supportsColor.stdout) { - return (text: string | number) => String(text); - } - - return (text: string | number) => `\x1b[${colorCode}m${text}\x1b[0m`; + resolve(/^\s*y(es)?\s*$/i.test(answer)); + }); + }); } - -export const bold = colorizer('1'); -export const green = colorizer('32'); -export const yellow = colorizer('33'); diff --git a/src/index.ts b/src/index.ts index c68d83f..9065429 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export * from './analyze.js'; export * from './clean.js'; -export { type SharedOptions } from './shared.js'; +export { type CleanFailure, type CleanFailurePhase, type SharedOptions } from './shared.js'; diff --git a/src/shared.ts b/src/shared.ts index d42fd9f..b4eb89b 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -1,13 +1,9 @@ -import path from 'path'; +import path from 'node:path'; import { fileDir } from './utils/filesystem.js'; -export const DEFAULT_GLOBS_FILE_PATH = path.resolve( - fileDir(import.meta.url), - '..', - '.cleanmodules-default' -); +export const DEFAULT_GLOBS_FILE_PATH = path.resolve(fileDir(import.meta.url), '..', '.cleanmodules-default'); -export interface SharedOptions { +export type SharedOptions = { /** The directory to clean, usually node_modules. */ directory?: string; /** Path to a custom glob file. Uses `.cleanmodules` by default. */ @@ -16,7 +12,7 @@ export interface SharedOptions { noDefaults?: boolean; /** List of custom globs to include or exclude. */ globs?: string[] | undefined; -} +}; export const sharedDefaultOptions: Required = { directory: path.resolve(process.cwd(), 'node_modules'), @@ -24,3 +20,18 @@ export const sharedDefaultOptions: Required = { noDefaults: false, globs: [], }; + +/** Filesystem phase where a failure occurred. */ +export type CleanFailurePhase = 'readdir' | 'stat' | 'unlink'; + +/** A non-fatal filesystem failure that occurred while crawling or removing files. */ +export type CleanFailure = { + /** Absolute path that the operation was attempted against. */ + path: string; + /** Which filesystem operation failed. */ + phase: CleanFailurePhase; + /** Error code, when the underlying error exposes one (e.g. `EACCES`, `EBUSY`). */ + code?: string; + /** Human-readable error message. */ + message: string; +}; diff --git a/src/utils/filesystem.test.ts b/src/utils/filesystem.test.ts index 60a1757..7682cd1 100644 --- a/src/utils/filesystem.test.ts +++ b/src/utils/filesystem.test.ts @@ -1,7 +1,7 @@ -import fs from 'fs'; +import fs from 'node:fs'; +import path from 'node:path'; import { vol } from 'memfs'; -import path from 'path'; -import { afterEach, beforeEach, describe, expect, it, MockedFunction, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; import { crawlDirFast, @@ -10,10 +10,22 @@ import { forEachDirentAsync, readDirectory, removeEmptyDirs, - removeEmptyDirsUp, removeFiles, + type CheckPathFunc, + type CrawlErrorHandler, + type DirentAction, } from './filesystem.js'; +type NodeError = Error & { code: string }; + +function makeNodeError(message: string, code: string): NodeError { + const error = new Error(message) as NodeError; + error.code = code; + return error; +} + +vi.setConfig({ testTimeout: 5000 }); + describe('file exists', () => { beforeEach(() => { vol.fromNestedJSON({ @@ -25,16 +37,22 @@ describe('file exists', () => { }); it('returns true if the file exists', async () => { + expect.hasAssertions(); + const result = await fileExists('testdir/foo'); expect(result).toBe(true); }); - it('returns false if the file does not exists', async () => { - const result = await fileExists('testdir/foo'); - expect(result).toBe(true); + it('returns false if the file does not exist', async () => { + expect.hasAssertions(); + + const result = await fileExists('testdir/nonexistent'); + expect(result).toBe(false); }); it("throws any error that isn't ENOENT", async () => { + expect.hasAssertions(); + const statSpy = vi.spyOn(fs.promises, 'stat').mockImplementation(() => { throw new Error('not an ENOENT!'); }); @@ -45,7 +63,7 @@ describe('file exists', () => { }); }); -describe('forEachDirentAsync', () => { +describe(forEachDirentAsync, () => { beforeEach(() => { vol.fromNestedJSON({ testdir: { @@ -57,7 +75,9 @@ describe('forEachDirentAsync', () => { }); it('runs action with dirents for each item in a directory', async () => { - const action = vi.fn(); + expect.hasAssertions(); + + const action = vi.fn(); await forEachDirentAsync('testdir', action); expect(action).toHaveBeenCalledTimes(3); @@ -79,67 +99,79 @@ describe('forEachDirentAsync', () => { expect.anything() ); }); -}); -describe('readDirectory', () => { - beforeEach(() => { - vol.fromNestedJSON({ 'parent/empty': { 'foo.txt': '', 'bar.txt': '' } }); + it('does not call onError for ENOENT', async () => { + expect.hasAssertions(); + + const onError = vi.fn(); + await forEachDirentAsync('nonexistent', vi.fn(), onError); + + expect(onError).not.toHaveBeenCalled(); }); - it('returns list of files in directory', async () => { - expect(await readDirectory('parent/empty')).toEqual( - expect.arrayContaining(['foo.txt', 'bar.txt']) - ); + it('does not call onError for ENOTDIR', async () => { + expect.hasAssertions(); + + const readdirSpy = vi + .spyOn(fs.promises, 'readdir') + .mockRejectedValue(makeNodeError('not a directory', 'ENOTDIR')); + const onError = vi.fn(); + + await forEachDirentAsync('testdir', vi.fn(), onError); + + expect(onError).not.toHaveBeenCalled(); + readdirSpy.mockRestore(); }); - it('returns empty array if directory does not exist', async () => { - expect(await readDirectory('parent/invalid')).toEqual([]); + it('calls onError with a failure when readdir fails with a non-ignored error', async () => { + expect.hasAssertions(); + + const readdirSpy = vi.spyOn(fs.promises, 'readdir').mockRejectedValue(makeNodeError('denied', 'EACCES')); + const onError = vi.fn(); + + await forEachDirentAsync('testdir', vi.fn(), onError); + + expect(onError).toHaveBeenCalledTimes(1); + expect(onError).toHaveBeenCalledWith({ + path: 'testdir', + phase: 'readdir', + code: 'EACCES', + message: 'denied', + }); + + readdirSpy.mockRestore(); }); }); -describe('removeEmptyDirsUp', () => { +describe(readDirectory, () => { beforeEach(() => { - vol.fromNestedJSON({ - a0: { - b0: { - c0: { - d0: { - e0: {}, - }, - }, - c1: 'its a file', - }, - }, - }); + vol.fromNestedJSON({ 'parent/empty': { 'foo.txt': '', 'bar.txt': '' } }); }); - it('recursively removes empty directories up in the file tree', async () => { - const checkedDirs = new Set(); - await removeEmptyDirsUp(checkedDirs, 'a0/b0/c0/d0/e0'); - - expect(Array.from(checkedDirs)).toEqual(['a0/b0/c0/d0/e0', 'a0/b0/c0/d0', 'a0/b0/c0', 'a0/b0']); + it('returns list of files in directory', async () => { + expect.hasAssertions(); - // dirs no longer exist - expect(fs.existsSync('a0/b0/c0/d0/e0')).toEqual(false); - expect(fs.existsSync('a0/b0/c0/d0')).toEqual(false); - expect(fs.existsSync('a0/b0/c0')).toEqual(false); - expect(fs.existsSync('a0/b0')).toEqual(true); - expect(fs.existsSync('a0')).toEqual(true); + await expect(readDirectory('parent/empty')).resolves.toStrictEqual( + expect.arrayContaining(['foo.txt', 'bar.txt']) + ); }); - it('does not throw if path is invalid', async () => { - const checkedDirs = new Set(); - expect(async () => await removeEmptyDirsUp(checkedDirs, 'invalid/path')).not.toThrow(); + it('returns empty array if directory does not exist', async () => { + expect.hasAssertions(); + + await expect(readDirectory('parent/invalid')).resolves.toStrictEqual([]); }); }); -describe('removeEmptyDirs', () => { +describe(removeEmptyDirs, () => { beforeEach(async () => { const fileStructure = await getMockedFileStructure(); vol.fromNestedJSON(fileStructure); }); it('cleans up empty parent dirs for provided files', async () => { + expect.hasAssertions(); + const filePaths = [ 'node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md', @@ -160,12 +192,63 @@ describe('removeEmptyDirs', () => { }); it('does not throw if path is invalid', async () => { + expect.hasAssertions(); + const filePaths = ['invalid/path/2', 'invalid/path/2']; - expect(async () => await removeEmptyDirs(filePaths)).not.toThrow(); + await expect(() => removeEmptyDirs(filePaths)).not.toThrow(); + }); + + it('walks up the tree, removing every empty ancestor', async () => { + expect.hasAssertions(); + + vol.fromNestedJSON({ + a0: { + b0: { + c0: { + d0: { + e0: { 'leaf.js': '.' }, + }, + }, + c1: 'its a file', + }, + }, + }); + + fs.unlinkSync('a0/b0/c0/d0/e0/leaf.js'); + const removed = await removeEmptyDirs(['a0/b0/c0/d0/e0/leaf.js']); + + expect(removed).toBe(3); + expect(fs.existsSync('a0/b0/c0/d0/e0')).toBe(false); + expect(fs.existsSync('a0/b0/c0/d0')).toBe(false); + expect(fs.existsSync('a0/b0/c0')).toBe(false); + expect(fs.existsSync('a0/b0')).toBe(true); // contains the unrelated c1 file + expect(fs.existsSync('a0')).toBe(true); + }); + + it('removes a parent that becomes empty only after multiple sibling cleanups', async () => { + expect.hasAssertions(); + + vol.fromNestedJSON({ + parent: { + sub1: { 'a.js': '.' }, + sub2: { 'b.js': '.' }, + }, + }); + + const filePaths = ['parent/sub1/a.js', 'parent/sub2/b.js']; + for (const filePath of filePaths) { + fs.unlinkSync(filePath); + } + + const removed = await removeEmptyDirs(filePaths); + + // sub1, sub2 and the parent that became empty because of them + expect(removed).toBe(3); + expect(fs.existsSync('parent')).toBe(false); }); }); -describe('crawlDirFast', () => { +describe(crawlDirFast, () => { beforeEach(() => { vol.fromNestedJSON({ a0: { @@ -191,9 +274,11 @@ describe('crawlDirFast', () => { }); it('appends all nested file paths to the provided array', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; await crawlDirFast(filePaths, 'a0'); - expect(filePaths).toEqual([ + expect(filePaths).toStrictEqual([ path.join('a0', 'b0', 'c1'), path.join('a0', 'b0', 'c2'), path.join('a0', 'b0', 'c0', 'd2'), @@ -202,12 +287,14 @@ describe('crawlDirFast', () => { }); it('does not throw if path is invalid', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; - expect(async () => await crawlDirFast(filePaths, 'invalid/path')).not.toThrow(); + await expect(() => crawlDirFast(filePaths, 'invalid/path')).not.toThrow(); }); }); -describe('crawlDirWithChecks', () => { +describe(crawlDirWithChecks, () => { beforeEach(() => { vol.fromNestedJSON({ a0: { @@ -233,9 +320,11 @@ describe('crawlDirWithChecks', () => { }); it('runs check functions on each nested item', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; - const checkDir = vi.fn(() => false); - const checkFile = vi.fn(() => true); + const checkDir = vi.fn(() => false); + const checkFile = vi.fn(() => true); await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); expect(checkDir).toHaveBeenCalledTimes(6); @@ -243,75 +332,237 @@ describe('crawlDirWithChecks', () => { }); it('includes full dir without checking remaining items if checkDir returns true', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; - const checkDir = vi.fn(() => true); - const checkFile = vi.fn(() => true); + const checkDir = vi.fn(() => true); + const checkFile = vi.fn(() => true); await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); - expect(filePaths).toEqual([ + expect(filePaths).toStrictEqual([ path.join('a0', 'b0', 'c1'), path.join('a0', 'b0', 'c2'), path.join('a0', 'b0', 'c0', 'd2'), path.join('a0', 'b0', 'c0', 'd1', 'e0', 'f0'), ]); expect(checkDir).toHaveBeenCalledTimes(1); - expect(checkFile).toHaveBeenCalledTimes(0); + expect(checkFile).not.toHaveBeenCalled(); }); it('skips file if checkFile function returns false', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; - const checkDir = vi.fn(() => false); - const checkFile = vi.fn(() => false); + const checkDir = vi.fn(() => false); + const checkFile = vi.fn(() => false); await crawlDirWithChecks(filePaths, 'a0', checkDir, checkFile); - expect(filePaths).toEqual([]); + expect(filePaths).toStrictEqual([]); expect(checkFile).toHaveBeenCalledTimes(4); }); it('does not throw if path is invalid', async () => { + expect.hasAssertions(); + const filePaths: string[] = []; - const checkDir = vi.fn(() => false); - const checkFile = vi.fn(() => false); + const checkDir = vi.fn(() => false); + const checkFile = vi.fn(() => false); - expect( - async () => await crawlDirWithChecks(filePaths, 'invalid/path', checkDir, checkFile) - ).not.toThrow(); + await expect(() => crawlDirWithChecks(filePaths, 'invalid/path', checkDir, checkFile)).not.toThrow(); }); }); -describe('removeFiles', () => { +describe(removeFiles, () => { beforeEach(async () => { const fileStructure = await getMockedFileStructure(); vol.fromNestedJSON(fileStructure); }); it('removes files at provided file paths', async () => { - const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md']; + expect.hasAssertions(); + + const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md'] as const; // files are initially there expect(fs.existsSync(filePaths[0])).toBe(true); expect(fs.existsSync(filePaths[1])).toBe(true); - await removeFiles(filePaths); + const result = await removeFiles(filePaths); // then they are not expect(fs.existsSync(filePaths[0])).toBe(false); expect(fs.existsSync(filePaths[1])).toBe(false); + expect(result.failures).toStrictEqual([]); + expect(result.reducedSize).toBeGreaterThan(0); }); it('does not remove files during dry runs', async () => { - const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md']; + expect.hasAssertions(); - await removeFiles(filePaths, { dryRun: true }); + const filePaths = ['node_modules/dep1/__tests__/test1.js', 'node_modules/dep1/a-dir/doc.md'] as const; + + const result = await removeFiles(filePaths, { dryRun: true }); expect(fs.existsSync(filePaths[0])).toBe(true); expect(fs.existsSync(filePaths[1])).toBe(true); + expect(result.failures).toStrictEqual([]); + expect(result.reducedSize).toBeGreaterThan(0); }); it('does not throw if path is invalid', async () => { + expect.hasAssertions(); + const filePaths = ['/invalid/path/2', '/invalid/path/2']; - expect(async () => await removeFiles(filePaths)).not.toThrow(); + await expect(() => removeFiles(filePaths)).not.toThrow(); + }); + + it('does not record failures for files that are already gone (ENOENT)', async () => { + expect.hasAssertions(); + + const result = await removeFiles(['/invalid/path/2']); + + expect(result.failures).toStrictEqual([]); + expect(result.reducedSize).toBe(0); + }); + + it('records failures when stat fails with a non-ENOENT error', async () => { + expect.hasAssertions(); + + const filePath = 'node_modules/dep1/__tests__/test1.js'; + const statSpy = vi.spyOn(fs.promises, 'stat').mockRejectedValue(makeNodeError('denied', 'EACCES')); + + const result = await removeFiles([filePath]); + + expect(result.failures).toStrictEqual([ + { + path: filePath, + phase: 'stat', + code: 'EACCES', + message: 'denied', + }, + ]); + expect(result.reducedSize).toBe(0); + statSpy.mockRestore(); + }); + + it('records failures when unlink fails with a non-ENOENT error', async () => { + expect.hasAssertions(); + + const filePath = 'node_modules/dep1/__tests__/test1.js'; + const unlinkSpy = vi.spyOn(fs.promises, 'unlink').mockRejectedValue(makeNodeError('busy', 'EBUSY')); + + const result = await removeFiles([filePath]); + + expect(result.failures).toStrictEqual([ + { + path: filePath, + phase: 'unlink', + code: 'EBUSY', + message: 'busy', + }, + ]); + // size should not be counted when unlink fails + expect(result.reducedSize).toBe(0); + unlinkSpy.mockRestore(); + }); + + it('continues processing other files when one fails', async () => { + expect.hasAssertions(); + + const failing = 'node_modules/dep1/__tests__/test1.js'; + const succeeding = 'node_modules/dep1/__tests__/test2.js'; + + // Capture the (memfs-backed) original before installing the spy so we can pass-through. + const originalUnlink = fs.promises.unlink.bind(fs.promises); + const unlinkSpy = vi.spyOn(fs.promises, 'unlink').mockImplementation(filePath => + // oxlint-disable-next-line vitest/no-conditional-in-test + filePath === failing + ? Promise.reject(makeNodeError('busy', 'EBUSY')) + : originalUnlink(filePath as Parameters[0]) + ); + + const result = await removeFiles([failing, succeeding]); + + expect(result.failures).toHaveLength(1); + expect(result.failures[0]?.path).toBe(failing); + expect(fs.existsSync(failing)).toBe(true); + expect(fs.existsSync(succeeding)).toBe(false); + + unlinkSpy.mockRestore(); + }); +}); + +describe('symlink handling', () => { + describe(crawlDirFast, () => { + beforeEach(() => { + vol.fromNestedJSON({ + root: { + realdir: { + 'file.txt': 'real', + }, + }, + }); + vol.symlinkSync(path.resolve('root/realdir'), path.resolve('root/linkdir')); + }); + + it('does not recurse into symlinks to directories', async () => { + expect.hasAssertions(); + + const filePaths: string[] = []; + await crawlDirFast(filePaths, 'root'); + + expect(filePaths).toContain(path.join('root', 'realdir', 'file.txt')); + expect(filePaths).toContain(path.join('root', 'linkdir')); + expect(filePaths).not.toContain(path.join('root', 'linkdir', 'file.txt')); + }); + }); + + describe(crawlDirWithChecks, () => { + beforeEach(() => { + vol.fromNestedJSON({ + root: { + realdir: { + 'file.txt': 'real', + }, + }, + }); + vol.symlinkSync(path.resolve('root/realdir'), path.resolve('root/linkdir')); + }); + + it('does not recurse into symlinks to directories', async () => { + expect.hasAssertions(); + + const filePaths: string[] = []; + const checkDir = vi.fn(() => false); + const checkFile = vi.fn(() => true); + + await crawlDirWithChecks(filePaths, 'root', checkDir, checkFile); + + expect(filePaths).toContain(path.join('root', 'realdir', 'file.txt')); + expect(filePaths).toContain(path.join('root', 'linkdir')); + expect(filePaths).not.toContain(path.join('root', 'linkdir', 'file.txt')); + // The symlink itself is offered to checkFile (not checkDir), so it can be matched + // by file globs but is never descended into. + expect(checkDir).toHaveBeenCalledWith(path.join('root', 'realdir')); + expect(checkDir).not.toHaveBeenCalledWith(path.join('root', 'linkdir')); + expect(checkFile).toHaveBeenCalledWith(path.join('root', 'linkdir')); + }); + }); + + describe(removeFiles, () => { + it('unlinks the symlink itself rather than its target', async () => { + expect.hasAssertions(); + + vol.fromNestedJSON({ 'target.md': 'content' }); + vol.symlinkSync(path.resolve('target.md'), path.resolve('link.md')); + + const result = await removeFiles(['link.md']); + + expect(result.failures).toStrictEqual([]); + expect(fs.existsSync('link.md')).toBe(false); + expect(fs.existsSync('target.md')).toBe(true); + }); }); }); diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts index ef4ce27..840db9d 100644 --- a/src/utils/filesystem.ts +++ b/src/utils/filesystem.ts @@ -1,12 +1,32 @@ -import fs, { Dirent } from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; +import fs, { type Dirent } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import type { CleanFailure, CleanFailurePhase } from '../shared.js'; export type DirentAction = (dirent: Dirent) => void; export type CheckPathFunc = (nextPath: string) => boolean; +export type CrawlErrorHandler = (failure: CleanFailure) => void; -function hasErrorCode(error: any, code: string): boolean { - return error?.code === code; +function hasErrorCode(error: unknown, code: string): boolean { + return (error as { code?: string })?.code === code; +} + +/** + * Builds a `CleanFailure` from a thrown filesystem error. + */ +export function toFailure(filePath: string, phase: CleanFailurePhase, error: unknown): CleanFailure { + const err = error as { code?: string; message?: string } | undefined; + const failure: CleanFailure = { + path: filePath, + phase, + message: err?.message ?? String(error), + }; + + if (err?.code !== undefined) { + failure.code = err.code; + } + + return failure; } /** @@ -28,14 +48,21 @@ export async function fileExists(filePath: string): Promise { /** * Asynchronously loop through each file in a directory, passing the dirents for each file to the provided action. + * ENOENT/ENOTDIR errors are treated as empty directories. Other errors are forwarded to `onError`, when provided. */ -export async function forEachDirentAsync(dirPath: string, action: DirentAction): Promise { +export async function forEachDirentAsync( + dirPath: string, + action: DirentAction, + onError?: CrawlErrorHandler +): Promise { let dirFiles: Dirent[] = []; try { dirFiles = await fs.promises.readdir(dirPath, { withFileTypes: true }); - } catch (error) { - // do nothing + } catch (error: unknown) { + if (!hasErrorCode(error, 'ENOENT') && !hasErrorCode(error, 'ENOTDIR')) { + onError?.(toFailure(dirPath, 'readdir', error)); + } } await Promise.all(dirFiles.map(action)); @@ -58,67 +85,126 @@ export async function readDirectory(dirPath: string): Promise { } /** - * Remove empty directories, recursively travelling up in the file until the first non-empty directory is reached. + * Counts the path separators in a directory path. Used to bucket directories + * by depth so they can be processed strictly bottom-up. */ -export async function removeEmptyDirsUp( - checkedDirs: Set, - dirPath: string, - count = 0 -): Promise { - if (!checkedDirs.has(dirPath)) { - const files = await readDirectory(dirPath); - const emptyDir = files.length === 0; - checkedDirs.add(dirPath); - - if (emptyDir) { - try { - await fs.promises.rmdir(dirPath); - // biome-ignore lint/style/noParameterAssign: recursive function - count++; - } catch (error) { - // do nothing - } - - const parentDir = path.dirname(dirPath); - // biome-ignore lint/style/noParameterAssign: recursive function - count = await removeEmptyDirsUp(checkedDirs, parentDir, count); +function depthOf(dirPath: string): number { + let count = 0; + for (const char of dirPath) { + // Counts both `/` and `\` because Windows accepts either as a separator and `path.dirname` preserves whichever the input used. + if (char === '/' || char === '\\') { + count++; } } - return count; } +/** + * Attempts to remove a directory if it's empty. Returns true if it was removed. + * ENOENT, EBUSY and similar errors are treated as no-ops (best effort). + */ +async function tryRemoveDirIfEmpty(dirPath: string): Promise { + try { + const entries = await fs.promises.readdir(dirPath); + if (entries.length > 0) { + return false; + } + await fs.promises.rmdir(dirPath); + return true; + } catch { + return false; + } +} + /** * Deeply removes empty directories for each file path. + * + * Walks up from each file's immediate parent and removes any ancestor that is + * empty. Directories are processed in waves grouped by depth to avoid redundant + * `readdir` calls on shared ancestors. + * * @param filePaths the file paths to remove empty directories for * @returns the number of empty directories removed */ -export async function removeEmptyDirs(filePaths: string[]): Promise { - let removedEmptyDirs = 0; +export async function removeEmptyDirs(filePaths: string[] | readonly string[]): Promise { + if (filePaths.length === 0) { + return 0; + } - await Promise.all( - filePaths.map(async filePath => { - const removedParentDirs = await removeEmptyDirsUp(new Set(), path.dirname(filePath)); - removedEmptyDirs += removedParentDirs; - }) - ); + const dirsByDepth = new Map>(); + let maxDepth = 0; + + function queue(dir: string): void { + const depth = depthOf(dir); + let bucket = dirsByDepth.get(depth); + + if (!bucket) { + bucket = new Set(); + dirsByDepth.set(depth, bucket); + } + + bucket.add(dir); + + if (depth > maxDepth) { + maxDepth = depth; + } + } + + for (const filePath of filePaths) { + queue(path.dirname(filePath)); + } + + let removedCount = 0; - return removedEmptyDirs; + for (let depth = maxDepth; depth >= 0; depth--) { + const bucket = dirsByDepth.get(depth); + if (!bucket || bucket.size === 0) { + continue; + } + + // oxlint-disable-next-line no-await-in-loop + const results = await Promise.all( + [...bucket].map(async dir => ({ dir, removed: await tryRemoveDirIfEmpty(dir) })) + ); + + for (const { dir, removed } of results) { + if (!removed) { + continue; + } + + removedCount++; + + const parent = path.dirname(dir); + if (parent !== dir) { + queue(parent); + } + } + } + + return removedCount; } /** * Find all files in a directory as fast as possible, without any extra checks or validations. */ -export async function crawlDirFast(filePaths: string[], dirPath: string): Promise { - await forEachDirentAsync(dirPath, async dirent => { - const nextPath = dirPath + path.sep + dirent.name; - - if (dirent.isDirectory()) { - await crawlDirFast(filePaths, nextPath); - } else { - filePaths.push(nextPath); - } - }); +export async function crawlDirFast( + filePaths: string[], + dirPath: string, + onError?: CrawlErrorHandler +): Promise { + await forEachDirentAsync( + dirPath, + async dirent => { + const nextPath = dirPath + path.sep + dirent.name; + + if (dirent.isDirectory()) { + await crawlDirFast(filePaths, nextPath, onError); + } else { + filePaths.push(nextPath); + } + }, + onError + ); } /** @@ -128,23 +214,28 @@ export async function crawlDirWithChecks( filePaths: string[], // Mutate array to avoid losing speed on spreading dirPath: string, checkDir: CheckPathFunc, - checkFile: CheckPathFunc + checkFile: CheckPathFunc, + onError?: CrawlErrorHandler ): Promise { - await forEachDirentAsync(dirPath, async nextPathDirent => { - const nextPath = dirPath + path.sep + nextPathDirent.name; + await forEachDirentAsync( + dirPath, + async nextPathDirent => { + const nextPath = dirPath + path.sep + nextPathDirent.name; - if (nextPathDirent.isDirectory()) { - if (checkDir(nextPath)) { - // If a full directory matches, include all of it. - await crawlDirFast(filePaths, nextPath); - } else { - // Keep recursively checking each directory - await crawlDirWithChecks(filePaths, nextPath, checkDir, checkFile); + if (nextPathDirent.isDirectory()) { + if (checkDir(nextPath)) { + // If a full directory matches, include all of it. + await crawlDirFast(filePaths, nextPath, onError); + } else { + // Keep recursively checking each directory + await crawlDirWithChecks(filePaths, nextPath, checkDir, checkFile, onError); + } + } else if (checkFile(nextPath)) { + filePaths.push(nextPath); } - } else if (checkFile(nextPath)) { - filePaths.push(nextPath); - } - }); + }, + onError + ); return filePaths; } @@ -153,41 +244,68 @@ export type RemoveFilesOptions = { dryRun?: boolean; }; +export type RemoveFilesResult = { + /** Number of files removed. */ + removedFilesCount: number; + /** Total size of the removed files, in bytes. */ + reducedSize: number; + /** Per-file failures encountered while stating or unlinking. */ + failures: CleanFailure[]; +}; + /** - * Removes files and returns the total size of the removed files. + * Removes files and returns the total size of the removed files alongside any per-file failures. + * + * ENOENT errors are treated as a no-op (the file already does not exist). Any other error during + * `stat` or `unlink` is captured as a {@link CleanFailure}. + * * @param filePaths the file paths to remove * @param options dryRun: if true, don't actually remove the files - * @returns the total size of the removed files */ export async function removeFiles( - filePaths: string[], + filePaths: string[] | readonly string[], options: RemoveFilesOptions = {} -): Promise { +): Promise { let reducedSize = 0; + const failures: CleanFailure[] = []; + let removedFilesCount = 0; await Promise.all( filePaths.map(async filePath => { + let fileStats: Awaited>; + try { - const fileStats = await fs.promises.stat(filePath); + fileStats = await fs.promises.stat(filePath); + } catch (error: unknown) { + if (!hasErrorCode(error, 'ENOENT')) { + failures.push(toFailure(filePath, 'stat', error)); + } + return; + } - if (!options.dryRun) { + if (!options.dryRun) { + try { await fs.promises.unlink(filePath); + removedFilesCount++; + } catch (error: unknown) { + if (!hasErrorCode(error, 'ENOENT')) { + failures.push(toFailure(filePath, 'unlink', error)); + } + return; } - - reducedSize += fileStats.size; - } catch (error) { - // do nothing } + + reducedSize += fileStats.size; }) ); - return reducedSize; + return { removedFilesCount, reducedSize, failures }; } /** * Get directory of the file directory, like CommonJS `__dirname`. * @example const thisFilesDir = fileDir(import.meta.url); */ -export function fileDir(importMetaUrl: string) { +export function fileDir(importMetaUrl: string): string { return path.dirname(fileURLToPath(importMetaUrl)); } diff --git a/src/utils/formatting.test.ts b/src/utils/formatting.test.ts new file mode 100644 index 0000000..9bbe398 --- /dev/null +++ b/src/utils/formatting.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it, vi } from 'vitest'; +import { formatBytes, formatMs } from './formatting.js'; + +vi.setConfig({ testTimeout: 5000 }); + +describe(formatBytes, () => { + it('returns 0 B for non-positive or non-finite input', () => { + expect(formatBytes(0)).toBe('0 B'); + expect(formatBytes(-1)).toBe('0 B'); + expect(formatBytes(Number.NaN)).toBe('0 B'); + expect(formatBytes(Number.POSITIVE_INFINITY)).toBe('0 B'); + }); + + it('formats values under 1 kB as B', () => { + expect(formatBytes(1)).toBe('1 B'); + expect(formatBytes(999)).toBe('999 B'); + }); + + it('uses decimal SI steps (1000) up to GB', () => { + expect(formatBytes(1000)).toBe('1 kB'); + expect(formatBytes(1500)).toBe('1.5 kB'); + expect(formatBytes(1_000_000)).toBe('1 MB'); + expect(formatBytes(1_234_567)).toBe('1.23 MB'); + expect(formatBytes(1_000_000_000)).toBe('1 GB'); + expect(formatBytes(2_500_000_000)).toBe('2.5 GB'); + }); + + it('stays on GB for very large values', () => { + expect(formatBytes(5_000_000_000_000)).toBe('5000 GB'); + }); +}); + +describe(formatMs, () => { + it('returns 0ms for non-positive or non-finite input', () => { + expect(formatMs(0)).toBe('0ms'); + expect(formatMs(-1)).toBe('0ms'); + expect(formatMs(Number.NaN)).toBe('0ms'); + expect(formatMs(Number.POSITIVE_INFINITY)).toBe('0ms'); + }); + + it('formats sub-second durations in ms', () => { + expect(formatMs(1)).toBe('1ms'); + expect(formatMs(500)).toBe('500ms'); + expect(formatMs(999)).toBe('999ms'); + }); + + it('formats under one minute in seconds', () => { + expect(formatMs(1000)).toBe('1s'); + expect(formatMs(1500)).toBe('1.5s'); + expect(formatMs(59_950)).toBe('59.95s'); + }); + + it('formats one minute and up with m / m s', () => { + expect(formatMs(60_000)).toBe('1m'); + expect(formatMs(90_000)).toBe('1m 30s'); + expect(formatMs(61_000)).toBe('1m 1s'); + expect(formatMs(125_000)).toBe('2m 5s'); + }); +}); diff --git a/src/utils/formatting.ts b/src/utils/formatting.ts new file mode 100644 index 0000000..aa67847 --- /dev/null +++ b/src/utils/formatting.ts @@ -0,0 +1,61 @@ +const SI = 1000; +const SIZE_UNITS = ['B', 'kB', 'MB', 'GB'] as const; +const MAX_FRACTION_DIGITS = 2; + +/** + * Trims a number to two fraction digits and removes trailing zeros. + */ +function trimNumber(value: number): string { + return value.toFixed(MAX_FRACTION_DIGITS).replace(/\.?0+$/, ''); +} + +/** + * Formats a byte length using decimal (SI) units up to GB (e.g. `1.2 MB`). + */ +export function formatBytes(bytes: number): string { + if (!Number.isFinite(bytes) || bytes <= 0) { + return '0 B'; + } + + if (bytes < SI) { + return `${Math.round(bytes)} B`; + } + + let value = bytes / SI; + let unit = 1; + while (value >= SI && unit < SIZE_UNITS.length - 1) { + value /= SI; + unit++; + } + + return `${trimNumber(value)} ${SIZE_UNITS[unit]}`; +} + +const MS_PER_SECOND = 1000; +const SEC_PER_MINUTE = 60; +const MS_PER_MINUTE = SEC_PER_MINUTE * MS_PER_SECOND; + +/** + * Formats a duration in milliseconds up to minutes (e.g. `250ms`, `2.5s`, `1m 30s`). + */ +export function formatMs(ms: number): string { + if (!Number.isFinite(ms) || ms <= 0) { + return '0ms'; + } + + if (ms < MS_PER_SECOND) { + return `${Math.round(ms)}ms`; + } + + if (ms < MS_PER_MINUTE) { + return `${trimNumber(ms / MS_PER_SECOND)}s`; + } + + const minutes = Math.floor(ms / MS_PER_MINUTE); + const seconds = Math.floor((ms % MS_PER_MINUTE) / MS_PER_SECOND); + return seconds === 0 ? `${minutes}m` : `${minutes}m ${seconds}s`; +} + +export function formatJson(value: unknown, indent = 2): string { + return JSON.stringify(value, null, indent); +} diff --git a/src/utils/glob.test.ts b/src/utils/glob.test.ts index 2dac0e8..bdf30cf 100644 --- a/src/utils/glob.test.ts +++ b/src/utils/glob.test.ts @@ -1,12 +1,11 @@ -import { vol } from 'memfs'; -import path from 'path'; -import pm from 'picomatch'; -import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; +import { vol, type NestedDirectoryJSON } from 'memfs'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; import { - DEFAULT_PICO_OPTIONS, findFilesByGlobLists, formatGlob, - GlobLists, initGlobLists, makeGlobMatcher, mergeGlobLists, @@ -17,10 +16,20 @@ import { toAbsoluteGlobLists, updateGlobLists, wrapGlobs, + type GlobLists, } from './glob.js'; -import { getMockedFileStructure } from '../__test__/getMockedFileStructure.js'; -describe('makeGlobMatcher', () => { +type NodeError = Error & { code: string }; + +function makeNodeError(message: string, code: string): NodeError { + const error = new Error(message) as NodeError; + error.code = code; + return error; +} + +vi.setConfig({ testTimeout: 5000 }); + +describe(makeGlobMatcher, () => { it('creates a picomatch globber with default options', () => { const pattern = '**/**'; @@ -31,7 +40,7 @@ describe('makeGlobMatcher', () => { }); }); -describe('updateGlobLists', () => { +describe(updateGlobLists, () => { const mockGlobLists: GlobLists = { ...initGlobLists(), included: ['foo'], @@ -40,7 +49,7 @@ describe('updateGlobLists', () => { }; it('runs passed function for correct files', () => { - const mockFn = vi.fn(); + const mockFn = vi.fn<(globs: string[], key: keyof GlobLists) => string[]>(); updateGlobLists(mockGlobLists, mockFn); @@ -54,15 +63,15 @@ describe('updateGlobLists', () => { const expectedResult: GlobLists = { ...mockGlobLists, included: [...mockGlobLists.included, 'included'], - includedDirs: [...(mockGlobLists.includedDirs || []), 'includedDirs'], + includedDirs: [...mockGlobLists.includedDirs, 'includedDirs'], excluded: [...mockGlobLists.excluded, 'excluded'], }; - expect(result).toEqual(expectedResult); + expect(result).toStrictEqual(expectedResult); }); }); -describe('mergeGlobLists', () => { +describe(mergeGlobLists, () => { it('merges glob lists', () => { const result = mergeGlobLists( { @@ -79,7 +88,7 @@ describe('mergeGlobLists', () => { } ); - expect(result).toEqual({ + expect(result).toStrictEqual({ ...initGlobLists(), included: ['foo', 'bar'], includedDirs: ['foo', 'bar'], @@ -88,7 +97,7 @@ describe('mergeGlobLists', () => { }); }); -describe('toAbsoluteGlobLists', () => { +describe(toAbsoluteGlobLists, () => { it('prepends globs with absolute paths on all platforms', () => { const result = toAbsoluteGlobLists( { @@ -100,7 +109,7 @@ describe('toAbsoluteGlobLists', () => { '/foo' ); - expect(result).toEqual({ + expect(result).toStrictEqual({ ...initGlobLists(), included: ['/foo/bar'], includedDirs: ['/foo/bar'], @@ -109,31 +118,31 @@ describe('toAbsoluteGlobLists', () => { }); }); -describe('wrapGlobs', () => { +describe(wrapGlobs, () => { it('wraps globs into a single glob', () => { const result = wrapGlobs(['**/foo', '**/bar', '*/baz']); - expect(result).toEqual('@((**/foo)|(**/bar)|(*/baz))'); + expect(result).toBe('@((**/foo)|(**/bar)|(*/baz))'); }); it('prepends an optional prefix to the result', () => { const result = wrapGlobs(['**/foo', '**/bar', '*/baz'], 'hello'); - expect(result).toEqual('hello@((**/foo)|(**/bar)|(*/baz))'); + expect(result).toBe('hello@((**/foo)|(**/bar)|(*/baz))'); }); }); -describe('optimizeGlobs', () => { +describe(optimizeGlobs, () => { it('splits globs by leading characters and merges into two globs', () => { const result = optimizeGlobs(['**/some', '*/where', 'over', '**/the', '/rainbow']); - expect(result).toEqual(['**/@((some)|(the))', '@((*/where)|(over)|(/rainbow))']); + expect(result).toStrictEqual(['**/@((some)|(the))', '@((*/where)|(over)|(/rainbow))']); }); it('normalizes leading globstars', () => { const result = optimizeGlobs(['**/**/**/one', '**/**/two', '**/three', '/**/four']); - expect(result).toEqual(['**/@((one)|(two)|(three)|(four))']); + expect(result).toStrictEqual(['**/@((one)|(two)|(three)|(four))']); }); }); -describe('optimizeGlobLists', () => { +describe(optimizeGlobLists, () => { it('splits globs by leading characters and merges into two globs', () => { const result = optimizeGlobLists({ ...initGlobLists(), @@ -162,34 +171,40 @@ describe('optimizeGlobLists', () => { }); }); -describe('formatGlob', () => { +describe(formatGlob, () => { it('trims globs', () => { const result = formatGlob(' foo/** '); - expect(result).toEqual('**/foo/**'); + expect(result).toBe('**/foo/**'); }); it('adds globstars to globs not starting with a slash', () => { const result = formatGlob('test.ts'); - expect(result).toEqual('**/test.ts'); + expect(result).toBe('**/test.ts'); }); it('replaces escaped leading exclamation marks', () => { - const result = formatGlob('\\!(*.d).ts'); - expect(result).toEqual('**/!(*.d).ts'); + const result = formatGlob(String.raw`\!(*.d).ts`); + expect(result).toBe('**/!(*.d).ts'); }); it('removes leading slashes', () => { const result = formatGlob('/path/to/file'); - expect(result).toEqual('path/to/file'); + expect(result).toBe('path/to/file'); }); it('appends globstars to directory globs', () => { const result = formatGlob('/path/to/directory/'); - expect(result).toEqual('path/to/directory/**'); + expect(result).toBe('path/to/directory/**'); + }); + + it('does not double-prepend globstars when the glob already starts with **/', () => { + expect(formatGlob('**/foo')).toBe('**/foo'); + expect(formatGlob('**/foo/**')).toBe('**/foo/**'); + expect(formatGlob('**/foo/')).toBe('**/foo/**'); }); }); -describe('processGlobs', () => { +describe(processGlobs, () => { it('formats globs and splits them into include/exclude glob lists', () => { const result = processGlobs(['**/test', '!test.js', '**/path/to/directory/', '*.ext']); expect(result).toMatchInlineSnapshot(` @@ -198,12 +213,12 @@ describe('processGlobs', () => { "**/test.js", ], "included": [ - "**/**/test", - "**/**/path/to/directory/**", + "**/test", + "**/path/to/directory/**", "**/*.ext", ], "includedDirs": [ - "**/**/path/to/directory", + "**/path/to/directory", ], "originalIncluded": [ "**/test", @@ -216,25 +231,27 @@ describe('processGlobs', () => { it('removes trailing globstars from dir globs', () => { const result = processGlobs(['**/foo/**', '/bar/**']); - expect(result.includedDirs).toEqual(['**/**/foo', 'bar']); + expect(result.includedDirs).toStrictEqual(['**/foo', 'bar']); }); it('filters empty strings', () => { const result = processGlobs(['foo', '', '', '', 'bar']); - expect(result.included).toEqual(['**/foo', '**/bar']); + expect(result.included).toStrictEqual(['**/foo', '**/bar']); }); it('keeps original inclusion globs', () => { const original = ['**/test', '**/path/to/directory/', '*.ext']; const result = processGlobs(original); - expect(result.originalIncluded).toEqual(original); + expect(result.originalIncluded).toStrictEqual(original); }); }); -describe('parseGlobsFile', () => { +describe(parseGlobsFile, () => { const globFilePath = process.cwd() + '/.cleanmodules'; it('loads globs from a glob file', async () => { + expect.hasAssertions(); + const globFile = ` # this is a comment __test__/ @@ -276,6 +293,8 @@ describe('parseGlobsFile', () => { }); it('removes comments', async () => { + expect.hasAssertions(); + const globFile = ` # this is a comment # this too @@ -285,11 +304,13 @@ describe('parseGlobsFile', () => { vol.fromNestedJSON({ [globFilePath]: globFile }); const result = await parseGlobsFile(globFilePath); - expect(result.included).toEqual(['**/path/to/something']); - expect(result.excluded).toEqual([]); + expect(result.included).toStrictEqual(['**/path/to/something']); + expect(result.excluded).toStrictEqual([]); }); it('removes empty lines', async () => { + expect.hasAssertions(); + const globFile = ` path/to/something @@ -298,62 +319,113 @@ describe('parseGlobsFile', () => { vol.fromNestedJSON({ [globFilePath]: globFile }); const result = await parseGlobsFile(globFilePath); - expect(result.included).toEqual(['**/path/to/something']); - expect(result.excluded).toEqual([]); + expect(result.included).toStrictEqual(['**/path/to/something']); + expect(result.excluded).toStrictEqual([]); }); it('adds lines starting with an exclamation point to excluded globs', async () => { + expect.hasAssertions(); + const globFile = ` !excludeMe `; vol.fromNestedJSON({ [globFilePath]: globFile }); const result = await parseGlobsFile(globFilePath); - expect(result.included).toEqual([]); - expect(result.excluded).toEqual(['**/excludeMe']); + expect(result.included).toStrictEqual([]); + expect(result.excluded).toStrictEqual(['**/excludeMe']); }); }); -describe('findFilesByGlobLists', async () => { - const fileStructure = await getMockedFileStructure(); +describe(findFilesByGlobLists, () => { + let fileStructure: NestedDirectoryJSON; const nodeModulesPath = 'node_modules'; - beforeEach(async () => { + beforeAll(async () => { + fileStructure = await getMockedFileStructure(); + }); + + beforeEach(() => { vol.fromNestedJSON(fileStructure); }); it('includes dirs', async () => { + expect.hasAssertions(); + const result = await findFilesByGlobLists(nodeModulesPath, { ...initGlobLists(), includedDirs: ['**/__tests__', '**/dep3'], }); - expect(result).toEqual([ + expect(result.files).toStrictEqual([ path.join('node_modules', 'dep1', '__tests__', 'test1.js'), path.join('node_modules', 'dep1', '__tests__', 'test2.js'), path.join('node_modules', 'dep3', 'deeply', 'nested', 'file.ext'), ]); + expect(result.failures).toStrictEqual([]); }); it('includes files', async () => { + expect.hasAssertions(); + const result = await findFilesByGlobLists(nodeModulesPath, { ...initGlobLists(), included: ['**/deeply/nested/file.ext', '**/dep4/**'], }); - expect(result).toEqual([ + expect(result.files).toStrictEqual([ path.join('node_modules', 'dep4', 'nonDefaultFile.ext'), path.join('node_modules', 'dep3', 'deeply', 'nested', 'file.ext'), ]); + expect(result.failures).toStrictEqual([]); }); it('can exclude files and dirs by glob patterns', async () => { + expect.hasAssertions(); + const result = await findFilesByGlobLists(nodeModulesPath, { ...initGlobLists(), included: ['**/*.js'], excluded: ['**/test*.js'], }); - expect(result).toEqual([path.join('node_modules', 'dep2', 'file.js')]); + expect(result.files).toStrictEqual([path.join('node_modules', 'dep2', 'file.js')]); + expect(result.failures).toStrictEqual([]); + }); + + it('collects crawl failures without aborting the walk', async () => { + expect.hasAssertions(); + + const failingDir = path.join('node_modules', 'dep1', '__tests__'); + + // Capture the (memfs-backed) original before installing the spy so we can pass-through. + const originalReaddir = fs.promises.readdir.bind(fs.promises); + const readdirSpy = vi.spyOn(fs.promises, 'readdir').mockImplementation(((dirPath, options) => + // oxlint-disable-next-line vitest/no-conditional-in-test + dirPath === failingDir + ? Promise.reject(makeNodeError('denied', 'EACCES')) + : originalReaddir(dirPath as never, options as never)) as typeof fs.promises.readdir); + + const result = await findFilesByGlobLists(nodeModulesPath, { + ...initGlobLists(), + included: ['**/*.js', '**/*.md'], + }); + + // file from the failing directory should be missing, but the walk should still complete + expect(result.files).toStrictEqual( + expect.arrayContaining([ + path.join('node_modules', 'dep2', 'CHANGELOG.md'), + path.join('node_modules', 'dep2', 'file.js'), + path.join('node_modules', 'dep1', 'a-dir', 'doc.md'), + ]) + ); + expect(result.failures).toHaveLength(1); + expect(result.failures[0]).toMatchObject({ + path: failingDir, + phase: 'readdir', + code: 'EACCES', + }); + + readdirSpy.mockRestore(); }); }); diff --git a/src/utils/glob.ts b/src/utils/glob.ts index 6559819..0433429 100644 --- a/src/utils/glob.ts +++ b/src/utils/glob.ts @@ -1,8 +1,9 @@ -import { promises as fsAsync } from 'fs'; -import path from 'path'; -import pm, { PicomatchOptions } from 'picomatch'; +import { promises as fsAsync } from 'node:fs'; +import path from 'node:path'; +import pm, { type PicomatchOptions } from 'picomatch'; +import type { CleanFailure } from '../shared.js'; import { DEFAULT_GLOBS_FILE_PATH } from '../shared.js'; -import { crawlDirWithChecks, fileExists } from './filesystem.js'; +import { type CrawlErrorHandler, crawlDirWithChecks, fileExists } from './filesystem.js'; export function initGlobLists(): GlobLists { return { excluded: [], included: [], includedDirs: [], originalIncluded: [] }; @@ -14,32 +15,29 @@ export const DEFAULT_PICO_OPTIONS: Partial = { strictSlashes: true, }; -export interface GlobberPicoOptions { +export type GlobberPicoOptions = { dot?: boolean; regex?: boolean; nocase?: boolean; ignore?: string | string[]; cwd?: string; -} +}; -export type GlobFunc = (filePath: string, test?: boolean) => boolean; +export type GlobFunc = (filePath: string, test?: false | undefined) => boolean; /** * Creates a picomatch matcher from a set of globs. */ -export function makeGlobMatcher( - globs: string | string[], - picoOptions?: GlobberPicoOptions -): GlobFunc { +export function makeGlobMatcher(globs: string | string[], picoOptions?: GlobberPicoOptions): GlobFunc { return pm(globs, { ...DEFAULT_PICO_OPTIONS, ...picoOptions }); } -export interface GlobLists { +export type GlobLists = { excluded: string[]; included: string[]; includedDirs: string[]; originalIncluded: string[]; -} +}; /** * Runs an action on all editable lists in a GlobLists object, and returns the updated object. @@ -68,23 +66,20 @@ export function mergeGlobLists(globListsA: GlobLists, globListsB: GlobLists): Gl }; } -/** Replaces path with forward slashes as separators if necessary. Globs should always have POSIX separators, even on Windows. */ +/** + * Replaces path with forward slashes as separators if necessary. Globs should always have POSIX separators, even on Windows. + */ export function toPosixPath(pathStr: string): string { - return path.sep === '/' ? pathStr : pathStr.replace(/\\/g, '/'); + return path.sep === '/' ? pathStr : pathStr.replaceAll('\\', '/'); } /** * Prepends an absolute path to all editable lists in a GlobLists object. */ -export function toAbsoluteGlobLists( - globLists: GlobLists, - absoluteNodeModulesPath: string -): GlobLists { +export function toAbsoluteGlobLists(globLists: GlobLists, absoluteNodeModulesPath: string): GlobLists { const absolutePathWithPosixSeparator = toPosixPath(absoluteNodeModulesPath); - return updateGlobLists(globLists, globs => - globs.map(glob => absolutePathWithPosixSeparator + '/' + glob) - ); + return updateGlobLists(globLists, globs => globs.map(glob => absolutePathWithPosixSeparator + '/' + glob)); } /** @@ -104,7 +99,7 @@ export function optimizeGlobs(globs: string[]): string[] { const fixedPathGlobs: string[] = []; for (const glob of globs) { - if (glob.match(GLOBSTAR_START_REGEX)) { + if (GLOBSTAR_START_REGEX.test(glob)) { globstarStartGlobs.push(glob); } else { fixedPathGlobs.push(glob); @@ -136,7 +131,7 @@ const EXCLAMATION_START = /^\s*!/; // Globs starting with ! const DIR_GLOB_REGEX = /\/\**$/; // Globs ending with /, /* or /** const EXCLUDED_GLOB_REGEX = /^!/; // Globs starting with ! const ESCAPED_NEGATIVE_GLOB_REGEX = /^\\!/; // Globs starting with \! -const NOT_FIXED_START_GLOB_REGEX = /^([^/])/; // Globs not starting with / +const NOT_FIXED_START_GLOB_REGEX = /^(?!\*\*\/)([^/])/; // Globs not starting with / or **/ const FIXED_START_GLOB_REGEX = /^\//; // Globs starting with / /** @@ -159,7 +154,7 @@ export function processGlobs(globs: string[]): GlobLists { const globLists = initGlobLists(); for (const glob of globs) { - const isExcluded = !!glob.match(EXCLAMATION_START); + const isExcluded = !!EXCLAMATION_START.test(glob); const formattedGlob = formatGlob(glob); if (!formattedGlob) { @@ -190,14 +185,14 @@ export async function parseGlobsFile(filePath: string): Promise { let fileContents: string; try { - fileContents = (await fsAsync.readFile(filePath, { encoding: 'utf-8' })).toString() || ''; + fileContents = await fsAsync.readFile(filePath, { encoding: 'utf8' }); } catch (error) { + // oxlint-disable-next-line no-console console.error(`Failed to read glob file (${filePath})`); throw error; } - const fileGlobs = - fileContents.split(/\r?\n/).filter(line => !line.match(COMMENT_OR_EMPTY_REGEX)) || []; + const fileGlobs = fileContents.split(/\r?\n/).filter(line => !COMMENT_OR_EMPTY_REGEX.test(line)) || []; return processGlobs(fileGlobs); } @@ -205,24 +200,20 @@ export async function parseGlobsFile(filePath: string): Promise { /** * Parses clean-modules' default glob file. */ -export async function parseDefaultGlobsFile(): Promise { +export function parseDefaultGlobsFile(): Promise { return parseGlobsFile(DEFAULT_GLOBS_FILE_PATH); } -export interface GetGlobListsOptions { +export type GetGlobListsOptions = { noDefaults?: boolean | undefined; globFile?: string | undefined; globs?: string[] | undefined; -} +}; /** * Parses and combines globs from all possible sources. */ -export async function getGlobLists({ - noDefaults, - globFile, - globs, -}: GetGlobListsOptions): Promise { +export async function getGlobLists({ noDefaults, globFile, globs }: GetGlobListsOptions): Promise { const globListsToMerge: GlobLists[] = []; if (!noDefaults) { @@ -240,38 +231,53 @@ export async function getGlobLists({ globListsToMerge.push(argGlobsLists); } - return globListsToMerge.reduce( - (mergedGlobLists, globLists) => mergeGlobLists(mergedGlobLists, globLists), - initGlobLists() - ); + let mergedGlobLists = initGlobLists(); + + for (const globLists of globListsToMerge) { + mergedGlobLists = mergeGlobLists(mergedGlobLists, globLists); + } + + return mergedGlobLists; } +export type FindFilesResult = { + /** File paths that matched the include rules. */ + files: string[]; + /** Non-fatal failures encountered while crawling the directory tree. */ + failures: CleanFailure[]; +}; + /** * Finds all files matching the given glob lists in the given directory. + * + * Crawl errors (e.g. permission failures on a subdirectory) are collected as `failures` so the + * walk can continue and partial results are still returned. + * * @param directory the directory to search in * @param globLists the glob lists to match against - * @returns a promise that resolves to an array of matching file paths */ export async function findFilesByGlobLists( directory: string, globLists: GlobLists -): Promise { - const { included, includedDirs, excluded } = toAbsoluteGlobLists( - optimizeGlobLists(globLists), - directory - ); +): Promise { + const { included, includedDirs, excluded } = toAbsoluteGlobLists(optimizeGlobLists(globLists), directory); const picoOptions = { ignore: excluded }; - const checkDir = includedDirs?.length ? makeGlobMatcher(includedDirs, picoOptions) : () => false; + const checkDir = includedDirs?.length ? makeGlobMatcher(includedDirs, picoOptions) : (): boolean => false; const checkFile = makeGlobMatcher(included, picoOptions); - let filesToRemove = await crawlDirWithChecks([], directory, checkDir, checkFile); + const failures: CleanFailure[] = []; + const onError: CrawlErrorHandler = failure => { + failures.push(failure); + }; + + let files = await crawlDirWithChecks([], directory, checkDir, checkFile, onError); if (excluded.length) { // make another pass to ensure that files included by dir globs are // matched with excluded globs too - filesToRemove = filesToRemove.filter(file => checkFile(file)); + files = files.filter(file => checkFile(file)); } - return filesToRemove; + return { files, failures }; } diff --git a/tsconfig.json b/tsconfig.json index 101a8af..440f4ff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,31 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "extends": ["@tsconfig/node14/tsconfig.json"], + "exclude": ["node_modules", "dist", ".history", "images"], "compilerOptions": { + "lib": ["esnext"], + "target": "esnext", + "module": "preserve", + "moduleResolution": "bundler", + "types": [], + "outDir": "dist", + "noEmit": true, + "tsBuildInfoFile": "./.tsbuildinfo", + "incremental": true, + "strict": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "erasableSyntaxOnly": true, + "isolatedModules": true, + "resolveJsonModule": true, + "exactOptionalPropertyTypes": true, "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, "noImplicitReturns": true, - "noImplicitOverride": true + "noUncheckedIndexedAccess": true, + "noUncheckedSideEffectImports": true } } diff --git a/tsup.config.ts b/tsdown.config.ts similarity index 52% rename from tsup.config.ts rename to tsdown.config.ts index c16cba8..f4fd828 100644 --- a/tsup.config.ts +++ b/tsdown.config.ts @@ -1,13 +1,12 @@ -import { defineConfig } from 'tsup'; +import { defineConfig } from 'tsdown'; export default defineConfig({ - format: ['cjs', 'esm'], + format: ['esm'], entry: ['src/index.ts', 'src/cli/cli.ts'], outDir: 'dist', - target: 'node14', - splitting: true, + target: 'node22', + outExtensions: () => ({ js: '.js', dts: '.d.ts' }), sourcemap: false, clean: true, - shims: true, dts: true, });