├── .github ├── FUNDING.yml └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── src ├── plugin │ ├── constants │ │ ├── DIRECTIVE.ts │ │ ├── PLUGIN_DIAGNOSTIC_TAG.ts │ │ └── UNUSED_DIRECTIVE_DIAGNOSTIC_CODE.ts │ ├── createOverwritingProxy.ts │ ├── utils │ │ ├── collections.ts │ │ └── diagnostics.ts │ ├── mod.ts │ └── createTsMigratingProxyLanguageService.ts ├── api │ ├── mod.ts │ ├── typescript │ │ ├── getPluginsFromCompilerOptions.ts │ │ └── projectService.ts │ ├── isPluginDiagnostic.ts │ ├── getTSInfoForFile.ts │ ├── getSemanticDiagnostics.ts │ ├── utils │ │ └── maxBy.ts │ ├── insertSingleLineCommentsAtPositions.ts │ └── getSemanticDiagnostics.test.ts └── cli │ ├── expandTSFilePaths.ts │ ├── constants │ └── annotate-warning.ts │ ├── commands │ ├── info.ts │ ├── check.ts │ └── annotate.ts │ ├── ops │ └── getPluginEnabledTSFilePaths.ts │ └── main.ts ├── pnpm-workspace.yaml ├── examples ├── check-js-migration │ ├── src │ │ ├── greet.ts │ │ └── index.js │ └── tsconfig.json ├── erasable-syntax-only-migration │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── no-unchecked-indexed-access-migration │ ├── src │ │ └── index.ts │ └── tsconfig.json └── strict-mode-migration │ ├── src │ └── index.ts │ └── tsconfig.json ├── assets ├── ts-migrating-no-unchecked-indexed-access.png └── ts-migrating-no-unchecked-indexed-access-marked.png ├── vitest.config.ts ├── tsup.config.ts ├── .vscode └── settings.json ├── tsconfig.json ├── scripts └── build-and-publish.bash ├── biome.json ├── LICENSE ├── package.json ├── README.md └── pnpm-lock.yaml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ycmjason 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | !examples/**/node_modules 4 | -------------------------------------------------------------------------------- /src/plugin/constants/DIRECTIVE.ts: -------------------------------------------------------------------------------- 1 | export const DIRECTIVE = '@ts-migrating'; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - '@biomejs/biome' 3 | - esbuild 4 | -------------------------------------------------------------------------------- /src/plugin/constants/PLUGIN_DIAGNOSTIC_TAG.ts: -------------------------------------------------------------------------------- 1 | export const PLUGIN_DIAGNOSTIC_TAG = '[ts-migrating]'; 2 | -------------------------------------------------------------------------------- /examples/check-js-migration/src/greet.ts: -------------------------------------------------------------------------------- 1 | export const greet = (name: string): void => { 2 | console.log(`Hello, ${name}`); 3 | }; 4 | -------------------------------------------------------------------------------- /assets/ts-migrating-no-unchecked-indexed-access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycmjason/ts-migrating/HEAD/assets/ts-migrating-no-unchecked-indexed-access.png -------------------------------------------------------------------------------- /assets/ts-migrating-no-unchecked-indexed-access-marked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycmjason/ts-migrating/HEAD/assets/ts-migrating-no-unchecked-indexed-access-marked.png -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | root: 'src', 6 | includeSource: ['**/*.{js,ts,tsx,jsx}'], 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /src/plugin/constants/UNUSED_DIRECTIVE_DIAGNOSTIC_CODE.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * arbitrary code to represent UNUSED_DIRECTIVE_DIAGNOSTIC for easy distinguishing of the error 3 | * 4 | * - 555 is my lucky number 5 | */ 6 | export const UNUSED_DIRECTIVE_DIAGNOSTIC_CODE = 555; 7 | -------------------------------------------------------------------------------- /examples/check-js-migration/src/index.js: -------------------------------------------------------------------------------- 1 | import { greet } from './greet'; 2 | 3 | // @ts-migrating 4 | greet(true); 5 | // ^ 👌 fallback to `checkJs: false` 6 | 7 | greet(999999999999); 8 | // ^ ❌ [ts-migrating] Argument of type 'number' is not assignable to parameter of type 'string'. 9 | -------------------------------------------------------------------------------- /src/api/mod.ts: -------------------------------------------------------------------------------- 1 | export { getSemanticDiagnosticsForFile } from './getSemanticDiagnostics'; 2 | export { getTSInfoForFile } from './getTSInfoForFile'; 3 | export { insertSingleLineCommentAtPositions } from './insertSingleLineCommentsAtPositions'; 4 | export { isPluginDiagnostic } from './isPluginDiagnostic'; 5 | -------------------------------------------------------------------------------- /src/api/typescript/getPluginsFromCompilerOptions.ts: -------------------------------------------------------------------------------- 1 | import type ts from 'typescript/lib/tsserverlibrary'; 2 | 3 | export const getPluginsFromCompilerOptions = ( 4 | compilerOptions: ts.CompilerOptions, 5 | ): ts.PluginImport[] => { 6 | return (compilerOptions.plugins ?? []) as ts.PluginImport[]; 7 | }; 8 | -------------------------------------------------------------------------------- /src/cli/expandTSFilePaths.ts: -------------------------------------------------------------------------------- 1 | import { globSync } from 'tinyglobby'; 2 | 3 | export const expandTSFilePaths = (paths: string[]): string[] => { 4 | return globSync(paths, { 5 | ignore: ['**/node_modules/**/*'], 6 | absolute: true, 7 | }).filter(path => path.endsWith('.ts') || path.endsWith('.tsx')); 8 | }; 9 | -------------------------------------------------------------------------------- /examples/erasable-syntax-only-migration/src/index.ts: -------------------------------------------------------------------------------- 1 | enum FRUITS { 2 | // ^^^^^^ --ERROR--> [ts-migrating] This syntax is not allowed when 'erasableSyntaxOnly' is enabled. 3 | APPLE, 4 | BANANA, 5 | KIWI, 6 | } 7 | 8 | // @ts-migrating - ✅ the next line is type-checked with `erasableSyntaxOnly` disabled! 9 | enum Type { 10 | A, 11 | B, 12 | C, 13 | } 14 | -------------------------------------------------------------------------------- /src/plugin/createOverwritingProxy.ts: -------------------------------------------------------------------------------- 1 | export const createOverwritingProxy = (object: T, overwrite: Partial) => 2 | new Proxy(object, { 3 | get(target, key, receiver) { 4 | if (key in overwrite) { 5 | return Reflect.get(overwrite, key, receiver); 6 | } 7 | return Reflect.get(target, key, receiver); 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /examples/no-unchecked-indexed-access-migration/src/index.ts: -------------------------------------------------------------------------------- 1 | const plusOne = (xs: number[]) => { 2 | return xs[0] + 1; 3 | // ^^^^^ --ERROR--> [ts-migrating] Object is possibly 'undefined'. 4 | }; 5 | 6 | const plusTwo = (xs: number[]) => { 7 | // @ts-migrating 8 | return xs[0] + 1; // ✅ - line reverted to original tsconfig, i.e. with `noUncheckedIndexedAccess` disabled! 9 | }; 10 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig(options => { 4 | return { 5 | sourcemap: true, 6 | format: ['esm', 'cjs'], 7 | clean: true, 8 | cjsInterop: true, 9 | splitting: true, 10 | minify: !options.watch, 11 | treeshake: 'safest', 12 | define: { 13 | 'import.meta.vitest': 'false', 14 | }, 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /src/cli/constants/annotate-warning.ts: -------------------------------------------------------------------------------- 1 | export const ANNOTATE_WARNING = 2 | '⚠️ Run this with a clean git state!!! This script will automatically add the `@ts-migrating` directive above every line with TypeScript error introduced by your new `tsconfig`. Please review the changes carefully. It is recommended to run your formatter and linter afterwards. You may need to run this command again after formatter / linter.'; 3 | -------------------------------------------------------------------------------- /src/plugin/utils/collections.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return a new array containing elements in xs but not ys based on getComparer 3 | */ 4 | export const differenceBy = ( 5 | xs: readonly X[], 6 | ys: readonly Y[], 7 | getComparer: (x: X | Y) => C, 8 | ): X[] => { 9 | const xs2ComparerSet = new Set(ys.map(x => getComparer(x))); 10 | return xs.filter(x => !xs2ComparerSet.has(getComparer(x))); 11 | }; 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "biome.enabled": true, 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | "search.exclude": { 5 | "node_modules": true, 6 | "dist": true 7 | }, 8 | "editor.formatOnSave": true, 9 | "editor.defaultFormatter": "biomejs.biome", 10 | "editor.codeActionsOnSave": { 11 | "source.organizeImports.biome": "explicit", 12 | "source.fixAll.biome": "explicit" 13 | }, 14 | "[jsonc]": { 15 | "editor.defaultFormatter": "biomejs.biome" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/isPluginDiagnostic.ts: -------------------------------------------------------------------------------- 1 | import type ts from 'typescript/lib/tsserverlibrary'; 2 | import { PLUGIN_DIAGNOSTIC_TAG } from '../plugin/constants/PLUGIN_DIAGNOSTIC_TAG'; 3 | 4 | export const isPluginDiagnostic = (diagnostic: ts.Diagnostic): boolean => { 5 | // all plugin diagnostic are a message chain 6 | if (typeof diagnostic.messageText === 'string') return false; 7 | 8 | // all plugin diagnostic's first message is this tag 9 | return diagnostic.messageText.messageText === PLUGIN_DIAGNOSTIC_TAG; 10 | }; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "erasableSyntaxOnly": true, 10 | "noUncheckedIndexedAccess": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true, 13 | "noEmit": true, 14 | "types": ["vitest/importMeta"] 15 | }, 16 | "include": ["./src", "./vitest.config.ts", "./tsup.config.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /examples/strict-mode-migration/src/index.ts: -------------------------------------------------------------------------------- 1 | const greet = (name: string): void => { 2 | console.log(`Hello, ${name}`); 3 | }; 4 | 5 | greet(undefined); 6 | // ^^^^^^^^^ --ERROR--> "[ts-migrating] Argument of type 'undefined' is not assignable to parameter of type 'string'." 7 | 8 | // @ts-migrating - the error next line will be ignored 9 | greet(undefined); // ✅ No errors 10 | 11 | // @ts-migrating-abc - no effect. 12 | 13 | // @ts-migrating - this will show "Unused @ts-migrating directive" error 14 | // ^^^^^^^^^^^^^ --ERROR--> "[ts-migrating] Unused '@ts-migrating' directive" 15 | -------------------------------------------------------------------------------- /examples/erasable-syntax-only-migration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "noEmit": true, 5 | "erasableSyntaxOnly": false, 6 | "plugins": [ 7 | { 8 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 9 | // this is why we add `ts-migrating` as dev dependency of itself. 10 | "name": "ts-migrating", 11 | "compilerOptions": { 12 | "erasableSyntaxOnly": true 13 | } 14 | } 15 | ] 16 | }, 17 | "include": ["src"] 18 | } 19 | -------------------------------------------------------------------------------- /examples/check-js-migration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "skipLibCheck": true, 11 | "allowJs": true, 12 | "checkJs": false, 13 | "plugins": [ 14 | { 15 | "name": "ts-migrating", 16 | "compilerOptions": { 17 | "checkJs": true 18 | } 19 | } 20 | ] 21 | }, 22 | "include": ["src"], 23 | "exclude": ["node_modules", "dist"] 24 | } 25 | -------------------------------------------------------------------------------- /examples/no-unchecked-indexed-access-migration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "noEmit": true, 5 | "noUncheckedIndexedAccess": false, 6 | "plugins": [ 7 | { 8 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 9 | // this is why we add `ts-migrating` as dev dependency of itself. 10 | "name": "ts-migrating", 11 | "compilerOptions": { 12 | "noUncheckedIndexedAccess": true 13 | } 14 | } 15 | ] 16 | }, 17 | "include": ["src"], 18 | "exclude": ["node_modules", "dist"] 19 | } 20 | -------------------------------------------------------------------------------- /examples/strict-mode-migration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src", 7 | "strict": false, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "plugins": [ 11 | { 12 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 13 | // this is why we add `ts-migrating` as dev dependency of itself. 14 | "name": "ts-migrating", 15 | "compilerOptions": { 16 | "strict": true 17 | } 18 | } 19 | ], 20 | "skipLibCheck": true 21 | }, 22 | "include": ["src"], 23 | "exclude": ["node_modules", "dist"] 24 | } 25 | -------------------------------------------------------------------------------- /scripts/build-and-publish.bash: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PACKAGE_NAME=$(node -p "require('./package.json').name") 4 | VERSION=$(node -p "require('./package.json').version") 5 | 6 | # Extract pre-release tag if present (e.g. 'beta' from '1.0.0-beta.1') 7 | PRERELEASE_TAG=$(echo "$VERSION" | grep --color=never -Po '(alpha|beta|rc)') 8 | 9 | # Check if version already exists on npm 10 | if npm view "$PACKAGE_NAME@$VERSION" > /dev/null 2>&1; then 11 | echo "Version $VERSION of $PACKAGE_NAME already exists, skipping publish." 12 | else 13 | echo "Publishing version $VERSION of $PACKAGE_NAME" 14 | 15 | pnpm build 16 | 17 | if [ -n "$PRERELEASE_TAG" ]; then 18 | echo "Detected pre-release tag: $PRERELEASE_TAG" 19 | npm publish --provenance --access public --tag "$PRERELEASE_TAG" 20 | else 21 | echo "Detected stable release" 22 | npm publish --provenance --access public 23 | fi 24 | fi 25 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | 12 | permissions: 13 | contents: read 14 | id-token: write 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | - name: Install pnpm 20 | uses: pnpm/action-setup@v4 21 | - name: Set up Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 24 25 | cache: 'pnpm' 26 | registry-url: "https://registry.npmjs.org" 27 | 28 | - name: Install dependencies 29 | run: pnpm install --frozen-lockfile 30 | 31 | - run: pnpm build 32 | 33 | - name: Publish to npm 34 | run: bash ./scripts/build-and-publish.bash 35 | env: 36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | jobs: 9 | ci: 10 | name: Build, Lint, Format, TypeCheck and Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | - name: Install pnpm 16 | uses: pnpm/action-setup@v4 17 | - name: Set up Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 24 21 | cache: 'pnpm' 22 | registry-url: "https://registry.npmjs.org" 23 | 24 | - name: Install dependencies 25 | run: pnpm install --frozen-lockfile 26 | 27 | - name: Build 28 | run: pnpm build 29 | 30 | - name: Lint and Formating 31 | run: pnpm check:lint 32 | 33 | - name: Type check 34 | run: pnpm check:type 35 | 36 | - name: Test 37 | run: pnpm test 38 | -------------------------------------------------------------------------------- /src/api/typescript/projectService.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript/lib/tsserverlibrary'; 2 | 3 | export const projectService = new ts.server.ProjectService({ 4 | host: { 5 | ...ts.sys, 6 | setTimeout, 7 | clearTimeout, 8 | setImmediate, 9 | clearImmediate, 10 | watchDirectory: () => ({ close: () => {} }), 11 | watchFile: () => ({ close: () => {} }), 12 | }, 13 | session: undefined, 14 | logger: { 15 | close: () => {}, 16 | hasLevel: () => false, 17 | loggingEnabled: () => false, 18 | perftrc: () => {}, 19 | info: () => {}, 20 | startGroup: () => {}, 21 | endGroup: () => {}, 22 | msg: () => {}, 23 | getLogFileName: () => undefined, 24 | }, 25 | cancellationToken: ts.server.nullCancellationToken, 26 | useSingleInferredProject: true, 27 | useInferredProjectPerProjectRoot: true, 28 | typingsInstaller: ts.server.nullTypingsInstaller, 29 | allowLocalPluginLoads: true, 30 | }); 31 | -------------------------------------------------------------------------------- /src/cli/commands/info.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript/lib/tsserverlibrary'; 2 | import packageJson from '../../../package.json' with { type: 'json' }; 3 | import { getTSInfoForFile } from '../../api/mod'; 4 | import { expandTSFilePaths } from '../expandTSFilePaths'; 5 | 6 | export const info = (_flags: unknown, ...pathsOrGlobs: string[]) => { 7 | console.log('System information:'); 8 | console.log( 9 | JSON.stringify( 10 | { 11 | cwd: process.cwd(), 12 | 'ts-migrating': { 13 | version: packageJson.version, 14 | }, 15 | TypeScript: { 16 | path: require.resolve('typescript'), 17 | version: ts.version, 18 | }, 19 | nodejs: { 20 | version: process.version, 21 | }, 22 | }, 23 | null, 24 | 2, 25 | ), 26 | ); 27 | console.log(); 28 | 29 | for (const filePath of expandTSFilePaths(pathsOrGlobs)) { 30 | console.log(`Info about "${filePath}":`); 31 | console.log(getTSInfoForFile(filePath)); 32 | console.log(); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "linter": { 4 | "enabled": true, 5 | "rules": { 6 | "recommended": true, 7 | "suspicious": { 8 | "noConfusingVoidType": { 9 | "level": "info", 10 | "fix": "none" 11 | }, 12 | "noArrayIndexKey": "off", 13 | "noExplicitAny": "off" 14 | }, 15 | "correctness": { 16 | "noUnusedImports": "error" 17 | } 18 | }, 19 | "includes": ["**", "!examples/**"] 20 | }, 21 | "assist": { 22 | "actions": { 23 | "source": { 24 | "organizeImports": "on" 25 | } 26 | } 27 | }, 28 | "files": { 29 | "includes": ["**", "!package.json", "!dist"] 30 | }, 31 | "formatter": { 32 | "enabled": true, 33 | "indentStyle": "space", 34 | "indentWidth": 2, 35 | "lineWidth": 100 36 | }, 37 | "javascript": { 38 | "formatter": { 39 | "trailingCommas": "all", 40 | "semicolons": "always", 41 | "arrowParentheses": "asNeeded", 42 | "quoteStyle": "single" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 YCM Jason 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do 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 | -------------------------------------------------------------------------------- /src/api/getTSInfoForFile.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import ts from 'typescript/lib/tsserverlibrary'; 3 | import { getPluginsFromCompilerOptions } from './typescript/getPluginsFromCompilerOptions'; 4 | import { projectService } from './typescript/projectService'; 5 | 6 | type TSInfo = { 7 | tsconfigPath: string; 8 | pluginEnabled: boolean; 9 | }; 10 | 11 | export const getTSInfoForFile = (filePath: string): TSInfo => { 12 | const file = ts.server.toNormalizedPath(path.resolve(process.cwd(), filePath)); 13 | projectService.openClientFile(file); 14 | 15 | const tsInfo = (() => { 16 | const project = projectService.getDefaultProjectForFile(file, true); 17 | const compilerOptions = project?.getCompilerOptions() ?? {}; 18 | return { 19 | tsconfigPath: (compilerOptions.configFilePath as string) ?? '[not found]', 20 | pluginEnabled: isPluginEnabled(compilerOptions), 21 | }; 22 | })(); 23 | 24 | projectService.closeClientFile(file); 25 | return tsInfo; 26 | }; 27 | 28 | export const isPluginEnabled = (compilerOptions: ts.CompilerOptions): boolean => 29 | getPluginsFromCompilerOptions(compilerOptions).some(({ name }) => name === 'ts-migrating'); 30 | -------------------------------------------------------------------------------- /src/cli/ops/getPluginEnabledTSFilePaths.ts: -------------------------------------------------------------------------------- 1 | import { getTSInfoForFile } from '../../api/getTSInfoForFile'; 2 | import { expandTSFilePaths } from '../expandTSFilePaths'; 3 | 4 | export const getPluginEnabledTSFilePaths = ( 5 | inputPaths: string[], 6 | { verbose }: { verbose: boolean }, 7 | ): string[] => { 8 | console.log('🔎 Looking for ts-migrating enabled TypeScript files...'); 9 | const pluginEnabledFiles = expandTSFilePaths(inputPaths.length <= 0 ? ['.'] : inputPaths).filter( 10 | path => { 11 | const { pluginEnabled } = getTSInfoForFile(path); 12 | if (verbose && !pluginEnabled) { 13 | console.warn(`⚠️ Skipping "${path}" (tsconfig missing ts-migrating plugin).`); 14 | } 15 | return pluginEnabled; 16 | }, 17 | ); 18 | 19 | const fileCount = pluginEnabledFiles.length; 20 | console.log( 21 | `👀 ${fileCount} file${fileCount === 1 ? '' : 's'} found${!verbose ? ' (use -v or --verbose to list files)' : ':'}`, 22 | ); 23 | 24 | if (verbose && fileCount > 0) { 25 | for (const file of pluginEnabledFiles) { 26 | console.log(` • ${file}`); 27 | } 28 | } 29 | 30 | console.log(); 31 | 32 | return pluginEnabledFiles; 33 | }; 34 | -------------------------------------------------------------------------------- /src/api/getSemanticDiagnostics.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import ts from 'typescript/lib/tsserverlibrary'; 3 | import { isPluginEnabled } from './getTSInfoForFile'; 4 | import { projectService } from './typescript/projectService'; 5 | 6 | /** 7 | * Returns a list of {@link ts.Diagnostic} of a given file. 8 | * 9 | * This function returns `[]` if the tsconfig for the file does not list `ts-migrating` in the plugin. 10 | */ 11 | export function getSemanticDiagnosticsForFile(targetFile: string): ts.Diagnostic[] { 12 | const file = ts.server.toNormalizedPath(path.resolve(process.cwd(), targetFile)); 13 | 14 | const projects = [...projectService.configuredProjects.values()]; 15 | if (projects.every(project => !project.containsFile(file))) { 16 | // clear existing projects to avoid running out of memory 17 | for (const project of projects) { 18 | project.close(); 19 | } 20 | projectService.configuredProjects.clear(); 21 | } 22 | 23 | projectService.openClientFile(file); 24 | 25 | const project = projectService.getDefaultProjectForFile(file, true); 26 | if (!project) { 27 | throw new Error('Expect project to exist'); 28 | } 29 | 30 | if (!isPluginEnabled(project.getCompilerOptions())) { 31 | // tsconfig that this file uses does not have `ts-migrating` declared in the plugin. 32 | return []; 33 | } 34 | 35 | const diagnostics = project.getLanguageService().getSemanticDiagnostics(file); 36 | 37 | projectService.closeClientFile(file); 38 | 39 | return diagnostics; 40 | } 41 | -------------------------------------------------------------------------------- /src/plugin/mod.ts: -------------------------------------------------------------------------------- 1 | import type ts from 'typescript/lib/tsserverlibrary'; 2 | import { createOverwritingProxy } from './createOverwritingProxy'; 3 | import { createTsMigratingProxyLanguageService } from './createTsMigratingProxyLanguageService'; 4 | 5 | export default (({ typescript: ts }) => ({ 6 | create: ({ languageService, languageServiceHost, config }) => 7 | createTsMigratingProxyLanguageService({ 8 | ts, 9 | fromLanguageService: languageService, 10 | toLanguageService: (() => { 11 | const compilerOptionsOverwrite = (() => { 12 | const { compilerOptions = {} } = config; 13 | return compilerOptions; 14 | })(); 15 | 16 | return ts.createLanguageService( 17 | createOverwritingProxy( 18 | languageServiceHost, 19 | { 20 | getCompilationSettings: (...args) => ({ 21 | ...languageServiceHost.getCompilationSettings(...args), 22 | ...compilerOptionsOverwrite, 23 | }), 24 | /** 25 | * BIG THANK YOU TO https://github.com/allegro/typescript-strict-plugin/blob/master/src/plugin/utils.ts#L28-L32 26 | * See also: https://github.com/microsoft/TypeScript/blob/v5.8.3/src/services/services.ts#L1693-L1695 27 | * 28 | * If we do not reset this, it may cause `getProgram()` to return undefined and crash the plugin. 29 | * This is especially important in the standalone script. 30 | */ 31 | updateFromProject: undefined, 32 | }, 33 | ), 34 | ); 35 | })(), 36 | }), 37 | })) satisfies ts.server.PluginModuleFactory; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-migrating", 3 | "version": "1.3.0", 4 | "description": "Progressively Upgrade `tsconfigs.json`", 5 | "author": "Jason Yu ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/ycmjason/ts-migrating.git" 10 | }, 11 | "homepage": "https://github.com/ycmjason/ts-migrating#readme", 12 | "bugs": { 13 | "url": "https://github.com/ycmjason/ts-migrating/issues" 14 | }, 15 | "keywords": [ 16 | "typescript", 17 | "migration", 18 | "cli", 19 | "plugin" 20 | ], 21 | "main": "./dist/plugin/mod.js", 22 | "bin": "./dist/cli/main.js", 23 | "exports": { 24 | ".": { 25 | "import": "./dist/plugin/mod.mjs", 26 | "require": "./dist/plugin/mod.js" 27 | }, 28 | "./cli": { 29 | "import": "./dist/cli/main.mjs", 30 | "require": "./dist/cli/main.js" 31 | }, 32 | "./api": { 33 | "import": "./dist/api/mod.mjs", 34 | "require": "./dist/api/mod.js" 35 | } 36 | }, 37 | "files": [ 38 | "dist", 39 | "README.md", 40 | "LICENSE", 41 | "biome.config.json" 42 | ], 43 | "scripts": { 44 | "build": "tsup src/cli/main.ts src/api/mod.ts src/plugin/mod.ts", 45 | "build:watch": "pnpm build --watch", 46 | "check": "pnpm check:lint && pnpm check:type && pnpm test", 47 | "check:lint": "biome check .", 48 | "check:lint:fix": "pnpm check:lint --write", 49 | "check:type": "tsc", 50 | "test": "vitest run", 51 | "test:watch": "vitest" 52 | }, 53 | "devDependencies": { 54 | "@biomejs/biome": "^2.2.2", 55 | "@types/node": "^24.3.1", 56 | "ts-migrating": "link:", 57 | "tsup": "^8.5.0", 58 | "typescript": "^5.9.2", 59 | "vitest": "^3.2.4" 60 | }, 61 | "peerDependencies": { 62 | "typescript": "^5.0.0" 63 | }, 64 | "engines": { 65 | "node": ">=18.0.0" 66 | }, 67 | "dependencies": { 68 | "@babel/parser": "^7.28.3", 69 | "@stricli/core": "^1.2.0", 70 | "recast": "^0.23.11", 71 | "tinyglobby": "^0.2.14" 72 | }, 73 | "packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad" 74 | } 75 | -------------------------------------------------------------------------------- /src/cli/commands/check.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript/lib/tsserverlibrary'; 2 | import { getSemanticDiagnosticsForFile } from '../../api/getSemanticDiagnostics'; 3 | import { isPluginDiagnostic } from '../../api/isPluginDiagnostic'; 4 | import { getPluginEnabledTSFilePaths } from '../ops/getPluginEnabledTSFilePaths'; 5 | 6 | export const check = async ( 7 | { verbose, allTypeErrors: isCheckingAllTypeErrors }: { verbose: boolean; allTypeErrors: boolean }, 8 | ...inputPaths: string[] 9 | ) => { 10 | const pluginEnabledFiles = getPluginEnabledTSFilePaths(inputPaths, { verbose }); 11 | 12 | console.log( 13 | `⏳ Checking for ${isCheckingAllTypeErrors ? 'all TypeScript errors' : '[ts-migrating] plugin errors only'}...`, 14 | ); 15 | 16 | console.log(); 17 | 18 | const allDiagnostics: ts.Diagnostic[] = []; 19 | 20 | console.time('Type checking duration'); 21 | for (const file of pluginEnabledFiles) { 22 | const diagnostics = getSemanticDiagnosticsForFile(file); 23 | allDiagnostics.push(...diagnostics); 24 | 25 | if (diagnostics.length > 0) { 26 | console.log( 27 | ts.formatDiagnosticsWithColorAndContext( 28 | diagnostics.filter(d => { 29 | if (isCheckingAllTypeErrors) return true; 30 | return isPluginDiagnostic(d); 31 | }), 32 | { 33 | getCanonicalFileName: fileName => fileName, 34 | getCurrentDirectory: () => process.cwd(), 35 | getNewLine: () => ts.sys.newLine, 36 | }, 37 | ), 38 | ); 39 | } 40 | } 41 | console.timeEnd('Type checking duration'); 42 | 43 | if (isCheckingAllTypeErrors) { 44 | if (allDiagnostics.length > 0) { 45 | console.error( 46 | `❌ ${allDiagnostics.length} type error${allDiagnostics.length === 1 ? '' : 's'} found.`, 47 | ); 48 | } else { 49 | console.log('✅ No type errors found.'); 50 | } 51 | } 52 | 53 | const pluginDiagnostics = allDiagnostics.filter(diagnostics => isPluginDiagnostic(diagnostics)); 54 | 55 | if (pluginDiagnostics.length > 0) { 56 | console.error( 57 | `❌ ${pluginDiagnostics.length} unmarked plugin error${pluginDiagnostics.length === 1 ? '' : 's'} found. Run \`npx ts-migrating annotate\` to automatically mark them!`, 58 | ); 59 | } else { 60 | console.log('✅ No unmarked plugin errors found.'); 61 | } 62 | 63 | process.exit(Math.min((isCheckingAllTypeErrors ? allDiagnostics : pluginDiagnostics).length, 1)); 64 | }; 65 | -------------------------------------------------------------------------------- /src/plugin/createTsMigratingProxyLanguageService.ts: -------------------------------------------------------------------------------- 1 | import type TsServerLibrary from 'typescript/lib/tsserverlibrary'; 2 | import { DIRECTIVE } from './constants/DIRECTIVE'; 3 | import { UNUSED_DIRECTIVE_DIAGNOSTIC_CODE } from './constants/UNUSED_DIRECTIVE_DIAGNOSTIC_CODE'; 4 | import { createOverwritingProxy } from './createOverwritingProxy'; 5 | import { differenceBy } from './utils/collections'; 6 | import { 7 | brandifyDiagnostic, 8 | getUnmarkedDiagnostics, 9 | getUnusedDirectiveComments, 10 | serializeDiagnostic, 11 | } from './utils/diagnostics'; 12 | 13 | export const createTsMigratingProxyLanguageService = ({ 14 | ts, 15 | fromLanguageService, 16 | toLanguageService, 17 | }: { 18 | ts: typeof TsServerLibrary; 19 | fromLanguageService: TsServerLibrary.LanguageService; 20 | toLanguageService: TsServerLibrary.LanguageService; 21 | }) => 22 | createOverwritingProxy(fromLanguageService, { 23 | getQuickInfoAtPosition: (...attrs) => toLanguageService.getQuickInfoAtPosition(...attrs), 24 | getSemanticDiagnostics: name => { 25 | const diagnostics = fromLanguageService.getSemanticDiagnostics(name); 26 | 27 | const sourceFile = fromLanguageService.getProgram()?.getSourceFile(name); 28 | 29 | if (!sourceFile) return diagnostics; 30 | 31 | const pluginDiagnostics = (() => { 32 | const getLineNumberByPosition = (position: number): number => 33 | sourceFile.getLineAndCharacterOfPosition(position).line; 34 | 35 | const newlyIntroducedDiagnostics = differenceBy( 36 | toLanguageService.getSemanticDiagnostics(name), 37 | diagnostics, 38 | serializeDiagnostic, 39 | ); 40 | 41 | const directiveComments = fromLanguageService 42 | .getTodoComments(name, [{ text: DIRECTIVE, priority: 0 }]) 43 | .filter(({ message }) => new RegExp(`^${DIRECTIVE}(\\s|$)`).test(message)); 44 | 45 | return [ 46 | ...getUnmarkedDiagnostics(newlyIntroducedDiagnostics, { 47 | sourceFile, 48 | directiveComments, 49 | getLineNumberByPosition, 50 | }), 51 | ...getUnusedDirectiveComments(directiveComments, { 52 | sourceFile, 53 | newlyIntroducedDiagnostics, 54 | getLineNumberByPosition, 55 | }).map(({ position, descriptor }) => ({ 56 | category: ts.DiagnosticCategory.Error, 57 | code: UNUSED_DIRECTIVE_DIAGNOSTIC_CODE, 58 | file: sourceFile, 59 | start: position, 60 | length: descriptor.text.length, 61 | messageText: `Unused '${descriptor.text}' directive.`, 62 | })), 63 | ].map(brandifyDiagnostic); 64 | })(); 65 | 66 | return [...diagnostics, ...pluginDiagnostics]; 67 | }, 68 | }); 69 | -------------------------------------------------------------------------------- /src/cli/main.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import process from 'node:process'; 3 | import { buildApplication, buildCommand, buildRouteMap, run } from '@stricli/core'; 4 | import packageJSON from '../../package.json' with { type: 'json' }; 5 | import { ANNOTATE_WARNING } from './constants/annotate-warning'; 6 | 7 | (async () => { 8 | await run( 9 | buildApplication( 10 | buildRouteMap({ 11 | routes: { 12 | check: buildCommand({ 13 | loader: async () => (await import('./commands/check')).check, 14 | parameters: { 15 | flags: { 16 | verbose: { 17 | kind: 'boolean', 18 | brief: 'Enable verbose logging.', 19 | default: false, 20 | }, 21 | allTypeErrors: { 22 | kind: 'boolean', 23 | brief: 24 | 'Display all type errors. By default, this command only show [ts-migrating] errors, which are type errors introduced by the new tsconfig.', 25 | default: false, 26 | }, 27 | }, 28 | aliases: { 29 | v: 'verbose', 30 | a: 'allTypeErrors', 31 | }, 32 | positional: { 33 | kind: 'array', 34 | parameter: { 35 | placeholder: 'path', 36 | brief: 'File / Directories Paths or Globs', 37 | parse: String, 38 | }, 39 | }, 40 | }, 41 | docs: { 42 | brief: 'Run `@ts-migrating`-aware type checks.', 43 | customUsage: ['path/to/file1.ts path/to/directory glob/**/example/*'], 44 | }, 45 | }), 46 | annotate: buildCommand({ 47 | loader: async () => (await import('./commands/annotate')).annotate, 48 | parameters: { 49 | flags: { 50 | verbose: { 51 | kind: 'boolean', 52 | brief: 'Enable verbose logging.', 53 | default: false, 54 | }, 55 | }, 56 | aliases: { 57 | v: 'verbose', 58 | }, 59 | positional: { 60 | kind: 'array', 61 | parameter: { 62 | placeholder: 'path', 63 | brief: 'File / Directories Paths or Globs', 64 | parse: String, 65 | }, 66 | }, 67 | }, 68 | docs: { 69 | brief: 'Annotate all errors introduced by the new config with @ts-migrating', 70 | fullDescription: `Annotate all errors introduced by the new config with @ts-migrating 71 | 72 | ${ANNOTATE_WARNING}`, 73 | }, 74 | }), 75 | info: buildCommand({ 76 | func: (await import('./commands/info')).info, 77 | parameters: { 78 | positional: { 79 | kind: 'array', 80 | parameter: { 81 | placeholder: 'path', 82 | brief: 'File / Directories Paths or Globs', 83 | parse: String, 84 | }, 85 | }, 86 | }, 87 | docs: { 88 | brief: 'Show some debug info that might be useful.', 89 | }, 90 | }), 91 | }, 92 | docs: { 93 | brief: '@ts-migrating CLI', 94 | }, 95 | }), 96 | { 97 | name: packageJSON.name, 98 | versionInfo: { 99 | currentVersion: packageJSON.version, 100 | }, 101 | }, 102 | ), 103 | process.argv.slice(2), 104 | { process }, 105 | ); 106 | })(); 107 | -------------------------------------------------------------------------------- /src/plugin/utils/diagnostics.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript/lib/tsserverlibrary'; 2 | import { PLUGIN_DIAGNOSTIC_TAG } from '../constants/PLUGIN_DIAGNOSTIC_TAG'; 3 | 4 | const serializeDiagnosticMessageText = ( 5 | m: ts.DiagnosticRelatedInformation['messageText'], 6 | ): string => { 7 | if (typeof m === 'string') return m; 8 | const serializedNext = 9 | m.next === undefined ? '' : m.next.map(chain => serializeDiagnosticMessageText(chain)).join(''); 10 | 11 | return m.messageText + serializedNext; 12 | }; 13 | 14 | export const serializeDiagnostic = (d: ts.DiagnosticRelatedInformation): string => 15 | `${d.code}/${d.start}/${d.length}/${d.file}/${d.category}/${serializeDiagnosticMessageText(d.messageText)}`; 16 | 17 | export const brandifyDiagnostic = ( 18 | d: ts.DiagnosticRelatedInformation, 19 | ): ts.DiagnosticRelatedInformation => { 20 | return { 21 | ...d, 22 | messageText: { 23 | category: d.category, 24 | code: d.code, 25 | messageText: PLUGIN_DIAGNOSTIC_TAG, 26 | next: [ 27 | typeof d.messageText === 'string' 28 | ? { 29 | category: d.category, 30 | code: d.code, 31 | messageText: d.messageText, 32 | } 33 | : d.messageText, 34 | ], 35 | }, 36 | }; 37 | }; 38 | 39 | const findNextNonCommentLine = ( 40 | sourceFile: ts.SourceFile, 41 | /** zero indexed */ 42 | fromLine: number, 43 | ): number => { 44 | // typescript ast has no comments! so find the first AST node's line number that is after `fromLine` 45 | let found: number | undefined; 46 | const visit = (node: ts.Node) => { 47 | const { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); 48 | if (found !== undefined) return node; 49 | if (line > fromLine) { 50 | found = line; 51 | return node; 52 | } 53 | 54 | return ts.visitEachChild(node, visit, undefined); 55 | }; 56 | 57 | visit(sourceFile); 58 | 59 | return found ?? fromLine + 1; 60 | }; 61 | 62 | /** 63 | * Returns a new array containing elements from `diagnostics` where they are not marked by the given directiveComments 64 | */ 65 | export const getUnmarkedDiagnostics = ( 66 | newlyIntroducedDiagnostics: readonly ts.DiagnosticRelatedInformation[], 67 | { 68 | sourceFile, 69 | directiveComments, 70 | getLineNumberByPosition, 71 | }: { 72 | sourceFile: ts.SourceFile; 73 | directiveComments: readonly ts.TodoComment[]; 74 | getLineNumberByPosition: (position: number) => number; 75 | }, 76 | ): ts.DiagnosticRelatedInformation[] => { 77 | const markedLineNumbers = new Set( 78 | directiveComments 79 | .map(({ position }) => getLineNumberByPosition(position)) 80 | .map(markerCommentLineNumber => findNextNonCommentLine(sourceFile, markerCommentLineNumber)), 81 | ); 82 | return newlyIntroducedDiagnostics.filter(d => { 83 | if (d.start === undefined) return true; 84 | 85 | return !markedLineNumbers.has(getLineNumberByPosition(d.start)); 86 | }); 87 | }; 88 | 89 | export const getUnusedDirectiveComments = ( 90 | directiveComments: readonly ts.TodoComment[], 91 | { 92 | sourceFile, 93 | newlyIntroducedDiagnostics, 94 | getLineNumberByPosition, 95 | }: { 96 | sourceFile: ts.SourceFile; 97 | newlyIntroducedDiagnostics: readonly ts.DiagnosticRelatedInformation[]; 98 | getLineNumberByPosition: (position: number) => number; 99 | }, 100 | ) => { 101 | const newlyIntroducedDiagnosticsLineNumbers = new Set( 102 | newlyIntroducedDiagnostics 103 | .map(({ start }) => start) 104 | .filter(startPosition => startPosition !== undefined) 105 | .map(startPosition => getLineNumberByPosition(startPosition)), 106 | ); 107 | 108 | return directiveComments.filter(({ position }) => { 109 | const markerCommentLine = getLineNumberByPosition(position); 110 | return !newlyIntroducedDiagnosticsLineNumbers.has( 111 | findNextNonCommentLine(sourceFile, markerCommentLine), 112 | ); 113 | }); 114 | }; 115 | -------------------------------------------------------------------------------- /src/api/utils/maxBy.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/denoland/std/blob/main/collections/max_by.ts 2 | // Copyright 2018-2025 the Deno authors. MIT license. 3 | // This module is browser compatible. 4 | 5 | /** 6 | * Returns the first element that is the largest value of the given function or 7 | * undefined if there are no elements. 8 | * 9 | * @typeParam T The type of the elements in the array. 10 | * 11 | * @param array The array to find the maximum element in. 12 | * @param selector The function to get the value to compare from each element. 13 | * 14 | * @returns The first element that is the largest value of the given function or 15 | * undefined if there are no elements. 16 | * 17 | * @example Basic usage 18 | * ```ts 19 | * import { maxBy } from "@std/collections/max-by"; 20 | * import { assertEquals } from "@std/assert"; 21 | * 22 | * const people = [ 23 | * { name: "Anna", age: 34 }, 24 | * { name: "Kim", age: 42 }, 25 | * { name: "John", age: 23 }, 26 | * ]; 27 | * 28 | * const personWithMaxAge = maxBy(people, (person) => person.age); 29 | * 30 | * assertEquals(personWithMaxAge, { name: "Kim", age: 42 }); 31 | * ``` 32 | */ 33 | export function maxBy(array: Iterable, selector: (el: T) => number): T | undefined; 34 | /** 35 | * Returns the first element that is the largest value of the given function or 36 | * undefined if there are no elements. 37 | * 38 | * @typeParam T The type of the elements in the array. 39 | * 40 | * @param array The array to find the maximum element in. 41 | * @param selector The function to get the value to compare from each element. 42 | * 43 | * @returns The first element that is the largest value of the given function or 44 | * undefined if there are no elements. 45 | * 46 | * @example Basic usage 47 | * ```ts 48 | * import { maxBy } from "@std/collections/max-by"; 49 | * import { assertEquals } from "@std/assert"; 50 | * 51 | * const people = [ 52 | * { name: "Anna" }, 53 | * { name: "Kim" }, 54 | * { name: "John" }, 55 | * ]; 56 | * 57 | * const personWithMaxName = maxBy(people, (person) => person.name); 58 | * 59 | * assertEquals(personWithMaxName, { name: "Kim" }); 60 | * ``` 61 | */ 62 | export function maxBy(array: Iterable, selector: (el: T) => string): T | undefined; 63 | /** 64 | * Returns the first element that is the largest value of the given function or 65 | * undefined if there are no elements. 66 | * 67 | * @typeParam T The type of the elements in the array. 68 | * 69 | * @param array The array to find the maximum element in. 70 | * @param selector The function to get the value to compare from each element. 71 | * 72 | * @returns The first element that is the largest value of the given function or 73 | * undefined if there are no elements. 74 | * 75 | * @example Basic usage 76 | * ```ts 77 | * import { maxBy } from "@std/collections/max-by"; 78 | * import { assertEquals } from "@std/assert"; 79 | * 80 | * const people = [ 81 | * { name: "Anna", age: 34n }, 82 | * { name: "Kim", age: 42n }, 83 | * { name: "John", age: 23n }, 84 | * ]; 85 | * 86 | * const personWithMaxAge = maxBy(people, (person) => person.age); 87 | * 88 | * assertEquals(personWithMaxAge, { name: "Kim", age: 42n }); 89 | * ``` 90 | */ 91 | export function maxBy(array: Iterable, selector: (el: T) => bigint): T | undefined; 92 | /** 93 | * Returns the first element that is the largest value of the given function or 94 | * undefined if there are no elements. 95 | * 96 | * @typeParam T The type of the elements in the array. 97 | * 98 | * @param array The array to find the maximum element in. 99 | * @param selector The function to get the value to compare from each element. 100 | * 101 | * @returns The first element that is the largest value of the given function or 102 | * undefined if there are no elements. 103 | * 104 | * @example Basic usage 105 | * ```ts 106 | * import { maxBy } from "@std/collections/max-by"; 107 | * import { assertEquals } from "@std/assert"; 108 | * 109 | * const people = [ 110 | * { name: "Anna", startedAt: new Date("2020-01-01") }, 111 | * { name: "Kim", startedAt: new Date("2021-03-01") }, 112 | * { name: "John", startedAt: new Date("2020-03-01") }, 113 | * ]; 114 | * 115 | * const personWithLastStartedAt = maxBy(people, (person) => person.startedAt); 116 | * 117 | * assertEquals(personWithLastStartedAt, { name: "Kim", startedAt: new Date("2021-03-01") }); 118 | * ``` 119 | */ 120 | export function maxBy(array: Iterable, selector: (el: T) => Date): T | undefined; 121 | export function maxBy( 122 | array: Iterable, 123 | selector: ((el: T) => number) | ((el: T) => string) | ((el: T) => bigint) | ((el: T) => Date), 124 | ): T | undefined { 125 | let max: T | undefined; 126 | let maxValue: ReturnType | undefined; 127 | 128 | for (const current of array) { 129 | const currentValue = selector(current); 130 | 131 | if (maxValue === undefined || currentValue > maxValue) { 132 | max = current; 133 | maxValue = currentValue; 134 | } 135 | } 136 | 137 | return max; 138 | } 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `@ts-migrating` — Progressively Upgrade `tsconfig.json` 2 | 3 | 🚀 **TypeScript keeps evolving — and your `tsconfig` should too.** 4 | 5 | This plugin lets you upgrade to your desired `compilerOptions` (e.g. `strict`, `noUncheckedIndexedAccess`, `erasableSyntaxOnly`, `checkJs`) across your entire codebase, while letting problematic lines fall back to the old `compilerOptions`. 6 | 7 | Upgrading `tsconfig` often breaks existing code, and fixing all errors at once is unrealistic. 8 | 9 | **`@ts-migrating`** helps your team migrate to a desired `tsconfig` gradually and safely. 10 | 11 | > I chose `@ts-migrating` (rather than `@ts-migration`) to better reflect the plugin’s progressive and incremental philosophy. 12 | 13 | ## 🙋‍♀️ Why not `@ts-expect-error` / `@ts-ignore`? 14 | 15 | Using `@ts-expect-error` or `@ts-ignore` to silence TypeScript errors can work in the short term — but they come with trade-offs: 16 | 17 | - They suppress all errors on the line, not just those introduced by the new `compilerOptions`. This can hide unrelated issues and introduce technical debt. 18 | - There are cases where you actually want to use `@ts-expect-error` and `@ts-ignore`. Mixing their real usages with `tsconfig` migration is 🤮. 19 | 20 | This plugin takes a different approach: it lets you apply the desired `compilerOptions` globally while allowing them to be reverted line-by-line. This keeps your code clean, and your intent clear — enabling a safer and more maintainable upgrade path. 21 | 22 | ## 🤖 How does this work? 23 | 24 | `@ts-migrating` is a TypeScript plugin that lets you **enable your target tsconfig during development** (in IDEs or editors that use the [TypeScript Language Service](https://github.com/microsoft/typescript/wiki/Using-the-Language-Service-API)) and in CI — **without affecting `tsc` or your production build**. 25 | 26 | The philosophy behind the plugin follows three simple steps: 27 | 28 | 1. 🛑 **Prevention** 29 | 30 | * Errors from your target config are surfaced during development and in CI. 31 | * This ensures no new violations are introduced into the codebase. 32 | 33 | 2. 🔧 **Reduction** 34 | 35 | * Lines marked with `@ts-migrating` will be typechecked with your original `tsconfig`. Ensuring type-safety throughout. 36 | * Developers can progressively fix these lines, reducing violations over time. 37 | 38 | 3. ✅ **Migration** 39 | 40 | * Once all violations are fixed, no `@ts-migrating` directives remain. 41 | * At this point, you're ready to fully adopt the new tsconfig — and the plugin has served its purpose. 42 | 43 | ## 📚 Overview 44 | 45 | `@ts-migrating` consists of two parts: 46 | 47 | 1. 🔌 **[TypeScript Language Service Plugin](https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)** 48 | 49 | * Enables IDEs to show errors from the `tsconfig` you're migrating to. 50 | * Revert lines marked with `@ts-migrating` to be type-checked with your original `tsconfig`. 51 | 52 | 2. 🖥️ **Standalone CLI: `ts-migrating`** 53 | 54 | * `ts-migrating check` 55 | 56 | * Run `@ts-migrating`-aware type checking using your new `tsconfig`. 57 | * `ts-migrating annotate` 58 | 59 | * Automatically mark all errors caused by your new `tsconfig` with `@ts-migrating`. 60 | * ⚠️ Run this with a clean git state!!! This script will automatically add the `@ts-migrating` directive above every line with TypeScript error introduced by your new `tsconfig`. Please review the changes carefully. It is recommended to run your formatter and linter afterwards. You may need to run this command again after formatter / linter.️ 61 | 62 | ## 🎪 Examples 63 | 64 | * [Migrating to `strict`](./examples/strict-mode-migration/src/index.ts) 65 | * [Migrating to `noUncheckedIndexedAccess`](./examples/no-unchecked-indexed-access-migration/src/index.ts): 66 | 67 | | Without `@ts-migrating` | With `@ts-migrating` | 68 | | ----------------------- | -------------------- | 69 | | ![Migrating to noUncheckedIndexedAccess](./assets/ts-migrating-no-unchecked-indexed-access.png) | ![Migrating to noUncheckedIndexedAccess marked](./assets/ts-migrating-no-unchecked-indexed-access-marked.png) | 70 | 71 | * [Migrating to `erasableSyntaxOnly`](./examples/erasable-syntax-only-migration/src/index.ts) 72 | * [Migrating to `checkJs`](./examples/check-js-migration/src/index.js) 73 | 74 | ## 📦 Install and Setup 75 | 76 | This project does **NOT** require any IDE extensions. It relies purely on TypeScript's own Language Service, so it works on most IDEs and editors that support TypeScript (e.g., VSCode, WebStorm). 77 | 78 | To install: 79 | 80 | ```bash 81 | cd my-cool-project 82 | npm install -D ts-migrating 83 | ``` 84 | 85 | In your existing `tsconfig.json`, add the plugin: 86 | 87 | ```jsonc 88 | { 89 | // ... 90 | "compilerOptions": { 91 | // ... 92 | "plugins": [ 93 | { 94 | "name": "ts-migrating", 95 | "compilerOptions": { 96 | // ... put the compiler options you wish to migrate to, for example: 97 | "strict": true 98 | } 99 | } 100 | ] 101 | // ... 102 | } 103 | // ... 104 | } 105 | ``` 106 | 107 | ℹ️ *Note: `plugins` only affect the TypeScript Language Service (used by IDEs). They do **not** impact `tsc` or your build.* 108 | 109 | 🎉 Your codebase is now ready! 110 | 111 | ### ✅ Verify the Setup 112 | 113 | #### 🧑‍💻 In your IDE 114 | 115 | * Restart the IDE, or just the TS server. 116 | * Confirm that type errors now reflect the new `compilerOptions`. For example, when migrating to strict mode, verify that strict-specific errors appear. 117 | * Add `// @ts-migrating` before a line with an error — the error should disappear in the IDE. 118 | 119 | #### 🖥 In the terminal 120 | 121 | * Run: 122 | 123 | ```bash 124 | npx ts-migrating check 125 | ``` 126 | 127 | You should see errors from the new config, excluding those marked with `@ts-migrating`. 128 | 129 | ### ✨ Optional Next Steps 130 | 131 | * Run `npx ts-migrating annotate` to automatically annotate newly introduced errors with `// @ts-migrating`. 132 | * Replace your CI type-check step with `npx ts-migrating check` to prevent unreviewed errors from slipping through. 133 | 134 | ## API 135 | 136 | You can use this project programmatically. This can be useful if you would like to have custom integrations, for example: reporting error counts to dashboard etc. 137 | 138 | Currently there are only 2 functions exposed, [`getSemanticDiagnosticsForFile`](./src/api/getSemanticDiagnostics.ts) and [`isPluginDiagnostic`](./src/api/isPluginDiagnostic.ts). You can import them via `ts-migrating/api`, e.g. 139 | 140 | ```ts 141 | import { getSemanticDiagnosticsForFile, isPluginDiagnostic } from 'ts-migrating/api'; 142 | 143 | getSemanticDiagnosticsForFile('path/to/file.ts') // returns all diagnostics using your new tsconfig, including non-plugin ones 144 | .filter(isPluginDiagnostic) // removes all non-plugin diagnostics 145 | ``` 146 | 147 | You could technically also import from `ts-migrating/cli` and `ts-migrating` (the ts plugin itself) too. 148 | 149 | ## 📣 Shoutout 150 | 151 | This project wouldn't be possible without inspiration from: 152 | 153 | * [allegro/typescript-strict-plugin](https://github.com/allegro/typescript-strict-plugin): 154 | Especially for revealing the undocumented [`updateFromProject` option](https://github.com/allegro/typescript-strict-plugin/blob/master/src/plugin/utils.ts#L28-L32) which helped fix a critical issue with the standalone script. [You can find out more here](./src/plugin/mod.ts#L31)). 155 | 156 | ## 👤 Author 157 | 158 | YCM Jason 159 | -------------------------------------------------------------------------------- /src/api/insertSingleLineCommentsAtPositions.ts: -------------------------------------------------------------------------------- 1 | import * as recast from 'recast'; 2 | import * as babelTsParser from 'recast/parsers/babel-ts'; 3 | import { expect } from 'vitest'; 4 | import { maxBy } from './utils/maxBy'; 5 | 6 | const b = recast.types.builders; 7 | 8 | const getAbsolutePosition = ( 9 | code: string, 10 | { line, column }: recast.types.namedTypes.Position, 11 | ): number => { 12 | const lines = code.split(/(?<=\n)/); 13 | 14 | const lineIndex = line - 1; 15 | if (lineIndex < 0 || lineIndex >= lines.length) throw new RangeError('Invalid line number'); 16 | if (column < 0 || column > (lines[lineIndex]?.length ?? 0)) 17 | throw new RangeError('Invalid column number'); 18 | 19 | return lines.slice(0, lineIndex).reduce((acc, l) => acc + l.length, 0) + column; 20 | }; 21 | 22 | const getLineColumnPosition = ( 23 | code: string, 24 | absolutePosition: number, 25 | ): recast.types.namedTypes.Position => { 26 | if (absolutePosition < 0 || absolutePosition >= code.length) { 27 | throw new RangeError('Invalid position'); 28 | } 29 | 30 | const lines = code.split(/(?<=\n)/); 31 | 32 | let currentPosition = 0; 33 | for (const [i, line] of lines.entries()) { 34 | currentPosition += line.length; 35 | 36 | if (currentPosition > absolutePosition) { 37 | return { 38 | line: i + 1, 39 | column: line.length - (currentPosition - absolutePosition), 40 | }; 41 | } 42 | } 43 | throw new RangeError('Invalide position'); 44 | }; 45 | 46 | if (import.meta.vitest) { 47 | const { it, describe } = import.meta.vitest; 48 | 49 | describe('getAbsolutePosition / getLineColumnPosition', async () => { 50 | const ts = await import('typescript/lib/tsserverlibrary'); 51 | 52 | it('should return the absolute position and back', () => { 53 | const code = `const a = 'hi' 54 | const b = 'bye' 55 | hello()`; 56 | const sourcefile = ts.createSourceFile( 57 | 'tmp.ts', 58 | code, 59 | ts.ScriptTarget.Latest, 60 | true, 61 | ts.ScriptKind.TSX, 62 | ); 63 | const checkForIndex = (index: number) => { 64 | const { line, character } = ts.getLineAndCharacterOfPosition(sourcefile, index); 65 | expect(getAbsolutePosition(code, { line: line + 1, column: character })).toBe(index); 66 | expect(getLineColumnPosition(code, index)).toEqual({ line: line + 1, column: character }); 67 | }; 68 | 69 | checkForIndex(code.indexOf('=')); 70 | checkForIndex(code.indexOf('\n')); 71 | checkForIndex(code.indexOf('const b')); 72 | checkForIndex(code.indexOf('bye')); 73 | checkForIndex(code.indexOf('llo()')); 74 | }); 75 | 76 | it('should deal with empty line start correctly', () => { 77 | const code = '\nconst a = "hi"'; 78 | expect(getLineColumnPosition(code, 0)).toEqual({ line: 1, column: 0 }); 79 | }); 80 | }); 81 | } 82 | 83 | const extractIndent = (code: string): string => { 84 | return code.replace(/^([ \t]*).*/s, '$1'); 85 | }; 86 | 87 | type NaiveCommentType = 'jsx-naive' | 'vanilla-naive'; 88 | type CommentType = NaiveCommentType | 'vanilla-attach-to-node'; 89 | export const insertSingleLineCommentAtPositions = ( 90 | code: string, 91 | comment: string, 92 | positions: number[], 93 | ): string => { 94 | const ast = recast.parse(code, { parser: babelTsParser }); 95 | 96 | const linesToAdd = new Set(positions.map(position => getLineColumnPosition(code, position).line)); 97 | 98 | const naiveComments = new Map(); 99 | const addedLineNumbers = new Set(); 100 | const commentTypeStack: CommentType[] = ['vanilla-naive']; 101 | 102 | recast.types.visit(ast, { 103 | visitTemplateElement(path) { 104 | this.traverse(path); 105 | }, 106 | 107 | visitNode(path) { 108 | const { node } = path; 109 | 110 | if (linesToAdd.size <= 0) { 111 | return this.abort(); 112 | } 113 | 114 | if (!node.loc) { 115 | this.traverse(path); 116 | return; 117 | } 118 | 119 | const preVisits: (() => void)[] = []; 120 | const postVisits: (() => void)[] = []; 121 | 122 | const setChildrenCommentType = (type: CommentType) => { 123 | preVisits.push(() => { 124 | commentTypeStack.push(type); 125 | }); 126 | 127 | postVisits.push(() => { 128 | commentTypeStack.pop(); 129 | }); 130 | }; 131 | 132 | if ( 133 | recast.types.namedTypes.JSXElement.check(node) || 134 | recast.types.namedTypes.JSXFragment.check(node) 135 | ) { 136 | setChildrenCommentType('jsx-naive'); 137 | } 138 | 139 | if ( 140 | recast.types.namedTypes.JSXExpressionContainer.check(node) || 141 | recast.types.namedTypes.JSXOpeningElement.check(node) || 142 | recast.types.namedTypes.JSXClosingElement.check(node) 143 | ) { 144 | setChildrenCommentType('vanilla-naive'); 145 | } 146 | 147 | if (recast.types.namedTypes.TemplateLiteral.check(node)) { 148 | setChildrenCommentType('vanilla-attach-to-node'); 149 | } 150 | 151 | const commentType = commentTypeStack.at(-1); 152 | if (linesToAdd.has(node.loc.start.line)) { 153 | linesToAdd.delete(node.loc.start.line); 154 | switch (commentType) { 155 | case 'vanilla-attach-to-node': { 156 | addedLineNumbers.add(node.loc.start.line); 157 | node.comments = [ 158 | ...(node?.comments ?? []), 159 | b.commentLine.from({ 160 | value: ` ${comment}`, 161 | leading: true, 162 | }), 163 | ]; 164 | break; 165 | } 166 | case 'vanilla-naive': 167 | case 'jsx-naive': { 168 | naiveComments.set(node.loc.start.line, commentType); 169 | break; 170 | } 171 | } 172 | } 173 | 174 | for (const preVisit of preVisits) preVisit(); 175 | this.traverse(path); 176 | for (const postVisit of postVisits) postVisit(); 177 | }, 178 | }); 179 | 180 | const adjustLineNumber = (originalLineNumber: number): number => { 181 | // this represents the number of lines already added during ast traversal 182 | const offset = [...addedLineNumbers].filter( 183 | lineNumber => lineNumber <= originalLineNumber, 184 | ).length; 185 | return originalLineNumber + offset; 186 | }; 187 | 188 | const adjustedAddedLineNumbers = new Set([...addedLineNumbers].map(adjustLineNumber)); 189 | 190 | const adjustedNaiveComments = new Map( 191 | [...naiveComments].map(([lineNumber, commentType]) => [ 192 | adjustLineNumber(lineNumber), 193 | commentType, 194 | ]), 195 | ); 196 | 197 | const lines = recast.print(ast).code.split('\n'); 198 | return lines 199 | .flatMap((line, i) => { 200 | const lineNumber = i + 1; // +1 because line numbers are 1-index 201 | if (adjustedAddedLineNumbers.has(lineNumber)) return [line]; 202 | 203 | const commentType = adjustedNaiveComments.get(lineNumber); 204 | if (commentType === undefined) return [line]; 205 | 206 | const indent = maxBy( 207 | [extractIndent(line), extractIndent(lines[i - 1] ?? '')], 208 | ({ length }) => length, 209 | ); 210 | return [ 211 | { 212 | 'vanilla-naive': `${indent}// ${comment}`, 213 | 'jsx-naive': `${indent}{/* ${comment} */}`, 214 | }[commentType], 215 | line, 216 | ]; 217 | }) 218 | .join('\n'); 219 | }; 220 | 221 | if (import.meta.vitest) { 222 | const { it, describe } = import.meta.vitest; 223 | 224 | describe('insertSingleLineCommentsAtPositions', () => { 225 | it('should insert comment at given position', () => { 226 | expect( 227 | insertSingleLineCommentAtPositions(`f();\ng();\n\nconst a = 'hello';`, 'hello', [0]), 228 | ).toMatchInlineSnapshot(` 229 | "// hello 230 | f(); 231 | g(); 232 | 233 | const a = 'hello';" 234 | `); 235 | }); 236 | 237 | it('should only insert one comment if given multiple positions on the same line', () => { 238 | expect( 239 | insertSingleLineCommentAtPositions(`f();\ng();\n\nconst a = 'hello';`, 'hello', [0, 1, 2]), 240 | ).toMatchInlineSnapshot(` 241 | "// hello 242 | f(); 243 | g(); 244 | 245 | const a = 'hello';" 246 | `); 247 | }); 248 | }); 249 | } 250 | -------------------------------------------------------------------------------- /src/api/getSemanticDiagnostics.test.ts: -------------------------------------------------------------------------------- 1 | import { rmSync } from 'node:fs'; 2 | import { mkdir, writeFile } from 'node:fs/promises'; 3 | import { tmpdir } from 'node:os'; 4 | import { dirname, join } from 'node:path'; 5 | import { describe } from 'node:test'; 6 | import ts from 'typescript/lib/tsserverlibrary'; 7 | import { afterAll, expect, it } from 'vitest'; 8 | import { getSemanticDiagnosticsForFile } from './getSemanticDiagnostics'; 9 | import { isPluginDiagnostic } from './isPluginDiagnostic'; 10 | 11 | const getTmpDir = (() => { 12 | const TMP_DIR = join(tmpdir(), 'ts-migrating-test'); 13 | let i = 0; 14 | afterAll(() => { 15 | rmSync(TMP_DIR, { recursive: true }); 16 | }); 17 | 18 | return () => join(TMP_DIR, (i++).toString()); 19 | })(); 20 | 21 | const setupTmpDir = async (json: Record): Promise => { 22 | const tmpDir = getTmpDir(); 23 | for (const [relativePath, content] of Object.entries(json)) { 24 | const fullPath = join(tmpDir, relativePath); 25 | await mkdir(dirname(fullPath), { recursive: true }); 26 | await writeFile(fullPath, content, 'utf8'); 27 | } 28 | 29 | return tmpDir; 30 | }; 31 | 32 | describe('@ts-migrating directive', () => { 33 | it('should mark enum line as error without the @ts-migrating directive', async () => { 34 | const tmpDir = await setupTmpDir({ 35 | './tsconfig.json': JSON.stringify({ 36 | compilerOptions: { 37 | target: 'ES2020', 38 | module: 'commonjs', 39 | outDir: './dist', 40 | rootDir: './src', 41 | strict: false, 42 | esModuleInterop: true, 43 | forceConsistentCasingInFileNames: true, 44 | plugins: [ 45 | { 46 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 47 | // this is why we add `ts-migrating` as dev dependency of itself. 48 | name: 'ts-migrating', 49 | compilerOptions: { 50 | erasableSyntaxOnly: true, 51 | }, 52 | }, 53 | ], 54 | skipLibCheck: true, 55 | }, 56 | include: ['src'], 57 | exclude: ['node_modules', 'dist'], 58 | }), 59 | './src/index.ts': `enum FRUITS { 60 | APPLE, 61 | BANANA, 62 | KIWI, 63 | } 64 | `, 65 | }); 66 | 67 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'src/index.ts')); 68 | expect(diagnostics).toHaveLength(1); 69 | // biome-ignore lint/style/noNonNullAssertion: checked in previous assertion 70 | const diagnostic = diagnostics[0]!; 71 | expect(isPluginDiagnostic(diagnostic)).toBe(true); 72 | expect(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n', 2)).toMatchInlineSnapshot(` 73 | " 74 | [ts-migrating] 75 | This syntax is not allowed when 'erasableSyntaxOnly' is enabled." 76 | `); 77 | }); 78 | 79 | it('should allow enum if marked with @ts-migrate', async () => { 80 | const tmpDir = await setupTmpDir({ 81 | './tsconfig.json': JSON.stringify({ 82 | compilerOptions: { 83 | target: 'ES2020', 84 | module: 'commonjs', 85 | outDir: './dist', 86 | rootDir: './src', 87 | strict: false, 88 | esModuleInterop: true, 89 | forceConsistentCasingInFileNames: true, 90 | plugins: [ 91 | { 92 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 93 | // this is why we add `ts-migrating` as dev dependency of itself. 94 | name: 'ts-migrating', 95 | compilerOptions: { 96 | erasableSyntaxOnly: true, 97 | }, 98 | }, 99 | ], 100 | skipLibCheck: true, 101 | }, 102 | include: ['src'], 103 | exclude: ['node_modules', 'dist'], 104 | }), 105 | './src/index.ts': `// @ts-migrating 106 | enum FRUITS { 107 | APPLE, 108 | BANANA, 109 | KIWI, 110 | } 111 | `, 112 | }); 113 | 114 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'src/index.ts')); 115 | expect(diagnostics).toHaveLength(0); 116 | }); 117 | }); 118 | 119 | it('should report unused @ts-migrating', async () => { 120 | const tmpDir = await setupTmpDir({ 121 | './tsconfig.json': JSON.stringify({ 122 | compilerOptions: { 123 | target: 'ES2020', 124 | module: 'commonjs', 125 | outDir: './dist', 126 | rootDir: './src', 127 | strict: false, 128 | esModuleInterop: true, 129 | forceConsistentCasingInFileNames: true, 130 | plugins: [ 131 | { 132 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 133 | // this is why we add `ts-migrating` as dev dependency of itself. 134 | name: 'ts-migrating', 135 | compilerOptions: { 136 | erasableSyntaxOnly: true, 137 | }, 138 | }, 139 | ], 140 | skipLibCheck: true, 141 | }, 142 | include: ['src'], 143 | exclude: ['node_modules', 'dist'], 144 | }), 145 | './src/index.ts': `// @ts-migrating 146 | enum FRUITS { 147 | APPLE, 148 | BANANA, 149 | // @ts-migrating 150 | KIWI, 151 | } 152 | `, 153 | }); 154 | 155 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'src/index.ts')); 156 | expect(diagnostics).toHaveLength(1); 157 | // biome-ignore lint/style/noNonNullAssertion: checked in previous assertion 158 | const diagnostic = diagnostics[0]!; 159 | expect(isPluginDiagnostic(diagnostic)).toBe(true); 160 | expect(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')).toMatchInlineSnapshot(` 161 | "[ts-migrating] 162 | Unused '@ts-migrating' directive." 163 | `); 164 | }); 165 | 166 | describe('ignoring files', () => { 167 | it('should ignore ts files without any tsconfig that includes them', async () => { 168 | const tmpDir = await setupTmpDir({ 169 | './tsconfig.json': JSON.stringify({ 170 | compilerOptions: { 171 | target: 'ES2020', 172 | module: 'commonjs', 173 | outDir: './dist', 174 | rootDir: './src', 175 | strict: false, 176 | esModuleInterop: true, 177 | forceConsistentCasingInFileNames: true, 178 | plugins: [ 179 | { 180 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 181 | // this is why we add `ts-migrating` as dev dependency of itself. 182 | name: 'ts-migrating', 183 | compilerOptions: { 184 | erasableSyntaxOnly: true, 185 | }, 186 | }, 187 | ], 188 | skipLibCheck: true, 189 | }, 190 | include: ['.'], 191 | exclude: [ 192 | 'node_modules', 193 | 'dist', 194 | // excluding `ignored` directory!!! 195 | 'ignored', 196 | ], 197 | }), 198 | './src/index.ts': `// @ts-migrating 199 | enum FRUITS { 200 | APPLE, 201 | BANANA, 202 | // @ts-migrating 203 | KIWI, 204 | } 205 | `, 206 | './ignored/index.ts': 'laksdjflkj oiwejflaskdjf', 207 | }); 208 | 209 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'ignored/index.ts')); 210 | expect(diagnostics).toHaveLength(0); 211 | }); 212 | 213 | it('should ignore projects without `ts-migrating` plugin', async () => { 214 | const tmpDir = await setupTmpDir({ 215 | './tsconfig.json': JSON.stringify({ 216 | compilerOptions: { 217 | target: 'ES2020', 218 | module: 'commonjs', 219 | outDir: './dist', 220 | rootDir: './src', 221 | strict: false, 222 | esModuleInterop: true, 223 | forceConsistentCasingInFileNames: true, 224 | plugins: [ 225 | // no plugin! 226 | ], 227 | skipLibCheck: true, 228 | }, 229 | include: ['.'], 230 | exclude: [ 231 | 'node_modules', 232 | 'dist', 233 | // excluding `ignored` directory!!! 234 | 'ignored', 235 | ], 236 | }), 237 | './src/index.ts': `// @ts-migrating 238 | enum FRUITS { 239 | APPLE, 240 | BANANA, 241 | // @ts-migrating 242 | KIWI, 243 | } 244 | `, 245 | }); 246 | 247 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'src/index.ts')); 248 | expect(diagnostics).toHaveLength(0); 249 | }); 250 | 251 | it('should ignore comments below', async () => { 252 | const tmpDir = await setupTmpDir({ 253 | './tsconfig.json': JSON.stringify({ 254 | compilerOptions: { 255 | target: 'ES2020', 256 | module: 'commonjs', 257 | outDir: './dist', 258 | rootDir: './src', 259 | strict: false, 260 | esModuleInterop: true, 261 | forceConsistentCasingInFileNames: true, 262 | plugins: [ 263 | { 264 | // ts will look up from the node_modules that the ts server is running from. e.g. ../../node_modules/ts-migrating 265 | // this is why we add `ts-migrating` as dev dependency of itself. 266 | name: 'ts-migrating', 267 | compilerOptions: { 268 | erasableSyntaxOnly: true, 269 | }, 270 | }, 271 | ], 272 | skipLibCheck: true, 273 | }, 274 | include: ['src'], 275 | exclude: ['node_modules', 'dist'], 276 | }), 277 | './src/index.ts': `// @ts-migrating 278 | // whatever here 279 | enum FRUITS { 280 | APPLE, 281 | BANANA, 282 | KIWI, 283 | } 284 | `, 285 | }); 286 | 287 | const diagnostics = getSemanticDiagnosticsForFile(join(tmpDir, 'src/index.ts')); 288 | expect(diagnostics).toHaveLength(0); 289 | }); 290 | }); 291 | -------------------------------------------------------------------------------- /src/cli/commands/annotate.ts: -------------------------------------------------------------------------------- 1 | /** biome-ignore-all lint/suspicious/noTemplateCurlyInString: need to store template curly */ 2 | import fs from 'node:fs/promises'; 3 | import type ts from 'typescript/lib/tsserverlibrary'; 4 | import { getSemanticDiagnosticsForFile } from '../../api/getSemanticDiagnostics'; 5 | import { insertSingleLineCommentAtPositions } from '../../api/insertSingleLineCommentsAtPositions'; 6 | import { isPluginDiagnostic } from '../../api/isPluginDiagnostic'; 7 | import { DIRECTIVE } from '../../plugin/constants/DIRECTIVE'; 8 | import { UNUSED_DIRECTIVE_DIAGNOSTIC_CODE } from '../../plugin/constants/UNUSED_DIRECTIVE_DIAGNOSTIC_CODE'; 9 | import { ANNOTATE_WARNING } from '../constants/annotate-warning'; 10 | import { getPluginEnabledTSFilePaths } from '../ops/getPluginEnabledTSFilePaths'; 11 | 12 | const annotateDiagnostics = (source: string, diagnostics: readonly ts.Diagnostic[]): string => { 13 | return insertSingleLineCommentAtPositions( 14 | source, 15 | DIRECTIVE, 16 | diagnostics 17 | .map(diagnostic => diagnostic.start) 18 | // filter last because of how typescript works :( 19 | .filter(s => s !== undefined), 20 | ); 21 | }; 22 | 23 | if (import.meta.vitest) { 24 | const { describe, it, expect } = import.meta.vitest; 25 | 26 | describe('annotateDiagnostics', async () => { 27 | const ts = await import('typescript/lib/tsserverlibrary'); 28 | 29 | const createDiagnosticAtPosition = (pos: number): ts.Diagnostic => ({ 30 | start: pos, 31 | category: ts.DiagnosticCategory.Warning, 32 | code: 0, 33 | file: undefined, 34 | length: undefined, 35 | messageText: '', 36 | }); 37 | 38 | it('should add @ts-migrating before the first line', () => { 39 | expect( 40 | annotateDiagnostics(`const a = 'hello';`, [createDiagnosticAtPosition(0)]), 41 | ).toMatchInlineSnapshot(` 42 | "// @ts-migrating 43 | const a = 'hello';" 44 | `); 45 | }); 46 | 47 | it('should add @ts-migrating before the string', () => { 48 | expect( 49 | annotateDiagnostics(`const a = 'hello';`, [createDiagnosticAtPosition(10)]), 50 | ).toMatchInlineSnapshot(` 51 | "// @ts-migrating 52 | const a = 'hello';" 53 | `); 54 | }); 55 | 56 | it('should add @ts-migrating before the first line and the string', () => { 57 | const code = `const a = 'hello';`; 58 | expect( 59 | annotateDiagnostics(code, [ 60 | createDiagnosticAtPosition(0), 61 | createDiagnosticAtPosition(code.indexOf("'")), 62 | ]), 63 | ).toMatchInlineSnapshot(` 64 | "// @ts-migrating 65 | const a = 'hello';" 66 | `); 67 | }); 68 | 69 | describe('jsx', () => { 70 | it('should add @ts-migrating correctly for jsx one-liner', () => { 71 | const code = 'const a =
;'; 72 | expect( 73 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('hi'))]), 74 | ).toMatchInlineSnapshot(` 75 | "// @ts-migrating 76 | const a =
;" 77 | `); 78 | }); 79 | 80 | it('should add @ts-migrating correctly for jsx attributes', () => { 81 | const code = 'const a = \n;'; 82 | expect( 83 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('hi'))]), 84 | ).toMatchInlineSnapshot(` 85 | "const a =
88 |
;" 89 | `); 90 | }); 91 | 92 | it('should add @ts-migrating correctly for jsx expressions', () => { 93 | const code = 'const a =
\n {hi}\n
;'; 94 | expect( 95 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('hi'))]), 96 | ).toMatchInlineSnapshot(` 97 | "const a =
98 | {/* @ts-migrating */} 99 | {hi} 100 |
;" 101 | `); 102 | }); 103 | 104 | it('should add @ts-migrating correctly for jsx fragment', () => { 105 | const code = 'const a = <>\n {hi}\n;'; 106 | expect( 107 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('hi'))]), 108 | ).toMatchInlineSnapshot(` 109 | "const a = <> 110 | {/* @ts-migrating */} 111 | {hi} 112 | ;" 113 | `); 114 | }); 115 | 116 | it('should add @ts-migrating correctly for jsx expressions with attributes', () => { 117 | const code = 'const a = \n
{hi}
\n; const b = 3'; 118 | expect( 119 | annotateDiagnostics(code, [ 120 | createDiagnosticAtPosition(code.indexOf('hi')), 121 | createDiagnosticAtPosition(code.indexOf('yo')), 122 | ]), 123 | ).toMatchInlineSnapshot(` 124 | "const a =
127 | {/* @ts-migrating */} 128 |
{hi}
129 |
; const b = 3" 130 | `); 131 | }); 132 | 133 | it('should add @ts-migrating correctly for iife within jsx', () => { 134 | const code = `const a = 140 | `; 141 | expect( 142 | annotateDiagnostics(code, [ 143 | createDiagnosticAtPosition(code.indexOf('f')), 144 | createDiagnosticAtPosition(code.indexOf('g')), 145 | ]), 146 | ).toMatchInlineSnapshot(` 147 | "const a = 155 | " 156 | `); 157 | }); 158 | }); 159 | 160 | describe('enum', () => { 161 | it('should add @ts-migrating correctly for enum', () => { 162 | const code = 'enum A { }'; 163 | expect( 164 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('A'))]), 165 | ).toMatchInlineSnapshot(` 166 | "// @ts-migrating 167 | enum A { }" 168 | `); 169 | }); 170 | 171 | it('should add @ts-migrating correctly for weird formatted enum', () => { 172 | const code = 'enum\nA { }'; 173 | expect( 174 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('A'))]), 175 | ).toMatchInlineSnapshot(` 176 | "enum 177 | // @ts-migrating 178 | A { }" 179 | `); 180 | }); 181 | }); 182 | 183 | describe('string template', () => { 184 | it('should add @ts-migrating correctly for string template', () => { 185 | const code = 'const message = `I am\n freaking ${awesome}.`;'; 186 | expect( 187 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('awesome'))]), 188 | ).toMatchInlineSnapshot(` 189 | "const message = \`I am 190 | freaking \${// @ts-migrating 191 | awesome}.\`;" 192 | `); 193 | }); 194 | 195 | it('should add @ts-migrating correctly for string template with multiple errors', () => { 196 | const code = 197 | 'const message = `I am\n freaking ${awesome}, ${cool}\n, ${fantastic}, \n${lovely}.`;'; 198 | expect( 199 | annotateDiagnostics(code, [ 200 | createDiagnosticAtPosition(code.indexOf('awesome')), 201 | createDiagnosticAtPosition(code.indexOf('cool')), 202 | createDiagnosticAtPosition(code.indexOf('fantastic')), 203 | createDiagnosticAtPosition(code.indexOf('lovely')), 204 | ]), 205 | ).toMatchInlineSnapshot(` 206 | "const message = \`I am 207 | freaking \${// @ts-migrating 208 | awesome}, \${cool} 209 | , \${// @ts-migrating 210 | fantastic}, 211 | \${// @ts-migrating 212 | lovely}.\`;" 213 | `); 214 | }); 215 | 216 | it('should add @ts-migrating correctly for naive lines after string template', () => { 217 | const code = 'const x = `I am ${cool}\n${awesome}`;\nf();\ng();'; 218 | expect( 219 | annotateDiagnostics(code, [ 220 | createDiagnosticAtPosition(code.indexOf('cool')), 221 | createDiagnosticAtPosition(code.indexOf('awesome')), 222 | createDiagnosticAtPosition(code.indexOf('f')), 223 | createDiagnosticAtPosition(code.indexOf('g')), 224 | ]), 225 | ).toMatchInlineSnapshot(` 226 | "// @ts-migrating 227 | const x = \`I am \${cool} 228 | \${// @ts-migrating 229 | awesome}\`; 230 | // @ts-migrating 231 | f(); 232 | // @ts-migrating 233 | g();" 234 | `); 235 | }); 236 | 237 | // https://github.com/ycmjason/ts-migrating/issues/2 238 | it('issue #2', () => { 239 | const code = `const x = z\`I am \${cool} 240 | \${(awesome) => { 241 | f(); 242 | }}\`; 243 | g();`; 244 | expect( 245 | annotateDiagnostics(code, [ 246 | createDiagnosticAtPosition(code.indexOf('cool')), 247 | createDiagnosticAtPosition(code.indexOf('awesome')), 248 | createDiagnosticAtPosition(code.indexOf('f')), 249 | createDiagnosticAtPosition(code.indexOf('g')), 250 | ]), 251 | ).toMatchInlineSnapshot(` 252 | "// @ts-migrating 253 | const x = z\`I am \${cool} 254 | \${// @ts-migrating 255 | awesome => { 256 | // @ts-migrating 257 | f(); 258 | }}\`; 259 | // @ts-migrating 260 | g();" 261 | `); 262 | }); 263 | }); 264 | 265 | it('should add @ts-migrating comment last', () => { 266 | const code = '// hello world\nconst x = 3;'; 267 | expect( 268 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('x'))]), 269 | ).toMatchInlineSnapshot(` 270 | "// hello world 271 | // @ts-migrating 272 | const x = 3;" 273 | `); 274 | }); 275 | 276 | it('should add @ts-migrating correctly for object computed property name', () => { 277 | const code = 'const obj = {\n [a]: 3,\n}'; 278 | expect( 279 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('a'))]), 280 | ).toMatchInlineSnapshot(` 281 | "const obj = { 282 | // @ts-migrating 283 | [a]: 3, 284 | }" 285 | `); 286 | }); 287 | 288 | describe('if-else', () => { 289 | it('should add @ts-migrating correctly for else if', () => { 290 | const code = `if (a) { 291 | f(); 292 | } else if (b) { 293 | } else { 294 | }`; 295 | expect( 296 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('b'))]), 297 | ).toMatchInlineSnapshot(` 298 | "if (a) { 299 | f(); 300 | // @ts-migrating 301 | } else if (b) { 302 | } else { 303 | }" 304 | `); 305 | }); 306 | 307 | it('should add @ts-migrating correctly for else if', () => { 308 | const code = `if (a) { 309 | f(); 310 | } 311 | else if (b) { 312 | } else { 313 | }`; 314 | expect( 315 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('b'))]), 316 | ).toMatchInlineSnapshot(` 317 | "if (a) { 318 | f(); 319 | } 320 | // @ts-migrating 321 | else if (b) { 322 | } else { 323 | }" 324 | `); 325 | }); 326 | }); 327 | 328 | describe('for-loop', () => { 329 | it('should add @ts-migrating correctly for for-loops', () => { 330 | const code = `for (let i = 0; i < callbacks.length; i += 1) { 331 | callbacks[i](event); 332 | }`; 333 | 334 | expect( 335 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('callbacks[i]'))]), 336 | ).toMatchInlineSnapshot(` 337 | "for (let i = 0; i < callbacks.length; i += 1) { 338 | // @ts-migrating 339 | callbacks[i](event); 340 | }" 341 | `); 342 | }); 343 | }); 344 | 345 | it('should deal with chains correctly', () => { 346 | const code = `const panel1ScrollHeight = jest 347 | .spyOn(panel1, "scrollHeight", "get");`; 348 | expect( 349 | annotateDiagnostics(code, [createDiagnosticAtPosition(code.indexOf('spyOn'))]), 350 | ).toMatchInlineSnapshot(` 351 | "const panel1ScrollHeight = jest 352 | // @ts-migrating 353 | .spyOn(panel1, "scrollHeight", "get");" 354 | `); 355 | }); 356 | 357 | // Currently failing due to: https://github.com/benjamn/recast/issues/1423 358 | it.skip('should annotate correctly for weird formatted ternary', () => { 359 | // failed case from https://github.com/bluesky-social/social-app/blob/fd37d92f85ddf0f075a67c4e9b2d85bef38f1835/src/components/KnownFollowers.tsx#L177-L245 360 | const code = `const a = 361 | {slice.length >= 2 ? ( 362 | // 2-n followers, including blocks 363 | serverCount > 2 ? ( 364 | 365 | Followed by{' '} 366 | 367 | {bbb.profile.displayName} 368 | 369 | ,{' '} 370 | 371 | {ddd.profile.displayName} 372 | 373 | , and{' '} 374 | 379 | 380 | ) : ( 381 | // only 2 382 | 383 | Followed by{' '} 384 | 385 | {fff.profile.displayName} 386 | {' '} 387 | and{' '} 388 | 389 | {hhh.profile.displayName} 390 | 391 | 392 | ) 393 | ) : serverCount > 1 ? ( 394 | // 1-n followers, including blocks 395 | 396 | Followed by{' '} 397 | 398 | {jjj.profile.displayName} 399 | {' '} 400 | and{' '} 401 | 406 | 407 | ) : ( 408 | // only 1 409 | 410 | Followed by{' '} 411 | 412 | {mmm.profile.displayName} 413 | 414 | 415 | )} 416 | `; 417 | expect( 418 | annotateDiagnostics(code, [ 419 | createDiagnosticAtPosition(code.indexOf('aaa')), 420 | createDiagnosticAtPosition(code.indexOf('bbb')), 421 | createDiagnosticAtPosition(code.indexOf('ccc')), 422 | createDiagnosticAtPosition(code.indexOf('ddd')), 423 | createDiagnosticAtPosition(code.indexOf('eee')), 424 | createDiagnosticAtPosition(code.indexOf('fff')), 425 | createDiagnosticAtPosition(code.indexOf('ggg')), 426 | createDiagnosticAtPosition(code.indexOf('hhh')), 427 | createDiagnosticAtPosition(code.indexOf('iii')), 428 | createDiagnosticAtPosition(code.indexOf('jjj')), 429 | createDiagnosticAtPosition(code.indexOf('lll')), 430 | createDiagnosticAtPosition(code.indexOf('mmm')), 431 | ]), 432 | ).toMatchInlineSnapshot(` 433 | "const a = 434 | {slice.length >= 2 ? ( 435 | // 2-n followers, including blocks 436 | serverCount > 2 ? ( 437 | 438 | Followed by{' '} 439 | {/* @ts-migrating */} 440 | 441 | {/* @ts-migrating */} 442 | {bbb.profile.displayName} 443 | 444 | ,{' '} 445 | {/* @ts-migrating */} 446 | 447 | {/* @ts-migrating */} 448 | {ddd.profile.displayName} 449 | 450 | , and{' '} 451 | 456 | 457 | ) : ( 458 | // only 2 459 | 460 | Followed by{' '} 461 | {/* @ts-migrating */} 462 | 463 | {/* @ts-migrating */} 464 | {fff.profile.displayName} 465 | {' '} 466 | and{' '} 467 | {/* @ts-migrating */} 468 | 469 | {/* @ts-migrating */} 470 | {hhh.profile.displayName} 471 | 472 | 473 | ) 474 | ) : serverCount > 1 ? ( 475 | // 1-n followers, including blocks 476 | 477 | Followed by{' '} 478 | {/* @ts-migrating */} 479 | 480 | {/* @ts-migrating */} 481 | {jjj.profile.displayName} 482 | {' '} 483 | and{' '} 484 | 489 | 490 | ) : ( 491 | // only 1 492 | 493 | Followed by{' '} 494 | {/* @ts-migrating */} 495 | 496 | {/* @ts-migrating */} 497 | {mmm.profile.displayName} 498 | 499 | 500 | )} 501 | " 502 | `); 503 | }); 504 | }); 505 | } 506 | 507 | export const annotate = async ({ verbose }: { verbose: boolean }, ...inputPaths: string[]) => { 508 | const files = getPluginEnabledTSFilePaths(inputPaths, { verbose }); 509 | 510 | console.log(ANNOTATE_WARNING); 511 | 512 | console.log('⏳ Gathering errors introduced in your new tsconfig. This may take a while...'); 513 | 514 | console.time('Type checking'); 515 | const filePathAndPluginDiagnostics = files.map(filePath => ({ 516 | filePath, 517 | diagnostics: getSemanticDiagnosticsForFile(filePath).filter( 518 | d => isPluginDiagnostic(d) && d.code !== UNUSED_DIRECTIVE_DIAGNOSTIC_CODE, 519 | ), 520 | })); 521 | console.timeEnd('Type checking'); 522 | 523 | console.time('Annotation'); 524 | await Promise.all( 525 | filePathAndPluginDiagnostics.map(async ({ filePath, diagnostics }) => { 526 | await fs.writeFile( 527 | filePath, 528 | annotateDiagnostics(await fs.readFile(filePath, 'utf8'), diagnostics), 529 | ); 530 | console.log( 531 | `✅ Annotated ${filePath} (${diagnostics.length} directive${diagnostics.length === 1 ? '' : 's'} added)`, 532 | ); 533 | }), 534 | ); 535 | console.timeEnd('Annotation'); 536 | 537 | console.log( 538 | '✨ Annotation complete! Remember to run your formatter / linter, and potentially this command again to fully annotate all errors.', 539 | ); 540 | }; 541 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@babel/parser': 12 | specifier: ^7.28.3 13 | version: 7.28.3 14 | '@stricli/core': 15 | specifier: ^1.2.0 16 | version: 1.2.0 17 | recast: 18 | specifier: ^0.23.11 19 | version: 0.23.11 20 | tinyglobby: 21 | specifier: ^0.2.14 22 | version: 0.2.14 23 | devDependencies: 24 | '@biomejs/biome': 25 | specifier: ^2.2.2 26 | version: 2.2.2 27 | '@types/node': 28 | specifier: ^24.3.1 29 | version: 24.3.1 30 | ts-migrating: 31 | specifier: 'link:' 32 | version: 'link:' 33 | tsup: 34 | specifier: ^8.5.0 35 | version: 8.5.0(postcss@8.5.6)(typescript@5.9.2) 36 | typescript: 37 | specifier: ^5.9.2 38 | version: 5.9.2 39 | vitest: 40 | specifier: ^3.2.4 41 | version: 3.2.4(@types/node@24.3.1) 42 | 43 | packages: 44 | 45 | '@babel/helper-string-parser@7.27.1': 46 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 47 | engines: {node: '>=6.9.0'} 48 | 49 | '@babel/helper-validator-identifier@7.27.1': 50 | resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 51 | engines: {node: '>=6.9.0'} 52 | 53 | '@babel/parser@7.28.3': 54 | resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} 55 | engines: {node: '>=6.0.0'} 56 | hasBin: true 57 | 58 | '@babel/types@7.28.2': 59 | resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} 60 | engines: {node: '>=6.9.0'} 61 | 62 | '@biomejs/biome@2.2.2': 63 | resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} 64 | engines: {node: '>=14.21.3'} 65 | hasBin: true 66 | 67 | '@biomejs/cli-darwin-arm64@2.2.2': 68 | resolution: {integrity: sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==} 69 | engines: {node: '>=14.21.3'} 70 | cpu: [arm64] 71 | os: [darwin] 72 | 73 | '@biomejs/cli-darwin-x64@2.2.2': 74 | resolution: {integrity: sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==} 75 | engines: {node: '>=14.21.3'} 76 | cpu: [x64] 77 | os: [darwin] 78 | 79 | '@biomejs/cli-linux-arm64-musl@2.2.2': 80 | resolution: {integrity: sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==} 81 | engines: {node: '>=14.21.3'} 82 | cpu: [arm64] 83 | os: [linux] 84 | 85 | '@biomejs/cli-linux-arm64@2.2.2': 86 | resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} 87 | engines: {node: '>=14.21.3'} 88 | cpu: [arm64] 89 | os: [linux] 90 | 91 | '@biomejs/cli-linux-x64-musl@2.2.2': 92 | resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} 93 | engines: {node: '>=14.21.3'} 94 | cpu: [x64] 95 | os: [linux] 96 | 97 | '@biomejs/cli-linux-x64@2.2.2': 98 | resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} 99 | engines: {node: '>=14.21.3'} 100 | cpu: [x64] 101 | os: [linux] 102 | 103 | '@biomejs/cli-win32-arm64@2.2.2': 104 | resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} 105 | engines: {node: '>=14.21.3'} 106 | cpu: [arm64] 107 | os: [win32] 108 | 109 | '@biomejs/cli-win32-x64@2.2.2': 110 | resolution: {integrity: sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==} 111 | engines: {node: '>=14.21.3'} 112 | cpu: [x64] 113 | os: [win32] 114 | 115 | '@esbuild/aix-ppc64@0.25.9': 116 | resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} 117 | engines: {node: '>=18'} 118 | cpu: [ppc64] 119 | os: [aix] 120 | 121 | '@esbuild/android-arm64@0.25.9': 122 | resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} 123 | engines: {node: '>=18'} 124 | cpu: [arm64] 125 | os: [android] 126 | 127 | '@esbuild/android-arm@0.25.9': 128 | resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} 129 | engines: {node: '>=18'} 130 | cpu: [arm] 131 | os: [android] 132 | 133 | '@esbuild/android-x64@0.25.9': 134 | resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} 135 | engines: {node: '>=18'} 136 | cpu: [x64] 137 | os: [android] 138 | 139 | '@esbuild/darwin-arm64@0.25.9': 140 | resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} 141 | engines: {node: '>=18'} 142 | cpu: [arm64] 143 | os: [darwin] 144 | 145 | '@esbuild/darwin-x64@0.25.9': 146 | resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} 147 | engines: {node: '>=18'} 148 | cpu: [x64] 149 | os: [darwin] 150 | 151 | '@esbuild/freebsd-arm64@0.25.9': 152 | resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} 153 | engines: {node: '>=18'} 154 | cpu: [arm64] 155 | os: [freebsd] 156 | 157 | '@esbuild/freebsd-x64@0.25.9': 158 | resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} 159 | engines: {node: '>=18'} 160 | cpu: [x64] 161 | os: [freebsd] 162 | 163 | '@esbuild/linux-arm64@0.25.9': 164 | resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} 165 | engines: {node: '>=18'} 166 | cpu: [arm64] 167 | os: [linux] 168 | 169 | '@esbuild/linux-arm@0.25.9': 170 | resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} 171 | engines: {node: '>=18'} 172 | cpu: [arm] 173 | os: [linux] 174 | 175 | '@esbuild/linux-ia32@0.25.9': 176 | resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} 177 | engines: {node: '>=18'} 178 | cpu: [ia32] 179 | os: [linux] 180 | 181 | '@esbuild/linux-loong64@0.25.9': 182 | resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} 183 | engines: {node: '>=18'} 184 | cpu: [loong64] 185 | os: [linux] 186 | 187 | '@esbuild/linux-mips64el@0.25.9': 188 | resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} 189 | engines: {node: '>=18'} 190 | cpu: [mips64el] 191 | os: [linux] 192 | 193 | '@esbuild/linux-ppc64@0.25.9': 194 | resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} 195 | engines: {node: '>=18'} 196 | cpu: [ppc64] 197 | os: [linux] 198 | 199 | '@esbuild/linux-riscv64@0.25.9': 200 | resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} 201 | engines: {node: '>=18'} 202 | cpu: [riscv64] 203 | os: [linux] 204 | 205 | '@esbuild/linux-s390x@0.25.9': 206 | resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} 207 | engines: {node: '>=18'} 208 | cpu: [s390x] 209 | os: [linux] 210 | 211 | '@esbuild/linux-x64@0.25.9': 212 | resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} 213 | engines: {node: '>=18'} 214 | cpu: [x64] 215 | os: [linux] 216 | 217 | '@esbuild/netbsd-arm64@0.25.9': 218 | resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} 219 | engines: {node: '>=18'} 220 | cpu: [arm64] 221 | os: [netbsd] 222 | 223 | '@esbuild/netbsd-x64@0.25.9': 224 | resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} 225 | engines: {node: '>=18'} 226 | cpu: [x64] 227 | os: [netbsd] 228 | 229 | '@esbuild/openbsd-arm64@0.25.9': 230 | resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} 231 | engines: {node: '>=18'} 232 | cpu: [arm64] 233 | os: [openbsd] 234 | 235 | '@esbuild/openbsd-x64@0.25.9': 236 | resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} 237 | engines: {node: '>=18'} 238 | cpu: [x64] 239 | os: [openbsd] 240 | 241 | '@esbuild/openharmony-arm64@0.25.9': 242 | resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} 243 | engines: {node: '>=18'} 244 | cpu: [arm64] 245 | os: [openharmony] 246 | 247 | '@esbuild/sunos-x64@0.25.9': 248 | resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} 249 | engines: {node: '>=18'} 250 | cpu: [x64] 251 | os: [sunos] 252 | 253 | '@esbuild/win32-arm64@0.25.9': 254 | resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} 255 | engines: {node: '>=18'} 256 | cpu: [arm64] 257 | os: [win32] 258 | 259 | '@esbuild/win32-ia32@0.25.9': 260 | resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} 261 | engines: {node: '>=18'} 262 | cpu: [ia32] 263 | os: [win32] 264 | 265 | '@esbuild/win32-x64@0.25.9': 266 | resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} 267 | engines: {node: '>=18'} 268 | cpu: [x64] 269 | os: [win32] 270 | 271 | '@isaacs/cliui@8.0.2': 272 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 273 | engines: {node: '>=12'} 274 | 275 | '@jridgewell/gen-mapping@0.3.13': 276 | resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} 277 | 278 | '@jridgewell/resolve-uri@3.1.2': 279 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 280 | engines: {node: '>=6.0.0'} 281 | 282 | '@jridgewell/sourcemap-codec@1.5.5': 283 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 284 | 285 | '@jridgewell/trace-mapping@0.3.30': 286 | resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} 287 | 288 | '@pkgjs/parseargs@0.11.0': 289 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 290 | engines: {node: '>=14'} 291 | 292 | '@rollup/rollup-android-arm-eabi@4.50.0': 293 | resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} 294 | cpu: [arm] 295 | os: [android] 296 | 297 | '@rollup/rollup-android-arm64@4.50.0': 298 | resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==} 299 | cpu: [arm64] 300 | os: [android] 301 | 302 | '@rollup/rollup-darwin-arm64@4.50.0': 303 | resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==} 304 | cpu: [arm64] 305 | os: [darwin] 306 | 307 | '@rollup/rollup-darwin-x64@4.50.0': 308 | resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==} 309 | cpu: [x64] 310 | os: [darwin] 311 | 312 | '@rollup/rollup-freebsd-arm64@4.50.0': 313 | resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==} 314 | cpu: [arm64] 315 | os: [freebsd] 316 | 317 | '@rollup/rollup-freebsd-x64@4.50.0': 318 | resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==} 319 | cpu: [x64] 320 | os: [freebsd] 321 | 322 | '@rollup/rollup-linux-arm-gnueabihf@4.50.0': 323 | resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==} 324 | cpu: [arm] 325 | os: [linux] 326 | 327 | '@rollup/rollup-linux-arm-musleabihf@4.50.0': 328 | resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==} 329 | cpu: [arm] 330 | os: [linux] 331 | 332 | '@rollup/rollup-linux-arm64-gnu@4.50.0': 333 | resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==} 334 | cpu: [arm64] 335 | os: [linux] 336 | 337 | '@rollup/rollup-linux-arm64-musl@4.50.0': 338 | resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==} 339 | cpu: [arm64] 340 | os: [linux] 341 | 342 | '@rollup/rollup-linux-loongarch64-gnu@4.50.0': 343 | resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==} 344 | cpu: [loong64] 345 | os: [linux] 346 | 347 | '@rollup/rollup-linux-ppc64-gnu@4.50.0': 348 | resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==} 349 | cpu: [ppc64] 350 | os: [linux] 351 | 352 | '@rollup/rollup-linux-riscv64-gnu@4.50.0': 353 | resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==} 354 | cpu: [riscv64] 355 | os: [linux] 356 | 357 | '@rollup/rollup-linux-riscv64-musl@4.50.0': 358 | resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==} 359 | cpu: [riscv64] 360 | os: [linux] 361 | 362 | '@rollup/rollup-linux-s390x-gnu@4.50.0': 363 | resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==} 364 | cpu: [s390x] 365 | os: [linux] 366 | 367 | '@rollup/rollup-linux-x64-gnu@4.50.0': 368 | resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==} 369 | cpu: [x64] 370 | os: [linux] 371 | 372 | '@rollup/rollup-linux-x64-musl@4.50.0': 373 | resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==} 374 | cpu: [x64] 375 | os: [linux] 376 | 377 | '@rollup/rollup-openharmony-arm64@4.50.0': 378 | resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==} 379 | cpu: [arm64] 380 | os: [openharmony] 381 | 382 | '@rollup/rollup-win32-arm64-msvc@4.50.0': 383 | resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==} 384 | cpu: [arm64] 385 | os: [win32] 386 | 387 | '@rollup/rollup-win32-ia32-msvc@4.50.0': 388 | resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==} 389 | cpu: [ia32] 390 | os: [win32] 391 | 392 | '@rollup/rollup-win32-x64-msvc@4.50.0': 393 | resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==} 394 | cpu: [x64] 395 | os: [win32] 396 | 397 | '@stricli/core@1.2.0': 398 | resolution: {integrity: sha512-5b+npntDY0TAB7wAw0daGlh3/R2sf0TDLyrB1By2jCNH+C+lmcSqMtJXOMLVtEGSkIOvqAgIWpLMSs1PXqzt3w==} 399 | 400 | '@types/chai@5.2.2': 401 | resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} 402 | 403 | '@types/deep-eql@4.0.2': 404 | resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} 405 | 406 | '@types/estree@1.0.8': 407 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 408 | 409 | '@types/node@24.3.1': 410 | resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} 411 | 412 | '@vitest/expect@3.2.4': 413 | resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} 414 | 415 | '@vitest/mocker@3.2.4': 416 | resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} 417 | peerDependencies: 418 | msw: ^2.4.9 419 | vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 420 | peerDependenciesMeta: 421 | msw: 422 | optional: true 423 | vite: 424 | optional: true 425 | 426 | '@vitest/pretty-format@3.2.4': 427 | resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} 428 | 429 | '@vitest/runner@3.2.4': 430 | resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} 431 | 432 | '@vitest/snapshot@3.2.4': 433 | resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} 434 | 435 | '@vitest/spy@3.2.4': 436 | resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} 437 | 438 | '@vitest/utils@3.2.4': 439 | resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} 440 | 441 | acorn@8.15.0: 442 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 443 | engines: {node: '>=0.4.0'} 444 | hasBin: true 445 | 446 | ansi-regex@5.0.1: 447 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 448 | engines: {node: '>=8'} 449 | 450 | ansi-regex@6.2.0: 451 | resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} 452 | engines: {node: '>=12'} 453 | 454 | ansi-styles@4.3.0: 455 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 456 | engines: {node: '>=8'} 457 | 458 | ansi-styles@6.2.1: 459 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 460 | engines: {node: '>=12'} 461 | 462 | any-promise@1.3.0: 463 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 464 | 465 | assertion-error@2.0.1: 466 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 467 | engines: {node: '>=12'} 468 | 469 | ast-types@0.16.1: 470 | resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} 471 | engines: {node: '>=4'} 472 | 473 | balanced-match@1.0.2: 474 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 475 | 476 | brace-expansion@2.0.2: 477 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 478 | 479 | bundle-require@5.1.0: 480 | resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} 481 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 482 | peerDependencies: 483 | esbuild: '>=0.18' 484 | 485 | cac@6.7.14: 486 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 487 | engines: {node: '>=8'} 488 | 489 | chai@5.3.3: 490 | resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} 491 | engines: {node: '>=18'} 492 | 493 | check-error@2.1.1: 494 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 495 | engines: {node: '>= 16'} 496 | 497 | chokidar@4.0.3: 498 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 499 | engines: {node: '>= 14.16.0'} 500 | 501 | color-convert@2.0.1: 502 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 503 | engines: {node: '>=7.0.0'} 504 | 505 | color-name@1.1.4: 506 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 507 | 508 | commander@4.1.1: 509 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 510 | engines: {node: '>= 6'} 511 | 512 | confbox@0.1.8: 513 | resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} 514 | 515 | consola@3.4.2: 516 | resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} 517 | engines: {node: ^14.18.0 || >=16.10.0} 518 | 519 | cross-spawn@7.0.6: 520 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 521 | engines: {node: '>= 8'} 522 | 523 | debug@4.4.1: 524 | resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 525 | engines: {node: '>=6.0'} 526 | peerDependencies: 527 | supports-color: '*' 528 | peerDependenciesMeta: 529 | supports-color: 530 | optional: true 531 | 532 | deep-eql@5.0.2: 533 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 534 | engines: {node: '>=6'} 535 | 536 | eastasianwidth@0.2.0: 537 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 538 | 539 | emoji-regex@8.0.0: 540 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 541 | 542 | emoji-regex@9.2.2: 543 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 544 | 545 | es-module-lexer@1.7.0: 546 | resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} 547 | 548 | esbuild@0.25.9: 549 | resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} 550 | engines: {node: '>=18'} 551 | hasBin: true 552 | 553 | esprima@4.0.1: 554 | resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 555 | engines: {node: '>=4'} 556 | hasBin: true 557 | 558 | estree-walker@3.0.3: 559 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 560 | 561 | expect-type@1.2.2: 562 | resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} 563 | engines: {node: '>=12.0.0'} 564 | 565 | fdir@6.5.0: 566 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 567 | engines: {node: '>=12.0.0'} 568 | peerDependencies: 569 | picomatch: ^3 || ^4 570 | peerDependenciesMeta: 571 | picomatch: 572 | optional: true 573 | 574 | fix-dts-default-cjs-exports@1.0.1: 575 | resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} 576 | 577 | foreground-child@3.3.1: 578 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 579 | engines: {node: '>=14'} 580 | 581 | fsevents@2.3.3: 582 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 583 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 584 | os: [darwin] 585 | 586 | glob@10.4.5: 587 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 588 | hasBin: true 589 | 590 | is-fullwidth-code-point@3.0.0: 591 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 592 | engines: {node: '>=8'} 593 | 594 | isexe@2.0.0: 595 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 596 | 597 | jackspeak@3.4.3: 598 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 599 | 600 | joycon@3.1.1: 601 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 602 | engines: {node: '>=10'} 603 | 604 | js-tokens@9.0.1: 605 | resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} 606 | 607 | lilconfig@3.1.3: 608 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 609 | engines: {node: '>=14'} 610 | 611 | lines-and-columns@1.2.4: 612 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 613 | 614 | load-tsconfig@0.2.5: 615 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 616 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 617 | 618 | lodash.sortby@4.7.0: 619 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 620 | 621 | loupe@3.2.1: 622 | resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} 623 | 624 | lru-cache@10.4.3: 625 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 626 | 627 | magic-string@0.30.18: 628 | resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} 629 | 630 | minimatch@9.0.5: 631 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 632 | engines: {node: '>=16 || 14 >=14.17'} 633 | 634 | minipass@7.1.2: 635 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 636 | engines: {node: '>=16 || 14 >=14.17'} 637 | 638 | mlly@1.8.0: 639 | resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} 640 | 641 | ms@2.1.3: 642 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 643 | 644 | mz@2.7.0: 645 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 646 | 647 | nanoid@3.3.11: 648 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 649 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 650 | hasBin: true 651 | 652 | object-assign@4.1.1: 653 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 654 | engines: {node: '>=0.10.0'} 655 | 656 | package-json-from-dist@1.0.1: 657 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 658 | 659 | path-key@3.1.1: 660 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 661 | engines: {node: '>=8'} 662 | 663 | path-scurry@1.11.1: 664 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 665 | engines: {node: '>=16 || 14 >=14.18'} 666 | 667 | pathe@2.0.3: 668 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 669 | 670 | pathval@2.0.1: 671 | resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} 672 | engines: {node: '>= 14.16'} 673 | 674 | picocolors@1.1.1: 675 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 676 | 677 | picomatch@4.0.3: 678 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 679 | engines: {node: '>=12'} 680 | 681 | pirates@4.0.7: 682 | resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} 683 | engines: {node: '>= 6'} 684 | 685 | pkg-types@1.3.1: 686 | resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} 687 | 688 | postcss-load-config@6.0.1: 689 | resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 690 | engines: {node: '>= 18'} 691 | peerDependencies: 692 | jiti: '>=1.21.0' 693 | postcss: '>=8.0.9' 694 | tsx: ^4.8.1 695 | yaml: ^2.4.2 696 | peerDependenciesMeta: 697 | jiti: 698 | optional: true 699 | postcss: 700 | optional: true 701 | tsx: 702 | optional: true 703 | yaml: 704 | optional: true 705 | 706 | postcss@8.5.6: 707 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 708 | engines: {node: ^10 || ^12 || >=14} 709 | 710 | punycode@2.3.1: 711 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 712 | engines: {node: '>=6'} 713 | 714 | readdirp@4.1.2: 715 | resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} 716 | engines: {node: '>= 14.18.0'} 717 | 718 | recast@0.23.11: 719 | resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} 720 | engines: {node: '>= 4'} 721 | 722 | resolve-from@5.0.0: 723 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 724 | engines: {node: '>=8'} 725 | 726 | rollup@4.50.0: 727 | resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==} 728 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 729 | hasBin: true 730 | 731 | shebang-command@2.0.0: 732 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 733 | engines: {node: '>=8'} 734 | 735 | shebang-regex@3.0.0: 736 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 737 | engines: {node: '>=8'} 738 | 739 | siginfo@2.0.0: 740 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 741 | 742 | signal-exit@4.1.0: 743 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 744 | engines: {node: '>=14'} 745 | 746 | source-map-js@1.2.1: 747 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 748 | engines: {node: '>=0.10.0'} 749 | 750 | source-map@0.6.1: 751 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 752 | engines: {node: '>=0.10.0'} 753 | 754 | source-map@0.8.0-beta.0: 755 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 756 | engines: {node: '>= 8'} 757 | deprecated: The work that was done in this beta branch won't be included in future versions 758 | 759 | stackback@0.0.2: 760 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 761 | 762 | std-env@3.9.0: 763 | resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} 764 | 765 | string-width@4.2.3: 766 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 767 | engines: {node: '>=8'} 768 | 769 | string-width@5.1.2: 770 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 771 | engines: {node: '>=12'} 772 | 773 | strip-ansi@6.0.1: 774 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 775 | engines: {node: '>=8'} 776 | 777 | strip-ansi@7.1.0: 778 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 779 | engines: {node: '>=12'} 780 | 781 | strip-literal@3.0.0: 782 | resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} 783 | 784 | sucrase@3.35.0: 785 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 786 | engines: {node: '>=16 || 14 >=14.17'} 787 | hasBin: true 788 | 789 | thenify-all@1.6.0: 790 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 791 | engines: {node: '>=0.8'} 792 | 793 | thenify@3.3.1: 794 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 795 | 796 | tiny-invariant@1.3.3: 797 | resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} 798 | 799 | tinybench@2.9.0: 800 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 801 | 802 | tinyexec@0.3.2: 803 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 804 | 805 | tinyglobby@0.2.14: 806 | resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} 807 | engines: {node: '>=12.0.0'} 808 | 809 | tinypool@1.1.1: 810 | resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} 811 | engines: {node: ^18.0.0 || >=20.0.0} 812 | 813 | tinyrainbow@2.0.0: 814 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 815 | engines: {node: '>=14.0.0'} 816 | 817 | tinyspy@4.0.3: 818 | resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} 819 | engines: {node: '>=14.0.0'} 820 | 821 | tr46@1.0.1: 822 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 823 | 824 | tree-kill@1.2.2: 825 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 826 | hasBin: true 827 | 828 | ts-interface-checker@0.1.13: 829 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 830 | 831 | tslib@2.8.1: 832 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 833 | 834 | tsup@8.5.0: 835 | resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} 836 | engines: {node: '>=18'} 837 | hasBin: true 838 | peerDependencies: 839 | '@microsoft/api-extractor': ^7.36.0 840 | '@swc/core': ^1 841 | postcss: ^8.4.12 842 | typescript: '>=4.5.0' 843 | peerDependenciesMeta: 844 | '@microsoft/api-extractor': 845 | optional: true 846 | '@swc/core': 847 | optional: true 848 | postcss: 849 | optional: true 850 | typescript: 851 | optional: true 852 | 853 | typescript@5.9.2: 854 | resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} 855 | engines: {node: '>=14.17'} 856 | hasBin: true 857 | 858 | ufo@1.6.1: 859 | resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} 860 | 861 | undici-types@7.10.0: 862 | resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} 863 | 864 | vite-node@3.2.4: 865 | resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} 866 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 867 | hasBin: true 868 | 869 | vite@7.1.4: 870 | resolution: {integrity: sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==} 871 | engines: {node: ^20.19.0 || >=22.12.0} 872 | hasBin: true 873 | peerDependencies: 874 | '@types/node': ^20.19.0 || >=22.12.0 875 | jiti: '>=1.21.0' 876 | less: ^4.0.0 877 | lightningcss: ^1.21.0 878 | sass: ^1.70.0 879 | sass-embedded: ^1.70.0 880 | stylus: '>=0.54.8' 881 | sugarss: ^5.0.0 882 | terser: ^5.16.0 883 | tsx: ^4.8.1 884 | yaml: ^2.4.2 885 | peerDependenciesMeta: 886 | '@types/node': 887 | optional: true 888 | jiti: 889 | optional: true 890 | less: 891 | optional: true 892 | lightningcss: 893 | optional: true 894 | sass: 895 | optional: true 896 | sass-embedded: 897 | optional: true 898 | stylus: 899 | optional: true 900 | sugarss: 901 | optional: true 902 | terser: 903 | optional: true 904 | tsx: 905 | optional: true 906 | yaml: 907 | optional: true 908 | 909 | vitest@3.2.4: 910 | resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} 911 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 912 | hasBin: true 913 | peerDependencies: 914 | '@edge-runtime/vm': '*' 915 | '@types/debug': ^4.1.12 916 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 917 | '@vitest/browser': 3.2.4 918 | '@vitest/ui': 3.2.4 919 | happy-dom: '*' 920 | jsdom: '*' 921 | peerDependenciesMeta: 922 | '@edge-runtime/vm': 923 | optional: true 924 | '@types/debug': 925 | optional: true 926 | '@types/node': 927 | optional: true 928 | '@vitest/browser': 929 | optional: true 930 | '@vitest/ui': 931 | optional: true 932 | happy-dom: 933 | optional: true 934 | jsdom: 935 | optional: true 936 | 937 | webidl-conversions@4.0.2: 938 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 939 | 940 | whatwg-url@7.1.0: 941 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 942 | 943 | which@2.0.2: 944 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 945 | engines: {node: '>= 8'} 946 | hasBin: true 947 | 948 | why-is-node-running@2.3.0: 949 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 950 | engines: {node: '>=8'} 951 | hasBin: true 952 | 953 | wrap-ansi@7.0.0: 954 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 955 | engines: {node: '>=10'} 956 | 957 | wrap-ansi@8.1.0: 958 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 959 | engines: {node: '>=12'} 960 | 961 | snapshots: 962 | 963 | '@babel/helper-string-parser@7.27.1': {} 964 | 965 | '@babel/helper-validator-identifier@7.27.1': {} 966 | 967 | '@babel/parser@7.28.3': 968 | dependencies: 969 | '@babel/types': 7.28.2 970 | 971 | '@babel/types@7.28.2': 972 | dependencies: 973 | '@babel/helper-string-parser': 7.27.1 974 | '@babel/helper-validator-identifier': 7.27.1 975 | 976 | '@biomejs/biome@2.2.2': 977 | optionalDependencies: 978 | '@biomejs/cli-darwin-arm64': 2.2.2 979 | '@biomejs/cli-darwin-x64': 2.2.2 980 | '@biomejs/cli-linux-arm64': 2.2.2 981 | '@biomejs/cli-linux-arm64-musl': 2.2.2 982 | '@biomejs/cli-linux-x64': 2.2.2 983 | '@biomejs/cli-linux-x64-musl': 2.2.2 984 | '@biomejs/cli-win32-arm64': 2.2.2 985 | '@biomejs/cli-win32-x64': 2.2.2 986 | 987 | '@biomejs/cli-darwin-arm64@2.2.2': 988 | optional: true 989 | 990 | '@biomejs/cli-darwin-x64@2.2.2': 991 | optional: true 992 | 993 | '@biomejs/cli-linux-arm64-musl@2.2.2': 994 | optional: true 995 | 996 | '@biomejs/cli-linux-arm64@2.2.2': 997 | optional: true 998 | 999 | '@biomejs/cli-linux-x64-musl@2.2.2': 1000 | optional: true 1001 | 1002 | '@biomejs/cli-linux-x64@2.2.2': 1003 | optional: true 1004 | 1005 | '@biomejs/cli-win32-arm64@2.2.2': 1006 | optional: true 1007 | 1008 | '@biomejs/cli-win32-x64@2.2.2': 1009 | optional: true 1010 | 1011 | '@esbuild/aix-ppc64@0.25.9': 1012 | optional: true 1013 | 1014 | '@esbuild/android-arm64@0.25.9': 1015 | optional: true 1016 | 1017 | '@esbuild/android-arm@0.25.9': 1018 | optional: true 1019 | 1020 | '@esbuild/android-x64@0.25.9': 1021 | optional: true 1022 | 1023 | '@esbuild/darwin-arm64@0.25.9': 1024 | optional: true 1025 | 1026 | '@esbuild/darwin-x64@0.25.9': 1027 | optional: true 1028 | 1029 | '@esbuild/freebsd-arm64@0.25.9': 1030 | optional: true 1031 | 1032 | '@esbuild/freebsd-x64@0.25.9': 1033 | optional: true 1034 | 1035 | '@esbuild/linux-arm64@0.25.9': 1036 | optional: true 1037 | 1038 | '@esbuild/linux-arm@0.25.9': 1039 | optional: true 1040 | 1041 | '@esbuild/linux-ia32@0.25.9': 1042 | optional: true 1043 | 1044 | '@esbuild/linux-loong64@0.25.9': 1045 | optional: true 1046 | 1047 | '@esbuild/linux-mips64el@0.25.9': 1048 | optional: true 1049 | 1050 | '@esbuild/linux-ppc64@0.25.9': 1051 | optional: true 1052 | 1053 | '@esbuild/linux-riscv64@0.25.9': 1054 | optional: true 1055 | 1056 | '@esbuild/linux-s390x@0.25.9': 1057 | optional: true 1058 | 1059 | '@esbuild/linux-x64@0.25.9': 1060 | optional: true 1061 | 1062 | '@esbuild/netbsd-arm64@0.25.9': 1063 | optional: true 1064 | 1065 | '@esbuild/netbsd-x64@0.25.9': 1066 | optional: true 1067 | 1068 | '@esbuild/openbsd-arm64@0.25.9': 1069 | optional: true 1070 | 1071 | '@esbuild/openbsd-x64@0.25.9': 1072 | optional: true 1073 | 1074 | '@esbuild/openharmony-arm64@0.25.9': 1075 | optional: true 1076 | 1077 | '@esbuild/sunos-x64@0.25.9': 1078 | optional: true 1079 | 1080 | '@esbuild/win32-arm64@0.25.9': 1081 | optional: true 1082 | 1083 | '@esbuild/win32-ia32@0.25.9': 1084 | optional: true 1085 | 1086 | '@esbuild/win32-x64@0.25.9': 1087 | optional: true 1088 | 1089 | '@isaacs/cliui@8.0.2': 1090 | dependencies: 1091 | string-width: 5.1.2 1092 | string-width-cjs: string-width@4.2.3 1093 | strip-ansi: 7.1.0 1094 | strip-ansi-cjs: strip-ansi@6.0.1 1095 | wrap-ansi: 8.1.0 1096 | wrap-ansi-cjs: wrap-ansi@7.0.0 1097 | 1098 | '@jridgewell/gen-mapping@0.3.13': 1099 | dependencies: 1100 | '@jridgewell/sourcemap-codec': 1.5.5 1101 | '@jridgewell/trace-mapping': 0.3.30 1102 | 1103 | '@jridgewell/resolve-uri@3.1.2': {} 1104 | 1105 | '@jridgewell/sourcemap-codec@1.5.5': {} 1106 | 1107 | '@jridgewell/trace-mapping@0.3.30': 1108 | dependencies: 1109 | '@jridgewell/resolve-uri': 3.1.2 1110 | '@jridgewell/sourcemap-codec': 1.5.5 1111 | 1112 | '@pkgjs/parseargs@0.11.0': 1113 | optional: true 1114 | 1115 | '@rollup/rollup-android-arm-eabi@4.50.0': 1116 | optional: true 1117 | 1118 | '@rollup/rollup-android-arm64@4.50.0': 1119 | optional: true 1120 | 1121 | '@rollup/rollup-darwin-arm64@4.50.0': 1122 | optional: true 1123 | 1124 | '@rollup/rollup-darwin-x64@4.50.0': 1125 | optional: true 1126 | 1127 | '@rollup/rollup-freebsd-arm64@4.50.0': 1128 | optional: true 1129 | 1130 | '@rollup/rollup-freebsd-x64@4.50.0': 1131 | optional: true 1132 | 1133 | '@rollup/rollup-linux-arm-gnueabihf@4.50.0': 1134 | optional: true 1135 | 1136 | '@rollup/rollup-linux-arm-musleabihf@4.50.0': 1137 | optional: true 1138 | 1139 | '@rollup/rollup-linux-arm64-gnu@4.50.0': 1140 | optional: true 1141 | 1142 | '@rollup/rollup-linux-arm64-musl@4.50.0': 1143 | optional: true 1144 | 1145 | '@rollup/rollup-linux-loongarch64-gnu@4.50.0': 1146 | optional: true 1147 | 1148 | '@rollup/rollup-linux-ppc64-gnu@4.50.0': 1149 | optional: true 1150 | 1151 | '@rollup/rollup-linux-riscv64-gnu@4.50.0': 1152 | optional: true 1153 | 1154 | '@rollup/rollup-linux-riscv64-musl@4.50.0': 1155 | optional: true 1156 | 1157 | '@rollup/rollup-linux-s390x-gnu@4.50.0': 1158 | optional: true 1159 | 1160 | '@rollup/rollup-linux-x64-gnu@4.50.0': 1161 | optional: true 1162 | 1163 | '@rollup/rollup-linux-x64-musl@4.50.0': 1164 | optional: true 1165 | 1166 | '@rollup/rollup-openharmony-arm64@4.50.0': 1167 | optional: true 1168 | 1169 | '@rollup/rollup-win32-arm64-msvc@4.50.0': 1170 | optional: true 1171 | 1172 | '@rollup/rollup-win32-ia32-msvc@4.50.0': 1173 | optional: true 1174 | 1175 | '@rollup/rollup-win32-x64-msvc@4.50.0': 1176 | optional: true 1177 | 1178 | '@stricli/core@1.2.0': {} 1179 | 1180 | '@types/chai@5.2.2': 1181 | dependencies: 1182 | '@types/deep-eql': 4.0.2 1183 | 1184 | '@types/deep-eql@4.0.2': {} 1185 | 1186 | '@types/estree@1.0.8': {} 1187 | 1188 | '@types/node@24.3.1': 1189 | dependencies: 1190 | undici-types: 7.10.0 1191 | 1192 | '@vitest/expect@3.2.4': 1193 | dependencies: 1194 | '@types/chai': 5.2.2 1195 | '@vitest/spy': 3.2.4 1196 | '@vitest/utils': 3.2.4 1197 | chai: 5.3.3 1198 | tinyrainbow: 2.0.0 1199 | 1200 | '@vitest/mocker@3.2.4(vite@7.1.4(@types/node@24.3.1))': 1201 | dependencies: 1202 | '@vitest/spy': 3.2.4 1203 | estree-walker: 3.0.3 1204 | magic-string: 0.30.18 1205 | optionalDependencies: 1206 | vite: 7.1.4(@types/node@24.3.1) 1207 | 1208 | '@vitest/pretty-format@3.2.4': 1209 | dependencies: 1210 | tinyrainbow: 2.0.0 1211 | 1212 | '@vitest/runner@3.2.4': 1213 | dependencies: 1214 | '@vitest/utils': 3.2.4 1215 | pathe: 2.0.3 1216 | strip-literal: 3.0.0 1217 | 1218 | '@vitest/snapshot@3.2.4': 1219 | dependencies: 1220 | '@vitest/pretty-format': 3.2.4 1221 | magic-string: 0.30.18 1222 | pathe: 2.0.3 1223 | 1224 | '@vitest/spy@3.2.4': 1225 | dependencies: 1226 | tinyspy: 4.0.3 1227 | 1228 | '@vitest/utils@3.2.4': 1229 | dependencies: 1230 | '@vitest/pretty-format': 3.2.4 1231 | loupe: 3.2.1 1232 | tinyrainbow: 2.0.0 1233 | 1234 | acorn@8.15.0: {} 1235 | 1236 | ansi-regex@5.0.1: {} 1237 | 1238 | ansi-regex@6.2.0: {} 1239 | 1240 | ansi-styles@4.3.0: 1241 | dependencies: 1242 | color-convert: 2.0.1 1243 | 1244 | ansi-styles@6.2.1: {} 1245 | 1246 | any-promise@1.3.0: {} 1247 | 1248 | assertion-error@2.0.1: {} 1249 | 1250 | ast-types@0.16.1: 1251 | dependencies: 1252 | tslib: 2.8.1 1253 | 1254 | balanced-match@1.0.2: {} 1255 | 1256 | brace-expansion@2.0.2: 1257 | dependencies: 1258 | balanced-match: 1.0.2 1259 | 1260 | bundle-require@5.1.0(esbuild@0.25.9): 1261 | dependencies: 1262 | esbuild: 0.25.9 1263 | load-tsconfig: 0.2.5 1264 | 1265 | cac@6.7.14: {} 1266 | 1267 | chai@5.3.3: 1268 | dependencies: 1269 | assertion-error: 2.0.1 1270 | check-error: 2.1.1 1271 | deep-eql: 5.0.2 1272 | loupe: 3.2.1 1273 | pathval: 2.0.1 1274 | 1275 | check-error@2.1.1: {} 1276 | 1277 | chokidar@4.0.3: 1278 | dependencies: 1279 | readdirp: 4.1.2 1280 | 1281 | color-convert@2.0.1: 1282 | dependencies: 1283 | color-name: 1.1.4 1284 | 1285 | color-name@1.1.4: {} 1286 | 1287 | commander@4.1.1: {} 1288 | 1289 | confbox@0.1.8: {} 1290 | 1291 | consola@3.4.2: {} 1292 | 1293 | cross-spawn@7.0.6: 1294 | dependencies: 1295 | path-key: 3.1.1 1296 | shebang-command: 2.0.0 1297 | which: 2.0.2 1298 | 1299 | debug@4.4.1: 1300 | dependencies: 1301 | ms: 2.1.3 1302 | 1303 | deep-eql@5.0.2: {} 1304 | 1305 | eastasianwidth@0.2.0: {} 1306 | 1307 | emoji-regex@8.0.0: {} 1308 | 1309 | emoji-regex@9.2.2: {} 1310 | 1311 | es-module-lexer@1.7.0: {} 1312 | 1313 | esbuild@0.25.9: 1314 | optionalDependencies: 1315 | '@esbuild/aix-ppc64': 0.25.9 1316 | '@esbuild/android-arm': 0.25.9 1317 | '@esbuild/android-arm64': 0.25.9 1318 | '@esbuild/android-x64': 0.25.9 1319 | '@esbuild/darwin-arm64': 0.25.9 1320 | '@esbuild/darwin-x64': 0.25.9 1321 | '@esbuild/freebsd-arm64': 0.25.9 1322 | '@esbuild/freebsd-x64': 0.25.9 1323 | '@esbuild/linux-arm': 0.25.9 1324 | '@esbuild/linux-arm64': 0.25.9 1325 | '@esbuild/linux-ia32': 0.25.9 1326 | '@esbuild/linux-loong64': 0.25.9 1327 | '@esbuild/linux-mips64el': 0.25.9 1328 | '@esbuild/linux-ppc64': 0.25.9 1329 | '@esbuild/linux-riscv64': 0.25.9 1330 | '@esbuild/linux-s390x': 0.25.9 1331 | '@esbuild/linux-x64': 0.25.9 1332 | '@esbuild/netbsd-arm64': 0.25.9 1333 | '@esbuild/netbsd-x64': 0.25.9 1334 | '@esbuild/openbsd-arm64': 0.25.9 1335 | '@esbuild/openbsd-x64': 0.25.9 1336 | '@esbuild/openharmony-arm64': 0.25.9 1337 | '@esbuild/sunos-x64': 0.25.9 1338 | '@esbuild/win32-arm64': 0.25.9 1339 | '@esbuild/win32-ia32': 0.25.9 1340 | '@esbuild/win32-x64': 0.25.9 1341 | 1342 | esprima@4.0.1: {} 1343 | 1344 | estree-walker@3.0.3: 1345 | dependencies: 1346 | '@types/estree': 1.0.8 1347 | 1348 | expect-type@1.2.2: {} 1349 | 1350 | fdir@6.5.0(picomatch@4.0.3): 1351 | optionalDependencies: 1352 | picomatch: 4.0.3 1353 | 1354 | fix-dts-default-cjs-exports@1.0.1: 1355 | dependencies: 1356 | magic-string: 0.30.18 1357 | mlly: 1.8.0 1358 | rollup: 4.50.0 1359 | 1360 | foreground-child@3.3.1: 1361 | dependencies: 1362 | cross-spawn: 7.0.6 1363 | signal-exit: 4.1.0 1364 | 1365 | fsevents@2.3.3: 1366 | optional: true 1367 | 1368 | glob@10.4.5: 1369 | dependencies: 1370 | foreground-child: 3.3.1 1371 | jackspeak: 3.4.3 1372 | minimatch: 9.0.5 1373 | minipass: 7.1.2 1374 | package-json-from-dist: 1.0.1 1375 | path-scurry: 1.11.1 1376 | 1377 | is-fullwidth-code-point@3.0.0: {} 1378 | 1379 | isexe@2.0.0: {} 1380 | 1381 | jackspeak@3.4.3: 1382 | dependencies: 1383 | '@isaacs/cliui': 8.0.2 1384 | optionalDependencies: 1385 | '@pkgjs/parseargs': 0.11.0 1386 | 1387 | joycon@3.1.1: {} 1388 | 1389 | js-tokens@9.0.1: {} 1390 | 1391 | lilconfig@3.1.3: {} 1392 | 1393 | lines-and-columns@1.2.4: {} 1394 | 1395 | load-tsconfig@0.2.5: {} 1396 | 1397 | lodash.sortby@4.7.0: {} 1398 | 1399 | loupe@3.2.1: {} 1400 | 1401 | lru-cache@10.4.3: {} 1402 | 1403 | magic-string@0.30.18: 1404 | dependencies: 1405 | '@jridgewell/sourcemap-codec': 1.5.5 1406 | 1407 | minimatch@9.0.5: 1408 | dependencies: 1409 | brace-expansion: 2.0.2 1410 | 1411 | minipass@7.1.2: {} 1412 | 1413 | mlly@1.8.0: 1414 | dependencies: 1415 | acorn: 8.15.0 1416 | pathe: 2.0.3 1417 | pkg-types: 1.3.1 1418 | ufo: 1.6.1 1419 | 1420 | ms@2.1.3: {} 1421 | 1422 | mz@2.7.0: 1423 | dependencies: 1424 | any-promise: 1.3.0 1425 | object-assign: 4.1.1 1426 | thenify-all: 1.6.0 1427 | 1428 | nanoid@3.3.11: {} 1429 | 1430 | object-assign@4.1.1: {} 1431 | 1432 | package-json-from-dist@1.0.1: {} 1433 | 1434 | path-key@3.1.1: {} 1435 | 1436 | path-scurry@1.11.1: 1437 | dependencies: 1438 | lru-cache: 10.4.3 1439 | minipass: 7.1.2 1440 | 1441 | pathe@2.0.3: {} 1442 | 1443 | pathval@2.0.1: {} 1444 | 1445 | picocolors@1.1.1: {} 1446 | 1447 | picomatch@4.0.3: {} 1448 | 1449 | pirates@4.0.7: {} 1450 | 1451 | pkg-types@1.3.1: 1452 | dependencies: 1453 | confbox: 0.1.8 1454 | mlly: 1.8.0 1455 | pathe: 2.0.3 1456 | 1457 | postcss-load-config@6.0.1(postcss@8.5.6): 1458 | dependencies: 1459 | lilconfig: 3.1.3 1460 | optionalDependencies: 1461 | postcss: 8.5.6 1462 | 1463 | postcss@8.5.6: 1464 | dependencies: 1465 | nanoid: 3.3.11 1466 | picocolors: 1.1.1 1467 | source-map-js: 1.2.1 1468 | 1469 | punycode@2.3.1: {} 1470 | 1471 | readdirp@4.1.2: {} 1472 | 1473 | recast@0.23.11: 1474 | dependencies: 1475 | ast-types: 0.16.1 1476 | esprima: 4.0.1 1477 | source-map: 0.6.1 1478 | tiny-invariant: 1.3.3 1479 | tslib: 2.8.1 1480 | 1481 | resolve-from@5.0.0: {} 1482 | 1483 | rollup@4.50.0: 1484 | dependencies: 1485 | '@types/estree': 1.0.8 1486 | optionalDependencies: 1487 | '@rollup/rollup-android-arm-eabi': 4.50.0 1488 | '@rollup/rollup-android-arm64': 4.50.0 1489 | '@rollup/rollup-darwin-arm64': 4.50.0 1490 | '@rollup/rollup-darwin-x64': 4.50.0 1491 | '@rollup/rollup-freebsd-arm64': 4.50.0 1492 | '@rollup/rollup-freebsd-x64': 4.50.0 1493 | '@rollup/rollup-linux-arm-gnueabihf': 4.50.0 1494 | '@rollup/rollup-linux-arm-musleabihf': 4.50.0 1495 | '@rollup/rollup-linux-arm64-gnu': 4.50.0 1496 | '@rollup/rollup-linux-arm64-musl': 4.50.0 1497 | '@rollup/rollup-linux-loongarch64-gnu': 4.50.0 1498 | '@rollup/rollup-linux-ppc64-gnu': 4.50.0 1499 | '@rollup/rollup-linux-riscv64-gnu': 4.50.0 1500 | '@rollup/rollup-linux-riscv64-musl': 4.50.0 1501 | '@rollup/rollup-linux-s390x-gnu': 4.50.0 1502 | '@rollup/rollup-linux-x64-gnu': 4.50.0 1503 | '@rollup/rollup-linux-x64-musl': 4.50.0 1504 | '@rollup/rollup-openharmony-arm64': 4.50.0 1505 | '@rollup/rollup-win32-arm64-msvc': 4.50.0 1506 | '@rollup/rollup-win32-ia32-msvc': 4.50.0 1507 | '@rollup/rollup-win32-x64-msvc': 4.50.0 1508 | fsevents: 2.3.3 1509 | 1510 | shebang-command@2.0.0: 1511 | dependencies: 1512 | shebang-regex: 3.0.0 1513 | 1514 | shebang-regex@3.0.0: {} 1515 | 1516 | siginfo@2.0.0: {} 1517 | 1518 | signal-exit@4.1.0: {} 1519 | 1520 | source-map-js@1.2.1: {} 1521 | 1522 | source-map@0.6.1: {} 1523 | 1524 | source-map@0.8.0-beta.0: 1525 | dependencies: 1526 | whatwg-url: 7.1.0 1527 | 1528 | stackback@0.0.2: {} 1529 | 1530 | std-env@3.9.0: {} 1531 | 1532 | string-width@4.2.3: 1533 | dependencies: 1534 | emoji-regex: 8.0.0 1535 | is-fullwidth-code-point: 3.0.0 1536 | strip-ansi: 6.0.1 1537 | 1538 | string-width@5.1.2: 1539 | dependencies: 1540 | eastasianwidth: 0.2.0 1541 | emoji-regex: 9.2.2 1542 | strip-ansi: 7.1.0 1543 | 1544 | strip-ansi@6.0.1: 1545 | dependencies: 1546 | ansi-regex: 5.0.1 1547 | 1548 | strip-ansi@7.1.0: 1549 | dependencies: 1550 | ansi-regex: 6.2.0 1551 | 1552 | strip-literal@3.0.0: 1553 | dependencies: 1554 | js-tokens: 9.0.1 1555 | 1556 | sucrase@3.35.0: 1557 | dependencies: 1558 | '@jridgewell/gen-mapping': 0.3.13 1559 | commander: 4.1.1 1560 | glob: 10.4.5 1561 | lines-and-columns: 1.2.4 1562 | mz: 2.7.0 1563 | pirates: 4.0.7 1564 | ts-interface-checker: 0.1.13 1565 | 1566 | thenify-all@1.6.0: 1567 | dependencies: 1568 | thenify: 3.3.1 1569 | 1570 | thenify@3.3.1: 1571 | dependencies: 1572 | any-promise: 1.3.0 1573 | 1574 | tiny-invariant@1.3.3: {} 1575 | 1576 | tinybench@2.9.0: {} 1577 | 1578 | tinyexec@0.3.2: {} 1579 | 1580 | tinyglobby@0.2.14: 1581 | dependencies: 1582 | fdir: 6.5.0(picomatch@4.0.3) 1583 | picomatch: 4.0.3 1584 | 1585 | tinypool@1.1.1: {} 1586 | 1587 | tinyrainbow@2.0.0: {} 1588 | 1589 | tinyspy@4.0.3: {} 1590 | 1591 | tr46@1.0.1: 1592 | dependencies: 1593 | punycode: 2.3.1 1594 | 1595 | tree-kill@1.2.2: {} 1596 | 1597 | ts-interface-checker@0.1.13: {} 1598 | 1599 | tslib@2.8.1: {} 1600 | 1601 | tsup@8.5.0(postcss@8.5.6)(typescript@5.9.2): 1602 | dependencies: 1603 | bundle-require: 5.1.0(esbuild@0.25.9) 1604 | cac: 6.7.14 1605 | chokidar: 4.0.3 1606 | consola: 3.4.2 1607 | debug: 4.4.1 1608 | esbuild: 0.25.9 1609 | fix-dts-default-cjs-exports: 1.0.1 1610 | joycon: 3.1.1 1611 | picocolors: 1.1.1 1612 | postcss-load-config: 6.0.1(postcss@8.5.6) 1613 | resolve-from: 5.0.0 1614 | rollup: 4.50.0 1615 | source-map: 0.8.0-beta.0 1616 | sucrase: 3.35.0 1617 | tinyexec: 0.3.2 1618 | tinyglobby: 0.2.14 1619 | tree-kill: 1.2.2 1620 | optionalDependencies: 1621 | postcss: 8.5.6 1622 | typescript: 5.9.2 1623 | transitivePeerDependencies: 1624 | - jiti 1625 | - supports-color 1626 | - tsx 1627 | - yaml 1628 | 1629 | typescript@5.9.2: {} 1630 | 1631 | ufo@1.6.1: {} 1632 | 1633 | undici-types@7.10.0: {} 1634 | 1635 | vite-node@3.2.4(@types/node@24.3.1): 1636 | dependencies: 1637 | cac: 6.7.14 1638 | debug: 4.4.1 1639 | es-module-lexer: 1.7.0 1640 | pathe: 2.0.3 1641 | vite: 7.1.4(@types/node@24.3.1) 1642 | transitivePeerDependencies: 1643 | - '@types/node' 1644 | - jiti 1645 | - less 1646 | - lightningcss 1647 | - sass 1648 | - sass-embedded 1649 | - stylus 1650 | - sugarss 1651 | - supports-color 1652 | - terser 1653 | - tsx 1654 | - yaml 1655 | 1656 | vite@7.1.4(@types/node@24.3.1): 1657 | dependencies: 1658 | esbuild: 0.25.9 1659 | fdir: 6.5.0(picomatch@4.0.3) 1660 | picomatch: 4.0.3 1661 | postcss: 8.5.6 1662 | rollup: 4.50.0 1663 | tinyglobby: 0.2.14 1664 | optionalDependencies: 1665 | '@types/node': 24.3.1 1666 | fsevents: 2.3.3 1667 | 1668 | vitest@3.2.4(@types/node@24.3.1): 1669 | dependencies: 1670 | '@types/chai': 5.2.2 1671 | '@vitest/expect': 3.2.4 1672 | '@vitest/mocker': 3.2.4(vite@7.1.4(@types/node@24.3.1)) 1673 | '@vitest/pretty-format': 3.2.4 1674 | '@vitest/runner': 3.2.4 1675 | '@vitest/snapshot': 3.2.4 1676 | '@vitest/spy': 3.2.4 1677 | '@vitest/utils': 3.2.4 1678 | chai: 5.3.3 1679 | debug: 4.4.1 1680 | expect-type: 1.2.2 1681 | magic-string: 0.30.18 1682 | pathe: 2.0.3 1683 | picomatch: 4.0.3 1684 | std-env: 3.9.0 1685 | tinybench: 2.9.0 1686 | tinyexec: 0.3.2 1687 | tinyglobby: 0.2.14 1688 | tinypool: 1.1.1 1689 | tinyrainbow: 2.0.0 1690 | vite: 7.1.4(@types/node@24.3.1) 1691 | vite-node: 3.2.4(@types/node@24.3.1) 1692 | why-is-node-running: 2.3.0 1693 | optionalDependencies: 1694 | '@types/node': 24.3.1 1695 | transitivePeerDependencies: 1696 | - jiti 1697 | - less 1698 | - lightningcss 1699 | - msw 1700 | - sass 1701 | - sass-embedded 1702 | - stylus 1703 | - sugarss 1704 | - supports-color 1705 | - terser 1706 | - tsx 1707 | - yaml 1708 | 1709 | webidl-conversions@4.0.2: {} 1710 | 1711 | whatwg-url@7.1.0: 1712 | dependencies: 1713 | lodash.sortby: 4.7.0 1714 | tr46: 1.0.1 1715 | webidl-conversions: 4.0.2 1716 | 1717 | which@2.0.2: 1718 | dependencies: 1719 | isexe: 2.0.0 1720 | 1721 | why-is-node-running@2.3.0: 1722 | dependencies: 1723 | siginfo: 2.0.0 1724 | stackback: 0.0.2 1725 | 1726 | wrap-ansi@7.0.0: 1727 | dependencies: 1728 | ansi-styles: 4.3.0 1729 | string-width: 4.2.3 1730 | strip-ansi: 6.0.1 1731 | 1732 | wrap-ansi@8.1.0: 1733 | dependencies: 1734 | ansi-styles: 6.2.1 1735 | string-width: 5.1.2 1736 | strip-ansi: 7.1.0 1737 | --------------------------------------------------------------------------------