├── .prettierignore ├── src ├── rules │ ├── __tests__ │ │ ├── fixtures │ │ │ ├── file.ts │ │ │ └── tsconfig.json │ │ └── utils.ts │ ├── no-object-assign-mutation │ │ ├── index.spec.ts │ │ ├── index.ts │ │ └── README.md │ ├── no-object-assign │ │ ├── index.spec.ts │ │ ├── README.md │ │ └── index.ts │ ├── index.ts │ ├── no-type-assertion │ │ ├── README.md │ │ ├── index.ts │ │ └── index.spec.ts │ ├── no-unsafe-object-property-check │ │ ├── index.spec.ts │ │ ├── index.ts │ │ └── README.md │ ├── utils.ts │ ├── no-unsafe-object-enum-method │ │ ├── index.ts │ │ ├── index.spec.ts │ │ └── README.md │ └── no-unsafe-object-property-overwrite │ │ ├── README.md │ │ ├── index.ts │ │ └── index.spec.ts ├── configs │ ├── index.ts │ └── recommended.ts └── index.ts ├── .vscode └── settings.json ├── vitest.config.js ├── tsconfig.build.json ├── .prettierrc.js ├── tsconfig.json ├── .github └── workflows │ ├── ci.yml │ └── publish.yml ├── LICENSE ├── eslint.config.js ├── package.json ├── .gitignore ├── README.md ├── CHANGELOG.md └── pnpm-lock.yaml /.prettierignore: -------------------------------------------------------------------------------- 1 | *.json 2 | -------------------------------------------------------------------------------- /src/rules/__tests__/fixtures/file.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /src/rules/__tests__/utils.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | export function getFixturesDir(): string { 4 | return path.join(__dirname, "fixtures"); 5 | } 6 | -------------------------------------------------------------------------------- /vitest.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | coverage: { 7 | include: ["src/**/*.ts"], 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/configs/index.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from "eslint"; 2 | import recommended from "./recommended.js"; 3 | 4 | const configs: { 5 | recommended: Linter.Config; 6 | } = { 7 | recommended, 8 | }; 9 | 10 | export default configs; 11 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { ESLint } from "eslint"; 2 | import rules from "./rules/index.js"; 3 | import configs from "./configs/index.js"; 4 | 5 | const plugin = { 6 | rules, 7 | configs, 8 | } satisfies ESLint.Plugin; 9 | 10 | export default plugin; 11 | -------------------------------------------------------------------------------- /src/rules/__tests__/fixtures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "esModuleInterop": true, 6 | "lib": ["esnext"], 7 | "jsx": "react-jsx", 8 | "strict": true 9 | }, 10 | "include": [ 11 | "file.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "lib", 6 | "sourceMap": true, 7 | "declaration": true, 8 | "declarationMap": true 9 | }, 10 | "include": [ 11 | "src/**/*" 12 | ], 13 | "exclude": [ 14 | "src/**/*.test.*", 15 | "src/**/*.spec.*", 16 | "src/**/__tests__/**/*" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 100, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: true, 6 | singleQuote: false, 7 | quoteProps: "as-needed", 8 | jsxSingleQuote: false, 9 | trailingComma: "all", 10 | bracketSpacing: true, 11 | bracketSameLine: false, 12 | objectWrap: "preserve", 13 | arrowParens: "always", 14 | endOfLine: "lf", 15 | experimentalTernaries: true, 16 | experimentalOperatorPosition: "start", 17 | }; 18 | -------------------------------------------------------------------------------- /src/rules/no-object-assign-mutation/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import rule from "./index.js"; 3 | 4 | const ruleTester = new RuleTester(); 5 | 6 | ruleTester.run("no-object-assign-mutation", rule, { 7 | valid: [`Object.assign`, `Object.assign({}, x)`, `Object.assign(x)`], 8 | invalid: [ 9 | { 10 | code: `Object.assign(x, y)`, 11 | errors: [ 12 | { 13 | messageId: "noMutation", 14 | line: 1, 15 | column: 1, 16 | }, 17 | ], 18 | }, 19 | ], 20 | }); 21 | -------------------------------------------------------------------------------- /src/configs/recommended.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from "eslint"; 2 | 3 | const config: Linter.Config = { 4 | name: "@susisu/safe-typescript/recommended", 5 | rules: { 6 | "@susisu/safe-typescript/no-object-assign-mutation": "error", 7 | "@susisu/safe-typescript/no-type-assertion": "error", 8 | "@susisu/safe-typescript/no-unsafe-object-enum-method": "error", 9 | "@susisu/safe-typescript/no-unsafe-object-property-check": "error", 10 | "@susisu/safe-typescript/no-unsafe-object-property-overwrite": "error", 11 | }, 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "nodenext", 5 | "moduleResolution": "nodenext", 6 | "esModuleInterop": true, 7 | "verbatimModuleSyntax": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "jsx": "react-jsx", 10 | "lib": ["esnext"], 11 | "strict": true, 12 | "exactOptionalPropertyTypes": true, 13 | "noImplicitOverride": true, 14 | "noImplicitReturns": true, 15 | "noPropertyAccessFromIndexSignature": true, 16 | "skipLibCheck": true 17 | }, 18 | "include": [ 19 | "**/*" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: 8 | - created 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: 16 | - 20 17 | - 22 18 | - 24 19 | steps: 20 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 21 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 22 | with: 23 | version: 10 24 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: pnpm 28 | - run: pnpm install 29 | - run: pnpm format:check 30 | - run: pnpm lint:check 31 | - run: pnpm typecheck 32 | - run: pnpm test 33 | - run: pnpm build 34 | -------------------------------------------------------------------------------- /src/rules/no-object-assign/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import rule from "./index.js"; 3 | 4 | const ruleTester = new RuleTester(); 5 | 6 | ruleTester.run("no-object-assign", rule, { 7 | valid: [`Object.assign`], 8 | invalid: [ 9 | { 10 | code: `Object.assign({}, x)`, 11 | errors: [ 12 | { 13 | messageId: "noCopying", 14 | line: 1, 15 | column: 1, 16 | }, 17 | ], 18 | }, 19 | { 20 | code: `Object.assign(x)`, 21 | errors: [ 22 | { 23 | messageId: "noMutation", 24 | line: 1, 25 | column: 1, 26 | }, 27 | ], 28 | }, 29 | { 30 | code: `Object.assign(x, y)`, 31 | errors: [ 32 | { 33 | messageId: "noMutation", 34 | line: 1, 35 | column: 1, 36 | }, 37 | ], 38 | }, 39 | ], 40 | }); 41 | -------------------------------------------------------------------------------- /src/rules/no-object-assign/README.md: -------------------------------------------------------------------------------- 1 | # `no-object-assign` 2 | 3 | Disallow the use of `Object.assign()`. 4 | 5 | The possible usage of the [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) method will be one of the following cases: 6 | 7 | 1. To copy an object. 8 | 2. To mutate an object by assigning properties. 9 | 10 | For the first case, [the object spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) is always the better choice. 11 | 12 | For the second case, it is possibly unsafe because `Object.assign()` mutates the object without knowing its type. 13 | For example, it may mutate properties that are marked as `readonly`, or internal properties that should not be exposed. 14 | 15 | ## Examples 16 | 17 | 👎 Examples of incorrect code for this rule: 18 | 19 | ```ts 20 | Object.assign({}, x); 21 | Object.assign(x, y); 22 | ``` 23 | 24 | 👍 Examples of correct code for this rule: 25 | 26 | ```ts 27 | Object.assign; 28 | ``` 29 | 30 | ## When Not To Use It 31 | 32 | If you really want to mutate an object, disable this rule. 33 | -------------------------------------------------------------------------------- /src/rules/no-object-assign-mutation/index.ts: -------------------------------------------------------------------------------- 1 | import { AST_NODE_TYPES } from "@typescript-eslint/utils"; 2 | import { createRule, matchObjectMethodCall } from "../utils.js"; 3 | 4 | type Options = []; 5 | 6 | type MessageIds = "noMutation"; 7 | 8 | const rule = createRule({ 9 | name: "no-object-assign-mutation", 10 | meta: { 11 | type: "suggestion", 12 | docs: { 13 | description: "Disallow mutations using Object.assign()", 14 | }, 15 | messages: { 16 | noMutation: "Object.assign() mutates the first argument without knowing its type.", 17 | }, 18 | schema: [], 19 | }, 20 | defaultOptions: [], 21 | create: (context) => ({ 22 | CallExpression: (node) => { 23 | const method = matchObjectMethodCall(node); 24 | if (method !== "assign") { 25 | return; 26 | } 27 | if ( 28 | node.arguments.length >= 2 29 | && node.arguments[0].type !== AST_NODE_TYPES.ObjectExpression 30 | ) { 31 | context.report({ 32 | node, 33 | messageId: "noMutation", 34 | }); 35 | } 36 | }, 37 | }), 38 | }); 39 | 40 | export default rule; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (http://opensource.org/licenses/mit-license.php) 2 | 3 | Copyright (c) 2022-2025 Susisu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | workflow_dispatch: 8 | inputs: 9 | dry-run: 10 | type: boolean 11 | default: false 12 | 13 | jobs: 14 | publish: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | id-token: write 19 | steps: 20 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 21 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 22 | with: 23 | version: 10 24 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 25 | with: 26 | node-version: 24 27 | cache: pnpm 28 | - name: update npm 29 | run: npm install -g npm@latest 30 | - run: pnpm install 31 | - name: publish 32 | run: | 33 | if [ "$DRY_RUN" ]; then 34 | pnpm publish --no-git-checks --dry-run 35 | else 36 | pnpm publish --no-git-checks 37 | fi 38 | env: 39 | DRY_RUN: ${{ (github.event_name == 'workflow_dispatch' && inputs.dry-run) && '1' || '' }} 40 | -------------------------------------------------------------------------------- /src/rules/index.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from "eslint"; 2 | import noObjectAssign from "./no-object-assign/index.js"; 3 | import noObjectAssignMutation from "./no-object-assign-mutation/index.js"; 4 | import noTypeAssertion from "./no-type-assertion/index.js"; 5 | import noUnsafeObjectEnumMethod from "./no-unsafe-object-enum-method/index.js"; 6 | import noUnsafeObjectPropertyCheck from "./no-unsafe-object-property-check/index.js"; 7 | import noUnsafeObjectPropertyOverwrite from "./no-unsafe-object-property-overwrite/index.js"; 8 | 9 | function asRuleModule(rule: unknown): Rule.RuleModule { 10 | // eslint-disable-next-line @susisu/safe-typescript/no-type-assertion 11 | return rule as Rule.RuleModule; 12 | } 13 | 14 | const rules = { 15 | "no-object-assign": asRuleModule(noObjectAssign), 16 | "no-object-assign-mutation": asRuleModule(noObjectAssignMutation), 17 | "no-type-assertion": asRuleModule(noTypeAssertion), 18 | "no-unsafe-object-enum-method": asRuleModule(noUnsafeObjectEnumMethod), 19 | "no-unsafe-object-property-check": asRuleModule(noUnsafeObjectPropertyCheck), 20 | "no-unsafe-object-property-overwrite": asRuleModule(noUnsafeObjectPropertyOverwrite), 21 | }; 22 | 23 | export default rules; 24 | -------------------------------------------------------------------------------- /src/rules/no-object-assign-mutation/README.md: -------------------------------------------------------------------------------- 1 | # `no-object-assign-mutation` 2 | 3 | Disallow mutations using `Object.assign()`. 4 | 5 | One possible use case of [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) is to mutate an object by assigning properties. 6 | 7 | However, mutations using `Object.assign()` is possibly unsafe, because it mutates the object without knowing its type. 8 | For example, it may mutate properties that are marked as `readonly`, or internal properties that are not exposed. 9 | 10 | ## Examples 11 | 12 | 👎 Examples of incorrect code for this rule: 13 | 14 | ```ts 15 | Object.assign(x, y); 16 | ``` 17 | 18 | 👍 Examples of correct code for this rule: 19 | 20 | ```ts 21 | Object.assign({}, x); 22 | Object.assign(x); 23 | ``` 24 | 25 | Although the above code is correct, [the object spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) is always a better choice. 26 | You can enable [`prefer-object-spread`](https://eslint.org/docs/latest/rules/prefer-object-spread) to enforce the object spread syntax. 27 | 28 | ## When Not To Use It 29 | 30 | If you really want to mutate an object, disable this rule. 31 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { config } from "@susisu/eslint-config"; 2 | import eslintPluginPlugin from "eslint-plugin-eslint-plugin"; 3 | import vitestPlugin from "@vitest/eslint-plugin"; 4 | import globals from "globals"; 5 | 6 | export default config( 7 | { 8 | tsconfigRootDir: import.meta.dirname, 9 | }, 10 | { 11 | plugins: { 12 | "eslint-plugin": eslintPluginPlugin, 13 | vitest: vitestPlugin, 14 | }, 15 | }, 16 | { 17 | files: ["src/**/*.ts"], 18 | languageOptions: { 19 | globals: { 20 | ...globals.es2023, 21 | ...globals.node, 22 | }, 23 | }, 24 | rules: { 25 | ...eslintPluginPlugin.configs.recommended.rules, 26 | // For now, `defaultOptions` are handled by `RuleCreator` from @typescript-eslint/utils, not ESLint. 27 | "eslint-plugin/require-meta-default-options": "off", 28 | }, 29 | }, 30 | { 31 | files: ["src/**/*.spec.ts", "src/**/__tests__/**/*.ts"], 32 | languageOptions: { 33 | globals: { 34 | ...vitestPlugin.environments.env.globals, 35 | }, 36 | }, 37 | rules: { 38 | ...vitestPlugin.configs.recommended.rules, 39 | }, 40 | }, 41 | { 42 | files: ["*.js"], 43 | languageOptions: { 44 | globals: { 45 | ...globals.es2023, 46 | ...globals.node, 47 | }, 48 | }, 49 | }, 50 | ); 51 | -------------------------------------------------------------------------------- /src/rules/no-object-assign/index.ts: -------------------------------------------------------------------------------- 1 | import { AST_NODE_TYPES } from "@typescript-eslint/utils"; 2 | import { createRule, matchObjectMethodCall } from "../utils.js"; 3 | 4 | type Options = []; 5 | 6 | type MessageIds = "noCopying" | "noMutation"; 7 | 8 | const rule = createRule({ 9 | name: "no-object-assign", 10 | meta: { 11 | deprecated: true, 12 | type: "suggestion", 13 | docs: { 14 | description: "Disallow the use of Object.assign()", 15 | }, 16 | messages: { 17 | noCopying: 18 | "Using Object.assign() to copy objects is not recommended. Use the object spread syntax instead.", 19 | noMutation: "Object.assign() mutates the first argument without knowing its type.", 20 | }, 21 | schema: [], 22 | }, 23 | defaultOptions: [], 24 | create: (context) => ({ 25 | CallExpression: (node) => { 26 | const method = matchObjectMethodCall(node); 27 | if (method !== "assign") { 28 | return; 29 | } 30 | if (node.arguments.length > 0 && node.arguments[0].type === AST_NODE_TYPES.ObjectExpression) { 31 | context.report({ 32 | node, 33 | messageId: "noCopying", 34 | }); 35 | } else { 36 | context.report({ 37 | node, 38 | messageId: "noMutation", 39 | }); 40 | } 41 | }, 42 | }), 43 | }); 44 | 45 | export default rule; 46 | -------------------------------------------------------------------------------- /src/rules/no-type-assertion/README.md: -------------------------------------------------------------------------------- 1 | # `no-type-assertion` 2 | 3 | Disallow type assertions like `x as T` and `x`. 4 | 5 | Because downcasting is allowed, type assertions are possibly unsafe. 6 | 7 | ```ts 8 | const obj = { foo: 42 } as { foo: number; bar: number }; 9 | obj.bar.toString(); // runtime error! 10 | ``` 11 | 12 | Although upcasting is still safe, you basically don't need to write type assertions for such cases. 13 | 14 | ## Examples 15 | 16 | 👎 Examples of incorrect code for this rule: 17 | 18 | ```ts 19 | const a = { foo: 42 } as { foo: number; bar: number }; 20 | const b = { foo: 42 } as never; 21 | // The following expressions are actually safe, but reported as incorrect. 22 | const c = { foo: 42 } as { foo: number }; 23 | const d = { foo: 42 } as {}; 24 | ``` 25 | 26 | ```ts 27 | const a = <{ foo: number; bar: number }>{ foo: 42 }; 28 | const b = { foo: 42 }; 29 | const c = <{ foo: number }>{ foo: 42 }; 30 | const d = <{}>{ foo: 42 }; 31 | ``` 32 | 33 | 👍 Examples of correct code for this rule: 34 | 35 | ```ts 36 | // `as const` is a special expression, and it's safe 37 | const x = { foo: 42 } as const; 38 | // `as unknown` is safe 39 | const y = { foo: 42 } as unknown; 40 | // `as any` is unsafe, but that is because of `any`, not `as` 41 | const z = { foo: 42 } as any; 42 | ``` 43 | 44 | ```ts 45 | const x = { foo: 42 }; 46 | const y = { foo: 42 }; 47 | const z = { foo: 42 }; 48 | ``` 49 | 50 | ## When Not To Use It 51 | 52 | If you use type assertions to avoid type checking intentionally, you may disable this rule. 53 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-check/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import { dedent } from "@qnighy/dedent"; 3 | import { getFixturesDir } from "../__tests__/utils.js"; 4 | import rule from "./index.js"; 5 | 6 | const ruleTester = new RuleTester({ 7 | languageOptions: { 8 | parserOptions: { 9 | tsconfigRootDir: getFixturesDir(), 10 | project: "./tsconfig.json", 11 | }, 12 | }, 13 | }); 14 | 15 | ruleTester.run("no-unsafe-object-property-check", rule, { 16 | valid: [ 17 | // OK for any and non-primitive values 18 | dedent`\ 19 | declare const x: any; 20 | "a" in x; 21 | `, 22 | dedent`\ 23 | declare const x: object; 24 | "a" in x; 25 | `, 26 | // OK for index signatures 27 | dedent`\ 28 | declare const x: { [key: string]: number }; 29 | "a" in x; 30 | `, 31 | ], 32 | invalid: [ 33 | // Error if it possibly narrows type 34 | { 35 | code: dedent`\ 36 | declare const x: { a: number } | { b: string }; 37 | "a" in x; 38 | `, 39 | errors: [ 40 | { 41 | messageId: "noInOperator", 42 | line: 2, 43 | column: 1, 44 | }, 45 | ], 46 | }, 47 | { 48 | code: dedent`\ 49 | declare const x: { a: number } | { b: string } | { [key: string]: number }; 50 | "a" in x; 51 | `, 52 | errors: [ 53 | { 54 | messageId: "noInOperator", 55 | line: 2, 56 | column: 1, 57 | }, 58 | ], 59 | }, 60 | ], 61 | }); 62 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-check/index.ts: -------------------------------------------------------------------------------- 1 | import { ESLintUtils } from "@typescript-eslint/utils"; 2 | import { createRule, hasIndexSignatures, isAnyType, isNonPrimitiveType } from "../utils.js"; 3 | 4 | type Options = []; 5 | 6 | type MessageIds = "noInOperator"; 7 | 8 | const rule = createRule({ 9 | name: "no-unsafe-object-property-check", 10 | meta: { 11 | type: "suggestion", 12 | docs: { 13 | description: "Disallow possibly unsafe property checks of object", 14 | requiresTypeChecking: true, 15 | }, 16 | messages: { 17 | noInOperator: 18 | "Type narrowing using the `in` operator is possibly unsafe. Consider using discriminated unions.", 19 | }, 20 | schema: [], 21 | }, 22 | defaultOptions: [], 23 | create: (context) => { 24 | const services = ESLintUtils.getParserServices(context); 25 | const checker = services.program.getTypeChecker(); 26 | return { 27 | BinaryExpression: (node) => { 28 | if (node.operator !== "in") { 29 | return; 30 | } 31 | const tsRightType = services.getTypeAtLocation(node.right); 32 | // The check work propertly for the any and non-primitive types. 33 | if (isAnyType(tsRightType) || isNonPrimitiveType(tsRightType)) { 34 | return; 35 | } 36 | // Safe if the argument has index signatures. 37 | if (hasIndexSignatures(checker, tsRightType)) { 38 | return; 39 | } 40 | context.report({ 41 | node, 42 | messageId: "noInOperator", 43 | }); 44 | }, 45 | }; 46 | }, 47 | }); 48 | 49 | export default rule; 50 | -------------------------------------------------------------------------------- /src/rules/no-type-assertion/index.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from "@typescript-eslint/utils"; 2 | import { AST_NODE_TYPES } from "@typescript-eslint/utils"; 3 | import { createRule } from "../utils.js"; 4 | 5 | type Options = []; 6 | 7 | type MessageIds = "noTypeAssertion"; 8 | 9 | const rule = createRule({ 10 | name: "no-type-assertion", 11 | meta: { 12 | type: "suggestion", 13 | docs: { 14 | description: "Disallow type assertions", 15 | }, 16 | messages: { 17 | noTypeAssertion: "A type assertion `x as T` possibly involves unsafe downcasting.", 18 | }, 19 | schema: [], 20 | }, 21 | defaultOptions: [], 22 | create: (context) => { 23 | const check = (node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion): void => { 24 | const annotation = node.typeAnnotation; 25 | // `as const` is a safe expression 26 | if ( 27 | annotation.type === AST_NODE_TYPES.TSTypeReference 28 | && annotation.typeName.type === AST_NODE_TYPES.Identifier 29 | && annotation.typeName.name === "const" 30 | ) { 31 | return; 32 | } 33 | // `as unknown` is safe 34 | // `as any` is unsafe, but that is because of `any`, not `as` 35 | if ( 36 | annotation.type === AST_NODE_TYPES.TSUnknownKeyword 37 | || annotation.type === AST_NODE_TYPES.TSAnyKeyword 38 | ) { 39 | return; 40 | } 41 | context.report({ 42 | node, 43 | messageId: "noTypeAssertion", 44 | }); 45 | }; 46 | return { 47 | TSAsExpression: (node) => { 48 | check(node); 49 | }, 50 | TSTypeAssertion: (node) => { 51 | check(node); 52 | }, 53 | }; 54 | }, 55 | }); 56 | 57 | export default rule; 58 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-check/README.md: -------------------------------------------------------------------------------- 1 | # `no-unsafe-object-property-check` 2 | 3 | Disallow possibly unsafe property checks of object. 4 | 5 | [The `in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) can narrow types, but it is not always safe. 6 | 7 | For example, suppose we have a union type `Data` and a function that reads it. 8 | 9 | ```ts 10 | type Data = { foo: number } | { bar: string }; 11 | 12 | function myFunc(data: Data): void { 13 | if ("foo" in data) { 14 | const foo: number = data.foo; 15 | // ... 16 | } else { 17 | const bar: string = data.bar; 18 | // ... 19 | } 20 | } 21 | ``` 22 | 23 | Since the `in` operator narrows the type of `data`, we can access `data.foo` and `data.bar`. 24 | 25 | However, you can call `myFunc()` like below, as the argument matches the `{ bar: string }` case. 26 | 27 | ```ts 28 | const myData = { foo: "xxx", bar: "yyy" }; 29 | myFunc(myData); 30 | ``` 31 | 32 | It's clear that this does not work correctly, because `myData` is treated as `{ foo: number }` in the function. 33 | 34 | To remedy this, you should define `Data` as a [discriminated (tagged) union](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions), and use the tag to narrow the type of `data`. 35 | 36 | ```ts 37 | type Data = { type: "foo"; value: number } | { type: "bar"; value: string }; 38 | 39 | function myFunc(data: Data): void { 40 | if (data.type === "foo") { 41 | const foo: number = data.value; 42 | // ... 43 | } else { 44 | const bar: string = data.value; 45 | // ... 46 | } 47 | } 48 | ``` 49 | 50 | ## Examples 51 | 52 | 👎 Examples of incorrect code for this rule: 53 | 54 | ```ts 55 | declare const counts: { foo: number } | { bar: string }; 56 | "foo" in counts; 57 | ``` 58 | 59 | 👍 Examples of correct code for this rule: 60 | 61 | ```ts 62 | declare const anyValue: any; 63 | "foo" in anyValue; 64 | 65 | declare const nonPrimitiveValue: object; 66 | "foo" in nonPrimitiveValue; 67 | 68 | declare const dictionary: { [key: string]: number }; 69 | "foo" in dictionary; 70 | ``` 71 | 72 | ## When Not To Use It 73 | 74 | If you don't care about this kind of type safety, disable this rule. 75 | -------------------------------------------------------------------------------- /src/rules/no-type-assertion/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import rule from "./index.js"; 3 | 4 | const ruleTester = new RuleTester(); 5 | 6 | ruleTester.run("no-type-assertion", rule, { 7 | valid: [ 8 | `const x = { foo: 42 } as const`, 9 | `const x = { foo: 42 } as unknown`, 10 | `const x = { foo: 42 } as any`, 11 | `const x = { foo: 42 }`, 12 | `const x = { foo: 42 }`, 13 | `const x = { foo: 42 }`, 14 | ], 15 | invalid: [ 16 | { 17 | code: `const x = { foo: 42 } as { foo: number; bar: number }`, 18 | errors: [ 19 | { 20 | messageId: "noTypeAssertion", 21 | line: 1, 22 | column: 11, 23 | }, 24 | ], 25 | }, 26 | { 27 | code: `const x = { foo: 42 } as never`, 28 | errors: [ 29 | { 30 | messageId: "noTypeAssertion", 31 | line: 1, 32 | column: 11, 33 | }, 34 | ], 35 | }, 36 | { 37 | code: `const x = { foo: 42 } as { foo: number }`, 38 | errors: [ 39 | { 40 | messageId: "noTypeAssertion", 41 | line: 1, 42 | column: 11, 43 | }, 44 | ], 45 | }, 46 | { 47 | code: `const x = { foo: 42 } as {}`, 48 | errors: [ 49 | { 50 | messageId: "noTypeAssertion", 51 | line: 1, 52 | column: 11, 53 | }, 54 | ], 55 | }, 56 | { 57 | code: `const x = <{ foo: number; bar: number }>{ foo: 42 }`, 58 | errors: [ 59 | { 60 | messageId: "noTypeAssertion", 61 | line: 1, 62 | column: 11, 63 | }, 64 | ], 65 | }, 66 | { 67 | code: `const x = { foo: 42 }`, 68 | errors: [ 69 | { 70 | messageId: "noTypeAssertion", 71 | line: 1, 72 | column: 11, 73 | }, 74 | ], 75 | }, 76 | { 77 | code: `const x = <{ foo: number }>{ foo: 42 }`, 78 | errors: [ 79 | { 80 | messageId: "noTypeAssertion", 81 | line: 1, 82 | column: 11, 83 | }, 84 | ], 85 | }, 86 | { 87 | code: `const x = <{}>{ foo: 42 }`, 88 | errors: [ 89 | { 90 | messageId: "noTypeAssertion", 91 | line: 1, 92 | column: 11, 93 | }, 94 | ], 95 | }, 96 | ], 97 | }); 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@susisu/eslint-plugin-safe-typescript", 3 | "version": "0.10.1", 4 | "description": "ESLint plugin that makes your TypeScript code safer", 5 | "repository": "https://github.com/susisu/eslint-plugin-safe-typescript.git", 6 | "author": "Susisu ", 7 | "license": "MIT", 8 | "type": "module", 9 | "sideEffects": false, 10 | "files": [ 11 | "lib", 12 | "src", 13 | "!src/**/*.{test,spec}.{ts,tsx}", 14 | "!src/**/__tests__" 15 | ], 16 | "main": "lib/index.js", 17 | "types": "lib/index.d.ts", 18 | "exports": { 19 | ".": { 20 | "import": { 21 | "types": "./lib/index.d.ts", 22 | "default": "./lib/index.js" 23 | } 24 | } 25 | }, 26 | "scripts": { 27 | "format": "prettier --write '*.js' src", 28 | "format:check": "prettier --check '*.js' src", 29 | "lint": "eslint --fix '*.js' src", 30 | "lint:check": "eslint '*.js' src", 31 | "test": "vitest run --coverage", 32 | "test:dev": "vitest dev --coverage.enabled --coverage.reporter=text", 33 | "typecheck": "tsc -p tsconfig.json --noEmit", 34 | "build": "tsc --build tsconfig.build.json", 35 | "clean": "run-s clean:tsc clean:rm", 36 | "clean:tsc": "tsc --build tsconfig.build.json --clean", 37 | "clean:rm": "rimraf lib", 38 | "prepublishOnly": "run-s clean format:check lint:check typecheck test build" 39 | }, 40 | "dependencies": { 41 | "@typescript-eslint/utils": "^8.49.0" 42 | }, 43 | "peerDependencies": { 44 | "@typescript-eslint/parser": "^8.0.0", 45 | "eslint": "^9.10.0", 46 | "typescript": "*", 47 | "typescript-eslint": "^8.0.0" 48 | }, 49 | "peerDependenciesMeta": { 50 | "@typescript-eslint/parser": { 51 | "optional": true 52 | }, 53 | "typescript-eslint": { 54 | "optional": true 55 | } 56 | }, 57 | "devDependencies": { 58 | "@qnighy/dedent": "^0.1.1", 59 | "@susisu/eslint-config": "^0.0.101", 60 | "@types/node": "^25.0.1", 61 | "@typescript-eslint/parser": "^8.49.0", 62 | "@typescript-eslint/rule-tester": "^8.49.0", 63 | "@vitest/coverage-v8": "^4.0.15", 64 | "@vitest/eslint-plugin": "^1.5.2", 65 | "eslint": "^9.39.1", 66 | "eslint-plugin-eslint-plugin": "^7.2.0", 67 | "globals": "^16.5.0", 68 | "npm-run-all2": "^8.0.4", 69 | "prettier": "^3.7.4", 70 | "rimraf": "^6.1.2", 71 | "typescript": "~5.9.3", 72 | "vitest": "^4.0.15" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | 3 | ### https://raw.github.com/github/gitignore/ce5da10a3a43c4dd8bd9572eda17c0a37ee0eac1/Node.gitignore 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .pnpm-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | .env.local 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | .cache 110 | 111 | # Docusaurus cache and generated files 112 | .docusaurus 113 | 114 | # Serverless directories 115 | .serverless/ 116 | 117 | # FuseBox cache 118 | .fusebox/ 119 | 120 | # DynamoDB Local files 121 | .dynamodb/ 122 | 123 | # TernJS port file 124 | .tern-port 125 | 126 | # Stores VSCode versions used for testing VSCode extensions 127 | .vscode-test 128 | 129 | # yarn v2 130 | .yarn/cache 131 | .yarn/unplugged 132 | .yarn/build-state.yml 133 | .yarn/install-state.gz 134 | .pnp.* 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/rules/utils.ts: -------------------------------------------------------------------------------- 1 | import type { TSESTree } from "@typescript-eslint/utils"; 2 | import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils"; 3 | import ts from "typescript"; 4 | 5 | export type PluginDocs = { 6 | requiresTypeChecking?: boolean | undefined; 7 | }; 8 | 9 | // eslint-disable-next-line new-cap 10 | export const createRule = ESLintUtils.RuleCreator( 11 | (ruleName) => 12 | `https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/${ruleName}/README.md`, 13 | ); 14 | 15 | /** 16 | * Checks if the value of the expression possibly contains properties that do not appear in its type. 17 | */ 18 | export function possiblyContainsUnknownProperties(node: TSESTree.Expression): boolean { 19 | if (node.type === AST_NODE_TYPES.ObjectExpression) { 20 | return node.properties.some((prop) => { 21 | if (prop.type === AST_NODE_TYPES.SpreadElement) { 22 | return possiblyContainsUnknownProperties(prop.argument); 23 | } else { 24 | return false; 25 | } 26 | }); 27 | } else if (node.type === AST_NODE_TYPES.ConditionalExpression) { 28 | return ( 29 | possiblyContainsUnknownProperties(node.consequent) 30 | || possiblyContainsUnknownProperties(node.alternate) 31 | ); 32 | } else { 33 | return true; 34 | } 35 | } 36 | 37 | /** 38 | * Matches Object method call and returns the method's name. 39 | */ 40 | export function matchObjectMethodCall(node: TSESTree.CallExpression): string | undefined { 41 | const { callee } = node; 42 | // We do not consider the following cases: 43 | // - const m = Object.keys; m(x) 44 | // - const o = Object; o.keys(x) 45 | // - const k = "keys"; Object[k](x) 46 | if (callee.type !== AST_NODE_TYPES.MemberExpression) { 47 | return undefined; 48 | } 49 | const { object, property } = callee; 50 | if (object.type !== AST_NODE_TYPES.Identifier || object.name !== "Object") { 51 | return undefined; 52 | } 53 | if (property.type === AST_NODE_TYPES.Identifier) { 54 | if (callee.computed) { 55 | return undefined; 56 | } 57 | const method = property.name; 58 | return method; 59 | } else if (property.type === AST_NODE_TYPES.Literal) { 60 | const method = property.value; 61 | if (typeof method !== "string") { 62 | return undefined; 63 | } 64 | return method; 65 | } else { 66 | return undefined; 67 | } 68 | } 69 | 70 | /** 71 | * Checks if the type is the any type. 72 | */ 73 | export function isAnyType(type: ts.Type): boolean { 74 | return (type.flags & ts.TypeFlags.Any) !== 0; 75 | } 76 | 77 | /** 78 | * Checks if the type is the non-primitive type i.e. `object`. 79 | */ 80 | export function isNonPrimitiveType(type: ts.Type): boolean { 81 | return (type.flags & ts.TypeFlags.NonPrimitive) !== 0; 82 | } 83 | 84 | /** 85 | * Checks if the type has index signatures. 86 | */ 87 | export function hasIndexSignatures(checker: ts.TypeChecker, type: ts.Type): boolean { 88 | return checker.getIndexInfosOfType(type).length > 0; 89 | } 90 | 91 | /** 92 | * Checks if the type has only index signatures. 93 | */ 94 | export function hasOnlyIndexSignatures(checker: ts.TypeChecker, type: ts.Type): boolean { 95 | return hasIndexSignatures(checker, type) && checker.getPropertiesOfType(type).length === 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-enum-method/index.ts: -------------------------------------------------------------------------------- 1 | import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils"; 2 | import { 3 | createRule, 4 | hasIndexSignatures, 5 | isAnyType, 6 | isNonPrimitiveType, 7 | matchObjectMethodCall, 8 | possiblyContainsUnknownProperties, 9 | } from "../utils.js"; 10 | 11 | type Options = [ 12 | { 13 | allowIndexSignatures?: boolean; 14 | }, 15 | ]; 16 | 17 | type MessageIds = "noEnumMethod"; 18 | 19 | const rule = createRule({ 20 | name: "no-unsafe-object-enum-method", 21 | meta: { 22 | type: "suggestion", 23 | docs: { 24 | description: "Disallow possibly unsafe property enumeration methods of Object", 25 | requiresTypeChecking: true, 26 | }, 27 | messages: { 28 | noEnumMethod: 29 | "Object.{{method}}() possibly enumerates unknown properties. Consider enumerating only known properties of the object.", 30 | }, 31 | schema: [ 32 | { 33 | type: "object", 34 | additionalProperties: false, 35 | properties: { 36 | allowIndexSignatures: { 37 | type: "boolean", 38 | description: 39 | "When set to `true`, allows `Object.keys()`, `Object.values()`, and `Object.entries()` if the object's type has index signatures e.g. `{ [key: string]: number }`", 40 | }, 41 | }, 42 | }, 43 | ], 44 | }, 45 | defaultOptions: [ 46 | { 47 | allowIndexSignatures: true, 48 | }, 49 | ], 50 | create: (context, [options]) => { 51 | const services = ESLintUtils.getParserServices(context); 52 | const checker = services.program.getTypeChecker(); 53 | return { 54 | CallExpression: (node) => { 55 | const method = matchObjectMethodCall(node); 56 | if (!isObjectEnumMethod(method)) { 57 | return; 58 | } 59 | const arg = node.arguments.length > 0 ? node.arguments[0] : undefined; 60 | if (!arg) { 61 | return; 62 | } 63 | // We do not consider the following cases: 64 | // - Object.keys(...[{ a: 0, b: 1 }]) 65 | // - declare const x: { [key: string]: number }; Object.keys(...[x]) 66 | if (arg.type !== AST_NODE_TYPES.SpreadElement) { 67 | // Safe if only contains known properties. 68 | if (!possiblyContainsUnknownProperties(arg)) { 69 | return; 70 | } 71 | const tsArgType = services.getTypeAtLocation(arg); 72 | // The enumeration methods work propertly for the any and non-primitive types. 73 | if (isAnyType(tsArgType) || isNonPrimitiveType(tsArgType)) { 74 | return; 75 | } 76 | // Mostly safe if the argument has index signatures. 77 | if (options.allowIndexSignatures && hasIndexSignatures(checker, tsArgType)) { 78 | return; 79 | } 80 | } 81 | context.report({ 82 | node, 83 | messageId: "noEnumMethod", 84 | data: { method }, 85 | }); 86 | }, 87 | }; 88 | }, 89 | }); 90 | 91 | const objectEnumMethods = ["keys", "values", "entries"] as const; 92 | type ObjectEnumMethod = (typeof objectEnumMethods)[number]; 93 | 94 | function isObjectEnumMethod(value: unknown): value is ObjectEnumMethod { 95 | return objectEnumMethods.some((method) => method === value); 96 | } 97 | 98 | export default rule; 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @susisu/eslint-plugin-safe-typescript 2 | 3 | [![CI](https://github.com/susisu/eslint-plugin-safe-typescript/workflows/CI/badge.svg)](https://github.com/susisu/eslint-plugin-safe-typescript/actions?query=workflow%3ACI) 4 | 5 | An [ESLint](https://eslint.org) plugin that makes your TypeScript code safer 6 | 7 | ## Installation 8 | 9 | This plugin requires [TypeScript](https://www.typescriptlang.org/) and [typescript-eslint](https://typescript-eslint.io/). 10 | If you haven't installed them in your project, follow the guide on typescript-eslint's [Getting Started](https://typescript-eslint.io/getting-started) page. 11 | 12 | Once you're ready, install this plugin: 13 | 14 | ``` shell 15 | # npm 16 | npm i --save-dev @susisu/eslint-plugin-safe-typescript 17 | # yarn 18 | yarn add -D @susisu/eslint-plugin-safe-typescript 19 | # pnpm 20 | pnpm add -D @susisu/eslint-plugin-safe-typescript 21 | ``` 22 | 23 | ## Configuration 24 | 25 | 1. Enable typescript-eslint parser 26 | 2. Add `@susisu/eslint-plugin-safe-typescript` to plugins 27 | 3. (Optional) Add `parserOptions.project` if you enable rules that use type information. 28 | 29 | ``` js 30 | // eslint.config.js 31 | 32 | import tsEslint from "typescript-eslint"; 33 | import safeTsPlugin from "@susisu/eslint-plugin-safe-typescript"; 34 | 35 | export default [ 36 | { 37 | languageOptions: { 38 | parser: tsEslint.parser, // (1) 39 | parserOptions: { 40 | project: true, // (3) 41 | }, 42 | }, 43 | plugins: { 44 | "@susisu/safe-typescript": safeTsPlugin, // (2) 45 | }, 46 | rules: { 47 | "@susisu/safe-typescript/no-object-assign": "error", 48 | }, 49 | }, 50 | ]; 51 | ``` 52 | 53 | ## Recommended configuration 54 | 55 | This plugin also provides a configuration set for the recommended rules (see [Rules](#rules) for which rules are recommended). 56 | 57 | Since some rules in the recommended configuration require type information, `parserOptions.project` must be set in your config. 58 | 59 | ``` js 60 | // eslint.config.js 61 | 62 | export default [ 63 | safeTsPlugin.configs.recommended, 64 | // or extend in `rules` 65 | // { 66 | // rules: { 67 | // ...safeTsPlugin.configs.recommended.rules, 68 | // }, 69 | // }, 70 | ]; 71 | ``` 72 | 73 | ## Rules 74 | 75 | ✅ = recommended, 🔧 = fixable, 💡 = has suggestions, 💭 = requires type information 76 | 77 | | Name | Description | ✅ | 🔧 | 💭 | 78 | | --- | --- | --- | --- | --- | 79 | | [`no-object-assign-mutation`](https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/no-object-assign-mutation/README.md) | Disallow mutations using `Object.assign()`. | ✅ | | | 80 | | [`no-type-assertion`](https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/no-type-assertion/README.md) | Disallow type assertions like `x as T`. | ✅ | | | 81 | | [`no-unsafe-object-enum-method`](https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/no-unsafe-object-enum-method/README.md) | Disallow possibly unsafe property enumeration methods of `Object`. | ✅ | | 💭 | 82 | | [`no-unsafe-object-property-check`](https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/no-unsafe-object-property-check/README.md) | Disallow possibly unsafe property checks of object. | ✅ | | 💭 | 83 | | [`no-unsafe-object-property-overwrite`](https://github.com/susisu/eslint-plugin-safe-typescript/blob/main/src/rules/no-unsafe-object-property-overwrite/README.md) | Disallow possibly unsafe overwrites of object properties. | ✅ | 💡 | 💭 | 84 | 85 | ## Contributing 86 | 87 | Issues and PRs are welcome! 88 | Feel free to open issues if you have any problems or ideas. 89 | 90 | ## License 91 | 92 | [MIT License](http://opensource.org/licenses/mit-license.php) 93 | 94 | ## Author 95 | 96 | Susisu ([GitHub](https://github.com/susisu), [Twitter](https://twitter.com/susisu2413)) 97 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-enum-method/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import { dedent } from "@qnighy/dedent"; 3 | import { getFixturesDir } from "../__tests__/utils.js"; 4 | import rule from "./index.js"; 5 | 6 | const ruleTester = new RuleTester({ 7 | languageOptions: { 8 | parserOptions: { 9 | tsconfigRootDir: getFixturesDir(), 10 | project: "./tsconfig.json", 11 | }, 12 | }, 13 | }); 14 | 15 | ruleTester.run("no-unsafe-object-enum-method", rule, { 16 | valid: [ 17 | // OK if all the argument's properties are known 18 | `Object.keys({ a: 0, b: 1, ...{ c: 2, d: 3, ...{ e: 4, f: 5 } } })`, 19 | // OK if the argument's type is any or non-primitive 20 | dedent`\ 21 | declare const x: any; 22 | Object.keys(x); 23 | `, 24 | dedent`\ 25 | declare const x: object; 26 | Object.keys(x); 27 | `, 28 | // OK if the argument's type has index signatures 29 | dedent`\ 30 | declare const x: { [key: string]: number }; 31 | Object.keys(x); 32 | `, 33 | dedent`\ 34 | declare const x: { [key: number]: number }; 35 | Object.keys(x); 36 | `, 37 | dedent`\ 38 | declare const x: { [key: symbol]: number }; 39 | Object.keys(x); 40 | `, 41 | ], 42 | invalid: [ 43 | // Error if the argument possibly has unknown properties 44 | { 45 | code: dedent`\ 46 | declare const x: { foo: number; bar: number }; 47 | Object.keys({ a: 0, b: 1, ...{ ...x, c: 1, d: 2 } }); 48 | `, 49 | errors: [ 50 | { 51 | messageId: "noEnumMethod", 52 | data: { method: "keys" }, 53 | line: 2, 54 | column: 1, 55 | }, 56 | ], 57 | }, 58 | { 59 | // In this case, the argument's type does not have index signatures, 60 | // and thus the argument has possibly unknown properties. 61 | code: dedent`\ 62 | declare const x: { [key: string]: number }; 63 | Object.keys({ a: 0, b: 1, ...{ ...x, c: 1, d: 2 } }); 64 | `, 65 | errors: [ 66 | { 67 | messageId: "noEnumMethod", 68 | data: { method: "keys" }, 69 | line: 2, 70 | column: 1, 71 | }, 72 | ], 73 | }, 74 | // Error if the argument's type does not have index signatures 75 | { 76 | code: dedent`\ 77 | declare const x: { foo: number; bar: number }; 78 | Object.keys(x); 79 | `, 80 | errors: [ 81 | { 82 | messageId: "noEnumMethod", 83 | data: { method: "keys" }, 84 | line: 2, 85 | column: 1, 86 | }, 87 | ], 88 | }, 89 | { 90 | code: dedent`\ 91 | declare const x: { foo: number; bar: number }; 92 | Object.values(x); 93 | `, 94 | errors: [ 95 | { 96 | messageId: "noEnumMethod", 97 | data: { method: "values" }, 98 | line: 2, 99 | column: 1, 100 | }, 101 | ], 102 | }, 103 | { 104 | code: dedent`\ 105 | declare const x: { foo: number; bar: number }; 106 | Object.entries(x); 107 | `, 108 | errors: [ 109 | { 110 | messageId: "noEnumMethod", 111 | data: { method: "entries" }, 112 | line: 2, 113 | column: 1, 114 | }, 115 | ], 116 | }, 117 | // Error if the argument's type has index signatures but it is disallowed by the option 118 | { 119 | code: dedent`\ 120 | declare const x: { [key: string]: number }; 121 | Object.keys(x); 122 | `, 123 | options: [{ allowIndexSignatures: false }], 124 | errors: [ 125 | { 126 | messageId: "noEnumMethod", 127 | data: { method: "keys" }, 128 | line: 2, 129 | column: 1, 130 | }, 131 | ], 132 | }, 133 | ], 134 | }); 135 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.10.1 (2025-09-20) 2 | 3 | - Upgrade dependencies 4 | 5 | ## 0.10.0 (2025-06-07) 6 | 7 | - Upgrade dependencies 8 | - Add suggestion fixer for `no-unsafe-object-property-overwrite` 9 | - Add `no-object-assign-mutation` rule, which replaces `no-object-assign` 10 | - Deprecate `no-object-assign` rule 11 | - Use `no-object-assign-mutation` and [`prefer-object-spread`](https://eslint.org/docs/latest/rules/prefer-object-spread) instead 12 | - **BREAKING** The package is now Pure ESM 13 | - **BREAKING** Update recommended config 14 | - Replace `no-object-assign` with `no-object-assign-mutation` 15 | - **BREAKING** Remove [`eslint-define-config`](https://github.com/eslint-types/eslint-define-config) support 16 | 17 | ## 0.9.4 (2025-04-29) 18 | 19 | - Upgrade dependencies 20 | 21 | ## 0.9.3 (2025-02-09) 22 | 23 | - Upgrade dependencies 24 | 25 | ## 0.9.2 (2024-11-23) 26 | 27 | - Upgrade dependencies 28 | 29 | ## 0.9.1 (2024-10-26) 30 | 31 | - Upgrade dependencies 32 | - Add name for the recommended config 33 | - Fix ordering in the `exports` field of `package.json` 34 | 35 | ## 0.9.0 (2024-08-04) 36 | 37 | - **BREAKING** The minimum supported version of typescript-eslint is now `8.0.0` 38 | - Upgrade typescript-eslint to v8 39 | 40 | ## 0.8.3 (2024-07-08) 41 | 42 | - Upgrade dependencies 43 | 44 | ## 0.8.2 (2024-06-20) 45 | 46 | - Fix CJS interop issue 47 | 48 | ## 0.8.1 (2024-06-19) 49 | 50 | - `no-unsafe-object-property-overwrite`: Fix false positive for conditionals 51 | - Upgrade dependencies 52 | 53 | ## 0.8.0 (2024-03-16) 54 | 55 | - Upgrade typescript-eslint to 7.2.x to support TypeScript 5.4 56 | - Change how the plugin and its type are exported to improve compatibility with `@types/eslint` 57 | - Shortly, the plugin type is now compatible with `ESLint.Plugin` 58 | - Move [`eslint-define-config`](https://github.com/eslint-types/eslint-define-config) support to a submodule `@susisu/eslint-plugin-safe-typescript/define-config-support` 59 | 60 | ## 0.7.0 (2024-02-15) 61 | 62 | - Upgrade typescript-eslint to v7 63 | - `no-type-assertion`: Add support for angle bracket style type assertions like `x` 64 | 65 | ## 0.6.1 (2024-02-11) 66 | 67 | - Refactor how the module augmentation for eslint-define-config is exported 68 | 69 | ## 0.6.0 (2024-02-11) 70 | 71 | - Provide rule option types for [`eslint-define-config`](https://github.com/eslint-types/eslint-define-config) 72 | 73 | ## 0.5.3 (2024-02-10) 74 | 75 | - Upgrade dependencies 76 | - Add docs for flat config 77 | 78 | ## 0.5.2 (2023-12-16) 79 | 80 | - Add default export for ESM 81 | 82 | ## 0.5.1 (2023-12-03) 83 | 84 | - Fix `types` were missing in the `exports` field 85 | 86 | ## 0.5.0 (2023-12-03) 87 | 88 | - **BREAKING** Drop support for Node.js 16 89 | - Type declarations are now included again 90 | - It would never be used in the previous config format (`.eslintrc`), but will be useful in the new flat config format. 91 | - The package is now ESM/CJS dual 92 | - Upgrade dependencies 93 | 94 | ## 0.4.1 (2023-07-16) 95 | 96 | - Fix peerDependencies was not updated 97 | 98 | ## 0.4.0 (2023-07-16) 99 | 100 | - Upgrade typescript to 5.1 101 | - **BREAKING** Upgrade typescript-eslint to 6.0 102 | - **BREAKING** Type declarations are no longer included in the distribution 103 | 104 | ## 0.3.5 (2023-03-17) 105 | 106 | - Upgrade dependencies 107 | - Upgrade TypeScript to 5.0 108 | 109 | ## 0.3.4 (2022-12-04) 110 | 111 | - Upgrade TypeScript to 4.9 112 | 113 | ## 0.3.3 (2022-10-02) 114 | 115 | - Upgrade dependencies 116 | - Update message of `no-type-assertion` 117 | 118 | ## 0.3.2 (2022-09-02) 119 | 120 | - Upgrade dependencies 121 | 122 | ## 0.3.1 (2022-08-11) 123 | 124 | - Upgrade dependencies 125 | 126 | ## 0.3.0 (2022-07-13) 127 | 128 | - Add `no-type-assertion` rule 129 | - **BREAKING** Add `no-type-assertion` rule to the recommended config 130 | 131 | ## 0.2.2 (2022-05-06) 132 | 133 | - Upgrade dependencies 134 | 135 | ## 0.2.1 (2022-03-21) 136 | 137 | - Fix `no-object-assign` was not exported 138 | 139 | ## 0.2.0 (2022-03-21) 140 | 141 | - Add `allowIndexSignatures` option to `no-unsafe-object-property-overwrite` rule 142 | - Add `no-object-assign` rule 143 | - **BREAKING** Add `no-object-assign` rule to the recommended config 144 | 145 | ## 0.1.0 (2022-03-06) 146 | 147 | - First release 148 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-enum-method/README.md: -------------------------------------------------------------------------------- 1 | # `no-unsafe-object-enum-method` 2 | 3 | Disallow possibly unsafe property enumeration methods of `Object`. 4 | 5 | [`Object.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys), [`Object.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values), and [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) enumerates an object's own properties, but it may contain properties that do not appear in the object's type. 6 | 7 | Suppose we have a type `Counts` and a function that reads it. 8 | 9 | ```ts 10 | type Counts = { 11 | foo: number; 12 | bar: number; 13 | baz: number; 14 | }; 15 | 16 | function myFunc(counts: Counts): void { 17 | const keys: string[] = Object.keys(counts); 18 | const values: number[] = Object.values(counts); 19 | const entries: [string, number][] = Object.entries(counts); 20 | // ... 21 | } 22 | ``` 23 | 24 | You can call `myFunc()` with an object that has additional properties. 25 | 26 | ```ts 27 | const countsWithMetadata = { 28 | foo: 1, 29 | bar: 2, 30 | baz: 3, 31 | metadata: "xxx", 32 | }; 33 | myFunc(countsWithMetadata); 34 | ``` 35 | 36 | Then the following problems will occur: 37 | 38 | - `keys` contains `"metadata"`, but people usually don't pay attention to it. 39 | - `values` contains `"xxx"`, but it is typed as `number[]`. 40 | - `entries` contains `["metadata", "xxx"]`, but it is typed as `[string, number][]`. 41 | 42 | To avoid these problems, enumerate only known properties of the object like below: 43 | 44 | ```ts 45 | const countKeys = ["foo", "bar", "baz"] as const; 46 | type CountKey = (typeof countKeys)[number]; 47 | // The same type as before 48 | type Counts = { [K in CountKey]: number }; 49 | 50 | function myFunc(counts: Counts): void { 51 | // Use only known keys of Counts i.e. countKeys 52 | const keys = countKeys; 53 | const values = countKeys.map((key) => counts[key]); 54 | const entrie = countKeys.map((key) => [key, counts[key]]); 55 | // ... 56 | } 57 | ``` 58 | 59 | ## Examples 60 | 61 | 👎 Examples of incorrect code for this rule: 62 | 63 | ```ts 64 | declare const counts: { foo: number; bar: number; baz: number }; 65 | Object.keys(counts); 66 | Object.values(counts); 67 | Object.entries(counts); 68 | ``` 69 | 70 | 👍 Examples of correct code for this rule: 71 | 72 | ```ts 73 | declare const anyValue: any; 74 | Object.keys(anyValue); 75 | Object.values(anyValue); 76 | Object.entries(anyValue); 77 | 78 | declare const nonPrimitiveValue: object; 79 | Object.keys(nonPrimitiveValue); 80 | Object.values(nonPrimitiveValue); 81 | Object.entries(nonPrimitiveValue); 82 | 83 | declare const dictionary: { [key: string]: number }; 84 | Object.keys(dictionary); 85 | Object.values(dictionary); 86 | Object.entries(dictionary); 87 | ``` 88 | 89 | ## Options 90 | 91 | ### `allowIndexSignatures` 92 | 93 | default = `true` 94 | 95 | When set to `true`, allows `Object.keys()`, `Object.values()`, and `Object.entries()` if the object's type has [index signatures](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures) e.g. `{ [key: string]: number }`. 96 | 97 | If the type has index signatures, enumerating properties is safe in most cases. 98 | However, there are still some cases where it becomes unsafe. 99 | For example: 100 | 101 | ```ts 102 | const countsWithMetadata = { 103 | foo: 1, 104 | bar: 2, 105 | baz: 3, 106 | metadata: "xxx", 107 | }; 108 | const counts: { foo: number; bar: number; baz: number } = countsWithMetadata; 109 | const dictionary: { [key: string]: number } = counts; 110 | 111 | // keys contains "metadata" that dictionary["metadata"] !== "number" 112 | const keys: string[] = Object.keys(dictionary); 113 | // values contains "xxx" 114 | const values: number[] = Object.values(dictionary); 115 | // entries contains ["metadata", "xxx"] 116 | const entries: [string, number][] = Object.entries(dictionary); 117 | ``` 118 | 119 | 👎 Examples of incorrect code for the `{ "allowIndexSignatures": false }` option: 120 | 121 | ```ts 122 | declare const dictionary: { [key: string]: number }; 123 | Object.keys(dictionary); 124 | Object.values(dictionary); 125 | Object.entries(dictionary); 126 | ``` 127 | 128 | ## When Not To Use It 129 | 130 | If you don't care about this kind of type safety, disable this rule. 131 | 132 | If it is ensured that the argument object does not contain unknown properties i.e. all the properties appear in its type, you can safely disable this rule for that specific case. 133 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-overwrite/README.md: -------------------------------------------------------------------------------- 1 | # `no-unsafe-object-property-overwrite` 2 | 3 | Disallow possibly unsafe overwrites of object properties. 4 | 5 | [The object spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) enumerates and copies an object's own properties, but it may contain properties that do not appear in the object's type. 6 | 7 | For example, suppose we have a type `Data`, and `DataWithMetadata` that extends `Data`. 8 | 9 | ```ts 10 | type Data = { 11 | foo: number; 12 | bar: number; 13 | }; 14 | 15 | type DataWithMetadata = Data & { 16 | metadata: string; 17 | }; 18 | 19 | function withMetadata(data: Data, metadata: string): DataWithMetadata { 20 | return { metadata, ...data }; 21 | } 22 | 23 | const dataA = { foo: 0, bar: 1 }; 24 | // metaA = "xxx" 25 | const metaA: string = withMetadata(dataA, "xxx").metadata; 26 | ``` 27 | 28 | This seems pretty good. 29 | However, the definition of `withMetadata()` is actually unsafe, because it allows overwriting `metadata` with the properties of `data`. 30 | 31 | ```ts 32 | const dataB = { foo: 0, bar: 1, metadata: 666 }; 33 | // metaB = 666 34 | const metaB: string = withMetadata(dataB, "xxx").metadata; 35 | ``` 36 | 37 | To fix the function, use the spread syntax at the beginning of the object literal. 38 | 39 | ```ts 40 | function withMetadata(data: Data, metadata: string): DataWithMetadata { 41 | // Write `...data` first so that it will not overwrite `metadata`. 42 | return { ...data, metadata }; 43 | } 44 | ``` 45 | 46 | The same applies to [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). 47 | 48 | ```ts 49 | function withMetadata(data: Data, metadata: string): DataWithMetadata { 50 | // Don't do `Object.assign({ metadata }, data)` 51 | return Object.assign({ ...data }, { metadata }); 52 | } 53 | ``` 54 | 55 | ## Examples 56 | 57 | 👎 Examples of incorrect code for this rule: 58 | 59 | ```ts 60 | declare const x: { foo: number; bar: number }; 61 | declare const cond: boolean; 62 | 63 | ({ a: 0, ...x, b: 1 }); 64 | ({ a: 0, b: 1, ...x }); 65 | ({ a: 0, b: 1, ...{ ...x, c: 1, d: 2 } }); 66 | ({ a: 0, b: 1, ...(cond ? { c: 1, d: 2 } : x) }); 67 | 68 | Object.assign({ a: 0 }, x, { b: 1 }); 69 | Object.assign({ a: 0, b: 1 }, x); 70 | Object.assign({ a: 0, b: 1 }, { ...x, c: 1, d: 2 }); 71 | Object.assign({ a: 0, b: 1 }, cond ? { c: 1, d: 2 } : x); 72 | ``` 73 | 74 | 👍 Examples of correct code for this rule: 75 | 76 | ```ts 77 | declare const x: { foo: number; bar: number }; 78 | declare const cond: boolean; 79 | 80 | ({ ...x }); 81 | ({ ...x, a: 0, b: 1 }); 82 | ({ a: 0, b: 1, ...{ c: 2, d: 3, ...{ e: 4, f: 5 } } }); 83 | ({ a: 0, b: 1, ...(cond ? { c: 2, d: 3 } : { e: 4, f: 5 }) }); 84 | 85 | Object.assign(x); 86 | Object.assign(x, { a: 0, b: 1 }); 87 | Object.assign({ a: 0, b: 1 }, { c: 2, d: 3, ...{ e: 4, f: 5 } }); 88 | Object.assign({ a: 0, b: 1 }, cond ? { c: 2, d: 3 } : { e: 4, f: 5 }); 89 | 90 | declare const anyValue: any; 91 | 92 | ({ a: 0, b: 1, ...anyValue }); 93 | Object.assign({ a: 0, b: 1 }, anyValue); 94 | 95 | declare const countsA: { [key: string]: number }; 96 | declare const countsB: { [key: string]: number }; 97 | 98 | ({ ...countsA, ...countsB }); 99 | ``` 100 | 101 | ## Options 102 | 103 | ### `allowIndexSignatures` 104 | 105 | default = `true` 106 | 107 | When set to `true`, allows object spreads in any position if the object's type has only [index signatures](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures) e.g. `{ [key: string]: number }`. 108 | 109 | If the type has index signatures, object spreads are safe in most cases, while there are still some cases where it becomes unsafe. 110 | For example: 111 | 112 | ```ts 113 | const countsA: { [key: string]: number } = { 114 | foo: 1, 115 | bar: 2, 116 | }; 117 | 118 | const x = { 119 | baz: 3, 120 | metadata: "xxx", 121 | }; 122 | const y: { baz: number } = x; 123 | const countsB: { [key: string]: number } = y; 124 | 125 | // counts["metadata"] = "xxx" but its type is number 126 | const counts: { [key: string]: number } = { ...countsA, ...countsB }; 127 | ``` 128 | 129 | 👎 Examples of incorrect code for the `{ "allowIndexSignatures": false }` option: 130 | 131 | ```ts 132 | declare const countsA: { [key: string]: number }; 133 | declare const countsB: { [key: string]: number }; 134 | 135 | ({ ...countsA, ...countsB }); 136 | ``` 137 | 138 | ## When Not To Use It 139 | 140 | If you don't care about this kind of type safety, disable this rule. 141 | 142 | If it is ensured that the object does not contain unknown properties i.e. all the properties appear in its type, you can safely disable this rule for that specific case. 143 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-overwrite/index.ts: -------------------------------------------------------------------------------- 1 | import { AST_NODE_TYPES, AST_TOKEN_TYPES, ESLintUtils } from "@typescript-eslint/utils"; 2 | import { 3 | createRule, 4 | hasOnlyIndexSignatures, 5 | isAnyType, 6 | matchObjectMethodCall, 7 | possiblyContainsUnknownProperties, 8 | } from "../utils.js"; 9 | 10 | type Options = [ 11 | { 12 | allowIndexSignatures?: boolean; 13 | }, 14 | ]; 15 | 16 | type MessageIds = "noSpreadSyntax" | "noObjectAssign" | "suggestMoveSpreadSyntax"; 17 | 18 | const rule = createRule({ 19 | name: "no-unsafe-object-property-overwrite", 20 | meta: { 21 | type: "suggestion", 22 | docs: { 23 | description: "Disallow possibly unsafe overwrites of object properties", 24 | requiresTypeChecking: true, 25 | }, 26 | hasSuggestions: true, 27 | messages: { 28 | noSpreadSyntax: 29 | "The spread syntax possibly overwrites properties with unknown values. Consider moving this to the beginning of the object literal.", 30 | noObjectAssign: 31 | "Object.assign() possibly overwrites properties with unknown values. Consider moving this to the beginning of the argument list.", 32 | suggestMoveSpreadSyntax: "Move the spread syntax to the front.", 33 | }, 34 | schema: [ 35 | { 36 | type: "object", 37 | additionalProperties: false, 38 | properties: { 39 | allowIndexSignatures: { 40 | type: "boolean", 41 | description: 42 | "When set to `true`, allows object spreads in any position if the object's type has only index signatures e.g. `{ [key: string]: number }`.", 43 | }, 44 | }, 45 | }, 46 | ], 47 | }, 48 | defaultOptions: [ 49 | { 50 | allowIndexSignatures: true, 51 | }, 52 | ], 53 | create: (context, [options]) => { 54 | const services = ESLintUtils.getParserServices(context); 55 | const checker = services.program.getTypeChecker(); 56 | return { 57 | ObjectExpression: (node) => { 58 | const tsNodeType = services.getTypeAtLocation(node); 59 | // Ignore any. 60 | if (isAnyType(tsNodeType)) { 61 | return; 62 | } 63 | // Mostly safe if the object has only index signatures. 64 | if (options.allowIndexSignatures && hasOnlyIndexSignatures(checker, tsNodeType)) { 65 | return; 66 | } 67 | // Nothing to check. 68 | if (node.properties.length <= 1) { 69 | return; 70 | } 71 | const firstNonSpreadProp = node.properties.find( 72 | (prop) => prop.type !== AST_NODE_TYPES.SpreadElement, 73 | ); 74 | for (let i = 1; i < node.properties.length; i++) { 75 | const prop = node.properties[i]; 76 | if (prop.type !== AST_NODE_TYPES.SpreadElement) { 77 | continue; 78 | } 79 | // Safe if the spread contains only known properties. 80 | if (!possiblyContainsUnknownProperties(prop.argument)) { 81 | continue; 82 | } 83 | context.report({ 84 | node: prop, 85 | messageId: "noSpreadSyntax", 86 | suggest: 87 | firstNonSpreadProp ? 88 | [ 89 | { 90 | messageId: "suggestMoveSpreadSyntax", 91 | *fix(fixer) { 92 | const nextToken = context.sourceCode.getTokenAfter(prop); 93 | const hasTrailingComma = 94 | !!nextToken 95 | && nextToken.type === AST_TOKEN_TYPES.Punctuator 96 | && nextToken.value === ","; 97 | yield fixer.insertTextBefore( 98 | firstNonSpreadProp, 99 | context.sourceCode.getText(prop) + ",", 100 | ); 101 | yield fixer.remove(prop); 102 | if (hasTrailingComma) { 103 | yield fixer.removeRange(nextToken.range); 104 | } 105 | }, 106 | }, 107 | ] 108 | : [], 109 | }); 110 | } 111 | }, 112 | CallExpression: (node) => { 113 | const method = matchObjectMethodCall(node); 114 | if (method !== "assign") { 115 | return; 116 | } 117 | // Nothing to check. 118 | if (node.arguments.length <= 1) { 119 | return; 120 | } 121 | for (let i = 1; i < node.arguments.length; i++) { 122 | const arg = node.arguments[i]; 123 | // Safe if the argument contains only known properties. 124 | // We do not consider Object.assign(...[{ a: 0, b: 1 }]) 125 | if (arg.type !== AST_NODE_TYPES.SpreadElement) { 126 | const tsArgType = services.getTypeAtLocation(arg); 127 | if (isAnyType(tsArgType)) { 128 | continue; 129 | } 130 | if (!possiblyContainsUnknownProperties(arg)) { 131 | continue; 132 | } 133 | } 134 | context.report({ 135 | node: arg, 136 | messageId: "noObjectAssign", 137 | }); 138 | } 139 | }, 140 | }; 141 | }, 142 | }); 143 | 144 | export default rule; 145 | -------------------------------------------------------------------------------- /src/rules/no-unsafe-object-property-overwrite/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { RuleTester } from "@typescript-eslint/rule-tester"; 2 | import { dedent } from "@qnighy/dedent"; 3 | import { getFixturesDir } from "../__tests__/utils.js"; 4 | import rule from "./index.js"; 5 | 6 | const ruleTester = new RuleTester({ 7 | languageOptions: { 8 | parserOptions: { 9 | tsconfigRootDir: getFixturesDir(), 10 | project: "./tsconfig.json", 11 | }, 12 | }, 13 | }); 14 | 15 | ruleTester.run("no-unsafe-object-property-overwrite", rule, { 16 | valid: [ 17 | // OK at the beginning of the object literal / Object.assign() 18 | dedent`\ 19 | declare const x: { foo: number; bar: number }; 20 | ({ ...x }); 21 | `, 22 | dedent`\ 23 | declare const x: { foo: number; bar: number }; 24 | Object.assign(x); 25 | `, 26 | dedent`\ 27 | declare const x: { foo: number; bar: number }; 28 | ({ ...x, a: 0, b: 1 }); 29 | `, 30 | dedent`\ 31 | declare const x: { foo: number; bar: number }; 32 | Object.assign(x, { a: 0, b: 1 }); 33 | `, 34 | // OK if all properties are known 35 | `({ a: 0, b: 1, ...{ c: 2, d: 3, ...{ e: 4, f: 5 } } })`, 36 | `Object.assign({ a: 0, b: 1 }, { c: 2, d: 3, ...{ e: 4, f: 5 } })`, 37 | dedent`\ 38 | declare const cond: boolean; 39 | ({ a: 0, b: 1, ...(cond ? { c: 2, d: 3 } : { e: 4, f: 5 }) }); 40 | `, 41 | dedent`\ 42 | declare const cond: boolean; 43 | Object.assign({ a: 0, b: 1 }, cond ? { c: 2, d: 3 } : { e: 4, f: 5 }); 44 | `, 45 | // OK for any 46 | dedent`\ 47 | declare const x: any; 48 | ({ a: 0, b: 1, ...x }); 49 | `, 50 | dedent`\ 51 | declare const x: any; 52 | Object.assign({ a: 0, b: 1 }, x); 53 | `, 54 | // OK if the object has only index signatures 55 | dedent`\ 56 | declare const x: { [key: string]: number }; 57 | declare const y: { [key: string]: string }; 58 | ({ ...x, ...y }); 59 | `, 60 | ], 61 | invalid: [ 62 | // Error at the middle or end of the object literal / Object.assign() 63 | { 64 | code: dedent`\ 65 | declare const x: { foo: number; bar: number }; 66 | ({ a: 0, ...x, b: 1 }); 67 | `, 68 | errors: [ 69 | { 70 | messageId: "noSpreadSyntax", 71 | line: 2, 72 | column: 10, 73 | suggestions: [ 74 | { 75 | messageId: "suggestMoveSpreadSyntax", 76 | output: dedent`\ 77 | declare const x: { foo: number; bar: number }; 78 | ({ ...x,a: 0, b: 1 }); 79 | `, 80 | }, 81 | ], 82 | }, 83 | ], 84 | }, 85 | { 86 | code: dedent`\ 87 | declare const x: { foo: number; bar: number }; 88 | Object.assign({ a: 0 }, x, { b: 1 }) 89 | `, 90 | errors: [ 91 | { 92 | messageId: "noObjectAssign", 93 | line: 2, 94 | column: 25, 95 | }, 96 | ], 97 | }, 98 | { 99 | code: dedent`\ 100 | declare const x: { foo: number; bar: number }; 101 | ({ a: 0, b: 1, ...x }); 102 | `, 103 | errors: [ 104 | { 105 | messageId: "noSpreadSyntax", 106 | line: 2, 107 | column: 16, 108 | suggestions: [ 109 | { 110 | messageId: "suggestMoveSpreadSyntax", 111 | output: dedent`\ 112 | declare const x: { foo: number; bar: number }; 113 | ({ ...x,a: 0, b: 1, }); 114 | `, 115 | }, 116 | ], 117 | }, 118 | ], 119 | }, 120 | { 121 | code: dedent`\ 122 | declare const x: { foo: number; bar: number }; 123 | Object.assign({ a: 0, b: 1 }, x); 124 | `, 125 | errors: [ 126 | { 127 | messageId: "noObjectAssign", 128 | line: 2, 129 | column: 31, 130 | }, 131 | ], 132 | }, 133 | // Error if possibly contains unknown properties 134 | { 135 | code: dedent`\ 136 | declare const x: { foo: number; bar: number }; 137 | ({ a: 0, b: 1, ...{ ...x, c: 1, d: 2 } }); 138 | `, 139 | errors: [ 140 | { 141 | messageId: "noSpreadSyntax", 142 | line: 2, 143 | column: 16, 144 | suggestions: [ 145 | { 146 | messageId: "suggestMoveSpreadSyntax", 147 | output: dedent`\ 148 | declare const x: { foo: number; bar: number }; 149 | ({ ...{ ...x, c: 1, d: 2 },a: 0, b: 1, }); 150 | `, 151 | }, 152 | ], 153 | }, 154 | ], 155 | }, 156 | { 157 | code: dedent`\ 158 | declare const x: { foo: number; bar: number }; 159 | Object.assign({ a: 0, b: 1 }, { ...x, c: 1, d: 2 }); 160 | `, 161 | errors: [ 162 | { 163 | messageId: "noObjectAssign", 164 | line: 2, 165 | column: 31, 166 | }, 167 | ], 168 | }, 169 | { 170 | code: dedent`\ 171 | declare const x: { foo: number; bar: number }; 172 | declare const cond: boolean; 173 | ({ a: 0, b: 1, ...(cond ? { c: 1, d: 2 } : x) }); 174 | `, 175 | errors: [ 176 | { 177 | messageId: "noSpreadSyntax", 178 | line: 3, 179 | column: 16, 180 | suggestions: [ 181 | { 182 | messageId: "suggestMoveSpreadSyntax", 183 | output: dedent`\ 184 | declare const x: { foo: number; bar: number }; 185 | declare const cond: boolean; 186 | ({ ...(cond ? { c: 1, d: 2 } : x),a: 0, b: 1, }); 187 | `, 188 | }, 189 | ], 190 | }, 191 | ], 192 | }, 193 | { 194 | code: dedent`\ 195 | declare const x: { foo: number; bar: number }; 196 | declare const cond: boolean; 197 | Object.assign({ a: 0, b: 1 }, cond ? { c: 1, d: 2 } : x); 198 | `, 199 | errors: [ 200 | { 201 | messageId: "noObjectAssign", 202 | line: 3, 203 | column: 31, 204 | }, 205 | ], 206 | }, 207 | // Multiple errors 208 | { 209 | code: dedent`\ 210 | declare const x: { foo: number; bar: number }; 211 | declare const y: { foo: number; bar: number }; 212 | declare const z: { foo: number; bar: number }; 213 | ({ ...x, a: 0, ...y, b: 1, ...z }); 214 | `, 215 | errors: [ 216 | { 217 | messageId: "noSpreadSyntax", 218 | line: 4, 219 | column: 16, 220 | suggestions: [ 221 | { 222 | messageId: "suggestMoveSpreadSyntax", 223 | output: dedent`\ 224 | declare const x: { foo: number; bar: number }; 225 | declare const y: { foo: number; bar: number }; 226 | declare const z: { foo: number; bar: number }; 227 | ({ ...x, ...y,a: 0, b: 1, ...z }); 228 | `, 229 | }, 230 | ], 231 | }, 232 | { 233 | messageId: "noSpreadSyntax", 234 | line: 4, 235 | column: 28, 236 | suggestions: [ 237 | { 238 | messageId: "suggestMoveSpreadSyntax", 239 | output: dedent`\ 240 | declare const x: { foo: number; bar: number }; 241 | declare const y: { foo: number; bar: number }; 242 | declare const z: { foo: number; bar: number }; 243 | ({ ...x, ...z,a: 0, ...y, b: 1, }); 244 | `, 245 | }, 246 | ], 247 | }, 248 | ], 249 | }, 250 | { 251 | code: dedent`\ 252 | declare const x: { foo: number; bar: number }; 253 | declare const y: { foo: number; bar: number }; 254 | declare const z: { foo: number; bar: number }; 255 | Object.assign(x, { a: 0 }, y, { b: 1 }, z); 256 | `, 257 | errors: [ 258 | { 259 | messageId: "noObjectAssign", 260 | line: 4, 261 | column: 28, 262 | }, 263 | { 264 | messageId: "noObjectAssign", 265 | line: 4, 266 | column: 41, 267 | }, 268 | ], 269 | }, 270 | // Error if the object has only index signatures but it is disallowed by the option 271 | { 272 | code: dedent`\ 273 | declare const x: { [key: string]: number }; 274 | declare const y: { [key: string]: string }; 275 | ({ ...x, ...y }); 276 | `, 277 | options: [{ allowIndexSignatures: false }], 278 | errors: [ 279 | { 280 | messageId: "noSpreadSyntax", 281 | line: 3, 282 | column: 10, 283 | }, 284 | ], 285 | }, 286 | ], 287 | }); 288 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@typescript-eslint/utils': 12 | specifier: ^8.49.0 13 | version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 14 | typescript-eslint: 15 | specifier: ^8.0.0 16 | version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 17 | devDependencies: 18 | '@qnighy/dedent': 19 | specifier: ^0.1.1 20 | version: 0.1.1 21 | '@susisu/eslint-config': 22 | specifier: ^0.0.101 23 | version: 0.0.101(eslint@9.39.1)(typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3) 24 | '@types/node': 25 | specifier: ^25.0.1 26 | version: 25.0.1 27 | '@typescript-eslint/parser': 28 | specifier: ^8.49.0 29 | version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 30 | '@typescript-eslint/rule-tester': 31 | specifier: ^8.49.0 32 | version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 33 | '@vitest/coverage-v8': 34 | specifier: ^4.0.15 35 | version: 4.0.15(vitest@4.0.15(@types/node@25.0.1)(yaml@2.5.1)) 36 | '@vitest/eslint-plugin': 37 | specifier: ^1.5.2 38 | version: 1.5.2(eslint@9.39.1)(typescript@5.9.3)(vitest@4.0.15(@types/node@25.0.1)(yaml@2.5.1)) 39 | eslint: 40 | specifier: ^9.39.1 41 | version: 9.39.1 42 | eslint-plugin-eslint-plugin: 43 | specifier: ^7.2.0 44 | version: 7.2.0(eslint@9.39.1) 45 | globals: 46 | specifier: ^16.5.0 47 | version: 16.5.0 48 | npm-run-all2: 49 | specifier: ^8.0.4 50 | version: 8.0.4 51 | prettier: 52 | specifier: ^3.7.4 53 | version: 3.7.4 54 | rimraf: 55 | specifier: ^6.1.2 56 | version: 6.1.2 57 | typescript: 58 | specifier: ~5.9.3 59 | version: 5.9.3 60 | vitest: 61 | specifier: ^4.0.15 62 | version: 4.0.15(@types/node@25.0.1)(yaml@2.5.1) 63 | 64 | packages: 65 | 66 | '@babel/helper-string-parser@7.27.1': 67 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 68 | engines: {node: '>=6.9.0'} 69 | 70 | '@babel/helper-validator-identifier@7.28.5': 71 | resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 72 | engines: {node: '>=6.9.0'} 73 | 74 | '@babel/parser@7.28.5': 75 | resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} 76 | engines: {node: '>=6.0.0'} 77 | hasBin: true 78 | 79 | '@babel/types@7.28.5': 80 | resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 81 | engines: {node: '>=6.9.0'} 82 | 83 | '@bcoe/v8-coverage@1.0.2': 84 | resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} 85 | engines: {node: '>=18'} 86 | 87 | '@esbuild/aix-ppc64@0.25.12': 88 | resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} 89 | engines: {node: '>=18'} 90 | cpu: [ppc64] 91 | os: [aix] 92 | 93 | '@esbuild/android-arm64@0.25.12': 94 | resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} 95 | engines: {node: '>=18'} 96 | cpu: [arm64] 97 | os: [android] 98 | 99 | '@esbuild/android-arm@0.25.12': 100 | resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} 101 | engines: {node: '>=18'} 102 | cpu: [arm] 103 | os: [android] 104 | 105 | '@esbuild/android-x64@0.25.12': 106 | resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} 107 | engines: {node: '>=18'} 108 | cpu: [x64] 109 | os: [android] 110 | 111 | '@esbuild/darwin-arm64@0.25.12': 112 | resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} 113 | engines: {node: '>=18'} 114 | cpu: [arm64] 115 | os: [darwin] 116 | 117 | '@esbuild/darwin-x64@0.25.12': 118 | resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} 119 | engines: {node: '>=18'} 120 | cpu: [x64] 121 | os: [darwin] 122 | 123 | '@esbuild/freebsd-arm64@0.25.12': 124 | resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} 125 | engines: {node: '>=18'} 126 | cpu: [arm64] 127 | os: [freebsd] 128 | 129 | '@esbuild/freebsd-x64@0.25.12': 130 | resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} 131 | engines: {node: '>=18'} 132 | cpu: [x64] 133 | os: [freebsd] 134 | 135 | '@esbuild/linux-arm64@0.25.12': 136 | resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} 137 | engines: {node: '>=18'} 138 | cpu: [arm64] 139 | os: [linux] 140 | 141 | '@esbuild/linux-arm@0.25.12': 142 | resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} 143 | engines: {node: '>=18'} 144 | cpu: [arm] 145 | os: [linux] 146 | 147 | '@esbuild/linux-ia32@0.25.12': 148 | resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} 149 | engines: {node: '>=18'} 150 | cpu: [ia32] 151 | os: [linux] 152 | 153 | '@esbuild/linux-loong64@0.25.12': 154 | resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} 155 | engines: {node: '>=18'} 156 | cpu: [loong64] 157 | os: [linux] 158 | 159 | '@esbuild/linux-mips64el@0.25.12': 160 | resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} 161 | engines: {node: '>=18'} 162 | cpu: [mips64el] 163 | os: [linux] 164 | 165 | '@esbuild/linux-ppc64@0.25.12': 166 | resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} 167 | engines: {node: '>=18'} 168 | cpu: [ppc64] 169 | os: [linux] 170 | 171 | '@esbuild/linux-riscv64@0.25.12': 172 | resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} 173 | engines: {node: '>=18'} 174 | cpu: [riscv64] 175 | os: [linux] 176 | 177 | '@esbuild/linux-s390x@0.25.12': 178 | resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} 179 | engines: {node: '>=18'} 180 | cpu: [s390x] 181 | os: [linux] 182 | 183 | '@esbuild/linux-x64@0.25.12': 184 | resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} 185 | engines: {node: '>=18'} 186 | cpu: [x64] 187 | os: [linux] 188 | 189 | '@esbuild/netbsd-arm64@0.25.12': 190 | resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} 191 | engines: {node: '>=18'} 192 | cpu: [arm64] 193 | os: [netbsd] 194 | 195 | '@esbuild/netbsd-x64@0.25.12': 196 | resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} 197 | engines: {node: '>=18'} 198 | cpu: [x64] 199 | os: [netbsd] 200 | 201 | '@esbuild/openbsd-arm64@0.25.12': 202 | resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} 203 | engines: {node: '>=18'} 204 | cpu: [arm64] 205 | os: [openbsd] 206 | 207 | '@esbuild/openbsd-x64@0.25.12': 208 | resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} 209 | engines: {node: '>=18'} 210 | cpu: [x64] 211 | os: [openbsd] 212 | 213 | '@esbuild/openharmony-arm64@0.25.12': 214 | resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} 215 | engines: {node: '>=18'} 216 | cpu: [arm64] 217 | os: [openharmony] 218 | 219 | '@esbuild/sunos-x64@0.25.12': 220 | resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} 221 | engines: {node: '>=18'} 222 | cpu: [x64] 223 | os: [sunos] 224 | 225 | '@esbuild/win32-arm64@0.25.12': 226 | resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} 227 | engines: {node: '>=18'} 228 | cpu: [arm64] 229 | os: [win32] 230 | 231 | '@esbuild/win32-ia32@0.25.12': 232 | resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} 233 | engines: {node: '>=18'} 234 | cpu: [ia32] 235 | os: [win32] 236 | 237 | '@esbuild/win32-x64@0.25.12': 238 | resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} 239 | engines: {node: '>=18'} 240 | cpu: [x64] 241 | os: [win32] 242 | 243 | '@eslint-community/eslint-utils@4.9.0': 244 | resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} 245 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 246 | peerDependencies: 247 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 248 | 249 | '@eslint-community/regexpp@4.12.2': 250 | resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} 251 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 252 | 253 | '@eslint/config-array@0.21.1': 254 | resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} 255 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 256 | 257 | '@eslint/config-helpers@0.4.2': 258 | resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} 259 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 260 | 261 | '@eslint/core@0.17.0': 262 | resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} 263 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 264 | 265 | '@eslint/eslintrc@3.3.3': 266 | resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} 267 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 268 | 269 | '@eslint/js@9.39.1': 270 | resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} 271 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 272 | 273 | '@eslint/object-schema@2.1.7': 274 | resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} 275 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 276 | 277 | '@eslint/plugin-kit@0.4.1': 278 | resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} 279 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 280 | 281 | '@humanfs/core@0.19.1': 282 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 283 | engines: {node: '>=18.18.0'} 284 | 285 | '@humanfs/node@0.16.7': 286 | resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 287 | engines: {node: '>=18.18.0'} 288 | 289 | '@humanwhocodes/module-importer@1.0.1': 290 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 291 | engines: {node: '>=12.22'} 292 | 293 | '@humanwhocodes/retry@0.4.3': 294 | resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 295 | engines: {node: '>=18.18'} 296 | 297 | '@isaacs/balanced-match@4.0.1': 298 | resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} 299 | engines: {node: 20 || >=22} 300 | 301 | '@isaacs/brace-expansion@5.0.0': 302 | resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} 303 | engines: {node: 20 || >=22} 304 | 305 | '@jridgewell/resolve-uri@3.1.2': 306 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 307 | engines: {node: '>=6.0.0'} 308 | 309 | '@jridgewell/sourcemap-codec@1.5.5': 310 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 311 | 312 | '@jridgewell/trace-mapping@0.3.31': 313 | resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 314 | 315 | '@qnighy/dedent@0.1.1': 316 | resolution: {integrity: sha512-1TogcOCQlxSL4fS2n3jUtwoTHIlXJN3cw3FmQKSdmVpbVr03gKeXTEskPxqqMT24xkid59bEGv4ZB6oUQqq2fw==} 317 | 318 | '@rollup/rollup-android-arm-eabi@4.53.3': 319 | resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} 320 | cpu: [arm] 321 | os: [android] 322 | 323 | '@rollup/rollup-android-arm64@4.53.3': 324 | resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} 325 | cpu: [arm64] 326 | os: [android] 327 | 328 | '@rollup/rollup-darwin-arm64@4.53.3': 329 | resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} 330 | cpu: [arm64] 331 | os: [darwin] 332 | 333 | '@rollup/rollup-darwin-x64@4.53.3': 334 | resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} 335 | cpu: [x64] 336 | os: [darwin] 337 | 338 | '@rollup/rollup-freebsd-arm64@4.53.3': 339 | resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} 340 | cpu: [arm64] 341 | os: [freebsd] 342 | 343 | '@rollup/rollup-freebsd-x64@4.53.3': 344 | resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} 345 | cpu: [x64] 346 | os: [freebsd] 347 | 348 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 349 | resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} 350 | cpu: [arm] 351 | os: [linux] 352 | 353 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 354 | resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} 355 | cpu: [arm] 356 | os: [linux] 357 | 358 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 359 | resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} 360 | cpu: [arm64] 361 | os: [linux] 362 | 363 | '@rollup/rollup-linux-arm64-musl@4.53.3': 364 | resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} 365 | cpu: [arm64] 366 | os: [linux] 367 | 368 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 369 | resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} 370 | cpu: [loong64] 371 | os: [linux] 372 | 373 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 374 | resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} 375 | cpu: [ppc64] 376 | os: [linux] 377 | 378 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 379 | resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} 380 | cpu: [riscv64] 381 | os: [linux] 382 | 383 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 384 | resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} 385 | cpu: [riscv64] 386 | os: [linux] 387 | 388 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 389 | resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} 390 | cpu: [s390x] 391 | os: [linux] 392 | 393 | '@rollup/rollup-linux-x64-gnu@4.53.3': 394 | resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} 395 | cpu: [x64] 396 | os: [linux] 397 | 398 | '@rollup/rollup-linux-x64-musl@4.53.3': 399 | resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} 400 | cpu: [x64] 401 | os: [linux] 402 | 403 | '@rollup/rollup-openharmony-arm64@4.53.3': 404 | resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} 405 | cpu: [arm64] 406 | os: [openharmony] 407 | 408 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 409 | resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} 410 | cpu: [arm64] 411 | os: [win32] 412 | 413 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 414 | resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} 415 | cpu: [ia32] 416 | os: [win32] 417 | 418 | '@rollup/rollup-win32-x64-gnu@4.53.3': 419 | resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} 420 | cpu: [x64] 421 | os: [win32] 422 | 423 | '@rollup/rollup-win32-x64-msvc@4.53.3': 424 | resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} 425 | cpu: [x64] 426 | os: [win32] 427 | 428 | '@standard-schema/spec@1.0.0': 429 | resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 430 | 431 | '@stylistic/eslint-plugin@5.6.1': 432 | resolution: {integrity: sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==} 433 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 434 | peerDependencies: 435 | eslint: '>=9.0.0' 436 | 437 | '@susisu/eslint-config@0.0.101': 438 | resolution: {integrity: sha512-VjpYk4d/x8nHURG/0z1MyPoHsjGJVzPd9zpmFaIIZGoBcr+W8pyEko7ol76L/PNUtnQVparkfZ8xUC/CDWuUoQ==} 439 | peerDependencies: 440 | eslint: ^9.35.0 441 | 442 | '@susisu/eslint-plugin-safe-typescript@0.10.1': 443 | resolution: {integrity: sha512-AGAALm1QVNlT+7GnfUmRsMrlDqUl1nZtu2/XbLVYCho10Q4/qyCFMQ9l4/N4AxbXcyAHvcchy2Vo2/LZj3AznQ==} 444 | peerDependencies: 445 | '@typescript-eslint/parser': ^8.0.0 446 | eslint: ^9.10.0 447 | typescript: '*' 448 | typescript-eslint: ^8.0.0 449 | peerDependenciesMeta: 450 | '@typescript-eslint/parser': 451 | optional: true 452 | typescript-eslint: 453 | optional: true 454 | 455 | '@types/chai@5.2.3': 456 | resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} 457 | 458 | '@types/deep-eql@4.0.2': 459 | resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} 460 | 461 | '@types/estree@1.0.8': 462 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 463 | 464 | '@types/json-schema@7.0.15': 465 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 466 | 467 | '@types/node@25.0.1': 468 | resolution: {integrity: sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==} 469 | 470 | '@typescript-eslint/eslint-plugin@8.49.0': 471 | resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} 472 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 473 | peerDependencies: 474 | '@typescript-eslint/parser': ^8.49.0 475 | eslint: ^8.57.0 || ^9.0.0 476 | typescript: '>=4.8.4 <6.0.0' 477 | 478 | '@typescript-eslint/parser@8.49.0': 479 | resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} 480 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 481 | peerDependencies: 482 | eslint: ^8.57.0 || ^9.0.0 483 | typescript: '>=4.8.4 <6.0.0' 484 | 485 | '@typescript-eslint/project-service@8.49.0': 486 | resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} 487 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 488 | peerDependencies: 489 | typescript: '>=4.8.4 <6.0.0' 490 | 491 | '@typescript-eslint/rule-tester@8.49.0': 492 | resolution: {integrity: sha512-2wu6JO/ND/QCg/zMNE04xCx1a5O6wOeklztpuqme8DPtjH1sejOIeHqyVW5I8pKx28D/Y0v+/N+B3I5NoO2I5g==} 493 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 494 | peerDependencies: 495 | eslint: ^8.57.0 || ^9.0.0 496 | 497 | '@typescript-eslint/scope-manager@8.49.0': 498 | resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} 499 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 500 | 501 | '@typescript-eslint/tsconfig-utils@8.49.0': 502 | resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} 503 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 504 | peerDependencies: 505 | typescript: '>=4.8.4 <6.0.0' 506 | 507 | '@typescript-eslint/type-utils@8.49.0': 508 | resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} 509 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 510 | peerDependencies: 511 | eslint: ^8.57.0 || ^9.0.0 512 | typescript: '>=4.8.4 <6.0.0' 513 | 514 | '@typescript-eslint/types@8.49.0': 515 | resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} 516 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 517 | 518 | '@typescript-eslint/typescript-estree@8.49.0': 519 | resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} 520 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 521 | peerDependencies: 522 | typescript: '>=4.8.4 <6.0.0' 523 | 524 | '@typescript-eslint/utils@8.49.0': 525 | resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} 526 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 527 | peerDependencies: 528 | eslint: ^8.57.0 || ^9.0.0 529 | typescript: '>=4.8.4 <6.0.0' 530 | 531 | '@typescript-eslint/visitor-keys@8.49.0': 532 | resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} 533 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 534 | 535 | '@vitest/coverage-v8@4.0.15': 536 | resolution: {integrity: sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==} 537 | peerDependencies: 538 | '@vitest/browser': 4.0.15 539 | vitest: 4.0.15 540 | peerDependenciesMeta: 541 | '@vitest/browser': 542 | optional: true 543 | 544 | '@vitest/eslint-plugin@1.5.2': 545 | resolution: {integrity: sha512-2t1F2iecXB/b1Ox4U137lhD3chihEE3dRVtu3qMD35tc6UqUjg1VGRJoS1AkFKwpT8zv8OQInzPQO06hrRkeqw==} 546 | engines: {node: '>=18'} 547 | peerDependencies: 548 | eslint: '>=8.57.0' 549 | typescript: '>=5.0.0' 550 | vitest: '*' 551 | peerDependenciesMeta: 552 | typescript: 553 | optional: true 554 | vitest: 555 | optional: true 556 | 557 | '@vitest/expect@4.0.15': 558 | resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} 559 | 560 | '@vitest/mocker@4.0.15': 561 | resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} 562 | peerDependencies: 563 | msw: ^2.4.9 564 | vite: ^6.0.0 || ^7.0.0-0 565 | peerDependenciesMeta: 566 | msw: 567 | optional: true 568 | vite: 569 | optional: true 570 | 571 | '@vitest/pretty-format@4.0.15': 572 | resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} 573 | 574 | '@vitest/runner@4.0.15': 575 | resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} 576 | 577 | '@vitest/snapshot@4.0.15': 578 | resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} 579 | 580 | '@vitest/spy@4.0.15': 581 | resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} 582 | 583 | '@vitest/utils@4.0.15': 584 | resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} 585 | 586 | acorn-jsx@5.3.2: 587 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 588 | peerDependencies: 589 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 590 | 591 | acorn@8.15.0: 592 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 593 | engines: {node: '>=0.4.0'} 594 | hasBin: true 595 | 596 | ajv@6.12.6: 597 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 598 | 599 | ansi-styles@4.3.0: 600 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 601 | engines: {node: '>=8'} 602 | 603 | ansi-styles@6.2.3: 604 | resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} 605 | engines: {node: '>=12'} 606 | 607 | argparse@2.0.1: 608 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 609 | 610 | assertion-error@2.0.1: 611 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 612 | engines: {node: '>=12'} 613 | 614 | ast-v8-to-istanbul@0.3.8: 615 | resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} 616 | 617 | balanced-match@1.0.2: 618 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 619 | 620 | brace-expansion@1.1.12: 621 | resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 622 | 623 | brace-expansion@2.0.2: 624 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 625 | 626 | callsites@3.1.0: 627 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 628 | engines: {node: '>=6'} 629 | 630 | chai@6.2.1: 631 | resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} 632 | engines: {node: '>=18'} 633 | 634 | chalk@4.1.2: 635 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 636 | engines: {node: '>=10'} 637 | 638 | color-convert@2.0.1: 639 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 640 | engines: {node: '>=7.0.0'} 641 | 642 | color-name@1.1.4: 643 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 644 | 645 | concat-map@0.0.1: 646 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 647 | 648 | cross-spawn@7.0.6: 649 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 650 | engines: {node: '>= 8'} 651 | 652 | debug@4.4.3: 653 | resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 654 | engines: {node: '>=6.0'} 655 | peerDependencies: 656 | supports-color: '*' 657 | peerDependenciesMeta: 658 | supports-color: 659 | optional: true 660 | 661 | deep-is@0.1.4: 662 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 663 | 664 | es-module-lexer@1.7.0: 665 | resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} 666 | 667 | esbuild@0.25.12: 668 | resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} 669 | engines: {node: '>=18'} 670 | hasBin: true 671 | 672 | escape-string-regexp@4.0.0: 673 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 674 | engines: {node: '>=10'} 675 | 676 | eslint-config-prettier@10.1.8: 677 | resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} 678 | hasBin: true 679 | peerDependencies: 680 | eslint: '>=7.0.0' 681 | 682 | eslint-plugin-eslint-plugin@7.2.0: 683 | resolution: {integrity: sha512-3WOuoauBlxCItqpIdyajCOVQbCmAlqHNQq82QunpzuGkBNr6OqHRjdPZKpy2Z0rGb005mIO0HEP9aaDCzkApxQ==} 684 | engines: {node: ^20.19.0 || ^22.13.1 || >=24.0.0} 685 | peerDependencies: 686 | eslint: '>=9.0.0' 687 | 688 | eslint-scope@8.4.0: 689 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 690 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 691 | 692 | eslint-visitor-keys@3.4.3: 693 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 694 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 695 | 696 | eslint-visitor-keys@4.2.1: 697 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 698 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 699 | 700 | eslint@9.39.1: 701 | resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} 702 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 703 | hasBin: true 704 | peerDependencies: 705 | jiti: '*' 706 | peerDependenciesMeta: 707 | jiti: 708 | optional: true 709 | 710 | espree@10.4.0: 711 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 712 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 713 | 714 | esquery@1.6.0: 715 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 716 | engines: {node: '>=0.10'} 717 | 718 | esrecurse@4.3.0: 719 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 720 | engines: {node: '>=4.0'} 721 | 722 | estraverse@5.3.0: 723 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 724 | engines: {node: '>=4.0'} 725 | 726 | estree-walker@3.0.3: 727 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 728 | 729 | esutils@2.0.3: 730 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 731 | engines: {node: '>=0.10.0'} 732 | 733 | expect-type@1.3.0: 734 | resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} 735 | engines: {node: '>=12.0.0'} 736 | 737 | fast-deep-equal@3.1.3: 738 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 739 | 740 | fast-json-stable-stringify@2.1.0: 741 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 742 | 743 | fast-levenshtein@2.0.6: 744 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 745 | 746 | fdir@6.5.0: 747 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 748 | engines: {node: '>=12.0.0'} 749 | peerDependencies: 750 | picomatch: ^3 || ^4 751 | peerDependenciesMeta: 752 | picomatch: 753 | optional: true 754 | 755 | file-entry-cache@8.0.0: 756 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 757 | engines: {node: '>=16.0.0'} 758 | 759 | find-up@5.0.0: 760 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 761 | engines: {node: '>=10'} 762 | 763 | flat-cache@4.0.1: 764 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 765 | engines: {node: '>=16'} 766 | 767 | flatted@3.3.3: 768 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 769 | 770 | fsevents@2.3.3: 771 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 772 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 773 | os: [darwin] 774 | 775 | glob-parent@6.0.2: 776 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 777 | engines: {node: '>=10.13.0'} 778 | 779 | glob@13.0.0: 780 | resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} 781 | engines: {node: 20 || >=22} 782 | 783 | globals@14.0.0: 784 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 785 | engines: {node: '>=18'} 786 | 787 | globals@16.5.0: 788 | resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} 789 | engines: {node: '>=18'} 790 | 791 | has-flag@4.0.0: 792 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 793 | engines: {node: '>=8'} 794 | 795 | html-escaper@2.0.2: 796 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 797 | 798 | ignore@5.3.2: 799 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 800 | engines: {node: '>= 4'} 801 | 802 | ignore@7.0.5: 803 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 804 | engines: {node: '>= 4'} 805 | 806 | import-fresh@3.3.1: 807 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 808 | engines: {node: '>=6'} 809 | 810 | imurmurhash@0.1.4: 811 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 812 | engines: {node: '>=0.8.19'} 813 | 814 | is-extglob@2.1.1: 815 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 816 | engines: {node: '>=0.10.0'} 817 | 818 | is-glob@4.0.3: 819 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 820 | engines: {node: '>=0.10.0'} 821 | 822 | isexe@2.0.0: 823 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 824 | 825 | isexe@3.1.1: 826 | resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} 827 | engines: {node: '>=16'} 828 | 829 | istanbul-lib-coverage@3.2.2: 830 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 831 | engines: {node: '>=8'} 832 | 833 | istanbul-lib-report@3.0.1: 834 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 835 | engines: {node: '>=10'} 836 | 837 | istanbul-lib-source-maps@5.0.6: 838 | resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} 839 | engines: {node: '>=10'} 840 | 841 | istanbul-reports@3.2.0: 842 | resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} 843 | engines: {node: '>=8'} 844 | 845 | js-tokens@9.0.1: 846 | resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} 847 | 848 | js-yaml@4.1.1: 849 | resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 850 | hasBin: true 851 | 852 | json-buffer@3.0.1: 853 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 854 | 855 | json-parse-even-better-errors@4.0.0: 856 | resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} 857 | engines: {node: ^18.17.0 || >=20.5.0} 858 | 859 | json-schema-traverse@0.4.1: 860 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 861 | 862 | json-stable-stringify-without-jsonify@1.0.1: 863 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 864 | 865 | keyv@4.5.4: 866 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 867 | 868 | levn@0.4.1: 869 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 870 | engines: {node: '>= 0.8.0'} 871 | 872 | locate-path@6.0.0: 873 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 874 | engines: {node: '>=10'} 875 | 876 | lodash.merge@4.6.2: 877 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 878 | 879 | lru-cache@11.2.4: 880 | resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} 881 | engines: {node: 20 || >=22} 882 | 883 | magic-string@0.30.21: 884 | resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} 885 | 886 | magicast@0.5.1: 887 | resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} 888 | 889 | make-dir@4.0.0: 890 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 891 | engines: {node: '>=10'} 892 | 893 | memorystream@0.3.1: 894 | resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} 895 | engines: {node: '>= 0.10.0'} 896 | 897 | minimatch@10.1.1: 898 | resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} 899 | engines: {node: 20 || >=22} 900 | 901 | minimatch@3.1.2: 902 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 903 | 904 | minimatch@9.0.5: 905 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 906 | engines: {node: '>=16 || 14 >=14.17'} 907 | 908 | minipass@7.1.2: 909 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 910 | engines: {node: '>=16 || 14 >=14.17'} 911 | 912 | ms@2.1.3: 913 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 914 | 915 | nanoid@3.3.11: 916 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 917 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 918 | hasBin: true 919 | 920 | natural-compare@1.4.0: 921 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 922 | 923 | npm-normalize-package-bin@4.0.0: 924 | resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} 925 | engines: {node: ^18.17.0 || >=20.5.0} 926 | 927 | npm-run-all2@8.0.4: 928 | resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==} 929 | engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'} 930 | hasBin: true 931 | 932 | obug@2.1.1: 933 | resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} 934 | 935 | optionator@0.9.4: 936 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 937 | engines: {node: '>= 0.8.0'} 938 | 939 | p-limit@3.1.0: 940 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 941 | engines: {node: '>=10'} 942 | 943 | p-locate@5.0.0: 944 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 945 | engines: {node: '>=10'} 946 | 947 | package-json-from-dist@1.0.1: 948 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 949 | 950 | parent-module@1.0.1: 951 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 952 | engines: {node: '>=6'} 953 | 954 | path-exists@4.0.0: 955 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 956 | engines: {node: '>=8'} 957 | 958 | path-key@3.1.1: 959 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 960 | engines: {node: '>=8'} 961 | 962 | path-scurry@2.0.1: 963 | resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} 964 | engines: {node: 20 || >=22} 965 | 966 | pathe@2.0.3: 967 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 968 | 969 | picocolors@1.1.1: 970 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 971 | 972 | picomatch@4.0.3: 973 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 974 | engines: {node: '>=12'} 975 | 976 | pidtree@0.6.0: 977 | resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} 978 | engines: {node: '>=0.10'} 979 | hasBin: true 980 | 981 | postcss@8.5.6: 982 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 983 | engines: {node: ^10 || ^12 || >=14} 984 | 985 | prelude-ls@1.2.1: 986 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 987 | engines: {node: '>= 0.8.0'} 988 | 989 | prettier@3.7.4: 990 | resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} 991 | engines: {node: '>=14'} 992 | hasBin: true 993 | 994 | punycode@2.3.1: 995 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 996 | engines: {node: '>=6'} 997 | 998 | read-package-json-fast@4.0.0: 999 | resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==} 1000 | engines: {node: ^18.17.0 || >=20.5.0} 1001 | 1002 | resolve-from@4.0.0: 1003 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1004 | engines: {node: '>=4'} 1005 | 1006 | rimraf@6.1.2: 1007 | resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} 1008 | engines: {node: 20 || >=22} 1009 | hasBin: true 1010 | 1011 | rollup@4.53.3: 1012 | resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} 1013 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1014 | hasBin: true 1015 | 1016 | semver@7.7.3: 1017 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 1018 | engines: {node: '>=10'} 1019 | hasBin: true 1020 | 1021 | shebang-command@2.0.0: 1022 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1023 | engines: {node: '>=8'} 1024 | 1025 | shebang-regex@3.0.0: 1026 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1027 | engines: {node: '>=8'} 1028 | 1029 | shell-quote@1.8.3: 1030 | resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} 1031 | engines: {node: '>= 0.4'} 1032 | 1033 | siginfo@2.0.0: 1034 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 1035 | 1036 | source-map-js@1.2.1: 1037 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1038 | engines: {node: '>=0.10.0'} 1039 | 1040 | stackback@0.0.2: 1041 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 1042 | 1043 | std-env@3.10.0: 1044 | resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} 1045 | 1046 | strip-json-comments@3.1.1: 1047 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1048 | engines: {node: '>=8'} 1049 | 1050 | supports-color@7.2.0: 1051 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1052 | engines: {node: '>=8'} 1053 | 1054 | tinybench@2.9.0: 1055 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 1056 | 1057 | tinyexec@1.0.2: 1058 | resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} 1059 | engines: {node: '>=18'} 1060 | 1061 | tinyglobby@0.2.15: 1062 | resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 1063 | engines: {node: '>=12.0.0'} 1064 | 1065 | tinyrainbow@3.0.3: 1066 | resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} 1067 | engines: {node: '>=14.0.0'} 1068 | 1069 | ts-api-utils@2.1.0: 1070 | resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 1071 | engines: {node: '>=18.12'} 1072 | peerDependencies: 1073 | typescript: '>=4.8.4' 1074 | 1075 | type-check@0.4.0: 1076 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1077 | engines: {node: '>= 0.8.0'} 1078 | 1079 | typescript-eslint@8.49.0: 1080 | resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==} 1081 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1082 | peerDependencies: 1083 | eslint: ^8.57.0 || ^9.0.0 1084 | typescript: '>=4.8.4 <6.0.0' 1085 | 1086 | typescript@5.9.3: 1087 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 1088 | engines: {node: '>=14.17'} 1089 | hasBin: true 1090 | 1091 | undici-types@7.16.0: 1092 | resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} 1093 | 1094 | uri-js@4.4.1: 1095 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1096 | 1097 | vite@7.2.7: 1098 | resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} 1099 | engines: {node: ^20.19.0 || >=22.12.0} 1100 | hasBin: true 1101 | peerDependencies: 1102 | '@types/node': ^20.19.0 || >=22.12.0 1103 | jiti: '>=1.21.0' 1104 | less: ^4.0.0 1105 | lightningcss: ^1.21.0 1106 | sass: ^1.70.0 1107 | sass-embedded: ^1.70.0 1108 | stylus: '>=0.54.8' 1109 | sugarss: ^5.0.0 1110 | terser: ^5.16.0 1111 | tsx: ^4.8.1 1112 | yaml: ^2.4.2 1113 | peerDependenciesMeta: 1114 | '@types/node': 1115 | optional: true 1116 | jiti: 1117 | optional: true 1118 | less: 1119 | optional: true 1120 | lightningcss: 1121 | optional: true 1122 | sass: 1123 | optional: true 1124 | sass-embedded: 1125 | optional: true 1126 | stylus: 1127 | optional: true 1128 | sugarss: 1129 | optional: true 1130 | terser: 1131 | optional: true 1132 | tsx: 1133 | optional: true 1134 | yaml: 1135 | optional: true 1136 | 1137 | vitest@4.0.15: 1138 | resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} 1139 | engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} 1140 | hasBin: true 1141 | peerDependencies: 1142 | '@edge-runtime/vm': '*' 1143 | '@opentelemetry/api': ^1.9.0 1144 | '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 1145 | '@vitest/browser-playwright': 4.0.15 1146 | '@vitest/browser-preview': 4.0.15 1147 | '@vitest/browser-webdriverio': 4.0.15 1148 | '@vitest/ui': 4.0.15 1149 | happy-dom: '*' 1150 | jsdom: '*' 1151 | peerDependenciesMeta: 1152 | '@edge-runtime/vm': 1153 | optional: true 1154 | '@opentelemetry/api': 1155 | optional: true 1156 | '@types/node': 1157 | optional: true 1158 | '@vitest/browser-playwright': 1159 | optional: true 1160 | '@vitest/browser-preview': 1161 | optional: true 1162 | '@vitest/browser-webdriverio': 1163 | optional: true 1164 | '@vitest/ui': 1165 | optional: true 1166 | happy-dom: 1167 | optional: true 1168 | jsdom: 1169 | optional: true 1170 | 1171 | which@2.0.2: 1172 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1173 | engines: {node: '>= 8'} 1174 | hasBin: true 1175 | 1176 | which@5.0.0: 1177 | resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} 1178 | engines: {node: ^18.17.0 || >=20.5.0} 1179 | hasBin: true 1180 | 1181 | why-is-node-running@2.3.0: 1182 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1183 | engines: {node: '>=8'} 1184 | hasBin: true 1185 | 1186 | word-wrap@1.2.5: 1187 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1188 | engines: {node: '>=0.10.0'} 1189 | 1190 | yaml@2.5.1: 1191 | resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} 1192 | engines: {node: '>= 14'} 1193 | hasBin: true 1194 | 1195 | yocto-queue@0.1.0: 1196 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1197 | engines: {node: '>=10'} 1198 | 1199 | snapshots: 1200 | 1201 | '@babel/helper-string-parser@7.27.1': {} 1202 | 1203 | '@babel/helper-validator-identifier@7.28.5': {} 1204 | 1205 | '@babel/parser@7.28.5': 1206 | dependencies: 1207 | '@babel/types': 7.28.5 1208 | 1209 | '@babel/types@7.28.5': 1210 | dependencies: 1211 | '@babel/helper-string-parser': 7.27.1 1212 | '@babel/helper-validator-identifier': 7.28.5 1213 | 1214 | '@bcoe/v8-coverage@1.0.2': {} 1215 | 1216 | '@esbuild/aix-ppc64@0.25.12': 1217 | optional: true 1218 | 1219 | '@esbuild/android-arm64@0.25.12': 1220 | optional: true 1221 | 1222 | '@esbuild/android-arm@0.25.12': 1223 | optional: true 1224 | 1225 | '@esbuild/android-x64@0.25.12': 1226 | optional: true 1227 | 1228 | '@esbuild/darwin-arm64@0.25.12': 1229 | optional: true 1230 | 1231 | '@esbuild/darwin-x64@0.25.12': 1232 | optional: true 1233 | 1234 | '@esbuild/freebsd-arm64@0.25.12': 1235 | optional: true 1236 | 1237 | '@esbuild/freebsd-x64@0.25.12': 1238 | optional: true 1239 | 1240 | '@esbuild/linux-arm64@0.25.12': 1241 | optional: true 1242 | 1243 | '@esbuild/linux-arm@0.25.12': 1244 | optional: true 1245 | 1246 | '@esbuild/linux-ia32@0.25.12': 1247 | optional: true 1248 | 1249 | '@esbuild/linux-loong64@0.25.12': 1250 | optional: true 1251 | 1252 | '@esbuild/linux-mips64el@0.25.12': 1253 | optional: true 1254 | 1255 | '@esbuild/linux-ppc64@0.25.12': 1256 | optional: true 1257 | 1258 | '@esbuild/linux-riscv64@0.25.12': 1259 | optional: true 1260 | 1261 | '@esbuild/linux-s390x@0.25.12': 1262 | optional: true 1263 | 1264 | '@esbuild/linux-x64@0.25.12': 1265 | optional: true 1266 | 1267 | '@esbuild/netbsd-arm64@0.25.12': 1268 | optional: true 1269 | 1270 | '@esbuild/netbsd-x64@0.25.12': 1271 | optional: true 1272 | 1273 | '@esbuild/openbsd-arm64@0.25.12': 1274 | optional: true 1275 | 1276 | '@esbuild/openbsd-x64@0.25.12': 1277 | optional: true 1278 | 1279 | '@esbuild/openharmony-arm64@0.25.12': 1280 | optional: true 1281 | 1282 | '@esbuild/sunos-x64@0.25.12': 1283 | optional: true 1284 | 1285 | '@esbuild/win32-arm64@0.25.12': 1286 | optional: true 1287 | 1288 | '@esbuild/win32-ia32@0.25.12': 1289 | optional: true 1290 | 1291 | '@esbuild/win32-x64@0.25.12': 1292 | optional: true 1293 | 1294 | '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': 1295 | dependencies: 1296 | eslint: 9.39.1 1297 | eslint-visitor-keys: 3.4.3 1298 | 1299 | '@eslint-community/regexpp@4.12.2': {} 1300 | 1301 | '@eslint/config-array@0.21.1': 1302 | dependencies: 1303 | '@eslint/object-schema': 2.1.7 1304 | debug: 4.4.3 1305 | minimatch: 3.1.2 1306 | transitivePeerDependencies: 1307 | - supports-color 1308 | 1309 | '@eslint/config-helpers@0.4.2': 1310 | dependencies: 1311 | '@eslint/core': 0.17.0 1312 | 1313 | '@eslint/core@0.17.0': 1314 | dependencies: 1315 | '@types/json-schema': 7.0.15 1316 | 1317 | '@eslint/eslintrc@3.3.3': 1318 | dependencies: 1319 | ajv: 6.12.6 1320 | debug: 4.4.3 1321 | espree: 10.4.0 1322 | globals: 14.0.0 1323 | ignore: 5.3.2 1324 | import-fresh: 3.3.1 1325 | js-yaml: 4.1.1 1326 | minimatch: 3.1.2 1327 | strip-json-comments: 3.1.1 1328 | transitivePeerDependencies: 1329 | - supports-color 1330 | 1331 | '@eslint/js@9.39.1': {} 1332 | 1333 | '@eslint/object-schema@2.1.7': {} 1334 | 1335 | '@eslint/plugin-kit@0.4.1': 1336 | dependencies: 1337 | '@eslint/core': 0.17.0 1338 | levn: 0.4.1 1339 | 1340 | '@humanfs/core@0.19.1': {} 1341 | 1342 | '@humanfs/node@0.16.7': 1343 | dependencies: 1344 | '@humanfs/core': 0.19.1 1345 | '@humanwhocodes/retry': 0.4.3 1346 | 1347 | '@humanwhocodes/module-importer@1.0.1': {} 1348 | 1349 | '@humanwhocodes/retry@0.4.3': {} 1350 | 1351 | '@isaacs/balanced-match@4.0.1': {} 1352 | 1353 | '@isaacs/brace-expansion@5.0.0': 1354 | dependencies: 1355 | '@isaacs/balanced-match': 4.0.1 1356 | 1357 | '@jridgewell/resolve-uri@3.1.2': {} 1358 | 1359 | '@jridgewell/sourcemap-codec@1.5.5': {} 1360 | 1361 | '@jridgewell/trace-mapping@0.3.31': 1362 | dependencies: 1363 | '@jridgewell/resolve-uri': 3.1.2 1364 | '@jridgewell/sourcemap-codec': 1.5.5 1365 | 1366 | '@qnighy/dedent@0.1.1': {} 1367 | 1368 | '@rollup/rollup-android-arm-eabi@4.53.3': 1369 | optional: true 1370 | 1371 | '@rollup/rollup-android-arm64@4.53.3': 1372 | optional: true 1373 | 1374 | '@rollup/rollup-darwin-arm64@4.53.3': 1375 | optional: true 1376 | 1377 | '@rollup/rollup-darwin-x64@4.53.3': 1378 | optional: true 1379 | 1380 | '@rollup/rollup-freebsd-arm64@4.53.3': 1381 | optional: true 1382 | 1383 | '@rollup/rollup-freebsd-x64@4.53.3': 1384 | optional: true 1385 | 1386 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 1387 | optional: true 1388 | 1389 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 1390 | optional: true 1391 | 1392 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 1393 | optional: true 1394 | 1395 | '@rollup/rollup-linux-arm64-musl@4.53.3': 1396 | optional: true 1397 | 1398 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 1399 | optional: true 1400 | 1401 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 1402 | optional: true 1403 | 1404 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 1405 | optional: true 1406 | 1407 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 1408 | optional: true 1409 | 1410 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 1411 | optional: true 1412 | 1413 | '@rollup/rollup-linux-x64-gnu@4.53.3': 1414 | optional: true 1415 | 1416 | '@rollup/rollup-linux-x64-musl@4.53.3': 1417 | optional: true 1418 | 1419 | '@rollup/rollup-openharmony-arm64@4.53.3': 1420 | optional: true 1421 | 1422 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 1423 | optional: true 1424 | 1425 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 1426 | optional: true 1427 | 1428 | '@rollup/rollup-win32-x64-gnu@4.53.3': 1429 | optional: true 1430 | 1431 | '@rollup/rollup-win32-x64-msvc@4.53.3': 1432 | optional: true 1433 | 1434 | '@standard-schema/spec@1.0.0': {} 1435 | 1436 | '@stylistic/eslint-plugin@5.6.1(eslint@9.39.1)': 1437 | dependencies: 1438 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1439 | '@typescript-eslint/types': 8.49.0 1440 | eslint: 9.39.1 1441 | eslint-visitor-keys: 4.2.1 1442 | espree: 10.4.0 1443 | estraverse: 5.3.0 1444 | picomatch: 4.0.3 1445 | 1446 | '@susisu/eslint-config@0.0.101(eslint@9.39.1)(typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3)': 1447 | dependencies: 1448 | '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.1) 1449 | '@susisu/eslint-plugin-safe-typescript': 0.10.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3) 1450 | '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) 1451 | '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1452 | eslint: 9.39.1 1453 | eslint-config-prettier: 10.1.8(eslint@9.39.1) 1454 | transitivePeerDependencies: 1455 | - supports-color 1456 | - typescript 1457 | - typescript-eslint 1458 | 1459 | '@susisu/eslint-plugin-safe-typescript@0.10.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3))(typescript@5.9.3)': 1460 | dependencies: 1461 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1462 | eslint: 9.39.1 1463 | typescript: 5.9.3 1464 | optionalDependencies: 1465 | '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1466 | typescript-eslint: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1467 | transitivePeerDependencies: 1468 | - supports-color 1469 | 1470 | '@types/chai@5.2.3': 1471 | dependencies: 1472 | '@types/deep-eql': 4.0.2 1473 | assertion-error: 2.0.1 1474 | 1475 | '@types/deep-eql@4.0.2': {} 1476 | 1477 | '@types/estree@1.0.8': {} 1478 | 1479 | '@types/json-schema@7.0.15': {} 1480 | 1481 | '@types/node@25.0.1': 1482 | dependencies: 1483 | undici-types: 7.16.0 1484 | 1485 | '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': 1486 | dependencies: 1487 | '@eslint-community/regexpp': 4.12.2 1488 | '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1489 | '@typescript-eslint/scope-manager': 8.49.0 1490 | '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1491 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1492 | '@typescript-eslint/visitor-keys': 8.49.0 1493 | eslint: 9.39.1 1494 | ignore: 7.0.5 1495 | natural-compare: 1.4.0 1496 | ts-api-utils: 2.1.0(typescript@5.9.3) 1497 | typescript: 5.9.3 1498 | transitivePeerDependencies: 1499 | - supports-color 1500 | 1501 | '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 1502 | dependencies: 1503 | '@typescript-eslint/scope-manager': 8.49.0 1504 | '@typescript-eslint/types': 8.49.0 1505 | '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 1506 | '@typescript-eslint/visitor-keys': 8.49.0 1507 | debug: 4.4.3 1508 | eslint: 9.39.1 1509 | typescript: 5.9.3 1510 | transitivePeerDependencies: 1511 | - supports-color 1512 | 1513 | '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': 1514 | dependencies: 1515 | '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) 1516 | '@typescript-eslint/types': 8.49.0 1517 | debug: 4.4.3 1518 | typescript: 5.9.3 1519 | transitivePeerDependencies: 1520 | - supports-color 1521 | 1522 | '@typescript-eslint/rule-tester@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 1523 | dependencies: 1524 | '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1525 | '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 1526 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1527 | ajv: 6.12.6 1528 | eslint: 9.39.1 1529 | json-stable-stringify-without-jsonify: 1.0.1 1530 | lodash.merge: 4.6.2 1531 | semver: 7.7.3 1532 | transitivePeerDependencies: 1533 | - supports-color 1534 | - typescript 1535 | 1536 | '@typescript-eslint/scope-manager@8.49.0': 1537 | dependencies: 1538 | '@typescript-eslint/types': 8.49.0 1539 | '@typescript-eslint/visitor-keys': 8.49.0 1540 | 1541 | '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': 1542 | dependencies: 1543 | typescript: 5.9.3 1544 | 1545 | '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 1546 | dependencies: 1547 | '@typescript-eslint/types': 8.49.0 1548 | '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 1549 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1550 | debug: 4.4.3 1551 | eslint: 9.39.1 1552 | ts-api-utils: 2.1.0(typescript@5.9.3) 1553 | typescript: 5.9.3 1554 | transitivePeerDependencies: 1555 | - supports-color 1556 | 1557 | '@typescript-eslint/types@8.49.0': {} 1558 | 1559 | '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': 1560 | dependencies: 1561 | '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) 1562 | '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) 1563 | '@typescript-eslint/types': 8.49.0 1564 | '@typescript-eslint/visitor-keys': 8.49.0 1565 | debug: 4.4.3 1566 | minimatch: 9.0.5 1567 | semver: 7.7.3 1568 | tinyglobby: 0.2.15 1569 | ts-api-utils: 2.1.0(typescript@5.9.3) 1570 | typescript: 5.9.3 1571 | transitivePeerDependencies: 1572 | - supports-color 1573 | 1574 | '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 1575 | dependencies: 1576 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1577 | '@typescript-eslint/scope-manager': 8.49.0 1578 | '@typescript-eslint/types': 8.49.0 1579 | '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 1580 | eslint: 9.39.1 1581 | typescript: 5.9.3 1582 | transitivePeerDependencies: 1583 | - supports-color 1584 | 1585 | '@typescript-eslint/visitor-keys@8.49.0': 1586 | dependencies: 1587 | '@typescript-eslint/types': 8.49.0 1588 | eslint-visitor-keys: 4.2.1 1589 | 1590 | '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@25.0.1)(yaml@2.5.1))': 1591 | dependencies: 1592 | '@bcoe/v8-coverage': 1.0.2 1593 | '@vitest/utils': 4.0.15 1594 | ast-v8-to-istanbul: 0.3.8 1595 | istanbul-lib-coverage: 3.2.2 1596 | istanbul-lib-report: 3.0.1 1597 | istanbul-lib-source-maps: 5.0.6 1598 | istanbul-reports: 3.2.0 1599 | magicast: 0.5.1 1600 | obug: 2.1.1 1601 | std-env: 3.10.0 1602 | tinyrainbow: 3.0.3 1603 | vitest: 4.0.15(@types/node@25.0.1)(yaml@2.5.1) 1604 | transitivePeerDependencies: 1605 | - supports-color 1606 | 1607 | '@vitest/eslint-plugin@1.5.2(eslint@9.39.1)(typescript@5.9.3)(vitest@4.0.15(@types/node@25.0.1)(yaml@2.5.1))': 1608 | dependencies: 1609 | '@typescript-eslint/scope-manager': 8.49.0 1610 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 1611 | eslint: 9.39.1 1612 | optionalDependencies: 1613 | typescript: 5.9.3 1614 | vitest: 4.0.15(@types/node@25.0.1)(yaml@2.5.1) 1615 | transitivePeerDependencies: 1616 | - supports-color 1617 | 1618 | '@vitest/expect@4.0.15': 1619 | dependencies: 1620 | '@standard-schema/spec': 1.0.0 1621 | '@types/chai': 5.2.3 1622 | '@vitest/spy': 4.0.15 1623 | '@vitest/utils': 4.0.15 1624 | chai: 6.2.1 1625 | tinyrainbow: 3.0.3 1626 | 1627 | '@vitest/mocker@4.0.15(vite@7.2.7(@types/node@25.0.1)(yaml@2.5.1))': 1628 | dependencies: 1629 | '@vitest/spy': 4.0.15 1630 | estree-walker: 3.0.3 1631 | magic-string: 0.30.21 1632 | optionalDependencies: 1633 | vite: 7.2.7(@types/node@25.0.1)(yaml@2.5.1) 1634 | 1635 | '@vitest/pretty-format@4.0.15': 1636 | dependencies: 1637 | tinyrainbow: 3.0.3 1638 | 1639 | '@vitest/runner@4.0.15': 1640 | dependencies: 1641 | '@vitest/utils': 4.0.15 1642 | pathe: 2.0.3 1643 | 1644 | '@vitest/snapshot@4.0.15': 1645 | dependencies: 1646 | '@vitest/pretty-format': 4.0.15 1647 | magic-string: 0.30.21 1648 | pathe: 2.0.3 1649 | 1650 | '@vitest/spy@4.0.15': {} 1651 | 1652 | '@vitest/utils@4.0.15': 1653 | dependencies: 1654 | '@vitest/pretty-format': 4.0.15 1655 | tinyrainbow: 3.0.3 1656 | 1657 | acorn-jsx@5.3.2(acorn@8.15.0): 1658 | dependencies: 1659 | acorn: 8.15.0 1660 | 1661 | acorn@8.15.0: {} 1662 | 1663 | ajv@6.12.6: 1664 | dependencies: 1665 | fast-deep-equal: 3.1.3 1666 | fast-json-stable-stringify: 2.1.0 1667 | json-schema-traverse: 0.4.1 1668 | uri-js: 4.4.1 1669 | 1670 | ansi-styles@4.3.0: 1671 | dependencies: 1672 | color-convert: 2.0.1 1673 | 1674 | ansi-styles@6.2.3: {} 1675 | 1676 | argparse@2.0.1: {} 1677 | 1678 | assertion-error@2.0.1: {} 1679 | 1680 | ast-v8-to-istanbul@0.3.8: 1681 | dependencies: 1682 | '@jridgewell/trace-mapping': 0.3.31 1683 | estree-walker: 3.0.3 1684 | js-tokens: 9.0.1 1685 | 1686 | balanced-match@1.0.2: {} 1687 | 1688 | brace-expansion@1.1.12: 1689 | dependencies: 1690 | balanced-match: 1.0.2 1691 | concat-map: 0.0.1 1692 | 1693 | brace-expansion@2.0.2: 1694 | dependencies: 1695 | balanced-match: 1.0.2 1696 | 1697 | callsites@3.1.0: {} 1698 | 1699 | chai@6.2.1: {} 1700 | 1701 | chalk@4.1.2: 1702 | dependencies: 1703 | ansi-styles: 4.3.0 1704 | supports-color: 7.2.0 1705 | 1706 | color-convert@2.0.1: 1707 | dependencies: 1708 | color-name: 1.1.4 1709 | 1710 | color-name@1.1.4: {} 1711 | 1712 | concat-map@0.0.1: {} 1713 | 1714 | cross-spawn@7.0.6: 1715 | dependencies: 1716 | path-key: 3.1.1 1717 | shebang-command: 2.0.0 1718 | which: 2.0.2 1719 | 1720 | debug@4.4.3: 1721 | dependencies: 1722 | ms: 2.1.3 1723 | 1724 | deep-is@0.1.4: {} 1725 | 1726 | es-module-lexer@1.7.0: {} 1727 | 1728 | esbuild@0.25.12: 1729 | optionalDependencies: 1730 | '@esbuild/aix-ppc64': 0.25.12 1731 | '@esbuild/android-arm': 0.25.12 1732 | '@esbuild/android-arm64': 0.25.12 1733 | '@esbuild/android-x64': 0.25.12 1734 | '@esbuild/darwin-arm64': 0.25.12 1735 | '@esbuild/darwin-x64': 0.25.12 1736 | '@esbuild/freebsd-arm64': 0.25.12 1737 | '@esbuild/freebsd-x64': 0.25.12 1738 | '@esbuild/linux-arm': 0.25.12 1739 | '@esbuild/linux-arm64': 0.25.12 1740 | '@esbuild/linux-ia32': 0.25.12 1741 | '@esbuild/linux-loong64': 0.25.12 1742 | '@esbuild/linux-mips64el': 0.25.12 1743 | '@esbuild/linux-ppc64': 0.25.12 1744 | '@esbuild/linux-riscv64': 0.25.12 1745 | '@esbuild/linux-s390x': 0.25.12 1746 | '@esbuild/linux-x64': 0.25.12 1747 | '@esbuild/netbsd-arm64': 0.25.12 1748 | '@esbuild/netbsd-x64': 0.25.12 1749 | '@esbuild/openbsd-arm64': 0.25.12 1750 | '@esbuild/openbsd-x64': 0.25.12 1751 | '@esbuild/openharmony-arm64': 0.25.12 1752 | '@esbuild/sunos-x64': 0.25.12 1753 | '@esbuild/win32-arm64': 0.25.12 1754 | '@esbuild/win32-ia32': 0.25.12 1755 | '@esbuild/win32-x64': 0.25.12 1756 | 1757 | escape-string-regexp@4.0.0: {} 1758 | 1759 | eslint-config-prettier@10.1.8(eslint@9.39.1): 1760 | dependencies: 1761 | eslint: 9.39.1 1762 | 1763 | eslint-plugin-eslint-plugin@7.2.0(eslint@9.39.1): 1764 | dependencies: 1765 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1766 | eslint: 9.39.1 1767 | estraverse: 5.3.0 1768 | 1769 | eslint-scope@8.4.0: 1770 | dependencies: 1771 | esrecurse: 4.3.0 1772 | estraverse: 5.3.0 1773 | 1774 | eslint-visitor-keys@3.4.3: {} 1775 | 1776 | eslint-visitor-keys@4.2.1: {} 1777 | 1778 | eslint@9.39.1: 1779 | dependencies: 1780 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1781 | '@eslint-community/regexpp': 4.12.2 1782 | '@eslint/config-array': 0.21.1 1783 | '@eslint/config-helpers': 0.4.2 1784 | '@eslint/core': 0.17.0 1785 | '@eslint/eslintrc': 3.3.3 1786 | '@eslint/js': 9.39.1 1787 | '@eslint/plugin-kit': 0.4.1 1788 | '@humanfs/node': 0.16.7 1789 | '@humanwhocodes/module-importer': 1.0.1 1790 | '@humanwhocodes/retry': 0.4.3 1791 | '@types/estree': 1.0.8 1792 | ajv: 6.12.6 1793 | chalk: 4.1.2 1794 | cross-spawn: 7.0.6 1795 | debug: 4.4.3 1796 | escape-string-regexp: 4.0.0 1797 | eslint-scope: 8.4.0 1798 | eslint-visitor-keys: 4.2.1 1799 | espree: 10.4.0 1800 | esquery: 1.6.0 1801 | esutils: 2.0.3 1802 | fast-deep-equal: 3.1.3 1803 | file-entry-cache: 8.0.0 1804 | find-up: 5.0.0 1805 | glob-parent: 6.0.2 1806 | ignore: 5.3.2 1807 | imurmurhash: 0.1.4 1808 | is-glob: 4.0.3 1809 | json-stable-stringify-without-jsonify: 1.0.1 1810 | lodash.merge: 4.6.2 1811 | minimatch: 3.1.2 1812 | natural-compare: 1.4.0 1813 | optionator: 0.9.4 1814 | transitivePeerDependencies: 1815 | - supports-color 1816 | 1817 | espree@10.4.0: 1818 | dependencies: 1819 | acorn: 8.15.0 1820 | acorn-jsx: 5.3.2(acorn@8.15.0) 1821 | eslint-visitor-keys: 4.2.1 1822 | 1823 | esquery@1.6.0: 1824 | dependencies: 1825 | estraverse: 5.3.0 1826 | 1827 | esrecurse@4.3.0: 1828 | dependencies: 1829 | estraverse: 5.3.0 1830 | 1831 | estraverse@5.3.0: {} 1832 | 1833 | estree-walker@3.0.3: 1834 | dependencies: 1835 | '@types/estree': 1.0.8 1836 | 1837 | esutils@2.0.3: {} 1838 | 1839 | expect-type@1.3.0: {} 1840 | 1841 | fast-deep-equal@3.1.3: {} 1842 | 1843 | fast-json-stable-stringify@2.1.0: {} 1844 | 1845 | fast-levenshtein@2.0.6: {} 1846 | 1847 | fdir@6.5.0(picomatch@4.0.3): 1848 | optionalDependencies: 1849 | picomatch: 4.0.3 1850 | 1851 | file-entry-cache@8.0.0: 1852 | dependencies: 1853 | flat-cache: 4.0.1 1854 | 1855 | find-up@5.0.0: 1856 | dependencies: 1857 | locate-path: 6.0.0 1858 | path-exists: 4.0.0 1859 | 1860 | flat-cache@4.0.1: 1861 | dependencies: 1862 | flatted: 3.3.3 1863 | keyv: 4.5.4 1864 | 1865 | flatted@3.3.3: {} 1866 | 1867 | fsevents@2.3.3: 1868 | optional: true 1869 | 1870 | glob-parent@6.0.2: 1871 | dependencies: 1872 | is-glob: 4.0.3 1873 | 1874 | glob@13.0.0: 1875 | dependencies: 1876 | minimatch: 10.1.1 1877 | minipass: 7.1.2 1878 | path-scurry: 2.0.1 1879 | 1880 | globals@14.0.0: {} 1881 | 1882 | globals@16.5.0: {} 1883 | 1884 | has-flag@4.0.0: {} 1885 | 1886 | html-escaper@2.0.2: {} 1887 | 1888 | ignore@5.3.2: {} 1889 | 1890 | ignore@7.0.5: {} 1891 | 1892 | import-fresh@3.3.1: 1893 | dependencies: 1894 | parent-module: 1.0.1 1895 | resolve-from: 4.0.0 1896 | 1897 | imurmurhash@0.1.4: {} 1898 | 1899 | is-extglob@2.1.1: {} 1900 | 1901 | is-glob@4.0.3: 1902 | dependencies: 1903 | is-extglob: 2.1.1 1904 | 1905 | isexe@2.0.0: {} 1906 | 1907 | isexe@3.1.1: {} 1908 | 1909 | istanbul-lib-coverage@3.2.2: {} 1910 | 1911 | istanbul-lib-report@3.0.1: 1912 | dependencies: 1913 | istanbul-lib-coverage: 3.2.2 1914 | make-dir: 4.0.0 1915 | supports-color: 7.2.0 1916 | 1917 | istanbul-lib-source-maps@5.0.6: 1918 | dependencies: 1919 | '@jridgewell/trace-mapping': 0.3.31 1920 | debug: 4.4.3 1921 | istanbul-lib-coverage: 3.2.2 1922 | transitivePeerDependencies: 1923 | - supports-color 1924 | 1925 | istanbul-reports@3.2.0: 1926 | dependencies: 1927 | html-escaper: 2.0.2 1928 | istanbul-lib-report: 3.0.1 1929 | 1930 | js-tokens@9.0.1: {} 1931 | 1932 | js-yaml@4.1.1: 1933 | dependencies: 1934 | argparse: 2.0.1 1935 | 1936 | json-buffer@3.0.1: {} 1937 | 1938 | json-parse-even-better-errors@4.0.0: {} 1939 | 1940 | json-schema-traverse@0.4.1: {} 1941 | 1942 | json-stable-stringify-without-jsonify@1.0.1: {} 1943 | 1944 | keyv@4.5.4: 1945 | dependencies: 1946 | json-buffer: 3.0.1 1947 | 1948 | levn@0.4.1: 1949 | dependencies: 1950 | prelude-ls: 1.2.1 1951 | type-check: 0.4.0 1952 | 1953 | locate-path@6.0.0: 1954 | dependencies: 1955 | p-locate: 5.0.0 1956 | 1957 | lodash.merge@4.6.2: {} 1958 | 1959 | lru-cache@11.2.4: {} 1960 | 1961 | magic-string@0.30.21: 1962 | dependencies: 1963 | '@jridgewell/sourcemap-codec': 1.5.5 1964 | 1965 | magicast@0.5.1: 1966 | dependencies: 1967 | '@babel/parser': 7.28.5 1968 | '@babel/types': 7.28.5 1969 | source-map-js: 1.2.1 1970 | 1971 | make-dir@4.0.0: 1972 | dependencies: 1973 | semver: 7.7.3 1974 | 1975 | memorystream@0.3.1: {} 1976 | 1977 | minimatch@10.1.1: 1978 | dependencies: 1979 | '@isaacs/brace-expansion': 5.0.0 1980 | 1981 | minimatch@3.1.2: 1982 | dependencies: 1983 | brace-expansion: 1.1.12 1984 | 1985 | minimatch@9.0.5: 1986 | dependencies: 1987 | brace-expansion: 2.0.2 1988 | 1989 | minipass@7.1.2: {} 1990 | 1991 | ms@2.1.3: {} 1992 | 1993 | nanoid@3.3.11: {} 1994 | 1995 | natural-compare@1.4.0: {} 1996 | 1997 | npm-normalize-package-bin@4.0.0: {} 1998 | 1999 | npm-run-all2@8.0.4: 2000 | dependencies: 2001 | ansi-styles: 6.2.3 2002 | cross-spawn: 7.0.6 2003 | memorystream: 0.3.1 2004 | picomatch: 4.0.3 2005 | pidtree: 0.6.0 2006 | read-package-json-fast: 4.0.0 2007 | shell-quote: 1.8.3 2008 | which: 5.0.0 2009 | 2010 | obug@2.1.1: {} 2011 | 2012 | optionator@0.9.4: 2013 | dependencies: 2014 | deep-is: 0.1.4 2015 | fast-levenshtein: 2.0.6 2016 | levn: 0.4.1 2017 | prelude-ls: 1.2.1 2018 | type-check: 0.4.0 2019 | word-wrap: 1.2.5 2020 | 2021 | p-limit@3.1.0: 2022 | dependencies: 2023 | yocto-queue: 0.1.0 2024 | 2025 | p-locate@5.0.0: 2026 | dependencies: 2027 | p-limit: 3.1.0 2028 | 2029 | package-json-from-dist@1.0.1: {} 2030 | 2031 | parent-module@1.0.1: 2032 | dependencies: 2033 | callsites: 3.1.0 2034 | 2035 | path-exists@4.0.0: {} 2036 | 2037 | path-key@3.1.1: {} 2038 | 2039 | path-scurry@2.0.1: 2040 | dependencies: 2041 | lru-cache: 11.2.4 2042 | minipass: 7.1.2 2043 | 2044 | pathe@2.0.3: {} 2045 | 2046 | picocolors@1.1.1: {} 2047 | 2048 | picomatch@4.0.3: {} 2049 | 2050 | pidtree@0.6.0: {} 2051 | 2052 | postcss@8.5.6: 2053 | dependencies: 2054 | nanoid: 3.3.11 2055 | picocolors: 1.1.1 2056 | source-map-js: 1.2.1 2057 | 2058 | prelude-ls@1.2.1: {} 2059 | 2060 | prettier@3.7.4: {} 2061 | 2062 | punycode@2.3.1: {} 2063 | 2064 | read-package-json-fast@4.0.0: 2065 | dependencies: 2066 | json-parse-even-better-errors: 4.0.0 2067 | npm-normalize-package-bin: 4.0.0 2068 | 2069 | resolve-from@4.0.0: {} 2070 | 2071 | rimraf@6.1.2: 2072 | dependencies: 2073 | glob: 13.0.0 2074 | package-json-from-dist: 1.0.1 2075 | 2076 | rollup@4.53.3: 2077 | dependencies: 2078 | '@types/estree': 1.0.8 2079 | optionalDependencies: 2080 | '@rollup/rollup-android-arm-eabi': 4.53.3 2081 | '@rollup/rollup-android-arm64': 4.53.3 2082 | '@rollup/rollup-darwin-arm64': 4.53.3 2083 | '@rollup/rollup-darwin-x64': 4.53.3 2084 | '@rollup/rollup-freebsd-arm64': 4.53.3 2085 | '@rollup/rollup-freebsd-x64': 4.53.3 2086 | '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 2087 | '@rollup/rollup-linux-arm-musleabihf': 4.53.3 2088 | '@rollup/rollup-linux-arm64-gnu': 4.53.3 2089 | '@rollup/rollup-linux-arm64-musl': 4.53.3 2090 | '@rollup/rollup-linux-loong64-gnu': 4.53.3 2091 | '@rollup/rollup-linux-ppc64-gnu': 4.53.3 2092 | '@rollup/rollup-linux-riscv64-gnu': 4.53.3 2093 | '@rollup/rollup-linux-riscv64-musl': 4.53.3 2094 | '@rollup/rollup-linux-s390x-gnu': 4.53.3 2095 | '@rollup/rollup-linux-x64-gnu': 4.53.3 2096 | '@rollup/rollup-linux-x64-musl': 4.53.3 2097 | '@rollup/rollup-openharmony-arm64': 4.53.3 2098 | '@rollup/rollup-win32-arm64-msvc': 4.53.3 2099 | '@rollup/rollup-win32-ia32-msvc': 4.53.3 2100 | '@rollup/rollup-win32-x64-gnu': 4.53.3 2101 | '@rollup/rollup-win32-x64-msvc': 4.53.3 2102 | fsevents: 2.3.3 2103 | 2104 | semver@7.7.3: {} 2105 | 2106 | shebang-command@2.0.0: 2107 | dependencies: 2108 | shebang-regex: 3.0.0 2109 | 2110 | shebang-regex@3.0.0: {} 2111 | 2112 | shell-quote@1.8.3: {} 2113 | 2114 | siginfo@2.0.0: {} 2115 | 2116 | source-map-js@1.2.1: {} 2117 | 2118 | stackback@0.0.2: {} 2119 | 2120 | std-env@3.10.0: {} 2121 | 2122 | strip-json-comments@3.1.1: {} 2123 | 2124 | supports-color@7.2.0: 2125 | dependencies: 2126 | has-flag: 4.0.0 2127 | 2128 | tinybench@2.9.0: {} 2129 | 2130 | tinyexec@1.0.2: {} 2131 | 2132 | tinyglobby@0.2.15: 2133 | dependencies: 2134 | fdir: 6.5.0(picomatch@4.0.3) 2135 | picomatch: 4.0.3 2136 | 2137 | tinyrainbow@3.0.3: {} 2138 | 2139 | ts-api-utils@2.1.0(typescript@5.9.3): 2140 | dependencies: 2141 | typescript: 5.9.3 2142 | 2143 | type-check@0.4.0: 2144 | dependencies: 2145 | prelude-ls: 1.2.1 2146 | 2147 | typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3): 2148 | dependencies: 2149 | '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) 2150 | '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 2151 | '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 2152 | '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 2153 | eslint: 9.39.1 2154 | typescript: 5.9.3 2155 | transitivePeerDependencies: 2156 | - supports-color 2157 | 2158 | typescript@5.9.3: {} 2159 | 2160 | undici-types@7.16.0: {} 2161 | 2162 | uri-js@4.4.1: 2163 | dependencies: 2164 | punycode: 2.3.1 2165 | 2166 | vite@7.2.7(@types/node@25.0.1)(yaml@2.5.1): 2167 | dependencies: 2168 | esbuild: 0.25.12 2169 | fdir: 6.5.0(picomatch@4.0.3) 2170 | picomatch: 4.0.3 2171 | postcss: 8.5.6 2172 | rollup: 4.53.3 2173 | tinyglobby: 0.2.15 2174 | optionalDependencies: 2175 | '@types/node': 25.0.1 2176 | fsevents: 2.3.3 2177 | yaml: 2.5.1 2178 | 2179 | vitest@4.0.15(@types/node@25.0.1)(yaml@2.5.1): 2180 | dependencies: 2181 | '@vitest/expect': 4.0.15 2182 | '@vitest/mocker': 4.0.15(vite@7.2.7(@types/node@25.0.1)(yaml@2.5.1)) 2183 | '@vitest/pretty-format': 4.0.15 2184 | '@vitest/runner': 4.0.15 2185 | '@vitest/snapshot': 4.0.15 2186 | '@vitest/spy': 4.0.15 2187 | '@vitest/utils': 4.0.15 2188 | es-module-lexer: 1.7.0 2189 | expect-type: 1.3.0 2190 | magic-string: 0.30.21 2191 | obug: 2.1.1 2192 | pathe: 2.0.3 2193 | picomatch: 4.0.3 2194 | std-env: 3.10.0 2195 | tinybench: 2.9.0 2196 | tinyexec: 1.0.2 2197 | tinyglobby: 0.2.15 2198 | tinyrainbow: 3.0.3 2199 | vite: 7.2.7(@types/node@25.0.1)(yaml@2.5.1) 2200 | why-is-node-running: 2.3.0 2201 | optionalDependencies: 2202 | '@types/node': 25.0.1 2203 | transitivePeerDependencies: 2204 | - jiti 2205 | - less 2206 | - lightningcss 2207 | - msw 2208 | - sass 2209 | - sass-embedded 2210 | - stylus 2211 | - sugarss 2212 | - terser 2213 | - tsx 2214 | - yaml 2215 | 2216 | which@2.0.2: 2217 | dependencies: 2218 | isexe: 2.0.0 2219 | 2220 | which@5.0.0: 2221 | dependencies: 2222 | isexe: 3.1.1 2223 | 2224 | why-is-node-running@2.3.0: 2225 | dependencies: 2226 | siginfo: 2.0.0 2227 | stackback: 0.0.2 2228 | 2229 | word-wrap@1.2.5: {} 2230 | 2231 | yaml@2.5.1: 2232 | optional: true 2233 | 2234 | yocto-queue@0.1.0: {} 2235 | --------------------------------------------------------------------------------