diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 198752a..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,126 +0,0 @@ -# AgentKit CLI - Development Instructions - -## **Project Overview** - -- CLI tool for creating and running AI agents locally -- TypeScript-based command-line interface -- Commands: init, install, list, login, logout, publish, run, search, update, whoami -- Published as `@agentage/cli` package - -## **Project Agreements** - -- Default branch: `master` -- Repository: `agentage/cli` -- Branch names: `feature/*`, `bugfix/*`, `hotfix/*`, `setup-*` -- Commits: `feat:`, `fix:`, `chore:` (max 72 chars) -- Verifications: `npm run verify` (type-check + lint + build + test) - -## **Publishing** - -- Published to npm as `@agentage/cli` -- Auto-publish on push to `master` when `package.json` version is bumped. - -## **Release Strategy** - -- ๐ŸŽฏ **MINIMAL FIRST**: Keep commands simple and focused -- ๐Ÿšซ **No Over-Engineering**: Single responsibility per command -- โšก **Essential Only**: Core CLI functionality - -## **Rules** - -- ๐Ÿ“Š Use icons/tables for structured output -- ๐Ÿ“ NO extra docs unless explicitly asked -- ๐Ÿ™ GitHub: owner `agentage`, repo `cli` -- โšก Prefer function calls over terminal commands -- ๐Ÿ“‚ Source code in `src/` directory - -## **Coding Standards** - -### TypeScript - -- ๐Ÿšซ No `any` type - explicit types always -- ๐Ÿ“ค Named exports only (no default exports) -- ๐Ÿ“ Files <300 lines, commands <200 lines -- ๐Ÿ”„ Functional: arrow functions, async/await, destructuring -- ๐Ÿ—๏ธ Interfaces over classes -- โœ… ESM modules (`type: "module"`) - -### Naming - -- **Interfaces**: `AgentConfig`, `RegistryAgent`, `LockfileEntry` -- **Types**: `CommandOptions`, `ServiceConfig` -- **Files**: `command.ts`, `service.ts`, `*.types.ts`, `*.test.ts` - -## **Tech Stack** - -- **Language**: TypeScript 5.3+ (strict mode) -- **Module**: ESNext with ESM -- **Testing**: Jest 30+ with ts-jest -- **Linting**: ESLint 9+ (flat config) -- **Formatting**: Prettier -- **Package Manager**: npm (workspaces) - -## **Node Requirements** - -- Node.js >= 20.0.0 -- npm >= 10.0.0 - -## **CLI Patterns** - -**Command Structure**: - -```typescript -import { Command } from 'commander'; - -export const myCommand = new Command('name') - .description('Command description') - .option('-o, --option ', 'Option description') - .action(async (options) => { - // Implementation - }); -``` - -**Service Pattern**: - -```typescript -export const myService = { - async doSomething(): Promise { - // Implementation - }, -}; -``` - -## **Workspace Structure** - -``` -src/ - cli.ts # Main CLI entry point - index.ts # Package exports - commands/ # CLI commands (init, run, install, etc.) - services/ # Business logic (auth, registry) - schemas/ # Zod validation schemas - types/ # TypeScript type definitions - utils/ # Utility functions -``` - -## **Scripts** - -All packages support: - -- `npm run build` - Build TypeScript -- `npm run type-check` - TypeScript validation -- `npm run lint` - ESLint check -- `npm run lint:fix` - Auto-fix linting -- `npm run test` - Run Jest tests -- `npm run test:watch` - Watch mode -- `npm run test:coverage` - Coverage report -- `npm run verify` - All checks -- `npm run clean` - Clean build artifacts - -## **Quality Gates** - -- โœ… Type check must pass -- โœ… Linting must pass (no warnings) -- โœ… All tests must pass -- โœ… Coverage >= 70% (branches, functions, lines, statements) -- โœ… Build must succeed diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b1e4ad1..bbcdb4d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,7 +13,24 @@ updates: groups: all-dependencies: patterns: - - '*' + - '@types/*' + - 'typescript' + - 'typescript-eslint' + - 'eslint*' + - '@eslint/*' + - 'prettier' + - 'vitest*' + - '@vitest/*' + - '@playwright/*' + - 'playwright*' + - '@testing-library/*' + - 'postcss' + - 'autoprefixer' + - '@tailwindcss/*' + - 'tailwindcss' + update-types: + - 'minor' + - 'patch' ignore: - dependency-name: '*' update-types: ['version-update:semver-major'] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7edc2c2..ba3d0a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,48 +8,28 @@ on: jobs: build-and-test: - name: ๐Ÿ—๏ธ Build & Test + name: Build & Test runs-on: ubuntu-latest strategy: matrix: - node-version: [20.x, 22.x] + node-version: [22.x] steps: - - name: ๐Ÿ“ฅ Checkout code + - name: Checkout code uses: actions/checkout@v6 - - name: ๐Ÿ”ง Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: 'npm' - - name: ๐Ÿ“ฆ Install dependencies + - name: Install dependencies run: npm ci - - name: ๐Ÿ” Type check - run: npm run type-check + - name: Verify + run: npm run verify - - name: ๐Ÿ” Lint - run: npm run lint - - - name: ๐Ÿ’… Format check - run: npm run format:check - - - name: ๐Ÿ—๏ธ Build - run: npm run build - - - name: ๐Ÿงช Test - run: npm test - - - name: ๐Ÿ“Š Test coverage + - name: Test coverage run: npm run test:coverage - - - name: ๐Ÿ“ค Upload coverage reports - uses: codecov/codecov-action@v5 - if: matrix.node-version == '20.x' - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage/lcov.info - fail_ci_if_error: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 91ec189..f8aa218 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,11 +12,11 @@ permissions: id-token: write env: - NODE_VERSION: '20' + NODE_VERSION: '22' jobs: detect-changes: - name: ๐Ÿ” Detect Version Changes + name: Detect Version Changes runs-on: ubuntu-latest outputs: should-publish: ${{ steps.detect.outputs.should-publish }} @@ -24,19 +24,19 @@ jobs: version: ${{ steps.detect.outputs.version }} steps: - - name: ๐Ÿ“ฅ Checkout code + - name: Checkout code uses: actions/checkout@v6 - - name: ๐Ÿ”ง Setup Node.js + - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - - name: ๐Ÿ“ฆ Install dependencies + - name: Install dependencies run: npm ci - - name: ๐Ÿ” Detect version changes + - name: Detect version changes id: detect run: | name=$(node -p "require('./package.json').name") @@ -46,87 +46,75 @@ jobs: echo "name=${name}" >> $GITHUB_OUTPUT echo "version=${version}" >> $GITHUB_OUTPUT - # Skip if private if [ "$private" = "true" ]; then echo "should-publish=false" >> $GITHUB_OUTPUT - echo "โ„น๏ธ Package is private, skipping publish" + echo "Package is private, skipping publish" exit 0 fi - echo "๐Ÿ“ฆ Checking ${name}@${version}..." + echo "Checking ${name}@${version}..." - # Check if this version exists on npm if npm view "${name}@${version}" version 2>/dev/null; then echo "should-publish=false" >> $GITHUB_OUTPUT - echo "โญ๏ธ Version ${version} already published for ${name}" + echo "Version ${version} already published for ${name}" else echo "should-publish=true" >> $GITHUB_OUTPUT - echo "โœจ New version detected: ${name}@${version}" + echo "New version detected: ${name}@${version}" fi verify: - name: โœ… Verify Package + name: Verify Package needs: [detect-changes] if: needs.detect-changes.outputs.should-publish == 'true' runs-on: ubuntu-latest steps: - - name: ๐Ÿ“ฅ Checkout code + - name: Checkout code uses: actions/checkout@v6 - - name: ๐Ÿ”ง Setup Node.js + - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - - name: ๐Ÿ“ฆ Install dependencies + - name: Install dependencies run: npm ci - - name: ๐Ÿ—๏ธ Build package - run: npm run build - - - name: ๐Ÿงช Run tests - run: npm test - - - name: ๐Ÿ” Run linting - run: npm run lint - - - name: โœ… Verification passed - run: | - echo "โœ… ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }} verified successfully" >> $GITHUB_STEP_SUMMARY + - name: Verify + run: npm run verify publish: - name: ๐Ÿ“ค Publish Package + name: Publish Package needs: [detect-changes, verify] if: needs.detect-changes.outputs.should-publish == 'true' && needs.verify.result == 'success' runs-on: ubuntu-latest steps: - - name: ๐Ÿ“ฅ Checkout code + - name: Checkout code uses: actions/checkout@v6 - - name: ๐Ÿ”ง Setup Node.js + - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' registry-url: 'https://registry.npmjs.org' - - name: ๐Ÿ“ฆ Install dependencies + - name: Install dependencies run: npm ci - - name: ๐Ÿ—๏ธ Build package + - name: Build package run: npm run build - - name: ๐Ÿ“ค Publish to npm + - name: Publish to npm env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | - echo "๐Ÿ“ค Publishing ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }}" + echo "Publishing ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }}" npm publish --access public --provenance - - name: ๐Ÿท๏ธ Create git tag + - name: Create git tag run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" @@ -136,35 +124,35 @@ jobs: git tag -a "${TAG_NAME}" -m "Release ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }}" git push origin "${TAG_NAME}" - - name: โœ… Published successfully + - name: Published successfully run: | - echo "๐ŸŽ‰ Successfully published ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "๐Ÿ“ฆ Package URL: https://www.npmjs.com/package/${{ needs.detect-changes.outputs.name }}/v/${{ needs.detect-changes.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Successfully published ${{ needs.detect-changes.outputs.name }}@${{ needs.detect-changes.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Package URL: https://www.npmjs.com/package/${{ needs.detect-changes.outputs.name }}/v/${{ needs.detect-changes.outputs.version }}" >> $GITHUB_STEP_SUMMARY summary: - name: ๐Ÿ“‹ Publish Summary + name: Publish Summary needs: [detect-changes, verify, publish] if: always() runs-on: ubuntu-latest steps: - - name: ๐Ÿ“Š Create summary + - name: Create summary run: | - echo "## ๐Ÿ“ฆ Package Publishing Summary" >> $GITHUB_STEP_SUMMARY + echo "## Package Publishing Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ "${{ needs.detect-changes.outputs.should-publish }}" != "true" ]; then - echo "โ„น๏ธ **No new version to publish**" >> $GITHUB_STEP_SUMMARY + echo "**No new version to publish**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Version ${{ needs.detect-changes.outputs.version }} is already published or package is private." >> $GITHUB_STEP_SUMMARY elif [ "${{ needs.verify.result }}" != "success" ]; then - echo "โŒ **Verification failed - package not published**" >> $GITHUB_STEP_SUMMARY + echo "**Verification failed - package not published**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Please fix verification errors and try again." >> $GITHUB_STEP_SUMMARY elif [ "${{ needs.publish.result }}" == "success" ]; then - echo "โœ… **Package published successfully!**" >> $GITHUB_STEP_SUMMARY + echo "**Package published successfully!**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "- **${{ needs.detect-changes.outputs.name }}** version ${{ needs.detect-changes.outputs.version }}" >> $GITHUB_STEP_SUMMARY else - echo "โš ๏ธ **Package failed to publish**" >> $GITHUB_STEP_SUMMARY + echo "**Package failed to publish**" >> $GITHUB_STEP_SUMMARY fi diff --git a/.npmignore b/.npmignore index c435169..781665b 100644 --- a/.npmignore +++ b/.npmignore @@ -4,8 +4,11 @@ src/ # Config files tsconfig.json -jest.config.js -eslint.config.js +eslint.config.mjs +vitest.config.ts +.prettierrc +.nvmrc +.npmrc # Coverage coverage/ @@ -18,3 +21,6 @@ coverage/ # OS .DS_Store Thumbs.db + +# CI +.github/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..c3608bf --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +save-exact=true +legacy-peer-deps=false diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.prettierrc b/.prettierrc index a95cb05..32a2397 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,10 @@ { "semi": true, - "singleQuote": true, "trailingComma": "es5", + "singleQuote": true, "printWidth": 100, - "tabWidth": 2 + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf" } diff --git a/eslint.config.js b/eslint.config.mjs similarity index 63% rename from eslint.config.js rename to eslint.config.mjs index 9924e01..0b7dc21 100644 --- a/eslint.config.js +++ b/eslint.config.mjs @@ -1,36 +1,41 @@ import typescriptEslint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; +import prettierPlugin from 'eslint-plugin-prettier'; +import prettierConfig from 'eslint-config-prettier'; export default [ { - ignores: ['src/__mocks__/**'], + ignores: ['dist/**', 'coverage/**', 'node_modules/**'], }, { - files: ['**/*.ts'], + files: ['src/**/*.ts'], languageOptions: { parser: tsParser, parserOptions: { - ecmaVersion: 2022, + ecmaVersion: 2024, sourceType: 'module', project: './tsconfig.json', }, }, plugins: { '@typescript-eslint': typescriptEslint, + prettier: prettierPlugin, }, rules: { + ...prettierConfig.rules, + 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/explicit-function-return-type': 'warn', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, + ], '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], '@typescript-eslint/naming-convention': [ 'error', { selector: 'interface', format: ['PascalCase'], - custom: { - regex: '^[A-Z]', - match: true, - }, + custom: { regex: '^[A-Z]', match: true }, }, { selector: 'typeAlias', diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index c07ad68..0000000 --- a/jest.config.js +++ /dev/null @@ -1,35 +0,0 @@ -export default { - preset: 'ts-jest/presets/default-esm', - testEnvironment: 'node', - extensionsToTreatAsEsm: ['.ts'], - // Run tests sequentially to avoid race conditions with process.chdir in tests - maxWorkers: 1, - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - '^chalk$': '/src/__mocks__/chalk.ts', - }, - transform: { - '^.+\\.ts$': [ - 'ts-jest', - { - useESM: true, - }, - ], - }, - testMatch: ['**/*.test.ts'], - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.test.ts', - '!src/**/*.d.ts', - '!src/cli.ts', - '!src/__mocks__/**', - ], - coverageThreshold: { - global: { - branches: 70, - functions: 70, - lines: 70, - statements: 70, - }, - }, -}; diff --git a/package-lock.json b/package-lock.json index b1aea8a..720d1e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,635 +1,381 @@ { "name": "@agentage/cli", - "version": "0.1.19", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agentage/cli", - "version": "0.1.19", + "version": "0.2.0", "license": "MIT", "dependencies": { - "chalk": "^5.6.2", - "commander": "^14.0.2", - "gray-matter": "^4.0.3", - "inquirer": "^13.0.1", - "js-yaml": "^4.1.0", - "node-machine-id": "1.1.12", - "open": "^11.0.0", - "yaml": "^2.8.1", - "zod": "^4.3.6" + "@agentage/core": "^0.2.0", + "@agentage/platform": "^0.2.0", + "chalk": "latest", + "commander": "latest", + "express": "latest", + "gray-matter": "latest", + "jiti": "latest", + "open": "latest", + "ws": "latest" }, "bin": { - "agent": "dist/cli.js", - "agentkit": "dist/cli.js" + "agentage": "dist/cli.js" }, "devDependencies": { - "@types/inquirer": "^9.0.9", - "@types/jest": "^30.0.0", - "@types/js-yaml": "^4.0.9", - "@types/node": "^25.0.3", - "@typescript-eslint/eslint-plugin": "^8.46.3", - "@typescript-eslint/parser": "^8.46.3", - "eslint": "^9.39.1", - "jest": "^30.2.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.5", - "tsx": "^4.20.6", - "typescript": "^5.9.3" - }, - "engines": { - "node": ">=20.0.0", + "@types/express": "latest", + "@types/node": "latest", + "@types/ws": "latest", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", + "@vitest/coverage-v8": "latest", + "eslint": "latest", + "eslint-config-prettier": "latest", + "eslint-plugin-prettier": "latest", + "prettier": "latest", + "typescript": "latest", + "vitest": "latest" + }, + "engines": { + "node": ">=22.0.0", "npm": ">=10.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, + "node_modules/@agentage/core": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@agentage/core/-/core-0.2.0.tgz", + "integrity": "sha512-qLTI2BQRbCcf6+5draTUAS7For9XG8drwZeNu6N7MiOSA0O3qpyMPcQnOQF6ViJMLI6LtK8fuFyXgdNxwmxEcA==", "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">=22.0.0", + "npm": ">=10.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", - "dev": true, + "node_modules/@agentage/platform": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@agentage/platform/-/platform-0.2.0.tgz", + "integrity": "sha512-Ugl6WurNsDZh9K/eCVkUbI7sx9yylLTRaZEQ0Ct6wMDUYoKuHaK3k1P7xgShllQXrpb98csjTBY/pUJyUtSg5Q==", "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=22.0.0", + "npm": ">=10.0.0" + }, + "peerDependencies": { + "@agentage/core": "^0.2.0" } }, - "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, "engines": { "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" } }, - "node_modules/@babel/generator": { + "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@babel/types": "^7.29.0" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", "bin": { - "semver": "bin/semver.js" + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.9.0" + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.9.0" + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "node_modules/@eslint/config-array": { + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@eslint/object-schema": "^3.0.3", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "engines": { - "node": ">=6.9.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "node_modules/@eslint/config-helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@eslint/core": "^1.1.1" }, "engines": { - "node": ">=6.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "node_modules/@eslint/core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@types/json-schema": "^7.0.15" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "node_modules/@eslint/object-schema": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/@eslint/plugin-kit": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@eslint/core": "^1.1.1", + "levn": "^0.4.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18.18.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18.18.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "license": "MIT" }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@oxc-project/types": { + "version": "0.120.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", + "integrity": "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://opencollective.com/pkgr" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emnapi/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", - "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==", "cpu": [ "arm64" ], @@ -640,30 +386,13 @@ "android" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==", "cpu": [ "arm64" ], @@ -674,13 +403,13 @@ "darwin" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.10.tgz", + "integrity": "sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==", "cpu": [ "x64" ], @@ -691,30 +420,13 @@ "darwin" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.10.tgz", + "integrity": "sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==", "cpu": [ "x64" ], @@ -725,13 +437,13 @@ "freebsd" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.10.tgz", + "integrity": "sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==", "cpu": [ "arm" ], @@ -742,13 +454,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==", "cpu": [ "arm64" ], @@ -759,15 +471,15 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.10.tgz", + "integrity": "sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", @@ -776,15 +488,15 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==", "cpu": [ - "loong64" + "ppc64" ], "dev": true, "license": "MIT", @@ -793,15 +505,15 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==", "cpu": [ - "mips64el" + "s390x" ], "dev": true, "license": "MIT", @@ -810,15 +522,15 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==", "cpu": [ - "ppc64" + "x64" ], "dev": true, "license": "MIT", @@ -827,15 +539,15 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.10.tgz", + "integrity": "sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==", "cpu": [ - "riscv64" + "x64" ], "dev": true, "license": "MIT", @@ -844,47 +556,47 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==", "cpu": [ - "s390x" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "openharmony" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.10.tgz", + "integrity": "sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==", "cpu": [ - "x64" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, "engines": { - "node": ">=18" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.10.tgz", + "integrity": "sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==", "cpu": [ "arm64" ], @@ -892,16 +604,16 @@ "license": "MIT", "optional": true, "os": [ - "netbsd" + "win32" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.10.tgz", + "integrity": "sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==", "cpu": [ "x64" ], @@ -909,5699 +621,1778 @@ "license": "MIT", "optional": true, "os": [ - "netbsd" + "win32" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.10.tgz", + "integrity": "sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } + "license": "MIT" }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } + "license": "MIT" }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "license": "MIT" }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } + "license": "MIT" }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "undici-types": "~7.18.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@types/node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@types/http-errors": "*", + "@types/node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", + "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/type-utils": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.57.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "node_modules/@typescript-eslint/parser": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", + "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "debug": "^4.4.3" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://eslint.org/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", + "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.1", + "@typescript-eslint/types": "^8.57.1", + "debug": "^4.4.3" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", + "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", + "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=18.18.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", + "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@typescript-eslint/types": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", + "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=18.18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@inquirer/ansi": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.4.tgz", - "integrity": "sha512-DpcZrQObd7S0R/U3bFdkcT5ebRwbTTC4D3tCc1vsJizmgPLxNJBo+AAFmrZwe8zk30P2QzgzGWZ3Q9uJwWuhIg==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.1.2.tgz", - "integrity": "sha512-PubpMPO2nJgMufkoB3P2wwxNXEMUXnBIKi/ACzDUYfaoPuM7gSTmuxJeMscoLVEsR4qqrCMf5p0SiYGWnVJ8kw==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.4", - "@inquirer/core": "^11.1.7", - "@inquirer/figures": "^2.0.4", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.10.tgz", - "integrity": "sha512-tiNyA73pgpQ0FQ7axqtoLUe4GDYjNCDcVsbgcA5anvwg2z6i+suEngLKKJrWKJolT//GFPZHwN30binDIHgSgQ==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "11.1.7", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.7.tgz", - "integrity": "sha512-1BiBNDk9btIwYIzNZpkikIHXWeNzNncJePPqwDyVMhXhD1ebqbpn1mKGctpoqAbzywZfdG0O4tvmsGIcOevAPQ==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.4", - "@inquirer/figures": "^2.0.4", - "@inquirer/type": "^4.0.4", - "cli-width": "^4.1.0", - "fast-wrap-ansi": "^0.2.0", - "mute-stream": "^3.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/editor": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.10.tgz", - "integrity": "sha512-VJx4XyaKea7t8hEApTw5dxeIyMtWXre2OiyJcICCRZI4hkoHsMoCnl/KbUnJJExLbH9csLLHMVR144ZhFE1CwA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/external-editor": "^2.0.4", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.10.tgz", - "integrity": "sha512-fC0UHJPXsTRvY2fObiwuQYaAnHrp3aDqfwKUJSdfpgv18QUG054ezGbaRNStk/BKD5IPijeMKWej8VV8O5Q/eQ==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-2.0.4.tgz", - "integrity": "sha512-Prenuv9C1PHj2Itx0BcAOVBTonz02Hc2Nd2DbU67PdGUaqn0nPCnV34oDyyoaZHnmfRxkpuhh/u51ThkrO+RdA==", - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.2" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.4.tgz", - "integrity": "sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - } - }, - "node_modules/@inquirer/input": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.10.tgz", - "integrity": "sha512-nvZ6qEVeX/zVtZ1dY2hTGDQpVGD3R7MYPLODPgKO8Y+RAqxkrP3i/3NwF3fZpLdaMiNuK0z2NaYIx9tPwiSegQ==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.10.tgz", - "integrity": "sha512-Ht8OQstxiS3APMGjHV0aYAjRAysidWdwurWEo2i8yI5xbhOBWqizT0+MU1S2GCcuhIBg+3SgWVjEoXgfhY+XaA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.10.tgz", - "integrity": "sha512-QbNyvIE8q2GTqKLYSsA8ATG+eETo+m31DSR0+AU7x3d2FhaTWzqQek80dj3JGTo743kQc6mhBR0erMjYw5jQ0A==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.4", - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.3.2.tgz", - "integrity": "sha512-yFroiSj2iiBFlm59amdTvAcQFvWS6ph5oKESls/uqPBect7rTU2GbjyZO2DqxMGuIwVA8z0P4K6ViPcd/cp+0w==", - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^5.1.2", - "@inquirer/confirm": "^6.0.10", - "@inquirer/editor": "^5.0.10", - "@inquirer/expand": "^5.0.10", - "@inquirer/input": "^5.0.10", - "@inquirer/number": "^4.0.10", - "@inquirer/password": "^5.0.10", - "@inquirer/rawlist": "^5.2.6", - "@inquirer/search": "^4.1.6", - "@inquirer/select": "^5.1.2" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.6.tgz", - "integrity": "sha512-jfw0MLJ5TilNsa9zlJ6nmRM0ZFVZhhTICt4/6CU2Dv1ndY7l3sqqo1gIYZyMMDw0LvE1u1nzJNisfHEhJIxq5w==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.6.tgz", - "integrity": "sha512-3/6kTRae98hhDevENScy7cdFEuURnSpM3JbBNg8yfXLw88HgTOl+neUuy/l9W0No5NzGsLVydhBzTIxZP7yChQ==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.7", - "@inquirer/figures": "^2.0.4", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.1.2.tgz", - "integrity": "sha512-kTK8YIkHV+f02y7bWCh7E0u2/11lul5WepVTclr3UMBtBr05PgcZNWfMa7FY57ihpQFQH/spLMHTcr0rXy50tA==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.4", - "@inquirer/core": "^11.1.7", - "@inquirer/figures": "^2.0.4", - "@inquirer/type": "^4.0.4" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.4.tgz", - "integrity": "sha512-PamArxO3cFJZoOzspzo6cxVlLeIftyBsZw/S9bKY5DzxqJVZgjoj1oP8d0rskKtp7sZxBycsoer1g6UeJV1BBA==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", - "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", - "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.3.0", - "jest-config": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-resolve-dependencies": "30.3.0", - "jest-runner": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "jest-watcher": "30.3.0", - "pretty-format": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", - "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-mock": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.3.0", - "jest-snapshot": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", - "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@sinonjs/fake-timers": "^15.0.0", - "@types/node": "*", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/types": "30.3.0", - "jest-mock": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", - "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", - "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/types": "30.3.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", - "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", - "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/inquirer": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.9.tgz", - "integrity": "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/through": "*", - "rxjs": "^7.2.0" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/through": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", - "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", - "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/type-utils": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.57.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", - "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.1", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", - "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", - "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.1", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/babel-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", - "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.3.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", - "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", - "dev": true, - "license": "BSD-3-Clause", - "workspaces": [ - "test/babel-8" - ], - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", - "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", - "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-beta.1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.0.tgz", - "integrity": "sha512-Mh++g+2LPfzZToywfE1BUzvZbfOY52Nil0rn9H1CPC5DJ7fX+Vir7nToBeoiSbB1zTNeGYbELEvJESujgGrzXw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001759", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", - "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "license": "MIT" - }, - "node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", - "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", - "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.263", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz", - "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", - "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-13.3.2.tgz", - "integrity": "sha512-bh/OjBGxNR9qvfQj1n5bxtIF58mbOTp2InN5dKuwUK03dXcDGFsjlDinQRuXMZ4EGiJaFieUWHCAaxH2p7iUBw==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.4", - "@inquirer/core": "^11.1.7", - "@inquirer/prompts": "^8.3.2", - "@inquirer/type": "^4.0.4", - "mute-stream": "^3.0.0", - "run-async": "^4.0.6", - "rxjs": "^7.8.2" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", + "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-in-ssh": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", - "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" + "@typescript-eslint/project-service": "8.57.1", + "@typescript-eslint/tsconfig-utils": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=14.16" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "18 || 20 || >=22" } }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, "license": "MIT", "dependencies": { - "is-inside-container": "^1.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "18 || 20 || >=22" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "BSD-3-Clause", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "node_modules/@typescript-eslint/utils": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", + "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", + "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "8.57.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "node_modules/@vitest/coverage-v8": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.0.tgz", + "integrity": "sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.0", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.0.3" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.0", + "vitest": "4.1.0" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/@vitest/expect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", + "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "chai": "^6.2.2", + "tinyrainbow": "^3.0.3" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "url": "https://opencollective.com/vitest" } }, - "node_modules/jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", - "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", + "node_modules/@vitest/mocker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", + "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/types": "30.3.0", - "import-local": "^3.2.0", - "jest-cli": "30.3.0" + "@vitest/spy": "4.1.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { - "node-notifier": { + "msw": { + "optional": true + }, + "vite": { "optional": true } } }, - "node_modules/jest-changed-files": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", - "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", + "node_modules/@vitest/pretty-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", + "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", "dev": true, "license": "MIT", "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.3.0", - "p-limit": "^3.1.0" + "tinyrainbow": "^3.0.3" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/jest-circus": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", - "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", + "node_modules/@vitest/runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", + "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "p-limit": "^3.1.0", - "pretty-format": "30.3.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "@vitest/utils": "4.1.0", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", + "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@vitest/pretty-format": "4.1.0", + "@vitest/utils": "4.1.0", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@vitest/spy": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", + "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", + "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "@vitest/pretty-format": "4.1.0", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.0.3" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/jest-cli": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", - "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", - "dev": true, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "@jest/core": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "yargs": "^17.7.2" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", "bin": { - "jest": "bin/jest.js" + "acorn": "bin/acorn" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "sprintf-js": "~1.0.2" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "node_modules/jest-config": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", - "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.3.0", - "@jest/types": "30.3.0", - "babel-jest": "30.3.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-circus": "30.3.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-runner": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "parse-json": "^5.2.0", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "run-applescript": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.8" } }, - "node_modules/jest-docblock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", - "dev": true, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { - "detect-newline": "^3.1.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-each": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", - "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "jest-util": "30.3.0", - "pretty-format": "30.3.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-environment-node": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", - "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", - "dev": true, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-mock": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=20" } }, - "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", - "dev": true, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "picomatch": "^4.0.3", - "walker": "^1.0.8" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">= 0.6" } }, - "node_modules/jest-leak-detector": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", - "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.3.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "dev": true, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6.6.0" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 8" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "MIT" }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-message-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "license": "MIT", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-util": "30.3.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-resolve": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", - "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" + "es-errors": "^1.3.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve-dependencies": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", - "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", + "node_modules/eslint": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz", + "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.3.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.3", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.1.1", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/jest-runner": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", - "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/environment": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-leak-detector": "30.3.0", - "jest-message-util": "30.3.0", - "jest-resolve": "30.3.0", - "jest-runtime": "30.3.0", - "jest-util": "30.3.0", - "jest-watcher": "30.3.0", - "jest-worker": "30.3.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-runtime": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", - "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/globals": "30.3.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 4" } }, - "node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "color-convert": "^2.0.1" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4.0" } }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=4.0" } }, - "node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@types/estree": "^1.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-watcher": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", - "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", - "dev": true, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.3.0", - "string-length": "^4.0.2" + "is-extendable": "^0.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=16.0.0" } }, - "node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", - "dev": true, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=16" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, + "license": "ISC" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-locate": "^5.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" } }, - "node_modules/make-dir": { + "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "tmpl": "1.0.5" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, "engines": { - "node": ">=6" + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=0.10.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 4" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=0.8.19" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, - "node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", - "license": "ISC", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 0.10" } }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "license": "MIT", "bin": { - "napi-postinstall": "lib/cli.js" + "is-docker": "cli.js" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" + "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/open": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", - "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", "license": "MIT", - "dependencies": { - "default-browser": "^5.4.0", - "define-lazy-prop": "^3.0.0", - "is-in-ssh": "^1.0.0", - "is-inside-container": "^1.0.0", - "powershell-utils": "^0.1.0", - "wsl-utils": "^0.3.0" - }, "engines": { "node": ">=20" }, @@ -6609,402 +2400,511 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "is-docker": "^3.0.0" }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" + "bin": { + "is-inside-container": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "ISC" }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "BlueOak-1.0.0" + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "callsites": "^3.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/path-is-absolute": { + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MPL-2.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC" + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC" + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8.6" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" + "node": ">= 12.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/powershell-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", - "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", - "license": "MIT", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "p-locate": "^5.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -7013,1010 +2913,1128 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-async": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", - "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "node": ">= 0.6" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=18" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "shebang-regex": "^3.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", "license": "MIT", "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" }, "engines": { - "node": ">=10" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": ">=6" + "node": "^10 || ^12 || >=14" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://opencollective.com/synckit" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "side-channel": "^1.1.0" }, "engines": { - "node": "*" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": "*" + "node": ">= 0.10" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/rolldown": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz", + "integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "@oxc-project/types": "=0.120.0", + "@rolldown/pluginutils": "1.0.0-rc.10" }, - "engines": { - "node": ">=12.0.0" + "bin": { + "rolldown": "bin/cli.mjs" }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": "^20.19.0 || >=22.12.0" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.10", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.10", + "@rolldown/binding-darwin-x64": "1.0.0-rc.10", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.10", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.10", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.10", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.10", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.10", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.10", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.10", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">= 18" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", "license": "MIT", - "engines": { - "node": ">=18.12" + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" }, - "peerDependencies": { - "typescript": ">=4.8.4" + "engines": { + "node": ">=4" } }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, + "license": "ISC", "bin": { - "ts-jest": "cli.js" + "semver": "bin/semver.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } + "node": ">=10" } }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, "engines": { - "node": ">=16" + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">=18.0.0" + "node": ">= 18" }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" + "engines": { + "node": ">= 0.4" }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.1.tgz", - "integrity": "sha512-R9NcHbbZ45RoWfTdhn1J9SS7zxNvlddv4YRrHTUaFdtjbmfncfedB45EC9IaqJQ97iAR1GZgOfyRQO+ExIF6EQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, - "bin": { - "update-browserslist-db": "cli.js" + "engines": { + "node": ">= 0.4" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { - "node": ">=10.12.0" + "node": ">= 0.8" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" + "license": "MIT" + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=18.12" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "MIT" + "license": "0BSD", + "optional": true }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=14.17" } }, - "node_modules/wsl-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.0.tgz", - "integrity": "sha512-3sFIGLiaDP7rTO4xh3g+b3AzhYDIUGGywE/WsmqzJWDxus5aJXVnPTNC/6L+r2WzrwXqVOdD262OaO+cEyPMSQ==", + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0", - "powershell-utils": "^0.1.0" - }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "node_modules/vite": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.1.tgz", + "integrity": "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==", "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "license": "ISC", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.10", + "tinyglobby": "^0.2.15" + }, "bin": { - "yaml": "bin.mjs" + "vite": "bin/vite.js" }, "engines": { - "node": ">= 14.6" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/eemeli" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "node_modules/vitest": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", + "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.0", + "@vitest/mocker": "4.1.0", + "@vitest/pretty-format": "4.1.0", + "@vitest/runner": "4.1.0", + "@vitest/snapshot": "4.1.0", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=12" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.0", + "@vitest/browser-preview": "4.1.0", + "@vitest/browser-webdriverio": "4.1.0", + "@vitest/ui": "4.1.0", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, "engines": { "node": ">=8" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/yocto-queue": { @@ -8031,15 +4049,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index 4ae591f..5a78f29 100644 --- a/package.json +++ b/package.json @@ -1,72 +1,59 @@ { "name": "@agentage/cli", - "version": "0.1.19", - "description": "CLI tool for creating and running AI agents locally", + "version": "0.2.0", + "description": "Agentage CLI and daemon โ€” control plane for AI agents", "type": "module", "main": "./dist/index.js", "bin": { - "agent": "./dist/cli.js", - "agentkit": "./dist/cli.js" + "agentage": "./dist/cli.js" + }, + "files": ["dist"], + "engines": { + "node": ">=22.0.0", + "npm": ">=10.0.0" }, "scripts": { - "dev": "tsx src/cli.ts", - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "type-check": "tsc --noEmit", - "lint": "eslint src --ext .ts", - "lint:fix": "eslint src --ext .ts --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "verify": "npm run type-check && npm run lint && npm run build && npm run test", - "clean": "rm -rf dist coverage", - "prepublishOnly": "npm run verify", - "format:check": "prettier --check .", - "format": "prettier --write ." + "lint": "eslint src", + "lint:fix": "eslint src --fix", + "format": "prettier --write \"src/**/*.ts\"", + "format:check": "prettier --check \"src/**/*.ts\"", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "clean": "rm -rf dist *.tsbuildinfo coverage", + "verify": "npm run type-check && npm run lint && npm run format:check && npm run test && npm run build", + "prepublishOnly": "npm run verify" + }, + "dependencies": { + "@agentage/core": "^0.2.0", + "@agentage/platform": "^0.2.0", + "chalk": "latest", + "commander": "latest", + "express": "latest", + "gray-matter": "latest", + "jiti": "latest", + "open": "latest", + "ws": "latest" + }, + "devDependencies": { + "@types/express": "latest", + "@types/node": "latest", + "@types/ws": "latest", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", + "eslint": "latest", + "eslint-config-prettier": "latest", + "eslint-plugin-prettier": "latest", + "prettier": "latest", + "typescript": "latest", + "vitest": "latest", + "@vitest/coverage-v8": "latest" }, - "keywords": [ - "cli", - "ai", - "agent", - "agentkit", - "typescript" - ], - "author": "Agentage", - "license": "MIT", "repository": { "type": "git", "url": "https://github.com/agentage/cli.git" }, - "bugs": { - "url": "https://github.com/agentage/cli/issues" - }, - "homepage": "https://github.com/agentage/cli#readme", - "engines": { - "node": ">=20.0.0", - "npm": ">=10.0.0" - }, - "dependencies": { - "chalk": "^5.6.2", - "commander": "^14.0.2", - "gray-matter": "^4.0.3", - "inquirer": "^13.0.1", - "js-yaml": "^4.1.0", - "node-machine-id": "1.1.12", - "open": "^11.0.0", - "yaml": "^2.8.1", - "zod": "^4.3.6" - }, - "devDependencies": { - "@types/inquirer": "^9.0.9", - "@types/jest": "^30.0.0", - "@types/js-yaml": "^4.0.9", - "@types/node": "^25.0.3", - "@typescript-eslint/eslint-plugin": "^8.46.3", - "@typescript-eslint/parser": "^8.46.3", - "eslint": "^9.39.1", - "jest": "^30.2.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.5", - "tsx": "^4.20.6", - "typescript": "^5.9.3" - } + "license": "MIT" } diff --git a/src/__mocks__/chalk.ts b/src/__mocks__/chalk.ts deleted file mode 100644 index 674a9f1..0000000 --- a/src/__mocks__/chalk.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ - -const createChainableColor = () => { - const fn = (s: string) => s; - // Add chainable methods - fn.underline = (s: string) => s; - fn.bold = (s: string) => s; - return fn; -}; - -const createBoldColor = () => { - const fn = (s: string) => s; - fn.yellow = (s: string) => s; - return fn; -}; - -const chalk = { - blue: createChainableColor(), - cyan: createChainableColor(), - gray: createChainableColor(), - green: createChainableColor(), - yellow: createChainableColor(), - red: createChainableColor(), - white: createChainableColor(), - bold: createBoldColor(), -}; - -export default chalk; diff --git a/src/cli.test.ts b/src/cli.test.ts deleted file mode 100644 index 18f402c..0000000 --- a/src/cli.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { execSync } from 'child_process'; -import { existsSync, mkdirSync, rmSync } from 'fs'; -import { join } from 'path'; - -const CLI_PATH = join(__dirname, 'cli.ts'); - -describe('CLI Commands', () => { - const testDir = 'test-cli-workspace'; - - beforeEach(() => { - // Create and change to test directory - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true }); - } - mkdirSync(testDir); - process.chdir(testDir); - }); - - afterEach(() => { - // Return to parent and clean up - process.chdir('..'); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true }); - } - }); - - test('CLI shows version', () => { - const output = execSync(`tsx ${CLI_PATH} --version`, { - encoding: 'utf-8', - }); - // Version is read from package.json dynamically - expect(output.trim()).toMatch(/^\d+\.\d+\.\d+$/); - }); - - test('CLI shows help', () => { - const output = execSync(`tsx ${CLI_PATH} --help`, { - encoding: 'utf-8', - }); - expect(output).toContain('AgentKit CLI'); - expect(output).toContain('init'); - expect(output).toContain('run'); - expect(output).toContain('list'); - expect(output).toContain('update'); - }); - - test('init command creates agent folder and config', () => { - const output = execSync(`tsx ${CLI_PATH} init test-agent`, { - encoding: 'utf-8', - }); - const expectedAgentPath = join('agents', 'test-agent.agent.md'); - const expectedConfigPath = 'agent.json'; - - expect(output).toContain('โœ… Created'); - expect(output).toContain('test-agent.agent.md'); - expect(output).toContain('agent.json'); - expect(existsSync(expectedAgentPath)).toBe(true); - expect(existsSync(expectedConfigPath)).toBe(true); - }); - - test('run command requires agent file', () => { - try { - execSync(`tsx ${CLI_PATH} run my-agent "hello"`, { - encoding: 'utf-8', - }); - fail('Should have thrown an error'); - } catch (error) { - const err = error as Error & { stdout: string; stderr: string }; - expect(err.stderr || err.stdout || err.message).toContain('Failed'); - } - }); - - test('list command runs successfully', () => { - const output = execSync(`tsx ${CLI_PATH} list`, { - encoding: 'utf-8', - }); - // Either shows no agents found or lists available agents (including global) - expect(output.includes('No agents found') || output.includes('Available Agents')).toBe(true); - }); -}); diff --git a/src/cli.ts b/src/cli.ts index 5980081..10fa2ef 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,268 +1,28 @@ #!/usr/bin/env node -import chalk from 'chalk'; import { Command } from 'commander'; -import { initCommand } from './commands/init.js'; -import { installCommand } from './commands/install.js'; -import { listCommand } from './commands/list.js'; -import { loginCommand } from './commands/login.js'; -import { logoutCommand } from './commands/logout.js'; -import { publishCommand } from './commands/publish.js'; -import { runCommand } from './commands/run.js'; -import { searchCommand } from './commands/search.js'; -import { updateCommand } from './commands/update.js'; -import { whoamiCommand } from './commands/whoami.js'; -import { version } from './index.js'; -import { AuthError, getMe } from './services/auth.service.js'; -import { loadAuth } from './utils/config.js'; -import { checkForUpdates } from './utils/version.js'; - -const displayBanner = (): void => { - console.log(); - console.log(chalk.cyan.bold(' โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—')); - console.log( - chalk.cyan.bold(' โ•‘') + - chalk.white.bold(' ๐Ÿค– AgentKit CLI ') + - chalk.cyan.bold('โ•‘') - ); - console.log(chalk.cyan.bold(' โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•')); - console.log(); -}; - -const displayVersionInfo = async (): Promise => { - console.log(chalk.gray(` Version: ${chalk.green.bold(version)}`)); - - // Display login status - try { - const auth = await loadAuth(); - if (auth.token) { - const user = await getMe(); - const displayName = user.name || user.email; - const aliasDisplay = user.verifiedAlias - ? chalk.gray(' (@') + chalk.green.bold(user.verifiedAlias) + chalk.gray(')') - : ''; - console.log(chalk.gray(' Logged in as: ') + chalk.green.bold(displayName) + aliasDisplay); - } else { - console.log( - chalk.gray(' Status: ') + - chalk.yellow('Not logged in') + - chalk.gray(' (run ') + - chalk.cyan('agent login') + - chalk.gray(')') - ); - } - } catch (error) { - if (error instanceof AuthError && error.code === 'session_expired') { - console.log( - chalk.gray(' Status: ') + - chalk.yellow('Session expired') + - chalk.gray(' (run ') + - chalk.cyan('agent login') + - chalk.gray(')') - ); - } else { - console.log( - chalk.gray(' Status: ') + - chalk.yellow('Not logged in') + - chalk.gray(' (run ') + - chalk.cyan('agent login') + - chalk.gray(')') - ); - } - } - - try { - const result = await checkForUpdates(version); - if (result.updateAvailable) { - console.log(); - console.log( - chalk.yellow.bold(' โš ๏ธ Update available: ') + - chalk.red(version) + - chalk.gray(' โ†’ ') + - chalk.green.bold(result.latestVersion) - ); - console.log( - chalk.yellow(' Run ') + - chalk.cyan.bold('agent update') + - chalk.yellow(' to update to the latest version') - ); - } - } catch { - // Silently ignore version check errors - } - console.log(); -}; - -const displayCustomHelp = (): void => { - console.log( - chalk.white.bold(' Usage: ') + chalk.cyan('agent') + chalk.gray(' [command] [options]') - ); - console.log(); - console.log(chalk.white.bold(' Commands:')); - console.log(); - - const commands = [ - { cmd: 'init', args: '[name]', desc: 'Initialize a new agent', icon: '๐Ÿš€' }, - { cmd: 'run', args: ' [prompt]', desc: 'Run an agent', icon: 'โ–ถ๏ธ ' }, - { cmd: 'list', args: '', desc: 'List all agents', icon: '๐Ÿ“‹' }, - { - cmd: 'publish', - args: '[path]', - desc: 'Publish agent to registry', - icon: '๐Ÿ“ค', - }, - { - cmd: 'install', - args: '', - desc: 'Install agent from registry', - icon: '๐Ÿ“ฅ', - }, - { - cmd: 'search', - args: '', - desc: 'Search for agents', - icon: '๐Ÿ”', - }, - { - cmd: 'login', - args: '', - desc: 'Login to the Agentage registry', - icon: '๐Ÿ”', - }, - { - cmd: 'logout', - args: '', - desc: 'Logout from the Agentage registry', - icon: '๐Ÿšช', - }, - { - cmd: 'whoami', - args: '', - desc: 'Display the currently logged in user', - icon: '๐Ÿ‘ค', - }, - { - cmd: 'update', - args: '', - desc: 'Update the CLI to the latest version', - icon: 'โฌ†๏ธ ', - }, - ]; - - for (const { cmd, args, desc, icon } of commands) { - const cmdStr = chalk.cyan.bold(cmd.padEnd(10)); - const argsStr = chalk.gray(args.padEnd(16)); - console.log(` ${icon} ${cmdStr} ${argsStr} ${chalk.white(desc)}`); - } - - console.log(); - console.log(chalk.white.bold(' Options:')); - console.log(); - console.log(` ${chalk.cyan.bold('-v, --version')} ${chalk.white('Display version number')}`); - console.log( - ` ${chalk.cyan.bold('-h, --help')} ${chalk.white('Display this help message')}` - ); - console.log(); - console.log(chalk.white.bold(' Examples:')); - console.log(); - console.log(chalk.white(' $ ') + chalk.cyan('agent init my-agent')); - console.log( - chalk.white(' $ ') + chalk.cyan('agent run my-agent') + chalk.white(' "Hello, how are you?"') - ); - console.log(chalk.white(' $ ') + chalk.cyan('agent list')); - console.log(); -}; +import { registerRun } from './commands/run.js'; +import { registerAgents } from './commands/agents.js'; +import { registerRuns } from './commands/runs.js'; +import { registerMachines } from './commands/machines.js'; +import { registerStatus } from './commands/status.js'; +import { registerLogin } from './commands/login.js'; +import { registerLogout } from './commands/logout.js'; +import { registerLogs } from './commands/logs.js'; +import { registerDaemon } from './commands/daemon-cmd.js'; const program = new Command(); -program - .name('agent') - .description('CLI tool for creating and running AI agents locally') - .version(version, '-v, --version', 'Display version number') - .configureHelp({ - formatHelp: () => '', // Suppress default help - }) - .helpOption('-h, --help', 'Display this help message') - .action(() => { - // When running just "agent" or "agent --help" - displayBanner(); - displayVersionInfo().then(() => { - displayCustomHelp(); - }); - }); - -program - .command('init') - .description('Initialize a new agent') - .argument('[name]', 'Agent name') - .option('-g, --global', 'Initialize in global directory (~/.agentage)') - .action(initCommand); - -program - .command('run') - .description('Run an agent') - .argument('', 'Agent name') - .argument('[prompt]', 'Prompt to send to the agent') - .action(runCommand); - -program.command('list').description('List all agents').action(listCommand); - -// Registry commands -program - .command('publish') - .description('Publish agent to registry') - .argument('[path]', 'Path to agent file') - .option('-v, --visibility ', 'Visibility (public or private)', 'public') - .option('--version ', 'Override version') - .option('-t, --tag ', 'Add tags') - .option('-c, --changelog ', 'Changelog message') - .option('--dry-run', 'Validate without publishing') - .action(publishCommand); - -program - .command('install') - .description('Install agent from registry') - .argument('', 'Agent name (owner/name[@version])') - .option('-g, --global', 'Install to global location') - .option('-l, --local', 'Install to local project') - .option('-f, --force', 'Overwrite existing') - .action(installCommand); - -program - .command('search') - .description('Search for agents in registry') - .argument('', 'Search query') - .option('-n, --limit ', 'Number of results', '10') - .option('-p, --page ', 'Page number', '1') - .option('--json', 'Output as JSON') - .action(searchCommand); - -// Auth commands -program.command('login').description('Login to the Agentage registry').action(loginCommand); - -program.command('logout').description('Logout from the Agentage registry').action(logoutCommand); - -program.command('whoami').description('Display the currently logged in user').action(whoamiCommand); +program.name('agentage').description('Agentage CLI โ€” control plane for AI agents').version('0.2.0'); -program.command('update').description('Update the CLI to the latest version').action(updateCommand); +registerRun(program); +registerAgents(program); +registerRuns(program); +registerMachines(program); +registerStatus(program); +registerLogin(program); +registerLogout(program); +registerLogs(program); +registerDaemon(program); -// Handle help flag explicitly -if (process.argv.includes('-h') || process.argv.includes('--help')) { - if (process.argv.length === 3) { - // Just "agent --help" or "agent -h" - displayBanner(); - displayVersionInfo().then(() => { - displayCustomHelp(); - }); - } else { - program.parse(); - } -} else if (process.argv.length === 2) { - // Just "agent" with no args - displayBanner(); - displayVersionInfo().then(() => { - displayCustomHelp(); - }); -} else { - program.parse(); -} +program.parse(); diff --git a/src/commands/agents.ts b/src/commands/agents.ts new file mode 100644 index 0000000..2cb73f5 --- /dev/null +++ b/src/commands/agents.ts @@ -0,0 +1,57 @@ +import { type Command } from 'commander'; +import chalk from 'chalk'; +import { type AgentManifest } from '@agentage/core'; +import { ensureDaemon } from '../utils/ensure-daemon.js'; +import { get, post } from '../utils/daemon-client.js'; + +export const registerAgents = (program: Command): void => { + program + .command('agents') + .description('List discovered agents') + .option('--refresh', 'Rescan directories first') + .option('--all', 'Show agents from all connected machines') + .option('--json', 'JSON output') + .action(async (opts: { refresh?: boolean; all?: boolean; json?: boolean }) => { + await ensureDaemon(); + + if (opts.all) { + console.error(chalk.red("Not connected to hub. Run 'agentage login' first.")); + process.exitCode = 1; + return; + } + + let agents: AgentManifest[]; + if (opts.refresh) { + agents = await post('/api/agents/refresh'); + } else { + agents = await get('/api/agents'); + } + + if (opts.json) { + console.log(JSON.stringify(agents, null, 2)); + return; + } + + if (agents.length === 0) { + console.log(chalk.gray('No agents discovered.')); + return; + } + + const nameWidth = Math.max(12, ...agents.map((a) => a.name.length)) + 2; + const descWidth = Math.max(12, ...agents.map((a) => (a.description || '').length)) + 2; + + console.log( + chalk.bold('NAME'.padEnd(nameWidth)) + + chalk.bold('DESCRIPTION'.padEnd(descWidth)) + + chalk.bold('PATH') + ); + + for (const agent of agents) { + console.log( + agent.name.padEnd(nameWidth) + + (agent.description || '').padEnd(descWidth) + + chalk.gray(agent.path) + ); + } + }); +}; diff --git a/src/commands/daemon-cmd.ts b/src/commands/daemon-cmd.ts new file mode 100644 index 0000000..632f413 --- /dev/null +++ b/src/commands/daemon-cmd.ts @@ -0,0 +1,29 @@ +import { type Command } from 'commander'; +import chalk from 'chalk'; +import { getDaemonPid, restartDaemon, stopDaemon } from '../daemon/daemon.js'; + +export const registerDaemon = (program: Command): void => { + const daemon = program.command('daemon').description('Manage the daemon'); + + daemon + .command('stop') + .description('Stop the daemon') + .action(() => { + const pid = getDaemonPid(); + if (pid === null) { + console.log(chalk.gray('Daemon is not running.')); + return; + } + stopDaemon(); + console.log(chalk.green('Daemon stopped.')); + }); + + daemon + .command('restart') + .description('Restart the daemon') + .action(async () => { + await restartDaemon(); + const pid = getDaemonPid(); + console.log(chalk.green(`Daemon restarted (PID ${pid}, port 4243).`)); + }); +}; diff --git a/src/commands/init.test.ts b/src/commands/init.test.ts deleted file mode 100644 index f3ba255..0000000 --- a/src/commands/init.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs'; -import { homedir } from 'os'; -import { join } from 'path'; -import { initCommand } from './init.js'; - -describe('initCommand', () => { - const testDir = 'test-init-workspace'; - const globalDir = join(homedir(), '.agentage'); - - beforeEach(() => { - // Create and change to test directory - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true }); - } - mkdirSync(testDir); - process.chdir(testDir); - }); - - afterEach(() => { - // Return to parent and clean up - process.chdir('..'); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true }); - } - }); - - test('creates agent.json in current directory and agents folder with default name', async () => { - await initCommand(); - - const agentFilePath = join('agents', 'my-agent.agent.md'); - const configFilePath = 'agent.json'; - - expect(existsSync('agents')).toBe(true); - expect(existsSync(agentFilePath)).toBe(true); - expect(existsSync(configFilePath)).toBe(true); - - const agentContent = readFileSync(agentFilePath, 'utf-8'); - expect(agentContent).toContain('name: my-agent'); - expect(agentContent).toContain('You are a helpful AI assistant'); - - const configContent = JSON.parse(readFileSync(configFilePath, 'utf-8')); - expect(configContent.paths).toEqual(['agents/']); - }); - - test('creates agent file with custom name', async () => { - await initCommand('custom-agent'); - - const agentFilePath = join('agents', 'custom-agent.agent.md'); - - expect(existsSync('agents')).toBe(true); - expect(existsSync(agentFilePath)).toBe(true); - - const agentContent = readFileSync(agentFilePath, 'utf-8'); - expect(agentContent).toContain('name: custom-agent'); - }); - - test('creates agent.json config with path property', async () => { - await initCommand('test-agent'); - - const configFilePath = 'agent.json'; - expect(existsSync(configFilePath)).toBe(true); - - const configContent = JSON.parse(readFileSync(configFilePath, 'utf-8')); - expect(configContent).toEqual({ paths: ['agents/'] }); - }); - - test('handles errors gracefully', async () => { - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - const consoleError = jest.spyOn(console, 'error').mockImplementation(); - - const originalWriteFile = require('fs/promises').writeFile; - jest.spyOn(require('fs/promises'), 'writeFile').mockRejectedValue(new Error('Write failed')); - - await expect(initCommand('test')).rejects.toThrow('process.exit called'); - expect(consoleError).toHaveBeenCalledWith(expect.stringContaining('โŒ Failed: Write failed')); - expect(mockExit).toHaveBeenCalledWith(1); - - // Restore - require('fs/promises').writeFile = originalWriteFile; - mockExit.mockRestore(); - consoleError.mockRestore(); - }); - - test('creates agent in global directory with --global flag', async () => { - await initCommand('global-agent', { global: true }); - - const globalAgentFilePath = join(globalDir, 'agents', 'global-agent.agent.md'); - const globalConfigFilePath = join(globalDir, 'agent.json'); - - expect(existsSync(join(globalDir, 'agents'))).toBe(true); - expect(existsSync(globalAgentFilePath)).toBe(true); - expect(existsSync(globalConfigFilePath)).toBe(true); - - const agentContent = readFileSync(globalAgentFilePath, 'utf-8'); - expect(agentContent).toContain('name: global-agent'); - - const configContent = JSON.parse(readFileSync(globalConfigFilePath, 'utf-8')); - expect(configContent.paths).toEqual(['agents/']); - - // Cleanup global test files - rmSync(globalAgentFilePath); - }); -}); diff --git a/src/commands/init.ts b/src/commands/init.ts deleted file mode 100644 index c895358..0000000 --- a/src/commands/init.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { mkdir, writeFile } from 'fs/promises'; -import { homedir } from 'os'; -import { join } from 'path'; - -const sampleAgentTemplate = `--- -name: {{name}} -description: An AI assistant agent -argument-hint: Describe what you want help with -tools: [] -handoffs: [] ---- -You are a helpful AI assistant. - -Respond clearly and concisely to user requests. -`; - -export interface AgentConfig { - paths: string[]; -} - -export interface InitOptions { - global?: boolean; -} - -/** - * Get global agentage directory (~/.agentage) - */ -const getGlobalDir = (): string => join(homedir(), '.agentage'); - -export const initCommand = async (name?: string, options?: InitOptions): Promise => { - const agentName = name || 'my-agent'; - const isGlobal = options?.global ?? false; - - // Determine base directory - const baseDir = isGlobal ? getGlobalDir() : '.'; - const agentsDir = join(baseDir, 'agents'); - const agentFilePath = join(agentsDir, `${agentName}.agent.md`); - const configFilePath = join(baseDir, 'agent.json'); - - const agentContent = sampleAgentTemplate.replace(/{{name}}/g, agentName); - - const agentConfig: AgentConfig = { - paths: ['agents/'], - }; - - try { - // Create agents directory if it doesn't exist - await mkdir(agentsDir, { recursive: true }); - - // Create agent.md file based on sample.agent.md template - await writeFile(agentFilePath, agentContent, 'utf-8'); - console.log(`โœ… Created ${agentFilePath}`); - - // Create agent.json config file - await writeFile(configFilePath, JSON.stringify(agentConfig, null, 2), 'utf-8'); - console.log(`โœ… Created ${configFilePath}`); - } catch (error) { - console.error(`โŒ Failed: ${(error as Error).message}`); - process.exit(1); - } -}; diff --git a/src/commands/install.test.ts b/src/commands/install.test.ts deleted file mode 100644 index dc1f5f0..0000000 --- a/src/commands/install.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; -import { installCommand } from './install.js'; - -// Mock os.homedir to isolate tests from real global agents directory -jest.mock('os', () => ({ - ...jest.requireActual('os'), - homedir: jest.fn(), -})); - -// Mock dependencies -jest.mock('../services/registry.service.js', () => ({ - getAgent: jest.fn(), - getAgentVersion: jest.fn(), - RegistryApiError: class extends Error { - constructor( - message: string, - public code: string, - public statusCode: number - ) { - super(message); - } - }, -})); - -describe('installCommand', () => { - const testDir = 'test-install-workspace'; - const originalCwd = process.cwd(); - let mockExit: jest.SpyInstance; - let mockConsoleError: jest.SpyInstance; - let mockConsoleLog: jest.SpyInstance; - - beforeEach(() => { - process.chdir(originalCwd); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true, force: true }); - } - mkdirSync(testDir, { recursive: true }); - process.chdir(testDir); - jest.clearAllMocks(); - - // Mock homedir to return test directory - (homedir as jest.Mock).mockReturnValue(testDir); - - mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => { - throw new Error(`process.exit(${code})`); - }); - mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - }); - - afterEach(() => { - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - process.chdir(originalCwd); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true, force: true }); - } - }); - - test('rejects invalid identifier format', async () => { - await expect(installCommand('invalid')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Invalid format')); - }); - - test('installs agent to local directory when agent.json exists', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockResolvedValue({ - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: '---\nname: test-agent\n---\nContent', - }); - - writeFileSync('agent.json', JSON.stringify({ paths: ['agents/'] })); - - await installCommand('testuser/test-agent'); - - expect(existsSync(join('agents', 'test-agent.agent.md'))).toBe(true); - const content = readFileSync(join('agents', 'test-agent.agent.md'), 'utf-8'); - expect(content).toContain('name: test-agent'); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Installed')); - }); - - test('installs specific version', async () => { - const { getAgentVersion } = require('../services/registry.service.js'); - - getAgentVersion.mockResolvedValue({ - version: '2025-11-01', - content: '---\nname: test-agent\nversion: 2025-11-01\n---\nOlder content', - }); - - writeFileSync('agent.json', '{}'); - - await installCommand('testuser/test-agent@2025-11-01'); - - const content = readFileSync(join('agents', 'test-agent.agent.md'), 'utf-8'); - expect(content).toContain('version: 2025-11-01'); - }); - - test('refuses to overwrite without --force', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockResolvedValue({ - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: '---\nname: test-agent\n---\nNew content', - }); - - mkdirSync('agents'); - writeFileSync(join('agents', 'test-agent.agent.md'), 'existing content'); - writeFileSync('agent.json', '{}'); - - try { - await installCommand('testuser/test-agent'); - fail('Should have thrown'); - } catch { - // Expected to exit - } - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('already exists')); - - // Verify file was not overwritten - const content = readFileSync(join('agents', 'test-agent.agent.md'), 'utf-8'); - expect(content).toBe('existing content'); - }); - - test('overwrites with --force flag', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockResolvedValue({ - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: '---\nname: test-agent\n---\nNew content', - }); - - mkdirSync('agents'); - writeFileSync(join('agents', 'test-agent.agent.md'), 'existing content'); - writeFileSync('agent.json', '{}'); - - await installCommand('testuser/test-agent', { force: true }); - - const content = readFileSync(join('agents', 'test-agent.agent.md'), 'utf-8'); - expect(content).toContain('New content'); - }); - - test('installs to global directory with --global flag', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockResolvedValue({ - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: '---\nname: test-agent\n---\nContent', - }); - - await installCommand('testuser/test-agent', { global: true }); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Installed')); - }); - - test('installs to local directory with --local flag', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockResolvedValue({ - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: '---\nname: test-agent\n---\nContent', - }); - - await installCommand('testuser/test-agent', { local: true }); - - expect(existsSync(join('agents', 'test-agent.agent.md'))).toBe(true); - }); - - test('handles 404 error', async () => { - const { getAgent, RegistryApiError } = require('../services/registry.service.js'); - - getAgent.mockRejectedValue(new RegistryApiError('Not found', 'not_found', 404)); - - writeFileSync('agent.json', '{}'); - - await expect(installCommand('testuser/nonexistent')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('not found')); - }); - - test('handles 403 error', async () => { - const { getAgent, RegistryApiError } = require('../services/registry.service.js'); - - getAgent.mockRejectedValue(new RegistryApiError('Forbidden', 'forbidden', 403)); - - writeFileSync('agent.json', '{}'); - - await expect(installCommand('testuser/private-agent')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Access denied')); - }); - - test('handles version without content', async () => { - const { getAgentVersion } = require('../services/registry.service.js'); - - getAgentVersion.mockResolvedValue({ - version: '2025-11-01', - content: null, - }); - - writeFileSync('agent.json', '{}'); - - await expect(installCommand('testuser/test-agent@2025-11-01')).rejects.toThrow( - 'process.exit(1)' - ); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('not available')); - }); - - test('handles generic error', async () => { - const { getAgent } = require('../services/registry.service.js'); - - getAgent.mockRejectedValue(new Error('Network error')); - - writeFileSync('agent.json', '{}'); - - await expect(installCommand('testuser/test-agent')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Network error')); - }); -}); diff --git a/src/commands/install.ts b/src/commands/install.ts deleted file mode 100644 index d9fc0e3..0000000 --- a/src/commands/install.ts +++ /dev/null @@ -1,125 +0,0 @@ -import chalk from 'chalk'; -import { existsSync } from 'fs'; -import { access, mkdir, writeFile } from 'fs/promises'; -import { homedir } from 'os'; -import { join } from 'path'; -import { getAgent, getAgentVersion, RegistryApiError } from '../services/registry.service.js'; -import { parseAgentIdentifier } from '../utils/agent-parser.js'; - -interface InstallOptions { - global?: boolean; - local?: boolean; - force?: boolean; -} - -/** - * Determine where to install the agent - */ -const getInstallDir = async (options: InstallOptions): Promise => { - if (options.global) { - return join(homedir(), '.agentage', 'agents'); - } - - if (options.local) { - return 'agents'; - } - - // Default: check if in a project (has agent.json) - if (existsSync('agent.json')) { - return 'agents'; - } - - return join(homedir(), '.agentage', 'agents'); -}; - -/** - * Install command - install agent from registry - */ -export const installCommand = async ( - identifier: string, - options: InstallOptions = {} -): Promise => { - try { - // 1. Parse identifier: owner/name[@version] - const { owner, name, version } = parseAgentIdentifier(identifier); - - if (!owner || !name) { - console.error(chalk.red('โŒ Invalid format.')); - console.log( - 'Use:', - chalk.cyan('agent install owner/name'), - 'or', - chalk.cyan('agent install owner/name@version') - ); - process.exit(1); - } - - const versionLabel = version ? `@${version}` : ''; - console.log(chalk.cyan(`๐Ÿ“ฅ Installing ${owner}/${name}${versionLabel}...`)); - - // 2. Fetch agent - let content: string; - let installedVersion: string; - - if (version) { - const versionData = await getAgentVersion(owner, name, version); - if (!versionData.content) { - console.error(chalk.red('โŒ Version content not available.')); - process.exit(1); - } - content = versionData.content; - installedVersion = version; - } else { - const agent = await getAgent(owner, name); - content = agent.latestContent; - installedVersion = agent.latestVersion; - } - - // 3. Determine install location - const installDir = await getInstallDir(options); - const fileName = `${name}.agent.md`; - const filePath = join(installDir, fileName); - - // 4. Check if exists - if (!options.force) { - let fileExists = false; - try { - await access(filePath); - fileExists = true; - } catch { - // File doesn't exist, good - } - - if (fileExists) { - console.error(chalk.red(`โŒ Agent already exists at ${filePath}`)); - console.log(chalk.gray(' Use --force to overwrite.')); - process.exit(1); - } - } - - // 5. Write file - await mkdir(installDir, { recursive: true }); - await writeFile(filePath, content, 'utf-8'); - - // 6. Success - console.log(); - console.log(chalk.green(`โœ… Installed ${owner}/${name}@${installedVersion}`)); - console.log(chalk.gray(` Location: ${filePath}`)); - console.log(chalk.gray(` Run with: agent run ${name}`)); - console.log(); - } catch (error) { - if (error instanceof RegistryApiError) { - if (error.statusCode === 404) { - console.error(chalk.red(`โŒ Agent not found: ${identifier}`)); - } else if (error.statusCode === 403) { - console.error(chalk.red('โŒ Access denied. This agent may be private.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate.'); - } else { - console.error(chalk.red(`โŒ ${error.message}`)); - } - } else { - console.error(chalk.red(`โŒ Failed: ${(error as Error).message}`)); - } - process.exit(1); - } -}; diff --git a/src/commands/list.test.ts b/src/commands/list.test.ts deleted file mode 100644 index dfe134c..0000000 --- a/src/commands/list.test.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; -import { homedir } from 'os'; -import { join } from 'path'; -import { listCommand } from './list.js'; - -// Mock os.homedir to isolate tests from real global agents -jest.mock('os', () => ({ - ...jest.requireActual('os'), - homedir: jest.fn(), -})); - -describe('listCommand', () => { - const testAgentsDir = 'test-agents'; - const originalCwd = process.cwd(); - - beforeEach(() => { - // Ensure we're in the original directory - process.chdir(originalCwd); - - if (existsSync(testAgentsDir)) { - rmSync(testAgentsDir, { recursive: true, force: true }); - } - mkdirSync(testAgentsDir, { recursive: true }); - process.chdir(testAgentsDir); - - // Mock homedir to return test directory (no global agents) - (homedir as jest.Mock).mockReturnValue(testAgentsDir); - }); - - afterEach(() => { - process.chdir(originalCwd); - if (existsSync(testAgentsDir)) { - rmSync(testAgentsDir, { recursive: true, force: true }); - } - }); - - test('shows message when no agents directory exists', async () => { - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('No agents found.')); - - consoleLog.mockRestore(); - }); - - test('shows message when agents directory is empty', async () => { - mkdirSync('agents'); - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('No agents found.')); - - consoleLog.mockRestore(); - }); - - test('lists valid agent yml files', async () => { - mkdirSync('agents'); - const validAgent = `name: test-agent -model: gpt-4 -instructions: Test instructions -tools: [] -variables: {}`; - writeFileSync(join('agents', 'test-agent.yml'), validAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith('\n๐Ÿ“‹ Available Agents:\n'); - expect(consoleLog).toHaveBeenCalledWith(' ๐Ÿ“ Local:'); - expect(consoleLog).toHaveBeenCalledWith(' โœ… test-agent (gpt-4)'); - - consoleLog.mockRestore(); - }); - - test('lists valid agent.md files', async () => { - mkdirSync('agents'); - const validAgentMd = `--- -name: my-agent -description: Test agent -model: gpt-4o ---- -You are a helpful assistant.`; - writeFileSync(join('agents', 'my-agent.agent.md'), validAgentMd); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith('\n๐Ÿ“‹ Available Agents:\n'); - expect(consoleLog).toHaveBeenCalledWith(' ๐Ÿ“ Local:'); - expect(consoleLog).toHaveBeenCalledWith(' โœ… my-agent (gpt-4o)'); - - consoleLog.mockRestore(); - }); - - test('shows validation errors for invalid agent files', async () => { - mkdirSync('agents'); - const invalidAgent = `invalid: yaml -content: here`; - writeFileSync(join('agents', 'invalid-agent.yml'), invalidAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith('\n๐Ÿ“‹ Available Agents:\n'); - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('โŒ invalid-agent -')); - - consoleLog.mockRestore(); - }); - - test('lists both valid and invalid agents', async () => { - mkdirSync('agents'); - - const validAgent = `name: valid-agent -model: gpt-4 -instructions: Test instructions -tools: [] -variables: {}`; - writeFileSync(join('agents', 'valid-agent.yml'), validAgent); - - const invalidAgent = `name: missing-instructions -model: gpt-4`; - writeFileSync(join('agents', 'invalid-agent.yml'), invalidAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith(' โœ… valid-agent (gpt-4)'); - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('โŒ invalid-agent -')); - - consoleLog.mockRestore(); - }); - - test('uses paths from agent.json config', async () => { - mkdirSync('custom-agents'); - const config = { paths: ['custom-agents/'] }; - writeFileSync('agent.json', JSON.stringify(config)); - - const validAgent = `name: custom-agent -model: gpt-4 -instructions: Test instructions`; - writeFileSync(join('custom-agents', 'custom-agent.yml'), validAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith(' โœ… custom-agent (gpt-4)'); - - consoleLog.mockRestore(); - }); - - test('handles errors gracefully', async () => { - mkdirSync('agents'); - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await listCommand(); - - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('No agents found.')); - - consoleLog.mockRestore(); - }); -}); diff --git a/src/commands/list.ts b/src/commands/list.ts deleted file mode 100644 index 86cc888..0000000 --- a/src/commands/list.ts +++ /dev/null @@ -1,187 +0,0 @@ -import chalk from 'chalk'; -import { existsSync } from 'fs'; -import { readdir, readFile } from 'fs/promises'; -import { homedir } from 'os'; -import { join } from 'path'; -import { parse } from 'yaml'; -import { agentYamlSchema } from '../schemas/agent.schema.js'; -import { AgentConfig } from './init.js'; - -interface AgentInfo { - name: string; - model: string; - location: 'global' | 'local'; - path: string; - error?: string; -} - -/** - * Get global agents directory (~/.agentage/agents) - */ -const getGlobalAgentsDir = (): string => join(homedir(), '.agentage', 'agents'); - -/** - * Load local agent.json config from current directory - */ -const loadLocalAgentConfig = async (): Promise => { - try { - const configPath = 'agent.json'; - const content = await readFile(configPath, 'utf-8'); - return JSON.parse(content) as AgentConfig; - } catch { - return null; - } -}; - -/** - * Load global agent.json config from ~/.agentage - */ -const loadGlobalAgentConfig = async (): Promise => { - try { - const configPath = join(homedir(), '.agentage', 'agent.json'); - const content = await readFile(configPath, 'utf-8'); - return JSON.parse(content) as AgentConfig; - } catch { - return null; - } -}; - -/** - * Get agent paths from config or use defaults - */ -const getAgentPaths = async ( - config: AgentConfig | null, - defaultPath: string -): Promise => { - if (config?.paths && config.paths.length > 0) { - return config.paths.filter((p) => existsSync(p)); - } - return existsSync(defaultPath) ? [defaultPath] : []; -}; - -/** - * Scan a directory for agent files (.yml and .agent.md) - */ -const scanAgentsInDir = async (dir: string, location: 'global' | 'local'): Promise => { - const agents: AgentInfo[] = []; - - try { - const files = await readdir(dir); - const agentFiles = files.filter((f) => f.endsWith('.yml') || f.endsWith('.agent.md')); - - for (const file of agentFiles) { - const filePath = join(dir, file); - try { - const content = await readFile(filePath, 'utf-8'); - - if (file.endsWith('.agent.md')) { - // Parse frontmatter from .agent.md files - const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); - if (frontmatterMatch) { - const frontmatter = parse(frontmatterMatch[1]); - agents.push({ - name: frontmatter.name || file.replace('.agent.md', ''), - model: frontmatter.model || 'default', - location, - path: filePath, - }); - } else { - agents.push({ - name: file.replace('.agent.md', ''), - model: 'unknown', - location, - path: filePath, - error: 'Missing frontmatter', - }); - } - } else { - // Parse .yml files - const yaml = parse(content); - const validated = agentYamlSchema.parse(yaml); - agents.push({ - name: validated.name, - model: validated.model, - location, - path: filePath, - }); - } - } catch (error) { - const name = file.replace('.yml', '').replace('.agent.md', ''); - agents.push({ - name, - model: 'unknown', - location, - path: filePath, - error: error instanceof Error ? error.message : 'Invalid format', - }); - } - } - } catch { - // Directory doesn't exist or can't be read - } - - return agents; -}; - -export const listCommand = async (): Promise => { - try { - const allAgents: AgentInfo[] = []; - - // 1. Load global agents - const globalConfig = await loadGlobalAgentConfig(); - const globalDefaultDir = getGlobalAgentsDir(); - const globalPaths = await getAgentPaths(globalConfig, globalDefaultDir); - - for (const path of globalPaths) { - const agents = await scanAgentsInDir(path, 'global'); - allAgents.push(...agents); - } - - // 2. Load local agents - const localConfig = await loadLocalAgentConfig(); - const localDefaultDir = 'agents'; - const localPaths = await getAgentPaths(localConfig, localDefaultDir); - - for (const path of localPaths) { - const agents = await scanAgentsInDir(path, 'local'); - allAgents.push(...agents); - } - - // 3. Display results - if (allAgents.length === 0) { - console.log('No agents found. Run ' + chalk.cyan('agent init') + ' to create one.'); - return; - } - - const globalAgents = allAgents.filter((a) => a.location === 'global'); - const localAgents = allAgents.filter((a) => a.location === 'local'); - - console.log('\n๐Ÿ“‹ Available Agents:\n'); - - if (globalAgents.length > 0) { - console.log(' ๐ŸŒ Global:'); - for (const agent of globalAgents) { - if (agent.error) { - console.log(` โŒ ${agent.name} - ${agent.error}`); - } else { - console.log(` โœ… ${agent.name} (${agent.model})`); - } - } - console.log(); - } - - if (localAgents.length > 0) { - console.log(' ๐Ÿ“ Local:'); - for (const agent of localAgents) { - if (agent.error) { - console.log(` โŒ ${agent.name} - ${agent.error}`); - } else { - console.log(` โœ… ${agent.name} (${agent.model})`); - } - } - console.log(); - } - } catch (error) { - console.error(`โŒ Failed: ${(error as Error).message}`); - } -}; diff --git a/src/commands/login.test.ts b/src/commands/login.test.ts deleted file mode 100644 index 66a1414..0000000 --- a/src/commands/login.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as authService from '../services/auth.service.js'; -import { AuthError } from '../services/auth.service.js'; -import * as configUtils from '../utils/config.js'; -import { loginCommand } from './login.js'; - -// Mock dependencies -jest.mock('../services/auth.service.js', () => { - const original = jest.requireActual('../services/auth.service.js'); - return { - ...original, - requestDeviceCode: jest.fn(), - pollForToken: jest.fn(), - }; -}); -jest.mock('../utils/config.js'); -jest.mock('open', () => ({ - default: jest.fn(), -})); - -const mockRequestDeviceCode = authService.requestDeviceCode as jest.MockedFunction< - typeof authService.requestDeviceCode ->; -const mockPollForToken = authService.pollForToken as jest.MockedFunction< - typeof authService.pollForToken ->; -const mockLoadAuth = configUtils.loadAuth as jest.MockedFunction; -const mockSaveAuth = configUtils.saveAuth as jest.MockedFunction; -const mockGetRegistryUrl = configUtils.getRegistryUrl as jest.MockedFunction< - typeof configUtils.getRegistryUrl ->; - -describe('loginCommand', () => { - let consoleSpy: jest.SpyInstance; - let consoleErrorSpy: jest.SpyInstance; - let processExitSpy: jest.SpyInstance; - - beforeEach(() => { - jest.clearAllMocks(); - consoleSpy = jest.spyOn(console, 'log').mockImplementation(); - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - mockGetRegistryUrl.mockResolvedValue('https://dev.agentage.io'); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - consoleErrorSpy.mockRestore(); - processExitSpy.mockRestore(); - }); - - it('shows message when already logged in', async () => { - mockLoadAuth.mockResolvedValue({ token: 'existing-token' }); - - await loginCommand(); - - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Already logged in'), - expect.any(String), - expect.any(String), - expect.any(String) - ); - }); - - it('completes login flow successfully', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockResolvedValue({ - device_code: 'device123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }); - mockPollForToken.mockResolvedValue({ - access_token: 'new-token', - token_type: 'Bearer', - user: { id: '1', email: 'test@example.com', name: 'Test User' }, - }); - mockSaveAuth.mockResolvedValue(undefined); - - await loginCommand(); - - expect(mockRequestDeviceCode).toHaveBeenCalled(); - expect(mockPollForToken).toHaveBeenCalledWith('device123', 5, 900); - expect(mockSaveAuth).toHaveBeenCalledWith({ - token: 'new-token', - user: { id: '1', email: 'test@example.com', name: 'Test User' }, - expiresAt: undefined, - }); - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Logged in as'), - expect.stringContaining('Test User') - ); - }); - - it('handles login errors', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockRejectedValue(new AuthError('Failed', 'request_failed')); - - await expect(loginCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Login failed'), 'Failed'); - }); - - it('handles expired token error with hint', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockResolvedValue({ - device_code: 'device123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }); - mockPollForToken.mockRejectedValue(new AuthError('Login timed out', 'expired_token')); - - await expect(loginCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith( - expect.stringContaining('Login failed'), - 'Login timed out' - ); - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Run'), - expect.stringContaining('agent login'), - expect.stringContaining('to try again') - ); - }); - - it('handles non-AuthError errors', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockRejectedValue(new Error('Network error')); - - await expect(loginCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith( - expect.stringContaining('Login failed'), - 'Network error' - ); - }); - - it('shows email when user has no name', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockResolvedValue({ - device_code: 'device123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }); - mockPollForToken.mockResolvedValue({ - access_token: 'new-token', - token_type: 'Bearer', - user: { id: '1', email: 'test@example.com' }, - }); - mockSaveAuth.mockResolvedValue(undefined); - - await loginCommand(); - - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Logged in as'), - expect.stringContaining('test@example.com') - ); - }); - - it('shows generic success when no user info', async () => { - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockResolvedValue({ - device_code: 'device123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }); - mockPollForToken.mockResolvedValue({ - access_token: 'new-token', - token_type: 'Bearer', - }); - mockSaveAuth.mockResolvedValue(undefined); - - await loginCommand(); - - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Login successful')); - }); - - it('handles browser open failure gracefully', async () => { - // Mock open to throw an error - jest.doMock('open', () => ({ - default: jest.fn().mockRejectedValue(new Error('Cannot open browser')), - })); - - mockLoadAuth.mockResolvedValue({}); - mockRequestDeviceCode.mockResolvedValue({ - device_code: 'device123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }); - mockPollForToken.mockResolvedValue({ - access_token: 'new-token', - token_type: 'Bearer', - user: { id: '1', email: 'test@example.com', name: 'Test User' }, - }); - mockSaveAuth.mockResolvedValue(undefined); - - await loginCommand(); - - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Logged in as'), - expect.stringContaining('Test User') - ); - }); -}); diff --git a/src/commands/login.ts b/src/commands/login.ts index 71be877..23e1fb6 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -1,85 +1,12 @@ +import { type Command } from 'commander'; import chalk from 'chalk'; -import { AuthError, pollForToken, requestDeviceCode } from '../services/auth.service.js'; -import { getRegistryUrl, loadAuth, saveAuth } from '../utils/config.js'; -/** - * Login command - authenticate via device authorization flow - */ -export const loginCommand = async (): Promise => { - try { - // Check if already logged in - const auth = await loadAuth(); - if (auth.token) { - console.log( - chalk.yellow('โš ๏ธ Already logged in.'), - 'Run', - chalk.cyan('agent logout'), - 'first to switch accounts.' - ); - return; - } - - const registryUrl = await getRegistryUrl(); - console.log(chalk.blue('๐Ÿ” Logging in to'), chalk.cyan(registryUrl)); - console.log(); - - // Request device code - console.log(chalk.gray('Requesting authentication code...')); - const deviceCode = await requestDeviceCode(); - - // Display instructions - console.log(); - console.log(chalk.bold('To complete login:')); - console.log(); - console.log(' 1. Open:', chalk.cyan.underline(deviceCode.verification_uri)); - console.log(' 2. Enter code:', chalk.bold.yellow(deviceCode.user_code)); - console.log(); - - // Try to open browser automatically - try { - const open = await import('open'); - await open.default(deviceCode.verification_uri); - console.log(chalk.gray('(Browser opened automatically)')); - } catch { - console.log(chalk.gray('(Could not open browser automatically - please open manually)')); - } - - console.log(); - console.log(chalk.gray('Waiting for authentication...')); - - // Poll for token - const tokenResponse = await pollForToken( - deviceCode.device_code, - deviceCode.interval, - deviceCode.expires_in - ); - - // Save token to auth.json (config.json with deviceId/registry is untouched) - await saveAuth({ - token: tokenResponse.access_token, - user: tokenResponse.user, - expiresAt: tokenResponse.expires_in - ? new Date(Date.now() + tokenResponse.expires_in * 1000).toISOString() - : undefined, +export const registerLogin = (program: Command): void => { + program + .command('login') + .description('Authenticate with hub') + .option('--hub ', 'Hub URL') + .action(() => { + console.log(chalk.yellow('Hub sync not yet available.')); }); - - console.log(); - if (tokenResponse.user?.name) { - console.log(chalk.green('โœ… Logged in as'), chalk.bold(tokenResponse.user.name)); - } else if (tokenResponse.user?.email) { - console.log(chalk.green('โœ… Logged in as'), chalk.bold(tokenResponse.user.email)); - } else { - console.log(chalk.green('โœ… Login successful!')); - } - } catch (error) { - if (error instanceof AuthError) { - console.error(chalk.red('โŒ Login failed:'), error.message); - if (error.code === 'expired_token') { - console.log(chalk.gray('Run'), chalk.cyan('agent login'), chalk.gray('to try again.')); - } - } else { - console.error(chalk.red('โŒ Login failed:'), (error as Error).message); - } - process.exit(1); - } }; diff --git a/src/commands/logout.test.ts b/src/commands/logout.test.ts deleted file mode 100644 index 1cbc7f1..0000000 --- a/src/commands/logout.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as authService from '../services/auth.service.js'; -import * as configUtils from '../utils/config.js'; -import { logoutCommand } from './logout.js'; - -// Mock dependencies -jest.mock('../services/auth.service.js'); -jest.mock('../utils/config.js'); - -const mockLogout = authService.logout as jest.MockedFunction; -const mockLoadAuth = configUtils.loadAuth as jest.MockedFunction; -const mockClearAuth = configUtils.clearAuth as jest.MockedFunction; - -describe('logoutCommand', () => { - let consoleSpy: jest.SpyInstance; - - beforeEach(() => { - jest.clearAllMocks(); - consoleSpy = jest.spyOn(console, 'log').mockImplementation(); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - }); - - it('shows message when not logged in', async () => { - mockLoadAuth.mockResolvedValue({}); - - await logoutCommand(); - - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Not logged in')); - expect(mockClearAuth).not.toHaveBeenCalled(); - }); - - it('clears credentials and shows success', async () => { - mockLoadAuth.mockResolvedValue({ - token: 'test-token', - user: { id: '1', email: 'test@example.com', name: 'Test User' }, - }); - mockLogout.mockResolvedValue(undefined); - mockClearAuth.mockResolvedValue(undefined); - - await logoutCommand(); - - expect(mockLogout).toHaveBeenCalled(); - expect(mockClearAuth).toHaveBeenCalled(); - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Logged out from'), - expect.stringContaining('Test User') - ); - }); - - it('clears credentials even when server logout fails', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockLogout.mockRejectedValue(new Error('Network error')); - mockClearAuth.mockResolvedValue(undefined); - - await logoutCommand(); - - expect(mockClearAuth).toHaveBeenCalled(); - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Logged out locally')); - }); -}); diff --git a/src/commands/logout.ts b/src/commands/logout.ts index 9e31d6b..f7f0975 100644 --- a/src/commands/logout.ts +++ b/src/commands/logout.ts @@ -1,39 +1,11 @@ +import { type Command } from 'commander'; import chalk from 'chalk'; -import { logout as logoutApi } from '../services/auth.service.js'; -import { clearAuth, loadAuth } from '../utils/config.js'; -/** - * Logout command - clear stored credentials - */ -export const logoutCommand = async (): Promise => { - try { - // Check if logged in - const auth = await loadAuth(); - if (!auth.token) { - console.log(chalk.yellow('Not logged in.')); - return; - } - - // Get user info before clearing - const userName = auth.user?.name || auth.user?.email; - - // Attempt server-side logout (optional, ignore errors) - console.log(chalk.gray('Logging out...')); - await logoutApi(); - - // Clear auth.json only โ€” config.json (deviceId, registry) is preserved - await clearAuth(); - - console.log(); - if (userName) { - console.log(chalk.green('โœ… Logged out from'), chalk.bold(userName)); - } else { - console.log(chalk.green('โœ… Logged out successfully.')); - } - } catch { - // Even if server logout fails, clear local credentials - await clearAuth(); - console.log(chalk.green('โœ… Logged out locally.')); - console.log(chalk.gray('(Server logout may have failed, but local credentials cleared)')); - } +export const registerLogout = (program: Command): void => { + program + .command('logout') + .description('Disconnect from hub') + .action(() => { + console.log(chalk.yellow('Hub sync not yet available.')); + }); }; diff --git a/src/commands/logs.ts b/src/commands/logs.ts new file mode 100644 index 0000000..ec6b3db --- /dev/null +++ b/src/commands/logs.ts @@ -0,0 +1,53 @@ +import { type Command } from 'commander'; +import { existsSync, readFileSync, watchFile, unwatchFile } from 'node:fs'; +import { join } from 'node:path'; +import chalk from 'chalk'; +import { getConfigDir } from '../daemon/config.js'; + +export const registerLogs = (program: Command): void => { + program + .command('logs') + .description('Tail daemon log') + .option('-f, --follow', 'Live tail') + .option('-n ', 'Number of lines', '50') + .action((opts: { follow?: boolean; n?: string }) => { + const logPath = join(getConfigDir(), 'daemon.log'); + + if (!existsSync(logPath)) { + console.log(chalk.gray('No daemon log found.')); + return; + } + + const lines = parseInt(opts.n || '50', 10); + const content = readFileSync(logPath, 'utf-8'); + const allLines = content.split('\n').filter(Boolean); + const tail = allLines.slice(-lines); + + for (const line of tail) { + console.log(line); + } + + if (opts.follow) { + let lastSize = Buffer.byteLength(content, 'utf-8'); + + watchFile(logPath, { interval: 500 }, () => { + const updated = readFileSync(logPath, 'utf-8'); + const currentSize = Buffer.byteLength(updated, 'utf-8'); + + if (currentSize > lastSize) { + const newContent = updated.slice(lastSize); + const newLines = newContent.split('\n').filter(Boolean); + for (const line of newLines) { + console.log(line); + } + lastSize = currentSize; + } + }); + + process.on('SIGINT', () => { + unwatchFile(logPath); + process.exit(0); + }); + } + }); +}; diff --git a/src/commands/machines.ts b/src/commands/machines.ts new file mode 100644 index 0000000..d4080b0 --- /dev/null +++ b/src/commands/machines.ts @@ -0,0 +1,15 @@ +import { type Command } from 'commander'; +import chalk from 'chalk'; +import { ensureDaemon } from '../utils/ensure-daemon.js'; + +export const registerMachines = (program: Command): void => { + program + .command('machines') + .description('List connected machines') + .option('--json', 'JSON output') + .action(async () => { + await ensureDaemon(); + console.error(chalk.red("Not connected to hub. Run 'agentage login' first.")); + process.exitCode = 1; + }); +}; diff --git a/src/commands/publish.test.ts b/src/commands/publish.test.ts deleted file mode 100644 index 9b10376..0000000 --- a/src/commands/publish.test.ts +++ /dev/null @@ -1,395 +0,0 @@ -import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; -import { publishCommand } from './publish.js'; - -// Mock dependencies -jest.mock('../utils/config.js', () => ({ - getAuthStatus: jest.fn(), - getRegistryUrl: jest.fn().mockResolvedValue('https://dev.agentage.io'), -})); - -jest.mock('../services/registry.service.js', () => ({ - publishAgent: jest.fn(), - RegistryApiError: class extends Error { - constructor( - message: string, - public code: string, - public statusCode: number - ) { - super(message); - } - }, -})); - -describe('publishCommand', () => { - const testDir = 'test-publish-workspace'; - const originalCwd = process.cwd(); - - beforeEach(() => { - process.chdir(originalCwd); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true, force: true }); - } - mkdirSync(testDir, { recursive: true }); - process.chdir(testDir); - jest.clearAllMocks(); - }); - - afterEach(() => { - process.chdir(originalCwd); - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true, force: true }); - } - }); - - test('requires authentication', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ status: 'not_authenticated' }); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Not logged in')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('shows expired session message', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ status: 'expired' }); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Session expired')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('finds agent file in current directory', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - publishAgent.mockResolvedValue({ - name: 'my-agent', - owner: 'testuser', - version: '2025-11-30', - }); - - const agentContent = `--- -name: my-agent -description: Test agent ---- -You are helpful.`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand(); - - expect(publishAgent).toHaveBeenCalledWith( - expect.objectContaining({ - name: 'my-agent', - visibility: 'public', - }) - ); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Published')); - - mockConsoleLog.mockRestore(); - }); - - test('supports dry run', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - const agentContent = `--- -name: my-agent -description: Test agent ---- -You are helpful.`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand(undefined, { dryRun: true }); - - expect(publishAgent).not.toHaveBeenCalled(); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Dry run')); - - mockConsoleLog.mockRestore(); - }); - - test('validates agent name', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - const agentContent = `--- -name: Invalid-Name ---- -Content`; - writeFileSync('agent.agent.md', agentContent); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Invalid agent name')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('finds agent with explicit path', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - publishAgent.mockResolvedValue({ - name: 'my-agent', - owner: 'testuser', - version: '2025-11-30', - }); - - const agentContent = `--- -name: my-agent -description: Test agent ---- -You are helpful.`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand('my-agent.agent.md'); - - expect(publishAgent).toHaveBeenCalled(); - mockConsoleLog.mockRestore(); - }); - - test('finds agent with name without extension', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - publishAgent.mockResolvedValue({ - name: 'my-agent', - owner: 'testuser', - version: '2025-11-30', - }); - - const agentContent = `--- -name: my-agent ---- -Content`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand('my-agent'); - - expect(publishAgent).toHaveBeenCalled(); - mockConsoleLog.mockRestore(); - }); - - test('finds agent in agents directory', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - publishAgent.mockResolvedValue({ - name: 'my-agent', - owner: 'testuser', - version: '2025-11-30', - }); - - mkdirSync('agents'); - const agentContent = `--- -name: my-agent ---- -Content`; - writeFileSync('agents/my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand('my-agent'); - - expect(publishAgent).toHaveBeenCalled(); - mockConsoleLog.mockRestore(); - }); - - test('fails when no agent file found', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand('nonexistent')).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('No agent file found')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('fails when agent has no name', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - const agentContent = `--- -description: No name agent ---- -Content`; - writeFileSync('agent.agent.md', agentContent); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('must have a name')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('shows multiple agent files warning', async () => { - const { getAuthStatus } = require('../utils/config.js'); - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - writeFileSync('agent1.agent.md', '---\nname: a1\n---\n'); - writeFileSync('agent2.agent.md', '---\nname: a2\n---\n'); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleLog).toHaveBeenCalledWith( - expect.stringContaining('Multiple agent files found') - ); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('handles RegistryApiError', async () => { - const { getAuthStatus } = require('../utils/config.js'); - const { publishAgent, RegistryApiError } = require('../services/registry.service.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - publishAgent.mockRejectedValue(new RegistryApiError('Version exists', 'version_exists', 409)); - - const agentContent = `--- -name: my-agent ---- -Content`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit'); - }); - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await expect(publishCommand()).rejects.toThrow('process.exit'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Version exists')); - - mockExit.mockRestore(); - mockConsoleError.mockRestore(); - mockConsoleLog.mockRestore(); - }); - - test('dry run shows all options', async () => { - const { getAuthStatus } = require('../utils/config.js'); - - getAuthStatus.mockResolvedValue({ - status: 'authenticated', - token: 'test-token', - }); - - const agentContent = `--- -name: my-agent -description: A test agent -version: 1.0.0 ---- -Content`; - writeFileSync('my-agent.agent.md', agentContent); - - const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await publishCommand(undefined, { - dryRun: true, - tag: ['ai', 'test'], - changelog: 'Initial release', - }); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Tags')); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Changelog')); - - mockConsoleLog.mockRestore(); - }); -}); diff --git a/src/commands/publish.ts b/src/commands/publish.ts deleted file mode 100644 index 4b5617e..0000000 --- a/src/commands/publish.ts +++ /dev/null @@ -1,177 +0,0 @@ -import chalk from 'chalk'; -import { existsSync } from 'fs'; -import { readdir } from 'fs/promises'; -import { join } from 'path'; -import { publishAgent, RegistryApiError } from '../services/registry.service.js'; -import { generateDateVersion, isValidAgentName, readAgentFile } from '../utils/agent-parser.js'; -import { getAuthStatus } from '../utils/config.js'; - -interface PublishOptions { - visibility?: 'public' | 'private'; - version?: string; - tag?: string[]; - changelog?: string; - dryRun?: boolean; -} - -/** - * Find agent file to publish - */ -const resolveAgentPath = async (pathArg?: string): Promise => { - // If path specified, use it - if (pathArg) { - if (existsSync(pathArg)) { - return pathArg; - } - // Try adding .agent.md extension - const withExt = pathArg.endsWith('.agent.md') ? pathArg : `${pathArg}.agent.md`; - if (existsSync(withExt)) { - return withExt; - } - // Try in agents directory - const inAgentsDir = join('agents', withExt); - if (existsSync(inAgentsDir)) { - return inAgentsDir; - } - return null; - } - - // Look for .agent.md files in current directory - const currentDirFiles = await readdir('.').catch(() => []); - const agentFiles = currentDirFiles.filter((f) => f.endsWith('.agent.md')); - if (agentFiles.length === 1) { - return agentFiles[0]; - } - if (agentFiles.length > 1) { - console.log(chalk.yellow('Multiple agent files found. Please specify one:')); - for (const file of agentFiles) { - console.log(` - ${file}`); - } - return null; - } - - // Look in agents directory - if (existsSync('agents')) { - const agentsDir = await readdir('agents').catch(() => []); - const agentMdFiles = agentsDir.filter((f) => f.endsWith('.agent.md')); - if (agentMdFiles.length === 1) { - return join('agents', agentMdFiles[0]); - } - if (agentMdFiles.length > 1) { - console.log(chalk.yellow('Multiple agent files found. Please specify one:')); - for (const file of agentMdFiles) { - console.log(` - agents/${file}`); - } - return null; - } - } - - return null; -}; - -/** - * Publish command - publish agent to registry - */ -export const publishCommand = async ( - pathArg?: string, - options: PublishOptions = {} -): Promise => { - try { - // 1. Check authentication - const authStatus = await getAuthStatus(); - if (authStatus.status === 'expired') { - console.error(chalk.red('โŒ Session expired.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate again.'); - process.exit(1); - } - if (authStatus.status === 'not_authenticated') { - console.error(chalk.red('โŒ Not logged in.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate.'); - process.exit(1); - } - - // 2. Find agent file - const agentPath = await resolveAgentPath(pathArg); - if (!agentPath) { - console.error(chalk.red('โŒ No agent file found.')); - console.log('Specify a path or run from a directory with a .agent.md file.'); - process.exit(1); - } - - // 3. Read and parse - console.log(chalk.gray(`Reading ${agentPath}...`)); - const { frontmatter, content } = await readAgentFile(agentPath); - - // 4. Validate required fields - if (!frontmatter.name) { - console.error(chalk.red('โŒ Agent must have a name in frontmatter.')); - console.log(chalk.gray('Add "name: your-agent-name" to the YAML frontmatter.')); - process.exit(1); - } - - if (!isValidAgentName(frontmatter.name)) { - console.error(chalk.red('โŒ Invalid agent name.')); - console.log(chalk.gray('Name must be lowercase alphanumeric with hyphens (e.g., my-agent).')); - process.exit(1); - } - - // 5. Determine version - const version = options.version || frontmatter.version || generateDateVersion(); - const visibility = options.visibility || 'public'; - - // 6. Dry run check - if (options.dryRun) { - console.log(); - console.log(chalk.cyan('๐Ÿ“‹ Dry run - would publish:')); - console.log(` Name: ${chalk.bold(frontmatter.name)}`); - console.log(` Version: ${chalk.bold(version)}`); - console.log(` Visibility: ${chalk.bold(visibility)}`); - if (frontmatter.description) { - console.log(` Description: ${frontmatter.description}`); - } - if (options.tag && options.tag.length > 0) { - console.log(` Tags: ${options.tag.join(', ')}`); - } - if (options.changelog) { - console.log(` Changelog: ${options.changelog}`); - } - console.log(); - return; - } - - // 7. Publish - console.log(chalk.cyan(`๐Ÿ“ค Publishing ${frontmatter.name}@${version}...`)); - - const result = await publishAgent({ - name: frontmatter.name, - description: frontmatter.description, - visibility, - version, - content, - contentType: 'markdown', - tags: options.tag, - changelog: options.changelog, - }); - - // 8. Success - console.log(); - console.log(chalk.green(`โœ… Published ${result.owner}/${result.name}@${result.version}`)); - console.log(chalk.gray(` Install with: agent install ${result.owner}/${result.name}`)); - console.log(); - } catch (error) { - if (error instanceof RegistryApiError) { - console.error(chalk.red(`โŒ ${error.message}`)); - if (error.details) { - for (const [field, msg] of Object.entries(error.details)) { - console.error(chalk.gray(` ${field}: ${msg}`)); - } - } - if (error.code === 'version_exists') { - console.log(chalk.gray(' Use --version to specify a newer version.')); - } - } else { - console.error(chalk.red(`โŒ Failed: ${(error as Error).message}`)); - } - process.exit(1); - } -}; diff --git a/src/commands/run.test.ts b/src/commands/run.test.ts index 02346a4..1478165 100644 --- a/src/commands/run.test.ts +++ b/src/commands/run.test.ts @@ -1,92 +1,8 @@ -import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; -import { join } from 'path'; -import { runCommand } from './run.js'; +import { describe, it, expect } from 'vitest'; -describe('runCommand', () => { - const testAgentsDir = 'test-agents'; - - beforeEach(() => { - if (existsSync(testAgentsDir)) { - rmSync(testAgentsDir, { recursive: true }); - } - mkdirSync(testAgentsDir); - process.chdir(testAgentsDir); - jest.clearAllMocks(); - }); - - afterEach(() => { - process.chdir('..'); - if (existsSync(testAgentsDir)) { - rmSync(testAgentsDir, { recursive: true }); - } - }); - - test('runs agent with valid config and shows warning', async () => { - mkdirSync('agents'); - const validAgent = `name: test-agent -model: gpt-4 -instructions: Test instructions -tools: [] -variables: {}`; - writeFileSync(join('agents', 'test-agent.yml'), validAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await runCommand('test-agent', 'Hello'); - - expect(consoleLog).toHaveBeenCalledWith('\n๐Ÿค– Running test-agent...\n'); - expect(consoleLog).toHaveBeenCalledWith(expect.stringContaining('Agent runtime not available')); - - consoleLog.mockRestore(); - }); - - test('uses default prompt when none provided', async () => { - mkdirSync('agents'); - const validAgent = `name: test-agent -model: gpt-4 -instructions: Test instructions -tools: [] -variables: {}`; - writeFileSync(join('agents', 'test-agent.yml'), validAgent); - - const consoleLog = jest.spyOn(console, 'log').mockImplementation(); - - await runCommand('test-agent'); - - expect(consoleLog).toHaveBeenCalledWith('\n๐Ÿค– Running test-agent...\n'); - - consoleLog.mockRestore(); - }); - - test('handles missing agent file', async () => { - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - const consoleError = jest.spyOn(console, 'error').mockImplementation(); - - await expect(runCommand('nonexistent')).rejects.toThrow('process.exit called'); - expect(consoleError).toHaveBeenCalledWith(expect.stringContaining('โŒ Failed:')); - expect(mockExit).toHaveBeenCalledWith(1); - - mockExit.mockRestore(); - consoleError.mockRestore(); - }); - - test('handles invalid agent YAML', async () => { - mkdirSync('agents'); - const invalidAgent = `name: test-agent -model: gpt-4`; - writeFileSync(join('agents', 'invalid.yml'), invalidAgent); - - const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - const consoleError = jest.spyOn(console, 'error').mockImplementation(); - - await expect(runCommand('invalid')).rejects.toThrow('process.exit called'); - expect(consoleError).toHaveBeenCalledWith(expect.stringContaining('โŒ Failed:')); - - mockExit.mockRestore(); - consoleError.mockRestore(); +describe('run command', () => { + it('exports registerRun function', async () => { + const mod = await import('./run.js'); + expect(typeof mod.registerRun).toBe('function'); }); }); diff --git a/src/commands/run.ts b/src/commands/run.ts index a10f837..677cc49 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,38 +1,111 @@ +import { type Command } from 'commander'; import chalk from 'chalk'; -import { readFile } from 'fs/promises'; -import { join } from 'path'; -import { parse } from 'yaml'; -import { agentYamlSchema } from '../schemas/agent.schema.js'; - -export const runCommand = async (name: string, prompt?: string): Promise => { - try { - const agentsDir = 'agents'; - const filename = join(agentsDir, `${name}.yml`); - const content = await readFile(filename, 'utf-8'); - const yaml = parse(content); - - const validated = agentYamlSchema.parse(yaml); - - const userPrompt = prompt || 'Hello!'; - console.log(`\n๐Ÿค– Running ${validated.name}...\n`); - console.log(chalk.gray(`Model: ${validated.model}`)); - console.log(chalk.gray(`Prompt: ${userPrompt}`)); - console.log(); - - // Note: The run command requires an AI provider integration. - // This is a placeholder that shows the agent would be run. - // TODO: Add support for direct OpenAI/Claude API calls or integrate a runtime. - console.log( - chalk.yellow( - 'โš ๏ธ Agent runtime not available. This is a standalone CLI without SDK integration.' - ) - ); - console.log( - chalk.gray('To run agents, integrate with an AI provider (OpenAI, Anthropic, etc.) directly.') +import { type RunEvent } from '@agentage/core'; +import { ensureDaemon } from '../utils/ensure-daemon.js'; +import { connectWs, post } from '../utils/daemon-client.js'; +import { renderEvent } from '../utils/render.js'; + +interface RunResponse { + runId: string; +} + +interface WsRunEventMessage { + type: 'run_event'; + runId: string; + event: RunEvent; +} + +interface WsRunStateMessage { + type: 'run_state'; + run: { id: string; state: string }; +} + +type WsMessage = WsRunEventMessage | WsRunStateMessage; + +const TERMINAL_STATES = ['completed', 'failed', 'canceled']; + +export const registerRun = (program: Command): void => { + program + .command('run') + .argument('', 'Agent name') + .argument('[prompt]', 'Task/prompt for the agent') + .description('Run an agent') + .option('-d, --detach', 'Run in background, print run ID') + .option('--json', 'Output events as JSON lines') + .option('--config ', 'Per-run config overrides (JSON)') + .option('--context ', 'Additional context files') + .action( + async ( + agent: string, + prompt: string | undefined, + opts: { detach?: boolean; json?: boolean; config?: string; context?: string[] } + ) => { + await ensureDaemon(); + + if (!prompt) { + console.error(chalk.red('Prompt is required. Usage: agentage run ""')); + process.exitCode = 1; + return; + } + + const config = opts.config + ? (JSON.parse(opts.config) as Record) + : undefined; + + const { runId } = await post(`/api/agents/${agent}/run`, { + task: prompt, + config, + context: opts.context, + }); + + if (opts.detach) { + console.log(runId); + return; + } + + // Stream events via WebSocket + await streamRun(runId, opts.json ?? false); + } ); - console.log(); - } catch (error) { - console.error(`โŒ Failed: ${(error as Error).message}`); - process.exit(1); - } }; + +const streamRun = (runId: string, jsonMode: boolean): Promise => + new Promise((resolve) => { + const ws = connectWs((data) => { + const msg = data as WsMessage; + + if (msg.type === 'run_event' && msg.runId === runId) { + if (jsonMode) { + console.log(JSON.stringify(msg.event)); + } else { + renderEvent(msg.event); + } + } + + if (msg.type === 'run_state' && msg.run.id === runId) { + if (TERMINAL_STATES.includes(msg.run.state)) { + ws.close(); + resolve(); + } + } + }); + + ws.on('open', () => { + ws.send(JSON.stringify({ type: 'subscribe', runId })); + }); + + ws.on('error', () => { + resolve(); + }); + + ws.on('close', () => { + resolve(); + }); + + // Handle Ctrl+C + process.on('SIGINT', () => { + post(`/api/runs/${runId}/cancel`).catch(() => {}); + ws.close(); + resolve(); + }); + }); diff --git a/src/commands/runs.ts b/src/commands/runs.ts new file mode 100644 index 0000000..65b19bd --- /dev/null +++ b/src/commands/runs.ts @@ -0,0 +1,87 @@ +import { type Command } from 'commander'; +import chalk from 'chalk'; +import { type Run } from '@agentage/core'; +import { ensureDaemon } from '../utils/ensure-daemon.js'; +import { get } from '../utils/daemon-client.js'; + +export const registerRuns = (program: Command): void => { + program + .command('runs') + .description('List runs') + .option('--all', 'All runs across all machines') + .option('--json', 'JSON output') + .action(async (opts: { all?: boolean; json?: boolean }) => { + await ensureDaemon(); + + if (opts.all) { + console.error(chalk.red("Not connected to hub. Run 'agentage login' first.")); + process.exitCode = 1; + return; + } + + const runs = await get('/api/runs'); + + if (opts.json) { + console.log(JSON.stringify(runs, null, 2)); + return; + } + + if (runs.length === 0) { + console.log(chalk.gray('No runs.')); + return; + } + + console.log( + chalk.bold('ID'.padEnd(12)) + + chalk.bold('AGENT'.padEnd(16)) + + chalk.bold('STATE'.padEnd(16)) + + chalk.bold('STARTED'.padEnd(16)) + + chalk.bold('DURATION') + ); + + for (const run of runs) { + const started = run.startedAt ? timeAgo(run.startedAt) : 'โ€”'; + const duration = + run.startedAt && run.endedAt ? formatDuration(run.endedAt - run.startedAt) : 'โ€”'; + const stateColor = getStateColor(run.state); + + console.log( + run.id.slice(0, 8).padEnd(12) + + run.agentName.padEnd(16) + + stateColor(run.state).padEnd(16 + (stateColor('').length - ''.length)) + + started.padEnd(16) + + duration + ); + } + }); +}; + +const timeAgo = (ts: number): string => { + const diff = Math.floor((Date.now() - ts) / 1000); + if (diff < 60) return `${diff}s ago`; + if (diff < 3600) return `${Math.floor(diff / 60)} min ago`; + return `${Math.floor(diff / 3600)}h ago`; +}; + +const formatDuration = (ms: number): string => { + const seconds = Math.floor(ms / 1000); + const m = Math.floor(seconds / 60); + const s = seconds % 60; + if (m > 0) return `${m}m ${s}s`; + return `${s}s`; +}; + +const getStateColor = (state: string): ((s: string) => string) => { + switch (state) { + case 'completed': + return chalk.green; + case 'failed': + return chalk.red; + case 'canceled': + return chalk.yellow; + case 'working': + return chalk.blue; + default: + return chalk.gray; + } +}; diff --git a/src/commands/search.test.ts b/src/commands/search.test.ts deleted file mode 100644 index a1d3171..0000000 --- a/src/commands/search.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Mock dependencies first, before imports -const mockSearchAgents = jest.fn(); - -jest.mock('../services/registry.service.js', () => ({ - searchAgents: mockSearchAgents, - RegistryApiError: class extends Error { - constructor( - message: string, - public code: string, - public statusCode: number - ) { - super(message); - } - }, -})); - -import { searchCommand } from './search.js'; - -describe('searchCommand', () => { - let mockConsoleLog: jest.SpyInstance; - let mockExit: jest.SpyInstance; - - beforeEach(() => { - jest.clearAllMocks(); - mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); - mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => { - throw new Error(`process.exit(${code})`); - }); - }); - - afterEach(() => { - mockConsoleLog.mockRestore(); - mockExit.mockRestore(); - }); - - test('displays search results', async () => { - mockSearchAgents.mockResolvedValue({ - agents: [ - { - name: 'test-agent', - owner: 'testuser', - description: 'A test agent', - latestVersion: '2025-11-30', - totalDownloads: 42, - tags: ['ai', 'test'], - }, - ], - total: 1, - page: 1, - limit: 10, - hasMore: false, - }); - - await searchCommand('test'); - - expect(mockSearchAgents).toHaveBeenCalledWith('test', 1, 10); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('testuser/test-agent')); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('A test agent')); - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('42 downloads')); - }); - - test('shows no results message', async () => { - mockSearchAgents.mockResolvedValue({ - agents: [], - total: 0, - page: 1, - limit: 10, - hasMore: false, - }); - - await searchCommand('nonexistent'); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('No agents found')); - }); - - test('outputs JSON format with --json flag', async () => { - const mockResult = { - agents: [{ name: 'test', owner: 'user' }], - total: 1, - page: 1, - limit: 10, - hasMore: false, - }; - - mockSearchAgents.mockResolvedValue(mockResult); - - await searchCommand('test', { json: true }); - - expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(mockResult, null, 2)); - }); - - test('respects limit and page options', async () => { - mockSearchAgents.mockResolvedValue({ - agents: [], - total: 0, - page: 2, - limit: 5, - hasMore: false, - }); - - await searchCommand('test', { limit: '5', page: '2' }); - - expect(mockSearchAgents).toHaveBeenCalledWith('test', 2, 5); - }); - - test('shows pagination info when more results exist', async () => { - mockSearchAgents.mockResolvedValue({ - agents: [ - { - name: 'test', - owner: 'user', - latestVersion: '1.0', - totalDownloads: 0, - }, - ], - total: 50, - page: 1, - limit: 10, - hasMore: true, - }); - - await searchCommand('test'); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('--page 2')); - }); - - test('handles RegistryApiError', async () => { - const { RegistryApiError } = require('../services/registry.service.js'); - mockSearchAgents.mockRejectedValue(new RegistryApiError('Rate limited', 'rate_limit', 429)); - - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - - await expect(searchCommand('test')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Rate limited')); - - mockConsoleError.mockRestore(); - }); - - test('handles generic error', async () => { - mockSearchAgents.mockRejectedValue(new Error('Network error')); - - const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); - - await expect(searchCommand('test')).rejects.toThrow('process.exit(1)'); - - expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Network error')); - - mockConsoleError.mockRestore(); - }); - - test('displays result without description', async () => { - mockSearchAgents.mockResolvedValue({ - agents: [ - { - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - totalDownloads: 5, - }, - ], - total: 1, - page: 1, - limit: 10, - hasMore: false, - }); - - await searchCommand('test'); - - expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('No description')); - }); -}); diff --git a/src/commands/search.ts b/src/commands/search.ts deleted file mode 100644 index 9948a16..0000000 --- a/src/commands/search.ts +++ /dev/null @@ -1,63 +0,0 @@ -import chalk from 'chalk'; -import { RegistryApiError, searchAgents } from '../services/registry.service.js'; - -interface SearchOptions { - limit?: string; - page?: string; - json?: boolean; -} - -/** - * Search command - search for agents in registry - */ -export const searchCommand = async (query: string, options: SearchOptions = {}): Promise => { - try { - const limit = options.limit ? parseInt(options.limit, 10) : 10; - const page = options.page ? parseInt(options.page, 10) : 1; - - console.log(chalk.cyan(`๐Ÿ” Searching for "${query}"...\n`)); - - const result = await searchAgents(query, page, limit); - - // JSON output - if (options.json) { - console.log(JSON.stringify(result, null, 2)); - return; - } - - // No results - if (result.agents.length === 0) { - console.log(chalk.yellow('No agents found.')); - return; - } - - // Display results - console.log(chalk.white.bold(`Found ${result.total} agent${result.total !== 1 ? 's' : ''}:\n`)); - - for (const agent of result.agents) { - console.log(chalk.cyan.bold(` ${agent.owner}/${agent.name}`)); - console.log(chalk.gray(` ${agent.description || 'No description'}`)); - console.log(chalk.gray(` v${agent.latestVersion} โ€ข ${agent.totalDownloads} downloads`)); - if (agent.tags && agent.tags.length > 0) { - console.log(chalk.gray(` Tags: ${agent.tags.join(', ')}`)); - } - console.log(); - } - - // Pagination info - if (result.hasMore) { - console.log( - chalk.gray( - `Showing ${result.agents.length} of ${result.total}. Use --page ${page + 1} for more.` - ) - ); - } - } catch (error) { - if (error instanceof RegistryApiError) { - console.error(chalk.red(`โŒ ${error.message}`)); - } else { - console.error(chalk.red(`โŒ Failed: ${(error as Error).message}`)); - } - process.exit(1); - } -}; diff --git a/src/commands/status.ts b/src/commands/status.ts new file mode 100644 index 0000000..9439d4d --- /dev/null +++ b/src/commands/status.ts @@ -0,0 +1,46 @@ +import { type Command } from 'commander'; +import chalk from 'chalk'; +import { ensureDaemon } from '../utils/ensure-daemon.js'; +import { get } from '../utils/daemon-client.js'; +import { getDaemonPid } from '../daemon/daemon.js'; + +interface HealthResponse { + status: string; + version: string; + uptime: number; + machineId: string; + hubConnected: boolean; +} + +export const registerStatus = (program: Command): void => { + program + .command('status') + .description('Show daemon and connection status') + .action(async () => { + await ensureDaemon(); + const health = await get('/api/health'); + const agents = await get('/api/agents'); + const runs = await get('/api/runs'); + const pid = getDaemonPid(); + + const uptime = formatUptime(health.uptime); + const activeRuns = runs.length; + + console.log(`Daemon: ${chalk.green('running')} (PID ${pid}, port 4243)`); + console.log(`Uptime: ${uptime}`); + console.log( + `Hub: ${health.hubConnected ? chalk.green('connected') : chalk.yellow('not connected (standalone mode)')}` + ); + console.log(`Machine: ${health.machineId}`); + console.log(`Agents: ${agents.length} discovered`); + console.log(`Runs: ${activeRuns} active`); + }); +}; + +const formatUptime = (seconds: number): string => { + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + if (h > 0) return `${h}h ${m}m`; + if (m > 0) return `${m}m`; + return `${seconds}s`; +}; diff --git a/src/commands/update.test.ts b/src/commands/update.test.ts deleted file mode 100644 index dfdfcde..0000000 --- a/src/commands/update.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -const mockExecAsync = jest.fn(); - -jest.mock('child_process', () => ({ - exec: jest.fn(), -})); - -jest.mock('util', () => ({ - promisify: (): typeof mockExecAsync => mockExecAsync, -})); - -import { updateCommand } from './update.js'; - -describe('updateCommand', () => { - let consoleSpy: jest.SpyInstance; - let consoleErrorSpy: jest.SpyInstance; - let processExitSpy: jest.SpyInstance; - - beforeEach(() => { - jest.clearAllMocks(); - consoleSpy = jest.spyOn(console, 'log').mockImplementation(); - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - consoleErrorSpy.mockRestore(); - processExitSpy.mockRestore(); - }); - - it('shows message when already on latest version', async () => { - mockExecAsync.mockImplementation((command: string) => { - if (command.includes('npm list')) { - return Promise.resolve({ - stdout: JSON.stringify({ - dependencies: { '@agentage/cli': { version: '1.0.0' } }, - }), - }); - } else if (command.includes('npm view')) { - return Promise.resolve({ stdout: '1.0.0\n' }); - } - return Promise.resolve({ stdout: '' }); - }); - - await updateCommand(); - - expect(consoleSpy).toHaveBeenCalledWith('๐Ÿ”„ Checking for updates...'); - expect(consoleSpy).toHaveBeenCalledWith('โœ… Already on the latest version (1.0.0)'); - }); - - it('updates successfully when new version available', async () => { - let listCallCount = 0; - mockExecAsync.mockImplementation((command: string) => { - if (command.includes('npm list')) { - listCallCount++; - const version = listCallCount === 1 ? '1.0.0' : '2.0.0'; - return Promise.resolve({ - stdout: JSON.stringify({ - dependencies: { '@agentage/cli': { version } }, - }), - }); - } else if (command.includes('npm view')) { - return Promise.resolve({ stdout: '2.0.0\n' }); - } else if (command.includes('npm install -g')) { - return Promise.resolve({ stdout: '' }); - } - return Promise.resolve({ stdout: '' }); - }); - - await updateCommand(); - - expect(consoleSpy).toHaveBeenCalledWith('๐Ÿ”„ Checking for updates...'); - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Updating @agentage/cli from 1.0.0 to 2.0.0') - ); - expect(consoleSpy).toHaveBeenCalledWith('โœ… Successfully updated to version 2.0.0'); - }); - - it('handles npm view error', async () => { - mockExecAsync.mockImplementation((command: string) => { - if (command.includes('npm list')) { - return Promise.resolve({ - stdout: JSON.stringify({ - dependencies: { '@agentage/cli': { version: '1.0.0' } }, - }), - }); - } else if (command.includes('npm view')) { - return Promise.reject(new Error('Network error')); - } - return Promise.resolve({ stdout: '' }); - }); - - await expect(updateCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Update failed')); - expect(processExitSpy).toHaveBeenCalledWith(1); - }); - - it('handles npm install error', async () => { - mockExecAsync.mockImplementation((command: string) => { - if (command.includes('npm list')) { - return Promise.resolve({ - stdout: JSON.stringify({ - dependencies: { '@agentage/cli': { version: '1.0.0' } }, - }), - }); - } else if (command.includes('npm view')) { - return Promise.resolve({ stdout: '2.0.0\n' }); - } else if (command.includes('npm install -g')) { - return Promise.reject(new Error('Permission denied')); - } - return Promise.resolve({ stdout: '' }); - }); - - await expect(updateCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Update failed')); - expect(processExitSpy).toHaveBeenCalledWith(1); - }); - - it('handles unknown installed version gracefully', async () => { - let listCallCount = 0; - mockExecAsync.mockImplementation((command: string) => { - if (command.includes('npm list')) { - listCallCount++; - if (listCallCount === 1) { - return Promise.reject(new Error('Not found')); - } - return Promise.resolve({ - stdout: JSON.stringify({ - dependencies: { '@agentage/cli': { version: '2.0.0' } }, - }), - }); - } else if (command.includes('npm view')) { - return Promise.resolve({ stdout: '2.0.0\n' }); - } else if (command.includes('npm install -g')) { - return Promise.resolve({ stdout: '' }); - } - return Promise.resolve({ stdout: '' }); - }); - - await updateCommand(); - - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Updating @agentage/cli from unknown to 2.0.0') - ); - }); -}); diff --git a/src/commands/update.ts b/src/commands/update.ts deleted file mode 100644 index 7574626..0000000 --- a/src/commands/update.ts +++ /dev/null @@ -1,47 +0,0 @@ -import chalk from 'chalk'; -import { exec } from 'child_process'; -import { promisify } from 'util'; -import { getInstalledVersion, getLatestVersion, PACKAGE_NAME } from '../utils/version.js'; - -const execAsync = promisify(exec); - -export interface UpdateResult { - success: boolean; - previousVersion: string; - currentVersion: string; - message: string; -} - -export const updateCommand = async (): Promise => { - console.log(chalk.cyan('๐Ÿ”„ Checking for updates...')); - - try { - const previousVersion = await getInstalledVersion(); - const latestVersion = await getLatestVersion(); - - if (latestVersion === 'unknown') { - console.error(chalk.red('โŒ Failed to fetch latest version from npm registry')); - process.exit(1); - } - - if (previousVersion === latestVersion) { - console.log(chalk.green(`โœ… Already on the latest version (${latestVersion})`)); - return; - } - - console.log( - chalk.yellow( - `๐Ÿ“ฆ Updating ${PACKAGE_NAME} from ${chalk.red( - previousVersion - )} to ${chalk.green.bold(latestVersion)}...` - ) - ); - - await execAsync(`npm install -g ${PACKAGE_NAME}@latest`); - - console.log(chalk.green.bold(`โœ… Successfully updated to version ${latestVersion}`)); - } catch (error) { - console.error(chalk.red(`โŒ Update failed: ${(error as Error).message}`)); - process.exit(1); - } -}; diff --git a/src/commands/whoami.test.ts b/src/commands/whoami.test.ts deleted file mode 100644 index 4115a40..0000000 --- a/src/commands/whoami.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -import * as authService from '../services/auth.service.js'; -import { AuthError } from '../services/auth.service.js'; -import * as configUtils from '../utils/config.js'; -import { whoamiCommand } from './whoami.js'; - -// Mock dependencies -jest.mock('../services/auth.service.js', () => { - const original = jest.requireActual('../services/auth.service.js'); - return { - ...original, - getMe: jest.fn(), - }; -}); -jest.mock('../utils/config.js'); - -const mockGetMe = authService.getMe as jest.MockedFunction; -const mockLoadAuth = configUtils.loadAuth as jest.MockedFunction; -const mockGetRegistryUrl = configUtils.getRegistryUrl as jest.MockedFunction< - typeof configUtils.getRegistryUrl ->; -const mockIsTokenExpired = configUtils.isTokenExpired as jest.MockedFunction< - typeof configUtils.isTokenExpired ->; - -describe('whoamiCommand', () => { - let consoleSpy: jest.SpyInstance; - let consoleErrorSpy: jest.SpyInstance; - let processExitSpy: jest.SpyInstance; - - beforeEach(() => { - jest.clearAllMocks(); - consoleSpy = jest.spyOn(console, 'log').mockImplementation(); - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit called'); - }); - mockGetRegistryUrl.mockResolvedValue('https://dev.agentage.io'); - mockIsTokenExpired.mockReturnValue(false); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - consoleErrorSpy.mockRestore(); - processExitSpy.mockRestore(); - }); - - it('shows not logged in when no token', async () => { - mockLoadAuth.mockResolvedValue({}); - - await whoamiCommand(); - - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Not logged in')); - }); - - it('shows expired session when token is locally expired', async () => { - mockLoadAuth.mockResolvedValue({ - token: 'expired-token', - expiresAt: '2020-01-01T00:00:00Z', - }); - mockIsTokenExpired.mockReturnValue(true); - - await expect(whoamiCommand()).rejects.toThrow('process.exit called'); - - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Session expired')); - }); - - it('displays user info when authenticated', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockResolvedValue({ - id: '123', - email: 'test@example.com', - name: 'Test User', - }); - - await whoamiCommand(); - - expect(mockGetMe).toHaveBeenCalled(); - expect(consoleSpy).toHaveBeenCalledWith( - expect.stringContaining('Logged in to'), - expect.any(String) - ); - }); - - it('handles session expired error', async () => { - mockLoadAuth.mockResolvedValue({ token: 'expired-token' }); - mockGetMe.mockRejectedValue(new AuthError('Session expired', 'session_expired')); - - await expect(whoamiCommand()).rejects.toThrow('process.exit called'); - - // The code logs "Session expired." (with period and emoji) - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Session expired')); - }); - - it('handles not_authenticated error', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockRejectedValue(new AuthError('Not authenticated', 'not_authenticated')); - - await expect(whoamiCommand()).rejects.toThrow('process.exit called'); - - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Not logged in')); - }); - - it('handles other AuthError errors', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockRejectedValue(new AuthError('Server error', 'server_error')); - - await expect(whoamiCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Error'), 'Server error'); - }); - - it('handles non-AuthError errors', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockRejectedValue(new Error('Network error')); - - await expect(whoamiCommand()).rejects.toThrow('process.exit called'); - - expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Error'), 'Network error'); - }); - - it('displays user without name', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockResolvedValue({ - id: '123', - email: 'test@example.com', - }); - - await whoamiCommand(); - - expect(consoleSpy).toHaveBeenCalledWith( - ' Email:', - expect.stringContaining('test@example.com') - ); - }); - - it('displays user with verifiedAlias', async () => { - mockLoadAuth.mockResolvedValue({ token: 'test-token' }); - mockGetMe.mockResolvedValue({ - id: '123', - email: 'test@example.com', - name: 'Test User', - verifiedAlias: 'testuser', - }); - - await whoamiCommand(); - - expect(consoleSpy).toHaveBeenCalledWith(' Alias:', expect.stringContaining('testuser')); - }); -}); diff --git a/src/commands/whoami.ts b/src/commands/whoami.ts deleted file mode 100644 index 3303a54..0000000 --- a/src/commands/whoami.ts +++ /dev/null @@ -1,60 +0,0 @@ -import chalk from 'chalk'; -import { AuthError, getMe } from '../services/auth.service.js'; -import { getRegistryUrl, isTokenExpired, loadAuth } from '../utils/config.js'; - -/** - * Whoami command - display current authenticated user - */ -export const whoamiCommand = async (): Promise => { - try { - // Check if token exists locally - const auth = await loadAuth(); - if (!auth.token) { - console.log(chalk.yellow('Not logged in.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate.'); - return; - } - - // Check if token is expired locally - if (isTokenExpired(auth.expiresAt)) { - console.log(chalk.yellow('โš ๏ธ Session expired.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate again.'); - process.exit(1); - } - - // Fetch current user from API - console.log(chalk.gray('Checking authentication...')); - const user = await getMe(); - - const registryUrl = await getRegistryUrl(); - console.log(); - console.log(chalk.green('โœ… Logged in to'), chalk.cyan(registryUrl)); - console.log(); - console.log(chalk.bold('User Information:')); - console.log(); - if (user.name) { - console.log(' Name: ', chalk.bold(user.name)); - } - console.log(' Email:', chalk.bold(user.email)); - if (user.verifiedAlias) { - console.log(' Alias:', chalk.bold(user.verifiedAlias)); - } - console.log(' ID: ', chalk.gray(user.id)); - console.log(); - } catch (error) { - if (error instanceof AuthError) { - if (error.code === 'not_authenticated') { - console.log(chalk.yellow('Not logged in.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate.'); - } else if (error.code === 'session_expired') { - console.log(chalk.yellow('โš ๏ธ Session expired.')); - console.log('Run', chalk.cyan('agent login'), 'to authenticate again.'); - } else { - console.error(chalk.red('โŒ Error:'), error.message); - } - } else { - console.error(chalk.red('โŒ Error:'), (error as Error).message); - } - process.exit(1); - } -}; diff --git a/src/daemon-entry.ts b/src/daemon-entry.ts new file mode 100644 index 0000000..2104920 --- /dev/null +++ b/src/daemon-entry.ts @@ -0,0 +1,52 @@ +import { loadConfig } from './daemon/config.js'; +import { logError, logInfo } from './daemon/logger.js'; +import { writePidFile, removePidFile } from './daemon/daemon.js'; +import { createDaemonServer } from './daemon/server.js'; +import { scanAgents } from './discovery/scanner.js'; +import { createMarkdownFactory } from './discovery/markdown-factory.js'; +import { createCodeFactory } from './discovery/code-factory.js'; + +const main = async (): Promise => { + const config = loadConfig(); + logInfo(`Daemon starting (PID ${process.pid})`); + writePidFile(process.pid); + + const server = createDaemonServer(); + const factories = [createMarkdownFactory(), createCodeFactory()]; + server.setFactories(factories); + + // Initial agent discovery + const agents = await scanAgents(config.discovery.dirs, factories); + server.updateAgents(agents); + logInfo(`Discovered ${agents.length} agent(s)`); + + await server.start(); + logInfo(`Daemon ready on port ${config.daemon.port}`); + + const shutdown = async (): Promise => { + logInfo('Daemon shutting down...'); + await server.stop(); + removePidFile(); + process.exit(0); + }; + + process.on('SIGTERM', () => { + shutdown().catch((err: unknown) => { + logError(`Shutdown error: ${err instanceof Error ? err.message : String(err)}`); + process.exit(1); + }); + }); + + process.on('SIGINT', () => { + shutdown().catch((err: unknown) => { + logError(`Shutdown error: ${err instanceof Error ? err.message : String(err)}`); + process.exit(1); + }); + }); +}; + +main().catch((err: unknown) => { + logError(`Daemon failed to start: ${err instanceof Error ? err.message : String(err)}`); + removePidFile(); + process.exit(1); +}); diff --git a/src/daemon/config.test.ts b/src/daemon/config.test.ts new file mode 100644 index 0000000..9a5c424 --- /dev/null +++ b/src/daemon/config.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-config-${Date.now()}`); + +describe('config', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('creates default config on first run', async () => { + const { loadConfig } = await import('./config.js'); + const config = loadConfig(); + + expect(config.machine.id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + expect(config.daemon.port).toBe(4243); + expect(config.discovery.dirs).toHaveLength(2); + expect(existsSync(join(testDir, 'config.json'))).toBe(true); + }); + + it('generates machine ID as UUID v4', async () => { + const { loadConfig } = await import('./config.js'); + const config = loadConfig(); + expect(config.machine.id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); + }); + + it('loads existing config without overwriting', async () => { + const { loadConfig } = await import('./config.js'); + const config = loadConfig(); + const originalId = config.machine.id; + + // Load again โ€” should return same config + const config2 = loadConfig(); + expect(config2.machine.id).toBe(originalId); + }); + + it('default discovery dirs include agents and skills', async () => { + const { loadConfig } = await import('./config.js'); + const config = loadConfig(); + + const agentsDir = config.discovery.dirs.find((d) => d.endsWith('/agents')); + const skillsDir = config.discovery.dirs.find((d) => d.endsWith('/skills')); + expect(agentsDir).toBeDefined(); + expect(skillsDir).toBeDefined(); + }); + + it('respects AGENTAGE_CONFIG_DIR', async () => { + const { getConfigDir } = await import('./config.js'); + expect(getConfigDir()).toBe(testDir); + }); + + it('saves and loads config', async () => { + const { loadConfig, saveConfig } = await import('./config.js'); + const config = loadConfig(); + config.daemon.port = 9999; + saveConfig(config); + + const raw = readFileSync(join(testDir, 'config.json'), 'utf-8'); + const loaded = JSON.parse(raw); + expect(loaded.daemon.port).toBe(9999); + }); + + it('creates config dir if it does not exist', async () => { + const nested = join(testDir, 'nested', 'deep'); + process.env['AGENTAGE_CONFIG_DIR'] = nested; + + const { getConfigDir } = await import('./config.js'); + const dir = getConfigDir(); + expect(dir).toBe(nested); + expect(existsSync(nested)).toBe(true); + }); + + it('machine name defaults to hostname', async () => { + const { loadConfig } = await import('./config.js'); + const config = loadConfig(); + expect(config.machine.name).toBeTruthy(); + expect(typeof config.machine.name).toBe('string'); + }); +}); diff --git a/src/daemon/config.ts b/src/daemon/config.ts new file mode 100644 index 0000000..11fc60f --- /dev/null +++ b/src/daemon/config.ts @@ -0,0 +1,92 @@ +import { randomUUID } from 'node:crypto'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { hostname } from 'node:os'; +import { join } from 'node:path'; + +export interface SyncEvents { + state: boolean; + result: boolean; + error: boolean; + input_required: boolean; + 'output.llm.delta': boolean; + 'output.llm.tool_call': boolean; + 'output.llm.usage': boolean; + 'output.progress': boolean; +} + +export interface DaemonConfig { + machine: { + id: string; + name: string; + }; + daemon: { + port: number; + }; + hub?: { + url: string; + }; + discovery: { + dirs: string[]; + }; + sync: { + events: SyncEvents; + }; +} + +export const getConfigDir = (): string => { + const dir = process.env['AGENTAGE_CONFIG_DIR'] || join(process.env['HOME'] || '~', '.agentage'); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } + return dir; +}; + +const createDefaultConfig = (): DaemonConfig => { + const configDir = getConfigDir(); + return { + machine: { + id: randomUUID(), + name: hostname(), + }, + daemon: { + port: 4243, + }, + discovery: { + dirs: [join(configDir, 'agents'), join(configDir, 'skills')], + }, + sync: { + events: { + state: true, + result: true, + error: true, + input_required: true, + 'output.llm.delta': true, + 'output.llm.tool_call': true, + 'output.llm.usage': true, + 'output.progress': true, + }, + }, + }; +}; + +export const loadConfig = (): DaemonConfig => { + const configPath = join(getConfigDir(), 'config.json'); + + if (existsSync(configPath)) { + const raw = readFileSync(configPath, 'utf-8'); + return JSON.parse(raw) as DaemonConfig; + } + + const config = createDefaultConfig(); + saveConfig(config); + return config; +}; + +export const saveConfig = (config: DaemonConfig): void => { + const configDir = getConfigDir(); + if (!existsSync(configDir)) { + mkdirSync(configDir, { recursive: true }); + } + const configPath = join(configDir, 'config.json'); + writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8'); +}; diff --git a/src/daemon/daemon.test.ts b/src/daemon/daemon.test.ts new file mode 100644 index 0000000..4bada5b --- /dev/null +++ b/src/daemon/daemon.test.ts @@ -0,0 +1,82 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-daemon-${Date.now()}`); + +describe('daemon', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('isDaemonRunning returns false when no PID file', async () => { + const { isDaemonRunning } = await import('./daemon.js'); + expect(isDaemonRunning()).toBe(false); + }); + + it('isDaemonRunning returns true for live process PID', async () => { + writeFileSync(join(testDir, 'daemon.pid'), String(process.pid)); + const { isDaemonRunning } = await import('./daemon.js'); + expect(isDaemonRunning()).toBe(true); + }); + + it('isDaemonRunning cleans up stale PID file', async () => { + writeFileSync(join(testDir, 'daemon.pid'), '999999'); + const { isDaemonRunning } = await import('./daemon.js'); + // Process 999999 is almost certainly not alive + expect(isDaemonRunning()).toBe(false); + }); + + it('getDaemonPid returns null when not running', async () => { + const { getDaemonPid } = await import('./daemon.js'); + expect(getDaemonPid()).toBeNull(); + }); + + it('getDaemonPid returns PID for live process', async () => { + writeFileSync(join(testDir, 'daemon.pid'), String(process.pid)); + const { getDaemonPid } = await import('./daemon.js'); + expect(getDaemonPid()).toBe(process.pid); + }); + + it('writePidFile and removePidFile work', async () => { + const { existsSync } = await import('node:fs'); + const { writePidFile, removePidFile } = await import('./daemon.js'); + + writePidFile(12345); + expect(existsSync(join(testDir, 'daemon.pid'))).toBe(true); + + removePidFile(); + expect(existsSync(join(testDir, 'daemon.pid'))).toBe(false); + }); + + it('stopDaemon handles no PID file gracefully', async () => { + const { stopDaemon } = await import('./daemon.js'); + // Should not throw + stopDaemon(); + }); + + it('isDaemonRunning handles invalid PID file content', async () => { + writeFileSync(join(testDir, 'daemon.pid'), 'not-a-number'); + const { isDaemonRunning } = await import('./daemon.js'); + expect(isDaemonRunning()).toBe(false); + }); + + it('getDaemonPid handles invalid PID file content', async () => { + writeFileSync(join(testDir, 'daemon.pid'), 'abc'); + const { getDaemonPid } = await import('./daemon.js'); + expect(getDaemonPid()).toBeNull(); + }); + + it('removePidFile is safe when no PID file exists', async () => { + const { removePidFile } = await import('./daemon.js'); + // Should not throw + removePidFile(); + }); +}); diff --git a/src/daemon/daemon.ts b/src/daemon/daemon.ts new file mode 100644 index 0000000..ee940b1 --- /dev/null +++ b/src/daemon/daemon.ts @@ -0,0 +1,115 @@ +import { fork } from 'node:child_process'; +import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { getConfigDir, loadConfig } from './config.js'; +import { logInfo } from './logger.js'; + +const getPidPath = (): string => join(getConfigDir(), 'daemon.pid'); + +export const writePidFile = (pid: number): void => { + writeFileSync(getPidPath(), String(pid), 'utf-8'); +}; + +export const removePidFile = (): void => { + const pidPath = getPidPath(); + if (existsSync(pidPath)) { + unlinkSync(pidPath); + } +}; + +const readPid = (): number | null => { + const pidPath = getPidPath(); + if (!existsSync(pidPath)) return null; + const raw = readFileSync(pidPath, 'utf-8').trim(); + const pid = parseInt(raw, 10); + return isNaN(pid) ? null : pid; +}; + +const isProcessAlive = (pid: number): boolean => { + try { + process.kill(pid, 0); + return true; + } catch { + return false; + } +}; + +export const isDaemonRunning = (): boolean => { + const pid = readPid(); + if (pid === null) return false; + if (!isProcessAlive(pid)) { + removePidFile(); + return false; + } + return true; +}; + +export const getDaemonPid = (): number | null => { + const pid = readPid(); + if (pid === null) return null; + if (!isProcessAlive(pid)) { + removePidFile(); + return null; + } + return pid; +}; + +export const startDaemon = (): Promise => { + if (isDaemonRunning()) return Promise.resolve(); + + const daemonScript = join(fileURLToPath(import.meta.url), '..', '..', 'daemon-entry.js'); + + const child = fork(daemonScript, [], { + detached: true, + stdio: 'ignore', + env: { ...process.env }, + }); + + if (child.pid) { + writePidFile(child.pid); + child.unref(); + logInfo(`Daemon started (PID ${child.pid})`); + } + + // Wait for daemon to be ready + return waitForDaemon(); +}; + +const waitForDaemon = async (): Promise => { + const config = loadConfig(); + const port = config.daemon.port; + const maxWait = 5000; + const interval = 200; + const start = Date.now(); + + while (Date.now() - start < maxWait) { + try { + const response = await fetch(`http://localhost:${port}/api/health`); + if (response.ok) return; + } catch { + // Not ready yet + } + await new Promise((resolve) => setTimeout(resolve, interval)); + } + + throw new Error('Daemon failed to start within 5 seconds'); +}; + +export const stopDaemon = (): void => { + const pid = readPid(); + if (pid === null) return; + + if (isProcessAlive(pid)) { + process.kill(pid, 'SIGTERM'); + } + removePidFile(); + logInfo('Daemon stopped'); +}; + +export const restartDaemon = async (): Promise => { + stopDaemon(); + // Brief pause for port release + await new Promise((resolve) => setTimeout(resolve, 500)); + await startDaemon(); +}; diff --git a/src/daemon/logger.test.ts b/src/daemon/logger.test.ts new file mode 100644 index 0000000..0281aa1 --- /dev/null +++ b/src/daemon/logger.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-logger-${Date.now()}`); + +describe('logger', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('writes to daemon.log', async () => { + const { logInfo } = await import('./logger.js'); + logInfo('test message'); + + const logPath = join(testDir, 'daemon.log'); + expect(existsSync(logPath)).toBe(true); + const content = readFileSync(logPath, 'utf-8'); + expect(content).toContain('test message'); + expect(content).toContain('[INFO]'); + }); + + it('respects log levels โ€” debug not written at info level', async () => { + const { logDebug, setLogLevel } = await import('./logger.js'); + setLogLevel('info'); + logDebug('debug message'); + + const logPath = join(testDir, 'daemon.log'); + if (existsSync(logPath)) { + const content = readFileSync(logPath, 'utf-8'); + expect(content).not.toContain('debug message'); + } + }); + + it('writes error level messages', async () => { + const { logError } = await import('./logger.js'); + logError('something broke'); + + const logPath = join(testDir, 'daemon.log'); + const content = readFileSync(logPath, 'utf-8'); + expect(content).toContain('[ERROR]'); + expect(content).toContain('something broke'); + }); + + it('writes warn level messages', async () => { + const { logWarn } = await import('./logger.js'); + logWarn('be careful'); + + const logPath = join(testDir, 'daemon.log'); + const content = readFileSync(logPath, 'utf-8'); + expect(content).toContain('[WARN]'); + expect(content).toContain('be careful'); + }); + + it('includes timestamp in log entries', async () => { + const { logInfo } = await import('./logger.js'); + logInfo('timestamp test'); + + const logPath = join(testDir, 'daemon.log'); + const content = readFileSync(logPath, 'utf-8'); + // ISO timestamp pattern + expect(content).toMatch(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); + }); +}); diff --git a/src/daemon/logger.ts b/src/daemon/logger.ts new file mode 100644 index 0000000..3891abd --- /dev/null +++ b/src/daemon/logger.ts @@ -0,0 +1,52 @@ +import { appendFileSync, existsSync, renameSync, statSync } from 'node:fs'; +import { join } from 'node:path'; +import { getConfigDir } from './config.js'; + +type LogLevel = 'error' | 'warn' | 'info' | 'debug'; + +const LOG_LEVELS: Record = { + error: 0, + warn: 1, + info: 2, + debug: 3, +}; + +const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB + +let currentLevel: LogLevel = 'info'; + +export const setLogLevel = (level: LogLevel): void => { + currentLevel = level; +}; + +export const getLogLevel = (): LogLevel => currentLevel; + +const getLogPath = (): string => join(getConfigDir(), 'daemon.log'); + +const rotateIfNeeded = (logPath: string): void => { + if (!existsSync(logPath)) return; + + const stats = statSync(logPath); + if (stats.size >= MAX_LOG_SIZE) { + const rotatedPath = logPath + '.1'; + renameSync(logPath, rotatedPath); + } +}; + +const formatMessage = (level: LogLevel, message: string): string => { + const timestamp = new Date().toISOString(); + return `[${timestamp}] [${level.toUpperCase()}] ${message}\n`; +}; + +export const log = (level: LogLevel, message: string): void => { + if (LOG_LEVELS[level] > LOG_LEVELS[currentLevel]) return; + + const logPath = getLogPath(); + rotateIfNeeded(logPath); + appendFileSync(logPath, formatMessage(level, message), 'utf-8'); +}; + +export const logError = (message: string): void => log('error', message); +export const logWarn = (message: string): void => log('warn', message); +export const logInfo = (message: string): void => log('info', message); +export const logDebug = (message: string): void => log('debug', message); diff --git a/src/daemon/routes.ts b/src/daemon/routes.ts new file mode 100644 index 0000000..be3f146 --- /dev/null +++ b/src/daemon/routes.ts @@ -0,0 +1,124 @@ +import { type Router, Router as createRouter, json } from 'express'; +import { type Agent } from '@agentage/core'; +import { loadConfig } from './config.js'; +import { cancelRun, getRun, getRuns, sendInput, startRun } from './run-manager.js'; + +const VERSION = '0.2.0'; +const startTime = Date.now(); + +let agents: Agent[] = []; +let refreshHandler: (() => Promise) | null = null; + +export const setAgents = (newAgents: Agent[]): void => { + agents = newAgents; +}; + +export const getAgents = (): Agent[] => agents; + +export const setRefreshHandler = (handler: () => Promise): void => { + refreshHandler = handler; +}; + +export const createRoutes = (): Router => { + const router = createRouter(); + router.use(json()); + + router.get('/api/health', (_req, res) => { + const config = loadConfig(); + res.json({ + status: 'ok', + version: VERSION, + uptime: Math.floor((Date.now() - startTime) / 1000), + machineId: config.machine.id, + hubConnected: false, + }); + }); + + router.get('/api/agents', (_req, res) => { + res.json(agents.map((a) => a.manifest)); + }); + + router.post('/api/agents/refresh', async (_req, res) => { + try { + if (refreshHandler) { + agents = await refreshHandler(); + } + res.json(agents.map((a) => a.manifest)); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + res.status(500).json({ error: message }); + } + }); + + router.post('/api/agents/:name/run', async (req, res) => { + try { + const { name } = req.params; + const { task, config, context } = req.body as { + task?: string; + config?: Record; + context?: string[]; + }; + + if (!task) { + res.status(400).json({ error: 'Missing "task" in request body' }); + return; + } + + const agent = agents.find((a) => a.manifest.name === name); + if (!agent) { + res.status(404).json({ error: `Agent "${name}" not found` }); + return; + } + + const runId = await startRun(agent, task, config, context); + res.json({ runId }); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + res.status(500).json({ error: message }); + } + }); + + router.get('/api/runs', (_req, res) => { + res.json(getRuns()); + }); + + router.get('/api/runs/:id', (req, res) => { + const run = getRun(req.params.id); + if (!run) { + res.status(404).json({ error: 'Run not found' }); + return; + } + res.json(run); + }); + + router.post('/api/runs/:id/cancel', (req, res) => { + const ok = cancelRun(req.params.id); + if (!ok) { + res.status(400).json({ error: 'Cannot cancel run' }); + return; + } + res.json({ ok: true }); + }); + + router.post('/api/runs/:id/input', (req, res) => { + const { text } = req.body as { text?: string }; + if (!text) { + res.status(400).json({ error: 'Missing "text" in request body' }); + return; + } + + const ok = sendInput(req.params.id, text); + if (!ok) { + res.status(400).json({ error: 'Cannot send input to run' }); + return; + } + res.json({ ok: true }); + }); + + // OAuth callback placeholder + router.get('/auth/callback', (_req, res) => { + res.send('Hub sync not yet available.'); + }); + + return router; +}; diff --git a/src/daemon/run-manager.test.ts b/src/daemon/run-manager.test.ts new file mode 100644 index 0000000..3f25d4c --- /dev/null +++ b/src/daemon/run-manager.test.ts @@ -0,0 +1,276 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { type Agent, type RunEvent } from '@agentage/core'; + +const testDir = join(tmpdir(), `agentage-test-rm-${Date.now()}`); + +describe('run-manager', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + const createMockAgent = (name: string, events: RunEvent[]): Agent => ({ + manifest: { name, path: '/test', description: `Test agent ${name}` }, + async run() { + const canceled = { value: false }; + + async function* gen(): AsyncIterable { + for (const event of events) { + if (canceled.value) return; + yield event; + } + } + + return { + runId: 'test-run', + events: gen(), + cancel: () => { + canceled.value = true; + }, + sendInput: () => {}, + }; + }, + }); + + it('starts a run and returns a runId', async () => { + const { startRun } = await import('./run-manager.js'); + const agent = createMockAgent('test', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + const runId = await startRun(agent, 'hello'); + expect(runId).toBeDefined(); + expect(typeof runId).toBe('string'); + }); + + it('tracks runs in getRuns', async () => { + const { startRun, getRuns } = await import('./run-manager.js'); + const agent = createMockAgent('test2', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + await startRun(agent, 'hello'); + + // Small delay for event consumption + await new Promise((r) => setTimeout(r, 50)); + + const runs = getRuns(); + expect(runs.length).toBeGreaterThanOrEqual(1); + }); + + it('gets a single run by id', async () => { + const { startRun, getRun } = await import('./run-manager.js'); + const agent = createMockAgent('test3', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + const runId = await startRun(agent, 'hello'); + const run = getRun(runId); + expect(run).toBeDefined(); + expect(run?.id).toBe(runId); + expect(run?.agentName).toBe('test3'); + }); + + it('emits events to listeners', async () => { + const { startRun, onRunEvent } = await import('./run-manager.js'); + const receivedEvents: RunEvent[] = []; + + onRunEvent((_runId, event) => { + receivedEvents.push(event); + }); + + const agent = createMockAgent('test4', [ + { + type: 'output', + data: { type: 'output', content: 'hello', format: 'text' }, + timestamp: Date.now(), + }, + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + await startRun(agent, 'hello'); + await new Promise((r) => setTimeout(r, 100)); + + expect(receivedEvents.length).toBeGreaterThanOrEqual(1); + }); + + it('concurrent runs allowed', async () => { + const { startRun } = await import('./run-manager.js'); + const agent1 = createMockAgent('agent-a', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + const agent2 = createMockAgent('agent-b', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + const [id1, id2] = await Promise.all([startRun(agent1, 'task1'), startRun(agent2, 'task2')]); + + expect(id1).not.toBe(id2); + }); + + it('cancelRun returns false for unknown run', async () => { + const { cancelRun } = await import('./run-manager.js'); + expect(cancelRun('nonexistent')).toBe(false); + }); + + it('sendInput returns false for unknown run', async () => { + const { sendInput } = await import('./run-manager.js'); + expect(sendInput('nonexistent', 'text')).toBe(false); + }); + + it('getRun returns undefined for unknown run', async () => { + const { getRun } = await import('./run-manager.js'); + expect(getRun('nonexistent')).toBeUndefined(); + }); + + it('handles failed result event', async () => { + const { startRun, getRun } = await import('./run-manager.js'); + const agent = createMockAgent('fail-agent', [ + { type: 'result', data: { type: 'result', success: false }, timestamp: Date.now() }, + ]); + + const runId = await startRun(agent, 'fail'); + await new Promise((r) => setTimeout(r, 100)); + + const run = getRun(runId); + expect(run?.state).toBe('failed'); + }); + + it('handles non-recoverable error event', async () => { + const { startRun, getRun } = await import('./run-manager.js'); + const agent = createMockAgent('error-agent', [ + { + type: 'error', + data: { type: 'error', code: 'FATAL', message: 'boom', recoverable: false }, + timestamp: Date.now(), + }, + ]); + + const runId = await startRun(agent, 'err'); + await new Promise((r) => setTimeout(r, 100)); + + const run = getRun(runId); + expect(run?.state).toBe('failed'); + expect(run?.error).toBe('boom'); + }); + + it('cancelAllRuns cancels active runs', async () => { + const { startRun, cancelAllRuns, getRun } = await import('./run-manager.js'); + + // Create a slow agent that yields many events + const slowAgent: Agent = { + manifest: { name: 'slow-cancel', path: '/test' }, + async run() { + async function* gen(): AsyncIterable { + for (let i = 0; i < 100; i++) { + yield { + type: 'output', + data: { type: 'output', content: `step ${i}`, format: 'text' }, + timestamp: Date.now(), + }; + await new Promise((r) => setTimeout(r, 50)); + } + } + return { + runId: 'slow', + events: gen(), + cancel: () => {}, + sendInput: () => {}, + }; + }, + }; + + const runId = await startRun(slowAgent, 'go'); + await new Promise((r) => setTimeout(r, 50)); + + cancelAllRuns(); + const run = getRun(runId); + expect(run?.state).toBe('canceled'); + }); + + it('handles input_required event and sendInput', async () => { + const { startRun, getRun, sendInput } = await import('./run-manager.js'); + + let resolveInput: (() => void) | undefined; + const inputPromise = new Promise((r) => { + resolveInput = r; + }); + + const agent: Agent = { + manifest: { name: 'input-agent', path: '/test' }, + async run() { + async function* gen(): AsyncIterable { + yield { + type: 'input_required', + data: { type: 'input_required', prompt: 'Enter name:' }, + timestamp: Date.now(), + }; + // Wait for input + await inputPromise; + yield { + type: 'result', + data: { type: 'result', success: true }, + timestamp: Date.now(), + }; + } + return { + runId: 'input-test', + events: gen(), + cancel: () => {}, + sendInput: () => { + resolveInput?.(); + }, + }; + }, + }; + + const runId = await startRun(agent, 'do input'); + await new Promise((r) => setTimeout(r, 100)); + + const run = getRun(runId); + expect(run?.state).toBe('input_required'); + + const ok = sendInput(runId, 'John'); + expect(ok).toBe(true); + }); + + it('sendInput returns false when not in input_required state', async () => { + const { startRun, sendInput } = await import('./run-manager.js'); + const agent = createMockAgent('no-input', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + const runId = await startRun(agent, 'test'); + await new Promise((r) => setTimeout(r, 100)); + + // Run is likely completed, not input_required + expect(sendInput(runId, 'text')).toBe(false); + }); + + it('emits state changes to listeners', async () => { + const { startRun, onRunStateChange } = await import('./run-manager.js'); + const stateChanges: string[] = []; + + onRunStateChange((run) => { + stateChanges.push(run.state); + }); + + const agent = createMockAgent('state-test', [ + { type: 'result', data: { type: 'result', success: true }, timestamp: Date.now() }, + ]); + + await startRun(agent, 'hello'); + await new Promise((r) => setTimeout(r, 100)); + + expect(stateChanges).toContain('working'); + expect(stateChanges).toContain('completed'); + }); +}); diff --git a/src/daemon/run-manager.ts b/src/daemon/run-manager.ts new file mode 100644 index 0000000..5569309 --- /dev/null +++ b/src/daemon/run-manager.ts @@ -0,0 +1,164 @@ +import { randomUUID } from 'node:crypto'; +import { + type Agent, + type AgentProcess, + type Run, + type RunEvent, + canTransition, +} from '@agentage/core'; +import { logError, logInfo } from './logger.js'; + +type RunEventListener = (runId: string, event: RunEvent) => void; +type RunStateListener = (run: Run) => void; + +interface TrackedRun { + run: Run; + process: AgentProcess; +} + +const runs = new Map(); +const eventListeners = new Set(); +const stateListeners = new Set(); + +export const onRunEvent = (listener: RunEventListener): (() => void) => { + eventListeners.add(listener); + return () => eventListeners.delete(listener); +}; + +export const onRunStateChange = (listener: RunStateListener): (() => void) => { + stateListeners.add(listener); + return () => stateListeners.delete(listener); +}; + +const emitEvent = (runId: string, event: RunEvent): void => { + for (const listener of eventListeners) { + listener(runId, event); + } +}; + +const emitStateChange = (run: Run): void => { + for (const listener of stateListeners) { + listener(run); + } +}; + +const updateRunState = ( + tracked: TrackedRun, + newState: Run['state'], + extra?: Partial +): void => { + if (!canTransition(tracked.run.state, newState)) { + logError( + `Invalid state transition: ${tracked.run.state} โ†’ ${newState} for run ${tracked.run.id}` + ); + return; + } + tracked.run.state = newState; + if (extra) { + Object.assign(tracked.run, extra); + } + emitStateChange(tracked.run); +}; + +export const startRun = async ( + agent: Agent, + task: string, + config?: Record, + context?: string[] +): Promise => { + const runId = randomUUID(); + const run: Run = { + id: runId, + agentName: agent.manifest.name, + input: task, + state: 'submitted', + createdAt: Date.now(), + }; + + logInfo(`Starting run ${runId} for agent "${agent.manifest.name}"`); + + const process = await agent.run({ task, config, context }); + const tracked: TrackedRun = { run, process }; + runs.set(runId, tracked); + + updateRunState(tracked, 'working', { startedAt: Date.now() }); + + // Consume events in background + consumeEvents(tracked).catch((err: unknown) => { + const message = err instanceof Error ? err.message : String(err); + logError(`Run ${runId} failed: ${message}`); + updateRunState(tracked, 'failed', { error: message, endedAt: Date.now() }); + }); + + return runId; +}; + +const consumeEvents = async (tracked: TrackedRun): Promise => { + for await (const event of tracked.process.events) { + emitEvent(tracked.run.id, event); + + if (event.data.type === 'state') { + updateRunState(tracked, event.data.state); + } + + if (event.data.type === 'result') { + updateRunState(tracked, event.data.success ? 'completed' : 'failed', { + endedAt: Date.now(), + error: event.data.success ? undefined : 'Agent returned unsuccessful result', + }); + } + + if (event.data.type === 'error' && !event.data.recoverable) { + updateRunState(tracked, 'failed', { + error: event.data.message, + endedAt: Date.now(), + }); + } + + if (event.data.type === 'input_required') { + if (canTransition(tracked.run.state, 'input_required')) { + updateRunState(tracked, 'input_required'); + } + } + } + + // If events ended without a terminal state, mark completed + if (!['completed', 'failed', 'canceled'].includes(tracked.run.state)) { + updateRunState(tracked, 'completed', { endedAt: Date.now() }); + } +}; + +export const cancelRun = (runId: string): boolean => { + const tracked = runs.get(runId); + if (!tracked) return false; + if (!canTransition(tracked.run.state, 'canceled')) return false; + + tracked.process.cancel(); + updateRunState(tracked, 'canceled', { endedAt: Date.now() }); + logInfo(`Run ${runId} canceled`); + return true; +}; + +export const sendInput = (runId: string, text: string): boolean => { + const tracked = runs.get(runId); + if (!tracked) return false; + if (tracked.run.state !== 'input_required') return false; + + tracked.process.sendInput(text); + updateRunState(tracked, 'working'); + return true; +}; + +export const getRuns = (): Run[] => [...runs.values()].map((t) => t.run); + +export const getRun = (runId: string): Run | undefined => runs.get(runId)?.run; + +export const cancelAllRuns = (): void => { + for (const [runId, tracked] of runs) { + if (canTransition(tracked.run.state, 'canceled')) { + tracked.process.cancel(); + updateRunState(tracked, 'canceled', { endedAt: Date.now() }); + logInfo(`Run ${runId} canceled (shutdown)`); + } + } +}; diff --git a/src/daemon/server.test.ts b/src/daemon/server.test.ts new file mode 100644 index 0000000..0ffab93 --- /dev/null +++ b/src/daemon/server.test.ts @@ -0,0 +1,183 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { mkdirSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { type Agent, type RunEvent } from '@agentage/core'; +import { type DaemonServer } from './server.js'; + +const testDir = join(tmpdir(), `agentage-test-server-${Date.now()}`); +let server: DaemonServer; +let port: number; + +describe('server', () => { + beforeAll(async () => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + + // Write config with a random port to avoid conflicts + port = 14000 + Math.floor(Math.random() * 1000); + const { writeFileSync } = await import('node:fs'); + const { randomUUID } = await import('node:crypto'); + writeFileSync( + join(testDir, 'config.json'), + JSON.stringify({ + machine: { id: randomUUID(), name: 'test' }, + daemon: { port }, + discovery: { dirs: [] }, + sync: { events: {} }, + }) + ); + + const { createDaemonServer } = await import('./server.js'); + server = createDaemonServer(); + + // Add a test agent + const mockAgent: Agent = { + manifest: { name: 'test-agent', description: 'Test', path: '/test' }, + async run() { + async function* gen(): AsyncIterable { + yield { + type: 'output', + data: { type: 'output', content: 'hello', format: 'text' }, + timestamp: Date.now(), + }; + yield { + type: 'result', + data: { type: 'result', success: true }, + timestamp: Date.now(), + }; + } + return { + runId: 'test', + events: gen(), + cancel: () => {}, + sendInput: () => {}, + }; + }, + }; + server.updateAgents([mockAgent]); + await server.start(); + }); + + afterAll(async () => { + await server.stop(); + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('GET /api/health returns ok', async () => { + const res = await fetch(`http://localhost:${port}/api/health`); + const body = (await res.json()) as Record; + expect(res.status).toBe(200); + expect(body.status).toBe('ok'); + expect(body.version).toBe('0.2.0'); + expect(typeof body.uptime).toBe('number'); + expect(body.hubConnected).toBe(false); + }); + + it('GET /api/agents returns agent list', async () => { + const res = await fetch(`http://localhost:${port}/api/agents`); + const body = (await res.json()) as Array>; + expect(res.status).toBe(200); + expect(Array.isArray(body)).toBe(true); + expect(body[0].name).toBe('test-agent'); + }); + + it('POST /api/agents/:name/run creates run', async () => { + const res = await fetch(`http://localhost:${port}/api/agents/test-agent/run`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ task: 'hello' }), + }); + const body = (await res.json()) as { runId: string }; + expect(res.status).toBe(200); + expect(body.runId).toBeDefined(); + }); + + it('GET /api/runs returns run list', async () => { + // Wait for the run from previous test to register + await new Promise((r) => setTimeout(r, 100)); + const res = await fetch(`http://localhost:${port}/api/runs`); + const body = (await res.json()) as unknown[]; + expect(res.status).toBe(200); + expect(Array.isArray(body)).toBe(true); + }); + + it('GET /api/runs/:id returns single run', async () => { + const runRes = await fetch(`http://localhost:${port}/api/agents/test-agent/run`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ task: 'test' }), + }); + const { runId } = (await runRes.json()) as { runId: string }; + + const res = await fetch(`http://localhost:${port}/api/runs/${runId}`); + const body = (await res.json()) as { id: string }; + expect(res.status).toBe(200); + expect(body.id).toBe(runId); + }); + + it('POST /api/agents/:name/run returns 404 for unknown agent', async () => { + const res = await fetch(`http://localhost:${port}/api/agents/nonexistent/run`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ task: 'hello' }), + }); + expect(res.status).toBe(404); + }); + + it('POST /api/agents/:name/run returns 400 without task', async () => { + const res = await fetch(`http://localhost:${port}/api/agents/test-agent/run`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }); + expect(res.status).toBe(400); + }); + + it('GET /api/runs/:id returns 404 for unknown run', async () => { + const res = await fetch(`http://localhost:${port}/api/runs/nonexistent`); + expect(res.status).toBe(404); + }); + + it('POST /api/runs/:id/cancel returns 400 for unknown run', async () => { + const res = await fetch(`http://localhost:${port}/api/runs/nonexistent/cancel`, { + method: 'POST', + }); + expect(res.status).toBe(400); + }); + + it('POST /api/runs/:id/input returns 400 without text', async () => { + const res = await fetch(`http://localhost:${port}/api/runs/some-id/input`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }); + expect(res.status).toBe(400); + }); + + it('POST /api/runs/:id/input returns 400 for invalid run', async () => { + const res = await fetch(`http://localhost:${port}/api/runs/nonexistent/input`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ text: 'hello' }), + }); + expect(res.status).toBe(400); + }); + + it('POST /api/agents/refresh returns agent list', async () => { + const res = await fetch(`http://localhost:${port}/api/agents/refresh`, { + method: 'POST', + }); + const body = await res.json(); + expect(res.status).toBe(200); + expect(Array.isArray(body)).toBe(true); + }); + + it('GET /auth/callback returns placeholder', async () => { + const res = await fetch(`http://localhost:${port}/auth/callback`); + expect(res.status).toBe(200); + const text = await res.text(); + expect(text).toContain('not yet available'); + }); +}); diff --git a/src/daemon/server.ts b/src/daemon/server.ts new file mode 100644 index 0000000..4adc514 --- /dev/null +++ b/src/daemon/server.ts @@ -0,0 +1,77 @@ +import { createServer, type Server } from 'node:http'; +import express from 'express'; +import { type Agent, type AgentFactory } from '@agentage/core'; +import { loadConfig } from './config.js'; +import { logInfo } from './logger.js'; +import { createRoutes, setAgents, setRefreshHandler } from './routes.js'; +import { setupWebSocket } from './websocket.js'; +import { cancelAllRuns } from './run-manager.js'; +import { scanAgents } from '../discovery/scanner.js'; + +export interface DaemonServer { + server: Server; + start: () => Promise; + stop: () => Promise; + updateAgents: (agents: Agent[]) => void; + setFactories: (factories: AgentFactory[]) => void; +} + +export const createDaemonServer = (): DaemonServer => { + const app = express(); + const server = createServer(app); + let factories: AgentFactory[] = []; + + const routes = createRoutes(); + app.use(routes); + + setupWebSocket(server); + + // Wire up refresh to actually rescan + setRefreshHandler(async () => { + const config = loadConfig(); + const agents = await scanAgents(config.discovery.dirs, factories); + setAgents(agents); + logInfo(`Refresh: discovered ${agents.length} agent(s)`); + return agents; + }); + + const start = async (): Promise => { + const config = loadConfig(); + const port = config.daemon.port; + + return new Promise((resolve, reject) => { + server.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EADDRINUSE') { + reject(new Error(`Port ${port} already in use. Is another daemon running?`)); + } else { + reject(err); + } + }); + + server.listen(port, () => { + logInfo(`Daemon server listening on port ${port}`); + resolve(); + }); + }); + }; + + const stop = async (): Promise => { + cancelAllRuns(); + return new Promise((resolve) => { + server.close(() => { + logInfo('Daemon server stopped'); + resolve(); + }); + }); + }; + + const updateAgents = (agents: Agent[]): void => { + setAgents(agents); + }; + + const setFactoriesFn = (f: AgentFactory[]): void => { + factories = f; + }; + + return { server, start, stop, updateAgents, setFactories: setFactoriesFn }; +}; diff --git a/src/daemon/websocket.test.ts b/src/daemon/websocket.test.ts new file mode 100644 index 0000000..d54cd75 --- /dev/null +++ b/src/daemon/websocket.test.ts @@ -0,0 +1,115 @@ +import { describe, it, beforeAll, afterAll } from 'vitest'; +import { createServer, type Server } from 'node:http'; +import { WebSocket } from 'ws'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { randomUUID } from 'node:crypto'; + +const testDir = join(tmpdir(), `agentage-test-ws-${Date.now()}`); +let httpServer: Server; +let wsPort: number; + +describe('websocket', () => { + beforeAll(async () => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + writeFileSync( + join(testDir, 'config.json'), + JSON.stringify({ + machine: { id: randomUUID(), name: 'test' }, + daemon: { port: 4243 }, + discovery: { dirs: [] }, + sync: { events: {} }, + }) + ); + + httpServer = createServer(); + const { setupWebSocket } = await import('./websocket.js'); + setupWebSocket(httpServer); + + wsPort = 15000 + Math.floor(Math.random() * 1000); + await new Promise((resolve) => { + httpServer.listen(wsPort, resolve); + }); + }); + + afterAll(async () => { + await new Promise((resolve) => { + httpServer.close(() => resolve()); + }); + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('accepts WebSocket connections', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve, reject) => { + ws.on('open', () => resolve()); + ws.on('error', reject); + }); + ws.close(); + }); + + it('handles subscribe message', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + ws.send(JSON.stringify({ type: 'subscribe', runId: 'test-run-123' })); + // Should not throw + await new Promise((r) => setTimeout(r, 50)); + ws.close(); + }); + + it('handles unsubscribe message', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + ws.send(JSON.stringify({ type: 'subscribe', runId: 'test-run-456' })); + ws.send(JSON.stringify({ type: 'unsubscribe', runId: 'test-run-456' })); + await new Promise((r) => setTimeout(r, 50)); + ws.close(); + }); + + it('handles invalid message gracefully', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + ws.send('not json'); + await new Promise((r) => setTimeout(r, 50)); + // Should not crash + ws.close(); + }); + + it('cleans up on disconnect', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + ws.send(JSON.stringify({ type: 'subscribe', runId: 'cleanup-test' })); + ws.close(); + await new Promise((r) => setTimeout(r, 100)); + // Should not throw โ€” cleanup happened + }); + + it('receives run events after subscribing', async () => { + const ws = new WebSocket(`ws://localhost:${wsPort}/ws`); + await new Promise((resolve) => { + ws.on('open', resolve); + }); + + const runId = 'event-test-run'; + ws.send(JSON.stringify({ type: 'subscribe', runId })); + await new Promise((r) => setTimeout(r, 50)); + + ws.close(); + await new Promise((r) => setTimeout(r, 50)); + }); +}); diff --git a/src/daemon/websocket.ts b/src/daemon/websocket.ts new file mode 100644 index 0000000..dff3b27 --- /dev/null +++ b/src/daemon/websocket.ts @@ -0,0 +1,119 @@ +import { type Server } from 'node:http'; +import { WebSocketServer, type WebSocket } from 'ws'; +import { type Run, type RunEvent } from '@agentage/core'; +import { onRunEvent, onRunStateChange } from './run-manager.js'; +import { logDebug, logInfo } from './logger.js'; + +interface SubscribeMessage { + type: 'subscribe'; + runId: string; +} + +interface UnsubscribeMessage { + type: 'unsubscribe'; + runId: string; +} + +type ClientMessage = SubscribeMessage | UnsubscribeMessage; + +type BufferedMessage = + | { type: 'run_event'; runId: string; event: RunEvent } + | { type: 'run_state'; run: Run }; + +const MAX_BUFFER_PER_RUN = 200; +const BUFFER_TTL_MS = 60_000; + +const clientSubscriptions = new Map>(); +const runBuffers = new Map(); +const bufferTimers = new Map>(); + +const bufferMessage = (runId: string, msg: BufferedMessage): void => { + let buf = runBuffers.get(runId); + if (!buf) { + buf = []; + runBuffers.set(runId, buf); + // Auto-cleanup after TTL + const timer = setTimeout(() => { + runBuffers.delete(runId); + bufferTimers.delete(runId); + }, BUFFER_TTL_MS); + bufferTimers.set(runId, timer); + } + if (buf.length < MAX_BUFFER_PER_RUN) { + buf.push(msg); + } +}; + +const replayBuffer = (ws: WebSocket, runId: string): void => { + const buf = runBuffers.get(runId); + if (!buf) return; + logDebug(`Replaying ${buf.length} buffered messages for run ${runId}`); + for (const msg of buf) { + sendToClient(ws, msg); + } +}; + +const sendToClient = (ws: WebSocket, data: unknown): void => { + if (ws.readyState === ws.OPEN) { + ws.send(JSON.stringify(data)); + } +}; + +export const setupWebSocket = (server: Server): WebSocketServer => { + const wss = new WebSocketServer({ server, path: '/ws' }); + + onRunEvent((runId: string, event: RunEvent) => { + const msg: BufferedMessage = { type: 'run_event', runId, event }; + bufferMessage(runId, msg); + + for (const [ws, subs] of clientSubscriptions) { + if (subs.has(runId)) { + sendToClient(ws, msg); + } + } + }); + + onRunStateChange((run: Run) => { + const msg: BufferedMessage = { type: 'run_state', run }; + bufferMessage(run.id, msg); + + for (const [ws, subs] of clientSubscriptions) { + if (subs.has(run.id)) { + sendToClient(ws, msg); + } + } + }); + + wss.on('connection', (ws) => { + logDebug('WebSocket client connected'); + clientSubscriptions.set(ws, new Set()); + + ws.on('message', (raw) => { + try { + const msg = JSON.parse(String(raw)) as ClientMessage; + + if (msg.type === 'subscribe') { + clientSubscriptions.get(ws)?.add(msg.runId); + logDebug(`Client subscribed to run ${msg.runId}`); + // Replay any buffered events the client missed + replayBuffer(ws, msg.runId); + } + + if (msg.type === 'unsubscribe') { + clientSubscriptions.get(ws)?.delete(msg.runId); + logDebug(`Client unsubscribed from run ${msg.runId}`); + } + } catch { + logDebug('Invalid WebSocket message received'); + } + }); + + ws.on('close', () => { + clientSubscriptions.delete(ws); + logDebug('WebSocket client disconnected'); + }); + }); + + logInfo('WebSocket server ready'); + return wss; +}; diff --git a/src/discovery/code-factory.test.ts b/src/discovery/code-factory.test.ts new file mode 100644 index 0000000..44de390 --- /dev/null +++ b/src/discovery/code-factory.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-code-${Date.now()}`); + +describe('code-factory', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = join(testDir, '.agentage'); + mkdirSync(join(testDir, '.agentage'), { recursive: true }); + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('returns null for non-matching paths', async () => { + const { createCodeFactory } = await import('./code-factory.js'); + const factory = createCodeFactory(); + const agent = await factory('/some/file.txt'); + expect(agent).toBeNull(); + }); + + it('returns null for invalid module', async () => { + const agentDir = join(testDir, 'bad'); + mkdirSync(agentDir, { recursive: true }); + writeFileSync(join(agentDir, 'agent.js'), 'module.exports = { notAnAgent: true };'); + + const { createCodeFactory } = await import('./code-factory.js'); + const factory = createCodeFactory(); + const agent = await factory(join(agentDir, 'agent.js')); + expect(agent).toBeNull(); + }); + + it('matches agent.ts and agent.js files', async () => { + const { createCodeFactory } = await import('./code-factory.js'); + const factory = createCodeFactory(); + + // Non-matching + expect(await factory('/path/to/other.ts')).toBeNull(); + expect(await factory('/path/to/agent.txt')).toBeNull(); + }); +}); diff --git a/src/discovery/code-factory.ts b/src/discovery/code-factory.ts new file mode 100644 index 0000000..8536431 --- /dev/null +++ b/src/discovery/code-factory.ts @@ -0,0 +1,42 @@ +import { basename } from 'node:path'; +import { type Agent, type AgentFactory } from '@agentage/core'; +import { createJiti } from 'jiti'; +import { logDebug, logWarn } from '../daemon/logger.js'; + +const matches = (filePath: string): boolean => + basename(filePath) === 'agent.ts' || basename(filePath) === 'agent.js'; + +export const createCodeFactory = + (): AgentFactory => + async (filePath: string): Promise => { + if (!matches(filePath)) return null; + + logDebug(`Loading code agent from ${filePath}`); + + try { + const jiti = createJiti(filePath, { + interopDefault: true, + moduleCache: false, + }); + + const mod = (await jiti.import(filePath)) as { default?: Agent } & Record; + const agent = mod.default ?? mod; + + if ( + agent && + typeof agent === 'object' && + 'manifest' in agent && + 'run' in agent && + typeof agent.run === 'function' + ) { + return agent as Agent; + } + + logWarn(`Module at ${filePath} does not export a valid Agent`); + return null; + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + logWarn(`Failed to load code agent from ${filePath}: ${message}`); + return null; + } + }; diff --git a/src/discovery/markdown-factory.test.ts b/src/discovery/markdown-factory.test.ts new file mode 100644 index 0000000..d4abce4 --- /dev/null +++ b/src/discovery/markdown-factory.test.ts @@ -0,0 +1,98 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-md-${Date.now()}`); + +describe('markdown-factory', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = join(testDir, '.agentage'); + mkdirSync(join(testDir, '.agentage'), { recursive: true }); + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('parses .agent.md with frontmatter', async () => { + const filePath = join(testDir, 'hello.agent.md'); + writeFileSync( + filePath, + '---\nname: hello\ndescription: A greeting agent\nversion: 1.0.0\ntags:\n - greeting\n---\nYou are friendly.' + ); + + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory(filePath); + + expect(agent).not.toBeNull(); + expect(agent!.manifest.name).toBe('hello'); + expect(agent!.manifest.description).toBe('A greeting agent'); + expect(agent!.manifest.version).toBe('1.0.0'); + expect(agent!.manifest.tags).toEqual(['greeting']); + }); + + it('returns null for non-matching paths', async () => { + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory('/some/file.txt'); + expect(agent).toBeNull(); + }); + + it('extracts name from filename when not in frontmatter', async () => { + const filePath = join(testDir, 'my-agent.agent.md'); + writeFileSync(filePath, '---\ndescription: Test\n---\nBody'); + + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory(filePath); + + expect(agent!.manifest.name).toBe('my-agent'); + }); + + it('body becomes systemPrompt in config', async () => { + const filePath = join(testDir, 'test.agent.md'); + writeFileSync(filePath, '---\nname: test\n---\nYou are a test agent.'); + + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory(filePath); + + expect(agent!.manifest.config?.['systemPrompt']).toBe('You are a test agent.'); + }); + + it('run() yields output and result events', async () => { + const filePath = join(testDir, 'run-test.agent.md'); + writeFileSync(filePath, '---\nname: runner\n---\nSystem prompt here.'); + + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory(filePath); + const process = await agent!.run({ task: 'do something' }); + + const events = []; + for await (const event of process.events) { + events.push(event); + } + + expect(events.length).toBeGreaterThanOrEqual(2); + expect(events[0].data.type).toBe('output'); + expect(events.at(-1)!.data.type).toBe('result'); + }); + + it('matches SKILL.md files', async () => { + const skillDir = join(testDir, 'my-skill'); + mkdirSync(skillDir, { recursive: true }); + const filePath = join(skillDir, 'SKILL.md'); + writeFileSync(filePath, '---\nname: my-skill\n---\nSkill body'); + + const { createMarkdownFactory } = await import('./markdown-factory.js'); + const factory = createMarkdownFactory(); + const agent = await factory(filePath); + expect(agent).not.toBeNull(); + expect(agent!.manifest.name).toBe('my-skill'); + }); +}); diff --git a/src/discovery/markdown-factory.ts b/src/discovery/markdown-factory.ts new file mode 100644 index 0000000..8da78e8 --- /dev/null +++ b/src/discovery/markdown-factory.ts @@ -0,0 +1,90 @@ +import { readFileSync } from 'node:fs'; +import { basename } from 'node:path'; +import matter from 'gray-matter'; +import { + type Agent, + type AgentFactory, + type AgentProcess, + type RunEvent, + type RunInput, +} from '@agentage/core'; +import { randomUUID } from 'node:crypto'; + +interface MarkdownFrontmatter { + name?: string; + description?: string; + version?: string; + tags?: string[]; + model?: string; + temperature?: number; +} + +const matches = (filePath: string): boolean => + filePath.endsWith('.agent.md') || filePath.endsWith('/SKILL.md'); + +export const createMarkdownFactory = + (): AgentFactory => + async (filePath: string): Promise => { + if (!matches(filePath)) return null; + + const raw = readFileSync(filePath, 'utf-8'); + const { data, content } = matter(raw); + const fm = data as MarkdownFrontmatter; + + const name = fm.name ?? basename(filePath, '.agent.md'); + const systemPrompt = content.trim(); + + const agent: Agent = { + manifest: { + name, + description: fm.description, + version: fm.version, + tags: fm.tags, + path: filePath, + config: { + systemPrompt, + ...(fm.model ? { model: fm.model } : {}), + ...(fm.temperature !== undefined ? { temperature: fm.temperature } : {}), + }, + }, + async run(input: RunInput): Promise { + const runId = randomUUID(); + let canceled = false; + + async function* generateEvents(): AsyncIterable { + yield { + type: 'output', + data: { type: 'output', content: systemPrompt, format: 'text' }, + timestamp: Date.now(), + }; + + if (!canceled) { + yield { + type: 'output', + data: { type: 'output', content: `\nTask: ${input.task}`, format: 'text' }, + timestamp: Date.now(), + }; + } + + yield { + type: 'result', + data: { type: 'result', success: true, output: 'Agent execution complete' }, + timestamp: Date.now(), + }; + } + + return { + runId, + events: generateEvents(), + cancel: () => { + canceled = true; + }, + sendInput: () => { + // No-op for markdown agents + }, + }; + }, + }; + + return agent; + }; diff --git a/src/discovery/scanner.test.ts b/src/discovery/scanner.test.ts new file mode 100644 index 0000000..3023c76 --- /dev/null +++ b/src/discovery/scanner.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { type AgentFactory } from '@agentage/core'; + +const testDir = join(tmpdir(), `agentage-test-scanner-${Date.now()}`); + +describe('scanner', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = join(testDir, '.agentage'); + mkdirSync(join(testDir, '.agentage'), { recursive: true }); + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('scans directory and returns agents from matching factories', async () => { + const agentsDir = join(testDir, 'agents'); + mkdirSync(agentsDir, { recursive: true }); + writeFileSync(join(agentsDir, 'hello.agent.md'), '---\nname: hello\n---\nHi'); + + const factory: AgentFactory = async (path) => { + if (!path.endsWith('.agent.md')) return null; + return { + manifest: { name: 'hello', description: 'Test', path }, + async run() { + return { + runId: 'test', + events: (async function* () {})(), + cancel: () => {}, + sendInput: () => {}, + }; + }, + }; + }; + + const { scanAgents } = await import('./scanner.js'); + const agents = await scanAgents([agentsDir], [factory]); + expect(agents).toHaveLength(1); + expect(agents[0].manifest.name).toBe('hello'); + }); + + it('skips non-matching files', async () => { + const agentsDir = join(testDir, 'agents2'); + mkdirSync(agentsDir, { recursive: true }); + writeFileSync(join(agentsDir, 'readme.txt'), 'not an agent'); + + const factory: AgentFactory = async () => null; + + const { scanAgents } = await import('./scanner.js'); + const agents = await scanAgents([agentsDir], [factory]); + expect(agents).toHaveLength(0); + }); + + it('handles non-existent directories', async () => { + const { scanAgents } = await import('./scanner.js'); + const agents = await scanAgents([join(testDir, 'nope')], []); + expect(agents).toHaveLength(0); + }); + + it('deduplicates agents by name', async () => { + const agentsDir = join(testDir, 'agents3'); + mkdirSync(agentsDir, { recursive: true }); + writeFileSync(join(agentsDir, 'a.agent.md'), '---\nname: dupe\n---\n'); + writeFileSync(join(agentsDir, 'b.agent.md'), '---\nname: dupe\n---\n'); + + const factory: AgentFactory = async (path) => { + if (!path.endsWith('.agent.md')) return null; + return { + manifest: { name: 'dupe', path }, + async run() { + return { + runId: 'x', + events: (async function* () {})(), + cancel: () => {}, + sendInput: () => {}, + }; + }, + }; + }; + + const { scanAgents } = await import('./scanner.js'); + const agents = await scanAgents([agentsDir], [factory]); + expect(agents).toHaveLength(1); + }); +}); diff --git a/src/discovery/scanner.ts b/src/discovery/scanner.ts new file mode 100644 index 0000000..f0e5ca2 --- /dev/null +++ b/src/discovery/scanner.ts @@ -0,0 +1,55 @@ +import { existsSync, readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { type Agent, type AgentFactory } from '@agentage/core'; +import { logDebug, logInfo, logWarn } from '../daemon/logger.js'; + +const getAllFiles = (dir: string): string[] => { + const results: string[] = []; + + if (!existsSync(dir)) { + logWarn(`Discovery dir does not exist: ${dir}`); + return results; + } + + const entries = readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + results.push(...getAllFiles(fullPath)); + } else { + results.push(fullPath); + } + } + return results; +}; + +export const scanAgents = async (dirs: string[], factories: AgentFactory[]): Promise => { + const agents: Agent[] = []; + const seen = new Set(); + + for (const dir of dirs) { + const expandedDir = dir.replace(/^~/, process.env['HOME'] || '~'); + logDebug(`Scanning directory: ${expandedDir}`); + const files = getAllFiles(expandedDir); + + for (const file of files) { + for (const factory of factories) { + try { + const agent = await factory(file); + if (agent && !seen.has(agent.manifest.name)) { + agents.push(agent); + seen.add(agent.manifest.name); + logDebug(`Discovered agent: ${agent.manifest.name} from ${file}`); + break; + } + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + logWarn(`Factory error on ${file}: ${message}`); + } + } + } + } + + logInfo(`Scan complete: ${agents.length} agent(s) found`); + return agents; +}; diff --git a/src/e2e/daemon-lifecycle.test.ts b/src/e2e/daemon-lifecycle.test.ts new file mode 100644 index 0000000..30d7d64 --- /dev/null +++ b/src/e2e/daemon-lifecycle.test.ts @@ -0,0 +1,432 @@ +/** + * E2E tests for the full daemon + discovery + execution pipeline. + * + * These tests boot a real daemon server in-process, create real .agent.md files + * on disk, and test the complete flow through REST API and WebSocket. + * + * Maps to Phase 2 exit criteria scenarios 1-9. + */ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { randomUUID } from 'node:crypto'; +import { WebSocket } from 'ws'; +import { type Run } from '@agentage/core'; +import { type DaemonServer } from '../daemon/server.js'; + +// --- Test environment setup --- + +const testDir = join(tmpdir(), `agentage-e2e-${Date.now()}`); +const agentsDir = join(testDir, 'agents'); +const skillsDir = join(testDir, 'skills'); +const port = 17000 + Math.floor(Math.random() * 1000); + +let server: DaemonServer; + +// Helper: write a config file pointing to our test dirs +const writeTestConfig = (): void => { + writeFileSync( + join(testDir, 'config.json'), + JSON.stringify({ + machine: { id: randomUUID(), name: 'e2e-test-machine' }, + daemon: { port }, + discovery: { dirs: [agentsDir, skillsDir] }, + sync: { + events: { + state: true, + result: true, + error: true, + input_required: true, + 'output.llm.delta': true, + 'output.llm.tool_call': true, + 'output.llm.usage': true, + 'output.progress': true, + }, + }, + }) + ); +}; + +// Helper: write a markdown agent to disk +const writeMarkdownAgent = (name: string, description: string, body: string): string => { + const filePath = join(agentsDir, `${name}.agent.md`); + writeFileSync(filePath, `---\nname: ${name}\ndescription: ${description}\n---\n${body}`); + return filePath; +}; + +// Helper: fetch with base URL +const api = { + get: async (path: string): Promise<{ status: number; body: T }> => { + const res = await fetch(`http://localhost:${port}${path}`); + const body = (await res.json()) as T; + return { status: res.status, body }; + }, + post: async (path: string, data?: unknown): Promise<{ status: number; body: T }> => { + const res = await fetch(`http://localhost:${port}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: data ? JSON.stringify(data) : undefined, + }); + const body = (await res.json()) as T; + return { status: res.status, body }; + }, +}; + +// Helper: open WebSocket, subscribe to a runId, THEN start the run, collect messages until predicate. +// This avoids the race condition where the run completes before WS subscribes. +const runAndCollectWs = async ( + agentName: string, + task: string, + until: (msg: Record) => boolean, + timeoutMs = 5000 +): Promise<{ runId: string; messages: Array> }> => { + // 1. Connect WebSocket + const ws = new WebSocket(`ws://localhost:${port}/ws`); + await new Promise((resolve, reject) => { + ws.on('open', resolve); + ws.on('error', reject); + }); + + // 2. Start the run via REST + const { body } = await api.post<{ runId: string }>(`/api/agents/${agentName}/run`, { task }); + const runId = body.runId; + + // 3. Subscribe THEN collect + ws.send(JSON.stringify({ type: 'subscribe', runId })); + + const messages: Array> = []; + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + ws.close(); + reject(new Error(`WebSocket timeout after ${timeoutMs}ms. Got ${messages.length} messages.`)); + }, timeoutMs); + + ws.on('message', (raw) => { + const msg = JSON.parse(String(raw)) as Record; + messages.push(msg); + if (until(msg)) { + clearTimeout(timer); + ws.close(); + resolve({ runId, messages }); + } + }); + + ws.on('error', (err) => { + clearTimeout(timer); + reject(err); + }); + }); +}; + +// --- Setup / Teardown --- + +describe('E2E: daemon lifecycle', () => { + beforeAll(async () => { + // Create test directories + mkdirSync(agentsDir, { recursive: true }); + mkdirSync(skillsDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + + writeTestConfig(); + + // Write test agents to disk BEFORE starting the daemon + writeMarkdownAgent('hello', 'A simple greeting agent', 'You are a friendly greeting agent.'); + writeMarkdownAgent( + 'echo', + 'Echoes input back', + 'You echo the user input back to them verbatim.' + ); + + // Boot full daemon: server + discovery + factories + const { createDaemonServer } = await import('../daemon/server.js'); + const { createMarkdownFactory } = await import('../discovery/markdown-factory.js'); + const { createCodeFactory } = await import('../discovery/code-factory.js'); + const { scanAgents } = await import('../discovery/scanner.js'); + + server = createDaemonServer(); + const factories = [createMarkdownFactory(), createCodeFactory()]; + server.setFactories(factories); + + // Run initial discovery (same as daemon-entry.ts) + const agents = await scanAgents([agentsDir, skillsDir], factories); + server.updateAgents(agents); + + await server.start(); + }); + + afterAll(async () => { + await server.stop(); + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + // --- Scenario 1: Config created, daemon running --- + + it('Scenario 1: config.json exists after startup', () => { + expect(existsSync(join(testDir, 'config.json'))).toBe(true); + const config = JSON.parse(readFileSync(join(testDir, 'config.json'), 'utf-8')); + expect(config.machine.id).toBeDefined(); + expect(config.daemon.port).toBe(port); + expect(config.discovery.dirs).toContain(agentsDir); + }); + + it('Scenario 1: daemon is running and healthy', async () => { + const { status, body } = await api.get>('/api/health'); + expect(status).toBe(200); + expect(body.status).toBe('ok'); + expect(body.version).toBe('0.2.0'); + expect(typeof body.uptime).toBe('number'); + expect(body.machineId).toBeDefined(); + expect(body.hubConnected).toBe(false); + }); + + // --- Scenario 2: Agent discovery from .agent.md --- + + it('Scenario 2: discovers agents from .agent.md files', async () => { + const { body } = await api.get>>('/api/agents'); + expect(body.length).toBe(2); + + const names = body.map((a) => a.name); + expect(names).toContain('hello'); + expect(names).toContain('echo'); + }); + + it('Scenario 2: agent manifest has correct fields', async () => { + const { body } = await api.get>>('/api/agents'); + const hello = body.find((a) => a.name === 'hello'); + expect(hello).toBeDefined(); + expect(hello!.description).toBe('A simple greeting agent'); + expect(hello!.path).toContain('hello.agent.md'); + }); + + it('Scenario 2: agents --json returns valid JSON', async () => { + const { status, body } = await api.get('/api/agents'); + expect(status).toBe(200); + expect(Array.isArray(body)).toBe(true); + expect(body.length).toBeGreaterThan(0); + }); + + // --- Scenario 3: Run an agent, receive streamed output --- + + it('Scenario 3: run agent via REST, get runId back', async () => { + const { status, body } = await api.post<{ runId: string }>('/api/agents/hello/run', { + task: 'say hi', + }); + expect(status).toBe(200); + expect(body.runId).toBeDefined(); + expect(typeof body.runId).toBe('string'); + expect(body.runId.length).toBeGreaterThan(0); + }); + + it('Scenario 3: run completes and appears in runs list', async () => { + const { body: runResult } = await api.post<{ runId: string }>('/api/agents/hello/run', { + task: 'greet me', + }); + + // Wait for run to complete + await new Promise((r) => setTimeout(r, 200)); + + const { body: runs } = await api.get('/api/runs'); + const run = runs.find((r) => r.id === runResult.runId); + expect(run).toBeDefined(); + expect(run!.agentName).toBe('hello'); + expect(run!.input).toBe('greet me'); + expect(run!.state).toBe('completed'); + expect(run!.startedAt).toBeDefined(); + expect(run!.endedAt).toBeDefined(); + }); + + it('Scenario 3: WebSocket streams output events during a run', async () => { + const isTerminal = (msg: Record): boolean => + msg.type === 'run_state' && + typeof msg.run === 'object' && + msg.run !== null && + ['completed', 'failed', 'canceled'].includes( + (msg.run as Record).state as string + ); + + const { messages } = await runAndCollectWs('echo', 'hello world', isTerminal); + + // Should have received run_event messages with output + const runEvents = messages.filter((m) => m.type === 'run_event'); + expect(runEvents.length).toBeGreaterThan(0); + + // At least one output event should contain the system prompt + const outputEvents = runEvents.filter((m) => { + const event = m.event as Record; + const data = event.data as Record; + return data.type === 'output'; + }); + expect(outputEvents.length).toBeGreaterThan(0); + + // Should have a run_state with completed + const stateMessages = messages.filter((m) => m.type === 'run_state'); + expect(stateMessages.length).toBeGreaterThan(0); + const finalState = stateMessages.at(-1)!; + expect((finalState.run as Record).state).toBe('completed'); + }); + + it('Scenario 3: WebSocket output includes task text', async () => { + const isTerminal = (msg: Record): boolean => + msg.type === 'run_state' && + typeof msg.run === 'object' && + msg.run !== null && + ['completed', 'failed', 'canceled'].includes( + (msg.run as Record).state as string + ); + + const { messages } = await runAndCollectWs('hello', 'tell me a joke', isTerminal); + + // Check that the task text appears in streamed output + const allContent = messages + .filter((m) => m.type === 'run_event') + .map((m) => { + const event = m.event as Record; + const data = event.data as Record; + return String(data.content ?? ''); + }) + .join(' '); + + expect(allContent).toContain('tell me a joke'); + }); + + // --- Scenario 5: Detached run (just verify run persists) --- + + it('Scenario 5: run persists after creation (detach mode equivalent)', async () => { + const { body } = await api.post<{ runId: string }>('/api/agents/hello/run', { + task: 'background task', + }); + const runId = body.runId; + + // Immediately query the run by ID โ€” it should exist + const { status, body: run } = await api.get(`/api/runs/${runId}`); + expect(status).toBe(200); + expect(run.id).toBe(runId); + expect(run.agentName).toBe('hello'); + }); + + // --- Scenario 7: Agent refresh discovers new agents --- + + it('Scenario 7: refresh discovers newly added agent', async () => { + // Initially we have 2 agents + const before = await api.get('/api/agents'); + const countBefore = before.body.length; + + // Write a new agent to disk + writeMarkdownAgent('newcomer', 'Just arrived', 'I am new here.'); + + // Trigger refresh + const { status, body } = await api.post>>('/api/agents/refresh'); + expect(status).toBe(200); + expect(body.length).toBe(countBefore + 1); + + const names = body.map((a) => a.name); + expect(names).toContain('newcomer'); + }); + + // --- Scenario 8: REST API contract --- + + it('Scenario 8: GET /api/health has correct schema', async () => { + const { body } = await api.get>('/api/health'); + expect(body).toHaveProperty('status'); + expect(body).toHaveProperty('version'); + expect(body).toHaveProperty('uptime'); + expect(body).toHaveProperty('machineId'); + expect(body).toHaveProperty('hubConnected'); + }); + + it('Scenario 8: GET /api/agents returns AgentManifest[]', async () => { + const { body } = await api.get>>('/api/agents'); + for (const agent of body) { + expect(agent).toHaveProperty('name'); + expect(agent).toHaveProperty('path'); + expect(typeof agent.name).toBe('string'); + expect(typeof agent.path).toBe('string'); + } + }); + + it('Scenario 8: POST /api/agents/:name/run returns { runId }', async () => { + const { status, body } = await api.post>('/api/agents/hello/run', { + task: 'api test', + }); + expect(status).toBe(200); + expect(body).toHaveProperty('runId'); + expect(typeof body.runId).toBe('string'); + }); + + it('Scenario 8: GET /api/runs returns Run[]', async () => { + await new Promise((r) => setTimeout(r, 100)); + const { body } = await api.get('/api/runs'); + expect(Array.isArray(body)).toBe(true); + for (const run of body) { + expect(run).toHaveProperty('id'); + expect(run).toHaveProperty('agentName'); + expect(run).toHaveProperty('state'); + expect(run).toHaveProperty('createdAt'); + } + }); + + it('Scenario 8: GET /api/runs/:id returns correct run', async () => { + const { body: created } = await api.post<{ runId: string }>('/api/agents/hello/run', { + task: 'single run test', + }); + await new Promise((r) => setTimeout(r, 100)); + + const { status, body: run } = await api.get(`/api/runs/${created.runId}`); + expect(status).toBe(200); + expect(run.id).toBe(created.runId); + expect(run.agentName).toBe('hello'); + expect(run.input).toBe('single run test'); + }); + + // --- Scenario 9: WebSocket protocol --- + + it('Scenario 9: WebSocket subscribe/receive/complete flow', async () => { + const isCompleted = (msg: Record): boolean => + msg.type === 'run_state' && + typeof msg.run === 'object' && + msg.run !== null && + (msg.run as Record).state === 'completed'; + + const { runId, messages } = await runAndCollectWs('hello', 'ws flow test', isCompleted); + + // Must have both event types + const types = new Set(messages.map((m) => m.type)); + expect(types.has('run_event')).toBe(true); + expect(types.has('run_state')).toBe(true); + + // run_event messages must have runId and event + for (const msg of messages.filter((m) => m.type === 'run_event')) { + expect(msg.runId).toBe(runId); + expect(msg.event).toBeDefined(); + } + + // run_state messages must have run object with state + for (const msg of messages.filter((m) => m.type === 'run_state')) { + const run = msg.run as Record; + expect(run.id).toBe(runId); + expect(run.state).toBeDefined(); + } + }); + + // --- Error cases --- + + it('returns 404 for unknown agent', async () => { + const { status } = await api.post('/api/agents/nonexistent/run', { + task: 'nope', + }); + expect(status).toBe(404); + }); + + it('returns 400 for run without task', async () => { + const { status } = await api.post('/api/agents/hello/run', {}); + expect(status).toBe(400); + }); + + it('returns 404 for unknown run ID', async () => { + const { status } = await api.get('/api/runs/nonexistent-id'); + expect(status).toBe(404); + }); +}); diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index 31df362..0000000 --- a/src/index.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; - -// Read package.json directly in test since index.ts uses ESM-specific import.meta -const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { - version: string; -}; - -// Mock the index module to avoid import.meta issues in Jest -jest.mock('./index.js', () => ({ - version: packageJson.version, - greet: (): string => 'Hello from AgentKit CLI!', -})); - -import { greet, version } from './index.js'; - -describe('CLI Package', () => { - test('version matches package.json', () => { - expect(version).toBe(packageJson.version); - }); - - test('greet returns correct message', () => { - expect(greet()).toBe('Hello from AgentKit CLI!'); - }); -}); diff --git a/src/index.ts b/src/index.ts index c2810e5..3461d4d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,2 @@ -import { readFileSync } from 'fs'; -import { dirname, join } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const packageJsonPath = join(__dirname, '..', 'package.json'); -const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as { - version: string; -}; - -export const version: string = packageJson.version; - -export const greet = (): string => 'Hello from AgentKit CLI!'; +export { loadConfig, saveConfig, getConfigDir } from './daemon/config.js'; +export type { DaemonConfig, SyncEvents } from './daemon/config.js'; diff --git a/src/schemas/agent.schema.test.ts b/src/schemas/agent.schema.test.ts deleted file mode 100644 index 942e595..0000000 --- a/src/schemas/agent.schema.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { agentYamlSchema, type AgentYaml } from './agent.schema.js'; - -describe('agentYamlSchema', () => { - test('validates correct agent configuration', () => { - const validConfig = { - name: 'test-agent', - model: 'gpt-4', - instructions: 'You are helpful', - tools: ['tool1', 'tool2'], - variables: { key1: 'value1', key2: 'value2' }, - }; - - const result = agentYamlSchema.parse(validConfig); - expect(result).toEqual(validConfig); - }); - - test('applies default values', () => { - const minimalConfig = { - name: 'test-agent', - instructions: 'You are helpful', - }; - - const result = agentYamlSchema.parse(minimalConfig); - expect(result.model).toBe('gpt-4'); - expect(result.tools).toEqual([]); - expect(result.variables).toEqual({}); - }); - - test('rejects empty name', () => { - const invalidConfig = { - name: '', - instructions: 'You are helpful', - }; - - expect(() => agentYamlSchema.parse(invalidConfig)).toThrow(); - }); - - test('rejects missing name', () => { - const invalidConfig = { - instructions: 'You are helpful', - }; - - expect(() => agentYamlSchema.parse(invalidConfig)).toThrow(); - }); - - test('rejects empty instructions', () => { - const invalidConfig = { - name: 'test-agent', - instructions: '', - }; - - expect(() => agentYamlSchema.parse(invalidConfig)).toThrow(); - }); - - test('rejects missing instructions', () => { - const invalidConfig = { - name: 'test-agent', - }; - - expect(() => agentYamlSchema.parse(invalidConfig)).toThrow(); - }); - - test('accepts optional tools array', () => { - const config = { - name: 'test-agent', - instructions: 'You are helpful', - tools: ['tool1'], - }; - - const result = agentYamlSchema.parse(config); - expect(result.tools).toEqual(['tool1']); - }); - - test('accepts optional variables object', () => { - const config = { - name: 'test-agent', - instructions: 'You are helpful', - variables: { foo: 'bar' }, - }; - - const result = agentYamlSchema.parse(config); - expect(result.variables).toEqual({ foo: 'bar' }); - }); - - test('type inference works correctly', () => { - const config: AgentYaml = { - name: 'test-agent', - model: 'gpt-4', - instructions: 'You are helpful', - tools: [], - variables: {}, - }; - - expect(config.name).toBe('test-agent'); - expect(config.model).toBe('gpt-4'); - }); -}); diff --git a/src/schemas/agent.schema.ts b/src/schemas/agent.schema.ts deleted file mode 100644 index c80c5cd..0000000 --- a/src/schemas/agent.schema.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from 'zod'; - -export const agentYamlSchema = z.object({ - name: z.string().min(1), - model: z.string().default('gpt-4'), - instructions: z.string().min(1), - tools: z.array(z.string()).optional().default([]), - variables: z.record(z.string(), z.string()).optional().default({}), -}); - -export type AgentYaml = z.infer; diff --git a/src/services/auth.service.test.ts b/src/services/auth.service.test.ts deleted file mode 100644 index 52cc082..0000000 --- a/src/services/auth.service.test.ts +++ /dev/null @@ -1,288 +0,0 @@ -import * as configUtils from '../utils/config.js'; -import { AuthError, getMe, logout, pollForToken, requestDeviceCode } from './auth.service.js'; - -// Mock the config utils -jest.mock('../utils/config.js'); - -const mockGetRegistryUrl = configUtils.getRegistryUrl as jest.MockedFunction< - typeof configUtils.getRegistryUrl ->; -const mockGetAuthToken = configUtils.getAuthToken as jest.MockedFunction< - typeof configUtils.getAuthToken ->; -const mockGetDeviceId = configUtils.getDeviceId as jest.MockedFunction< - typeof configUtils.getDeviceId ->; - -// Mock fetch -const mockFetch = jest.fn(); -global.fetch = mockFetch; - -describe('auth.service', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockGetRegistryUrl.mockResolvedValue('https://dev.agentage.io'); - mockGetDeviceId.mockResolvedValue('test-device-id-12345678'); - }); - - describe('requestDeviceCode', () => { - it('returns device code response on success', async () => { - const deviceCodeResponse = { - device_code: 'abc123', - user_code: 'ABCD-1234', - verification_uri: 'https://dev.agentage.io/device', - expires_in: 900, - interval: 5, - }; - - mockFetch.mockResolvedValue({ - ok: true, - json: () => Promise.resolve(deviceCodeResponse), - }); - - const result = await requestDeviceCode(); - - expect(result).toEqual(deviceCodeResponse); - expect(mockFetch).toHaveBeenCalledWith('https://dev.agentage.io/api/auth/device/code', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ device_id: 'test-device-id-12345678' }), - }); - }); - - it('throws AuthError on failure', async () => { - mockFetch.mockResolvedValue({ - ok: false, - json: () => - Promise.resolve({ - error: 'server_error', - error_description: 'Something went wrong', - }), - }); - - await expect(requestDeviceCode()).rejects.toThrow(AuthError); - await expect(requestDeviceCode()).rejects.toThrow('Something went wrong'); - }); - }); - - describe('pollForToken', () => { - it('returns token on successful authentication', async () => { - const tokenResponse = { - access_token: 'token123', - token_type: 'Bearer', - user: { id: '1', email: 'test@example.com' }, - }; - - mockFetch.mockResolvedValue({ - ok: true, - json: () => Promise.resolve(tokenResponse), - }); - - // Use very short interval for testing (0.01 seconds) - const result = await pollForToken('device123', 0.01, 60); - - expect(result).toEqual(tokenResponse); - expect(mockFetch).toHaveBeenCalledWith( - 'https://dev.agentage.io/api/auth/device/token', - expect.objectContaining({ - method: 'POST', - body: JSON.stringify({ device_code: 'device123' }), - }) - ); - }); - - it('throws on access_denied', async () => { - mockFetch.mockResolvedValue({ - ok: false, - json: () => Promise.resolve({ error: 'access_denied' }), - }); - - await expect(pollForToken('device123', 0.01, 60)).rejects.toThrow('Authorization was denied'); - }); - - it('throws on expired_token', async () => { - mockFetch.mockResolvedValue({ - ok: false, - json: () => Promise.resolve({ error: 'expired_token' }), - }); - - await expect(pollForToken('device123', 0.01, 60)).rejects.toThrow('Login timed out'); - }); - - it('continues polling on authorization_pending then succeeds', async () => { - const tokenResponse = { - access_token: 'token123', - token_type: 'Bearer', - }; - - mockFetch - .mockResolvedValueOnce({ - ok: false, - json: () => Promise.resolve({ error: 'authorization_pending' }), - }) - .mockResolvedValueOnce({ - ok: true, - json: () => Promise.resolve(tokenResponse), - }); - - const result = await pollForToken('device123', 0.01, 60); - - expect(result).toEqual(tokenResponse); - expect(mockFetch).toHaveBeenCalledTimes(2); - }); - - it('slows down polling on slow_down error', async () => { - const tokenResponse = { - access_token: 'token123', - token_type: 'Bearer', - }; - - mockFetch - .mockResolvedValueOnce({ - ok: false, - json: () => Promise.resolve({ error: 'slow_down' }), - }) - .mockResolvedValueOnce({ - ok: true, - json: () => Promise.resolve(tokenResponse), - }); - - // Note: slow_down adds 5 seconds, but with 0.01 initial interval - // it should still complete reasonably fast for testing - const result = await pollForToken('device123', 0.001, 60); - - expect(result).toEqual(tokenResponse); - expect(mockFetch).toHaveBeenCalledTimes(2); - }, 10000); // Increase timeout for this test - - it('throws on unknown error with description', async () => { - mockFetch.mockReset(); - mockFetch.mockResolvedValue({ - ok: false, - json: () => - Promise.resolve({ - error: 'unknown_error', - error_description: 'Something unexpected happened', - }), - }); - - await expect(pollForToken('device123', 0.01, 60)).rejects.toThrow( - 'Something unexpected happened' - ); - }); - - it('throws on unknown error without description', async () => { - mockFetch.mockReset(); - mockFetch.mockResolvedValue({ - ok: false, - json: () => Promise.resolve({ error: 'some_error' }), - }); - - await expect(pollForToken('device123', 0.01, 60)).rejects.toThrow('Authentication failed'); - }); - }); - - describe('getMe', () => { - beforeEach(() => { - mockFetch.mockReset(); - }); - - it('returns user on success', async () => { - const user = { id: '1', email: 'test@example.com', name: 'Test User' }; - mockGetAuthToken.mockResolvedValue('token123'); - mockFetch.mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ user }), - }); - - const result = await getMe(); - - expect(result).toEqual(user); - expect(mockFetch).toHaveBeenCalledWith('https://dev.agentage.io/api/auth/me', { - headers: { Authorization: 'Bearer token123' }, - }); - }); - - it('throws when not authenticated', async () => { - mockGetAuthToken.mockResolvedValue(undefined); - - await expect(getMe()).rejects.toThrow('Not authenticated'); - }); - - it('throws on session expired (401)', async () => { - mockGetAuthToken.mockResolvedValue('expired-token'); - mockFetch.mockResolvedValue({ - ok: false, - status: 401, - json: () => Promise.resolve({}), - }); - - await expect(getMe()).rejects.toThrow('Session expired'); - }); - - it('throws on other API errors with description', async () => { - mockGetAuthToken.mockResolvedValue('token123'); - mockFetch.mockResolvedValue({ - ok: false, - status: 500, - json: () => - Promise.resolve({ - error: 'server_error', - error_description: 'Internal server error', - }), - }); - - await expect(getMe()).rejects.toThrow('Internal server error'); - }); - - it('throws on other API errors without description', async () => { - mockGetAuthToken.mockResolvedValue('token123'); - mockFetch.mockResolvedValue({ - ok: false, - status: 500, - json: () => Promise.resolve({ error: 'server_error' }), - }); - - await expect(getMe()).rejects.toThrow('Failed to get user info'); - }); - }); - - describe('logout', () => { - it('calls logout endpoint when authenticated', async () => { - mockGetAuthToken.mockResolvedValue('token123'); - mockFetch.mockResolvedValue({ ok: true }); - - await logout(); - - expect(mockFetch).toHaveBeenCalledWith('https://dev.agentage.io/api/auth/logout', { - method: 'POST', - headers: { Authorization: 'Bearer token123' }, - }); - }); - - it('does nothing when not authenticated', async () => { - mockGetAuthToken.mockResolvedValue(undefined); - - await logout(); - - expect(mockFetch).not.toHaveBeenCalled(); - }); - - it('ignores fetch errors', async () => { - mockGetAuthToken.mockResolvedValue('token123'); - mockFetch.mockRejectedValue(new Error('Network error')); - - await expect(logout()).resolves.not.toThrow(); - }); - }); - - describe('AuthError', () => { - it('has correct name and code', () => { - const error = new AuthError('Test message', 'test_code'); - - expect(error.name).toBe('AuthError'); - expect(error.message).toBe('Test message'); - expect(error.code).toBe('test_code'); - }); - }); -}); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts deleted file mode 100644 index fe6a4bd..0000000 --- a/src/services/auth.service.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - AuthErrorResponse, - DeviceCodeResponse, - TokenResponse, - User, -} from '../types/config.types.js'; -import { getAuthToken, getDeviceId, getRegistryUrl } from '../utils/config.js'; - -/** - * Auth error class - */ -export class AuthError extends Error { - constructor( - message: string, - public code: string - ) { - super(message); - this.name = 'AuthError'; - } -} - -/** - * Request a device code for authentication - */ -export const requestDeviceCode = async (): Promise => { - const registryUrl = await getRegistryUrl(); - const deviceId = await getDeviceId(); - - const response = await fetch(`${registryUrl}/api/auth/device/code`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ device_id: deviceId }), - }); - - if (!response.ok) { - const error = (await response.json()) as AuthErrorResponse; - throw new AuthError( - error.error_description || 'Failed to request device code', - error.error || 'request_failed' - ); - } - - return response.json() as Promise; -}; - -/** - * Poll for token after user completes authentication - */ -export const pollForToken = async ( - deviceCode: string, - interval: number, - expiresIn: number -): Promise => { - const registryUrl = await getRegistryUrl(); - const startTime = Date.now(); - const expiryTime = startTime + expiresIn * 1000; - let currentInterval = interval; - - while (Date.now() < expiryTime) { - // Wait for the specified interval - await sleep(currentInterval * 1000); - - const response = await fetch(`${registryUrl}/api/auth/device/token`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ device_code: deviceCode }), - }); - - if (response.ok) { - return response.json() as Promise; - } - - const error = (await response.json()) as AuthErrorResponse; - - switch (error.error) { - case 'authorization_pending': - // User hasn't completed auth yet, continue polling - continue; - case 'slow_down': - // Increase polling interval - currentInterval += 5; - continue; - case 'expired_token': - throw new AuthError('Login timed out. Please try again.', error.error); - case 'access_denied': - throw new AuthError('Authorization was denied.', error.error); - default: - throw new AuthError( - error.error_description || 'Authentication failed', - error.error || 'unknown_error' - ); - } - } - - throw new AuthError('Login timed out. Please try again.', 'expired_token'); -}; - -/** - * API response for /api/auth/me endpoint - */ -interface MeResponse { - user: User; -} - -/** - * Get the current authenticated user - */ -export const getMe = async (): Promise => { - const registryUrl = await getRegistryUrl(); - const token = await getAuthToken(); - - if (!token) { - throw new AuthError('Not authenticated', 'not_authenticated'); - } - - const response = await fetch(`${registryUrl}/api/auth/me`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (!response.ok) { - if (response.status === 401) { - throw new AuthError('Session expired. Please login again.', 'session_expired'); - } - const error = (await response.json()) as AuthErrorResponse; - throw new AuthError( - error.error_description || 'Failed to get user info', - error.error || 'request_failed' - ); - } - - const data = (await response.json()) as MeResponse; - return data.user; -}; - -/** - * Invalidate the current session (optional server-side logout) - */ -export const logout = async (): Promise => { - const registryUrl = await getRegistryUrl(); - const token = await getAuthToken(); - - if (!token) { - return; // Already logged out - } - - try { - await fetch(`${registryUrl}/api/auth/logout`, { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - }, - }); - } catch { - // Ignore errors - local logout will still work - } -}; - -/** - * Sleep utility - */ -const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/services/registry.service.test.ts b/src/services/registry.service.test.ts deleted file mode 100644 index 13866b9..0000000 --- a/src/services/registry.service.test.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { RegistryApiError } from './registry.service.js'; - -// Mock fetch globally -const mockFetch = jest.fn(); -global.fetch = mockFetch; - -// Mock config utilities -jest.mock('../utils/config.js', () => ({ - getAuthToken: jest.fn().mockResolvedValue('test-token'), - getRegistryUrl: jest.fn().mockResolvedValue('https://dev.agentage.io'), -})); - -describe('Registry Service', () => { - beforeEach(() => { - mockFetch.mockReset(); - }); - - describe('RegistryApiError', () => { - test('creates error with all properties', () => { - const error = new RegistryApiError('Test error', 'test_error', 400, { - field: 'value', - }); - - expect(error.message).toBe('Test error'); - expect(error.code).toBe('test_error'); - expect(error.statusCode).toBe(400); - expect(error.details).toEqual({ field: 'value' }); - expect(error.name).toBe('RegistryApiError'); - }); - }); - - describe('publishAgent', () => { - test('publishes agent successfully', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - agent: { - name: 'test-agent', - owner: 'testuser', - version: '2025-11-30', - visibility: 'public', - publishedAt: '2025-11-30T12:00:00Z', - }, - }), - }); - - const { publishAgent } = await import('./registry.service.js'); - - const result = await publishAgent({ - name: 'test-agent', - visibility: 'public', - version: '2025-11-30', - content: '---\nname: test-agent\n---\nContent', - }); - - expect(result.name).toBe('test-agent'); - expect(result.owner).toBe('testuser'); - expect(mockFetch).toHaveBeenCalledWith( - 'https://dev.agentage.io/api/agents', - expect.objectContaining({ - method: 'POST', - headers: expect.objectContaining({ - Authorization: 'Bearer test-token', - 'Content-Type': 'application/json', - }), - }) - ); - }); - - test('throws error on failure', async () => { - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 400, - json: () => - Promise.resolve({ - error: 'validation_error', - message: 'Invalid agent name', - }), - }); - - const { publishAgent } = await import('./registry.service.js'); - - await expect( - publishAgent({ - name: 'invalid', - visibility: 'public', - version: '1.0.0', - content: 'test', - }) - ).rejects.toThrow(RegistryApiError); - }); - }); - - describe('getAgent', () => { - test('fetches agent details', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - name: 'test-agent', - owner: 'testuser', - latestVersion: '2025-11-30', - latestContent: 'content', - }, - }), - }); - - const { getAgent } = await import('./registry.service.js'); - - const result = await getAgent('testuser', 'test-agent'); - - expect(result.name).toBe('test-agent'); - expect(mockFetch).toHaveBeenCalledWith( - 'https://dev.agentage.io/api/agents/testuser/test-agent', - expect.any(Object) - ); - }); - }); - - describe('searchAgents', () => { - test('searches with query', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - agents: [{ name: 'test', owner: 'user' }], - total: 1, - page: 1, - limit: 10, - hasMore: false, - }, - }), - }); - - const { searchAgents } = await import('./registry.service.js'); - - const result = await searchAgents('test', 1, 10); - - expect(result.agents).toHaveLength(1); - expect(mockFetch).toHaveBeenCalledWith( - 'https://dev.agentage.io/api/agents/search?q=test&page=1&limit=10', - expect.any(Object) - ); - }); - }); - - describe('getAgentVersion', () => { - test('fetches specific version', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - version: '2025-11-01', - content: 'version content', - downloads: 10, - publishedAt: '2025-11-01T00:00:00Z', - isLatest: false, - }, - }), - }); - - const { getAgentVersion } = await import('./registry.service.js'); - - const result = await getAgentVersion('testuser', 'test-agent', '2025-11-01'); - - expect(result.version).toBe('2025-11-01'); - expect(mockFetch).toHaveBeenCalledWith( - 'https://dev.agentage.io/api/agents/testuser/test-agent/versions/2025-11-01', - expect.any(Object) - ); - }); - }); - - describe('listAgents', () => { - test('lists agents with filters', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - agents: [{ name: 'test', owner: 'user' }], - total: 1, - page: 1, - limit: 10, - hasMore: false, - }, - }), - }); - - const { listAgents } = await import('./registry.service.js'); - - const result = await listAgents({ - page: 1, - limit: 10, - sort: 'downloads', - owner: 'testuser', - tag: 'ai', - }); - - expect(result.agents).toHaveLength(1); - expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('page=1'), expect.any(Object)); - }); - - test('lists agents without filters', async () => { - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - agents: [], - total: 0, - page: 1, - limit: 10, - hasMore: false, - }, - }), - }); - - const { listAgents } = await import('./registry.service.js'); - - const result = await listAgents(); - - expect(result.agents).toHaveLength(0); - }); - }); - - describe('registryFetch error handling', () => { - test('handles missing error message', async () => { - mockFetch.mockResolvedValueOnce({ - ok: false, - status: 500, - json: () => Promise.resolve({}), - }); - - const { publishAgent } = await import('./registry.service.js'); - - await expect( - publishAgent({ - name: 'test', - visibility: 'public', - version: '1.0.0', - content: 'test', - }) - ).rejects.toThrow('Request failed'); - }); - - test('works without auth token', async () => { - const { getAuthToken } = require('../utils/config.js'); - getAuthToken.mockResolvedValueOnce(null); - - mockFetch.mockResolvedValueOnce({ - ok: true, - json: () => - Promise.resolve({ - success: true, - data: { - agents: [], - total: 0, - page: 1, - limit: 10, - hasMore: false, - }, - }), - }); - - const { searchAgents } = await import('./registry.service.js'); - - await searchAgents('test'); - - expect(mockFetch).toHaveBeenCalledWith( - expect.any(String), - expect.objectContaining({ - headers: expect.not.objectContaining({ - Authorization: expect.any(String), - }), - }) - ); - }); - }); -}); diff --git a/src/services/registry.service.ts b/src/services/registry.service.ts deleted file mode 100644 index 945328e..0000000 --- a/src/services/registry.service.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { - AgentDetail, - AgentVersionInfo, - ListFilters, - PublishRequest, - PublishResponse, - RegistryError, - SearchResult, -} from '../types/registry.types.js'; -import { getAuthToken, getRegistryUrl } from '../utils/config.js'; - -/** - * Registry API Error - */ -export class RegistryApiError extends Error { - constructor( - message: string, - public code: string, - public statusCode: number, - public details?: Record - ) { - super(message); - this.name = 'RegistryApiError'; - } -} - -/** - * Make authenticated request to registry API - */ -const registryFetch = async (path: string, options: RequestInit = {}): Promise => { - const registryUrl = await getRegistryUrl(); - const token = await getAuthToken(); - - const headers: Record = { - 'Content-Type': 'application/json', - ...(options.headers as Record), - }; - - if (token) { - headers['Authorization'] = `Bearer ${token}`; - } - - const response = await fetch(`${registryUrl}${path}`, { - ...options, - headers, - }); - - const data = await response.json(); - - if (!response.ok) { - const error = data as RegistryError; - throw new RegistryApiError( - error.message || 'Request failed', - error.error || 'request_failed', - response.status, - error.details - ); - } - - return data as T; -}; - -/** - * Publish an agent to the registry - */ -export const publishAgent = async (data: PublishRequest): Promise => { - const response = await registryFetch<{ - success: boolean; - agent: PublishResponse; - }>('/api/agents', { - method: 'POST', - body: JSON.stringify(data), - }); - return response.agent; -}; - -/** - * Get agent details from registry - */ -export const getAgent = async (owner: string, name: string): Promise => { - const response = await registryFetch<{ success: boolean; data: AgentDetail }>( - `/api/agents/${encodeURIComponent(owner)}/${encodeURIComponent(name)}` - ); - return response.data; -}; - -/** - * Get specific version of an agent - */ -export const getAgentVersion = async ( - owner: string, - name: string, - version: string -): Promise => { - const response = await registryFetch<{ - success: boolean; - data: AgentVersionInfo; - }>( - `/api/agents/${encodeURIComponent(owner)}/${encodeURIComponent( - name - )}/versions/${encodeURIComponent(version)}` - ); - return response.data; -}; - -/** - * Search for agents in registry - */ -export const searchAgents = async (query: string, page = 1, limit = 10): Promise => { - const params = new URLSearchParams({ - q: query, - page: String(page), - limit: String(limit), - }); - - const response = await registryFetch<{ - success: boolean; - data: SearchResult; - }>(`/api/agents/search?${params.toString()}`); - return response.data; -}; - -/** - * List agents from registry with filters - */ -export const listAgents = async (filters: ListFilters = {}): Promise => { - const params = new URLSearchParams(); - - if (filters.page) params.set('page', String(filters.page)); - if (filters.limit) params.set('limit', String(filters.limit)); - if (filters.sort) params.set('sort', filters.sort); - if (filters.owner) params.set('owner', filters.owner); - if (filters.tag) params.set('tag', filters.tag); - - const response = await registryFetch<{ - success: boolean; - data: SearchResult; - }>(`/api/agents?${params.toString()}`); - return response.data; -}; diff --git a/src/types/config.types.ts b/src/types/config.types.ts deleted file mode 100644 index 92af17e..0000000 --- a/src/types/config.types.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { z } from 'zod'; - -/** - * User information schema - */ -export const userSchema = z.object({ - id: z.string(), - email: z.string(), - name: z.string().optional(), - avatar: z.string().optional(), - verifiedAlias: z.string().optional(), -}); - -/** - * Auth config schema (auth.json) - */ -export const authFileSchema = z.object({ - token: z.string().optional(), - expiresAt: z.string().optional(), - user: userSchema.optional(), -}); - -/** - * Registry configuration schema - */ -export const registryConfigSchema = z.object({ - url: z.url().default('https://dev.agentage.io'), -}); - -/** - * App config schema (config.json) โ€” NO tokens - */ -export const appConfigSchema = z.object({ - registry: registryConfigSchema.optional(), - deviceId: z.string().optional(), -}); - -/** - * User information from the API - */ -export type User = z.infer; - -/** - * Auth config (auth.json) - */ -export type AuthFileConfig = z.infer; - -/** - * Registry configuration - */ -export type RegistryConfig = z.infer; - -/** - * App configuration (config.json โ€” no tokens) - */ -export type AppConfig = z.infer; - -/** - * Device code response from the auth API - */ -export interface DeviceCodeResponse { - device_code: string; - user_code: string; - verification_uri: string; - expires_in: number; - interval: number; -} - -/** - * Token response from the auth API - */ -export interface TokenResponse { - access_token: string; - token_type: string; - expires_in?: number; - user?: User; -} - -/** - * Auth error response - */ -export interface AuthErrorResponse { - error: string; - error_description?: string; -} diff --git a/src/types/lockfile.types.ts b/src/types/lockfile.types.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/types/registry.types.ts b/src/types/registry.types.ts deleted file mode 100644 index c04f8a2..0000000 --- a/src/types/registry.types.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Registry API Types - */ - -export interface PublishRequest { - name: string; - description?: string; - visibility: 'public' | 'private'; - version: string; - content: string; - contentType?: 'markdown' | 'plain'; - tags?: string[]; - changelog?: string; -} - -export interface PublishResponse { - name: string; - owner: string; - version: string; - visibility: 'public' | 'private'; - publishedAt: string; -} - -export interface AgentSummary { - name: string; - owner: string; - description?: string; - visibility: 'public' | 'private'; - tags: string[]; - latestVersion: string; - totalDownloads: number; -} - -export interface AgentDetail { - name: string; - owner: string; - description?: string; - visibility: 'public' | 'private'; - tags: string[]; - latestVersion: string; - latestContent: string; - contentType: 'markdown' | 'plain'; - totalDownloads: number; - versions: AgentVersionInfo[]; -} - -export interface AgentVersionInfo { - version: string; - content?: string; - downloads: number; - publishedAt: string; - isLatest: boolean; -} - -export interface SearchResult { - agents: AgentSummary[]; - total: number; - page: number; - limit: number; - hasMore: boolean; -} - -export interface ListFilters { - page?: number; - limit?: number; - sort?: 'downloads' | 'newest' | 'name'; - owner?: string; - tag?: string; -} - -export interface RegistryError { - error: string; - message: string; - details?: Record; -} diff --git a/src/utils/agent-parser.test.ts b/src/utils/agent-parser.test.ts deleted file mode 100644 index c9e6604..0000000 --- a/src/utils/agent-parser.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - extractFrontmatter, - generateDateVersion, - isValidAgentName, - parseAgentFile, - parseAgentIdentifier, -} from './agent-parser.js'; - -describe('agent-parser', () => { - describe('extractFrontmatter', () => { - test('extracts valid frontmatter', () => { - const content = `--- -name: test-agent -description: A test agent -version: 2025-11-30 ---- -You are a helpful assistant.`; - - const result = extractFrontmatter(content); - - expect(result.name).toBe('test-agent'); - expect(result.description).toBe('A test agent'); - expect(result.version).toBe('2025-11-30'); - }); - - test('returns empty object for missing frontmatter', () => { - const content = 'Just plain text without frontmatter'; - const result = extractFrontmatter(content); - expect(result).toEqual({}); - }); - - test('returns empty object for invalid YAML', () => { - const content = `--- -invalid: yaml: content: here ---- -Body`; - const result = extractFrontmatter(content); - expect(result).toEqual({}); - }); - }); - - describe('parseAgentFile', () => { - test('parses full agent file', () => { - const content = `--- -name: my-agent -model: gpt-4 ---- -You are a helpful assistant. - -## Instructions -Be kind and helpful.`; - - const result = parseAgentFile(content); - - expect(result.frontmatter.name).toBe('my-agent'); - expect(result.frontmatter.model).toBe('gpt-4'); - expect(result.content).toBe(content); - expect(result.body).toContain('You are a helpful assistant.'); - expect(result.body).toContain('## Instructions'); - }); - - test('handles content without frontmatter', () => { - const content = 'Just plain text'; - const result = parseAgentFile(content); - - expect(result.frontmatter).toEqual({}); - expect(result.body).toBe(content); - }); - }); - - describe('generateDateVersion', () => { - test('generates date in YYYY-MM-DD format', () => { - const version = generateDateVersion(); - expect(version).toMatch(/^\d{4}-\d{2}-\d{2}$/); - }); - }); - - describe('isValidAgentName', () => { - test('accepts valid names', () => { - expect(isValidAgentName('my-agent')).toBe(true); - expect(isValidAgentName('agent123')).toBe(true); - expect(isValidAgentName('a')).toBe(true); - expect(isValidAgentName('test-agent-v2')).toBe(true); - }); - - test('rejects invalid names', () => { - expect(isValidAgentName('My-Agent')).toBe(false); - expect(isValidAgentName('-agent')).toBe(false); - expect(isValidAgentName('agent-')).toBe(false); - expect(isValidAgentName('agent_name')).toBe(false); - expect(isValidAgentName('')).toBe(false); - }); - }); - - describe('parseAgentIdentifier', () => { - test('parses owner/name format', () => { - const result = parseAgentIdentifier('owner/my-agent'); - expect(result).toEqual({ - owner: 'owner', - name: 'my-agent', - version: undefined, - }); - }); - - test('parses owner/name@version format', () => { - const result = parseAgentIdentifier('owner/my-agent@2025-11-30'); - expect(result).toEqual({ - owner: 'owner', - name: 'my-agent', - version: '2025-11-30', - }); - }); - - test('parses name-only format', () => { - const result = parseAgentIdentifier('my-agent'); - expect(result).toEqual({ name: 'my-agent', version: undefined }); - }); - - test('parses name@version format', () => { - const result = parseAgentIdentifier('my-agent@1.0.0'); - expect(result).toEqual({ name: 'my-agent', version: '1.0.0' }); - }); - }); -}); diff --git a/src/utils/agent-parser.ts b/src/utils/agent-parser.ts deleted file mode 100644 index d1efc06..0000000 --- a/src/utils/agent-parser.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { readFile } from 'fs/promises'; -import { parse } from 'yaml'; - -export interface AgentFrontmatter { - name?: string; - description?: string; - version?: string; - tools?: string[]; - handoffs?: string[]; - 'argument-hint'?: string; - 'mcp-servers'?: Record; - [key: string]: unknown; -} - -export interface ParsedAgent { - frontmatter: AgentFrontmatter; - content: string; - body: string; -} - -/** - * Extract frontmatter from .agent.md content - */ -export const extractFrontmatter = (content: string): AgentFrontmatter => { - const match = content.match(/^---\n([\s\S]*?)\n---/); - if (!match) { - return {}; - } - - try { - return parse(match[1]) as AgentFrontmatter; - } catch { - return {}; - } -}; - -/** - * Parse a full agent file into frontmatter and body - */ -export const parseAgentFile = (content: string): ParsedAgent => { - const frontmatter = extractFrontmatter(content); - const bodyMatch = content.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/); - const body = bodyMatch ? bodyMatch[1].trim() : content; - - return { - frontmatter, - content, - body, - }; -}; - -/** - * Read and parse an agent file from disk - */ -export const readAgentFile = async (filePath: string): Promise => { - const content = await readFile(filePath, 'utf-8'); - return parseAgentFile(content); -}; - -/** - * Generate a date-based version string (YYYY-MM-DD) - */ -export const generateDateVersion = (): string => { - const now = new Date(); - return now.toISOString().split('T')[0]; -}; - -/** - * Validate agent name format (lowercase alphanumeric with hyphens) - */ -export const isValidAgentName = (name: string): boolean => - /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(name); - -/** - * Parse agent identifier: owner/name[@version] - */ -export const parseAgentIdentifier = ( - identifier: string -): { owner?: string; name?: string; version?: string } => { - const [fullName, version] = identifier.split('@'); - const parts = fullName.split('/'); - - if (parts.length === 1) { - // Just name without owner - return { name: parts[0], version }; - } - - if (parts.length === 2) { - return { owner: parts[0], name: parts[1], version }; - } - - return {}; -}; diff --git a/src/utils/config.test.ts b/src/utils/config.test.ts deleted file mode 100644 index daa5b5c..0000000 --- a/src/utils/config.test.ts +++ /dev/null @@ -1,394 +0,0 @@ -import { mkdir, readFile, writeFile } from 'fs/promises'; -import { homedir } from 'os'; -import type { AppConfig, AuthFileConfig } from '../types/config.types.js'; -import { - clearAuth, - DEFAULT_REGISTRY_URL, - getAuthPath, - getAuthStatus, - getAuthToken, - getConfigDir, - getConfigPath, - getDeviceId, - getRegistryUrl, - isTokenExpired, - loadAppConfig, - loadAuth, - saveAppConfig, - saveAuth, -} from './config.js'; - -// Mock fs/promises -jest.mock('fs/promises'); -jest.mock('os'); - -const mockMkdir = mkdir as jest.MockedFunction; -const mockReadFile = readFile as jest.MockedFunction; -const mockWriteFile = writeFile as jest.MockedFunction; -const mockHomedir = homedir as jest.MockedFunction; - -describe('config utils', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockHomedir.mockReturnValue('/home/testuser'); - delete process.env.AGENTAGE_REGISTRY_URL; - delete process.env.AGENTAGE_AUTH_TOKEN; - }); - - describe('getConfigDir', () => { - it('returns the correct config directory path', () => { - expect(getConfigDir()).toBe('/home/testuser/.agentage'); - }); - }); - - describe('getConfigPath', () => { - it('returns the correct config file path', () => { - expect(getConfigPath()).toBe('/home/testuser/.agentage/config.json'); - }); - }); - - describe('getAuthPath', () => { - it('returns the correct auth file path', () => { - expect(getAuthPath()).toBe('/home/testuser/.agentage/auth.json'); - }); - }); - - describe('loadAuth', () => { - it('returns parsed auth when file exists', async () => { - const auth: AuthFileConfig = { - token: 'test-token', - expiresAt: '2030-01-01T00:00:00.000Z', - user: { id: '123', email: 'test@example.com' }, - }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await loadAuth(); - - expect(result).toEqual(auth); - expect(mockReadFile).toHaveBeenCalledWith('/home/testuser/.agentage/auth.json', 'utf-8'); - }); - - it('returns empty object when file does not exist', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - - const result = await loadAuth(); - - expect(result).toEqual({}); - }); - - it('returns empty object when file contains invalid JSON', async () => { - mockReadFile.mockResolvedValue('not json'); - - const result = await loadAuth(); - - expect(result).toEqual({}); - }); - }); - - describe('saveAuth', () => { - it('creates directory and writes auth.json', async () => { - mockMkdir.mockResolvedValue(undefined); - mockWriteFile.mockResolvedValue(undefined); - - const auth: AuthFileConfig = { - token: 'my-token', - user: { id: '1', email: 'a@b.com' }, - }; - - await saveAuth(auth); - - expect(mockMkdir).toHaveBeenCalledWith('/home/testuser/.agentage', { - recursive: true, - }); - expect(mockWriteFile).toHaveBeenCalledWith( - '/home/testuser/.agentage/auth.json', - JSON.stringify(auth, null, 2), - 'utf-8' - ); - }); - }); - - describe('clearAuth', () => { - it('writes empty object to auth.json', async () => { - mockWriteFile.mockResolvedValue(undefined); - - await clearAuth(); - - expect(mockWriteFile).toHaveBeenCalledWith( - '/home/testuser/.agentage/auth.json', - JSON.stringify({}, null, 2), - 'utf-8' - ); - }); - - it('does not touch config.json', async () => { - mockWriteFile.mockResolvedValue(undefined); - - await clearAuth(); - - expect(mockWriteFile).toHaveBeenCalledTimes(1); - const writtenPath = mockWriteFile.mock.calls[0][0] as string; - expect(writtenPath).toContain('auth.json'); - expect(writtenPath).not.toContain('config.json'); - }); - - it('ignores errors gracefully', async () => { - mockWriteFile.mockRejectedValue(new Error('ENOENT')); - - await expect(clearAuth()).resolves.not.toThrow(); - }); - }); - - describe('loadAppConfig', () => { - it('returns parsed app config when file exists', async () => { - const config: AppConfig = { - registry: { url: 'https://agentage.io' }, - deviceId: 'abc123', - }; - mockReadFile.mockResolvedValue(JSON.stringify(config)); - - const result = await loadAppConfig(); - - expect(result).toEqual(config); - expect(mockReadFile).toHaveBeenCalledWith('/home/testuser/.agentage/config.json', 'utf-8'); - }); - - it('returns empty object when file does not exist', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - - const result = await loadAppConfig(); - - expect(result).toEqual({}); - }); - - it('strips unknown fields from config.json', async () => { - const configWithExtra = { - auth: { token: 'should-be-ignored' }, - registry: { url: 'https://agentage.io' }, - deviceId: 'abc123', - }; - mockReadFile.mockResolvedValue(JSON.stringify(configWithExtra)); - - const result = await loadAppConfig(); - - expect(result).toEqual({ - registry: { url: 'https://agentage.io' }, - deviceId: 'abc123', - }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect((result as any).auth).toBeUndefined(); - }); - }); - - describe('saveAppConfig', () => { - it('creates directory and writes config.json', async () => { - mockMkdir.mockResolvedValue(undefined); - mockWriteFile.mockResolvedValue(undefined); - - const config: AppConfig = { - registry: { url: 'https://agentage.io' }, - deviceId: 'dev-123', - }; - - await saveAppConfig(config); - - expect(mockMkdir).toHaveBeenCalledWith('/home/testuser/.agentage', { - recursive: true, - }); - expect(mockWriteFile).toHaveBeenCalledWith( - '/home/testuser/.agentage/config.json', - JSON.stringify(config, null, 2), - 'utf-8' - ); - }); - }); - - describe('getRegistryUrl', () => { - it('returns environment variable when set', async () => { - process.env.AGENTAGE_REGISTRY_URL = 'https://custom.registry.io'; - - const result = await getRegistryUrl(); - - expect(result).toBe('https://custom.registry.io'); - }); - - it('returns config value when no env var', async () => { - const config: AppConfig = { - registry: { url: 'https://config.registry.io' }, - }; - mockReadFile.mockResolvedValue(JSON.stringify(config)); - - const result = await getRegistryUrl(); - - expect(result).toBe('https://config.registry.io'); - }); - - it('returns default when no env var or config', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - - const result = await getRegistryUrl(); - - expect(result).toBe(DEFAULT_REGISTRY_URL); - }); - }); - - describe('getAuthToken', () => { - it('returns environment variable when set', async () => { - process.env.AGENTAGE_AUTH_TOKEN = 'env-token'; - - const result = await getAuthToken(); - - expect(result).toBe('env-token'); - }); - - it('returns token from auth.json when no env var', async () => { - const auth: AuthFileConfig = { token: 'auth-file-token' }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await getAuthToken(); - - expect(result).toBe('auth-file-token'); - }); - - it('returns undefined when no token available', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - - const result = await getAuthToken(); - - expect(result).toBeUndefined(); - }); - - it('returns undefined when token is expired', async () => { - const pastDate = new Date(Date.now() - 86400000).toISOString(); - const auth: AuthFileConfig = { - token: 'expired-token', - expiresAt: pastDate, - }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await getAuthToken(); - - expect(result).toBeUndefined(); - }); - - it('returns token when not expired', async () => { - const futureDate = new Date(Date.now() + 86400000).toISOString(); - const auth: AuthFileConfig = { - token: 'valid-token', - expiresAt: futureDate, - }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await getAuthToken(); - - expect(result).toBe('valid-token'); - }); - }); - - describe('isTokenExpired', () => { - it('returns false when expiresAt is undefined', () => { - expect(isTokenExpired(undefined)).toBe(false); - }); - - it('returns true when token is expired', () => { - const pastDate = new Date(Date.now() - 86400000).toISOString(); - expect(isTokenExpired(pastDate)).toBe(true); - }); - - it('returns false when token is not expired', () => { - const futureDate = new Date(Date.now() + 86400000).toISOString(); - expect(isTokenExpired(futureDate)).toBe(false); - }); - - it('returns true when token just expired', () => { - const now = new Date().toISOString(); - expect(isTokenExpired(now)).toBe(true); - }); - }); - - describe('getAuthStatus', () => { - it('returns authenticated with env token', async () => { - process.env.AGENTAGE_AUTH_TOKEN = 'env-token'; - - const result = await getAuthStatus(); - - expect(result).toEqual({ status: 'authenticated', token: 'env-token' }); - }); - - it('returns not_authenticated when no token', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - - const result = await getAuthStatus(); - - expect(result).toEqual({ status: 'not_authenticated' }); - }); - - it('returns expired when token is expired', async () => { - const pastDate = new Date(Date.now() - 86400000).toISOString(); - const auth: AuthFileConfig = { - token: 'expired-token', - expiresAt: pastDate, - }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await getAuthStatus(); - - expect(result).toEqual({ status: 'expired' }); - }); - - it('returns authenticated when token is valid', async () => { - const futureDate = new Date(Date.now() + 86400000).toISOString(); - const auth: AuthFileConfig = { - token: 'valid-token', - expiresAt: futureDate, - }; - mockReadFile.mockResolvedValue(JSON.stringify(auth)); - - const result = await getAuthStatus(); - - expect(result).toEqual({ status: 'authenticated', token: 'valid-token' }); - }); - }); - - describe('getDeviceId', () => { - it('returns existing device ID from config.json', async () => { - const config: AppConfig = { - deviceId: 'existing-device-id-12345678', - }; - mockReadFile.mockResolvedValue(JSON.stringify(config)); - - const result = await getDeviceId(); - - expect(result).toBe('existing-device-id-12345678'); - expect(mockWriteFile).not.toHaveBeenCalled(); - }); - - it('generates and saves new device ID when not in config', async () => { - mockReadFile.mockRejectedValue(new Error('ENOENT')); - mockMkdir.mockResolvedValue(undefined); - mockWriteFile.mockResolvedValue(undefined); - - const result = await getDeviceId(); - - expect(result).toMatch(/^[a-f0-9]{32}$/); - expect(mockWriteFile).toHaveBeenCalled(); - const writtenPath = mockWriteFile.mock.calls[0][0] as string; - expect(writtenPath).toContain('config.json'); - const savedConfig = JSON.parse(mockWriteFile.mock.calls[0][1] as string) as AppConfig; - expect(savedConfig.deviceId).toBe(result); - }); - - it('returns consistent device ID on subsequent calls', async () => { - const config: AppConfig = { - deviceId: 'consistent-device-id-abc', - }; - mockReadFile.mockResolvedValue(JSON.stringify(config)); - - const result1 = await getDeviceId(); - const result2 = await getDeviceId(); - - expect(result1).toBe(result2); - expect(result1).toBe('consistent-device-id-abc'); - }); - }); -}); diff --git a/src/utils/config.ts b/src/utils/config.ts deleted file mode 100644 index 7cdf88c..0000000 --- a/src/utils/config.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { createHash } from 'crypto'; -import { mkdir, readFile, writeFile } from 'fs/promises'; -import { arch, homedir, hostname, platform } from 'os'; -import { dirname, join } from 'path'; -import { - AppConfig, - appConfigSchema, - AuthFileConfig, - authFileSchema, -} from '../types/config.types.js'; - -/** - * Default registry URL - */ -export const DEFAULT_REGISTRY_URL = 'https://dev.agentage.io'; - -/** - * Get the config directory path - */ -export const getConfigDir = (): string => join(homedir(), '.agentage'); - -/** - * Get the config file path (config.json) - */ -export const getConfigPath = (): string => join(getConfigDir(), 'config.json'); - -/** - * Get the auth file path (auth.json) - */ -export const getAuthPath = (): string => join(getConfigDir(), 'auth.json'); - -/** - * Load auth state from auth.json - */ -export const loadAuth = async (): Promise => { - try { - const authPath = getAuthPath(); - const content = await readFile(authPath, 'utf-8'); - const parsed = JSON.parse(content); - return authFileSchema.parse(parsed); - } catch { - return {}; - } -}; - -/** - * Save auth state to auth.json - */ -export const saveAuth = async (auth: AuthFileConfig): Promise => { - const authPath = getAuthPath(); - const authDir = dirname(authPath); - await mkdir(authDir, { recursive: true }); - await writeFile(authPath, JSON.stringify(auth, null, 2), 'utf-8'); -}; - -/** - * Clear auth state (writes empty object to auth.json) - * Does NOT touch config.json โ€” deviceId and registry are preserved - */ -export const clearAuth = async (): Promise => { - try { - const authPath = getAuthPath(); - await writeFile(authPath, JSON.stringify({}, null, 2), 'utf-8'); - } catch { - // Ignore if directory doesn't exist - } -}; - -/** - * Load app config from config.json (registry + deviceId only, no tokens) - */ -export const loadAppConfig = async (): Promise => { - try { - const configPath = getConfigPath(); - const content = await readFile(configPath, 'utf-8'); - const parsed = JSON.parse(content); - return appConfigSchema.parse(parsed); - } catch { - return {}; - } -}; - -/** - * Save app config to config.json (registry + deviceId only) - */ -export const saveAppConfig = async (config: AppConfig): Promise => { - const configPath = getConfigPath(); - const configDir = dirname(configPath); - await mkdir(configDir, { recursive: true }); - await writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8'); -}; - -/** - * Get the registry URL from config or environment - */ -export const getRegistryUrl = async (): Promise => { - // Environment variable takes precedence - const envUrl = process.env.AGENTAGE_REGISTRY_URL; - if (envUrl) { - return envUrl; - } - - const config = await loadAppConfig(); - return config.registry?.url || DEFAULT_REGISTRY_URL; -}; - -/** - * Check if a token is expired - */ -export const isTokenExpired = (expiresAt: string | undefined): boolean => { - if (!expiresAt) { - return false; // No expiry means token doesn't expire - } - const expiryDate = new Date(expiresAt); - return expiryDate <= new Date(); -}; - -/** - * Get the auth token from auth.json or environment - * Returns undefined if the token is expired - */ -export const getAuthToken = async (): Promise => { - // Environment variable takes precedence - const envToken = process.env.AGENTAGE_AUTH_TOKEN; - if (envToken) { - return envToken; - } - - const auth = await loadAuth(); - - if (auth.token) { - if (isTokenExpired(auth.expiresAt)) { - return undefined; - } - return auth.token; - } - - return undefined; -}; - -/** - * Auth status types - */ -export type AuthStatus = - | { status: 'authenticated'; token: string } - | { status: 'expired' } - | { status: 'not_authenticated' }; - -/** - * Get detailed auth status including whether token is expired - */ -export const getAuthStatus = async (): Promise => { - // Environment variable takes precedence - const envToken = process.env.AGENTAGE_AUTH_TOKEN; - if (envToken) { - return { status: 'authenticated', token: envToken }; - } - - const auth = await loadAuth(); - - if (!auth.token) { - return { status: 'not_authenticated' }; - } - - if (isTokenExpired(auth.expiresAt)) { - return { status: 'expired' }; - } - - return { status: 'authenticated', token: auth.token }; -}; - -/** - * Generate a device fingerprint based on machine ID and OS info - */ -const generateDeviceFingerprint = async (): Promise => { - try { - const { machineIdSync } = await import('node-machine-id'); - const machineId = machineIdSync(); - const osInfo = `${platform()}-${arch()}-${hostname()}`; - const data = `${machineId}|${osInfo}`; - return createHash('sha256').update(data).digest('hex').slice(0, 32); - } catch { - // Fallback if machine ID is not available - const osInfo = `${platform()}-${arch()}-${hostname()}-${Date.now()}`; - return createHash('sha256').update(osInfo).digest('hex').slice(0, 32); - } -}; - -/** - * Get or create a unique device ID - * The device ID is generated from machine ID + OS info and stored in config.json - */ -export const getDeviceId = async (): Promise => { - const config = await loadAppConfig(); - - if (config.deviceId) { - return config.deviceId; - } - - const deviceId = await generateDeviceFingerprint(); - await saveAppConfig({ ...config, deviceId }); - - return deviceId; -}; diff --git a/src/utils/daemon-client.test.ts b/src/utils/daemon-client.test.ts new file mode 100644 index 0000000..20d9f6d --- /dev/null +++ b/src/utils/daemon-client.test.ts @@ -0,0 +1,88 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { createServer, type Server } from 'node:http'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { randomUUID } from 'node:crypto'; +import express from 'express'; + +const testDir = join(tmpdir(), `agentage-test-client-${Date.now()}`); +let httpServer: Server; +let port: number; + +describe('daemon-client', () => { + beforeAll(async () => { + mkdirSync(testDir, { recursive: true }); + port = 16000 + Math.floor(Math.random() * 1000); + + writeFileSync( + join(testDir, 'config.json'), + JSON.stringify({ + machine: { id: randomUUID(), name: 'test' }, + daemon: { port }, + discovery: { dirs: [] }, + sync: { events: {} }, + }) + ); + + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + + const app = express(); + app.use(express.json()); + app.get('/api/health', (_req, res) => res.json({ status: 'ok' })); + app.post('/api/test', (req, res) => res.json({ received: req.body })); + app.get('/api/fail', (_req, res) => res.status(500).json({ error: 'fail' })); + + httpServer = createServer(app); + await new Promise((resolve) => { + httpServer.listen(port, resolve); + }); + }); + + afterAll(async () => { + await new Promise((resolve) => { + httpServer.close(() => resolve()); + }); + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + }); + + it('get() fetches data from daemon', async () => { + const { get } = await import('./daemon-client.js'); + const result = await get<{ status: string }>('/api/health'); + expect(result.status).toBe('ok'); + }); + + it('post() sends data to daemon', async () => { + const { post } = await import('./daemon-client.js'); + const result = await post<{ received: { hello: string } }>('/api/test', { hello: 'world' }); + expect(result.received.hello).toBe('world'); + }); + + it('get() throws on error response', async () => { + const { get } = await import('./daemon-client.js'); + await expect(get('/api/fail')).rejects.toThrow('fail'); + }); + + it('post() without body works', async () => { + // Add a POST endpoint that accepts no body + const { post } = await import('./daemon-client.js'); + // POST to test endpoint with undefined body + const result = await post<{ received: unknown }>('/api/test'); + expect(result).toBeDefined(); + }); + + it('connectWs connects and calls onMessage', async () => { + const { connectWs } = await import('./daemon-client.js'); + // Note: no WS server on this port, so it'll error โ€” but we test the function exists + const messages: unknown[] = []; + const ws = connectWs((data) => messages.push(data)); + + // Wait for error (no WS server) + await new Promise((r) => { + ws.on('error', () => r(undefined)); + setTimeout(() => r(undefined), 200); + }); + ws.close(); + }); +}); diff --git a/src/utils/daemon-client.ts b/src/utils/daemon-client.ts new file mode 100644 index 0000000..350c0b2 --- /dev/null +++ b/src/utils/daemon-client.ts @@ -0,0 +1,47 @@ +import { WebSocket } from 'ws'; +import { loadConfig } from '../daemon/config.js'; + +const getBaseUrl = (): string => { + const config = loadConfig(); + return `http://localhost:${config.daemon.port}`; +}; + +const getWsUrl = (): string => { + const config = loadConfig(); + return `ws://localhost:${config.daemon.port}/ws`; +}; + +export const get = async (path: string): Promise => { + const response = await fetch(`${getBaseUrl()}${path}`); + if (!response.ok) { + const body = (await response.json().catch(() => ({}))) as { error?: string }; + throw new Error(body.error || `Request failed: ${response.status}`); + } + return response.json() as Promise; +}; + +export const post = async (path: string, body?: unknown): Promise => { + const response = await fetch(`${getBaseUrl()}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: body ? JSON.stringify(body) : undefined, + }); + if (!response.ok) { + const data = (await response.json().catch(() => ({}))) as { error?: string }; + throw new Error(data.error || `Request failed: ${response.status}`); + } + return response.json() as Promise; +}; + +export const connectWs = (onMessage: (data: unknown) => void): WebSocket => { + const ws = new WebSocket(getWsUrl()); + ws.on('message', (raw) => { + try { + const data: unknown = JSON.parse(String(raw)); + onMessage(data); + } catch { + // Ignore malformed messages + } + }); + return ws; +}; diff --git a/src/utils/ensure-daemon.test.ts b/src/utils/ensure-daemon.test.ts new file mode 100644 index 0000000..6326d33 --- /dev/null +++ b/src/utils/ensure-daemon.test.ts @@ -0,0 +1,28 @@ +import { describe, it, vi, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; + +const testDir = join(tmpdir(), `agentage-test-ensure-${Date.now()}`); + +describe('ensure-daemon', () => { + beforeEach(() => { + mkdirSync(testDir, { recursive: true }); + process.env['AGENTAGE_CONFIG_DIR'] = testDir; + }); + + afterEach(() => { + delete process.env['AGENTAGE_CONFIG_DIR']; + rmSync(testDir, { recursive: true, force: true }); + vi.restoreAllMocks(); + }); + + it('returns immediately if daemon is running', async () => { + // Write a PID file with current process PID (which is alive) + writeFileSync(join(testDir, 'daemon.pid'), String(process.pid)); + + const { ensureDaemon } = await import('./ensure-daemon.js'); + // Should not throw โ€” daemon PID file points to a live process + await ensureDaemon(); + }); +}); diff --git a/src/utils/ensure-daemon.ts b/src/utils/ensure-daemon.ts new file mode 100644 index 0000000..4276632 --- /dev/null +++ b/src/utils/ensure-daemon.ts @@ -0,0 +1,6 @@ +import { isDaemonRunning, startDaemon } from '../daemon/daemon.js'; + +export const ensureDaemon = async (): Promise => { + if (isDaemonRunning()) return; + await startDaemon(); +}; diff --git a/src/utils/lockfile.test.ts b/src/utils/lockfile.test.ts deleted file mode 100644 index 2550913..0000000 --- a/src/utils/lockfile.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe('lockfile', () => { - it('should be implemented', () => { - // TODO: Implement lockfile tests - expect(true).toBe(true); - }); -}); diff --git a/src/utils/lockfile.ts b/src/utils/lockfile.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/render.test.ts b/src/utils/render.test.ts new file mode 100644 index 0000000..3a7311b --- /dev/null +++ b/src/utils/render.test.ts @@ -0,0 +1,408 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type RunEvent } from '@agentage/core'; + +describe('render', () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it('renders text format as-is', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: 'Hello world', format: 'text' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('Hello world'); + }); + + it('renders llm.delta inline', async () => { + const spy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: { text: 'token' }, format: 'llm.delta' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('token'); + }); + + it('renders llm.tool_call with arrow syntax', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { name: 'search', input: { query: 'test' } }, + format: 'llm.tool_call', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + const callArgs = spy.mock.calls[0][0] as string; + expect(callArgs).toContain('tool: search'); + }); + + it('renders progress with percent', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { percent: 45, message: 'Building...' }, + format: 'progress', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + const callArgs = spy.mock.calls[0][0] as string; + expect(callArgs).toContain('[45%]'); + expect(callArgs).toContain('Building...'); + }); + + it('renders unknown format as JSON.stringify', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: { custom: 'data' }, format: 'unknown-format' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith(JSON.stringify({ custom: 'data' })); + }); + + it('renders result event', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'result', + data: { type: 'result', success: true, output: 'All done' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('All done'); + }); + + it('renders error event', async () => { + const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'error', + data: { type: 'error', code: 'ERR', message: 'Something failed', recoverable: false }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders markdown format as-is', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: '## Title', format: 'markdown' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('## Title'); + }); + + it('renders output without format', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: 'no format' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('no format'); + }); + + it('renders non-string content as JSON in text format', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: { key: 'value' }, format: 'text' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('{"key":"value"}'); + }); + + it('renders llm.thinking in dim text', async () => { + const spy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: { text: 'thinking...' }, format: 'llm.thinking' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders llm.tool_result with truncation', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const longOutput = 'x'.repeat(300); + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { output: longOutput }, + format: 'llm.tool_result', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + const callArgs = spy.mock.calls[0][0] as string; + expect(callArgs).toContain('...'); + }); + + it('renders llm.usage', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { input: 1234, output: 567 }, + format: 'llm.usage', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + const callArgs = spy.mock.calls[0][0] as string; + expect(callArgs).toContain('Tokens'); + }); + + it('renders json format', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: { data: [1, 2, 3] }, format: 'json' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith(JSON.stringify({ data: [1, 2, 3] }, null, 2)); + }); + + it('renders state completed', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'state', + data: { type: 'state', state: 'completed' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders state failed', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'state', + data: { type: 'state', state: 'failed', message: 'oops' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders state input_required', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'state', + data: { type: 'state', state: 'input_required' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders input_required event with prompt', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'input_required', + data: { type: 'input_required', prompt: 'Enter your name:' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders result with object output', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'result', + data: { type: 'result', success: true, output: { key: 'value' } }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith(JSON.stringify({ key: 'value' }, null, 2)); + }); + + it('skips result with no output', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'result', + data: { type: 'result', success: true }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).not.toHaveBeenCalled(); + }); + + it('renders llm.delta without text', async () => { + const spy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: {}, format: 'llm.delta' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).not.toHaveBeenCalled(); + }); + + it('renders progress without percent', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { message: 'Loading...' }, + format: 'progress', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalledWith('Loading...'); + }); + + it('renders llm.tool_result with non-string output', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { + type: 'output', + content: { output: { nested: true } }, + format: 'llm.tool_result', + }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); + + it('renders state failed without message', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'state', + data: { type: 'state', state: 'failed' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + const callArgs = spy.mock.calls[0][0] as string; + expect(callArgs).toContain('Unknown error'); + }); + + it('ignores non-terminal state events', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'state', + data: { type: 'state', state: 'working' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).not.toHaveBeenCalled(); + }); + + it('renders llm.usage with no values', async () => { + const spy = vi.spyOn(console, 'log').mockImplementation(() => {}); + const { renderEvent } = await import('./render.js'); + + const event: RunEvent = { + type: 'output', + data: { type: 'output', content: {}, format: 'llm.usage' }, + timestamp: Date.now(), + }; + + renderEvent(event); + expect(spy).toHaveBeenCalled(); + }); +}); diff --git a/src/utils/render.ts b/src/utils/render.ts new file mode 100644 index 0000000..0f75908 --- /dev/null +++ b/src/utils/render.ts @@ -0,0 +1,103 @@ +import chalk from 'chalk'; +import { type RunEvent } from '@agentage/core'; + +export const renderEvent = (event: RunEvent): void => { + const { data } = event; + + if (data.type === 'output') { + renderOutput(data.content, data.format); + return; + } + + if (data.type === 'state') { + if (data.state === 'completed') { + console.log(chalk.green('\nDone.')); + } else if (data.state === 'failed') { + console.log(chalk.red(`\nFailed: ${data.message || 'Unknown error'}`)); + } else if (data.state === 'input_required') { + console.log(chalk.yellow('\nInput required.')); + } + return; + } + + if (data.type === 'error') { + console.error(chalk.red(`Error [${data.code}]: ${data.message}`)); + return; + } + + if (data.type === 'input_required') { + console.log(chalk.yellow(`\n${data.prompt}`)); + return; + } + + if (data.type === 'result') { + if (data.output) { + console.log( + typeof data.output === 'string' ? data.output : JSON.stringify(data.output, null, 2) + ); + } + return; + } +}; + +const renderOutput = (content: unknown, format?: string): void => { + if (!format || format === 'text' || format === 'markdown') { + console.log(typeof content === 'string' ? content : JSON.stringify(content)); + return; + } + + if (format === 'llm.delta') { + const delta = content as { text?: string }; + if (delta.text) { + process.stdout.write(delta.text); + } + return; + } + + if (format === 'llm.thinking') { + const thinking = content as { text?: string }; + if (thinking.text) { + process.stdout.write(chalk.dim(thinking.text)); + } + return; + } + + if (format === 'llm.tool_call') { + const call = content as { name?: string; input?: unknown }; + console.log(chalk.cyan(`> tool: ${call.name}(${JSON.stringify(call.input)})`)); + return; + } + + if (format === 'llm.tool_result') { + const result = content as { output?: unknown }; + const text = typeof result.output === 'string' ? result.output : JSON.stringify(result.output); + const truncated = text.length > 200 ? text.slice(0, 200) + '...' : text; + console.log(chalk.gray(`< result: ${truncated}`)); + return; + } + + if (format === 'llm.usage') { + const usage = content as { input?: number; output?: number }; + console.log( + chalk.gray( + `Tokens: ${(usage.input ?? 0).toLocaleString()} in / ${(usage.output ?? 0).toLocaleString()} out` + ) + ); + return; + } + + if (format === 'progress') { + const progress = content as { percent?: number; message?: string }; + const pct = progress.percent !== undefined ? `[${progress.percent}%] ` : ''; + console.log(`${pct}${progress.message || ''}`); + return; + } + + if (format === 'json') { + console.log(JSON.stringify(content, null, 2)); + return; + } + + // Unknown format + console.log(JSON.stringify(content)); +}; diff --git a/src/utils/version.test.ts b/src/utils/version.test.ts deleted file mode 100644 index 059df19..0000000 --- a/src/utils/version.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PACKAGE_NAME, checkForUpdates } from './version.js'; - -describe('version utils', () => { - describe('PACKAGE_NAME', () => { - test('should be @agentage/cli', () => { - expect(PACKAGE_NAME).toBe('@agentage/cli'); - }); - }); - - describe('checkForUpdates', () => { - test('should return updateAvailable false when versions match', async () => { - const result = await checkForUpdates('0.1.8'); - // If the current npm version is 0.1.8, updateAvailable should be false - if (result.latestVersion === '0.1.8') { - expect(result.updateAvailable).toBe(false); - } - expect(result.currentVersion).toBe('0.1.8'); - }); - - test('should return updateAvailable true when versions differ', async () => { - const result = await checkForUpdates('0.0.1'); - // Unless npm is unreachable, an old version should show update available - if (result.latestVersion !== 'unknown') { - expect(result.updateAvailable).toBe(true); - } - expect(result.currentVersion).toBe('0.0.1'); - }); - - test('should handle unknown latest version gracefully', async () => { - const result = await checkForUpdates('1.0.0'); - expect(result.currentVersion).toBe('1.0.0'); - expect(typeof result.updateAvailable).toBe('boolean'); - }); - }); -}); diff --git a/src/utils/version.ts b/src/utils/version.ts deleted file mode 100644 index 2ce3ba0..0000000 --- a/src/utils/version.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { exec } from 'child_process'; -import { promisify } from 'util'; - -const execAsync = promisify(exec); - -export const PACKAGE_NAME = '@agentage/cli'; - -export const getInstalledVersion = async (): Promise => { - try { - const { stdout } = await execAsync(`npm list -g ${PACKAGE_NAME} --json`); - const data = JSON.parse(stdout); - return data.dependencies?.[PACKAGE_NAME]?.version || 'unknown'; - } catch { - return 'unknown'; - } -}; - -export const getLatestVersion = async (): Promise => { - try { - const { stdout } = await execAsync(`npm view ${PACKAGE_NAME} version`); - return stdout.trim(); - } catch { - return 'unknown'; - } -}; - -export interface VersionCheckResult { - currentVersion: string; - latestVersion: string; - updateAvailable: boolean; -} - -export const checkForUpdates = async (currentVersion: string): Promise => { - const latestVersion = await getLatestVersion(); - const updateAvailable = latestVersion !== 'unknown' && latestVersion !== currentVersion; - - return { - currentVersion, - latestVersion, - updateAvailable, - }; -}; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..b47647e --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "coverage", "src/**/*.test.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index b1eb279..2d09c87 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,12 @@ { "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", - "resolveJsonModule": true, - "allowJs": false, - "outDir": "./dist", - "rootDir": "./src", - "removeComments": true, + "target": "ES2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2024"], + "outDir": "dist", + "rootDir": "src", "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictBindCallApply": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, @@ -24,10 +14,11 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, "declaration": true, "declarationMap": true, "sourceMap": true }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "src/**/__mocks__"] + "include": ["src"], + "exclude": ["node_modules", "dist", "coverage"] } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..b48ee74 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + coverage: { + provider: 'v8', + include: ['src/**/*.ts'], + exclude: [ + '**/*.test.ts', + '**/index.ts', + 'src/cli.ts', + 'src/daemon-entry.ts', + 'src/commands/**', + ], + thresholds: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + }, + }, +});