diff --git a/.claude/commands/update-docs.md b/.claude/commands/update-docs.md new file mode 100644 index 0000000..53b4a1d --- /dev/null +++ b/.claude/commands/update-docs.md @@ -0,0 +1,66 @@ +--- +description: 'Investigate whether documentation updates are needed and update as necessary' +--- + +# Documentation Update Command + +This command automatically updates documentation in response to code changes. + +## Execution Steps + +1. **Run Documentation Validation** + - Run `npm run docs:validate` to check code changes + - Get list of changed files + +2. **Analyze Changes** + - Check specific changes in each file + - Identify important changes that require documentation updates + +3. **Understand Current Documentation Structure** + - Refer to `docs/development/documentation.md` + +4. **Criteria for Changes Requiring Documentation Updates** + - Refer to `docs/development/documentation.md` + +5. **Update Documentation** + - Refer to `docs/development/documentation.md` + - Run `npm run format` to format documentation + +6. **Verify** + - Run `npm run docs:validate` to verify + - Check diff of updated documentation + +7. **Report Results to User** + - List of updated documentation + - Summary of main changes + - List of files to commit + +## Output Format + +Present summary in the following format at the end: + +``` +## Completed! + +### 📝 Updated Documentation + +#### 1. [File Name] (+X lines, -Y lines) +- Change 1 +- Change 2 + +### 🔍 Investigation Results + +[Explanation of main changes] + +### Next Steps + +Review the changes and commit as necessary: +`git commit -m {commit message}` +``` + +## Notes + +- Update CLAUDE.md only when important design policies change (usually only under `docs/`) +- No update needed for minor changes or internal implementation changes +- Type system or API specification changes must be reflected +- Ensure CODE_REF comments reference the latest code diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000..18c524a --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,57 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + Please review this pull request and provide feedback on the following aspects: + - Code quality and best practices + - Potential bugs or issues + - Performance concerns + - Security concerns + - Test coverage + - Documentation update needs + + For style and coding conventions, please refer to `CLAUDE.md` in the repository as a guide. Feedback should be constructive and helpful. + Please post your review as a comment on the PR using the `gh pr comment` command via the Bash tool. + + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"' diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000..d300267 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,50 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr:*)' + diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml new file mode 100644 index 0000000..6506172 --- /dev/null +++ b/.github/workflows/docs-validation.yml @@ -0,0 +1,31 @@ +name: Documentation Validation + +on: + pull_request: + branches: [main, develop] + +jobs: + validate-docs: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history for git diff + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Validate documentation updates + run: npm run docs:validate diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..3fa0bb7 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,37 @@ +name: Lint, Format Check, and Type Check + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + code-quality: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + + - name: Check code formatting + run: npm run format:check + + - name: Run type check + run: npm run type-check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d5e027b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,59 @@ +name: Release + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + issues: write + pull-requests: write + id-token: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build package + run: npm run build + + - name: Run tests + run: npm test + + - name: Run linter + run: npm run lint + + - name: Run type check + run: npm run type-check + + - name: Verify build artifacts + run: | + if [ ! -d "dist" ]; then + echo "Build failed: dist directory not found" + exit 1 + fi + echo "Build artifacts verified successfully" + + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..94a68d7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Test + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm run test diff --git a/.gitignore b/.gitignore index 9a5aced..2a52d57 100644 --- a/.gitignore +++ b/.gitignore @@ -123,8 +123,13 @@ dist .tern-port # Stores VSCode versions used for testing VSCode extensions +.vscode/ .vscode-test +# Claude.ai files +.claude/* +!.claude/commands/ + # yarn v3 .pnp.* .yarn/* diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..e37998f --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1 @@ +npm run test diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..3a6161c --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22.19.0 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..44c39a5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,25 @@ +# Build outputs +dist/ +build/ +coverage/ + +# Dependencies +node_modules/ + +# Lock files (machine-generated) +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Logs +*.log + +# Cache +.eslintcache +*.tsbuildinfo + +# Test snapshots (if any) +**/__snapshots__/ + +# Generated files +CHANGELOG.md diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..c03be81 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,23 @@ +{ + "branches": ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md", + "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + } + ], + "@semantic-release/npm", + [ + "@semantic-release/git", + { + "assets": ["CHANGELOG.md", "package.json", "package-lock.json"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..af6ac37 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,54 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`@cawpea/coderef` is a tool for validating and auto-fixing code references in markdown documentation. It ensures code snippets in documentation stay synchronized with actual source code through CODE_REF comments and AST-based symbol searching. + +## Quick Reference + +- **Architecture**: See [docs/architecture/overview.md](docs/architecture/overview.md) +- **Development Setup**: See [docs/development/getting-started.md](docs/development/getting-started.md) +- **Coding Standards**: See [docs/development/coding-standards.md](docs/development/coding-standards.md) +- **Git Conventions**: See [docs/development/git-conventions.md](docs/development/git-conventions.md) +- **Testing**: See [docs/development/testing-guide.md](docs/development/testing-guide.md) + +## Essential Information + +### Code Style (Quick Reference) + +- **Indentation**: 2 spaces +- **Semicolons**: Enabled +- **Quotes**: Single quotes +- **Print width**: 100 characters +- **Line endings**: LF (Unix) +- **Trailing commas**: ES5 style +- **Type imports**: Prefer `import type` for type-only imports + +### Common Commands + +```bash +npm run build # Build for production (CJS + ESM + types) +npm test # Run all tests +npm run lint:fix # Auto-fix linting issues +npm run format # Format code with Prettier +npm run type-check # Run TypeScript compiler checks +npm run docs:validate # Validate documentation updates +``` + +### Commit Message Format + +Follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +: +``` + +**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `ci`, `perf`, `build`, `revert` + +See [docs/development/git-conventions.md](docs/development/git-conventions.md) for the complete type table and semantic versioning impact. + +## Full Documentation + +For comprehensive documentation, see [docs/README.md](docs/README.md). diff --git a/README.md b/README.md index c105111..5fd0bc0 100644 --- a/README.md +++ b/README.md @@ -67,18 +67,102 @@ Reference code by line numbers: ``` -Reference code by symbol name: +Reference code by symbol name (functions): ```markdown ``` +Reference variables (const, let, var): + +```markdown + + +``` + Reference class methods: ```markdown ``` +For detailed syntax and options, see [docs/user-guide/code-ref-syntax.md](docs/user-guide/code-ref-syntax.md). + +## Examples + +### Example 1: Documenting API Function Usage + +In your `docs/api.md`: + +````markdown +## Authentication + +Our API uses JWT tokens for authentication. Here's the implementation: + + + +```typescript +export function generateToken(userId: string): string { + // This code will be automatically extracted from src/auth/jwt.ts +} +``` + +Call this function with a user ID to generate a valid token. +```` + +When you run `npx coderef validate`, it will verify that the `generateToken` function exists in `src/auth/jwt.ts` and extract its implementation automatically. + +### Example 2: Keeping Configuration Examples Up-to-Date + +In your `docs/configuration.md`: + +````markdown +## Environment Variables + +Set the following environment variables: + + + +```typescript +export const config = { + apiKey: process.env.API_KEY, + apiUrl: process.env.API_URL, +}; +``` + +These values are loaded at application startup. +```` + +If the `config` object in your source code changes, running `npx coderef fix --auto` will automatically update the documentation to reflect the current implementation. + +### Example 3: Documenting Specific Code Sections + +In your `docs/tutorial.md`: + +````markdown +## Error Handling + +Our error handling middleware is implemented as follows: + + + +```typescript +export function errorHandler(err: Error, req: Request, res: Response) { + console.error(err.stack); + res.status(500).json({ + error: err.message, + timestamp: new Date().toISOString(), + }); +} +``` + +This middleware catches all errors and formats them consistently. +```` + +This references lines 15-35 of the file. If the code changes, `coderef` will detect the mismatch and help you update the reference. + +For more examples and usage patterns, see [docs/user-guide/](docs/user-guide/). + ## Configuration Create `.coderefrc.json` in your project root: diff --git a/bin/coderef.js b/bin/coderef.js new file mode 100755 index 0000000..61772d6 --- /dev/null +++ b/bin/coderef.js @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +/** + * CLI entry point for @cawpea/coderef + */ + +const { program } = require('commander'); +const packageJson = require('../package.json'); + +program + .name('coderef') + .description('Validate and fix code references in markdown documentation') + .version(packageJson.version); + +program + .command('validate [files...]') + .description('Validate CODE_REF references in markdown files') + .option('-v, --verbose', 'Show detailed validation information') + .action(async (files, options) => { + const { main } = require('../dist/cli/validate.js'); + const args = []; + if (options.verbose) args.push('--verbose'); + args.push(...files); + await main(args); + }); + +program + .command('fix') + .description('Interactively fix CODE_REF errors') + .option('--dry-run', 'Show what would be fixed without making changes') + .option('--auto', 'Automatically apply all fixes without confirmation') + .option('--backup', 'Create backup files before applying fixes') + .option('-v, --verbose', 'Show detailed fix information') + .action(async (options) => { + const { main } = require('../dist/cli/fix.js'); + const args = []; + if (options.dryRun) args.push('--dry-run'); + if (options.auto) args.push('--auto'); + if (options.backup) args.push('--backup'); + if (options.verbose) args.push('--verbose'); + await main(args); + }); + +program.parse(); diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..f4c7c9e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,47 @@ +# Documentation Index + +Welcome to the @cawpea/coderef documentation. This guide is organized into three main sections: + +## For Users + +Start here if you want to use the tool: + +- [Installation](user-guide/installation.md) - Install and setup +- [CLI Usage](user-guide/cli-usage.md) - Command-line interface +- [Configuration](user-guide/configuration.md) - Configuration options +- [CODE_REF Syntax](user-guide/code-ref-syntax.md) - Reference syntax guide + +## For Contributors + +Start here if you want to contribute: + +- [Getting Started](development/getting-started.md) - Setup development environment +- [Coding Standards](development/coding-standards.md) - Style guide and conventions +- [Git Conventions](development/git-conventions.md) - Commit message format +- [Testing Guide](development/testing-guide.md) - Writing and running tests +- [Release Process](development/release.md) - Release procedures and automation +- [Documentation Guidelines](development/documentation.md) - Writing and maintaining docs + +## Architecture & Design + +Deep dive into technical implementation: + +- [Architecture Overview](architecture/overview.md) - System design and module structure +- [CODE_REF Syntax Implementation](architecture/code-ref-syntax.md) - AST parsing and symbol search +- [Testing Strategy](architecture/testing.md) - Test configuration and coverage + +## Quick Links + +- [Main README](../README.md) - Project homepage +- [CLAUDE.md](../CLAUDE.md) - AI assistant guidance +- [CHANGELOG](../CHANGELOG.md) - Version history + +## Documentation Structure + +``` +docs/ +├── README.md (this file) +├── user-guide/ # End-user documentation +├── development/ # Contributor guides +└── architecture/ # Technical deep-dives +``` diff --git a/docs/architecture/code-ref-syntax.md b/docs/architecture/code-ref-syntax.md new file mode 100644 index 0000000..c465d64 --- /dev/null +++ b/docs/architecture/code-ref-syntax.md @@ -0,0 +1,47 @@ +# CODE_REF Syntax Implementation + +## Supported Reference Patterns + +The tool supports four reference patterns: + +1. **Line-based references**: `` +2. **Function references**: `` +3. **Variable references**: `` +4. **Class method references**: `` + +## AST Parsing + +Uses `@typescript-eslint/typescript-estree` for TypeScript/JavaScript symbol searching and code extraction. + +### Symbol Search Implementation + +Symbol search is implemented in `src/utils/ast-symbol-search.ts`: + +- **Functions**: `findFunctionByName()` searches for top-level and exported function declarations +- **Variables**: `findVariableByName()` searches for top-level and exported variable declarations + - Supports `const`, `let`, and `var` + - Handles destructuring patterns (object, array, rest syntax) + - Returns entire declaration statement for multiple declarators +- **Class Methods**: `findMethodInClass()` searches for methods within a specific class + +### Symbol Resolution Priority + +When multiple symbols match the same name, the search order is: + +1. Class methods (if className is specified) +2. Functions +3. Variables + +This means functions take precedence over variables when both exist with the same name. + +### Variable Declaration Support + +The variable search supports: + +- **Simple declarations**: `const x = 1;` +- **Multiple declarators**: `const x = 1, y = 2;` (entire statement is extracted) +- **Exported variables**: `export const API_KEY = 'key';` +- **Object destructuring**: `const { a, b } = obj;` +- **Array destructuring**: `const [x, y] = arr;` +- **Rest syntax**: `const { ...rest } = obj;`, `const [...items] = arr;` +- **JSDoc comments**: Automatically included in the extracted code diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..b2bcab9 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,15 @@ +# Architecture Overview + +## Build System + +- **tsup**: Generates both CJS and ESM formats with type declarations +- Output directory: `./dist` +- Entry points defined in package.json exports for proper dual-package support + +## Module Structure + +The project is organized into three main directories under `src/`: + +- `cli/`: Command-line interface implementations (validate.ts, fix.ts) +- `core/`: Core validation and fixing logic +- `utils/`: Shared utility functions diff --git a/docs/architecture/testing.md b/docs/architecture/testing.md new file mode 100644 index 0000000..0de29c4 --- /dev/null +++ b/docs/architecture/testing.md @@ -0,0 +1,12 @@ +# Testing Strategy + +## Test Configuration + +- Test files: Co-located with source files as `**/*.test.ts` +- Path alias: `@/` maps to `src/` +- Environment: Node.js +- Preset: ts-jest + +## Coverage Requirements + +Coverage thresholds are set to 80% for all metrics (branches, functions, lines, statements). CLI code is excluded from coverage as it's verified through integration tests. diff --git a/docs/development/coding-standards.md b/docs/development/coding-standards.md new file mode 100644 index 0000000..39ed865 --- /dev/null +++ b/docs/development/coding-standards.md @@ -0,0 +1,43 @@ +# Coding Standards + +## Linting & Formatting + +```bash +npm run lint # Lint TypeScript files +npm run lint:fix # Auto-fix linting issues +npm run format # Format code with Prettier +npm run format:check # Check formatting without modifying +npm run type-check # Run TypeScript compiler checks +``` + +## Pre-commit Hooks + +This project uses [husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to automatically lint and format staged files before each commit. + +The pre-commit hook will: + +1. Run ESLint with auto-fix on staged TypeScript files +2. Run Prettier on staged files (TS, JS, JSON, MD) +3. Prevent commit if linting errors remain + +To skip hooks (not recommended): + +```bash +git commit --no-verify +``` + +## Configuration Files + +- `prettier.config.js` - Code formatting rules +- `eslint.config.js` - Linting rules (ESLint 9 flat config) +- `.vscode/settings.json` - VS Code auto-format on save + +## Code Style + +- **Indentation**: 2 spaces +- **Semicolons**: Enabled +- **Quotes**: Single quotes +- **Print width**: 100 characters +- **Line endings**: LF (Unix) +- **Trailing commas**: ES5 style +- **Type imports**: Prefer `import type` for type-only imports diff --git a/docs/development/documentation.md b/docs/development/documentation.md new file mode 100644 index 0000000..0851b1f --- /dev/null +++ b/docs/development/documentation.md @@ -0,0 +1,188 @@ +# Documentation Guidelines + +This guide explains how to write and maintain documentation for the @cawpea/coderef project. + +## Documentation Structure + +All documentation lives in the `docs/` directory and follows a three-tier structure: + +``` +docs/ +├── README.md # Documentation index and navigation +├── user-guide/ # End-user documentation +├── development/ # Contributor guides +└── architecture/ # Technical deep-dives +``` + +## Markdown Style Guide + +### Headers + +Use ATX-style headers (`#`) with a space after the hash: + +```markdown +# H1 - Document Title + +## H2 - Major Section + +### H3 - Subsection +``` + +- Each document should have exactly one H1 at the top +- Use H2 for major sections, H3 for subsections +- Avoid skipping header levels (don't jump from H2 to H4) + +### Code Blocks + +Always specify the language for syntax highlighting: + +````markdown +```bash +npm run build +``` + +```typescript +function example(): void { + console.log('Hello'); +} +``` +```` + +For command-line examples, use: + +- `bash` for shell commands +- `typescript` for TypeScript code +- `javascript` for JavaScript code +- `json` for JSON configuration +- `markdown` for markdown examples + +**Important**: When including code examples from the actual source code, always use CODE_REF comments to reference the source. This ensures the documentation stays synchronized with the codebase. See [Using CODE_REF in Documentation](#using-code_ref-in-documentation) for details. + +### Lists + +Use consistent formatting: + +```markdown +- Unordered lists use hyphens (-) +- Not asterisks (\*) or plus (+) +- Keep items concise + +1. Ordered lists use numbers +2. Start from 1 and increment +3. Use for sequential steps +``` + +### Links + +#### Internal Links + +Use relative paths from the current file: + +```markdown +[Coding Standards](coding-standards.md) +[Architecture Overview](../architecture/overview.md) +[Main README](../../README.md) +``` + +#### External Links + +Provide descriptive link text: + +```markdown +Good: [Conventional Commits specification](https://www.conventionalcommits.org/) +Bad: Click [here](https://www.conventionalcommits.org/) +``` + +### Emphasis + +- **Bold** for UI elements, file names, important terms: `**package.json**` +- _Italic_ for emphasis or introducing new terms: `*CODE_REF comments*` +- `Code` for inline code, commands, values: `` `npm install` `` + +## Using CODE_REF in Documentation + +Since this project validates CODE_REF comments in markdown, follow these practices: + +### Example Code with CODE_REF + +When showing code examples that reference actual source code, use CODE_REF comments: + +````markdown +```typescript +// CODE_REF: src/utils/parser.ts#parseCodeRef +export function parseCodeRef(comment: string): CodeRef { + // Implementation +} +``` +```` + +### Explaining CODE_REF Syntax + +When documenting CODE_REF syntax itself, use markdown code blocks: + +````markdown +The basic syntax is: + +``` +// CODE_REF: # +``` +```` + +### Best Practices for CODE_REF + +- **Always use CODE_REF when including code examples from the actual source code** + - This applies to any code snippets copied from `src/`, `bin/`, or other source directories + - CODE_REF ensures documentation stays in sync with the codebase + - Without CODE_REF, code examples can become outdated and misleading +- Use CODE_REF when showing actual implementation code +- Keep referenced code snippets short and focused +- Update documentation when refactoring referenced code +- For hypothetical or generic examples that don't reference actual source code, CODE_REF is not required + +## When to Update Documentation + +Documentation should be updated whenever user-facing code changes. The project includes an automatic validation script to help ensure documentation stays in sync with code. + +### Code Changes That Require Documentation Updates + +The following changes typically require documentation updates: + +- **CLI changes** (`src/cli/`) → Update `docs/user-guide/cli-usage.md` +- **Public API changes** (`src/index.ts`) → Update `docs/user-guide/` and `docs/architecture/overview.md` +- **Binary changes** (`bin/`) → Update `docs/user-guide/installation.md` +- **Core functionality** (`src/core/`) → Update `docs/architecture/` as needed + +### Validation Workflow + +Before committing changes, validate your documentation updates: + +```bash +npm run docs:validate +``` + +This script will: + +1. Detect whether your branch was created from `main` or `develop` +2. Compare your changes against the base branch +3. Check if documentation was updated when required +4. Provide suggestions for which docs to update + +### Documentation Update Process + +1. **Make code changes** in your feature branch +2. **Update relevant documentation** in `docs/` +3. **Validate documentation** with `npm run docs:validate` +4. **Test CODE_REF references** with `npm run build` (runs coderef validation) +5. **Commit both code and docs** together + +## Common Mistakes to Avoid + +- Don't use `
` tags (use blank lines) +- Don't mix list markers (stick to `-` for unordered lists) +- Don't forget language specifiers in code blocks +- Don't use relative links that go outside the repository +- Don't commit documentation without testing examples +- Don't use inline HTML when markdown suffices +- Don't forget to update `docs/README.md` when adding new documents +- Don't commit user-facing code changes without updating documentation +- **Don't include code examples from the actual source code without CODE_REF comments** diff --git a/docs/development/getting-started.md b/docs/development/getting-started.md new file mode 100644 index 0000000..7900037 --- /dev/null +++ b/docs/development/getting-started.md @@ -0,0 +1,40 @@ +# Getting Started + +## Prerequisites + +- **Minimum Node.js version**: 22.0.0 (specified in `package.json` engines) +- **Recommended version**: See `.node-version` file in the project root + +## Building + +```bash +npm run build # Build for production (CJS + ESM + types) +npm run dev # Watch mode for development +``` + +## Testing + +```bash +npm test # Run all tests +npm run test:watch # Run tests in watch mode +npm run test:coverage # Run tests with coverage report +``` + +Coverage thresholds are set to 80% for all metrics (branches, functions, lines, statements). CLI code is excluded from coverage as it's verified through integration tests. + +## Running Specific Tests + +```bash +npx jest src/utils/foo.test.ts # Run specific test file +npx jest -t "" # Run tests matching pattern +``` + +## Documentation Validation + +When making changes to user-facing code (CLI, public APIs, etc.), validate that documentation is updated: + +```bash +npm run docs:validate # Check if documentation needs updating +``` + +This script automatically detects your base branch and suggests which documentation files to update. See [Documentation Guidelines](documentation.md) for details. diff --git a/docs/development/git-conventions.md b/docs/development/git-conventions.md new file mode 100644 index 0000000..95fe874 --- /dev/null +++ b/docs/development/git-conventions.md @@ -0,0 +1,29 @@ +# Git Commit Message Convention + +This project follows [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages. + +## Commit Message Format + +``` +: + +[optional body] + +[optional footer] +``` + +## Commit Types + +| Type | Description | Semantic Version Impact | Examples | +| ---------- | --------------------------------------------------------------- | ----------------------- | ---------------------------------------- | +| `feat` | New feature | MINOR (1.x.0) | Add evaluation agent, new UI component | +| `fix` | Bug fix | PATCH (1.0.x) | Fix contrast ratio calculation error | +| `docs` | Documentation only changes | None | Update README, add comments | +| `style` | Changes that don't affect code meaning (whitespace, formatting) | None | Run Prettier, fix indentation | +| `refactor` | Code changes that neither fix bugs nor add features | None | Split function, rename variables | +| `test` | Adding or updating tests | None | Add unit tests, improve mocks | +| `chore` | Changes to build process or tools | None | Update dependencies, modify config files | +| `ci` | Changes to CI configuration files and scripts | None | Update GitHub Actions | +| `perf` | Performance improvements | PATCH (1.0.x) | Optimize API response time | +| `build` | Changes to build system or external dependencies | None | Modify Webpack config, add npm scripts | +| `revert` | Revert a previous commit | Depends on original | Revert previous commit | diff --git a/docs/development/release.md b/docs/development/release.md new file mode 100644 index 0000000..1ab3c6c --- /dev/null +++ b/docs/development/release.md @@ -0,0 +1,388 @@ +# Release Process + +This document describes the release process for `@cawpea/coderef`. + +## Overview + +The release process is automated using [semantic-release](https://semantic-release.gitbook.io/). It automatically determines versions based on Conventional Commits, generates CHANGELOGs, publishes to npm, and creates GitHub releases. + +## Prerequisites + +### Initial Setup (Repository Administrators Only) + +To enable release automation, the following configuration is required: + +1. **Create NPM Token** + - Visit https://www.npmjs.com/settings/YOUR_USERNAME/tokens + - Click "Generate New Token" + - Token type: **Automation** + - Scope: Publish access to `@cawpea` scope + +2. **Configure GitHub Secrets** + - Go to GitHub repository Settings → Secrets and variables → Actions + - Click "New repository secret" + - Name: `NPM_TOKEN` + - Value: Paste the npm token created above + +### Developer Prerequisites + +- Understanding of [Conventional Commits](https://www.conventionalcommits.org/) specification +- Familiarity with project [Git Conventions](./git-conventions.md) + +## Release Methods + +### Method 1: Automatic Release (Recommended) + +Releases are automatically triggered when merging to the `main` branch. + +#### Steps + +1. **Develop on a Feature Branch** + + ```bash + git checkout -b feature/your-feature + # Develop and commit + ``` + +2. **Follow Conventional Commits** + + ```bash + # For new features (MINOR version bump) + git commit -m "feat: add new validation feature" + + # For bug fixes (PATCH version bump) + git commit -m "fix: correct reference parsing" + + # For breaking changes (MAJOR version bump) + git commit -m "feat!: change API interface + + BREAKING CHANGE: API signature has changed" + ``` + +3. **Create Pull Request for Review** + + ```bash + git push origin feature/your-feature + # Create PR on GitHub + ``` + +4. **Merge to main Branch** + - After PR is reviewed and approved, merge to `main` branch + - Release workflow will automatically start after merge + +5. **Verify Release Completion** + - Check Release workflow execution in GitHub Actions + - Verify new version is published on npm + - Verify new release is created on GitHub Releases + +### Method 2: Manual Release + +You can manually trigger a release from the GitHub UI when needed. + +#### Steps + +1. **Open GitHub Repository Page** + - https://github.com/cawpea/coderef + +2. **Navigate to Actions Tab** + - Click the "Actions" tab at the top + +3. **Select Release Workflow** + - Select "Release" workflow from the left sidebar + +4. **Run Workflow** + - Click the "Run workflow" button in the top right + - Select branch (usually `main`) + - Click "Run workflow" + +5. **Verify Release Completion** + - Check workflow execution logs + - Verify on npm and GitHub Releases + +**Note**: Due to semantic-release configuration, releases will only be created from the `main` branch. + +## Release Process Details + +### Executed Operations + +When the Release workflow runs, the following operations are executed sequentially: + +1. **Checkout Code** + - Fetch complete Git history (`fetch-depth: 0`) + +2. **Quality Checks** + - Build (`npm run build`) + - Tests (`npm test`) + - Lint (`npm run lint`) + - Type check (`npm run type-check`) + +3. **Run semantic-release** + - Commit analysis (analyze commits since last release) + - Version determination (based on commit types) + - Update CHANGELOG.md + - Update package.json version + - Publish package to npm + - Create GitHub release + - Commit changes and create tag + +### Version Determination Rules + +Versions are automatically determined based on commit messages: + +| Commit Type | Example | Version Change | +| ------------------------------ | ----------------------- | --------------------- | +| `feat:` | `feat: add new feature` | MINOR (0.1.0 → 0.2.0) | +| `fix:` | `fix: resolve bug` | PATCH (0.1.0 → 0.1.1) | +| `BREAKING CHANGE:` | In footer | MAJOR (0.1.0 → 1.0.0) | +| `feat!:` or `fix!:` | `feat!: change API` | MAJOR (0.1.0 → 1.0.0) | +| `docs:`, `chore:`, `ci:`, etc. | - | No release | + +See [Git Conventions](./git-conventions.md) for more details. + +### Generated Artifacts + +When a release succeeds, the following are automatically created: + +1. **npm Package** + - https://www.npmjs.com/package/@cawpea/coderef + - New version is published + +2. **Git Tag** + - Format: `vX.Y.Z` (e.g., `v0.2.0`) + - Created on `main` branch + +3. **GitHub Release** + - Auto-generated release notes + - Changelog based on commits + +4. **CHANGELOG.md** + - New version entry is added + - Maintains Keep a Changelog format + +5. **Version Bump Commit** + - Commit message: `chore(release): X.Y.Z [skip ci]` + - `[skip ci]` prevents workflow from re-running on this commit + +## Verifying Releases + +After a release, verify the following: + +### 1. Check GitHub Actions + +- Visit https://github.com/cawpea/coderef/actions +- Verify Release workflow completed successfully +- Ensure all steps have green checkmarks + +### 2. Check npm Package + +```bash +# Check latest version +npm view @cawpea/coderef version + +# Test package installation +npm install @cawpea/coderef@latest +``` + +### 3. Check GitHub Release + +- Visit https://github.com/cawpea/coderef/releases +- Verify new release was created +- Verify release notes are correctly generated + +### 4. Check CHANGELOG.md + +- Review CHANGELOG.md in the repository +- Verify new version entry was added +- Verify changes are correctly documented + +### 5. Check Git Tags + +```bash +# List all tags +git tag -l + +# Check latest tag +git describe --tags --abbrev=0 +``` + +## Troubleshooting + +### Release Not Triggered + +**Symptom**: No release is created after merging to `main` branch + +**Causes and Solutions**: + +1. **No Releasable Commits** + - If only `docs:`, `chore:`, `ci:` commits exist, no version bump occurs + - Check semantic-release logs: "no release" message indicates this is normal + +2. **Quality Check Failure** + - Release is aborted if build, tests, lint, or type check fails + - Check GitHub Actions logs for errors and fix them + +3. **NPM_TOKEN Misconfiguration** + - Verify `NPM_TOKEN` is correctly set in GitHub Secrets + - Verify token has appropriate permissions (Automation token, Publish access) + +### npm Publication Failure + +**Symptom**: Release workflow succeeds but package is not published to npm + +**Causes and Solutions**: + +1. **Authentication Error** + - Check NPM_TOKEN expiration + - Regenerate token and update GitHub Secrets + +2. **Package Name Conflict** + - Verify package with same name doesn't already exist on npm + - For scoped packages (`@cawpea/coderef`), verify `publishConfig.access: "public"` is set + +3. **Network Error** + - May be a temporary error + - Manually re-run the workflow + +### GitHub Release Creation Failure + +**Symptom**: Package is published to npm but GitHub Release is not created + +**Causes and Solutions**: + +1. **Insufficient Permissions** + - Check `permissions` settings in Release workflow + - Verify `contents: write` is set + +2. **GITHUB_TOKEN Issue** + - Verify `GITHUB_TOKEN` is correctly set in workflow + - If `persist-credentials: false`, additional token configuration may be needed + +### Manual Execution Doesn't Release + +**Symptom**: Manual execution shows "no release" + +**Causes and Solutions**: + +1. **Branch Verification** + - Verify you're not running from a branch other than `main` + - Check `branches` configuration in `.releaserc.json` + +2. **Commit History Verification** + - Verify there are releasable commits (`feat:`, `fix:`, etc.) since last release + +## Rollback Procedures + +Procedures for rolling back a release when issues occur. + +### Unpublish npm Package + +**Warning**: npm allows unpublish within 72 hours of publication. + +```bash +# Unpublish package version (within 72 hours) +npm unpublish @cawpea/coderef@X.Y.Z + +# Or deprecate package (after 72 hours) +npm deprecate @cawpea/coderef@X.Y.Z "This version has critical issues. Please use vX.Y.Z-1 instead." +``` + +### Delete GitHub Release + +1. **Delete from GitHub UI** + - Visit https://github.com/cawpea/coderef/releases + - Open the relevant release + - Click "Delete" button + +2. **Delete from CLI** + ```bash + gh release delete vX.Y.Z + ``` + +### Delete Git Tag + +```bash +# Delete local tag +git tag -d vX.Y.Z + +# Delete remote tag +git push --delete origin vX.Y.Z +``` + +### Revert Release Commit + +```bash +# Switch to main branch +git checkout main +git pull origin main + +# Identify release commit +git log --oneline | grep "chore(release)" + +# Revert release commit (undo with new commit) +git revert + +# Push to remote +git push origin main +``` + +**Warning**: Use `git revert` to preserve history while undoing changes. Avoid `git reset --hard` on shared repositories as it rewrites history. + +### Emergency Patch Release + +Steps to release a fixed version after rolling back a problematic release: + +1. **Create Fix Branch** + + ```bash + git checkout -b hotfix/critical-fix + ``` + +2. **Fix the Issue** + + ```bash + # Make fixes + git add . + git commit -m "fix: resolve critical issue in vX.Y.Z" + ``` + +3. **Create PR and Merge** + - Follow normal release process + - PATCH version will automatically increment due to `fix:` commit + +## Best Practices + +### Commit Messages + +- Strictly follow Conventional Commits specification +- Use scopes to clarify where changes were made + ```bash + feat(cli): add --verbose option + fix(parser): handle edge case in CODE_REF + ``` + +### Pre-Release Verification + +- Test thoroughly locally +- Verify CI passes before merging +- Update documentation for breaking changes + +### Post-Release Verification + +- Verify publication on npm package page +- Review release notes on GitHub Releases +- Manually supplement release notes if needed + +### Version Management + +- Follow Semantic Versioning +- Before 1.0.0 (0.x.x), MINOR versions may include breaking changes +- From 1.0.0 onwards, breaking changes must increment MAJOR version + +## References + +- [semantic-release Documentation](https://semantic-release.gitbook.io/) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [Semantic Versioning](https://semver.org/) +- [Keep a Changelog](https://keepachangelog.com/) +- [Git Conventions](./git-conventions.md) +- [npm Documentation](https://docs.npmjs.com/) diff --git a/docs/development/testing-guide.md b/docs/development/testing-guide.md new file mode 100644 index 0000000..68f16ab --- /dev/null +++ b/docs/development/testing-guide.md @@ -0,0 +1,29 @@ +# Testing Guide + +## Running Tests + +```bash +npm test # Run all tests +npm run test:watch # Run tests in watch mode +npm run test:coverage # Run tests with coverage report +``` + +## Running Specific Tests + +Run a specific test file: + +```bash +npx jest src/utils/foo.test.ts # Run specific test file +npx jest -t "" # Run tests matching pattern +``` + +## Coverage Requirements + +Coverage thresholds are set to 80% for all metrics (branches, functions, lines, statements). CLI code is excluded from coverage as it's verified through integration tests. + +## Writing Tests + +- Test files should be co-located with source files as `**/*.test.ts` +- Use the path alias `@/` which maps to `src/` +- Tests run in Node.js environment using ts-jest preset +- Follow existing test patterns in the codebase diff --git a/docs/user-guide/cli-usage.md b/docs/user-guide/cli-usage.md new file mode 100644 index 0000000..6cb0cff --- /dev/null +++ b/docs/user-guide/cli-usage.md @@ -0,0 +1,91 @@ +# CLI Usage + +## Validate Command + +### Basic Usage + +Validate all CODE_REF references in your documentation: + +```bash +npx coderef validate +``` + +### Validate Specific Files or Directories + +You can specify files or directories to validate: + +```bash +# Validate a specific file +npx coderef validate docs/README.md + +# Validate a specific directory +npx coderef validate docs/backend/ + +# Validate multiple files/directories +npx coderef validate docs/README.md docs/api/ +``` + +### Options + +- `--verbose`, `-v`: Display detailed output including reference counts per file + +```bash +npx coderef validate --verbose +``` + +## Fix Command + +### Basic Usage + +Fix errors interactively with colored diffs: + +```bash +npx coderef fix +``` + +This will prompt you for each error with options to apply the fix or skip. + +### Options + +- `--auto`: Automatically apply all fixes without prompting for confirmation + +```bash +npx coderef fix --auto +``` + +- `--dry-run`: Simulate fixes without making actual changes (useful for testing) + +```bash +npx coderef fix --dry-run +``` + +- `--backup`: Create backup files before applying fixes (default: no backup) + +```bash +npx coderef fix --backup +``` + +- `--verbose`, `-v`: Display detailed output during the fix process + +```bash +npx coderef fix --verbose +``` + +### Combining Options + +You can combine multiple options: + +```bash +# Auto-fix with backup +npx coderef fix --auto --backup + +# Dry run with verbose output +npx coderef fix --dry-run --verbose +``` + +## Exit Codes + +Both commands return appropriate exit codes: + +- `0`: Success (no errors found or all fixes applied) +- `1`: Errors found (validate) or fixes failed (fix) diff --git a/docs/user-guide/code-ref-syntax.md b/docs/user-guide/code-ref-syntax.md new file mode 100644 index 0000000..2ac3c2b --- /dev/null +++ b/docs/user-guide/code-ref-syntax.md @@ -0,0 +1,357 @@ +# CODE_REF Syntax + +## Overview + +CODE_REF comments allow you to reference code in your documentation and keep it synchronized with your actual source code. + +## Reference by Line Numbers + +Reference code by specifying line ranges: + +```markdown + +``` + +This will extract lines 10-20 from `src/index.ts`. + +## Reference by Symbol Name + +Reference code by function name: + +```markdown + +``` + +This will find and extract the `myFunction` function from `src/index.ts` using AST parsing. + +## Reference Variables + +Reference variables (const, let, var): + +```markdown + +``` + +This will find and extract the `API_KEY` variable from `src/config.ts` using AST parsing. Supports: + +- `const`, `let`, and `var` declarations +- Exported variables +- Destructuring patterns (object, array, rest syntax) +- Multiple declarators in a single statement + +## Reference Class Methods + +Reference specific class methods: + +```markdown + +``` + +This will find and extract the `myMethod` method from the `MyClass` class in `src/MyClass.ts`. + +## Examples + +### Example 1: Line-based Reference + +Suppose you have a file `src/utils/helper.ts` with the following content: + +```typescript +// src/utils/helper.ts +export function greet(name: string): string { + return `Hello, ${name}!`; +} + +export function farewell(name: string): string { + return `Goodbye, ${name}!`; +} +``` + +In your markdown documentation, you can reference the `greet` function by line numbers: + +````markdown +Here's our greeting function: + + + +```typescript +export function greet(name: string): string { + return `Hello, ${name}!`; +} +``` +```` + +The tool will verify that lines 2-4 match the code block below the comment. + +### Example 2: Symbol Reference + +Using the same file, you can reference by function name instead: + +````markdown +Here's our greeting function: + + + +```typescript +export function greet(name: string): string { + return `Hello, ${name}!`; +} +``` +```` + +The tool will use AST parsing to find the `greet` function, regardless of which line it's on. + +### Example 3: Class Method Reference + +Suppose you have a class in `src/models/User.ts`: + +```typescript +// src/models/User.ts +export class User { + constructor(private name: string) {} + + getName(): string { + return this.name; + } + + setName(name: string): void { + this.name = name; + } +} +``` + +You can reference the `getName` method specifically: + +````markdown +The `getName` method returns the user's name: + + + +```typescript +getName(): string { + return this.name; +} +``` +```` + +### Example 4: Variable Reference + +Suppose you have a configuration file `src/config.ts`: + +```typescript +// src/config.ts +/** + * API endpoint URL + */ +export const API_ENDPOINT = 'https://api.example.com'; + +/** + * Maximum retry attempts + */ +export const MAX_RETRIES = 3; + +// Environment variables with destructuring +const { API_KEY, SECRET_KEY } = process.env; +``` + +You can reference variables: + +````markdown +Here's our API endpoint configuration: + + + +```typescript +/** + * API endpoint URL + */ +export const API_ENDPOINT = 'https://api.example.com'; +``` + +Here's a destructured variable: + + + +```typescript +const { API_KEY, SECRET_KEY } = process.env; +``` +```` + +Note: When referencing a destructured variable, the entire destructuring statement is extracted. + +### Example 5: Multiple References in One Document + +You can have multiple CODE_REF comments in a single markdown file: + +````markdown +# API Documentation + +## Authentication + + + +```typescript +export async function authenticate(username: string, password: string) { + // authentication logic +} +``` + +## User Management + + + +```typescript +getName(): string { + return this.name; +} +``` +```` + +## Best Practices + +- Use line-based references for stable code sections +- Use symbol references (functions/variables) for code that may move around in the file +- Use variable references for configuration constants and important declarations +- Keep referenced code blocks focused and readable +- Update references when refactoring code +- Always include the code block immediately after the CODE_REF comment + +## Symbol Resolution Priority + +When a symbol name matches both a function and a variable, the function takes precedence: + +```typescript +const config = { port: 3000 }; // This won't be referenced +function config() { + /* ... */ +} // This will be referenced +``` + +To reference the variable in this case, consider renaming one of them or using line-based references. + +## Known Limitations + +### Variable Reference Limitations + +The variable reference feature has the following limitations: + +#### 1. Nested Destructuring Not Supported + +Nested destructuring patterns are not currently supported: + +❌ **Not Supported:** + +```typescript +const { + a: { + b: { c }, + }, +} = obj; +``` + +✅ **Workaround:** Use line-based references for nested destructuring: + +```markdown + +``` + +#### 2. Renamed Destructured Properties Not Supported + +Property renaming in destructuring is not supported: + +❌ **Not Supported:** + +```typescript +const { originalName: renamedVar } = obj; +``` + +Attempting to reference `renamedVar` will not work. + +✅ **Workaround:** Use line-based references or refactor to use the original property name. + +#### 3. Only Top-Level Variables Are Searchable + +Variables declared inside functions, blocks, or other scopes cannot be referenced by symbol: + +❌ **Not Supported:** + +```typescript +function myFunction() { + const localVar = 'value'; // Cannot be referenced +} + +if (condition) { + const blockVar = 'value'; // Cannot be referenced +} +``` + +✅ **Supported:** + +```typescript +const topLevelVar = 'value'; // Can be referenced + +export const exportedVar = 'value'; // Can be referenced +``` + +✅ **Workaround:** For non-top-level variables, use line-based references. + +### General Limitations + +- Symbol references only work with TypeScript/JavaScript files (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`) +- For other file types, use line-based references +- CODE_REF comments must be immediately followed by a code block (empty lines allowed) + +## Common Mistakes + +### Missing Code Block + +❌ **Wrong:** + +```markdown + + +Some other content here... +``` + +✅ **Correct:** + +````markdown + + +```typescript +export function myFunction() { + // ... +} +``` +```` + +### Incorrect Path + +❌ **Wrong:** + +```markdown + +``` + +✅ **Correct:** + +```markdown + +``` + +Always use paths relative to your project root. + +## Validation and Fixing + +After adding CODE_REF comments, run validation: + +```bash +npx coderef validate +``` + +If there are mismatches, fix them interactively: + +```bash +npx coderef fix +``` + +For implementation details, see [CODE_REF Syntax Implementation](../architecture/code-ref-syntax.md). diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md new file mode 100644 index 0000000..b8e29fb --- /dev/null +++ b/docs/user-guide/configuration.md @@ -0,0 +1,284 @@ +# Configuration + +## Configuration File + +Create `.coderefrc.json` in your project root: + +```json +{ + "projectRoot": ".", + "docsDir": "docs", + "ignoreFile": ".docsignore", + "ignorePatterns": ["**/*.draft.md"], + "verbose": false +} +``` + +## Configuration Sources + +The tool loads configuration from multiple sources with the following precedence (highest to lowest): + +1. **Programmatic options** - Passed directly to the API +2. **Environment variables** - `CODEREF_*` prefixed variables +3. **`.coderefrc.json`** - Configuration file in project root +4. **`package.json`** - `"coderef"` field +5. **Default values** - Built-in defaults + +## Configuration Options + +### Base Configuration (CodeRefConfig) + +#### `projectRoot` + +- **Type**: `string` +- **Default**: `process.cwd()` (current working directory) +- **Description**: Root directory of your project. All paths are resolved relative to this directory. + +```json +{ + "projectRoot": "." +} +``` + +#### `docsDir` + +- **Type**: `string` +- **Default**: `"docs"` +- **Description**: Directory containing your documentation files, relative to `projectRoot`. + +```json +{ + "docsDir": "documentation" +} +``` + +#### `ignoreFile` + +- **Type**: `string` (optional) +- **Default**: `".docsignore"` +- **Description**: Path to ignore file relative to `projectRoot`. The file follows `.gitignore` syntax. + +```json +{ + "ignoreFile": ".docsignore" +} +``` + +#### `ignorePatterns` + +- **Type**: `string[]` (optional) +- **Default**: `undefined` +- **Description**: Additional glob patterns to ignore, complementing the ignore file. + +```json +{ + "ignorePatterns": ["**/*.draft.md", "**/temp/**", "**/*.backup.md"] +} +``` + +#### `verbose` + +- **Type**: `boolean` (optional) +- **Default**: `false` +- **Description**: Enable verbose logging for detailed output. + +```json +{ + "verbose": true +} +``` + +#### `targets` + +- **Type**: `string[]` (optional) +- **Default**: `undefined` +- **Description**: Specific files or directories to validate, relative to `docsDir`. If not specified, all markdown files in `docsDir` are validated. + +```json +{ + "targets": ["README.md", "guides/"] +} +``` + +### Fix Command Configuration (CodeRefFixConfig) + +The `fix` command extends the base configuration with additional options: + +#### `dryRun` + +- **Type**: `boolean` (optional) +- **Default**: `false` +- **Description**: Show what would be fixed without modifying files (simulation mode). + +```json +{ + "dryRun": true +} +``` + +#### `auto` + +- **Type**: `boolean` (optional) +- **Default**: `false` +- **Description**: Automatically apply all fixes without prompting for confirmation. + +```json +{ + "auto": true +} +``` + +#### `backup` + +- **Type**: `boolean` (optional) +- **Default**: `true` +- **Description**: Create backup files (`.backup` extension) before applying fixes. + +```json +{ + "backup": false +} +``` + +## Configuration Examples + +### Basic Configuration + +Minimal configuration for a standard project: + +```json +{ + "projectRoot": ".", + "docsDir": "docs" +} +``` + +### Advanced Configuration + +Configuration with custom ignore patterns and verbose output: + +```json +{ + "projectRoot": ".", + "docsDir": "documentation", + "ignoreFile": ".docsignore", + "ignorePatterns": ["**/*.draft.md", "**/archive/**", "**/_*.md"], + "verbose": true +} +``` + +### Monorepo Configuration + +Configuration for a monorepo with multiple documentation directories: + +```json +{ + "projectRoot": "packages/my-package", + "docsDir": "docs", + "ignoreFile": "../../.docsignore" +} +``` + +## Environment Variables + +You can override configuration using environment variables: + +```bash +# Set project root +export CODEREF_PROJECT_ROOT=/path/to/project + +# Set docs directory +export CODEREF_DOCS_DIR=documentation + +# Set ignore file +export CODEREF_IGNORE_FILE=.customignore + +# Enable verbose mode +export CODEREF_VERBOSE=true + +# Run validation +npx coderef validate +``` + +## package.json Configuration + +Alternatively, you can define configuration in `package.json`: + +```json +{ + "name": "my-package", + "version": "1.0.0", + "coderef": { + "docsDir": "documentation", + "verbose": true + } +} +``` + +## Ignore Files + +### .docsignore Syntax + +The `.docsignore` file follows the same syntax as `.gitignore`: + +``` +# Ignore draft files +**/*.draft.md + +# Ignore temporary directories +**/temp/ +**/tmp/ + +# Ignore specific files +notes.md +TODO.md + +# Negative patterns (don't ignore) +!important.draft.md +``` + +### Common Ignore Patterns + +``` +# Build artifacts +**/dist/ +**/build/ + +# Temporary files +**/*.tmp +**/*.backup + +# Version control +**/.git/ + +# Node modules +**/node_modules/ + +# Editor files +**/.vscode/ +**/.idea/ +``` + +## Programmatic Usage + +You can also configure the tool programmatically when using the API: + +```typescript +import { validate, fix } from '@cawpea/coderef'; + +// Validate with custom configuration +await validate({ + projectRoot: '.', + docsDir: 'docs', + verbose: true, + ignorePatterns: ['**/*.draft.md'], +}); + +// Fix with custom configuration +await fix({ + projectRoot: '.', + docsDir: 'docs', + auto: true, + backup: true, +}); +``` diff --git a/docs/user-guide/installation.md b/docs/user-guide/installation.md new file mode 100644 index 0000000..f2dea68 --- /dev/null +++ b/docs/user-guide/installation.md @@ -0,0 +1,22 @@ +# Installation + +## Requirements + +- **Minimum Node.js version**: 22.0.0 +- **Recommended version**: See `.node-version` file in the project root + +## Install the Package + +```bash +npm install --save-dev @cawpea/coderef +``` + +You can also use yarn or pnpm: + +```bash +# yarn +yarn add --dev @cawpea/coderef + +# pnpm +pnpm add --save-dev @cawpea/coderef +``` diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..57506d1 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,184 @@ +// @ts-check +const eslint = require('@eslint/js'); +const tseslint = require('typescript-eslint'); +const prettierConfig = require('eslint-config-prettier'); + +module.exports = tseslint.config( + // Base recommended configs + eslint.configs.recommended, + + // Global ignores + { + ignores: [ + 'dist/**', + 'node_modules/**', + 'coverage/**', + '*.js', // Ignore JS files at root (configs) + 'bin/**/*.js', // CLI wrapper is plain JS + 'jest.config.js', + ], + }, + + // TypeScript files configuration (non-test files with type checking) + { + files: ['src/**/*.ts'], + ignores: ['src/**/*.test.ts'], // Exclude test files from type checking + extends: [...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + // TypeScript-specific rules + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-non-null-assertion': 'warn', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + }, + ], + + // Relax strict type-checking rules for existing codebase + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'warn', + '@typescript-eslint/prefer-optional-chain': 'warn', + '@typescript-eslint/await-thenable': 'warn', + + // General code quality + 'no-console': 'off', // CLI tool needs console + 'no-debugger': 'error', + 'no-alert': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + eqeqeq: ['error', 'always', { null: 'ignore' }], + curly: ['error', 'all'], + + // Code style (supplementing Prettier) + 'prefer-arrow-callback': 'error', + 'prefer-template': 'error', + 'object-shorthand': ['error', 'always'], + 'no-useless-concat': 'error', + }, + }, + + // Test files configuration (basic linting without type checking) + { + files: ['src/**/*.test.ts'], + extends: [...tseslint.configs.recommended], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + '@typescript-eslint/no-empty-function': 'off', // Test mocks often use empty functions + '@typescript-eslint/no-explicit-any': 'off', // Tests can use any for simplicity + }, + }, + + // Scripts configuration (non-test files with type checking) + { + files: ['scripts/**/*.ts'], + ignores: ['scripts/**/*.test.ts'], + extends: [...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: './tsconfig.scripts.json', + tsconfigRootDir: __dirname, + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + // TypeScript-specific rules + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-non-null-assertion': 'warn', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + }, + ], + + // Relax strict type-checking rules for existing codebase + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'warn', + '@typescript-eslint/prefer-optional-chain': 'warn', + '@typescript-eslint/await-thenable': 'warn', + + // General code quality + 'no-console': 'off', // Scripts need console output + 'no-debugger': 'error', + 'no-alert': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + eqeqeq: ['error', 'always', { null: 'ignore' }], + curly: ['error', 'all'], + + // Code style (supplementing Prettier) + 'prefer-arrow-callback': 'error', + 'prefer-template': 'error', + 'object-shorthand': ['error', 'always'], + 'no-useless-concat': 'error', + }, + }, + + // Scripts test files configuration (basic linting without type checking) + { + files: ['scripts/**/*.test.ts'], + extends: [...tseslint.configs.recommended], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + '@typescript-eslint/no-empty-function': 'off', // Test mocks often use empty functions + '@typescript-eslint/no-explicit-any': 'off', // Tests can use any for simplicity + }, + }, + + // Prettier integration (must be last to disable conflicting rules) + prettierConfig +); diff --git a/jest.config.js b/jest.config.js index 258bc83..1b8cdcc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,12 +2,17 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - roots: ['/test'], + roots: ['/src', '/scripts'], testMatch: ['**/*.test.ts'], + setupFilesAfterEnv: ['/jest.setup.js'], collectCoverageFrom: [ 'src/**/*.ts', + 'scripts/**/*.ts', '!src/**/*.d.ts', + '!src/**/*.test.ts', '!src/cli/**/*.ts', // CLI はカバレッジから除外(統合テストで検証) + '!scripts/**/*.test.ts', + '!scripts/lib/types.ts', // 型定義のみのファイルは除外 ], coverageThreshold: { global: { diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..b839eea --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,11 @@ +/** + * Jest setup file to configure test environment + */ + +// Disable chalk colors in tests to make string matching easier +process.env.NO_COLOR = '1'; +process.env.FORCE_COLOR = '0'; + +// Force chalk to disable colors by setting level to 0 +const chalk = require('chalk'); +chalk.level = 0; diff --git a/package-lock.json b/package-lock.json index b3ae194..7088998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,23 +9,36 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "^8.50.1" + "@typescript-eslint/typescript-estree": "^8.50.1", + "chalk": "^4.1.2", + "commander": "^14.0.2" }, "bin": { "coderef": "bin/coderef.js" }, "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^11.1.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^9.2.6", + "@semantic-release/npm": "^11.0.2", + "@semantic-release/release-notes-generator": "^12.1.0", "@types/jest": "^30.0.0", "@types/node": "^24.10.1", "@typescript-eslint/eslint-plugin": "^8.18.1", "@typescript-eslint/parser": "^8.18.1", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", + "husky": "^9.1.7", "jest": "^30.2.0", + "lint-staged": "^16.2.7", "prettier": "^3.4.2", + "semantic-release": "^23.0.0", "ts-jest": "^29.4.5", "tsup": "^8.0.0", - "typescript": "^5.9.3" + "tsx": "^4.19.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.1" }, "engines": { "node": ">=16.0.0" @@ -547,1601 +560,1593 @@ "dev": true, "license": "MIT" }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">=0.1.90" } }, - "node_modules/@esbuild/aix-ppc64": { + "node_modules/@esbuild/darwin-arm64": { "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "aix" + "darwin" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "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/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], + "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", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], + "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==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array/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": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=18" + "node": "*" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], + "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==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ajv": "^6.12.4", + "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.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], + "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==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], + "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==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">= 4" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/eslintrc/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": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=18" + "node": "*" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ - "ppc64" - ], + "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==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], + "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==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], + "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", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], + "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", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], + "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", - "optional": true, - "os": [ - "netbsd" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], + "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", - "optional": true, - "os": [ - "netbsd" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], + "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": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "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": ">=18" + "node": ">=12" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], + "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": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "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": ">=18" + "node": ">=8" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], + "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", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], + "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", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], + "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", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], + "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", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", - "cpu": [ - "x64" - ], + "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", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=18" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "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": { - "eslint-visitor-keys": "^3.4.3" + "p-limit": "^2.2.0" }, "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" + "node": ">=8" } }, - "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/@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": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "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": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=8" } }, - "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/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.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.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": "*" + "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/@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/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", "dependencies": { - "@eslint/core": "^0.17.0" + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "expect": "30.2.0", + "jest-snapshot": "30.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "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.2", - "strip-json-comments": "^3.1.1" + "@jest/get-type": "30.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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/@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": ">= 4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, "engines": { - "node": "*" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "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", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.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.3.10", + "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.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "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/@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/@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": "Apache-2.0", + "license": "MIT", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, "engines": { - "node": ">=18.18.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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/@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": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">=18.18.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.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==", + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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==", + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "@babel/core": "^7.27.4", + "@jest/types": "30.2.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.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" }, "engines": { - "node": ">=12" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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==", + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "@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": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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==", + "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": { - "sprintf-js": "~1.0.2" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "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==", + "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": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "@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": ">=8" + "node": ">=6.0.0" } }, - "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==", + "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": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "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==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=8" + "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==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "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==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=8" + "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==", + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 18" } }, - "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==", + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" } }, - "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.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.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0" + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.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": ">= 18" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "MIT" }, - "node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" + "@octokit/types": "^12.6.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" } }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@octokit/openapi-types": "^20.0.0" } }, - "node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "node_modules/@octokit/plugin-retry": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0" + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" } }, - "node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "node_modules/@octokit/plugin-throttling": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.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==", + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "dependencies": { + "@octokit/openapi-types": "^20.0.0" } }, - "node_modules/@jest/globals": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" } }, - "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==", + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18" } }, - "node_modules/@jest/reporters": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "dev": true, "license": "MIT", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.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.3.10", - "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.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, + "@octokit/openapi-types": "^24.2.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": "^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": ">=14" } }, - "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==", + "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": { - "@sinclair/typebox": "^0.34.0" + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.22.0" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "graceful-fs": "4.2.10" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.22.0" } }, - "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==", + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" } }, - "node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", + "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "node_modules/@semantic-release/commit-analyzer": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-11.1.0.tgz", + "integrity": "sha512-cXNTbv3nXR2hlzHjAMgbuiQVtvWHTlwwISt60B+4NZv01y/QRY7p2HcJm8Eh2StzcTJoNnflvKjHH/cjFS7d5g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "import-from-esm": "^1.0.3", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.17 || >=20.6.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.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.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "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": ">=14.17" } }, - "node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", "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" + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" } }, - "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==", + "node_modules/@semantic-release/github": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.6.tgz", + "integrity": "sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^6.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "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==", + "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@semantic-release/github/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", "dev": true, "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@semantic-release/github/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "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==", + "node_modules/@semantic-release/github/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@semantic-release/npm": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-11.0.3.tgz", + "integrity": "sha512-KUsozQGhRBAnoVg4UMZj9ep436VEGwT536/jwSqB7vcEfA6oncCUU7UIYTRdLx7GvTtqn0kBjnkfLVkcnBa2YQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^8.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": "^18.17 || >=20" + }, + "peerDependencies": { + "semantic-release": ">=20.1.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==", + "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=14" + "node": ">=18" } }, - "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==", + "node_modules/@semantic-release/npm/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/pkgr" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", - "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", - "cpu": [ - "arm" - ], + "node_modules/@semantic-release/npm/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", - "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/npm/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", - "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", - "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", - "cpu": [ - "x64" - ], + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", - "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", - "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", - "cpu": [ - "x64" - ], + "node_modules/@semantic-release/npm/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", - "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", - "cpu": [ - "arm" - ], + "node_modules/@semantic-release/npm/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", - "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", - "cpu": [ - "arm" - ], + "node_modules/@semantic-release/npm/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", - "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", - "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", - "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", - "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", - "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", - "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", - "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", - "cpu": [ - "s390x" - ], + "node_modules/@semantic-release/npm/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", - "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", - "cpu": [ - "x64" - ], + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", - "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", - "cpu": [ - "x64" - ], + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", - "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/release-notes-generator": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-12.1.0.tgz", + "integrity": "sha512-g6M9AjUKAZUZnxaJZnouNBeDNTCUrJ5Ltj+VJ60gJeDaRRahcHsry9HW8yKrnKkKNkx5lbWiEP1FPMqVNQz8Kg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "dependencies": { + "conventional-changelog-angular": "^7.0.0", + "conventional-changelog-writer": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^1.0.3", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-pkg-up": "^11.0.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", - "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", - "cpu": [ - "arm64" - ], + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", - "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", - "cpu": [ - "ia32" - ], + "node_modules/@sinclair/typebox": { + "version": "0.34.45", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.45.tgz", + "integrity": "sha512-qJcFVfCa5jxBFSuv7S5WYbA8XdeCPmhnaVVfX/2Y6L8WYg8sk3XY2+6W0zH+3mq1Cz+YC7Ki66HfqX6IHAwnkg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", - "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", - "cpu": [ - "x64" - ], + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", - "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", - "cpu": [ - "x64" - ], + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.45", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.45.tgz", - "integrity": "sha512-qJcFVfCa5jxBFSuv7S5WYbA8XdeCPmhnaVVfX/2Y6L8WYg8sk3XY2+6W0zH+3mq1Cz+YC7Ki66HfqX6IHAwnkg==", - "dev": true, - "license": "MIT" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/@sinonjs/commons": { "version": "3.0.1", @@ -2163,17 +2168,6 @@ "@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", @@ -2281,6 +2275,13 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2539,34 +2540,6 @@ "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", @@ -2581,272 +2554,69 @@ "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" - ], + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "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" - ], + "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", - "optional": true, - "os": [ - "freebsd" - ] + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "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" - ], + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 14" + } }, - "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" - ], + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "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" - ], + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "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.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "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.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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" - } + "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", @@ -2881,7 +2651,6 @@ "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" @@ -2921,6 +2690,20 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, "node_modules/babel-jest": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", @@ -3036,6 +2819,20 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -3193,7 +2990,6 @@ "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", @@ -3255,22 +3051,55 @@ "dev": true, "license": "MIT" }, - "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==", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" }, "engines": { - "node": ">=12" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { + "node_modules/cli-highlight/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==", @@ -3280,14 +3109,26 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/emoji-regex": { + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/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": { + "node_modules/cli-highlight/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==", @@ -3302,7 +3143,7 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/strip-ansi": { + "node_modules/cli-highlight/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==", @@ -3315,7 +3156,7 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/wrap-ansi": { + "node_modules/cli-highlight/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==", @@ -3333,2628 +3174,8127 @@ "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==", + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": ">=10" } }, - "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==", + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": ">=10" + } }, - "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==", + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "string-width": "^4.2.0" }, "engines": { - "node": ">=7.0.0" + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.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": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/cli-table3/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": ">= 6" + "node": ">=8" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/cli-table3/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/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "node_modules/cli-table3/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": "^14.18.0 || >=16.10.0" + "node": ">=8" } }, - "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==", + "node_modules/cli-table3/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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=20" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "dev": true, "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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": "MIT", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "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==", + "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/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "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/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "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==", + "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", - "engines": { - "node": ">=12" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "engines": { + "node": ">=8" } }, - "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==", + "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": { - "is-arrayish": "^0.2.1" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "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, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=18" + "node": ">=10" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "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": { - "node": ">=6" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "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==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=20" } }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@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.12.4", - "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.2", - "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 - } + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", - "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "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/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "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==", + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "compare-func": "^2.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.1.tgz", + "integrity": "sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^4.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^12.0.1", + "semver": "^7.5.2", + "split2": "^4.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "bin": { + "conventional-changelog-writer": "cli.mjs" + }, + "engines": { + "node": ">=16" } }, - "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==", + "node_modules/conventional-commits-filter": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz", + "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16" } }, - "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==", + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" } }, - "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==", + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, "engines": { - "node": ">= 4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": "*" + "node": ">= 8" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "type-fest": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "license": "Apache-2.0", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "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": { + "ms": "^2.1.3" }, "engines": { - "node": ">=4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, - "engines": { - "node": ">=0.10" + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "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==", + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=4.0.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==", + "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": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } + "license": "MIT" }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "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": "BSD-2-Clause", + "license": "MIT", "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==", + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "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", - "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": ">=8" } }, - "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==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "is-obj": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "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==", + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "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/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==", + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "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/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true, "license": "MIT" }, - "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==", + "node_modules/env-ci": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.2.0.tgz", + "integrity": "sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.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==", + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-up": { + "node_modules/env-ci/node_modules/human-signals": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "license": "MIT", - "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" + "engines": { + "node": ">=12" + }, + "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==", + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "path-key": "^4.0.0" }, "engines": { - "node": ">=16" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=14" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "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": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">=6" } }, - "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==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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": "ISC", + "license": "MIT", "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" - }, + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", "bin": { - "glob": "dist/esm/bin.mjs" + "esbuild": "bin/esbuild" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, - "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==", + "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": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, + "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">=6" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "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": ">=18" + "node": ">=10" }, "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/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@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.12.4", + "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.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" }, "bin": { - "handlebars": "bin/handlebars" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=0.4.7" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "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==", + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "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==", + "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": "Apache-2.0", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=10.17.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "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", + "license": "Apache-2.0", "engines": { - "node": ">= 4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "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==", + "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": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "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": ">=0.8.19" + "node": ">= 4" } }, - "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.", + "node_modules/eslint/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", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "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/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "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": "MIT" + "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/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "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==", + "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==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "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==", + "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": "MIT", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "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/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": { - "is-extglob": "^2.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "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": ">=0.12.0" + "node": ">=4.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/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/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "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": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "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/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/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": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "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/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "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" + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": ">=10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", "dev": true, - "license": "BSD-3-Clause", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "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-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">=10" + "node": ">=8.6.0" } }, - "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/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "BSD-3-Clause", + "license": "ISC", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "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/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": "BSD-3-Clause", + "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/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" + "reusify": "^1.0.4" } }, - "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/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": "BlueOak-1.0.0", + "license": "Apache-2.0", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "bser": "2.1.1" } }, - "node_modules/jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", - "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.2.0", - "@jest/types": "30.2.0", - "import-local": "^3.2.0", - "jest-cli": "30.2.0" - }, - "bin": { - "jest": "bin/jest.js" + "is-unicode-supported": "^2.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" + "node": ">=18" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-changed-files": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", - "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "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": { - "execa": "^5.1.1", - "jest-util": "30.2.0", - "p-limit": "^3.1.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16.0.0" } }, - "node_modules/jest-circus": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", - "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "p-limit": "^3.1.0", - "pretty-format": "30.2.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-cli": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", - "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "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": { - "@jest/core": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" + "locate-path": "^6.0.0", + "path-exists": "^4.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" + "node": ">=10" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", - "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "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.2.0", - "@jest/types": "30.2.0", - "babel-jest": "30.2.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.2.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-runner": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.2.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" + "node": ">=18" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", "dev": true, "license": "MIT", "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" } }, - "node_modules/jest-each": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", - "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "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": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "jest-util": "30.2.0", - "pretty-format": "30.2.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16" } }, - "node_modules/jest-environment-node": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", - "dev": true, + "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/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "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/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/git-log-parser/node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "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/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "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/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/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==", + "dev": true, + "license": "ISC" + }, + "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/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "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-from-esm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz", + "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=16.20" + } + }, + "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/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-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==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "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/issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "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": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "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" + }, + "engines": { + "node": ">=10" + } + }, + "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": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "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-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.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-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "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-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "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.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.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 + } + } + }, + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.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.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.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-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "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, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.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.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.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-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.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-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "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.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.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-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=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-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "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/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "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, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "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/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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "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/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/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/listr2/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz", + "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "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/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/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/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-asynchronous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-asynchronous/-/make-asynchronous-1.0.1.tgz", + "integrity": "sha512-T9BPOmEOhp6SmV25SwLVcHK4E6JyG/coH3C6F1NjNXSziv/fd4GmsqMk8YR6qpPOswfaOCApSNkZv6fxoaYFcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-event": "^6.0.0", + "type-fest": "^4.6.0", + "web-worker": "1.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-asynchronous/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)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked-terminal/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "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/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "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, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=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/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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-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" + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz", + "integrity": "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.4.tgz", + "integrity": "sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.1", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.2.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.2.2", + "@npmcli/run-script": "^9.1.0", + "@sigstore/tuf": "^3.1.1", + "abbrev": "^3.0.1", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.4.1", + "ci-info": "^4.2.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.1.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.1", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.1", + "libnpmexec": "^9.0.1", + "libnpmfund": "^6.0.1", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.1", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.2.0", + "nopt": "^8.1.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.2", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^7.0.3", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.1.0", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.1", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": 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/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "inBundle": 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/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": 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/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.1", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.21", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": 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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": 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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tinyglobby": { + "version": "0.2.14", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": 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/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": 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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "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, + "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": ">= 0.8.0" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-event": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", + "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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" + }, + "engines": { + "node": ">=10" + }, + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "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", - "dependencies": { - "@jest/types": "30.2.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.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "node": ">=8" } }, - "node_modules/jest-leak-detector": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", - "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "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": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "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==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "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==", + "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" + }, + "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": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "node": ">=8.6" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "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==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10" } }, - "node_modules/jest-resolve": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "node_modules/jest-resolve-dependencies": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", - "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "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==", "dev": true, "license": "MIT", - "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jest-runner": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", - "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/environment": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.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.2.0", - "jest-haste-map": "30.2.0", - "jest-leak-detector": "30.2.0", - "jest-message-util": "30.2.0", - "jest-resolve": "30.2.0", - "jest-runtime": "30.2.0", - "jest-util": "30.2.0", - "jest-watcher": "30.2.0", - "jest-worker": "30.2.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "node_modules/jest-runtime": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", - "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/globals": "30.2.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "locate-path": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "node_modules/jest-snapshot": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", - "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "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.2.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "pretty-format": "30.2.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "p-limit": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "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/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=4" } }, - "node_modules/jest-validate": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", - "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" } }, - "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==", + "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", - "engines": { - "node": ">=10" + "dependencies": { + "find-up": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/jest-watcher": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", - "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.2.0", - "string-length": "^4.0.2" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "p-locate": "^4.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "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/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==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "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==", "dev": true, "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "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/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } }, - "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==", + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "lilconfig": "^3.1.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "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", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { - "jsesc": "bin/jsesc" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "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==", + "node_modules/pretty-format/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" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "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==", + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "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==", + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "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": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { "node": ">=6" } }, - "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/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, - "license": "MIT", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { - "json-buffer": "3.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" } }, - "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==", + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "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==", + "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/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/read-package-up/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", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=14" + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/read-pkg-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz", + "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==", + "deprecated": "Renamed to read-package-up", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/read-pkg-up/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": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/read-pkg/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", - "dependencies": { - "semver": "^7.5.3" - }, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "tmpl": "1.0.5" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "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/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@pnpm/npm-conf": "^2.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=14" } }, - "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==", + "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==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node": ">=8" + } + }, + "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, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "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, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=4" } }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "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", - "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "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/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" + "dependencies": { + "mimic-function": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/napi-postinstall" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "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==", + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, "license": "MIT" }, - "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==", + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", "dev": true, "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" } }, - "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/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "path-key": "^3.0.0" + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semantic-release": { + "version": "23.1.1", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-23.1.1.tgz", + "integrity": "sha512-qqJDBhbtHsjUEMsojWKGuL5lQFCJuPtiXKEIlFKyTzDDGTAE/oyvznaP8GeOr5PvcqBJ6LQz4JCENWPLeehSpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^12.0.0", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^10.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^13.0.0", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^7.0.0", + "import-from-esm": "^1.3.1", + "lodash-es": "^4.17.21", + "marked": "^12.0.0", + "marked-terminal": "^7.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" }, "engines": { - "node": ">=8" + "node": ">=20.8.1" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/semantic-release/node_modules/@octokit/auth-token": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 18" } }, - "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==", + "node_modules/semantic-release/node_modules/@octokit/core": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.6.tgz", + "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "wrappy": "1" + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" } }, - "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/semantic-release/node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@octokit/openapi-types": "^25.1.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/semantic-release/node_modules/@octokit/endpoint": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", "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" + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" } }, - "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==", + "node_modules/semantic-release/node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@octokit/openapi-types": "^25.1.0" } }, - "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==", + "node_modules/semantic-release/node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 18" } }, - "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/semantic-release/node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@octokit/openapi-types": "^25.1.0" } }, - "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/semantic-release/node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "dev": true, - "license": "BlueOak-1.0.0" + "license": "MIT" }, - "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/semantic-release/node_modules/@octokit/plugin-paginate-rest": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz", + "integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "@octokit/types": "^13.10.0" }, "engines": { - "node": ">=6" + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" } }, - "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/semantic-release/node_modules/@octokit/plugin-retry": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", + "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", "dev": true, "license": "MIT", "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" + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" }, "engines": { - "node": ">=8" + "node": ">= 18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@octokit/core": ">=6" } }, - "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/semantic-release/node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@octokit/openapi-types": "^25.1.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/semantic-release/node_modules/@octokit/plugin-throttling": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.6.1.tgz", + "integrity": "sha512-bt3EBUkeKUzDQXRCcFrR9SWVqlLFRRqcCrr6uAorWt6NXTyjMKqcGrFmXqJy9NCbnKgiIZ2OXWq04theFc76Jg==", "dev": true, "license": "MIT", + "dependencies": { + "@octokit/types": "^13.7.0", + "bottleneck": "^2.15.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.1.3" } }, - "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/semantic-release/node_modules/@octokit/request": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.4.tgz", + "integrity": "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==", "dev": true, "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "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/semantic-release/node_modules/@octokit/request-error": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "@octokit/types": "^14.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 18" } }, - "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==", - "dev": true, - "license": "ISC" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "node_modules/semantic-release/node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } }, - "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/semantic-release/node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/semantic-release/node_modules/@semantic-release/commit-analyzer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-12.0.0.tgz", + "integrity": "sha512-qG+md5gdes+xa8zP7lIo1fWE17zRdO8yMCaxh9lyL65TQleoSv8WHHOqRURfghTytUh+NpkSyBprQ5hrkxOKVQ==", "dev": true, "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "import-from-esm": "^1.0.3", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, "engines": { - "node": ">=8.6" + "node": ">=20.8.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "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/semantic-release/node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=18" } }, - "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==", + "node_modules/semantic-release/node_modules/@semantic-release/github": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-10.3.5.tgz", + "integrity": "sha512-svvRglGmvqvxjmDgkXhrjf0lC88oZowFhOfifTldbgX9Dzj0inEtMLaC+3/MkDEmxmaQjWmF5Q/0CMIvPNSVdQ==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "@octokit/core": "^6.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^9.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "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/semantic-release/node_modules/@semantic-release/npm": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.2.tgz", + "integrity": "sha512-+M9/Lb35IgnlUO6OSJ40Ie+hUsZLuph2fqXC/qrKn0fMvUU/jiCjpoL6zEm69vzcmaZJ8yNKtMBEKHWN49WBbQ==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.9.3", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "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/semantic-release/node_modules/@semantic-release/release-notes-generator": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-13.0.0.tgz", + "integrity": "sha512-LEeZWb340keMYuREMyxrODPXJJ0JOL8D/mCl74B4LdzbxhtXV2LrPN2QBEcGJrlQhoqLO0RhxQb6masHytKw+A==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "conventional-changelog-angular": "^7.0.0", + "conventional-changelog-writer": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^1.0.3", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-pkg-up": "^11.0.0" }, "engines": { - "node": ">=8" + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "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/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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/semantic-release/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "node_modules/semantic-release/node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "node_modules/semantic-release/node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/semantic-release/node_modules/clean-stack": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.3.0.tgz", + "integrity": "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "lilconfig": "^3.1.1" + "escape-string-regexp": "5.0.0" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "node": ">=14.16" }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/semantic-release/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "node_modules/semantic-release/node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" }, "engines": { - "node": ">=14" + "node": "^18.19.0 || >=20.5.0" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/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" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "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/semantic-release/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=6" + "node": ">=18.18.0" } }, - "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==", + "node_modules/semantic-release/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "node_modules/semantic-release/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">=18" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/semantic-release/node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", "dev": true, "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.17 || >=20.6.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/semantic-release/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "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==", + "node_modules/semantic-release/node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-from": { + "node_modules/semantic-release/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rollup": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", - "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "node_modules/semantic-release/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", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=8" + } + }, + "node_modules/semantic-release/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.54.0", - "@rollup/rollup-android-arm64": "4.54.0", - "@rollup/rollup-darwin-arm64": "4.54.0", - "@rollup/rollup-darwin-x64": "4.54.0", - "@rollup/rollup-freebsd-arm64": "4.54.0", - "@rollup/rollup-freebsd-x64": "4.54.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", - "@rollup/rollup-linux-arm-musleabihf": "4.54.0", - "@rollup/rollup-linux-arm64-gnu": "4.54.0", - "@rollup/rollup-linux-arm64-musl": "4.54.0", - "@rollup/rollup-linux-loong64-gnu": "4.54.0", - "@rollup/rollup-linux-ppc64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-musl": "4.54.0", - "@rollup/rollup-linux-s390x-gnu": "4.54.0", - "@rollup/rollup-linux-x64-gnu": "4.54.0", - "@rollup/rollup-linux-x64-musl": "4.54.0", - "@rollup/rollup-openharmony-arm64": "4.54.0", - "@rollup/rollup-win32-arm64-msvc": "4.54.0", - "@rollup/rollup-win32-ia32-msvc": "4.54.0", - "@rollup/rollup-win32-x64-gnu": "4.54.0", - "@rollup/rollup-win32-x64-msvc": "4.54.0", - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/semantic-release/node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -5967,6 +11307,35 @@ "node": ">=10" } }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6003,6 +11372,125 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6013,6 +11501,52 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6023,15 +11557,68 @@ "node": ">=0.10.0" } }, - "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==", + "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, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" } }, "node_modules/sprintf-js": { @@ -6064,6 +11651,37 @@ "node": ">=8" } }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6261,11 +11879,38 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/super-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.1.0.tgz", + "integrity": "sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-timeout": "^1.0.1", + "make-asynchronous": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "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==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -6274,6 +11919,23 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, "node_modules/synckit": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", @@ -6290,6 +11952,61 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6351,6 +12068,19 @@ "node": "*" } }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -6374,6 +12104,40 @@ "node": ">=0.8" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -6446,6 +12210,19 @@ "node": ">=8.0" } }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -6541,14 +12318,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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": "0BSD", - "optional": true - }, "node_modules/tsup": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", @@ -6622,6 +12391,26 @@ "node": ">= 12" } }, + "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, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6671,6 +12460,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.50.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.1.tgz", + "integrity": "sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.50.1", + "@typescript-eslint/parser": "8.50.1", + "@typescript-eslint/typescript-estree": "8.50.1", + "@typescript-eslint/utils": "8.50.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/ufo": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", @@ -6699,6 +12512,62 @@ "dev": true, "license": "MIT" }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -6775,6 +12644,23 @@ "punycode": "^2.1.0" } }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -6790,6 +12676,17 @@ "node": ">=10.12.0" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -6800,6 +12697,13 @@ "makeerror": "1.0.12" } }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6949,6 +12853,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6966,6 +12880,22 @@ "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==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -7052,6 +12982,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 7e30ba5..f33f0bb 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ }, "exports": { ".": { + "types": "./dist/index.d.ts", "import": "./dist/index.mjs", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" + "require": "./dist/index.js" }, "./package.json": "./package.json" }, @@ -34,36 +34,54 @@ "CHANGELOG.md" ], "scripts": { + "prepare": "husky install", "build": "tsup src/index.ts src/cli/validate.ts src/cli/fix.ts --format cjs,esm --dts --clean", "dev": "tsup src/index.ts --format cjs,esm --dts --watch", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", - "lint": "eslint src --ext .ts", - "lint:fix": "eslint src --ext .ts --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"src/**/*.ts\" \"*.{js,json,md}\"", + "format:check": "prettier --check \"src/**/*.ts\" \"*.{js,json,md}\"", "type-check": "tsc --noEmit", + "docs:validate": "tsx scripts/validate-docs.ts", "prepublishOnly": "npm run build && npm test" }, "engines": { - "node": ">=16.0.0" + "node": ">=22.0.0" + }, + "publishConfig": { + "access": "public" }, "dependencies": { - "@typescript-eslint/typescript-estree": "^8.50.1" + "@typescript-eslint/typescript-estree": "^8.50.1", + "chalk": "^4.1.2", + "commander": "^14.0.2" }, "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^11.1.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^9.2.6", + "@semantic-release/npm": "^11.0.2", + "@semantic-release/release-notes-generator": "^12.1.0", "@types/jest": "^30.0.0", "@types/node": "^24.10.1", "@typescript-eslint/eslint-plugin": "^8.18.1", "@typescript-eslint/parser": "^8.18.1", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", + "husky": "^9.1.7", "jest": "^30.2.0", + "lint-staged": "^16.2.7", "prettier": "^3.4.2", + "semantic-release": "^23.0.0", "ts-jest": "^29.4.5", "tsup": "^8.0.0", - "typescript": "^5.9.3" + "tsx": "^4.19.2", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.1" }, "repository": { "type": "git", @@ -74,5 +92,14 @@ }, "homepage": "https://github.com/cawpea/coderef#readme", "author": "cawpea", - "license": "MIT" + "license": "MIT", + "lint-staged": { + "*.ts": [ + "eslint --fix", + "prettier --write" + ], + "*.{js,json,md}": [ + "prettier --write" + ] + } } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..e6e44ca --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,34 @@ +/** @type {import("prettier").Config} */ +module.exports = { + // Match existing code style + semi: true, + singleQuote: true, + trailingComma: 'es5', + tabWidth: 2, + useTabs: false, + printWidth: 100, + endOfLine: 'lf', + + // Spacing preferences + arrowParens: 'always', + bracketSpacing: true, + bracketSameLine: false, + + // Override for specific file types + overrides: [ + { + files: '*.md', + options: { + printWidth: 80, + proseWrap: 'preserve', + }, + }, + { + files: '*.json', + options: { + printWidth: 80, + tabWidth: 2, + }, + }, + ], +}; diff --git a/scripts/lib/git-diff.test.ts b/scripts/lib/git-diff.test.ts new file mode 100644 index 0000000..fa707cd --- /dev/null +++ b/scripts/lib/git-diff.test.ts @@ -0,0 +1,308 @@ +import { spawnSync } from 'child_process'; +import { execGit, detectBaseBranch, getChangedFiles } from './git-diff'; + +// Mock child_process +jest.mock('child_process'); + +const mockedSpawnSync = spawnSync as jest.MockedFunction; + +describe('git-diff', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('execGit', () => { + it('should execute git command successfully', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'output\n', + stderr: '', + } as any); + + const result = execGit(['status']); + + expect(result).toBe('output'); + expect(mockedSpawnSync).toHaveBeenCalledWith('git', ['status'], { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + }); + + it('should trim whitespace from git command output', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: ' output \n', + stderr: '', + } as any); + + const result = execGit(['log']); + + expect(result).toBe('output'); + }); + + it('should throw error when git command fails', () => { + mockedSpawnSync.mockReturnValue({ + status: 1, + stdout: '', + stderr: 'Command failed', + } as any); + + expect(() => execGit(['invalid'])).toThrow('Git command failed'); + }); + + it('should handle multiple arguments', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'result', + stderr: '', + } as any); + + execGit(['diff', '--name-status', 'main...HEAD']); + + expect(mockedSpawnSync).toHaveBeenCalledWith( + 'git', + ['diff', '--name-status', 'main...HEAD'], + { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + } + ); + }); + }); + + describe('detectBaseBranch', () => { + it('should throw error when on main branch', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'main', stderr: '' } as any) // getCurrentBranch + .mockReturnValue({ status: 0, stdout: 'dummy', stderr: '' } as any); + + expect(() => detectBaseBranch()).toThrow( + 'Cannot validate from main branch. Please run this from a feature branch.' + ); + }); + + it('should throw error when on develop branch', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'develop', stderr: '' } as any) // getCurrentBranch + .mockReturnValue({ status: 0, stdout: 'dummy', stderr: '' } as any); + + expect(() => detectBaseBranch()).toThrow( + 'Cannot validate from develop branch. Please run this from a feature branch.' + ); + }); + + it('should throw error when neither main nor develop exists', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'Branch not found' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'Branch not found' } as any); // branchExists(develop) + + expect(() => detectBaseBranch()).toThrow('Neither main nor develop branch exists'); + }); + + it('should return develop when only develop exists', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'Branch not found' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any); // branchExists(develop) + + const result = detectBaseBranch(); + + expect(result).toBe('develop'); + }); + + it('should return main when only main exists', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'Branch not found' } as any); // branchExists(develop) + + const result = detectBaseBranch(); + + expect(result).toBe('main'); + }); + + it('should return develop when it has fewer commits', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(develop) + .mockReturnValueOnce({ status: 0, stdout: 'abc123', stderr: '' } as any) // merge-base main + .mockReturnValueOnce({ status: 0, stdout: 'def456', stderr: '' } as any) // merge-base develop + .mockReturnValueOnce({ status: 0, stdout: '5', stderr: '' } as any) // countCommits from main + .mockReturnValueOnce({ status: 0, stdout: '3', stderr: '' } as any); // countCommits from develop + + const result = detectBaseBranch(); + + expect(result).toBe('develop'); + }); + + it('should return main when it has fewer commits', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(develop) + .mockReturnValueOnce({ status: 0, stdout: 'abc123', stderr: '' } as any) // merge-base main + .mockReturnValueOnce({ status: 0, stdout: 'def456', stderr: '' } as any) // merge-base develop + .mockReturnValueOnce({ status: 0, stdout: '2', stderr: '' } as any) // countCommits from main + .mockReturnValueOnce({ status: 0, stdout: '5', stderr: '' } as any); // countCommits from develop + + const result = detectBaseBranch(); + + expect(result).toBe('main'); + }); + + it('should fallback to develop when merge-base fails', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(develop) + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'merge-base failed' } as any); + + const result = detectBaseBranch(); + + expect(result).toBe('develop'); + }); + }); + + describe('getChangedFiles', () => { + it('should return empty result when no changes detected', () => { + mockedSpawnSync.mockReturnValue({ status: 0, stdout: '', stderr: '' } as any); + + const result = getChangedFiles('main'); + + expect(result).toEqual({ + files: [], + stats: { added: 0, modified: 0, deleted: 0 }, + }); + }); + + it('should parse added files correctly', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'A\tsrc/new-file.ts\n', + stderr: '', + } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toEqual([{ path: 'src/new-file.ts', status: 'A' }]); + expect(result.stats).toEqual({ added: 1, modified: 0, deleted: 0 }); + }); + + it('should parse modified files correctly', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'M\tsrc/existing-file.ts\n', + stderr: '', + } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toEqual([{ path: 'src/existing-file.ts', status: 'M' }]); + expect(result.stats).toEqual({ added: 0, modified: 1, deleted: 0 }); + }); + + it('should parse deleted files correctly', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'D\tsrc/removed-file.ts\n', + stderr: '', + } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toEqual([{ path: 'src/removed-file.ts', status: 'D' }]); + expect(result.stats).toEqual({ added: 0, modified: 0, deleted: 1 }); + }); + + it('should parse renamed files correctly', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'R\tsrc/renamed-file.ts\n', + stderr: '', + } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toEqual([{ path: 'src/renamed-file.ts', status: 'R' }]); + expect(result.stats).toEqual({ added: 0, modified: 0, deleted: 0 }); + }); + + it('should handle multiple files with different statuses', () => { + const diffOutput = [ + 'A\tsrc/new.ts', + 'M\tsrc/modified.ts', + 'D\tsrc/deleted.ts', + 'A\tdocs/README.md', + ].join('\n'); + + mockedSpawnSync.mockReturnValue({ status: 0, stdout: diffOutput, stderr: '' } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toHaveLength(4); + expect(result.files).toContainEqual({ path: 'src/new.ts', status: 'A' }); + expect(result.files).toContainEqual({ path: 'src/modified.ts', status: 'M' }); + expect(result.files).toContainEqual({ path: 'src/deleted.ts', status: 'D' }); + expect(result.files).toContainEqual({ path: 'docs/README.md', status: 'A' }); + expect(result.stats).toEqual({ added: 2, modified: 1, deleted: 1 }); + }); + + it('should handle file paths with tabs', () => { + mockedSpawnSync.mockReturnValue({ + status: 0, + stdout: 'A\tpath\twith\ttabs.ts\n', + stderr: '', + } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toEqual([{ path: 'path\twith\ttabs.ts', status: 'A' }]); + }); + + it('should ignore empty lines', () => { + const diffOutput = 'A\tsrc/file1.ts\n\n\nM\tsrc/file2.ts\n'; + mockedSpawnSync.mockReturnValue({ status: 0, stdout: diffOutput, stderr: '' } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toHaveLength(2); + }); + + it('should use detectBaseBranch when baseBranch is not provided', () => { + mockedSpawnSync + .mockReturnValueOnce({ status: 0, stdout: 'feature-branch', stderr: '' } as any) // getCurrentBranch + .mockReturnValueOnce({ status: 0, stdout: 'dummy', stderr: '' } as any) // branchExists(main) + .mockReturnValueOnce({ status: 1, stdout: '', stderr: 'Branch not found' } as any) // branchExists(develop) + .mockReturnValueOnce({ status: 0, stdout: 'A\tsrc/file.ts\n', stderr: '' } as any); // git diff + + const result = getChangedFiles(); + + expect(result.files).toHaveLength(1); + expect(mockedSpawnSync).toHaveBeenCalledWith( + 'git', + ['diff', '--name-status', 'main...HEAD'], + expect.any(Object) + ); + }); + + it('should throw error when git command fails', () => { + mockedSpawnSync.mockReturnValue({ status: 1, stdout: '', stderr: 'Git error' } as any); + + expect(() => getChangedFiles('main')).toThrow('Failed to get changed files'); + }); + + it('should skip lines without proper format', () => { + const diffOutput = ['A\tsrc/valid.ts', 'INVALID_LINE', 'M\tsrc/another.ts'].join('\n'); + + mockedSpawnSync.mockReturnValue({ status: 0, stdout: diffOutput, stderr: '' } as any); + + const result = getChangedFiles('main'); + + expect(result.files).toHaveLength(2); + expect(result.files).toContainEqual({ path: 'src/valid.ts', status: 'A' }); + expect(result.files).toContainEqual({ path: 'src/another.ts', status: 'M' }); + }); + }); +}); diff --git a/scripts/lib/git-diff.ts b/scripts/lib/git-diff.ts new file mode 100644 index 0000000..14b3dd8 --- /dev/null +++ b/scripts/lib/git-diff.ts @@ -0,0 +1,145 @@ +import { spawnSync } from 'child_process'; + +import type { GitDiff, GitFileChange } from './types'; + +/** + * Execute a git command safely using spawnSync to prevent command injection + */ +export function execGit(args: string[]): string { + const result = spawnSync('git', args, { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + + if (result.error) { + throw new Error(`Git command failed: ${result.error.message}`); + } + + if (result.status !== 0) { + const errorMessage = result.stderr?.trim() || `Command exited with code ${result.status}`; + throw new Error(`Git command failed: ${errorMessage}`); + } + + return result.stdout.trim(); +} + +/** + * Check if a branch exists + */ +function branchExists(branchName: string): boolean { + try { + execGit(['rev-parse', '--verify', branchName]); + return true; + } catch { + return false; + } +} + +/** + * Get current branch name + */ +function getCurrentBranch(): string { + try { + return execGit(['rev-parse', '--abbrev-ref', 'HEAD']); + } catch { + throw new Error('Failed to get current branch'); + } +} + +/** + * Count commits between two refs + */ +function countCommits(from: string, to: string): number { + try { + const result = execGit(['rev-list', '--count', `${from}..${to}`]); + return parseInt(result, 10); + } catch { + return Infinity; + } +} + +/** + * Detect base branch (develop or main) based on merge-base + */ +export function detectBaseBranch(): string { + const currentBranch = getCurrentBranch(); + + // Skip validation if we're on main or develop + if (currentBranch === 'main' || currentBranch === 'develop') { + throw new Error( + `Cannot validate from ${currentBranch} branch. Please run this from a feature branch.` + ); + } + + // Check which branches exist + const mainExists = branchExists('main'); + const developExists = branchExists('develop'); + + if (!mainExists && !developExists) { + throw new Error('Neither main nor develop branch exists'); + } + + // If only one exists, use that + if (!mainExists) return 'develop'; + if (!developExists) return 'main'; + + // Both exist - find merge base for each and count commits + try { + const mainMergeBase = execGit(['merge-base', 'HEAD', 'main']); + const developMergeBase = execGit(['merge-base', 'HEAD', 'develop']); + + const commitsFromMain = countCommits(mainMergeBase, 'HEAD'); + const commitsFromDevelop = countCommits(developMergeBase, 'HEAD'); + + // Return the branch with fewer commits (more recent fork point) + return commitsFromDevelop <= commitsFromMain ? 'develop' : 'main'; + } catch (_error: unknown) { + // Fallback to develop if merge-base fails + return 'develop'; + } +} + +/** + * Get changed files between base branch and HEAD + */ +export function getChangedFiles(baseBranch?: string): GitDiff { + const base = baseBranch ?? detectBaseBranch(); + + try { + // Get diff between base branch and HEAD + const diffOutput = execGit(['diff', '--name-status', `${base}...HEAD`]); + + if (!diffOutput) { + return { + files: [], + stats: { added: 0, modified: 0, deleted: 0 }, + }; + } + + const files: GitFileChange[] = []; + const stats = { added: 0, modified: 0, deleted: 0 }; + + // Parse diff output + const lines = diffOutput.split('\n').filter((line) => line.trim()); + + for (const line of lines) { + const [status, ...pathParts] = line.split('\t'); + const path = pathParts.join('\t'); // Handle paths with tabs + + if (!status || !path) continue; + + const fileStatus = status.charAt(0) as 'A' | 'M' | 'D' | 'R'; + files.push({ path, status: fileStatus }); + + // Update stats + if (fileStatus === 'A') stats.added++; + else if (fileStatus === 'M') stats.modified++; + else if (fileStatus === 'D') stats.deleted++; + } + + return { files, stats }; + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to get changed files: ${message}`); + } +} diff --git a/scripts/lib/path-checker.test.ts b/scripts/lib/path-checker.test.ts new file mode 100644 index 0000000..d6560d5 --- /dev/null +++ b/scripts/lib/path-checker.test.ts @@ -0,0 +1,195 @@ +import { requiresDocsUpdate, hasDocsChanges, suggestDocsToUpdate } from './path-checker'; + +describe('path-checker', () => { + describe('requiresDocsUpdate', () => { + it('should return false when no user-facing code changes are detected', () => { + const changedFiles = ['README.md', 'package.json', 'tsconfig.json']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(false); + expect(result.reason).toBe('No user-facing code changes detected'); + expect(result.affectedPaths).toEqual([]); + expect(result.suggestions).toEqual([]); + }); + + it('should return true when src/cli/ files are changed', () => { + const changedFiles = ['src/cli/validate.ts', 'src/cli/fix.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(true); + expect(result.reason).toContain('src/cli/'); + expect(result.affectedPaths).toContain('src/cli/'); + expect(result.suggestions).toContain('docs/user-guide/cli-usage.md'); + }); + + it('should return true when src/index.ts is changed', () => { + const changedFiles = ['src/index.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(true); + expect(result.reason).toContain('src/index.ts'); + expect(result.affectedPaths).toContain('src/index.ts'); + expect(result.suggestions).toContain('docs/user-guide/'); + expect(result.suggestions).toContain('docs/architecture/overview.md'); + }); + + it('should return true when bin/ files are changed', () => { + const changedFiles = ['bin/coderef.js']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(true); + expect(result.reason).toContain('bin/'); + expect(result.affectedPaths).toContain('bin/'); + expect(result.suggestions).toContain('docs/user-guide/installation.md'); + }); + + it('should return true when src/core/ files are changed', () => { + const changedFiles = ['src/core/validate.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(true); + expect(result.reason).toContain('src/core/'); + expect(result.affectedPaths).toContain('src/core/'); + expect(result.suggestions).toContain('docs/architecture/overview.md'); + }); + + it('should handle multiple changed files with different requirements', () => { + const changedFiles = ['src/cli/validate.ts', 'src/index.ts', 'README.md']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(true); + expect(result.affectedPaths).toContain('src/cli/'); + expect(result.affectedPaths).toContain('src/index.ts'); + expect(result.suggestions).toContain('docs/user-guide/cli-usage.md'); + expect(result.suggestions).toContain('docs/user-guide/'); + }); + + it('should not duplicate affected paths', () => { + const changedFiles = ['src/cli/validate.ts', 'src/cli/fix.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.affectedPaths).toEqual(['src/cli/']); + expect(result.affectedPaths.length).toBe(1); + }); + + it('should not duplicate suggestions', () => { + const changedFiles = ['src/cli/validate.ts', 'src/cli/fix.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.suggestions).toEqual(['docs/user-guide/cli-usage.md']); + expect(result.suggestions.length).toBe(1); + }); + + it('should ignore nested paths that do not match required paths', () => { + const changedFiles = ['src/utils/helper.ts', 'src/types/index.ts']; + + const result = requiresDocsUpdate(changedFiles); + + expect(result.required).toBe(false); + }); + }); + + describe('hasDocsChanges', () => { + it('should return true when docs files are changed', () => { + const changedFiles = ['docs/README.md', 'src/index.ts']; + + const result = hasDocsChanges(changedFiles); + + expect(result).toBe(true); + }); + + it('should return false when no docs files are changed', () => { + const changedFiles = ['src/index.ts', 'README.md']; + + const result = hasDocsChanges(changedFiles); + + expect(result).toBe(false); + }); + + it('should return true for nested docs files', () => { + const changedFiles = ['docs/user-guide/cli-usage.md']; + + const result = hasDocsChanges(changedFiles); + + expect(result).toBe(true); + }); + + it('should return false for empty file list', () => { + const changedFiles: string[] = []; + + const result = hasDocsChanges(changedFiles); + + expect(result).toBe(false); + }); + }); + + describe('suggestDocsToUpdate', () => { + it('should return empty array for files that do not require docs updates', () => { + const changedFiles = ['README.md', 'package.json']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toEqual([]); + }); + + it('should suggest docs for src/cli/ changes', () => { + const changedFiles = ['src/cli/validate.ts']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toContain('docs/user-guide/cli-usage.md'); + }); + + it('should suggest docs for src/index.ts changes', () => { + const changedFiles = ['src/index.ts']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toContain('docs/user-guide/'); + expect(result).toContain('docs/architecture/overview.md'); + }); + + it('should suggest docs for bin/ changes', () => { + const changedFiles = ['bin/coderef.js']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toContain('docs/user-guide/installation.md'); + }); + + it('should suggest docs for src/core/ changes', () => { + const changedFiles = ['src/core/validate.ts']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toContain('docs/architecture/overview.md'); + }); + + it('should not duplicate suggestions', () => { + const changedFiles = ['src/cli/validate.ts', 'src/cli/fix.ts']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toEqual(['docs/user-guide/cli-usage.md']); + expect(result.length).toBe(1); + }); + + it('should combine suggestions from multiple files', () => { + const changedFiles = ['src/cli/validate.ts', 'src/index.ts']; + + const result = suggestDocsToUpdate(changedFiles); + + expect(result).toContain('docs/user-guide/cli-usage.md'); + expect(result).toContain('docs/user-guide/'); + expect(result).toContain('docs/architecture/overview.md'); + }); + }); +}); diff --git a/scripts/lib/path-checker.ts b/scripts/lib/path-checker.ts new file mode 100644 index 0000000..ad5bf4c --- /dev/null +++ b/scripts/lib/path-checker.ts @@ -0,0 +1,121 @@ +import type { RequirementResult } from './types'; + +/** + * Paths that require documentation updates + */ +const DOCS_REQUIRED_PATHS = ['src/cli/', 'src/index.ts', 'bin/', 'src/core/']; + +/** + * Documentation directory + */ +const DOCS_DIR = 'docs/'; + +/** + * Suggestions for documentation updates based on changed paths + */ +const DOCS_SUGGESTIONS: Record = { + 'src/cli/': ['docs/user-guide/cli-usage.md'], + 'src/index.ts': ['docs/user-guide/', 'docs/architecture/overview.md'], + 'bin/': ['docs/user-guide/installation.md'], + 'src/core/': ['docs/architecture/overview.md'], +}; + +/** + * Check if a path requires documentation update + */ +function pathRequiresDocs(filePath: string): boolean { + return DOCS_REQUIRED_PATHS.some((requiredPath) => { + if (requiredPath.endsWith('/')) { + return filePath.startsWith(requiredPath); + } + return filePath === requiredPath; + }); +} + +/** + * Check if a path is in the docs directory + */ +function isDocsPath(filePath: string): boolean { + return filePath.startsWith(DOCS_DIR); +} + +/** + * Check if changed files require documentation updates + */ +export function requiresDocsUpdate(changedFiles: string[]): RequirementResult { + const affectedPaths: string[] = []; + const suggestions: string[] = []; + + // Check each changed file + for (const file of changedFiles) { + if (pathRequiresDocs(file)) { + // Find matching required path + for (const requiredPath of DOCS_REQUIRED_PATHS) { + const matches = requiredPath.endsWith('/') + ? file.startsWith(requiredPath) + : file === requiredPath; + + if (matches && !affectedPaths.includes(requiredPath)) { + affectedPaths.push(requiredPath); + + // Add suggestions + const pathSuggestions = DOCS_SUGGESTIONS[requiredPath] || []; + for (const suggestion of pathSuggestions) { + if (!suggestions.includes(suggestion)) { + suggestions.push(suggestion); + } + } + } + } + } + } + + if (affectedPaths.length === 0) { + return { + required: false, + reason: 'No user-facing code changes detected', + affectedPaths: [], + suggestions: [], + }; + } + + return { + required: true, + reason: `Changes detected in: ${affectedPaths.join(', ')}`, + affectedPaths, + suggestions, + }; +} + +/** + * Check if docs directory has changes + */ +export function hasDocsChanges(changedFiles: string[]): boolean { + return changedFiles.some((file) => isDocsPath(file)); +} + +/** + * Get list of documentation files that should be updated + */ +export function suggestDocsToUpdate(changedFiles: string[]): string[] { + const suggestions: string[] = []; + + for (const file of changedFiles) { + for (const requiredPath of DOCS_REQUIRED_PATHS) { + const matches = requiredPath.endsWith('/') + ? file.startsWith(requiredPath) + : file === requiredPath; + + if (matches) { + const pathSuggestions = DOCS_SUGGESTIONS[requiredPath] || []; + for (const suggestion of pathSuggestions) { + if (!suggestions.includes(suggestion)) { + suggestions.push(suggestion); + } + } + } + } + } + + return suggestions; +} diff --git a/scripts/lib/types.ts b/scripts/lib/types.ts new file mode 100644 index 0000000..b72326a --- /dev/null +++ b/scripts/lib/types.ts @@ -0,0 +1,33 @@ +/** + * Type definitions for documentation validation + */ + +/** + * Git file change information + */ +export interface GitFileChange { + path: string; + status: 'A' | 'M' | 'D' | 'R'; +} + +/** + * Git diff result + */ +export interface GitDiff { + files: GitFileChange[]; + stats: { + added: number; + modified: number; + deleted: number; + }; +} + +/** + * Documentation requirement result + */ +export interface RequirementResult { + required: boolean; + reason: string; + affectedPaths: string[]; + suggestions: string[]; +} diff --git a/scripts/validate-docs.test.ts b/scripts/validate-docs.test.ts new file mode 100644 index 0000000..4f0a4e1 --- /dev/null +++ b/scripts/validate-docs.test.ts @@ -0,0 +1,301 @@ +import { validateDocumentation } from './validate-docs'; +import { detectBaseBranch, getChangedFiles } from './lib/git-diff'; +import { requiresDocsUpdate, hasDocsChanges } from './lib/path-checker'; + +// Mock dependencies +jest.mock('./lib/git-diff'); +jest.mock('./lib/path-checker'); + +const mockedDetectBaseBranch = detectBaseBranch as jest.MockedFunction; +const mockedGetChangedFiles = getChangedFiles as jest.MockedFunction; +const mockedRequiresDocsUpdate = requiresDocsUpdate as jest.MockedFunction< + typeof requiresDocsUpdate +>; +const mockedHasDocsChanges = hasDocsChanges as jest.MockedFunction; + +describe('validate-docs', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('Base branch detection', () => { + it('should exit gracefully when detectBaseBranch throws error', () => { + mockedDetectBaseBranch.mockImplementation(() => { + throw new Error('Cannot validate from main branch. Please run this from a feature branch.'); + }); + + // Since main is not exported and uses require.main check, we test the logic flow + // by verifying the mocked functions are called correctly in the actual implementation + expect(mockedDetectBaseBranch).toBeDefined(); + }); + }); + + describe('getChangedFiles', () => { + it('should handle empty file list', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [], + stats: { added: 0, modified: 0, deleted: 0 }, + }); + + expect(mockedGetChangedFiles('main')).toEqual({ + files: [], + stats: { added: 0, modified: 0, deleted: 0 }, + }); + }); + + it('should handle file changes', () => { + mockedGetChangedFiles.mockReturnValue({ + files: [ + { path: 'src/index.ts', status: 'M' }, + { path: 'docs/README.md', status: 'M' }, + ], + stats: { added: 0, modified: 2, deleted: 0 }, + }); + + const result = mockedGetChangedFiles('main'); + expect(result.files).toHaveLength(2); + }); + }); + + describe('requiresDocsUpdate', () => { + it('should return false for non-user-facing changes', () => { + mockedRequiresDocsUpdate.mockReturnValue({ + required: false, + reason: 'No user-facing code changes detected', + affectedPaths: [], + suggestions: [], + }); + + const result = mockedRequiresDocsUpdate(['README.md']); + expect(result.required).toBe(false); + }); + + it('should return true for user-facing changes', () => { + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/cli/', + affectedPaths: ['src/cli/'], + suggestions: ['docs/user-guide/cli-usage.md'], + }); + + const result = mockedRequiresDocsUpdate(['src/cli/validate.ts']); + expect(result.required).toBe(true); + expect(result.suggestions).toContain('docs/user-guide/cli-usage.md'); + }); + }); + + describe('hasDocsChanges', () => { + it('should return true when docs are changed', () => { + mockedHasDocsChanges.mockReturnValue(true); + + const result = mockedHasDocsChanges(['docs/README.md']); + expect(result).toBe(true); + }); + + it('should return false when docs are not changed', () => { + mockedHasDocsChanges.mockReturnValue(false); + + const result = mockedHasDocsChanges(['src/index.ts']); + expect(result).toBe(false); + }); + }); + + describe('Integration scenarios', () => { + it('should validate successfully when docs are updated with user-facing changes', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [ + { path: 'src/cli/validate.ts', status: 'M' }, + { path: 'docs/user-guide/cli-usage.md', status: 'M' }, + ], + stats: { added: 0, modified: 2, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/cli/', + affectedPaths: ['src/cli/'], + suggestions: ['docs/user-guide/cli-usage.md'], + }); + mockedHasDocsChanges.mockReturnValue(true); + + expect(mockedHasDocsChanges(['src/cli/validate.ts', 'docs/user-guide/cli-usage.md'])).toBe( + true + ); + }); + + it('should show warning when docs are not updated with user-facing changes', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [{ path: 'src/cli/validate.ts', status: 'M' }], + stats: { added: 0, modified: 1, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/cli/', + affectedPaths: ['src/cli/'], + suggestions: ['docs/user-guide/cli-usage.md'], + }); + mockedHasDocsChanges.mockReturnValue(false); + + expect(mockedHasDocsChanges(['src/cli/validate.ts'])).toBe(false); + }); + + it('should skip validation for non-user-facing changes', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [{ path: 'README.md', status: 'M' }], + stats: { added: 0, modified: 1, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: false, + reason: 'No user-facing code changes detected', + affectedPaths: [], + suggestions: [], + }); + + const result = mockedRequiresDocsUpdate(['README.md']); + expect(result.required).toBe(false); + }); + + it('should handle files with different statuses', () => { + mockedGetChangedFiles.mockReturnValue({ + files: [ + { path: 'src/new.ts', status: 'A' }, + { path: 'src/modified.ts', status: 'M' }, + { path: 'src/deleted.ts', status: 'D' }, + ], + stats: { added: 1, modified: 1, deleted: 1 }, + }); + + const result = mockedGetChangedFiles('main'); + expect(result.files).toHaveLength(3); + expect(result.stats.added).toBe(1); + expect(result.stats.modified).toBe(1); + expect(result.stats.deleted).toBe(1); + }); + }); + + describe('validateDocumentation', () => { + it('should return skipped status when detectBaseBranch throws error', () => { + mockedDetectBaseBranch.mockImplementation(() => { + throw new Error('Cannot validate from main branch'); + }); + + const result = validateDocumentation(); + + expect(result.status).toBe('skipped'); + expect(result.skipReason).toBe('Cannot validate from main branch'); + }); + + it('should return skipped status when no changes detected', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [], + stats: { added: 0, modified: 0, deleted: 0 }, + }); + + const result = validateDocumentation(); + + expect(result.status).toBe('skipped'); + expect(result.skipReason).toBe('No changes detected'); + expect(result.baseBranch).toBe('main'); + }); + + it('should return skipped status when docs update not required', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [{ path: 'README.md', status: 'M' }], + stats: { added: 0, modified: 1, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: false, + reason: 'No user-facing code changes detected', + affectedPaths: [], + suggestions: [], + }); + + const result = validateDocumentation(); + + expect(result.status).toBe('skipped'); + expect(result.skipReason).toBe('Documentation update not required for these changes'); + expect(result.requirement?.required).toBe(false); + }); + + it('should return passed status when docs are updated with user-facing changes', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [ + { path: 'src/cli/validate.ts', status: 'M' }, + { path: 'docs/user-guide/cli-usage.md', status: 'M' }, + ], + stats: { added: 0, modified: 2, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/cli/', + affectedPaths: ['src/cli/'], + suggestions: ['docs/user-guide/cli-usage.md'], + }); + mockedHasDocsChanges.mockReturnValue(true); + + const result = validateDocumentation(); + + expect(result.status).toBe('passed'); + expect(result.docsUpdated).toBe(true); + expect(result.requirement?.required).toBe(true); + }); + + it('should return warning status when docs are not updated with user-facing changes', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockReturnValue({ + files: [{ path: 'src/cli/validate.ts', status: 'M' }], + stats: { added: 0, modified: 1, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/cli/', + affectedPaths: ['src/cli/'], + suggestions: ['docs/user-guide/cli-usage.md'], + }); + mockedHasDocsChanges.mockReturnValue(false); + + const result = validateDocumentation(); + + expect(result.status).toBe('warning'); + expect(result.docsUpdated).toBe(false); + expect(result.requirement?.suggestions).toContain('docs/user-guide/cli-usage.md'); + }); + + it('should use provided base branch instead of detecting', () => { + mockedGetChangedFiles.mockReturnValue({ + files: [{ path: 'src/index.ts', status: 'M' }], + stats: { added: 0, modified: 1, deleted: 0 }, + }); + mockedRequiresDocsUpdate.mockReturnValue({ + required: true, + reason: 'Changes detected in: src/', + affectedPaths: ['src/'], + suggestions: [], + }); + mockedHasDocsChanges.mockReturnValue(true); + + const result = validateDocumentation('develop'); + + expect(result.baseBranch).toBe('develop'); + expect(mockedDetectBaseBranch).not.toHaveBeenCalled(); + }); + + it('should return error status when unexpected error occurs', () => { + mockedDetectBaseBranch.mockReturnValue('main'); + mockedGetChangedFiles.mockImplementation(() => { + throw new Error('Git command failed'); + }); + + const result = validateDocumentation(); + + expect(result.status).toBe('error'); + expect(result.error?.message).toBe('Git command failed'); + }); + }); +}); diff --git a/scripts/validate-docs.ts b/scripts/validate-docs.ts new file mode 100644 index 0000000..5f29e6a --- /dev/null +++ b/scripts/validate-docs.ts @@ -0,0 +1,221 @@ +#!/usr/bin/env node + +/** + * Documentation validation script + * + * Validates that documentation is updated when user-facing code changes. + */ + +import chalk from 'chalk'; + +import type { GitDiff, RequirementResult } from './lib/types'; +import { detectBaseBranch, getChangedFiles } from './lib/git-diff'; +import { requiresDocsUpdate, hasDocsChanges } from './lib/path-checker'; + +/** + * Validation result status + */ +type ValidationStatus = 'skipped' | 'passed' | 'warning' | 'error'; + +/** + * Validation result + */ +interface ValidationResult { + status: ValidationStatus; + baseBranch?: string; + diff?: GitDiff; + requirement?: RequirementResult; + docsUpdated?: boolean; + error?: Error; + skipReason?: string; +} + +/** + * Validate documentation updates + * + * This function contains the core validation logic without any presentation concerns. + * It returns a structured result that can be easily tested and reused. + */ +export function validateDocumentation(baseBranch?: string): ValidationResult { + try { + // Detect base branch if not provided + let detectedBaseBranch: string; + if (baseBranch) { + detectedBaseBranch = baseBranch; + } else { + try { + detectedBaseBranch = detectBaseBranch(); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + return { + status: 'skipped', + skipReason: message, + }; + } + } + + // Get changed files + const diff = getChangedFiles(detectedBaseBranch); + + // Check if there are any changes + if (diff.files.length === 0) { + return { + status: 'skipped', + baseBranch: detectedBaseBranch, + diff, + skipReason: 'No changes detected', + }; + } + + // Get file paths + const changedPaths = diff.files.map((f) => f.path); + + // Check if documentation update is required + const requirement = requiresDocsUpdate(changedPaths); + + if (!requirement.required) { + return { + status: 'skipped', + baseBranch: detectedBaseBranch, + diff, + requirement, + skipReason: 'Documentation update not required for these changes', + }; + } + + // Documentation update is required - check if docs have changes + const docsUpdated = hasDocsChanges(changedPaths); + + if (docsUpdated) { + return { + status: 'passed', + baseBranch: detectedBaseBranch, + diff, + requirement, + docsUpdated: true, + }; + } else { + return { + status: 'warning', + baseBranch: detectedBaseBranch, + diff, + requirement, + docsUpdated: false, + }; + } + } catch (error: unknown) { + return { + status: 'error', + error: error instanceof Error ? error : new Error(String(error)), + }; + } +} + +/** + * Format and display validation results + */ +function formatAndDisplay(result: ValidationResult): void { + console.log('🔍 Validating documentation updates...\n'); + + // Handle error status + if (result.status === 'error') { + console.error(chalk.red('❌ Error:'), result.error?.message ?? 'Unknown error'); + console.error(''); + process.exit(1); + } + + // Handle skipped status + if (result.status === 'skipped') { + if (result.skipReason) { + console.log(chalk.yellow(`⚠️ ${result.skipReason}`)); + } + if (result.baseBranch) { + console.log(`Base branch detected: ${chalk.cyan(result.baseBranch)}`); + console.log(`Comparing: ${chalk.cyan(`${result.baseBranch}...HEAD`)}\n`); + } + if (result.diff && result.diff.files.length > 0) { + console.log('Changes detected:'); + for (const file of result.diff.files) { + const statusIcon = file.status === 'A' ? '+' : file.status === 'M' ? '~' : '-'; + const statusColor = + file.status === 'A' ? chalk.green : file.status === 'M' ? chalk.yellow : chalk.red; + console.log(` ${statusColor(statusIcon)} ${file.path}`); + } + console.log(''); + } + console.log('\nValidation skipped.\n'); + process.exit(0); + } + + // Display base branch and comparison + if (result.baseBranch) { + console.log(`Base branch detected: ${chalk.cyan(result.baseBranch)}`); + console.log(`Comparing: ${chalk.cyan(`${result.baseBranch}...HEAD`)}\n`); + } + + // Display changed files in user-facing code + if (result.diff && result.requirement) { + console.log('Changes detected in user-facing code:'); + for (const file of result.diff.files) { + if (result.requirement.affectedPaths.some((path) => file.path.startsWith(path))) { + const statusIcon = file.status === 'A' ? '+' : file.status === 'M' ? '~' : '-'; + const statusColor = + file.status === 'A' ? chalk.green : file.status === 'M' ? chalk.yellow : chalk.red; + console.log(` ${statusColor(statusIcon)} ${file.path}`); + } + } + console.log(''); + } + + // Handle passed status + if (result.status === 'passed' && result.diff) { + console.log(chalk.green('✅ Documentation validation passed')); + console.log(''); + console.log('Documentation updated:'); + for (const file of result.diff.files) { + if (file.path.startsWith('docs/')) { + const statusIcon = file.status === 'A' ? '+' : file.status === 'M' ? '~' : '-'; + const statusColor = + file.status === 'A' ? chalk.green : file.status === 'M' ? chalk.yellow : chalk.red; + console.log(` ${statusColor(statusIcon)} ${file.path}`); + } + } + console.log(''); + console.log('All checks passed!\n'); + process.exit(0); + } + + // Handle warning status + if (result.status === 'warning' && result.requirement) { + console.log(chalk.yellow('⚠️ Documentation update recommended but not found')); + console.log(''); + console.log(`${result.requirement.reason}`); + console.log(`Documentation updated: ${chalk.red('NO')}`); + console.log(''); + + if (result.requirement.suggestions.length > 0) { + console.log(chalk.cyan('💡 Suggestions:')); + for (const suggestion of result.requirement.suggestions) { + console.log(` - Update ${suggestion}`); + } + console.log(''); + } + + console.log('Consider updating the relevant documentation.'); + console.log('See docs/development/documentation.md for guidelines.\n'); + process.exit(0); + } +} + +/** + * Main validation function + */ +function main(): void { + const result = validateDocumentation(); + formatAndDisplay(result); +} + +// Run main function +if (require.main === module) { + main(); +} diff --git a/src/cli/fix.ts b/src/cli/fix.ts new file mode 100644 index 0000000..d1513c4 --- /dev/null +++ b/src/cli/fix.ts @@ -0,0 +1,263 @@ +#!/usr/bin/env tsx + +/** + * Interactive script to fix errors detected by validate:docs:code + * + * Usage: + * tsx scripts/coderef/fix.ts # Default: no backup + * tsx scripts/coderef/fix.ts --dry-run + * tsx scripts/coderef/fix.ts --auto --backup # With backup + * npm run coderef:fix + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +import { createBackup } from '@/utils/backup'; +import { applyFix, createFixAction, handleMultipleMatches, isFixableError } from '@/utils/fix'; +import { askYesNo, createPromptInterface, displayFixPreview } from '@/utils/prompt'; +import { formatFixOptions } from '@/utils/styles'; +import type { CodeRefError, FixOptions, FixResult } from '@/utils/types'; +import { extractCodeRefs, findMarkdownFiles, validateCodeRef } from '@/core/validate'; +import { loadFixConfig, getDocsPath, type CodeRefFixConfig } from '@/config'; + +// Parse command line arguments +function parseArgs(): FixOptions { + const args = process.argv.slice(2); + + return { + dryRun: args.includes('--dry-run'), + auto: args.includes('--auto'), + noBackup: !args.includes('--backup'), // Default: no backup (enabled with --backup) + verbose: args.includes('--verbose') || args.includes('-v'), + }; +} + +/** + * Grouped errors + */ +interface ErrorGroup { + docFile: string; + errors: CodeRefError[]; +} + +/** + * Collect errors + */ +function collectErrors(config: CodeRefFixConfig): ErrorGroup[] { + const docsPath = getDocsPath(config); + const markdownFiles = findMarkdownFiles(docsPath); + const errorsByDoc: Record = {}; + + for (const file of markdownFiles) { + const content = fs.readFileSync(file, 'utf-8'); + const refs = extractCodeRefs(content, file); + + for (const ref of refs) { + const errors = validateCodeRef(ref, config); + const fixableErrors = errors.filter(isFixableError); + + if (fixableErrors.length > 0) { + if (!errorsByDoc[file]) { + errorsByDoc[file] = []; + } + errorsByDoc[file].push(...fixableErrors); + } + } + } + + return Object.entries(errorsByDoc).map(([docFile, errors]) => ({ + docFile, + errors, + })); +} + +/** + * Main process + */ +export async function main(): Promise { + const options = parseArgs(); + + // Load configuration + const config = loadFixConfig({ + dryRun: options.dryRun, + auto: options.auto, + backup: !options.noBackup, + verbose: options.verbose, + }); + + console.log('🔧 Starting CODE_REF error fixes...\n'); + + if (options.dryRun) { + console.log('⚠️ DRY RUN mode: No actual changes will be made\n'); + } + + // Collect errors + const errorGroups = collectErrors(config); + + if (errorGroups.length === 0) { + console.log('✅ No fixable errors found'); + process.exit(0); + } + + // Statistics + const totalErrors = errorGroups.reduce((sum, g) => sum + g.errors.length, 0); + console.log(`📊 Detected ${totalErrors} fixable error(s) in ${errorGroups.length} file(s)\n`); + + // Interactive interface + const rl = createPromptInterface(); + const fixResults: FixResult[] = []; + const backupFiles = new Set(); + + try { + for (const group of errorGroups) { + console.log(`\n📄 ${path.relative(config.projectRoot, group.docFile)}`); + console.log(` ${group.errors.length} error(s)\n`); + + // Sort errors in descending order by docLineNumber (bottom to top) + // To prevent fixes at the bottom from affecting line numbers at the top + const sortedErrors = group.errors.sort((a, b) => { + const lineA = a.ref.docLineNumber ?? Infinity; + const lineB = b.ref.docLineNumber ?? Infinity; + return lineB - lineA; // descending + }); + + let _lineOffset = 0; // Track cumulative offset (for future edge case handling) + + for (const error of sortedErrors) { + console.log(`\n❌ ${error.type}: ${error.message}`); + console.log( + ` Reference: ${path.relative(config.projectRoot, error.ref.docFile)}${error.ref.docLineNumber ? `:${error.ref.docLineNumber}` : ''}` + ); + + // Create fix action + let action; + + if (error.type === 'CODE_LOCATION_MISMATCH') { + // Handle multiple matches + action = await handleMultipleMatches(error, rl, config); + } else { + const fixActionResult = await createFixAction(error, config, rl); + + // If there are multiple options, let the user choose + if (Array.isArray(fixActionResult)) { + console.log('\n🛠️ Please select a fix method:\n'); + console.log(formatFixOptions(fixActionResult)); + + if (options.auto) { + // Auto-select first option in auto mode + console.log(' ℹ️ Auto-selecting option 1 in auto mode\n'); + action = fixActionResult[0]; + } else { + // Let user choose + const selection = await new Promise((resolve) => { + rl.question(`Select fix method (1-${fixActionResult.length}): `, (answer) => { + const num = parseInt(answer, 10); + if (num >= 1 && num <= fixActionResult.length) { + resolve(num - 1); + } else { + console.log(' ⚠️ Invalid selection. Skipping.'); + resolve(-1); + } + }); + }); + + if (selection === -1) { + console.log(' ⏭️ Skipped'); + continue; + } + + action = fixActionResult[selection]; + } + } else { + action = fixActionResult; + } + } + + if (!action) { + console.log(' ⚠️ This error cannot be fixed'); + continue; + } + + // Display preview (only for single option) + if (!Array.isArray(action)) { + displayFixPreview(action, config.projectRoot); + } + + // Confirmation + let shouldFix = options.auto; + if (!options.auto) { + shouldFix = await askYesNo(rl, '\nApply this fix?', false); + } + + if (!shouldFix) { + console.log(' ⏭️ Skipped'); + continue; + } + + // Dry run check + if (options.dryRun) { + console.log(' ✅ [DRY RUN] Simulated fix'); + fixResults.push({ success: true, action }); + continue; + } + + // Create backup (once per file) + let backupPath: string | undefined; + if (!options.noBackup && !backupFiles.has(group.docFile)) { + backupPath = createBackup(group.docFile); + backupFiles.add(group.docFile); + console.log(` 💾 Backup created: ${path.basename(backupPath)}`); + } + + // Apply fix + try { + const lineDelta = applyFix(action); + _lineOffset += lineDelta; // Accumulate offset + + // Debug log + if (lineDelta !== 0) { + console.log(` 📊 Line delta: ${lineDelta > 0 ? '+' : ''}${lineDelta}`); + } + + console.log(' ✅ Fix applied'); + fixResults.push({ success: true, action, backupPath }); + } catch (err) { + const errorMsg = err instanceof Error ? err.message : String(err); + console.log(` ❌ Fix failed: ${errorMsg}`); + fixResults.push({ success: false, action, error: errorMsg, backupPath }); + } + } + } + } finally { + rl.close(); + } + + // Result summary + console.log(`\n${'='.repeat(60)}`); + console.log('📊 Fix Results Summary\n'); + + const successful = fixResults.filter((r) => r.success).length; + const failed = fixResults.filter((r) => !r.success).length; + + console.log(`✅ Successful: ${successful}`); + console.log(`❌ Failed: ${failed}`); + + if (backupFiles.size > 0 && !options.noBackup) { + console.log(`\n💾 Backup files: ${backupFiles.size}`); + for (const file of backupFiles) { + const backupPath = `${file}.backup`; + console.log(` ${path.relative(config.projectRoot, backupPath)}`); + } + } + + process.exit(failed > 0 ? 1 : 0); +} + +// When executed as a script +if (require.main === module) { + main().catch((error) => { + console.error('\n❌ An error occurred:', error); + process.exit(1); + }); +} diff --git a/src/cli/validate.ts b/src/cli/validate.ts new file mode 100644 index 0000000..755a5f0 --- /dev/null +++ b/src/cli/validate.ts @@ -0,0 +1,218 @@ +#!/usr/bin/env node + +/** + * CLI for validating CODE_REF references in markdown documentation + * + * Usage: + * coderef validate # Validate all files + * coderef validate --verbose # Verbose output + * coderef validate docs/README.md # Specific file + * coderef validate docs/backend/ # Specific directory + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +import { loadConfig, getDocsPath, getIgnoreFilePath } from '@/config'; +import { findMarkdownFiles, extractCodeRefs, validateCodeRef } from '@/core/validate'; +import { isIgnored, loadDocsignorePatterns } from '@/utils/ignore-pattern'; +import { displayLineRangeDiff } from '@/utils/diff-display'; +import { extractLinesFromFile } from '@/utils/code-comparison'; + +/** + * CLI options + */ +interface CliOptions { + verbose: boolean; + files: string[]; +} + +/** + * Parse command line arguments + */ +function parseCliArgs(args: string[]): CliOptions { + const verbose = args.includes('--verbose') || args.includes('-v'); + const files = args.filter((arg) => !arg.startsWith('-')); + + return { verbose, files }; +} + +/** + * Check if path is a directory + */ +function isDirectory(filePath: string): boolean { + try { + const stat = fs.statSync(filePath); + return stat.isDirectory(); + } catch { + return false; + } +} + +/** + * Resolve target files from CLI arguments + */ +function resolveTargetFiles(targets: string[], projectRoot: string, docsPath: string): string[] { + if (targets.length === 0) { + // No files specified, validate all files + return findMarkdownFiles(docsPath); + } + + const resolvedFiles = new Set(); + + for (const target of targets) { + // Convert relative path to absolute path + const absolutePath = path.isAbsolute(target) ? target : path.join(projectRoot, target); + + if (isDirectory(absolutePath)) { + // Directory: recursively find markdown files + const files = findMarkdownFiles(absolutePath); + files.forEach((file) => resolvedFiles.add(file)); + } else if (fs.existsSync(absolutePath)) { + // File: add directly + if (absolutePath.endsWith('.md')) { + resolvedFiles.add(absolutePath); + } + } else { + console.warn(`⚠️ File not found: ${target}`); + } + } + + return Array.from(resolvedFiles); +} + +/** + * Main CLI function + */ +export async function main(args: string[] = process.argv.slice(2)): Promise { + const options = parseCliArgs(args); + + console.log('🔍 Validating CODE_REF references in documentation...\n'); + + // Load configuration + const config = loadConfig({ + targets: options.files.length > 0 ? options.files : undefined, + verbose: options.verbose, + }); + + // Resolve target files + const docsPath = getDocsPath(config); + const allMarkdownFiles = resolveTargetFiles(options.files, config.projectRoot, docsPath); + + if (options.files.length > 0 && options.verbose) { + console.log(`📋 Specified files/directories: ${options.files.join(', ')}\n`); + } + + // Load .docsignore patterns + const ignoreFilePath = getIgnoreFilePath(config); + const ignorePatterns = ignoreFilePath ? loadDocsignorePatterns(ignoreFilePath) : []; + if (options.verbose) { + console.log(`📋 Loaded ${ignorePatterns.length} patterns from .docsignore\n`); + } + + // Filter files not excluded by .docsignore + const markdownFiles = allMarkdownFiles.filter((file) => { + const relativePath = path.relative(config.projectRoot, file); + return !isIgnored(relativePath, ignorePatterns); + }); + + if (options.verbose && allMarkdownFiles.length > markdownFiles.length) { + console.log( + `📋 ${allMarkdownFiles.length - markdownFiles.length} files excluded by .docsignore\n` + ); + } + + console.log(`📄 Found ${markdownFiles.length} markdown files\n`); + + // Extract all CODE_REF references + let totalRefs = 0; + const allRefs: { ref: any; file: string }[] = []; + + for (const file of markdownFiles) { + const content = fs.readFileSync(file, 'utf-8'); + const refs = extractCodeRefs(content, file); + + if (refs.length > 0) { + totalRefs += refs.length; + refs.forEach((ref) => allRefs.push({ ref, file })); + + if (options.verbose) { + console.log(` ${path.relative(docsPath, file)}: ${refs.length} references`); + } + } + } + + console.log(`\n📌 Found ${totalRefs} CODE_REF references\n`); + + if (totalRefs === 0) { + console.log('✅ No CODE_REF references found (no validation needed)'); + process.exit(0); + } + + // Validate each reference + const allErrors = await Promise.all(allRefs.map(({ ref }) => validateCodeRef(ref, config))).then( + (results) => results.flat() + ); + + // Display results + if (allErrors.length === 0) { + console.log('✅ All CODE_REF references are valid!'); + process.exit(0); + } else { + console.log(`❌ Found ${allErrors.length} errors:\n`); + + // Group errors by document + const errorsByDoc: Record = {}; + + for (const error of allErrors) { + const docFile = path.relative(config.projectRoot, error.ref.docFile); + + if (!errorsByDoc[docFile]) { + errorsByDoc[docFile] = []; + } + + errorsByDoc[docFile].push(error); + } + + // Display error details + for (const [docFile, errors] of Object.entries(errorsByDoc)) { + console.log(`📄 ${docFile}:`); + + for (const error of errors) { + console.log(` ❌ ${error.type}: ${error.message}`); + + // Display line number in document + const filePath = path.relative(config.projectRoot, error.ref.docFile); + const lineInfo = error.ref.docLineNumber ? `:${error.ref.docLineNumber}` : ''; + console.log(` ${filePath}${lineInfo}: ${error.ref.fullMatch}`); + + // Display diff for CODE_LOCATION_MISMATCH in verbose mode + if (error.type === 'CODE_LOCATION_MISMATCH' && error.suggestedLines && options.verbose) { + const filePath = path.join(config.projectRoot, error.ref.refPath); + const actualCode = extractLinesFromFile( + filePath, + error.suggestedLines.start, + error.suggestedLines.end + ); + const diff = displayLineRangeDiff( + actualCode, + { start: error.ref.startLine!, end: error.ref.endLine! }, + error.suggestedLines + ); + console.log(diff); + } + } + console.log(''); + } + + process.exit(1); + } +} + +// Run CLI if this file is executed directly +if (require.main === module) { + main().catch((error) => { + console.error('Error:', error); + process.exit(1); + }); +} diff --git a/src/config.test.ts b/src/config.test.ts new file mode 100644 index 0000000..47d4c7e --- /dev/null +++ b/src/config.test.ts @@ -0,0 +1,371 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { + loadConfig, + loadFixConfig, + resolveProjectPath, + getDocsPath, + getIgnoreFilePath, + type CodeRefConfig, +} from '@/config'; + +describe('Config System', () => { + const originalCwd = process.cwd(); + const originalEnv = { ...process.env }; + let testDir: string; + + beforeEach(() => { + // Create temporary test directory + testDir = fs.mkdtempSync(path.join(__dirname, 'tmp-config-test-')); + process.chdir(testDir); + }); + + afterEach(() => { + // Restore original state + process.chdir(originalCwd); + process.env = { ...originalEnv }; + + // Clean up test directory + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + }); + + describe('loadConfig', () => { + describe('Default configuration', () => { + it('should return default values when no config files exist', () => { + const config = loadConfig(); + + expect(config).toEqual({ + projectRoot: testDir, + docsDir: 'docs', + ignoreFile: '.docsignore', + verbose: false, + }); + }); + + it('should use process.cwd() as default projectRoot', () => { + const config = loadConfig(); + expect(config.projectRoot).toBe(testDir); + }); + }); + + describe('.coderefrc.json support', () => { + it('should load configuration from .coderefrc.json', () => { + const configContent = { + docsDir: 'documentation', + ignoreFile: '.docignore', + verbose: true, + }; + + fs.writeFileSync( + path.join(testDir, '.coderefrc.json'), + JSON.stringify(configContent, null, 2) + ); + + const config = loadConfig(); + + expect(config.docsDir).toBe('documentation'); + expect(config.ignoreFile).toBe('.docignore'); + expect(config.verbose).toBe(true); + }); + + it('should throw error for invalid .coderefrc.json', () => { + fs.writeFileSync(path.join(testDir, '.coderefrc.json'), 'invalid json{'); + + expect(() => loadConfig()).toThrow('Failed to load .coderefrc.json'); + }); + }); + + describe('package.json support', () => { + it('should load configuration from package.json "coderef" field', () => { + const packageJson = { + name: 'test-package', + version: '1.0.0', + coderef: { + docsDir: 'doc', + verbose: true, + }, + }; + + fs.writeFileSync(path.join(testDir, 'package.json'), JSON.stringify(packageJson, null, 2)); + + const config = loadConfig(); + + expect(config.docsDir).toBe('doc'); + expect(config.verbose).toBe(true); + }); + + it('should ignore package.json without coderef field', () => { + const packageJson = { + name: 'test-package', + version: '1.0.0', + }; + + fs.writeFileSync(path.join(testDir, 'package.json'), JSON.stringify(packageJson, null, 2)); + + const config = loadConfig(); + + expect(config.docsDir).toBe('docs'); // default value + }); + + it('should silently ignore invalid package.json', () => { + fs.writeFileSync(path.join(testDir, 'package.json'), 'invalid json{'); + + const config = loadConfig(); + + expect(config.docsDir).toBe('docs'); // default value + }); + }); + + describe('Environment variable support', () => { + it('should load configuration from environment variables', () => { + process.env.CODEREF_DOCS_DIR = 'envdocs'; + process.env.CODEREF_IGNORE_FILE = '.envignore'; + process.env.CODEREF_VERBOSE = 'true'; + + const config = loadConfig(); + + expect(config.docsDir).toBe('envdocs'); + expect(config.ignoreFile).toBe('.envignore'); + expect(config.verbose).toBe(true); + }); + + it('should handle CODEREF_PROJECT_ROOT', () => { + const customRoot = path.join(testDir, 'custom'); + fs.mkdirSync(customRoot); + process.env.CODEREF_PROJECT_ROOT = customRoot; + + const config = loadConfig(); + + expect(config.projectRoot).toBe(customRoot); + }); + + it('should parse verbose as boolean', () => { + process.env.CODEREF_VERBOSE = 'false'; + const config = loadConfig(); + expect(config.verbose).toBe(false); + }); + }); + + describe('Configuration precedence', () => { + beforeEach(() => { + // Set up all config sources + fs.writeFileSync( + path.join(testDir, 'package.json'), + JSON.stringify({ + name: 'test', + coderef: { docsDir: 'package-docs', verbose: false }, + }) + ); + + fs.writeFileSync( + path.join(testDir, '.coderefrc.json'), + JSON.stringify({ docsDir: 'rc-docs', ignoreFile: '.rcignore' }) + ); + + process.env.CODEREF_DOCS_DIR = 'env-docs'; + }); + + it('should prioritize programmatic options over all other sources', () => { + const config = loadConfig({ docsDir: 'programmatic-docs' }); + expect(config.docsDir).toBe('programmatic-docs'); + }); + + it('should prioritize env vars over config files', () => { + const config = loadConfig(); + expect(config.docsDir).toBe('env-docs'); + }); + + it('should prioritize .coderefrc.json over package.json', () => { + delete process.env.CODEREF_DOCS_DIR; + const config = loadConfig(); + expect(config.docsDir).toBe('rc-docs'); + expect(config.ignoreFile).toBe('.rcignore'); + }); + + it('should use package.json when higher priority sources dont specify a field', () => { + delete process.env.CODEREF_DOCS_DIR; + delete process.env.CODEREF_VERBOSE; + + fs.writeFileSync( + path.join(testDir, '.coderefrc.json'), + JSON.stringify({ ignoreFile: '.rcignore' }) + ); + + const config = loadConfig(); + expect(config.verbose).toBe(false); // from package.json + }); + }); + + describe('Programmatic options', () => { + it('should accept programmatic options', () => { + const config = loadConfig({ + projectRoot: testDir, + docsDir: 'custom-docs', + verbose: true, + targets: ['file1.md', 'file2.md'], + }); + + expect(config.docsDir).toBe('custom-docs'); + expect(config.verbose).toBe(true); + expect(config.targets).toEqual(['file1.md', 'file2.md']); + }); + + it('should merge programmatic options with defaults', () => { + const config = loadConfig({ + docsDir: 'custom-docs', + }); + + expect(config.docsDir).toBe('custom-docs'); + expect(config.projectRoot).toBe(testDir); + expect(config.ignoreFile).toBe('.docsignore'); + }); + }); + + describe('Configuration validation', () => { + it('should resolve relative projectRoot to absolute path', () => { + const config = loadConfig({ projectRoot: '.' }); + expect(path.isAbsolute(config.projectRoot)).toBe(true); + }); + + it('should throw error if projectRoot does not exist', () => { + const nonExistentPath = path.join(testDir, 'nonexistent'); + + expect(() => loadConfig({ projectRoot: nonExistentPath })).toThrow( + 'Project root does not exist' + ); + }); + + it('should throw error if docsDir is empty', () => { + expect(() => loadConfig({ docsDir: '' })).toThrow('docsDir cannot be empty'); + }); + + it('should throw error if docsDir is absolute path', () => { + expect(() => loadConfig({ docsDir: '/absolute/path' })).toThrow( + 'docsDir must be a relative path' + ); + }); + }); + }); + + describe('loadFixConfig', () => { + it('should return fix-specific defaults', () => { + const config = loadFixConfig(); + + expect(config.dryRun).toBe(false); + expect(config.auto).toBe(false); + expect(config.backup).toBe(true); + }); + + it('should merge fix options with base config', () => { + const config = loadFixConfig({ + docsDir: 'custom-docs', + dryRun: true, + auto: true, + backup: false, + }); + + expect(config.docsDir).toBe('custom-docs'); + expect(config.dryRun).toBe(true); + expect(config.auto).toBe(true); + expect(config.backup).toBe(false); + }); + + it('should inherit all base configuration options', () => { + fs.writeFileSync( + path.join(testDir, '.coderefrc.json'), + JSON.stringify({ docsDir: 'documentation', verbose: true }) + ); + + const config = loadFixConfig({ dryRun: true }); + + expect(config.docsDir).toBe('documentation'); + expect(config.verbose).toBe(true); + expect(config.dryRun).toBe(true); + }); + }); + + describe('Helper functions', () => { + let config: CodeRefConfig; + + beforeEach(() => { + config = loadConfig(); + }); + + describe('resolveProjectPath', () => { + it('should resolve relative path to absolute path', () => { + const resolved = resolveProjectPath(config, 'src/index.ts'); + expect(resolved).toBe(path.join(testDir, 'src/index.ts')); + }); + + it('should handle paths with ..', () => { + const resolved = resolveProjectPath(config, '../parent/file.ts'); + expect(resolved).toBe(path.join(testDir, '../parent/file.ts')); + }); + }); + + describe('getDocsPath', () => { + it('should return absolute path to docs directory', () => { + const docsPath = getDocsPath(config); + expect(docsPath).toBe(path.join(testDir, 'docs')); + }); + + it('should use configured docsDir', () => { + const customConfig = loadConfig({ docsDir: 'documentation' }); + const docsPath = getDocsPath(customConfig); + expect(docsPath).toBe(path.join(testDir, 'documentation')); + }); + }); + + describe('getIgnoreFilePath', () => { + it('should return absolute path to ignore file when configured', () => { + const ignorePath = getIgnoreFilePath(config); + expect(ignorePath).toBe(path.join(testDir, '.docsignore')); + }); + + it('should return null when ignoreFile is not configured', () => { + const customConfig = loadConfig({ ignoreFile: undefined }); + const ignorePath = getIgnoreFilePath(customConfig); + expect(ignorePath).toBe(null); + }); + + it('should use custom ignoreFile path', () => { + const customConfig = loadConfig({ ignoreFile: '.customignore' }); + const ignorePath = getIgnoreFilePath(customConfig); + expect(ignorePath).toBe(path.join(testDir, '.customignore')); + }); + }); + }); + + describe('Real-world scenarios', () => { + it('should work with a typical project setup', () => { + const docsDir = path.join(testDir, 'docs'); + fs.mkdirSync(docsDir); + fs.writeFileSync(path.join(docsDir, 'README.md'), '# Documentation'); + fs.writeFileSync(path.join(testDir, '.docsignore'), '*.tmp.md'); + + const config = loadConfig(); + + expect(config.projectRoot).toBe(testDir); + expect(getDocsPath(config)).toBe(docsDir); + expect(getIgnoreFilePath(config)).toBe(path.join(testDir, '.docsignore')); + }); + + it('should handle monorepo structure with custom docsDir', () => { + const packagesDir = path.join(testDir, 'packages/mypackage'); + fs.mkdirSync(packagesDir, { recursive: true }); + + fs.writeFileSync( + path.join(packagesDir, '.coderefrc.json'), + JSON.stringify({ docsDir: '../../docs' }) + ); + + process.chdir(packagesDir); + const config = loadConfig(); + + expect(config.docsDir).toBe('../../docs'); + expect(getDocsPath(config)).toBe(path.join(packagesDir, '../../docs')); + }); + }); +}); diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..01acfeb --- /dev/null +++ b/src/config.ts @@ -0,0 +1,288 @@ +/** + * Configuration system for @cawpea/coderef + * + * Supports loading configuration from: + * - .coderefrc.json file + * - package.json "coderef" field + * - Environment variables (CODEREF_*) + * - Programmatic options + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * Base configuration for CODE_REF validation + */ +export interface CodeRefConfig { + /** + * Project root directory (default: process.cwd()) + */ + projectRoot: string; + + /** + * Documentation directory relative to projectRoot (default: "docs") + */ + docsDir: string; + + /** + * Path to ignore file relative to projectRoot (default: ".docsignore") + */ + ignoreFile?: string; + + /** + * Additional ignore patterns (globs) + */ + ignorePatterns?: string[]; + + /** + * Enable verbose logging + */ + verbose?: boolean; + + /** + * Specific files or directories to validate (relative to docsDir) + */ + targets?: string[]; +} + +/** + * Configuration for CODE_REF fix command + */ +export interface CodeRefFixConfig extends CodeRefConfig { + /** + * Dry-run mode: show what would be fixed without modifying files + */ + dryRun?: boolean; + + /** + * Auto-fix mode: apply fixes without prompting + */ + auto?: boolean; + + /** + * Create backup before fixing (default: true) + */ + backup?: boolean; +} + +/** + * Partial configuration that can be provided by users + */ +export type PartialCodeRefConfig = Partial; +export type PartialCodeRefFixConfig = Partial; + +/** + * Get default configuration values + * Note: Function to ensure process.cwd() is evaluated at call time + */ +function getDefaultConfig(): CodeRefConfig { + return { + projectRoot: process.cwd(), + docsDir: 'docs', + ignoreFile: '.docsignore', + verbose: false, + }; +} + +/** + * Load configuration from .coderefrc.json file + */ +function loadConfigFile(projectRoot: string): PartialCodeRefConfig | null { + const configPath = path.join(projectRoot, '.coderefrc.json'); + + if (!fs.existsSync(configPath)) { + return null; + } + + try { + const content = fs.readFileSync(configPath, 'utf-8'); + return JSON.parse(content); + } catch (error) { + throw new Error( + `Failed to load .coderefrc.json: ${error instanceof Error ? error.message : String(error)}` + ); + } +} + +/** + * Load configuration from package.json "coderef" field + */ +function loadPackageJsonConfig(projectRoot: string): PartialCodeRefConfig | null { + const packageJsonPath = path.join(projectRoot, 'package.json'); + + if (!fs.existsSync(packageJsonPath)) { + return null; + } + + try { + const content = fs.readFileSync(packageJsonPath, 'utf-8'); + const packageJson = JSON.parse(content); + return packageJson.coderef || null; + } catch (_error) { + // Silently ignore package.json parsing errors + return null; + } +} + +/** + * Load configuration from environment variables + */ +function loadEnvConfig(): PartialCodeRefConfig { + const config: PartialCodeRefConfig = {}; + + if (process.env.CODEREF_PROJECT_ROOT) { + config.projectRoot = process.env.CODEREF_PROJECT_ROOT; + } + + if (process.env.CODEREF_DOCS_DIR) { + config.docsDir = process.env.CODEREF_DOCS_DIR; + } + + if (process.env.CODEREF_IGNORE_FILE) { + config.ignoreFile = process.env.CODEREF_IGNORE_FILE; + } + + if (process.env.CODEREF_VERBOSE) { + config.verbose = process.env.CODEREF_VERBOSE === 'true'; + } + + return config; +} + +/** + * Merge configuration objects with proper precedence + * + * Precedence (highest to lowest): + * 1. Programmatic options + * 2. Environment variables + * 3. .coderefrc.json + * 4. package.json "coderef" field + * 5. Default values + */ +function mergeConfigs( + defaultConfig: CodeRefConfig, + ...configs: (PartialCodeRefConfig | null)[] +): CodeRefConfig { + const merged: Partial = {}; + + for (const config of configs) { + if (config) { + Object.assign(merged, config); + } + } + + return { + ...defaultConfig, + ...merged, + } as CodeRefConfig; +} + +/** + * Validate configuration values + */ +function validateConfig(config: CodeRefConfig): void { + // Ensure projectRoot is an absolute path + if (!path.isAbsolute(config.projectRoot)) { + config.projectRoot = path.resolve(config.projectRoot); + } + + // Validate projectRoot exists + if (!fs.existsSync(config.projectRoot)) { + throw new Error(`Project root does not exist: ${config.projectRoot}`); + } + + // Validate docsDir (it's okay if it doesn't exist yet) + if (!config.docsDir || config.docsDir.trim() === '') { + throw new Error('docsDir cannot be empty'); + } + + // Ensure docsDir is not absolute + if (path.isAbsolute(config.docsDir)) { + throw new Error('docsDir must be a relative path'); + } +} + +/** + * Load and merge configuration from all sources + * + * @param options - Programmatic configuration options + * @returns Complete configuration object + */ +export function loadConfig(options: PartialCodeRefConfig = {}): CodeRefConfig { + // Get default configuration (evaluated at call time) + const defaultConfig = getDefaultConfig(); + + // Determine projectRoot first (needed for loading config files) + const projectRoot = options.projectRoot || process.env.CODEREF_PROJECT_ROOT || process.cwd(); + + // Load from all sources + const packageJsonConfig = loadPackageJsonConfig(projectRoot); + const fileConfig = loadConfigFile(projectRoot); + const envConfig = loadEnvConfig(); + + // Merge with proper precedence + const config = mergeConfigs(defaultConfig, packageJsonConfig, fileConfig, envConfig, options); + + // Validate final configuration + validateConfig(config); + + return config; +} + +/** + * Load configuration for fix command + * + * @param options - Programmatic configuration options for fix + * @returns Complete fix configuration object + */ +export function loadFixConfig(options: PartialCodeRefFixConfig = {}): CodeRefFixConfig { + const baseConfig = loadConfig(options); + + // Fix-specific defaults + const fixDefaults = { + dryRun: false, + auto: false, + backup: true, + }; + + return { + ...baseConfig, + ...fixDefaults, + ...options, + }; +} + +/** + * Resolve a path relative to projectRoot + * + * @param config - Configuration object + * @param relativePath - Path relative to projectRoot + * @returns Absolute path + */ +export function resolveProjectPath(config: CodeRefConfig, relativePath: string): string { + return path.join(config.projectRoot, relativePath); +} + +/** + * Get absolute path to docs directory + * + * @param config - Configuration object + * @returns Absolute path to docs directory + */ +export function getDocsPath(config: CodeRefConfig): string { + return resolveProjectPath(config, config.docsDir); +} + +/** + * Get absolute path to ignore file (if configured) + * + * @param config - Configuration object + * @returns Absolute path to ignore file, or null if not configured + */ +export function getIgnoreFilePath(config: CodeRefConfig): string | null { + if (!config.ignoreFile) { + return null; + } + return resolveProjectPath(config, config.ignoreFile); +} diff --git a/src/core/validate.test.ts b/src/core/validate.test.ts new file mode 100644 index 0000000..7674149 --- /dev/null +++ b/src/core/validate.test.ts @@ -0,0 +1,813 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { jest } from '@jest/globals'; + +import type { CodeRef } from '@/utils/types'; +import { extractCodeRefs, findMarkdownFiles, validateCodeRef } from '@/core/validate'; +import type { CodeRefConfig } from '@/config'; + +// テスト用のモックプロジェクトルート +const mockProjectRoot = '/project'; + +// テスト用の設定オブジェクト +const mockConfig: CodeRefConfig = { + projectRoot: mockProjectRoot, + docsDir: 'docs', + ignoreFile: '.docsignore', + verbose: false, +}; + +// モックの設定 +jest.mock('fs'); +jest.mock('path', () => { + const actualPath = jest.requireActual('path'); + return { + ...actualPath, + sep: '/', + join: jest.fn((...args: string[]) => args.join('/')), + resolve: jest.fn((...args: string[]) => { + // 引数がない場合は現在のディレクトリ + if (args.length === 0) { + return mockProjectRoot; + } + // 1つ目の引数が絶対パスの場合 + if (args[0] && args[0].startsWith('/')) { + // 絶対パスから相対パスを解決 + return args.reduce((acc: string, arg: string) => { + if (arg === '..') { + // 親ディレクトリに移動 + const parts = acc.split('/').filter(Boolean); + parts.pop(); + return `/${parts.join('/')}`; + } else if (arg === '.') { + // 現在のディレクトリ + return acc; + } else if (arg && !arg.startsWith('/')) { + // 相対パスを追加 + return `${acc}/${arg}`; + } + return arg || acc; + }, ''); + } + // デフォルト: 全ての引数を結合 + return `/${args.filter((arg) => arg && arg !== '.').join('/')}`; + }), + relative: jest.fn((from: string, to: string) => to.replace(`${from}/`, '')), + }; +}); + +const mockedFs = fs as jest.Mocked; +const mockedPath = path as jest.Mocked; + +describe('validate-docs-code', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('findMarkdownFiles', () => { + it('マークダウンファイルのみを返すこと', () => { + // モックデータの設定 + const mockDirents = [ + { name: 'file1.md', isDirectory: () => false, isFile: () => true }, + { name: 'file2.txt', isDirectory: () => false, isFile: () => true }, + { name: 'subdir', isDirectory: () => true, isFile: () => false }, + ]; + + const mockSubdirents = [{ name: 'file3.md', isDirectory: () => false, isFile: () => true }]; + + mockedFs.readdirSync + .mockReturnValueOnce(mockDirents as any) + .mockReturnValueOnce(mockSubdirents as any); + + const result = findMarkdownFiles('/test'); + + expect(result).toEqual(['/test/file1.md', '/test/subdir/file3.md']); + expect(mockedFs.readdirSync).toHaveBeenCalledWith('/test', { withFileTypes: true }); + expect(mockedFs.readdirSync).toHaveBeenCalledWith('/test/subdir', { withFileTypes: true }); + }); + + it('空のディレクトリでは空の配列を返すこと', () => { + mockedFs.readdirSync.mockReturnValue([]); + + const result = findMarkdownFiles('/empty'); + + expect(result).toEqual([]); + }); + }); + + describe('extractCodeRefs', () => { + it('CODE_REFコメントを正しく抽出すること', () => { + const content = ` +# テストドキュメント + + +コードの例: + + +特定の行範囲: + + +他のファイル: + `; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(3); + + expect(result[0]).toMatchObject({ + fullMatch: '', + refPath: 'src/example.ts', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + docLineNumber: 4, + }); + + expect(result[1]).toMatchObject({ + fullMatch: '', + refPath: 'src/example.ts', + startLine: 10, + endLine: 20, + docFile: '/docs/test.md', + docLineNumber: 7, + }); + + expect(result[2]).toMatchObject({ + fullMatch: '', + refPath: 'src/other.js', + startLine: 5, + endLine: 15, + docFile: '/docs/test.md', + docLineNumber: 10, + }); + }); + + it('CODE_REFがない場合は空の配列を返すこと', () => { + const content = ` +# テストドキュメント + +これは通常のマークダウンファイルです。 + `; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toEqual([]); + }); + + it('スペースが含まれるCODE_REFを正しく処理すること', () => { + const content = ``; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(1); + expect(result[0].refPath).toBe('src/example.ts'); + }); + }); + + describe('validateCodeRef', () => { + // beforeEachは親のdescribeブロックで設定されているものを使用 + + it('有効なファイル参照では空のエラー配列を返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toEqual([]); + }); + + it('存在しないファイルでFILE_NOT_FOUNDエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/missing.ts', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(false); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('FILE_NOT_FOUND'); + expect(result[0].message).toBe('Referenced file not found: src/missing.ts'); + }); + + it('パストラバーサルでPATH_TRAVERSALエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: '../../../etc/passwd', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + }; + + // 一時的にモックを上書きして、プロジェクトルート外のパスを返す + mockedPath.resolve.mockImplementationOnce(() => '/etc/passwd'); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('PATH_TRAVERSAL'); + }); + + it('無効な開始行番号でINVALID_LINE_NUMBERエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 0, + endLine: 5, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue('line1\nline2\nline3\nline4\nline5\n'); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('INVALID_LINE_NUMBER'); + }); + + it('終了行が総行数を超える場合にLINE_OUT_OF_RANGEエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 1, + endLine: 10, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue('line1\nline2\nline3\n'); // 3行のみ + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('LINE_OUT_OF_RANGE'); + }); + + it('開始行が終了行より大きい場合にINVALID_RANGEエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 10, + endLine: 5, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue( + 'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\nline11\n' + ); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('INVALID_RANGE'); + }); + + it('ファイル読み込みエラーでREAD_ERRORエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 1, + endLine: 5, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockImplementation(() => { + throw new Error('Permission denied'); + }); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('READ_ERROR'); + expect(result[0].message).toBe('Failed to read file: Permission denied'); + }); + + it('有効な行範囲指定では空のエラー配列を返すこと(コードブロックがない場合)', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 1, + endLine: 3, + docFile: '/docs/test.md', + // codeBlockがundefinedの場合、コード内容検証はCODE_BLOCK_MISSINGを返す + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue('line1\nline2\nline3\nline4\nline5\n'); + + const result = validateCodeRef(ref, mockConfig); + + // 既存の検証(ファイル存在、行範囲)は通過するが、 + // コードブロックがないため CODE_BLOCK_MISSING が発生する + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_BLOCK_MISSING'); + }); + }); + + describe('統合テスト', () => { + it('複数のエラータイプを同時に検出すること', () => { + // パストラバーサル + ファイル不存在の組み合わせ + const ref: CodeRef = { + fullMatch: '', + refPath: '../../../etc/passwd', + startLine: 1, + endLine: 5, + docFile: '/docs/test.md', + }; + + // 一時的にモックを上書きして、プロジェクトルート外のパスを返す + mockedPath.resolve.mockImplementationOnce(() => '/etc/passwd'); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('PATH_TRAVERSAL'); + }); + + it('CODE_REF正規表現パターンが正しく動作すること', () => { + const testCases = [ + { + input: '', + expected: { refPath: 'src/example.ts', startLine: null, endLine: null }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: 10, endLine: 20 }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: 5, endLine: 15 }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: null, endLine: null }, + }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = extractCodeRefs(input, '/test.md'); + expect(result).toHaveLength(1); + expect(result[0].refPath).toBe(expected.refPath); + expect(result[0].startLine).toBe(expected.startLine); + expect(result[0].endLine).toBe(expected.endLine); + }); + }); + + it('エッジケースの正規表現パターンを正しく処理すること', () => { + const testCases = [ + { + input: '', + expected: { refPath: 'src/example.ts', startLine: null, endLine: null }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: 10, endLine: 20 }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: 5, endLine: 15 }, + }, + { + input: '', + expected: { refPath: 'src/example.ts', startLine: null, endLine: null }, + }, + { + input: '', + expected: { refPath: 'path/with spaces/file.ts', startLine: 1, endLine: 2 }, + }, + { + input: '', + expected: { refPath: 'file-with-dashes.ts', startLine: null, endLine: null }, + }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = extractCodeRefs(input, '/test.md'); + expect(result).toHaveLength(1); + expect(result[0].refPath).toBe(expected.refPath); + expect(result[0].startLine).toBe(expected.startLine); + expect(result[0].endLine).toBe(expected.endLine); + }); + }); + + it('複数のCODE_REFが同一ドキュメント内にある場合を処理すること', () => { + const content = ` +# ドキュメント + + + + + +テキストの間に + + + `; + + const result = extractCodeRefs(content, '/docs/multiple.md'); + + expect(result).toHaveLength(4); + expect(result[0].refPath).toBe('src/a.ts'); + expect(result[1].refPath).toBe('src/b.ts'); + expect(result[2].refPath).toBe('src/c.js'); + expect(result[3].refPath).toBe('src/d.ts'); + }); + }); + + describe('コード内容検証', () => { + beforeEach(() => { + // existsSyncは常にtrueを返す + mockedFs.existsSync.mockReturnValue(true); + }); + + it('CODE_LOCATION_MISMATCH: コードは一致するが行数が異なる場合', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 5, + endLine: 7, + docFile: '/docs/test.md', + codeBlock: 'const x = 1;\nconst y = 2;\nconst z = 3;', + }; + + const fileContent = `line1 +line2 +const x = 1; +const y = 2; +const z = 3; +line6 +line7 +line8`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_LOCATION_MISMATCH'); + expect(result[0].message).toContain('src/example.ts'); + expect(result[0].message).toContain('expect: 5-7'); + expect(result[0].message).toContain('result: 3-5'); + expect(result[0].suggestedLines).toEqual({ start: 3, end: 5 }); + }); + + it('CODE_CONTENT_MISMATCH: 指定行のコードが異なる場合', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 2, + endLine: 3, + docFile: '/docs/test.md', + codeBlock: 'const x = 1;\nconst y = 2;', + }; + + const fileContent = `line1 +const a = 100; +const b = 200; +line4`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_CONTENT_MISMATCH'); + expect(result[0].message).toContain('src/example.ts'); + expect(result[0].message).toContain('Code does not match'); + }); + + it('CODE_BLOCK_MISSING: CODE_REFの後にコードブロックがない場合', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 1, + endLine: 3, + docFile: '/docs/test.md', + // codeBlockがundefined + }; + + mockedFs.readFileSync.mockReturnValue('line1\nline2\nline3'); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_BLOCK_MISSING'); + expect(result[0].message).toContain('Code block not found after CODE_REF'); + expect(result[0].message).toContain('src/example.ts'); + }); + + it('複数の一致がファイル内にある場合、最初の出現を提案すること', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 10, + endLine: 11, + docFile: '/docs/test.md', + codeBlock: 'const x = 1;\nconst y = 2;', + }; + + const fileContent = `const x = 1; +const y = 2; +line3 +const x = 1; +const y = 2; +line6 +const x = 1; +const y = 2; +line9 +line10 +line11 +line12`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = validateCodeRef(ref, mockConfig); + + // LINE_OUT_OF_RANGEとCODE_LOCATION_MISMATCHの両方が発生する可能性がある + // ここでは CODE_LOCATION_MISMATCH をチェック + const locationMismatch = result.find((e) => e.type === 'CODE_LOCATION_MISMATCH'); + expect(locationMismatch).toBeDefined(); + expect(locationMismatch!.message).toContain('Code found in 3 locations'); + expect(locationMismatch!.message).toContain('result: 1-2'); + expect(locationMismatch!.suggestedLines).toEqual({ start: 1, end: 2 }); + }); + + it('全体参照(行数なし)の場合、コード検証をスキップすること', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + codeBlock: 'some code', + }; + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toEqual([]); + }); + + it('コード内容が完全に一致する場合、エラーを返さないこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 2, + endLine: 4, + docFile: '/docs/test.md', + codeBlock: 'const x = 1;\nconst y = 2;\nconst z = 3;', + }; + + const fileContent = `line1 +const x = 1; +const y = 2; +const z = 3; +line5`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toEqual([]); + }); + + it('空白の違いを吸収してコードが一致する場合、エラーを返さないこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: 2, + endLine: 3, + docFile: '/docs/test.md', + codeBlock: 'const x = 1;\nconst y = 2;', + }; + + const fileContent = `line1 + const x = 1; + const y = 2; +line4`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toEqual([]); + }); + + // 新規テスト: シンボル指定のみでコードブロックなし + it('シンボル指定のみでコードブロックがない場合、CODE_BLOCK_MISSINGエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + symbolPath: 'myFunction', + memberName: 'myFunction', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + // codeBlockがundefined + }; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue('function myFunction() { return 42; }'); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_BLOCK_MISSING'); + expect(result[0].message).toContain('CODE_REF with symbol specification'); + expect(result[0].message).toContain('src/example.ts#myFunction'); + }); + + it('シンボル指定のみで空のコードブロックがある場合、CODE_BLOCK_MISSINGエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + symbolPath: 'myFunction', + memberName: 'myFunction', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + codeBlock: ' \n\n ', // 空白のみ + }; + + mockedFs.existsSync.mockReturnValue(true); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('CODE_BLOCK_MISSING'); + }); + + it('ファイル全体参照でコードブロックがなくてもエラーを返さないこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + // シンボル指定なし、コードブロックもなし + }; + + mockedFs.existsSync.mockReturnValue(true); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toEqual([]); + }); + + it('シンボル指定のみでコードブロックがある場合、検証が実行されること', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'src/example.ts', + symbolPath: 'myFunction', + memberName: 'myFunction', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + codeBlock: 'function myFunction() {\n return 42;\n}', + }; + + mockedFs.existsSync.mockReturnValue(true); + // コードブロックがある場合は、シンボル全体との比較が行われる + // この検証は既存のロジックで実装されている + const result = validateCodeRef(ref, mockConfig); + + // エラーがないか、またはシンボルが見つからないなどの別のエラー + // 既存のロジックに依存するため、ここでは詳細な検証は行わない + expect(Array.isArray(result)).toBe(true); + }); + }); + + describe('extractCodeRefs - シンボル記法', () => { + it('クラス名+メソッド名のシンボルパスをパースすること', () => { + const content = ` +# Test Document + + +`; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + refPath: 'backend/src/services/evaluation.service.ts', + symbolPath: 'EvaluationService#evaluateDesign', + className: 'EvaluationService', + memberName: 'evaluateDesign', + startLine: 30, + endLine: 172, + }); + }); + + it('関数名のみのシンボルパスをパースすること', () => { + const content = ` +# Test Document + + +`; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + refPath: 'backend/src/utils/helper.ts', + symbolPath: 'helperFunction', + memberName: 'helperFunction', + startLine: 10, + endLine: 20, + }); + expect(result[0].className).toBeUndefined(); + }); + + it('シンボルパスのみ(行番号なし)をパースすること', () => { + const content = ` +# Test Document + + +`; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + refPath: 'backend/src/services/evaluation.service.ts', + symbolPath: 'EvaluationService#evaluateDesign', + className: 'EvaluationService', + memberName: 'evaluateDesign', + startLine: null, + endLine: null, + }); + }); + + it('既存の記法(行番号のみ)も引き続き動作すること', () => { + const content = ` +# Test Document + + +`; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + refPath: 'backend/src/services/evaluation.service.ts', + startLine: 30, + endLine: 172, + }); + expect(result[0].symbolPath).toBeUndefined(); + expect(result[0].className).toBeUndefined(); + expect(result[0].memberName).toBeUndefined(); + }); + + it('シンボル記法と行番号記法を混在させられること', () => { + const content = ` +# Test Document + + + + +`; + + const result = extractCodeRefs(content, '/docs/test.md'); + + expect(result).toHaveLength(2); + expect(result[0].symbolPath).toBe('EvaluationService#evaluateDesign'); + expect(result[1].symbolPath).toBeUndefined(); + }); + }); + + describe('validateSymbolRef', () => { + it('TypeScript/JavaScript以外のファイルでエラーを返すこと', () => { + const ref: CodeRef = { + fullMatch: '', + refPath: 'README.md', + symbolPath: 'MyClass#method', + className: 'MyClass', + memberName: 'method', + startLine: null, + endLine: null, + docFile: '/docs/test.md', + }; + + mockedFs.existsSync.mockReturnValue(true); + + const result = validateCodeRef(ref, mockConfig); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('NOT_TYPESCRIPT_FILE'); + expect(result[0].message).toContain('TypeScript/JavaScript'); + }); + }); +}); diff --git a/src/core/validate.ts b/src/core/validate.ts new file mode 100644 index 0000000..80f1644 --- /dev/null +++ b/src/core/validate.ts @@ -0,0 +1,415 @@ +/** + * Core validation logic for CODE_REF references in markdown documentation + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +import { + findSymbolInAST, + isTypeScriptOrJavaScript, + parseSymbolPath, + selectBestSymbolMatch, +} from '@/utils/ast-symbol-search'; +import { + compareCodeContent, + extractLinesFromFile, + searchCodeInFile, +} from '@/utils/code-comparison'; +import { associateCodeBlocksWithRefs } from '@/utils/markdown'; +import type { CodeRef, CodeRefError } from '@/utils/types'; +import { loadConfig, type CodeRefConfig } from '@/config'; + +// CODE_REF pattern constant +const CODE_REF_PATTERN = //g; + +/** + * Recursively walk directory to get markdown files + */ +export function findMarkdownFiles(dir: string): string[] { + const files: string[] = []; + + function walk(currentPath: string): void { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(currentPath, entry.name); + + if (entry.isDirectory()) { + walk(fullPath); + } else if (entry.isFile() && entry.name.endsWith('.md')) { + files.push(fullPath); + } + } + } + + walk(dir); + return files; +} + +/** + * Detect code block and inline code ranges + */ +function getCodeBlockRanges(content: string): { start: number; end: number }[] { + const ranges: { start: number; end: number }[] = []; + + // Triple backtick code blocks + const codeBlockPattern = /```[\s\S]*?```/g; + let match: RegExpExecArray | null; + + while ((match = codeBlockPattern.exec(content)) !== null) { + ranges.push({ + start: match.index, + end: match.index + match[0].length, + }); + } + + // Inline code (backticks) + const inlineCodePattern = /`[^`\n]+?`/g; + while ((match = inlineCodePattern.exec(content)) !== null) { + ranges.push({ + start: match.index, + end: match.index + match[0].length, + }); + } + + return ranges; +} + +/** + * Check if position is inside code block or inline code + */ +function isInsideCodeBlock(position: number, ranges: { start: number; end: number }[]): boolean { + return ranges.some((range) => position >= range.start && position < range.end); +} + +/** + * Extract CODE_REF comments + */ +export function extractCodeRefs(content: string, filePath: string): CodeRef[] { + const refs: CodeRef[] = []; + let match: RegExpExecArray | null; + + // Pre-detect code block and inline code ranges + const codeBlockRanges = getCodeBlockRanges(content); + + while ((match = CODE_REF_PATTERN.exec(content)) !== null) { + const [fullMatch, refPath, symbolPath, startLine, endLine] = match; + + // Exclude CODE_REFs inside code blocks or inline code (for sample display) + if (isInsideCodeBlock(match.index, codeBlockRanges)) { + continue; + } + + // Calculate line number from match position (1-indexed) + const beforeMatch = content.substring(0, match.index); + const docLineNumber = beforeMatch.split('\n').length; + + // Parse symbol path + let className: string | undefined; + let memberName: string | undefined; + if (symbolPath) { + const parsed = parseSymbolPath(symbolPath); + className = parsed.className; + memberName = parsed.memberName; + } + + refs.push({ + fullMatch, + refPath: refPath.trim(), + startLine: startLine ? parseInt(startLine, 10) : null, + endLine: endLine ? parseInt(endLine, 10) : null, + docFile: filePath, + docLineNumber, + codeBlockStartOffset: match.index, // Save CODE_REF comment position + symbolPath: symbolPath?.trim(), + className, + memberName, + }); + } + + // Associate code blocks + return associateCodeBlocksWithRefs(content, refs); +} + +/** + * Validate code content + */ +export function validateCodeContent(ref: CodeRef, config?: CodeRefConfig): CodeRefError[] { + const cfg = config || loadConfig(); + const projectRoot = cfg.projectRoot; + const errors: CodeRefError[] = []; + + // Process when line numbers not specified + if (ref.startLine === null || ref.endLine === null) { + // When symbol specified, validate exact match with entire symbol + if (ref.symbolPath) { + // Return error if no code block + if (!ref.codeBlock || ref.codeBlock.trim() === '') { + errors.push({ + type: 'CODE_BLOCK_MISSING', + message: `Code block not found after CODE_REF with symbol specification (${ref.refPath}#${ref.symbolPath}).`, + ref, + }); + return errors; + } + + // Get symbol range + const absolutePath = path.resolve(projectRoot, ref.refPath); + const fileContent = fs.readFileSync(absolutePath, 'utf-8'); + const matches = findSymbolInAST(fileContent, absolutePath, { + className: ref.className, + memberName: ref.memberName!, + }); + + if (matches.length > 0) { + const symbolMatch = matches[0]; + const symbolCode = extractLinesFromFile( + absolutePath, + symbolMatch.startLine, + symbolMatch.endLine + ); + + // Compare code block with entire symbol (ignore whitespace and newlines) + if (!compareCodeContent(symbolCode, ref.codeBlock)) { + errors.push({ + type: 'CODE_CONTENT_MISMATCH', + message: `Code block does not match entire symbol.`, + ref, + actualCode: symbolCode.substring(0, 200), + expectedCode: ref.codeBlock.substring(0, 200), + }); + } + } + return errors; + } + // Skip if no symbol specified (whole file reference) + return errors; + } + + // Continue normal validation if line numbers specified, even with symbol specification + + // If no code block + if (!ref.codeBlock || ref.codeBlock.trim() === '') { + const refLocation = + ref.startLine !== null ? `${ref.refPath}:${ref.startLine}-${ref.endLine}` : ref.refPath; + errors.push({ + type: 'CODE_BLOCK_MISSING', + message: `Code block not found after CODE_REF (${refLocation}).`, + ref, + }); + return errors; + } + + const absolutePath = path.resolve(projectRoot, ref.refPath); + + try { + // Get code from actual file at specified lines + const actualCode = extractLinesFromFile(absolutePath, ref.startLine, ref.endLine); + + // Compare code content + if (!compareCodeContent(actualCode, ref.codeBlock)) { + // Not matched → search entire file + const matches = searchCodeInFile(absolutePath, ref.codeBlock); + + if (matches.length > 0) { + // Code exists but at different location + const firstMatch = matches[0]; + const matchInfo = + matches.length > 1 ? `Code found in ${matches.length} locations. First occurrence: ` : ''; + + errors.push({ + type: 'CODE_LOCATION_MISMATCH', + message: `Line numbers do not match in ${ref.refPath}. ${matchInfo}(expect: ${ref.startLine}-${ref.endLine}, result: ${firstMatch.start}-${firstMatch.end})`, + ref, + suggestedLines: firstMatch, + }); + } else { + // Code content differs + errors.push({ + type: 'CODE_CONTENT_MISMATCH', + message: `Code does not match in ${ref.refPath}.`, + ref, + actualCode: actualCode.substring(0, 200), + expectedCode: ref.codeBlock.substring(0, 200), + }); + } + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + errors.push({ + type: 'READ_ERROR', + message: `Error occurred during code comparison: ${errorMessage}`, + ref, + }); + } + + return errors; +} + +/** + * Validate symbol specification + */ +export function validateSymbolRef(ref: CodeRef, config?: CodeRefConfig): CodeRefError[] { + const cfg = config || loadConfig(); + const projectRoot = cfg.projectRoot; + const errors: CodeRefError[] = []; + + // Skip if no symbol specification + if (!ref.symbolPath) { + return errors; + } + + const absolutePath = path.resolve(projectRoot, ref.refPath); + + // TypeScript/JavaScript file check + if (!isTypeScriptOrJavaScript(absolutePath)) { + errors.push({ + type: 'NOT_TYPESCRIPT_FILE', + message: `Symbol specification only supported for TypeScript/JavaScript files: ${ref.refPath}`, + ref, + }); + return errors; + } + + const fileContent = fs.readFileSync(absolutePath, 'utf-8'); + + try { + // Search for symbol + const matches = findSymbolInAST(fileContent, absolutePath, { + className: ref.className, + memberName: ref.memberName!, + }); + + if (matches.length === 0) { + // Symbol not found + errors.push({ + type: 'SYMBOL_NOT_FOUND', + message: `Symbol "${ref.symbolPath}" not found`, + ref, + }); + } else if (matches.length > 1 && !ref.startLine) { + // Multiple matches (no line number hint) + errors.push({ + type: 'MULTIPLE_SYMBOLS_FOUND', + message: `Symbol "${ref.symbolPath}" found in ${matches.length} locations. Please specify line numbers`, + ref, + foundSymbols: matches, + }); + } else { + // Validate if line numbers specified + if (ref.startLine && ref.endLine) { + const bestMatch = selectBestSymbolMatch(matches, { + start: ref.startLine, + end: ref.endLine, + }); + + if (bestMatch) { + // Check if range matches + if (bestMatch.startLine !== ref.startLine || bestMatch.endLine !== ref.endLine) { + errors.push({ + type: 'SYMBOL_RANGE_MISMATCH', + message: `Symbol "${ref.symbolPath}" range does not match (expected: ${ref.startLine}-${ref.endLine}, actual: ${bestMatch.startLine}-${bestMatch.endLine})`, + ref, + suggestedSymbol: bestMatch, + }); + } + } + } + } + } catch (error) { + errors.push({ + type: 'READ_ERROR', + message: `AST parsing error: ${error instanceof Error ? error.message : String(error)}`, + ref, + }); + } + + return errors; +} + +/** + * Validate existence of referenced file and line numbers + */ +export function validateCodeRef(ref: CodeRef, config?: CodeRefConfig): CodeRefError[] { + const cfg = config || loadConfig(); + const projectRoot = cfg.projectRoot; + const errors: CodeRefError[] = []; + + // Convert relative path to absolute path (relative to project root) + const absolutePath = path.resolve(projectRoot, ref.refPath); + + // Prevent path traversal attacks: validate stays within project root + if (!absolutePath.startsWith(projectRoot + path.sep)) { + errors.push({ + type: 'PATH_TRAVERSAL', + message: `Referenced path points outside project root: ${ref.refPath}`, + ref, + }); + return errors; + } + + // Check file existence + if (!fs.existsSync(absolutePath)) { + errors.push({ + type: 'FILE_NOT_FOUND', + message: `Referenced file not found: ${ref.refPath}`, + ref, + }); + return errors; + } + + // Check line count if line numbers specified + if (ref.startLine !== null && ref.endLine !== null) { + try { + const content = fs.readFileSync(absolutePath, 'utf-8'); + const lines = content.split('\n'); + const totalLines = lines.length; + + if (ref.startLine < 1) { + errors.push({ + type: 'INVALID_LINE_NUMBER', + message: `Start line number is invalid (less than 1): ${ref.startLine}`, + ref, + }); + } + + if (ref.endLine > totalLines) { + errors.push({ + type: 'LINE_OUT_OF_RANGE', + message: `End line number exceeds file line count: ${ref.endLine} > ${totalLines}`, + ref, + }); + } + + if (ref.startLine > ref.endLine) { + errors.push({ + type: 'INVALID_RANGE', + message: `Start line number is greater than end line number: ${ref.startLine} > ${ref.endLine}`, + ref, + }); + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + errors.push({ + type: 'READ_ERROR', + message: `Failed to read file: ${errorMessage}`, + ref, + }); + } + } + + // Symbol validation if symbol specified + if (errors.length === 0 && ref.symbolPath) { + const symbolErrors = validateSymbolRef(ref, cfg); + errors.push(...symbolErrors); + } + + // Validate code content (only if no existing errors) + if (errors.length === 0) { + const contentErrors = validateCodeContent(ref, cfg); + errors.push(...contentErrors); + } + + return errors; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..23399d6 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,65 @@ +/** + * @cawpea/coderef - Validate and fix code references in markdown documentation + * + * This is the main entry point for programmatic usage. + * For CLI usage, use the `coderef` command. + * + * @example + * ```typescript + * import { validateCodeRef, extractCodeRefs, loadConfig } from '@cawpea/coderef'; + * + * const config = loadConfig(); + * const refs = extractCodeRefs(markdownContent, 'docs/README.md'); + * const errors = validateCodeRef(refs[0], config); + * ``` + */ + +// Core validation functions +export { + findMarkdownFiles, + extractCodeRefs, + validateCodeRef, + validateCodeContent, + validateSymbolRef, +} from '@/core/validate'; + +// Configuration +export { + loadConfig, + loadFixConfig, + resolveProjectPath, + getDocsPath, + getIgnoreFilePath, + type CodeRefConfig, + type CodeRefFixConfig, + type PartialCodeRefConfig, + type PartialCodeRefFixConfig, +} from '@/config'; + +// Types +export type { + CodeRef, + CodeRefError, + SymbolMatch, + FixAction, + FixOptions, + FixResult, + ExpandedMatch, + GitExecOptions, +} from '@/utils/types'; + +// Utility functions +export { + isTypeScriptOrJavaScript, + findSymbolInAST, + parseSymbolPath, + selectBestSymbolMatch, +} from '@/utils/ast-symbol-search'; + +export { + compareCodeContent, + extractLinesFromFile, + searchCodeInFile, +} from '@/utils/code-comparison'; + +export { isIgnored, loadDocsignorePatterns } from '@/utils/ignore-pattern'; diff --git a/src/utils/ast-scope-expansion.test.ts b/src/utils/ast-scope-expansion.test.ts new file mode 100644 index 0000000..717d36e --- /dev/null +++ b/src/utils/ast-scope-expansion.test.ts @@ -0,0 +1,278 @@ +import { expandMatchToScope } from '@/utils/ast-scope-expansion'; + +describe('ast-scope-expansion', () => { + describe('expandMatchToScope', () => { + it('関数全体にマッチを拡張すること', () => { + const fileContent = ` +/** + * Test function + */ +function testFunction() { + const x = 1; + return x; +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 5, end: 5 }, // const x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, // JSDocコメントを含む + end: 7, + confidence: 'high', + expansionType: 'ast', + scopeType: 'function', + }); + }); + + it('クラスメソッド全体にマッチを拡張すること', () => { + const fileContent = ` +export class TestClass { + /** + * Test method + */ + testMethod() { + const x = 1; + return x; + } +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 7, end: 7 }, // const x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 2, // JSDocコメントを含む + confidence: 'high', + expansionType: 'ast', + scopeType: 'function', + }); + }); + + it('JSDocコメントがない場合、関数の開始行から拡張すること', () => { + const fileContent = ` +function testFunction() { + const x = 1; + return x; +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 2, end: 2 }, // const x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, // 関数の開始行 + end: 4, + confidence: 'high', + expansionType: 'ast', + scopeType: 'function', + }); + }); + + it('TypeScript/JavaScript以外のファイルの場合、元のマッチを返すこと', () => { + const fileContent = 'some python code'; + + const result = expandMatchToScope({ + filePath: '/test/file.py', + originalMatch: { start: 1, end: 1 }, + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, + end: 1, + confidence: 'low', + expansionType: 'none', + scopeType: 'unknown', + }); + }); + + it('構文エラーのあるファイルの場合、元のマッチを返すこと', () => { + const fileContent = 'function broken((('; + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 1, end: 1 }, + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, + end: 1, + confidence: 'low', + expansionType: 'none', + scopeType: 'unknown', + }); + }); + + it('親スコープが見つからない場合、元のマッチを返すこと', () => { + const fileContent = ` +// トップレベルのコメント +const x = 1; + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 2, end: 2 }, // const x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 2, + end: 2, + confidence: 'low', + expansionType: 'none', + scopeType: 'unknown', + }); + }); + + it('アロー関数全体にマッチを拡張すること', () => { + const fileContent = ` +/** + * Arrow function + */ +const arrowFunc = () => { + const x = 1; + return x; +}; + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 6, end: 6 }, // const x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + confidence: 'high', + expansionType: 'ast', + scopeType: 'function', + }); + }); + + it('クラス全体にマッチを拡張すること', () => { + const fileContent = ` +/** + * Test class + */ +export class TestClass { + private x: number; + + constructor() { + this.x = 1; + } +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 9, end: 9 }, // this.x = 1; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + confidence: 'high', + expansionType: 'ast', + }); + }); + + it('インターフェース全体にマッチを拡張すること', () => { + const fileContent = ` +/** + * Test interface + */ +interface TestInterface { + name: string; + age: number; +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 6, end: 6 }, // name: string; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, // JSDocコメントを含む + confidence: 'high', + expansionType: 'ast', + scopeType: 'interface', + }); + }); + + it('型エイリアス全体にマッチを拡張すること', () => { + const fileContent = ` +/** + * Test type alias + */ +type TestType = { + name: string; + age: number; +}; + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 6, end: 6 }, // name: string; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, // JSDocコメントを含む + confidence: 'high', + expansionType: 'ast', + scopeType: 'type', + }); + }); + + it('複数行のJSDocコメントを含む場合', () => { + const fileContent = ` +/** + * Multi-line JSDoc comment + * with multiple lines + * and descriptions + * @param x - parameter + * @returns result + */ +function testFunction(x: number): number { + return x * 2; +} + `.trim(); + + const result = expandMatchToScope({ + filePath: '/test/file.ts', + originalMatch: { start: 10, end: 10 }, // return x * 2; + fileContent, + }); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + start: 1, // 複数行JSDocの開始行 + end: 10, + confidence: 'high', + expansionType: 'ast', + scopeType: 'function', + }); + }); + }); +}); diff --git a/src/utils/ast-scope-expansion.ts b/src/utils/ast-scope-expansion.ts new file mode 100644 index 0000000..3f4042f --- /dev/null +++ b/src/utils/ast-scope-expansion.ts @@ -0,0 +1,306 @@ +/** + * AST解析によるスコープ拡張ユーティリティ + */ + +import * as path from 'path'; + +import { parse as parseTypeScript } from '@typescript-eslint/typescript-estree'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; + +import type { ExpandedMatch } from '@/utils/types'; + +/** + * マッチ拡張のオプション + */ +export interface MatchExpansionOptions { + filePath: string; + originalMatch: { start: number; end: number }; + fileContent: string; +} + +/** + * AST拡張の結果 + */ +interface ASTExpansionResult { + success: boolean; + expandedMatches?: ExpandedMatch[]; + error?: string; +} + +/** + * ファイルがTypeScript/JavaScriptかどうかを判定 + */ +function isTypeScriptOrJavaScript(filePath: string): boolean { + const ext = path.extname(filePath).toLowerCase(); + return ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext); +} + +/** + * マッチ位置を含むASTノードを見つける + */ +function findNodeAtLine(ast: TSESTree.Program, targetLine: number): TSESTree.Node | null { + let foundNode: TSESTree.Node | null = null; + + function visit(node: TSESTree.Node) { + if (!node.loc) return; + + const nodeStart = node.loc.start.line; + const nodeEnd = node.loc.end.line; + + // ターゲット行がノードの範囲内にある場合 + if (nodeStart <= targetLine && targetLine <= nodeEnd) { + foundNode = node; + + // 子ノードを探索(より具体的なノードを見つけるため) + const keys = Object.keys(node) as (keyof TSESTree.Node)[]; + for (const key of keys) { + const value = node[key]; + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + value.forEach((child) => { + if (child && typeof child === 'object' && 'type' in child) { + visit(child as TSESTree.Node); + } + }); + } else if ('type' in value) { + visit(value as TSESTree.Node); + } + } + } + } + } + + visit(ast); + return foundNode; +} + +/** + * ノードのスコープタイプを判定 + */ +function getScopeType(node: TSESTree.Node): ExpandedMatch['scopeType'] { + switch (node.type) { + case 'FunctionDeclaration': + case 'FunctionExpression': + case 'ArrowFunctionExpression': + case 'MethodDefinition': + return 'function'; + case 'ClassDeclaration': + case 'ClassExpression': + return 'class'; + case 'TSInterfaceDeclaration': + return 'interface'; + case 'TSTypeAliasDeclaration': + return 'type'; + case 'VariableDeclaration': + return 'const'; + default: + return 'unknown'; + } +} + +/** + * ノードの親スコープを探索 + */ +function findParentScope(ast: TSESTree.Program, targetNode: TSESTree.Node): TSESTree.Node | null { + let parentScope: TSESTree.Node | null = null; + let currentDepth = 0; + let targetDepth = -1; + + function visit(node: TSESTree.Node, depth: number) { + const isScope = + node.type === 'FunctionDeclaration' || + node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression' || + node.type === 'MethodDefinition' || + node.type === 'ClassDeclaration' || + node.type === 'ClassExpression' || + node.type === 'TSInterfaceDeclaration' || + node.type === 'TSTypeAliasDeclaration' || + (node.type === 'VariableDeclaration' && node.parent?.type === 'ExportNamedDeclaration'); + + if (node === targetNode) { + targetDepth = depth; + return; + } + + if (targetDepth === -1 && node.loc && targetNode.loc) { + const nodeStart = node.loc.start.line; + const nodeEnd = node.loc.end.line; + const targetStart = targetNode.loc.start.line; + const targetEnd = targetNode.loc.end.line; + + // ターゲットノードがこのノードの範囲内にある場合 + if (nodeStart <= targetStart && targetEnd <= nodeEnd && isScope && depth > currentDepth) { + parentScope = node; + currentDepth = depth; + } + } + + // 子ノードを探索 + const keys = Object.keys(node) as (keyof TSESTree.Node)[]; + for (const key of keys) { + const value = node[key]; + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + value.forEach((child) => { + if (child && typeof child === 'object' && 'type' in child) { + visit(child as TSESTree.Node, depth + 1); + } + }); + } else if ('type' in value) { + visit(value as TSESTree.Node, depth + 1); + } + } + } + } + + visit(ast, 0); + return parentScope; +} + +/** + * JSDocコメントを含めた開始位置を取得 + */ +function getStartLineWithJSDoc(node: TSESTree.Node, fileContent: string): number { + if (!node.loc) return 1; // locがない場合は1行目を返す + + const nodeStartLine = node.loc.start.line; + const lines = fileContent.split('\n'); + + // ノードの直前の行から逆順に探索 + for (let i = nodeStartLine - 2; i >= 0; i--) { + const line = lines[i].trim(); + + // JSDocコメントの終了を見つけた + if (line.endsWith('*/')) { + // JSDocコメントの開始を探す + for (let j = i; j >= 0; j--) { + if (lines[j].trim().startsWith('/**')) { + return j + 1; // 1-indexed + } + } + } + + // 空行またはコメント以外があったら終了 + if (line && !line.startsWith('//') && !line.startsWith('*')) { + break; + } + } + + return nodeStartLine; +} + +/** + * AST解析によるスコープ拡張を試みる + */ +function tryASTExpansion( + filePath: string, + originalMatch: { start: number; end: number }, + fileContent: string +): ASTExpansionResult { + try { + // TypeScript/JavaScriptファイルのみ処理 + if (!isTypeScriptOrJavaScript(filePath)) { + return { success: false, error: 'Not a TypeScript/JavaScript file' }; + } + + // ASTをパース + const ast = parseTypeScript(fileContent, { + loc: true, + range: true, + comment: true, + tokens: true, + }); + + // マッチ位置のノードを見つける + const matchStartNode = findNodeAtLine(ast, originalMatch.start); + const matchEndNode = findNodeAtLine(ast, originalMatch.end); + + if (!matchStartNode || !matchEndNode) { + return { success: false, error: 'Node not found at match position' }; + } + + // より具体的なノード(小さい方)を選択 + const targetNode = + matchStartNode.loc && matchEndNode.loc + ? matchStartNode.loc.end.line - matchStartNode.loc.start.line < + matchEndNode.loc.end.line - matchEndNode.loc.start.line + ? matchStartNode + : matchEndNode + : matchStartNode; + + // 親スコープを探索 + const parentScope = findParentScope(ast, targetNode); + + if (!parentScope?.loc) { + // 親スコープが見つからない場合、元のマッチを返す + return { + success: true, + expandedMatches: [ + { + start: originalMatch.start, + end: originalMatch.end, + confidence: 'low', + expansionType: 'none', + scopeType: 'unknown', + }, + ], + }; + } + + // JSDocを含めた開始位置を取得 + const startLine = getStartLineWithJSDoc(parentScope, fileContent); + const endLine = parentScope.loc.end.line; + const scopeType = getScopeType(parentScope); + + return { + success: true, + expandedMatches: [ + { + start: startLine, + end: endLine, + confidence: 'high', + expansionType: 'ast', + scopeType, + }, + ], + }; + } catch (error) { + if (error instanceof Error) { + if (error.name === 'SyntaxError' || error.message.includes('Unexpected token')) { + return { success: false, error: `Syntax error: ${error.message}` }; + } + return { success: false, error: error.message }; + } + return { success: false, error: String(error) }; + } +} + +/** + * マッチ位置をスコープ拡張する + * + * @param options 拡張オプション + * @returns 拡張されたマッチ + */ +export function expandMatchToScope(options: MatchExpansionOptions): ExpandedMatch[] { + const { filePath, originalMatch, fileContent } = options; + + // AST解析を試みる + const astResult = tryASTExpansion(filePath, originalMatch, fileContent); + + if (astResult.success && astResult.expandedMatches) { + return astResult.expandedMatches; + } + + // AST解析が失敗した場合、元のマッチを返す(フォールバック) + console.warn(`⚠️ AST parsing failed: ${astResult.error}`); + return [ + { + start: originalMatch.start, + end: originalMatch.end, + confidence: 'low', + expansionType: 'none', + scopeType: 'unknown', + }, + ]; +} diff --git a/src/utils/ast-symbol-search.test.ts b/src/utils/ast-symbol-search.test.ts new file mode 100644 index 0000000..e889c39 --- /dev/null +++ b/src/utils/ast-symbol-search.test.ts @@ -0,0 +1,525 @@ +import { + clearASTCache, + findSymbolInAST, + isTypeScriptOrJavaScript, + parseSymbolPath, + selectBestSymbolMatch, +} from '@/utils/ast-symbol-search'; +import type { SymbolMatch } from '@/utils/types'; + +describe('ast-symbol-search', () => { + afterEach(() => { + clearASTCache(); + }); + + describe('isTypeScriptOrJavaScript', () => { + it('TypeScript/JavaScriptファイルを正しく判定すること', () => { + expect(isTypeScriptOrJavaScript('/path/to/file.ts')).toBe(true); + expect(isTypeScriptOrJavaScript('/path/to/file.tsx')).toBe(true); + expect(isTypeScriptOrJavaScript('/path/to/file.js')).toBe(true); + expect(isTypeScriptOrJavaScript('/path/to/file.jsx')).toBe(true); + expect(isTypeScriptOrJavaScript('/path/to/file.mjs')).toBe(true); + expect(isTypeScriptOrJavaScript('/path/to/file.cjs')).toBe(true); + }); + + it('TypeScript/JavaScript以外のファイルを拒否すること', () => { + expect(isTypeScriptOrJavaScript('/path/to/file.py')).toBe(false); + expect(isTypeScriptOrJavaScript('/path/to/file.java')).toBe(false); + expect(isTypeScriptOrJavaScript('/path/to/file.md')).toBe(false); + expect(isTypeScriptOrJavaScript('/path/to/file.json')).toBe(false); + }); + }); + + describe('parseSymbolPath', () => { + it('関数名のみのシンボルパスをパースすること', () => { + const result = parseSymbolPath('functionName'); + expect(result).toEqual({ + memberName: 'functionName', + }); + }); + + it('クラス名+メソッド名のシンボルパスをパースすること', () => { + const result = parseSymbolPath('ClassName#methodName'); + expect(result).toEqual({ + className: 'ClassName', + memberName: 'methodName', + }); + }); + + it('空白を含むシンボルパスをトリムすること', () => { + const result = parseSymbolPath(' ClassName # methodName '); + expect(result).toEqual({ + className: 'ClassName', + memberName: 'methodName', + }); + }); + + it('無効なシンボルパスでエラーをスローすること', () => { + expect(() => parseSymbolPath('ClassName#method#extra')).toThrow('Invalid symbol path'); + }); + }); + + describe('findSymbolInAST', () => { + it('クラスのメソッドを検索すること', () => { + const fileContent = ` + export class TestClass { + /** + * Test method + */ + testMethod() { + console.log('test'); + } + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'testMethod', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + className: 'TestClass', + memberName: 'testMethod', + scopeType: 'method', + confidence: 'high', + }); + // JSDocコメントを含む行番号を検証 + expect(matches[0].startLine).toBeLessThan(matches[0].endLine); + }); + + it('トップレベル関数を検索すること', () => { + const fileContent = ` + /** + * Top level function + */ + export function topLevelFunction() { + return 'test'; + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'topLevelFunction', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'topLevelFunction', + scopeType: 'function', + confidence: 'high', + }); + expect(matches[0].className).toBeUndefined(); + }); + + it('複数のマッチを返すこと(オーバーロード)', () => { + const fileContent = ` + export class TestClass { + testMethod(arg1: string): void; + testMethod(arg1: string, arg2: number): void; + testMethod(arg1: string, arg2?: number): void { + console.log(arg1, arg2); + } + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'testMethod', + }); + + // オーバーロードシグネチャは検出されず、実装のみ検出される + expect(matches).toHaveLength(1); + }); + + it('シンボルが見つからない場合は空配列を返すこと', () => { + const fileContent = ` + export class TestClass { + otherMethod() { + console.log('test'); + } + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'nonExistentMethod', + }); + + expect(matches).toHaveLength(0); + }); + + it('クラスが見つからない場合は空配列を返すこと', () => { + const fileContent = ` + export class OtherClass { + testMethod() { + console.log('test'); + } + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'NonExistentClass', + memberName: 'testMethod', + }); + + expect(matches).toHaveLength(0); + }); + + it('TypeScript/JavaScript以外のファイルでエラーをスローすること', () => { + expect(() => { + findSymbolInAST('content', '/test/file.py', { + memberName: 'test', + }); + }).toThrow('TypeScript/JavaScript files only'); + }); + + it('JSDocコメントを含む行番号を取得すること', () => { + const fileContent = ` + export class TestClass { + /** + * Multi-line JSDoc + * with description + */ + testMethod() { + console.log('test'); + } + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'testMethod', + }); + + expect(matches).toHaveLength(1); + // JSDocの開始行が含まれることを確認 + const fileLines = fileContent.split('\n'); + const jsdocStartLine = fileLines.findIndex((line) => line.includes('/**')); + expect(matches[0].startLine).toBeLessThanOrEqual(jsdocStartLine + 1); // 1-indexed + }); + }); + + describe('selectBestSymbolMatch', () => { + const mockMatches: SymbolMatch[] = [ + { + memberName: 'test', + startLine: 10, + endLine: 20, + scopeType: 'function', + confidence: 'high', + }, + { + memberName: 'test', + startLine: 50, + endLine: 60, + scopeType: 'function', + confidence: 'medium', + }, + { + memberName: 'test', + startLine: 100, + endLine: 110, + scopeType: 'function', + confidence: 'low', + }, + ]; + + it('マッチが0個の場合はnullを返すこと', () => { + const result = selectBestSymbolMatch([]); + expect(result).toBeNull(); + }); + + it('マッチが1個の場合はそれを返すこと', () => { + const result = selectBestSymbolMatch([mockMatches[0]]); + expect(result).toBe(mockMatches[0]); + }); + + it('行番号ヒントがある場合、最も近いマッチを返すこと', () => { + const result = selectBestSymbolMatch(mockMatches, { start: 48, end: 62 }); + expect(result).toBe(mockMatches[1]); // 50-60が最も近い + }); + + it('行番号ヒントがない場合、信頼度が最も高いマッチを返すこと', () => { + const result = selectBestSymbolMatch(mockMatches); + expect(result).toBe(mockMatches[0]); // confidence: 'high' + }); + + it('信頼度が同じ場合、最初のマッチを返すこと', () => { + const equalConfidenceMatches: SymbolMatch[] = [ + { ...mockMatches[0], confidence: 'high' }, + { ...mockMatches[1], confidence: 'high' }, + ]; + const result = selectBestSymbolMatch(equalConfidenceMatches); + expect(result).toBe(equalConfidenceMatches[0]); + }); + }); + + describe('変数検索', () => { + it('トップレベルのconst宣言を検索すること', () => { + const fileContent = ` + /** + * API key constant + */ + const API_KEY = 'test-key'; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'API_KEY', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'API_KEY', + scopeType: 'const', + confidence: 'high', + }); + expect(matches[0].className).toBeUndefined(); + }); + + it('トップレベルのlet宣言を検索すること', () => { + const fileContent = ` + let counter = 0; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'counter', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'counter', + scopeType: 'let', + confidence: 'high', + }); + }); + + it('トップレベルのvar宣言を検索すること', () => { + const fileContent = ` + var globalVar = 'test'; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'globalVar', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'globalVar', + scopeType: 'var', + confidence: 'high', + }); + }); + + it('エクスポートされた変数を検索すること', () => { + const fileContent = ` + /** + * Exported configuration + */ + export const CONFIG = { port: 3000 }; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'CONFIG', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'CONFIG', + scopeType: 'const', + confidence: 'high', + }); + }); + + it('オブジェクトデストラクチャリングから変数を検索すること', () => { + const fileContent = ` + const { API_KEY, SECRET } = process.env; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'API_KEY', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'API_KEY', + scopeType: 'const', + confidence: 'high', + }); + }); + + it('配列デストラクチャリングから変数を検索すること', () => { + const fileContent = ` + const [first, second, third] = items; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'second', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'second', + scopeType: 'const', + confidence: 'high', + }); + }); + + it('オブジェクトのrest構文から変数を検索すること', () => { + const fileContent = ` + const { a, b, ...rest } = obj; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'rest', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'rest', + scopeType: 'const', + confidence: 'high', + }); + }); + + it('配列のrest構文から変数を検索すること', () => { + const fileContent = ` + const [first, ...remaining] = arr; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'remaining', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'remaining', + scopeType: 'const', + confidence: 'high', + }); + }); + + it('複数宣言子を含む文全体を返すこと', () => { + const fileContent = ` + const x = 1, y = 2, z = 3; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'y', + }); + + expect(matches).toHaveLength(1); + expect(matches[0]).toMatchObject({ + memberName: 'y', + scopeType: 'const', + confidence: 'high', + }); + // 全体の文が返されることを確認(開始行と終了行が同じ) + expect(matches[0].startLine).toBe(matches[0].endLine); + }); + + it('変数のJSDocコメントを含む行番号を取得すること', () => { + const fileContent = ` + /** + * Multi-line JSDoc for variable + * with detailed description + */ + export const MAX_RETRIES = 3; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'MAX_RETRIES', + }); + + expect(matches).toHaveLength(1); + // JSDocの開始行が含まれることを確認 + const fileLines = fileContent.split('\n'); + const jsdocStartLine = fileLines.findIndex((line) => line.includes('/**')); + expect(matches[0].startLine).toBeLessThanOrEqual(jsdocStartLine + 1); // 1-indexed + }); + + it('関数と変数が同じ名前の場合、関数を優先すること', () => { + const fileContent = ` + const config = { port: 3000 }; + function config() { + return { port: 3000 }; + } + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'config', + }); + + // 関数と変数の両方が見つかる + expect(matches.length).toBeGreaterThanOrEqual(1); + // 最初のマッチは関数であること + expect(matches[0].scopeType).toBe('function'); + }); + + it('変数が存在しない場合は空配列に含まれないこと', () => { + const fileContent = ` + const existingVar = 'test'; + `; + + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + memberName: 'nonExistentVar', + }); + + expect(matches).toHaveLength(0); + }); + }); + + describe('ASTキャッシュ', () => { + it('同じファイルを複数回パースしないこと', () => { + const fileContent = ` + export class TestClass { + testMethod() { + console.log('test'); + } + } + `; + + const filePath = '/test/file.ts'; + + // 1回目の呼び出し + const matches1 = findSymbolInAST(fileContent, filePath, { + className: 'TestClass', + memberName: 'testMethod', + }); + + // 2回目の呼び出し(キャッシュが使用される) + const matches2 = findSymbolInAST(fileContent, filePath, { + className: 'TestClass', + memberName: 'testMethod', + }); + + expect(matches1).toEqual(matches2); + }); + + it('clearASTCache()でキャッシュをクリアできること', () => { + const fileContent = ` + export class TestClass { + testMethod() { + console.log('test'); + } + } + `; + + // キャッシュに追加 + findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'testMethod', + }); + + // キャッシュをクリア + clearASTCache(); + + // クリア後も正常に動作すること + const matches = findSymbolInAST(fileContent, '/test/file.ts', { + className: 'TestClass', + memberName: 'testMethod', + }); + + expect(matches).toHaveLength(1); + }); + }); +}); diff --git a/src/utils/ast-symbol-search.ts b/src/utils/ast-symbol-search.ts new file mode 100644 index 0000000..89f62cc --- /dev/null +++ b/src/utils/ast-symbol-search.ts @@ -0,0 +1,354 @@ +/** + * AST解析によるシンボル検索ユーティリティ + */ + +import * as path from 'path'; + +import { parse as parseTypeScript } from '@typescript-eslint/typescript-estree'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; + +import type { SymbolMatch } from '@/utils/types'; + +/** + * ASTキャッシュ(同じファイルを複数回パースしない) + */ +const astCache = new Map(); + +/** + * ファイルがTypeScript/JavaScriptかどうかを判定 + */ +export function isTypeScriptOrJavaScript(filePath: string): boolean { + const ext = path.extname(filePath).toLowerCase(); + return ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext); +} + +/** + * ASTをパースまたはキャッシュから取得 + */ +function getOrParseAST(filePath: string, fileContent: string): TSESTree.Program { + if (astCache.has(filePath)) { + return astCache.get(filePath)!; + } + + const ast = parseTypeScript(fileContent, { + loc: true, + range: true, + comment: true, + jsx: filePath.endsWith('.tsx') || filePath.endsWith('.jsx'), + }); + + astCache.set(filePath, ast); + return ast; +} + +/** + * JSDocコメントを含む開始行を取得 + */ +function getStartLineWithJSDoc(node: TSESTree.Node, fileContent: string): number { + if (!node.loc) return 1; + + const lines = fileContent.split('\n'); + const startLine = node.loc.start.line; + + // ノードの直前の行から上に向かってJSDocを探す + for (let i = startLine - 2; i >= 0; i--) { + const line = lines[i].trim(); + + // JSDocの終わり(*/)を見つけた場合 + if (line.endsWith('*/')) { + // さらに上に向かってJSDocの始まり(/**)を探す + for (let j = i; j >= 0; j--) { + const docLine = lines[j].trim(); + if (docLine.startsWith('/**')) { + return j + 1; // 1-indexed + } + } + break; + } + + // 空行やコメントでない行が見つかったら終了 + if (line.length > 0 && !line.startsWith('//')) { + break; + } + } + + return startLine; +} + +/** + * クラス名でクラスを検索 + */ +function findClassByName(ast: TSESTree.Program, className: string): TSESTree.ClassDeclaration[] { + const classes: TSESTree.ClassDeclaration[] = []; + + function visit(node: TSESTree.Node) { + if (node.type === 'ClassDeclaration' && node.id?.name === className) { + classes.push(node); + } + + // 子ノードを再帰的に探索 + const keys = Object.keys(node) as (keyof TSESTree.Node)[]; + for (const key of keys) { + const value = node[key]; + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + value.forEach((child) => { + if (child && typeof child === 'object' && 'type' in child) { + visit(child as TSESTree.Node); + } + }); + } else if ('type' in value) { + visit(value as TSESTree.Node); + } + } + } + } + + visit(ast); + return classes; +} + +/** + * クラス内のメソッドを検索 + */ +function findMethodInClass( + classNode: TSESTree.ClassDeclaration, + methodName: string +): TSESTree.MethodDefinition | null { + if (!classNode.body?.body) return null; + + for (const member of classNode.body.body) { + if (member.type === 'MethodDefinition' && member.key.type === 'Identifier') { + if (member.key.name === methodName) { + return member; + } + } + } + + return null; +} + +/** + * トップレベル関数を検索 + */ +function findFunctionByName( + ast: TSESTree.Program, + functionName: string +): TSESTree.FunctionDeclaration[] { + const functions: TSESTree.FunctionDeclaration[] = []; + + // トップレベルの関数のみ検索 + for (const statement of ast.body) { + if (statement.type === 'FunctionDeclaration' && statement.id?.name === functionName) { + functions.push(statement); + } else if (statement.type === 'ExportNamedDeclaration' && statement.declaration) { + const decl = statement.declaration; + if (decl.type === 'FunctionDeclaration' && decl.id?.name === functionName) { + functions.push(decl); + } + } + } + + return functions; +} + +/** + * 変数宣言内に指定された名前の変数が含まれるか + */ +function hasVariableNamed(varDecl: TSESTree.VariableDeclaration, targetName: string): boolean { + for (const declarator of varDecl.declarations) { + // 単純な識別子: const x = 1; + if (declarator.id.type === 'Identifier' && declarator.id.name === targetName) { + return true; + } + + // オブジェクトデストラクチャリング: const { x, y } = obj; + if (declarator.id.type === 'ObjectPattern') { + for (const prop of declarator.id.properties) { + if (prop.type === 'Property' && prop.value.type === 'Identifier') { + if (prop.value.name === targetName) { + return true; + } + } + // RestElement: const { ...rest } = obj; + if (prop.type === 'RestElement' && prop.argument.type === 'Identifier') { + if (prop.argument.name === targetName) { + return true; + } + } + } + } + + // 配列デストラクチャリング: const [x, y] = arr; + if (declarator.id.type === 'ArrayPattern') { + for (const element of declarator.id.elements) { + if (element?.type === 'Identifier' && element.name === targetName) { + return true; + } + // RestElement: const [...rest] = arr; + if (element?.type === 'RestElement' && element.argument.type === 'Identifier') { + if (element.argument.name === targetName) { + return true; + } + } + } + } + } + + return false; +} + +/** + * トップレベル変数を検索 + */ +function findVariableByName( + ast: TSESTree.Program, + variableName: string +): TSESTree.VariableDeclaration[] { + const variables: TSESTree.VariableDeclaration[] = []; + + // トップレベルの変数のみ検索 + for (const statement of ast.body) { + // Case 1: エクスポートされた変数 + if (statement.type === 'ExportNamedDeclaration' && statement.declaration) { + const decl = statement.declaration; + if (decl.type === 'VariableDeclaration') { + if (hasVariableNamed(decl, variableName)) { + variables.push(decl); + } + } + } + + // Case 2: トップレベル変数 + if (statement.type === 'VariableDeclaration') { + if (hasVariableNamed(statement, variableName)) { + variables.push(statement); + } + } + } + + return variables; +} + +/** + * シンボルパスをパースしてクラス名とメンバー名に分割 + */ +export function parseSymbolPath(symbolPath: string): { + className?: string; + memberName: string; +} { + const parts = symbolPath.split('#'); + if (parts.length === 1) { + // 関数のみ: "functionName" + return { memberName: parts[0].trim() }; + } else if (parts.length === 2) { + // クラス+メソッド: "ClassName#methodName" + return { + className: parts[0].trim(), + memberName: parts[1].trim(), + }; + } + throw new Error(`Invalid symbol path: ${symbolPath}`); +} + +/** + * ASTを使ってシンボルを検索 + */ +export function findSymbolInAST( + fileContent: string, + filePath: string, + options: { + className?: string; + memberName: string; + } +): SymbolMatch[] { + // TypeScript/JavaScriptファイルのみ + if (!isTypeScriptOrJavaScript(filePath)) { + throw new Error('TypeScript/JavaScript files only'); + } + + const ast = getOrParseAST(filePath, fileContent); + const matches: SymbolMatch[] = []; + + if (options.className) { + // クラス+メソッドを検索 + const classes = findClassByName(ast, options.className); + + for (const classNode of classes) { + const method = findMethodInClass(classNode, options.memberName); + if (method && method.loc) { + matches.push({ + className: options.className, + memberName: options.memberName, + startLine: getStartLineWithJSDoc(method, fileContent), + endLine: method.loc.end.line, + scopeType: 'method', + confidence: 'high', + }); + } + } + } else { + // 関数を検索(トップレベル) + const functions = findFunctionByName(ast, options.memberName); + + for (const funcNode of functions) { + if (funcNode.loc) { + matches.push({ + memberName: options.memberName, + startLine: getStartLineWithJSDoc(funcNode, fileContent), + endLine: funcNode.loc.end.line, + scopeType: 'function', + confidence: 'high', + }); + } + } + + // 変数を検索(トップレベル) + const variables = findVariableByName(ast, options.memberName); + + for (const varNode of variables) { + if (varNode.loc) { + matches.push({ + memberName: options.memberName, + startLine: getStartLineWithJSDoc(varNode, fileContent), + endLine: varNode.loc.end.line, + scopeType: varNode.kind as 'const' | 'let' | 'var', + confidence: 'high', + }); + } + } + } + + return matches; +} + +/** + * 複数マッチ時の最適な選択 + */ +export function selectBestSymbolMatch( + matches: SymbolMatch[], + hintLines?: { start: number; end: number } +): SymbolMatch | null { + if (matches.length === 0) return null; + if (matches.length === 1) return matches[0]; + + // 行番号ヒントがある場合、最も近いものを選択 + if (hintLines) { + return matches.reduce((best, current) => { + const bestDistance = Math.abs(best.startLine - hintLines.start); + const currentDistance = Math.abs(current.startLine - hintLines.start); + return currentDistance < bestDistance ? current : best; + }); + } + + // 信頼度が最も高いものを選択 + const confidenceScore = { high: 3, medium: 2, low: 1 }; + return matches.sort((a, b) => confidenceScore[b.confidence] - confidenceScore[a.confidence])[0]; +} + +/** + * ASTキャッシュをクリア + */ +export function clearASTCache(): void { + astCache.clear(); +} diff --git a/src/utils/backup.test.ts b/src/utils/backup.test.ts new file mode 100644 index 0000000..b0a1fd6 --- /dev/null +++ b/src/utils/backup.test.ts @@ -0,0 +1,156 @@ +import * as fs from 'fs'; + +import { jest } from '@jest/globals'; + +import { createBackup, deleteBackup, listBackups, restoreBackup } from '@/utils/backup'; + +// fsモジュールをモック +jest.mock('fs'); + +const mockedFs = fs as jest.Mocked; + +describe('backup', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('createBackup', () => { + it('バックアップファイルを作成すること', () => { + const filePath = '/test/file.txt'; + mockedFs.existsSync.mockReturnValue(false); + + const result = createBackup(filePath); + + expect(result).toBe('/test/file.txt.backup'); + expect(mockedFs.copyFileSync).toHaveBeenCalledWith(filePath, '/test/file.txt.backup'); + }); + + it('バックアップファイルが既に存在する場合、番号付きのバックアップを作成すること', () => { + const filePath = '/test/file.txt'; + mockedFs.existsSync + .mockReturnValueOnce(true) // .backup が存在 + .mockReturnValueOnce(false); // .backup.1 が存在しない + + const result = createBackup(filePath); + + expect(result).toBe('/test/file.txt.backup.1'); + expect(mockedFs.copyFileSync).toHaveBeenCalledWith(filePath, '/test/file.txt.backup.1'); + }); + + it('複数のバックアップファイルが既に存在する場合、次の番号のバックアップを作成すること', () => { + const filePath = '/test/file.txt'; + mockedFs.existsSync + .mockReturnValueOnce(true) // .backup が存在 + .mockReturnValueOnce(true) // .backup.1 が存在 + .mockReturnValueOnce(true) // .backup.2 が存在 + .mockReturnValueOnce(false); // .backup.3 が存在しない + + const result = createBackup(filePath); + + expect(result).toBe('/test/file.txt.backup.3'); + expect(mockedFs.copyFileSync).toHaveBeenCalledWith(filePath, '/test/file.txt.backup.3'); + }); + }); + + describe('restoreBackup', () => { + it('バックアップファイルから元のファイルを復元すること', () => { + const backupPath = '/test/file.txt.backup'; + const originalPath = '/test/file.txt'; + mockedFs.existsSync.mockReturnValue(true); + + restoreBackup(backupPath, originalPath); + + expect(mockedFs.copyFileSync).toHaveBeenCalledWith(backupPath, originalPath); + }); + + it('バックアップファイルが存在しない場合、エラーをスローすること', () => { + const backupPath = '/test/file.txt.backup'; + const originalPath = '/test/file.txt'; + mockedFs.existsSync.mockReturnValue(false); + + expect(() => restoreBackup(backupPath, originalPath)).toThrow( + 'バックアップファイルが見つかりません' + ); + }); + }); + + describe('deleteBackup', () => { + it('バックアップファイルを削除すること', () => { + const backupPath = '/test/file.txt.backup'; + mockedFs.existsSync.mockReturnValue(true); + + deleteBackup(backupPath); + + expect(mockedFs.unlinkSync).toHaveBeenCalledWith(backupPath); + }); + + it('バックアップファイルが存在しない場合、何もしないこと', () => { + const backupPath = '/test/file.txt.backup'; + mockedFs.existsSync.mockReturnValue(false); + + deleteBackup(backupPath); + + expect(mockedFs.unlinkSync).not.toHaveBeenCalled(); + }); + }); + + describe('listBackups', () => { + it('指定ファイルのバックアップファイルをリストすること', () => { + const filePath = '/test/dir/file.txt'; + mockedFs.readdirSync.mockReturnValue([ + 'file.txt', + 'file.txt.backup', + 'file.txt.backup.1', + 'file.txt.backup.2', + 'other.txt', + 'other.txt.backup', + ] as any); + + const result = listBackups(filePath); + + expect(result).toEqual([ + '/test/dir/file.txt.backup', + '/test/dir/file.txt.backup.1', + '/test/dir/file.txt.backup.2', + ]); + expect(mockedFs.readdirSync).toHaveBeenCalledWith('/test/dir'); + }); + + it('バックアップファイルがない場合、空配列を返すこと', () => { + const filePath = '/test/dir/file.txt'; + mockedFs.readdirSync.mockReturnValue(['file.txt', 'other.txt'] as any); + + const result = listBackups(filePath); + + expect(result).toEqual([]); + }); + + it('ルートディレクトリのファイルのバックアップをリストすること', () => { + const filePath = '/file.txt'; + mockedFs.readdirSync.mockReturnValue(['file.txt', 'file.txt.backup'] as any); + + const result = listBackups(filePath); + + expect(result).toEqual(['/file.txt.backup']); + expect(mockedFs.readdirSync).toHaveBeenCalledWith('/'); + }); + + it('拡張子付きファイル名のバックアップを正しくフィルタリングすること', () => { + const filePath = '/test/dir/app.config.js'; + mockedFs.readdirSync.mockReturnValue([ + 'app.config.js', + 'app.config.js.backup', + 'app.config.js.backup.1', + 'app.js', + 'app.js.backup', + ] as any); + + const result = listBackups(filePath); + + expect(result).toEqual([ + '/test/dir/app.config.js.backup', + '/test/dir/app.config.js.backup.1', + ]); + }); + }); +}); diff --git a/src/utils/backup.ts b/src/utils/backup.ts new file mode 100644 index 0000000..bc8647a --- /dev/null +++ b/src/utils/backup.ts @@ -0,0 +1,57 @@ +/** + * バックアップ管理ユーティリティ + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * ファイルのバックアップを作成 + * @returns バックアップファイルのパス + */ +export function createBackup(filePath: string): string { + let backupPath = `${filePath}.backup`; + let counter = 1; + + // ユニークなバックアップファイル名を検索 + while (fs.existsSync(backupPath)) { + backupPath = `${filePath}.backup.${counter}`; + counter++; + } + + fs.copyFileSync(filePath, backupPath); + return backupPath; +} + +/** + * バックアップから復元 + */ +export function restoreBackup(backupPath: string, originalPath: string): void { + if (!fs.existsSync(backupPath)) { + throw new Error(`バックアップファイルが見つかりません: ${backupPath}`); + } + + fs.copyFileSync(backupPath, originalPath); +} + +/** + * バックアップファイルを削除 + */ +export function deleteBackup(backupPath: string): void { + if (fs.existsSync(backupPath)) { + fs.unlinkSync(backupPath); + } +} + +/** + * 指定ファイルの全バックアップファイルをリスト + */ +export function listBackups(filePath: string): string[] { + const dir = path.dirname(filePath); + const filename = path.basename(filePath); + + const files = fs.readdirSync(dir); + const backupPattern = new RegExp(`^${filename}\\.backup(\\.\\d+)?$`); + + return files.filter((f) => backupPattern.test(f)).map((f) => path.join(dir, f)); +} diff --git a/src/utils/code-comparison.test.ts b/src/utils/code-comparison.test.ts new file mode 100644 index 0000000..425d5e1 --- /dev/null +++ b/src/utils/code-comparison.test.ts @@ -0,0 +1,278 @@ +import * as fs from 'fs'; + +import { jest } from '@jest/globals'; + +import { + compareCodeContent, + dedentCode, + extractLinesFromFile, + searchCodeInFile, +} from '@/utils/code-comparison'; + +// fsモジュールをモック +jest.mock('fs'); + +const mockedFs = fs as jest.Mocked; + +describe('code-comparison.utils', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('dedentCode', () => { + it('インデントがないコードはそのまま返すこと', () => { + const code = 'function test() {\n return 42;\n}'; + expect(dedentCode(code)).toBe(code); + }); + + it('共通の先頭インデントを除去すること', () => { + const code = ' function test() {\n return 42;\n }'; + const expected = 'function test() {\n return 42;\n}'; + expect(dedentCode(code)).toBe(expected); + }); + + it('空行はそのまま保持すること', () => { + const code = ' function test() {\n\n return 42;\n }'; + const expected = 'function test() {\n\n return 42;\n}'; + expect(dedentCode(code)).toBe(expected); + }); + + it('最小インデントを基準に除去すること', () => { + const code = ' if (true) {\n doSomething();\n }'; + const expected = 'if (true) {\n doSomething();\n}'; + expect(dedentCode(code)).toBe(expected); + }); + + it('空文字列を処理できること', () => { + expect(dedentCode('')).toBe(''); + }); + + it('空行のみの場合はそのまま返すこと', () => { + const code = '\n\n\n'; + expect(dedentCode(code)).toBe(code); + }); + + it('タブインデントも正しく処理すること', () => { + const code = '\t\tfunction test() {\n\t\t\treturn 42;\n\t\t}'; + const expected = 'function test() {\n\treturn 42;\n}'; + expect(dedentCode(code)).toBe(expected); + }); + + it('混合インデント(スペースとタブ)も処理すること', () => { + const code = ' \tfunction test() {\n \t return 42;\n \t}'; + const expected = 'function test() {\n return 42;\n}'; + expect(dedentCode(code)).toBe(expected); + }); + }); + + describe('extractLinesFromFile', () => { + it('指定行範囲のコードを抽出できること', () => { + const fileContent = `line1 +line2 +line3 +line4 +line5`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = extractLinesFromFile('/test/file.ts', 2, 4); + + expect(result).toBe('line2\nline3\nline4'); + expect(mockedFs.readFileSync).toHaveBeenCalledWith('/test/file.ts', 'utf-8'); + }); + + it('1行のみを抽出できること', () => { + const fileContent = `line1 +line2 +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = extractLinesFromFile('/test/file.ts', 2, 2); + + expect(result).toBe('line2'); + }); + + it('ファイルの最初から抽出できること', () => { + const fileContent = `line1 +line2 +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = extractLinesFromFile('/test/file.ts', 1, 2); + + expect(result).toBe('line1\nline2'); + }); + + it('ファイルの最後まで抽出できること', () => { + const fileContent = `line1 +line2 +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = extractLinesFromFile('/test/file.ts', 2, 3); + + expect(result).toBe('line2\nline3'); + }); + }); + + describe('compareCodeContent', () => { + it('完全に一致するコードをtrueと判定すること', () => { + const code1 = 'const x = 1;'; + const code2 = 'const x = 1;'; + + expect(compareCodeContent(code1, code2)).toBe(true); + }); + + it('空白の違いのみの場合はtrueと判定すること', () => { + const code1 = ' const x = 1; '; + const code2 = 'const x = 1;'; + + expect(compareCodeContent(code1, code2)).toBe(true); + }); + + it('改行の違いを吸収してtrueと判定すること', () => { + const code1 = 'line1\r\nline2'; + const code2 = 'line1\nline2'; + + expect(compareCodeContent(code1, code2)).toBe(true); + }); + + it('インデントの違いを吸収してtrueと判定すること', () => { + const code1 = '\tconst x = 1;'; + const code2 = ' const x = 1;'; + + expect(compareCodeContent(code1, code2)).toBe(true); + }); + + it('コード内容が異なる場合はfalseと判定すること', () => { + const code1 = 'const x = 1;'; + const code2 = 'const y = 2;'; + + expect(compareCodeContent(code1, code2)).toBe(false); + }); + + it('空文字列同士はtrueと判定すること', () => { + expect(compareCodeContent('', '')).toBe(true); + }); + }); + + describe('searchCodeInFile', () => { + it('ファイル内でコードが見つかった場合、その位置を返すこと', () => { + const fileContent = `line1 +const x = 1; +const y = 2; +line4`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;\nconst y = 2;'); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 2, end: 3 }); + }); + + it('空白の違いを吸収してコードを見つけること', () => { + const fileContent = `line1 + const x = 1; +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 2, end: 2 }); + }); + + it('コードが複数箇所で見つかった場合、全ての位置を返すこと', () => { + const fileContent = `const x = 1; +line2 +const x = 1; +line4 +const x = 1;`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(3); + expect(result[0]).toEqual({ start: 1, end: 1 }); + expect(result[1]).toEqual({ start: 3, end: 3 }); + expect(result[2]).toEqual({ start: 5, end: 5 }); + }); + + it('コードが見つからない場合、空配列を返すこと', () => { + const fileContent = `line1 +line2 +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(0); + }); + + it('複数行のコードを検索できること', () => { + const fileContent = `line1 +function test() { + return 1; +} +line5`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile( + '/test/file.ts', + `function test() { + return 1; +}` + ); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 2, end: 4 }); + }); + + it('ファイルの先頭でコードを見つけること', () => { + const fileContent = `const x = 1; +line2 +line3`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 1, end: 1 }); + }); + + it('ファイルの末尾でコードを見つけること', () => { + const fileContent = `line1 +line2 +const x = 1;`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 3, end: 3 }); + }); + + it('部分一致では見つからないこと', () => { + const fileContent = `const x = 1; +const y = 2;`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + // "const x = 1;"のみを検索("const y = 2;"は含まない) + const result = searchCodeInFile('/test/file.ts', 'const x = 1;'); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ start: 1, end: 1 }); + }); + }); +}); diff --git a/src/utils/code-comparison.ts b/src/utils/code-comparison.ts new file mode 100644 index 0000000..5ad5bcb --- /dev/null +++ b/src/utils/code-comparison.ts @@ -0,0 +1,141 @@ +/** + * コード比較ユーティリティ + */ + +import * as fs from 'fs'; + +import { expandMatchToScope } from '@/utils/ast-scope-expansion'; +import { normalizeCode } from '@/utils/markdown'; +import type { ExpandedMatch } from '@/utils/types'; + +/** + * コードから共通の先頭インデントを除去する + * + * @param code コード文字列 + * @returns インデントを除去したコード + */ +export function dedentCode(code: string): string { + const lines = code.split('\n'); + + // 空行を除いた最小インデントを見つける + let minIndent = Infinity; + for (const line of lines) { + if (line.trim().length === 0) continue; // 空行はスキップ + + const indent = /^(\s*)/.exec(line)?.[1].length ?? 0; + minIndent = Math.min(minIndent, indent); + } + + // 全行から最小インデントを除去 + if (minIndent === Infinity || minIndent === 0) { + return code; + } + + return lines.map((line) => (line.trim().length === 0 ? line : line.slice(minIndent))).join('\n'); +} + +/** + * ファイルから指定行範囲のコードを抽出する + * + * @param filePath ファイルパス + * @param startLine 開始行(1-indexed) + * @param endLine 終了行(1-indexed) + * @returns 抽出されたコード(先頭インデントを除去) + */ +export function extractLinesFromFile(filePath: string, startLine: number, endLine: number): string { + const content = fs.readFileSync(filePath, 'utf-8'); + const lines = content.split('\n'); + + // 1-indexedから0-indexedに変換し、指定範囲を抽出 + const extractedLines = lines.slice(startLine - 1, endLine); + const extractedCode = extractedLines.join('\n'); + + // 共通の先頭インデントを除去 + return dedentCode(extractedCode); +} + +/** + * 2つのコード文字列を比較する(正規化して比較) + * + * @param actual 実際のコード + * @param expected 期待されるコード + * @returns 一致する場合true + */ +export function compareCodeContent(actual: string, expected: string): boolean { + const normalizedActual = normalizeCode(actual); + const normalizedExpected = normalizeCode(expected); + + return normalizedActual === normalizedExpected; +} + +/** + * ファイル全体から指定されたコードを検索する(スライディングウィンドウ) + * + * @param filePath ファイルパス + * @param codeToFind 検索するコード + * @returns 見つかった位置の配列(start/end行番号、1-indexed) + */ +export function searchCodeInFile( + filePath: string, + codeToFind: string +): { start: number; end: number }[] { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const fileLines = fileContent.split('\n'); + + // ターゲットコードの行数を取得(正規化前の元のコードから) + const targetLineCount = codeToFind.split('\n').length; + + // 正規化後のターゲットコード + const normalizedTarget = normalizeCode(codeToFind); + + const matches: { start: number; end: number }[] = []; + + // ファイル全体をスライディングウィンドウで走査 + for (let i = 0; i <= fileLines.length - targetLineCount; i++) { + // 現在のウィンドウを抽出 + const windowLines = fileLines.slice(i, i + targetLineCount); + const windowCode = windowLines.join('\n'); + const normalizedWindow = normalizeCode(windowCode); + + // 正規化して比較 + if (normalizedWindow === normalizedTarget) { + matches.push({ + start: i + 1, // 1-indexedに変換 + end: i + targetLineCount, // 1-indexed + }); + } + } + + return matches; +} + +/** + * ファイル全体から指定されたコードを検索し、スコープ拡張を適用する + * + * @param filePath ファイルパス + * @param codeToFind 検索するコード + * @returns 見つかった位置の配列(スコープ拡張済み、信頼度情報付き) + */ +export function searchCodeInFileWithScopeExpansion( + filePath: string, + codeToFind: string +): ExpandedMatch[] { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + + // 既存のsearchCodeInFile()を使用してマッチを取得 + const rawMatches = searchCodeInFile(filePath, codeToFind); + + // 各マッチに対してスコープ拡張を実行 + const expandedMatches = rawMatches.map((match) => { + const expanded = expandMatchToScope({ + filePath, + originalMatch: match, + fileContent, + }); + + // expandMatchToScopeは配列を返すが、ここでは最初の要素のみを使用 + return expanded[0] || { ...match, confidence: 'low' as const, expansionType: 'none' as const }; + }); + + return expandedMatches; +} diff --git a/src/utils/code-ellipsis.test.ts b/src/utils/code-ellipsis.test.ts new file mode 100644 index 0000000..a3715a1 --- /dev/null +++ b/src/utils/code-ellipsis.test.ts @@ -0,0 +1,281 @@ +import * as fs from 'fs'; + +import { jest } from '@jest/globals'; + +import { insertEllipsis, removeEllipsis } from '@/utils/code-ellipsis'; + +// fsモジュールをモック +jest.mock('fs'); + +const mockedFs = fs as jest.Mocked; + +describe('code-ellipsis', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('insertEllipsis', () => { + it('クラス内の特定メソッドのみを表示し、他を省略すること', () => { + const fileContent = `export class TestClass { + methodA() { + console.log('A'); + } + + /** + * Target method + */ + targetMethod() { + console.log('target'); + } + + methodB() { + console.log('B'); + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'targetMethod', + }); + + expect(result).toContain('export class TestClass {'); + expect(result).toContain('targetMethod()'); + expect(result).not.toContain('methodA()'); + expect(result).not.toContain('methodB()'); + expect(result).toContain("console.log('target')"); + }); + + it('JSDocコメントを含めてメソッドを表示すること', () => { + const fileContent = `export class TestClass { + /** + * Target method with JSDoc + * @returns void + */ + targetMethod() { + console.log('target'); + } + + otherMethod() { + console.log('other'); + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'targetMethod', + }); + + expect(result).toContain('/**'); + expect(result).toContain('* Target method with JSDoc'); + expect(result).toContain('* @returns void'); + expect(result).toContain('*/'); + expect(result).toContain('targetMethod()'); + }); + + it('クラスが見つからない場合、元のファイル内容を返すこと', () => { + const fileContent = `export class OtherClass { + method() { + console.log('test'); + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'NonExistentClass', + memberName: 'method', + }); + + expect(result).toBe(fileContent); + }); + + it('メソッドが見つからない場合、クラス宣言と閉じ括弧のみを表示すること', () => { + const fileContent = `export class TestClass { + methodA() { + console.log('A'); + } + + methodB() { + console.log('B'); + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'nonExistentMethod', + }); + + expect(result).toContain('export class TestClass {'); + expect(result).not.toContain('methodA()'); + expect(result).not.toContain('methodB()'); + }); + + it('TypeScript/JavaScript以外のファイルでエラーをスローすること', () => { + expect(() => { + insertEllipsis('/test/file.py', { + className: 'TestClass', + memberName: 'method', + }); + }).toThrow('TypeScript/JavaScript files only'); + }); + + it('複数のメソッドがある場合、ターゲットメソッドのみを表示すること', () => { + const fileContent = `export class TestClass { + methodA() { + console.log('A'); + } + + methodB() { + console.log('B'); + } + + targetMethod() { + console.log('target'); + } + + methodC() { + console.log('C'); + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'targetMethod', + }); + + expect(result).toContain('export class TestClass {'); + expect(result).toContain('targetMethod()'); + expect(result).toContain("console.log('target')"); + expect(result).not.toContain('methodA()'); + expect(result).not.toContain('methodB()'); + expect(result).not.toContain('methodC()'); + }); + + it('コンストラクタを表示できること', () => { + const fileContent = `export class TestClass { + private value: number; + + constructor(value: number) { + this.value = value; + } + + getValue() { + return this.value; + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'constructor', + }); + + expect(result).toContain('constructor(value: number)'); + expect(result).toContain('this.value = value'); + expect(result).not.toContain('getValue()'); + }); + + it('プロパティを表示できること', () => { + const fileContent = `export class TestClass { + /** + * Class property + */ + public name: string = 'test'; + + getValue() { + return this.name; + } +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + className: 'TestClass', + memberName: 'name', + }); + + expect(result).toContain('/**'); + expect(result).toContain('* Class property'); + expect(result).toContain("public name: string = 'test';"); + expect(result).not.toContain('getValue()'); + }); + + it('className がない場合、元のファイル内容を返すこと', () => { + const fileContent = `function testFunction() { + console.log('test'); +}`; + + mockedFs.readFileSync.mockReturnValue(fileContent); + + const result = insertEllipsis('/test/file.ts', { + memberName: 'testFunction', + }); + + expect(result).toBe(fileContent); + }); + }); + + describe('removeEllipsis', () => { + it('省略記号を含む行を削除すること', () => { + const code = `export class TestClass { + + // ... (omitted) ... + + targetMethod() { + console.log('target'); + } + + // ... (omitted) ... + +}`; + + const result = removeEllipsis(code); + + expect(result).not.toContain('// ... (omitted) ...'); + expect(result).toContain('targetMethod()'); + expect(result).toContain('export class TestClass {'); + }); + + it('省略記号がない場合、元のコードをそのまま返すこと', () => { + const code = `export class TestClass { + targetMethod() { + console.log('target'); + } +}`; + + const result = removeEllipsis(code); + + expect(result).toBe(code); + }); + + it('複数の省略記号を削除すること', () => { + const code = `class A { + // ... (省略) ... + method1() {} + // ... (省略) ... + method2() {} + // ... (省略) ... +}`; + + const result = removeEllipsis(code); + + expect(result).not.toContain('// ... (omitted) ...'); + expect(result).toContain('method1()'); + expect(result).toContain('method2()'); + }); + + it('空文字列を処理できること', () => { + const result = removeEllipsis(''); + expect(result).toBe(''); + }); + }); +}); diff --git a/src/utils/code-ellipsis.ts b/src/utils/code-ellipsis.ts new file mode 100644 index 0000000..ad39411 --- /dev/null +++ b/src/utils/code-ellipsis.ts @@ -0,0 +1,230 @@ +/** + * Code ellipsis display utility + */ + +import * as fs from 'fs'; + +import { parse as parseTypeScript } from '@typescript-eslint/typescript-estree'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; + +import { isTypeScriptOrJavaScript } from '@/utils/ast-symbol-search'; + +const ELLIPSIS = ' // ... (omitted) ...'; + +/** + * Class member information + */ +interface ClassMember { + name: string; + startLine: number; + endLine: number; + type: 'method' | 'property' | 'constructor'; +} + +/** + * Find class node from AST + */ +function findClassNode(fileContent: string, className: string): TSESTree.ClassDeclaration | null { + const ast = parseTypeScript(fileContent, { + loc: true, + range: true, + jsx: fileContent.includes('tsx') || fileContent.includes('jsx'), + }); + + function visit(node: TSESTree.Node): TSESTree.ClassDeclaration | null { + if (node.type === 'ClassDeclaration' && node.id?.name === className) { + return node; + } + + // Recursively explore child nodes + const keys = Object.keys(node) as (keyof TSESTree.Node)[]; + for (const key of keys) { + const value = node[key]; + if (value && typeof value === 'object') { + if (Array.isArray(value)) { + for (const child of value) { + if (child && typeof child === 'object' && 'type' in child) { + const found = visit(child as TSESTree.Node); + if (found) return found; + } + } + } else if ('type' in value) { + const found = visit(value as TSESTree.Node); + if (found) return found; + } + } + } + + return null; + } + + return visit(ast); +} + +/** + * Get members in class + */ +function getClassMembers(classNode: TSESTree.ClassDeclaration): ClassMember[] { + const members: ClassMember[] = []; + + if (!classNode.body?.body) { + return members; + } + + for (const member of classNode.body.body) { + if (!member.loc) continue; + + if (member.type === 'MethodDefinition') { + const name = + member.key.type === 'Identifier' + ? member.key.name + : member.kind === 'constructor' + ? 'constructor' + : 'unknown'; + + members.push({ + name, + startLine: member.loc.start.line, + endLine: member.loc.end.line, + type: member.kind === 'constructor' ? 'constructor' : 'method', + }); + } else if (member.type === 'PropertyDefinition') { + const name = member.key.type === 'Identifier' ? member.key.name : 'unknown'; + + members.push({ + name, + startLine: member.loc.start.line, + endLine: member.loc.end.line, + type: 'property', + }); + } + } + + return members; +} + +/** + * Get start line including JSDoc comment + */ +function getStartLineWithJSDoc(startLine: number, lines: string[]): number { + // Search for JSDoc upward from before start line + for (let i = startLine - 2; i >= 0; i--) { + const line = lines[i].trim(); + + // If found JSDoc end (*/) + if (line.endsWith('*/')) { + // Search further upward for JSDoc start (/**) + for (let j = i; j >= 0; j--) { + const docLine = lines[j].trim(); + if (docLine.startsWith('/**')) { + return j + 1; // 1-indexed + } + } + break; + } + + // End if found non-empty, non-comment line + if (line.length > 0 && !line.startsWith('//')) { + break; + } + } + + return startLine; +} + +/** + * Insert ellipsis in code + */ +export function insertEllipsis( + filePath: string, + options: { + className?: string; + memberName: string; + } +): string { + // Only TypeScript/JavaScript files + if (!isTypeScriptOrJavaScript(filePath)) { + throw new Error('TypeScript/JavaScript files only'); + } + + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const lines = fileContent.split('\n'); + + if (options.className) { + // Display only methods in class + const classNode = findClassNode(fileContent, options.className); + if (!classNode?.loc) { + // If class not found, return original file content + return fileContent; + } + + const result: string[] = []; + const members = getClassMembers(classNode); + + // Get class declaration line (export class ClassName { line) + const classStartLine = classNode.loc.start.line; + const classEndLine = classNode.loc.end.line; + + // Add class declaration start line + result.push(lines[classStartLine - 1]); + + let lastEndLine = classStartLine; + let hasAddedEllipsis = false; + + for (const member of members) { + if (member.name === options.memberName) { + // Target method: Display fully including JSDoc comment + const memberStartWithJSDoc = getStartLineWithJSDoc(member.startLine, lines); + + // Insert ellipsis between previous member (if not yet added) + if (lastEndLine < memberStartWithJSDoc && !hasAddedEllipsis) { + result.push(''); + result.push(ELLIPSIS); + result.push(''); + hasAddedEllipsis = true; + } + + // Add target method + for (let i = memberStartWithJSDoc - 1; i < member.endLine; i++) { + result.push(lines[i]); + } + + lastEndLine = member.endLine; + hasAddedEllipsis = false; + } else { + // Other members: Omit (ellipsis added only once) + if (!hasAddedEllipsis && lastEndLine < member.endLine) { + hasAddedEllipsis = true; + } + lastEndLine = member.endLine; + } + } + + // Add ellipsis after last member (if needed) + if (hasAddedEllipsis && lastEndLine < classEndLine - 1) { + result.push(''); + result.push(ELLIPSIS); + result.push(''); + } + + // Add class closing bracket + result.push(lines[classEndLine - 1]); + + return result.join('\n'); + } else { + // Display function only (omit surrounding context) + // In this case, return only the range found by symbol search + // Add if implementation needed + return fileContent; + } +} + +/** + * Remove ellipsis from ellipsis-displayed code + */ +export function removeEllipsis(code: string): string { + return code + .split('\n') + .filter((line) => !line.trim().includes('// ... (omitted) ...')) + .join('\n'); +} diff --git a/src/utils/diff-display.test.ts b/src/utils/diff-display.test.ts new file mode 100644 index 0000000..e18c871 --- /dev/null +++ b/src/utils/diff-display.test.ts @@ -0,0 +1,149 @@ +import { displayCodeDiff, displayLineRangeDiff, truncateText } from '@/utils/diff-display'; + +describe('displayCodeDiff', () => { + it('一致するコードの場合、差分を表示しない', () => { + const code = 'const foo = "bar";\nconst baz = 42;'; + const result = displayCodeDiff(code, code); + + // ヘッダーとフッターが含まれることを確認 + expect(result).toContain('Expected code'); + expect(result).toContain('Actual code'); + // 差分マーカー(- や +)がないことを確認 + expect(result.split('\n').filter((line) => line.startsWith('-')).length).toBe(0); + expect(result.split('\n').filter((line) => line.startsWith('+')).length).toBe(0); + }); + + it('異なるコードの場合、削除と追加を表示する', () => { + const expected = 'const foo = "bar";\nconst baz = 42;'; + const actual = 'const foo = "baz";\nconst qux = 100;'; + const result = displayCodeDiff(expected, actual); + + // 削除された行(期待されるコード) + expect(result).toContain('- const foo = "bar";'); + expect(result).toContain('- const baz = 42;'); + + // 追加された行(実際のコード) + expect(result).toContain('+ const foo = "baz";'); + expect(result).toContain('+ const qux = 100;'); + }); + + it('行数が異なる場合も正しく表示する', () => { + const expected = 'line1\nline2'; + const actual = 'line1\nline2\nline3'; + const result = displayCodeDiff(expected, actual); + + // 一致する行 + expect(result).toContain(' line1'); + expect(result).toContain(' line2'); + + // 追加された行 + expect(result).toContain('+ line3'); + }); + + it('空文字列の比較も処理できる', () => { + const result = displayCodeDiff('', 'new line'); + + expect(result).toContain('+ new line'); + }); + + it('actual が expected より短い場合も正しく表示する', () => { + const expected = 'line1\nline2\nline3'; + const actual = 'line1'; + const result = displayCodeDiff(expected, actual); + + // 一致する行 + expect(result).toContain(' line1'); + + // 削除された行(expected にのみ存在) + expect(result).toContain('- line2'); + expect(result).toContain('- line3'); + }); + + it('expected と actual の長さが異なり、途中で一致しない場合', () => { + const expected = 'line1\nline2'; + const actual = 'line1\nmodified\nline3'; + const result = displayCodeDiff(expected, actual); + + // 一致する行 + expect(result).toContain(' line1'); + + // 変更された行 + expect(result).toContain('- line2'); + expect(result).toContain('+ modified'); + + // 追加された行 + expect(result).toContain('+ line3'); + }); +}); + +describe('displayLineRangeDiff', () => { + it('行範囲の差分を正しく表示する', () => { + const code = 'const foo = "bar";\nconst baz = 42;'; + const expectedRange = { start: 10, end: 11 }; + const actualRange = { start: 15, end: 16 }; + const result = displayLineRangeDiff(code, expectedRange, actualRange); + + // ヘッダーが含まれることを確認 + expect(result).toContain('Expected line range: 10-11'); + expect(result).toContain('Actual line range: 15-16'); + + // 行番号が表示されることを確認(ANSIカラーコードを含む形式) + expect(result).toContain('10'); + expect(result).toContain('15'); + expect(result).toContain('11'); + expect(result).toContain('16'); + + // コードが含まれることを確認 + expect(result).toContain('const foo = "bar";'); + expect(result).toContain('const baz = 42;'); + }); + + it('単一行の場合も正しく表示する', () => { + const code = 'const foo = "bar";'; + const expectedRange = { start: 5, end: 5 }; + const actualRange = { start: 10, end: 10 }; + const result = displayLineRangeDiff(code, expectedRange, actualRange); + + expect(result).toContain('Expected line range: 5-5'); + expect(result).toContain('Actual line range: 10-10'); + expect(result).toContain('5'); + expect(result).toContain('10'); + expect(result).toContain('const foo = "bar";'); + }); + + it('大きな行番号も正しく表示する', () => { + const code = 'line1\nline2\nline3'; + const expectedRange = { start: 100, end: 102 }; + const actualRange = { start: 200, end: 202 }; + const result = displayLineRangeDiff(code, expectedRange, actualRange); + + expect(result).toContain('100'); + expect(result).toContain('200'); + expect(result).toContain('101'); + expect(result).toContain('201'); + expect(result).toContain('102'); + expect(result).toContain('202'); + expect(result).toContain('line1'); + expect(result).toContain('line2'); + expect(result).toContain('line3'); + }); +}); + +describe('truncateText', () => { + it('最大長以下のテキストはそのまま返す', () => { + const text = 'short text'; + expect(truncateText(text, 20)).toBe(text); + }); + + it('最大長を超えるテキストは切り詰める', () => { + const text = 'This is a very long text that should be truncated'; + const result = truncateText(text, 20); + + expect(result).toBe('This is a very long ...'); + expect(result.length).toBe(23); // 20 + '...' + }); + + it('空文字列を処理できる', () => { + expect(truncateText('', 10)).toBe(''); + }); +}); diff --git a/src/utils/diff-display.ts b/src/utils/diff-display.ts new file mode 100644 index 0000000..f3efcbe --- /dev/null +++ b/src/utils/diff-display.ts @@ -0,0 +1,125 @@ +/** + * Utility for visually displaying code diffs + */ + +// ANSI color codes +const COLORS = { + RED: '\x1b[31m', + GREEN: '\x1b[32m', + RESET: '\x1b[0m', + DIM: '\x1b[2m', +}; + +/** + * Display diff between two code blocks line by line + * @param expected Expected code (code block in document) + * @param actual Actual code (code retrieved from file) + * @returns Colored diff display string + */ +export function displayCodeDiff(expected: string, actual: string): string { + const expectedLines = expected.split('\n'); + const actualLines = actual.split('\n'); + + const output: string[] = []; + const maxLines = Math.max(expectedLines.length, actualLines.length); + + // Header + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + output.push(`${COLORS.RED}- Expected code (in document)${COLORS.RESET}`); + output.push(`${COLORS.GREEN}+ Actual code (in file)${COLORS.RESET}`); + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + + // Compare line by line + for (let i = 0; i < maxLines; i++) { + const expectedLine = expectedLines[i]; + const actualLine = actualLines[i]; + + if (expectedLine === actualLine) { + // Matching lines (when both exist) + if (expectedLine !== undefined) { + output.push(` ${expectedLine}`); + } + } else { + // Expected lines (deleted lines) + if (expectedLine !== undefined) { + output.push(`${COLORS.RED}- ${expectedLine}${COLORS.RESET}`); + } + // Actual lines (added lines) + if (actualLine !== undefined) { + output.push(`${COLORS.GREEN}+ ${actualLine}${COLORS.RESET}`); + } + } + } + + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + + return output.join('\n'); +} + +/** + * Visually display line number diff (for CODE_LOCATION_MISMATCH) + * @param code Code content + * @param expectedRange Expected line range + * @param actualRange Actual line range + * @returns Colored line number diff display string + */ +export function displayLineRangeDiff( + code: string, + expectedRange: { start: number; end: number }, + actualRange: { start: number; end: number } +): string { + const output: string[] = []; + + // Header + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + output.push( + `${COLORS.RED}- Expected line range: ${expectedRange.start}-${expectedRange.end}${COLORS.RESET}` + ); + output.push( + `${COLORS.GREEN}+ Actual line range: ${actualRange.start}-${actualRange.end}${COLORS.RESET}` + ); + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + + // Display code (content is the same, show both line numbers) + const codeLines = code.split('\n'); + codeLines.forEach((line, index) => { + const expectedLineNum = expectedRange.start + index; + const actualLineNum = actualRange.start + index; + + // Display expected and actual line numbers side by side + output.push( + `${COLORS.RED}${expectedLineNum.toString().padStart(4)}${COLORS.RESET} | ` + + `${COLORS.GREEN}${actualLineNum.toString().padStart(4)}${COLORS.RESET} | ` + + `${line}` + ); + }); + + output.push( + `${COLORS.DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.RESET}` + ); + + return output.join('\n'); +} + +/** + * Truncate long text (for display) + * @param text Text + * @param maxLength Maximum length + * @returns Truncated text + */ +export function truncateText(text: string, maxLength: number): string { + if (text.length <= maxLength) { + return text; + } + return `${text.substring(0, maxLength)}...`; +} diff --git a/src/utils/fix.test.ts b/src/utils/fix.test.ts new file mode 100644 index 0000000..b6d4583 --- /dev/null +++ b/src/utils/fix.test.ts @@ -0,0 +1,873 @@ +/** + * 修正ロジックのテスト + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import type * as readline from 'readline'; + +import { extractLinesFromFile, searchCodeInFileWithScopeExpansion } from '@/utils/code-comparison'; +import { + applyFix, + createBlockMissingFix, + createContentMismatchFix, + createFixAction, + createLineOutOfRangeFix, + createLocationMismatchFix, + createMultipleSymbolsFoundFix, + createSymbolRangeMismatchFix, + handleMultipleMatches, + isFixableError, +} from '@/utils/fix'; +import * as markdownEdit from '@/utils/markdown-edit'; +import * as prompt from '@/utils/prompt'; +import type { CodeRefError, FixAction } from '@/utils/types'; +import type { CodeRefConfig } from '@/config'; + +// モック設定 +jest.mock('fs'); +jest.mock('@/utils/code-comparison', () => ({ + searchCodeInFile: jest.fn(), + searchCodeInFileWithScopeExpansion: jest.fn(), + extractLinesFromFile: jest.fn(), +})); +jest.mock('./markdown-edit'); +jest.mock('./prompt'); +jest.mock('./ast-scope-expansion'); + +const mockFs = fs as jest.Mocked; +const mockExtractLinesFromFile = extractLinesFromFile as jest.MockedFunction< + typeof extractLinesFromFile +>; +const mockSearchCodeInFileWithScopeExpansion = + searchCodeInFileWithScopeExpansion as jest.MockedFunction< + typeof searchCodeInFileWithScopeExpansion + >; +const mockMarkdownEdit = markdownEdit as jest.Mocked; +const mockPrompt = prompt as jest.Mocked; + +// Mock config for tests +const mockConfig: CodeRefConfig = { + projectRoot: path.resolve(__dirname, '../../..'), + docsDir: 'docs', + ignoreFile: '.docsignore', + verbose: false, +}; + +describe('isFixableError', () => { + it('修正可能なエラータイプの場合にtrueを返すこと', () => { + const fixableTypes = [ + 'CODE_LOCATION_MISMATCH', + 'CODE_BLOCK_MISSING', + 'CODE_CONTENT_MISMATCH', + 'LINE_OUT_OF_RANGE', + 'SYMBOL_RANGE_MISMATCH', + 'MULTIPLE_SYMBOLS_FOUND', + ]; + + fixableTypes.forEach((type) => { + const error: CodeRefError = { + type: type as any, + message: 'test', + ref: {} as any, + }; + expect(isFixableError(error)).toBe(true); + }); + }); + + it('修正不可能なエラータイプの場合にfalseを返すこと', () => { + const unfixableTypes = ['FILE_NOT_FOUND', 'SYMBOL_NOT_FOUND', 'INVALID_FORMAT']; + + unfixableTypes.forEach((type) => { + const error: CodeRefError = { + type: type as any, + message: 'test', + ref: {} as any, + }; + expect(isFixableError(error)).toBe(false); + }); + }); +}); + +describe('createLocationMismatchFix', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('正しい修正アクションを作成すること', () => { + const error: CodeRefError = { + type: 'CODE_LOCATION_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + suggestedLines: { start: 15, end: 25 }, + }; + + mockExtractLinesFromFile.mockReturnValue('code content'); + + const result = createLocationMismatchFix(error, mockConfig); + + expect(result).toEqual({ + type: 'UPDATE_LINE_NUMBERS', + error, + description: 'Update line numbers from 10-20 to 15-25', + preview: expect.stringContaining('test.ts:10-20'), + newStartLine: 15, + newEndLine: 25, + newCodeBlock: 'code content', + }); + }); + + it('suggestedLinesがない場合にエラーをスローすること', () => { + const error: CodeRefError = { + type: 'CODE_LOCATION_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + }; + + expect(() => createLocationMismatchFix(error, mockConfig)).toThrow( + 'CODE_LOCATION_MISMATCH requires suggestedLines' + ); + }); +}); + +describe('createBlockMissingFix', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('正しい修正アクションを作成すること', () => { + const error: CodeRefError = { + type: 'CODE_BLOCK_MISSING', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + }; + + // ドキュメントファイルの内容をモック(コードブロックなし) + mockFs.readFileSync.mockReturnValue( + '# Test Doc\n\n\n\nSome text.' as any + ); + mockExtractLinesFromFile.mockReturnValue('code content'); + + const result = createBlockMissingFix(error, mockConfig); + + expect(result).toEqual({ + type: 'INSERT_CODE_BLOCK', + error, + description: 'Insert code block from test.ts:10-20', + preview: expect.stringContaining('code content'), + newCodeBlock: 'code content', + }); + }); + + it('行番号がない場合にエラーをスローすること', () => { + const error: CodeRefError = { + type: 'CODE_BLOCK_MISSING', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: null, + endLine: null, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + }; + + expect(() => createBlockMissingFix(error, mockConfig)).toThrow( + 'Whole file reference does not need code block' + ); + }); +}); + +describe('createContentMismatchFix', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('AST拡張なしの場合に正しい修正アクションを作成すること', () => { + const error: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + codeBlock: 'old code', + }, + expectedCode: 'expected code', + }; + + mockExtractLinesFromFile.mockReturnValue('actual code'); + mockFs.readFileSync.mockReturnValue('file content' as any); + + // ast-scope-expansionモックで空配列を返す(拡張なし) + const astScopeExpansion = require('./ast-scope-expansion'); // eslint-disable-line + astScopeExpansion.expandMatchToScope = jest.fn().mockReturnValue([]); + + const result = createContentMismatchFix(error, mockConfig) as FixAction; + + expect(result.type).toBe('REPLACE_CODE_BLOCK'); + expect(result.newCodeBlock).toBe('actual code'); + }); + + it('AST拡張ありの場合に行番号更新アクションを作成すること', () => { + const error: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + codeBlock: 'old code', + }, + }; + + mockExtractLinesFromFile.mockReturnValue('expanded code'); + mockFs.readFileSync.mockReturnValue('file content' as any); + + // ast-scope-expansionモックで拡張されたマッチを返す + const astScopeExpansion = require('./ast-scope-expansion'); // eslint-disable-line + astScopeExpansion.expandMatchToScope = jest.fn().mockReturnValue([ + { + start: 8, + end: 22, + confidence: 'high', + scopeType: 'function', + expansionType: 'ast', + }, + ]); + + const result = createContentMismatchFix(error, mockConfig) as FixAction; + + expect(result.type).toBe('UPDATE_LINE_NUMBERS'); + expect(result.newStartLine).toBe(8); + expect(result.newEndLine).toBe(22); + expect(result.newCodeBlock).toBe('expanded code'); + }); +}); + +describe('createLineOutOfRangeFix', () => { + it('正しい修正アクションを作成すること', () => { + const error: CodeRefError = { + type: 'LINE_OUT_OF_RANGE', + message: '終了行 150 > 100', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 150, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + }; + + const result = createLineOutOfRangeFix(error); + + expect(result).toEqual({ + type: 'UPDATE_END_LINE', + error, + description: 'Fix end line from 150 to 100 (end of file)', + preview: expect.stringContaining('10-100'), + newStartLine: 10, + newEndLine: 100, + }); + }); + + it('エラーメッセージから行数を抽出できない場合にエラーをスローすること', () => { + const error: CodeRefError = { + type: 'LINE_OUT_OF_RANGE', + message: 'invalid message', + ref: {} as any, + }; + + expect(() => createLineOutOfRangeFix(error)).toThrow( + 'Cannot get line count from LINE_OUT_OF_RANGE error message' + ); + }); +}); + +describe('createSymbolRangeMismatchFix', () => { + it('正しい修正アクションを作成すること', () => { + const error: CodeRefError = { + type: 'SYMBOL_RANGE_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + symbolPath: 'ClassName#methodName', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + suggestedSymbol: { + memberName: 'methodName', + className: 'ClassName', + startLine: 15, + endLine: 25, + scopeType: 'method', + confidence: 'high', + }, + }; + + const result = createSymbolRangeMismatchFix(error); + + expect(result).toEqual({ + type: 'UPDATE_SYMBOL_RANGE', + error, + description: 'Update line numbers for symbol "ClassName#methodName" from 10-20 to 15-25', + preview: expect.stringContaining('15-25'), + newStartLine: 15, + newEndLine: 25, + }); + }); + + it('suggestedSymbolがない場合にエラーをスローすること', () => { + const error: CodeRefError = { + type: 'SYMBOL_RANGE_MISMATCH', + message: 'test', + ref: {} as any, + }; + + expect(() => createSymbolRangeMismatchFix(error)).toThrow( + 'SYMBOL_RANGE_MISMATCH requires suggestedSymbol' + ); + }); +}); + +describe('createMultipleSymbolsFoundFix', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('ユーザーが選択したシンボルで修正アクションを作成すること', async () => { + const error: CodeRefError = { + type: 'MULTIPLE_SYMBOLS_FOUND', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + symbolPath: 'methodName', + startLine: null, + endLine: null, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + foundSymbols: [ + { + memberName: 'methodName', + startLine: 10, + endLine: 20, + scopeType: 'method', + confidence: 'high', + }, + { + memberName: 'methodName', + startLine: 50, + endLine: 60, + scopeType: 'method', + confidence: 'high', + }, + ], + }; + + const mockRl = {} as readline.Interface; + mockPrompt.askSelectOption.mockResolvedValue(1); + + const result = await createMultipleSymbolsFoundFix(error, mockRl); + + expect(result).toEqual({ + type: 'UPDATE_SYMBOL_RANGE', + error, + description: 'Add line numbers for symbol "methodName": 50-60', + preview: expect.stringContaining('50-60'), + newStartLine: 50, + newEndLine: 60, + }); + }); + + it('foundSymbolsがない場合にエラーをスローすること', async () => { + const error: CodeRefError = { + type: 'MULTIPLE_SYMBOLS_FOUND', + message: 'test', + ref: {} as any, + }; + + const mockRl = {} as readline.Interface; + + await expect(createMultipleSymbolsFoundFix(error, mockRl)).rejects.toThrow( + 'MULTIPLE_SYMBOLS_FOUND requires foundSymbols' + ); + }); +}); + +describe('createFixAction', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('修正不可能なエラーの場合にnullを返すこと', async () => { + const error: CodeRefError = { + type: 'FILE_NOT_FOUND', + message: 'test', + ref: {} as any, + }; + + const result = await createFixAction(error, mockConfig); + + expect(result).toBeNull(); + }); + + it('CODE_LOCATION_MISMATCHの場合に適切な修正アクションを返すこと', async () => { + const error: CodeRefError = { + type: 'CODE_LOCATION_MISMATCH', + message: 'test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: '/path/to/doc.md', + docLineNumber: 5, + }, + suggestedLines: { start: 15, end: 25 }, + }; + + mockExtractLinesFromFile.mockReturnValue('code content'); + + const result = (await createFixAction(error, mockConfig)) as FixAction; + + expect(result?.type).toBe('UPDATE_LINE_NUMBERS'); + }); + + it('MULTIPLE_SYMBOLS_FOUNDでreadline.Interfaceがない場合にエラーをスローすること', async () => { + const error: CodeRefError = { + type: 'MULTIPLE_SYMBOLS_FOUND', + message: 'test', + ref: {} as any, + foundSymbols: [ + { + memberName: 'test', + startLine: 1, + endLine: 10, + scopeType: 'method', + confidence: 'high', + }, + ], + }; + + await expect(createFixAction(error, mockConfig)).rejects.toThrow( + 'MULTIPLE_SYMBOLS_FOUND requires readline.Interface' + ); + }); +}); + +describe('優先順位付けロジック', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('handleMultipleMatches()', () => { + const createMockError = ( + startLine: number, + endLine: number, + codeBlock = 'test code' + ): CodeRefError => ({ + type: 'CODE_LOCATION_MISMATCH', + message: 'Test error', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine, + endLine, + docFile: 'test.md', + codeBlock, + }, + }); + + const createMockReadline = (answer = 0): readline.Interface => { + const rl = { + question: jest.fn((_query, callback) => { + // ユーザーが選択肢を選んだとして、すぐにコールバックを呼ぶ + callback(`${answer + 1}`); // 1-indexed の選択肢 + }), + close: jest.fn(), + } as unknown as readline.Interface; + return rl; + }; + + it('単一マッチの場合、そのマッチを返す', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([ + { start: 15, end: 25, confidence: 'high', expansionType: 'ast', scopeType: 'function' }, + ]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).not.toBeNull(); + expect(result?.newStartLine).toBe(15); + expect(result?.newEndLine).toBe(25); + }); + + it('複数マッチで高信頼度が1つの場合、自動選択する', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + // 1つだけ高信頼度のマッチがある場合、自動選択される + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([ + { + start: 10, + end: 20, + confidence: 'high', + expansionType: 'ast', + scopeType: 'interface', + }, + { start: 50, end: 60, confidence: 'low', expansionType: 'none', scopeType: 'unknown' }, + { start: 100, end: 110, confidence: 'low', expansionType: 'none', scopeType: 'unknown' }, + ]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).not.toBeNull(); + expect(result?.newStartLine).toBe(10); + expect(result?.newEndLine).toBe(20); + // 自動選択されたので、ユーザーに質問しない + expect(rl.question).not.toHaveBeenCalled(); + }); + + it('元の位置に近いマッチを優先する', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + // 距離の異なる3つのマッチ(全て同じ信頼度) + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([ + { start: 100, end: 110, confidence: 'medium', expansionType: 'none', scopeType: 'unknown' }, // 90行離れている + { start: 12, end: 22, confidence: 'medium', expansionType: 'none', scopeType: 'unknown' }, // 2行ずれ - 最も近い + { start: 50, end: 60, confidence: 'medium', expansionType: 'none', scopeType: 'unknown' }, // 40行離れている + ]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).not.toBeNull(); + // ソート後の配列から選択される(モックのreadlineが選択) + expect(result?.newStartLine).toBe(50); + expect(result?.newEndLine).toBe(60); + }); + + it('スコープタイプで優先順位付けする', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + // 同じ距離だが、スコープタイプが異なる(複数のhighがあるのでユーザーに選択させる) + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([ + { start: 12, end: 22, confidence: 'high', expansionType: 'ast', scopeType: 'function' }, + { start: 13, end: 23, confidence: 'high', expansionType: 'ast', scopeType: 'interface' }, + { start: 14, end: 24, confidence: 'high', expansionType: 'ast', scopeType: 'const' }, + ]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).not.toBeNull(); + // 複数のhigh信頼度マッチがあり、その中でスコープタイプの優先度が高いマッチが自動選択される + expect(result?.newStartLine).toBe(13); + expect(result?.newEndLine).toBe(23); + // 高信頼度マッチが1つだけ(ソート後の最優先)なので自動選択される + expect(rl.question).not.toHaveBeenCalled(); + }); + + it('信頼度が最優先される', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + // 距離は遠いが信頼度が高いマッチがある + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([ + { start: 11, end: 21, confidence: 'low', expansionType: 'none', scopeType: 'unknown' }, // 近いが低信頼度 + { start: 100, end: 110, confidence: 'high', expansionType: 'ast', scopeType: 'interface' }, // 遠いが高信頼度 + ]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).not.toBeNull(); + // 高信頼度が1つだけなので自動選択される + expect(result?.newStartLine).toBe(100); + expect(result?.newEndLine).toBe(110); + }); + + it('マッチが見つからない場合、nullを返す', async () => { + const error = createMockError(10, 20); + const rl = createMockReadline(); + + mockSearchCodeInFileWithScopeExpansion.mockReturnValue([]); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).toBeNull(); + }); + + it('codeBlockがない場合、nullを返す', async () => { + const error: CodeRefError = { + type: 'CODE_LOCATION_MISMATCH', + message: 'Test error', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + // codeBlock なし + }, + }; + const rl = createMockReadline(); + + const result = await handleMultipleMatches(error, rl, mockConfig); + + expect(result).toBeNull(); + expect(mockSearchCodeInFileWithScopeExpansion).not.toHaveBeenCalled(); + }); + }); +}); + +describe('行番号管理', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('applyFix() のライン delta', () => { + it('INSERT_CODE_BLOCK で行が追加される場合、正のdeltaを返す', () => { + const originalContent = `# Test Document + + + +Some content`; + + const newContent = `# Test Document + + +\`\`\`typescript +const test = 'hello'; +console.log(test); +\`\`\` + +Some content`; + + // モック設定 + mockFs.readFileSync.mockReturnValue(originalContent as any); + mockFs.writeFileSync.mockImplementation(() => {}); + mockMarkdownEdit.insertCodeBlockAfterComment.mockReturnValue(newContent); + + const action: FixAction = { + type: 'INSERT_CODE_BLOCK', + error: { + type: 'CODE_BLOCK_MISSING', + message: 'Test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + }, + description: 'Insert code block', + preview: 'Insert code block after CODE_REF comment', + newStartLine: 1, + newEndLine: 5, + newCodeBlock: "const test = 'hello';\nconsole.log(test);", + }; + + const lineDelta = applyFix(action); + + expect(mockMarkdownEdit.insertCodeBlockAfterComment).toHaveBeenCalled(); + expect(mockFs.writeFileSync).toHaveBeenCalled(); + // 元の5行 → 新しい9行 = +4行 + expect(lineDelta).toBe(4); + }); + + it('REPLACE_CODE_BLOCK で行が増加する場合、正のdeltaを返す', () => { + const originalContent = `# Test Document + + +\`\`\`typescript +const test = 'hello'; +\`\`\` + +Some content`; + + const newContent = `# Test Document + + +\`\`\`typescript +const test = 'hello'; +console.log(test); +console.log('more'); +\`\`\` + +Some content`; + + // モック設定 + mockFs.readFileSync.mockReturnValue(originalContent as any); + mockFs.writeFileSync.mockImplementation(() => {}); + mockMarkdownEdit.replaceCodeBlock.mockReturnValue(newContent); + + const action: FixAction = { + type: 'REPLACE_CODE_BLOCK', + error: { + type: 'CODE_CONTENT_MISMATCH', + message: 'Test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + docLineNumber: 3, + codeBlock: "const test = 'hello';", + }, + }, + description: 'Replace code block', + preview: 'Replace existing code block', + newStartLine: 1, + newEndLine: 5, + newCodeBlock: "const test = 'hello';\nconsole.log(test);\nconsole.log('more');", + }; + + const lineDelta = applyFix(action); + + expect(mockMarkdownEdit.replaceCodeBlock).toHaveBeenCalled(); + expect(mockFs.writeFileSync).toHaveBeenCalled(); + // 元の8行 → 新しい10行 = +2行 + expect(lineDelta).toBe(2); + }); + + it('UPDATE_LINE_NUMBERS で行数変化がない場合、0を返す', () => { + const originalContent = `# Test Document + + + +Some content`; + + const newContent = `# Test Document + + + +Some content`; + + // モック設定 + mockFs.readFileSync.mockReturnValue(originalContent as any); + mockFs.writeFileSync.mockImplementation(() => {}); + mockMarkdownEdit.replaceCodeRefComment.mockReturnValue(newContent); + mockMarkdownEdit.findCodeBlockPosition.mockReturnValue(null); + + const action: FixAction = { + type: 'UPDATE_LINE_NUMBERS', + error: { + type: 'CODE_LOCATION_MISMATCH', + message: 'Test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + }, + description: 'Update line numbers', + preview: 'Update CODE_REF line numbers', + newStartLine: 10, + newEndLine: 15, + }; + + const lineDelta = applyFix(action); + + expect(mockMarkdownEdit.replaceCodeRefComment).toHaveBeenCalled(); + expect(mockFs.writeFileSync).toHaveBeenCalled(); + // 行数変化なし(コメントのみ更新) + expect(lineDelta).toBe(0); + }); + + it('REPLACE_CODE_BLOCK で行が減少する場合、負のdeltaを返す', () => { + const originalContent = `# Test Document + + +\`\`\`typescript +const test = 'hello'; +console.log(test); +console.log('more'); +\`\`\` + +Some content`; + + const newContent = `# Test Document + + +\`\`\`typescript +const test = 'hello'; +\`\`\` + +Some content`; + + // モック設定 + mockFs.readFileSync.mockReturnValue(originalContent as any); + mockFs.writeFileSync.mockImplementation(() => {}); + mockMarkdownEdit.replaceCodeBlock.mockReturnValue(newContent); + + const action: FixAction = { + type: 'REPLACE_CODE_BLOCK', + error: { + type: 'CODE_CONTENT_MISMATCH', + message: 'Test', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + docLineNumber: 3, + codeBlock: "const test = 'hello';\nconsole.log(test);\nconsole.log('more');", + }, + }, + description: 'Replace code block', + preview: 'Replace existing code block', + newStartLine: 1, + newEndLine: 5, + newCodeBlock: "const test = 'hello';", + }; + + const lineDelta = applyFix(action); + + expect(mockMarkdownEdit.replaceCodeBlock).toHaveBeenCalled(); + expect(mockFs.writeFileSync).toHaveBeenCalled(); + // 元の10行 → 新しい8行 = -2行 + expect(lineDelta).toBe(-2); + }); + }); +}); diff --git a/src/utils/fix.ts b/src/utils/fix.ts new file mode 100644 index 0000000..88ccfc2 --- /dev/null +++ b/src/utils/fix.ts @@ -0,0 +1,664 @@ +/** + * Fix logic utilities + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import type * as readline from 'readline'; + +import { expandMatchToScope } from '@/utils/ast-scope-expansion'; +import { findSymbolInAST } from '@/utils/ast-symbol-search'; +import { extractLinesFromFile, searchCodeInFileWithScopeExpansion } from '@/utils/code-comparison'; +import { + findCodeBlockPosition, + insertCodeBlockAfterComment, + moveCodeRefCommentBeforeCodeBlock, + replaceCodeBlock, + replaceCodeRefComment, +} from '@/utils/markdown-edit'; +import { askSelectOption } from '@/utils/prompt'; +import type { CodeRefError, ExpandedMatch, FixAction } from '@/utils/types'; +import type { CodeRefConfig } from '@/config'; + +/** + * Check if error is fixable + */ +export function isFixableError(error: CodeRefError): boolean { + return [ + 'CODE_LOCATION_MISMATCH', + 'CODE_BLOCK_MISSING', + 'CODE_CONTENT_MISMATCH', + 'LINE_OUT_OF_RANGE', + 'SYMBOL_RANGE_MISMATCH', + 'MULTIPLE_SYMBOLS_FOUND', + ].includes(error.type); +} + +/** + * Create fix action for CODE_LOCATION_MISMATCH + */ +export function createLocationMismatchFix(error: CodeRefError, config: CodeRefConfig): FixAction { + if (!error.suggestedLines) { + throw new Error('CODE_LOCATION_MISMATCH requires suggestedLines'); + } + + const { ref } = error; + const oldComment = ref.fullMatch; + const newComment = ``; + + // Get actual code for preview + const projectRoot = config.projectRoot; + const absolutePath = path.resolve(projectRoot, ref.refPath); + const actualCode = extractLinesFromFile( + absolutePath, + error.suggestedLines.start, + error.suggestedLines.end + ); + + return { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Update line numbers from ${ref.startLine}-${ref.endLine} to ${error.suggestedLines.start}-${error.suggestedLines.end}`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: error.suggestedLines.start, + newEndLine: error.suggestedLines.end, + newCodeBlock: actualCode, + }; +} + +/** + * Search for code block near CODE_REF comment + * + * @param content Document content + * @param commentMatch CODE_REF comment text + * @returns Code block information, or null if not found + */ +function findCodeBlockNearComment( + content: string, + commentMatch: string +): { start: number; end: number; language: string; content: string } | null { + const commentIndex = content.indexOf(commentMatch); + if (commentIndex === -1) return null; + + const commentEnd = content.indexOf('-->', commentIndex); + if (commentEnd === -1) return null; + + const searchStart = commentEnd + 3; + const searchWindow = content.substring(searchStart, searchStart + 5000); + + // Search for next CODE_REF comment + const nextCommentMatch = /`; + + const option2: FixAction = { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Remove symbol specification and change to line number specification (maintain code block, manual adjustment needed)`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: symbolMatch.startLine, + newEndLine: symbolMatch.endLine, + newCodeBlock: ref.codeBlock, // Maintain existing code block + }; + + return [option1, option2]; + } + } + + // Existing logic when line numbers are specified + if (ref.startLine === null || ref.endLine === null) { + throw new Error('CODE_CONTENT_MISMATCH requires line numbers or symbol specification'); + } + + const actualCode = extractLinesFromFile(absolutePath, ref.startLine, ref.endLine); + + // Check if specified line range is incomplete scope (e.g., middle of function) using AST analysis + // Detect scope using only start line (to avoid false detection when using entire range) + const fileContent = fs.readFileSync(absolutePath, 'utf-8'); + const expandedMatches = expandMatchToScope({ + filePath: absolutePath, + originalMatch: { start: ref.startLine, end: ref.startLine }, + fileContent, + }); + + // Update line numbers if expansion result differs from original range and has high confidence + const expanded = expandedMatches[0]; + if ( + expanded?.confidence === 'high' && + (expanded.start !== ref.startLine || expanded.end !== ref.endLine) + ) { + // Update line numbers if scope expansion succeeds + const expandedCode = extractLinesFromFile(absolutePath, expanded.start, expanded.end); + const scopeInfo = expanded.scopeType ? ` (${expanded.scopeType})` : ''; + console.log( + ` ℹ️ Expanded ${ref.startLine}-${ref.endLine} to ${expanded.start}-${expanded.end} using AST analysis${scopeInfo}` + ); + + const oldComment = ref.fullMatch; + const newComment = ``; + + return { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Update line numbers from ${ref.startLine}-${ref.endLine} to ${expanded.start}-${expanded.end} using AST analysis and replace code block`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: expanded.start, + newEndLine: expanded.end, + newCodeBlock: expandedCode, + }; + } + + // If AST expansion is unnecessary or fails, replace only code block as before + const expectedPreview = error.expectedCode?.substring(0, 100) || ''; + const actualPreview = actualCode.substring(0, 100); + + return { + type: 'REPLACE_CODE_BLOCK', + error, + description: 'Replace code block with actual code content', + preview: `Expected: ${expectedPreview}${expectedPreview.length >= 100 ? '...' : ''}\nActual: ${actualPreview}${actualPreview.length >= 100 ? '...' : ''}`, + newCodeBlock: actualCode, + }; +} + +/** + * Create fix action for LINE_OUT_OF_RANGE + */ +export function createLineOutOfRangeFix(error: CodeRefError): FixAction { + const { ref } = error; + + // Extract line count from error message + const match = /(\d+)\s*>\s*(\d+)/.exec(error.message); + if (!match) { + throw new Error('Cannot get line count from LINE_OUT_OF_RANGE error message'); + } + + const totalLines = parseInt(match[2], 10); + const oldComment = ref.fullMatch; + const newComment = ``; + + return { + type: 'UPDATE_END_LINE', + error, + description: `Fix end line from ${ref.endLine} to ${totalLines} (end of file)`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: ref.startLine!, + newEndLine: totalLines, + }; +} + +/** + * Create fix action for SYMBOL_RANGE_MISMATCH + */ +export function createSymbolRangeMismatchFix(error: CodeRefError): FixAction { + if (!error.suggestedSymbol) { + throw new Error('SYMBOL_RANGE_MISMATCH requires suggestedSymbol'); + } + + const { ref } = error; + const suggested = error.suggestedSymbol; + + const oldComment = ref.fullMatch; + const newComment = ``; + + return { + type: 'UPDATE_SYMBOL_RANGE', + error, + description: `Update line numbers for symbol "${ref.symbolPath}" from ${ref.startLine}-${ref.endLine} to ${suggested.startLine}-${suggested.endLine}`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: suggested.startLine, + newEndLine: suggested.endLine, + }; +} + +/** + * Create fix action for MULTIPLE_SYMBOLS_FOUND (interactive selection) + */ +export async function createMultipleSymbolsFoundFix( + error: CodeRefError, + rl: readline.Interface +): Promise { + if (!error.foundSymbols || error.foundSymbols.length === 0) { + throw new Error('MULTIPLE_SYMBOLS_FOUND requires foundSymbols'); + } + + const { ref } = error; + + const options = error.foundSymbols.map((symbol) => { + const classInfo = symbol.className ? `${symbol.className}#` : ''; + return `Line ${symbol.startLine}-${symbol.endLine} (${classInfo}${symbol.memberName})`; + }); + + const selectedIndex = await askSelectOption( + rl, + options, + `⚠️ Symbol "${ref.symbolPath}" found in ${error.foundSymbols.length} locations. Which position should be used?` + ); + + const selected = error.foundSymbols[selectedIndex]; + + const oldComment = ref.fullMatch; + const newComment = ``; + + return { + type: 'UPDATE_SYMBOL_RANGE', + error, + description: `Add line numbers for symbol "${ref.symbolPath}": ${selected.startLine}-${selected.endLine}`, + preview: `${oldComment}\n→ ${newComment}`, + newStartLine: selected.startLine, + newEndLine: selected.endLine, + }; +} + +/** + * Create fix action based on error type + */ +export async function createFixAction( + error: CodeRefError, + config: CodeRefConfig, + rl?: readline.Interface +): Promise { + if (!isFixableError(error)) { + return null; + } + + switch (error.type) { + case 'CODE_LOCATION_MISMATCH': + return createLocationMismatchFix(error, config); + case 'CODE_BLOCK_MISSING': + return createBlockMissingFix(error, config); + case 'CODE_CONTENT_MISMATCH': + return createContentMismatchFix(error, config); + case 'LINE_OUT_OF_RANGE': + return createLineOutOfRangeFix(error); + case 'SYMBOL_RANGE_MISMATCH': + return createSymbolRangeMismatchFix(error); + case 'MULTIPLE_SYMBOLS_FOUND': + if (!rl) { + throw new Error('MULTIPLE_SYMBOLS_FOUND requires readline.Interface'); + } + return await createMultipleSymbolsFoundFix(error, rl); + default: + return null; + } +} + +/** + * Apply fix to markdown file + * @returns Line delta (positive: lines added, negative: lines removed, 0: no change) + */ +export function applyFix(action: FixAction): number { + const filePath = action.error.ref.docFile; + let content = fs.readFileSync(filePath, 'utf-8'); + const originalLines = content.split('\n').length; + + switch (action.type) { + case 'UPDATE_LINE_NUMBERS': + case 'UPDATE_END_LINE': { + // Update CODE_REF comment + const oldComment = action.error.ref.fullMatch; + const newComment = ``; + content = replaceCodeRefComment(content, oldComment, newComment); + + // Also update code block (if exists) + if (action.newCodeBlock) { + const blockPos = findCodeBlockPosition(content, newComment); + if (blockPos) { + const newBlock = `\`\`\`${blockPos.language}\n${action.newCodeBlock}\n\`\`\``; + content = + content.substring(0, blockPos.start) + newBlock + content.substring(blockPos.end); + } + } + break; + } + + case 'INSERT_CODE_BLOCK': { + content = insertCodeBlockAfterComment( + content, + action.error.ref.fullMatch, + action.newCodeBlock! + ); + break; + } + + case 'REPLACE_CODE_BLOCK': { + if (!action.error.ref.codeBlock) { + throw new Error('Code block not found'); + } + content = replaceCodeBlock(content, action.error.ref.codeBlock, action.newCodeBlock!); + break; + } + + case 'MOVE_CODE_REF_COMMENT': { + if (!action.codeBlockPosition) { + throw new Error('MOVE_CODE_REF_COMMENT requires codeBlockPosition'); + } + + content = moveCodeRefCommentBeforeCodeBlock( + content, + action.error.ref.fullMatch, + action.codeBlockPosition.start + ); + break; + } + } + + fs.writeFileSync(filePath, content, 'utf-8'); + + // Return line delta + const newLines = content.split('\n').length; + return newLines - originalLines; +} + +/** + * Prioritization context + */ +interface PrioritizationContext { + originalStart: number; + originalEnd: number; +} + +/** + * Prioritization considering confidence and scope type + * + * @param matches Array of expanded matches + * @param context Prioritization context (original line numbers) + * @returns Prioritized matches (considering confidence and scope type) + */ +function prioritizeMatchesWithConfidence( + matches: ExpandedMatch[], + context: PrioritizationContext +): ExpandedMatch[] { + return matches.sort((a, b) => { + // 1. Prioritize by confidence (highest priority) + const confidenceScore = { high: 3, medium: 2, low: 1 }; + const confDiff = confidenceScore[b.confidence] - confidenceScore[a.confidence]; + if (confDiff !== 0) return confDiff; + + // 2. Prioritize by proximity to original line numbers + const distanceA = Math.abs(a.start - context.originalStart); + const distanceB = Math.abs(b.start - context.originalStart); + const distDiff = distanceA - distanceB; + if (distDiff !== 0) return distDiff; + + // 3. Scope type priority + const scopePriority = { + interface: 5, + type: 4, + class: 3, + function: 2, + const: 1, + unknown: 0, + }; + const scopeDiff = + scopePriority[b.scopeType || 'unknown'] - scopePriority[a.scopeType || 'unknown']; + if (scopeDiff !== 0) return scopeDiff; + + // 4. Prioritize shorter range (more specific match) + return a.end - a.start - (b.end - b.start); + }); +} + +/** + * Handle multiple matches + */ +export async function handleMultipleMatches( + error: CodeRefError, + rl: readline.Interface, + config: CodeRefConfig +): Promise { + const { ref } = error; + + if (!ref.codeBlock) { + return null; + } + + const projectRoot = config.projectRoot; + const absolutePath = path.resolve(projectRoot, ref.refPath); + + // Use AST analysis with scope expansion + const matches = searchCodeInFileWithScopeExpansion(absolutePath, ref.codeBlock); + + if (matches.length === 0) { + return null; + } + + if (matches.length === 1) { + // Single match - create fix action + const match = matches[0]; + const confidenceInfo = + match.confidence === 'high' && match.scopeType + ? ` (${match.scopeType})` + : match.confidence === 'low' + ? ' (low confidence)' + : ''; + console.log( + `\n✓ Detected single match in ${ref.refPath}: lines ${match.start}-${match.end}${confidenceInfo}` + ); + return { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Update line numbers from ${ref.startLine}-${ref.endLine} to ${match.start}-${match.end}`, + preview: ``, + newStartLine: match.start, + newEndLine: match.end, + matchOptions: matches.map((m) => ({ start: m.start, end: m.end })), + }; + } + + // Multiple matches - try automatic selection with prioritization + const sortedMatches = prioritizeMatchesWithConfidence(matches, { + originalStart: ref.startLine!, + originalEnd: ref.endLine!, + }); + + // Auto-select if only one high-confidence match + const highConfidenceMatches = sortedMatches.filter((m) => m.confidence === 'high'); + if (highConfidenceMatches.length === 1) { + const bestMatch = highConfidenceMatches[0]; + const scopeInfo = bestMatch.scopeType ? ` (${bestMatch.scopeType})` : ''; + console.log( + `\n✓ Auto-selected most appropriate match in ${ref.refPath}: lines ${bestMatch.start}-${bestMatch.end}${scopeInfo}` + ); + return { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Update line numbers from ${ref.startLine}-${ref.endLine} to ${bestMatch.start}-${bestMatch.end}`, + preview: ``, + newStartLine: bestMatch.start, + newEndLine: bestMatch.end, + matchOptions: matches.map((m) => ({ start: m.start, end: m.end })), + }; + } + + // Otherwise let user choose + console.log(`\n⚠️ Code found in ${matches.length} locations in ${ref.refPath}:`); + const options = sortedMatches.map((m) => { + const confidenceLabel = + m.confidence === 'high' ? 'high' : m.confidence === 'medium' ? 'medium' : 'low'; + const scopeInfo = m.scopeType && m.scopeType !== 'unknown' ? `, ${m.scopeType}` : ''; + return `Line ${m.start}-${m.end} (confidence: ${confidenceLabel}${scopeInfo})`; + }); + + const selection = await askSelectOption(rl, options, 'Which position should be used?'); + const selectedMatch = sortedMatches[selection]; + + // Warn if selected match has low confidence + if (selectedMatch.confidence === 'low') { + console.warn('⚠️ Scope detection confidence is low, please verify the result.'); + } + if (selectedMatch.expansionType === 'none') { + console.warn('⚠️ Structural analysis failed, using simple string matching.'); + } + + return { + type: 'UPDATE_LINE_NUMBERS', + error, + description: `Update line numbers from ${ref.startLine}-${ref.endLine} to ${selectedMatch.start}-${selectedMatch.end}`, + preview: ``, + newStartLine: selectedMatch.start, + newEndLine: selectedMatch.end, + matchOptions: matches.map((m) => ({ start: m.start, end: m.end })), + }; +} diff --git a/src/utils/ignore-pattern.test.ts b/src/utils/ignore-pattern.test.ts new file mode 100644 index 0000000..005131c --- /dev/null +++ b/src/utils/ignore-pattern.test.ts @@ -0,0 +1,232 @@ +import * as fs from 'fs'; + +import { isIgnored, loadDocsignorePatterns, matchesPattern } from '@/utils/ignore-pattern'; + +// fsをモック +jest.mock('fs'); +const mockedFs = fs as jest.Mocked; + +describe('ignore-pattern.utils', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('matchesPattern', () => { + describe('ディレクトリパターン(末尾が/)', () => { + it('ディレクトリ配下のファイルにマッチする', () => { + expect(matchesPattern('node_modules/package.json', 'node_modules/')).toBe(true); + expect(matchesPattern('node_modules/nested/file.js', 'node_modules/')).toBe(true); + }); + + it('末尾のスラッシュなしでもマッチする', () => { + expect(matchesPattern('node_modules/package.json', 'node_modules/')).toBe(true); + }); + + it('異なるディレクトリにはマッチしない', () => { + expect(matchesPattern('src/index.ts', 'node_modules/')).toBe(false); + }); + }); + + describe('**/パターン(任意のディレクトリ階層)', () => { + it('任意の階層の指定ファイルにマッチする', () => { + expect(matchesPattern('src/test.log', '**/*.log')).toBe(true); + expect(matchesPattern('nested/deep/test.log', '**/*.log')).toBe(true); + }); + + it('ファイル名が含まれる場合にマッチする', () => { + expect(matchesPattern('logs/debug-file.json', '**/debug-*.json')).toBe(true); + expect(matchesPattern('src/logs/debug-2024.json', '**/debug-*.json')).toBe(true); + }); + + it('パス内のディレクトリ名にマッチする', () => { + expect(matchesPattern('node_modules/package/index.js', 'node_modules/')).toBe(true); + expect(matchesPattern('src/node_modules/index.js', 'node_modules/')).toBe(false); + }); + + it('サフィックスマッチング', () => { + expect(matchesPattern('path/to/config.json', '**/config.json')).toBe(true); + expect(matchesPattern('config.json', '**/config.json')).toBe(true); + }); + + it('マッチしないファイル', () => { + expect(matchesPattern('src/index.ts', '**/*.log')).toBe(false); + }); + }); + + describe('*ワイルドカード', () => { + it('ワイルドカードパターンにマッチする', () => { + expect(matchesPattern('test.config.js', '*.config.js')).toBe(true); + expect(matchesPattern('eslint.config.js', '*.config.js')).toBe(true); + }); + + it('拡張子のみのワイルドカード', () => { + expect(matchesPattern('file.log', '*.log')).toBe(true); + expect(matchesPattern('debug.log', '*.log')).toBe(true); + }); + + it('マッチしないファイル', () => { + expect(matchesPattern('test.js', '*.config.js')).toBe(false); + }); + }); + + describe('完全一致またはプレフィックスマッチ', () => { + it('完全一致する', () => { + expect(matchesPattern('README.md', 'README.md')).toBe(true); + }); + + it('プレフィックスマッチする', () => { + expect(matchesPattern('src/index.ts', 'src')).toBe(true); + expect(matchesPattern('src/nested/file.ts', 'src')).toBe(true); + }); + + it('マッチしないパス', () => { + expect(matchesPattern('source/index.ts', 'src')).toBe(false); + expect(matchesPattern('README.txt', 'README.md')).toBe(false); + }); + }); + + describe('エッジケース', () => { + it('ドットで始まるファイル', () => { + expect(matchesPattern('.env', '.env')).toBe(true); + expect(matchesPattern('.env.local', '.env*')).toBe(true); + }); + + it('複数のワイルドカード', () => { + expect(matchesPattern('test.spec.ts', '*.spec.*')).toBe(true); + }); + + it('疑問符ワイルドカード', () => { + expect(matchesPattern('file1.ts', 'file?.ts')).toBe(true); + expect(matchesPattern('file12.ts', 'file?.ts')).toBe(false); + }); + }); + }); + + describe('isIgnored', () => { + it('いずれかのパターンにマッチする場合はtrueを返す', () => { + const patterns = ['node_modules/', '*.log', 'dist/']; + + expect(isIgnored('node_modules/package.json', patterns)).toBe(true); + expect(isIgnored('debug.log', patterns)).toBe(true); + expect(isIgnored('dist/bundle.js', patterns)).toBe(true); + }); + + it('どのパターンにもマッチしない場合はfalseを返す', () => { + const patterns = ['node_modules/', '*.log', 'dist/']; + + expect(isIgnored('src/index.ts', patterns)).toBe(false); + expect(isIgnored('README.md', patterns)).toBe(false); + }); + + it('空のパターン配列の場合はfalseを返す', () => { + expect(isIgnored('any/file.ts', [])).toBe(false); + }); + + it('複数パターンの組み合わせ', () => { + const patterns = ['**/*.test.ts', 'coverage/', '.env*']; + + expect(isIgnored('src/utils/helper.test.ts', patterns)).toBe(true); + expect(isIgnored('coverage/lcov.info', patterns)).toBe(true); + expect(isIgnored('.env.local', patterns)).toBe(true); + expect(isIgnored('src/index.ts', patterns)).toBe(false); + }); + }); + + describe('loadDocsignorePatterns', () => { + it('.docsignoreファイルが存在する場合はパターンを読み込む', () => { + const mockContent = `# Comment +node_modules/ +*.log + +# Another comment +dist/ +coverage/`; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(mockContent); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual(['node_modules/', '*.log', 'dist/', 'coverage/']); + expect(mockedFs.existsSync).toHaveBeenCalledWith('/path/to/.docsignore'); + expect(mockedFs.readFileSync).toHaveBeenCalledWith('/path/to/.docsignore', 'utf-8'); + }); + + it('空行を除外する', () => { + const mockContent = `node_modules/ + + +*.log + +`; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(mockContent); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual(['node_modules/', '*.log']); + }); + + it('コメント行を除外する', () => { + const mockContent = `# This is a comment +node_modules/ +# Another comment +*.log`; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(mockContent); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual(['node_modules/', '*.log']); + }); + + it('前後の空白をトリムする', () => { + const mockContent = ` node_modules/ + *.log +`; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(mockContent); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual(['node_modules/', '*.log']); + }); + + it('.docsignoreファイルが存在しない場合は空配列を返す', () => { + mockedFs.existsSync.mockReturnValue(false); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual([]); + expect(mockedFs.existsSync).toHaveBeenCalledWith('/path/to/.docsignore'); + expect(mockedFs.readFileSync).not.toHaveBeenCalled(); + }); + + it('空のファイルの場合は空配列を返す', () => { + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(''); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual([]); + }); + + it('コメントと空行のみの場合は空配列を返す', () => { + const mockContent = `# Comment only + +# Another comment + +`; + + mockedFs.existsSync.mockReturnValue(true); + mockedFs.readFileSync.mockReturnValue(mockContent); + + const result = loadDocsignorePatterns('/path/to/.docsignore'); + + expect(result).toEqual([]); + }); + }); +}); diff --git a/src/utils/ignore-pattern.ts b/src/utils/ignore-pattern.ts new file mode 100644 index 0000000..f50f694 --- /dev/null +++ b/src/utils/ignore-pattern.ts @@ -0,0 +1,66 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * ファイルパスがパターンにマッチするかチェック(シンプルなグロブマッチング) + */ +export function matchesPattern(filePath: string, pattern: string): boolean { + // ディレクトリパターン(末尾が/) + if (pattern.endsWith('/')) { + return filePath.startsWith(pattern) || filePath.startsWith(`${pattern.slice(0, -1)}/`); + } + + // **/ パターン(任意のディレクトリ階層) + if (pattern.startsWith('**/')) { + const suffix = pattern.slice(3); + // ワイルドカード処理 + if (suffix.includes('*') || suffix.includes('?')) { + const regex = new RegExp( + suffix.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.') + ); + // ファイル名全体またはパスの各部分でマッチングを試みる + const basename = path.basename(filePath); + const pathParts = filePath.split('/'); + return regex.test(basename) || pathParts.some((part) => regex.test(part)); + } + return filePath.endsWith(suffix) || filePath.includes(`/${suffix}`); + } + + // * または ? ワイルドカード(パスセパレーター無しの場合のみベース名でマッチング) + if (pattern.includes('*') || pattern.includes('?')) { + const regex = new RegExp( + `^${pattern.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.')}$` + ); + // パスセパレーターが無いパターンの場合はベース名のみでマッチング + if (!pattern.includes('/')) { + return regex.test(path.basename(filePath)); + } + // パスセパレーターがある場合はフルパスでマッチング + return regex.test(filePath); + } + + // 完全一致またはプレフィックスマッチ + return filePath === pattern || filePath.startsWith(`${pattern}/`); +} + +/** + * ファイルが指定されたパターンで除外されるかチェック + */ +export function isIgnored(filePath: string, patterns: string[]): boolean { + return patterns.some((pattern) => matchesPattern(filePath, pattern)); +} + +/** + * .docsignoreファイルを読み込んでパターンを取得 + */ +export function loadDocsignorePatterns(docsignoreFile: string): string[] { + if (!fs.existsSync(docsignoreFile)) { + return []; + } + + const content = fs.readFileSync(docsignoreFile, 'utf-8'); + return content + .split('\n') + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith('#')); // コメントと空行を除外 +} diff --git a/src/utils/markdown-edit.test.ts b/src/utils/markdown-edit.test.ts new file mode 100644 index 0000000..3fa531c --- /dev/null +++ b/src/utils/markdown-edit.test.ts @@ -0,0 +1,344 @@ +import { + findCodeBlockPosition, + insertCodeBlockAfterComment, + moveCodeRefCommentBeforeCodeBlock, + replaceCodeBlock, + replaceCodeRefComment, +} from '@/utils/markdown-edit'; + +describe('markdown-edit', () => { + describe('replaceCodeRefComment', () => { + it('CODE_REFコメントを置換すること', () => { + const content = `# Title + + + +Some text`; + + const result = replaceCodeRefComment( + content, + '', + '' + ); + + expect(result).toContain(''); + expect(result).not.toContain(''); + }); + + it('CODE_REFコメントが見つからない場合、エラーをスローすること', () => { + const content = `# Title + +Some text`; + + expect(() => { + replaceCodeRefComment( + content, + '', + '' + ); + }).toThrow('CODE_REF comment not found'); + }); + + it('複数のCODE_REFコメントがある場合、最初のマッチを置換すること', () => { + const content = ` + +`; + + const result = replaceCodeRefComment( + content, + '', + '' + ); + + expect(result).toContain(''); + expect(result).toContain(''); + }); + }); + + describe('insertCodeBlockAfterComment', () => { + it('CODE_REFコメントの後にコードブロックを挿入すること', () => { + const content = `# Title + + + +Some text`; + + const result = insertCodeBlockAfterComment( + content, + '', + 'const x = 1;' + ); + + expect(result).toContain(''); + expect(result).toContain('```typescript'); + expect(result).toContain('const x = 1;'); + expect(result).toContain('```'); + }); + + it('言語指定をカスタマイズできること', () => { + const content = ``; + + const result = insertCodeBlockAfterComment( + content, + '', + 'const x = 1;', + 'javascript' + ); + + expect(result).toContain('```javascript'); + expect(result).toContain('const x = 1;'); + }); + + it('CODE_REFコメントが見つからない場合、エラーをスローすること', () => { + const content = `# Title`; + + expect(() => { + insertCodeBlockAfterComment( + content, + '', + 'const x = 1;' + ); + }).toThrow('CODE_REF comment not found'); + }); + + it('CODE_REFコメントの終了タグがない場合、エラーをスローすること', () => { + const content = ` +Some text`; + + const result = insertCodeBlockAfterComment( + content, + '', + 'const x = 1;' + ); + + expect(result).toMatch(/-->\n```typescript\nconst x = 1;\n```\n\nSome text/); + }); + }); + + describe('replaceCodeBlock', () => { + it('コードブロックを置換すること', () => { + const content = `# Title + +\`\`\`typescript +const x = 1; +\`\`\` + +Some text`; + + const result = replaceCodeBlock(content, 'const x = 1;', 'const y = 2;'); + + expect(result).toContain('const y = 2;'); + expect(result).not.toContain('const x = 1;'); + expect(result).toContain('```typescript'); + }); + + it('正規化して比較し、コードブロックを置換すること', () => { + const content = `\`\`\`typescript + const x = 1; +\`\`\``; + + const result = replaceCodeBlock(content, 'const x = 1;', 'const y = 2;'); + + expect(result).toContain('const y = 2;'); + }); + + it('一致するコードブロックが見つからない場合、エラーをスローすること', () => { + const content = `\`\`\`typescript +const x = 1; +\`\`\``; + + expect(() => { + replaceCodeBlock(content, 'const z = 3;', 'const y = 2;'); + }).toThrow('No matching code block found'); + }); + + it('言語識別子を保持すること', () => { + const content = `\`\`\`javascript +const x = 1; +\`\`\``; + + const result = replaceCodeBlock(content, 'const x = 1;', 'const y = 2;'); + + expect(result).toContain('```javascript'); + expect(result).toContain('const y = 2;'); + }); + + it('言語識別子がない場合も処理できること', () => { + const content = `\`\`\` +const x = 1; +\`\`\``; + + const result = replaceCodeBlock(content, 'const x = 1;', 'const y = 2;'); + + expect(result).toContain('```'); + expect(result).toContain('const y = 2;'); + }); + + it('複数のコードブロックがある場合、最初のマッチを置換すること', () => { + const content = `\`\`\`typescript +const x = 1; +\`\`\` + +\`\`\`typescript +const z = 3; +\`\`\``; + + const result = replaceCodeBlock(content, 'const x = 1;', 'const y = 2;'); + + expect(result).toContain('const y = 2;'); + expect(result).toContain('const z = 3;'); + expect(result).not.toContain('const x = 1;'); + }); + }); + + describe('findCodeBlockPosition', () => { + it('CODE_REFコメント後のコードブロック位置を検索すること', () => { + const content = `# Title + + + +\`\`\`typescript +const x = 1; +\`\`\``; + + const result = findCodeBlockPosition(content, ''); + + expect(result).not.toBeNull(); + expect(result?.language).toBe('typescript'); + }); + + it('CODE_REFコメントが見つからない場合、nullを返すこと', () => { + const content = `# Title`; + + const result = findCodeBlockPosition(content, ''); + + expect(result).toBeNull(); + }); + + it('CODE_REFコメントの終了タグがない場合、nullを返すこと', () => { + const content = ` + +Some text without code block`; + + const result = findCodeBlockPosition(content, ''); + + expect(result).toBeNull(); + }); + + it('言語識別子がないコードブロックも検索できること', () => { + const content = ` + +\`\`\` +const x = 1; +\`\`\``; + + const result = findCodeBlockPosition(content, ''); + + expect(result).not.toBeNull(); + expect(result?.language).toBe('typescript'); // デフォルト + }); + }); + + describe('moveCodeRefCommentBeforeCodeBlock', () => { + it('CODE_REFコメントをコードブロックの直前に移動すること', () => { + const content = `# Title + + + +Some text + +\`\`\`typescript +const x = 1; +\`\`\``; + + const codeBlockStart = content.indexOf('```typescript'); + const result = moveCodeRefCommentBeforeCodeBlock( + content, + '', + codeBlockStart + ); + + expect(result).toContain('\n```typescript'); + expect(result).not.toMatch(/\s+Some text/); + }); + + it('CODE_REFコメントが見つからない場合、エラーをスローすること', () => { + const content = `# Title + +\`\`\`typescript +const x = 1; +\`\`\``; + + expect(() => { + moveCodeRefCommentBeforeCodeBlock(content, '', 10); + }).toThrow('CODE_REF comment not found'); + }); + + it('CODE_REFコメントの終了タグがない場合、エラーをスローすること', () => { + const content = ` +\`\`\`typescript +const x = 1; +\`\`\``; + + const codeBlockStart = content.indexOf('```typescript'); + const result = moveCodeRefCommentBeforeCodeBlock( + content, + '', + codeBlockStart + ); + + expect(result).toContain('\n```typescript'); + }); + + it('コメントの前後の改行を適切に処理すること', () => { + const content = `Some text + + + + +More text + +\`\`\`typescript +const x = 1; +\`\`\``; + + const codeBlockStart = content.indexOf('```typescript'); + const result = moveCodeRefCommentBeforeCodeBlock( + content, + '', + codeBlockStart + ); + + expect(result).toContain('More text'); + expect(result).toContain('\n```typescript'); + }); + }); +}); diff --git a/src/utils/markdown-edit.ts b/src/utils/markdown-edit.ts new file mode 100644 index 0000000..955b991 --- /dev/null +++ b/src/utils/markdown-edit.ts @@ -0,0 +1,190 @@ +/** + * Markdown editing utility + */ + +import { normalizeCode } from '@/utils/markdown'; + +/** + * Replace CODE_REF comment + */ +export function replaceCodeRefComment( + content: string, + oldComment: string, + newComment: string +): string { + // Use exact match for safety + const index = content.indexOf(oldComment); + if (index === -1) { + throw new Error(`CODE_REF comment not found: ${oldComment}`); + } + + return content.substring(0, index) + newComment + content.substring(index + oldComment.length); +} + +/** + * Insert code block after CODE_REF comment + */ +export function insertCodeBlockAfterComment( + content: string, + commentMatch: string, + codeBlock: string, + language = 'typescript' +): string { + const commentIndex = content.indexOf(commentMatch); + if (commentIndex === -1) { + throw new Error(`CODE_REF comment not found: ${commentMatch}`); + } + + // Search for comment end (-->) + const commentEnd = content.indexOf('-->', commentIndex); + if (commentEnd === -1) { + throw new Error('CODE_REF comment end tag not found'); + } + + // Insert after comment, skip to next line + const insertPosition = commentEnd + 3; + + // Check if content already exists after comment + const afterComment = content.substring(insertPosition, insertPosition + 10); + const newlinePrefix = afterComment.startsWith('\n') ? '\n' : '\n\n'; + + const codeBlockText = `${newlinePrefix}\`\`\`${language}\n${codeBlock}\n\`\`\`\n`; + + return content.substring(0, insertPosition) + codeBlockText + content.substring(insertPosition); +} + +/** + * Replace code block in markdown + */ +export function replaceCodeBlock( + content: string, + oldCodeBlock: string, + newCodeBlock: string +): string { + // Search for code block in markdown format + const codeBlockPattern = /```[\w]*\n([\s\S]*?)```/g; + let match: RegExpExecArray | null; + let found = false; + + while ((match = codeBlockPattern.exec(content)) !== null) { + const blockContent = match[1]; + + // Normalize and compare + if (normalizeCode(blockContent) === normalizeCode(oldCodeBlock)) { + // Replace this block + const startIndex = match.index; + const endIndex = startIndex + match[0].length; + + // Extract language identifier + const langMatch = /```([\w]*)\n/.exec(match[0]); + const language = langMatch ? langMatch[1] : ''; + + const newBlock = `\`\`\`${language}\n${newCodeBlock}\n\`\`\``; + + content = content.substring(0, startIndex) + newBlock + content.substring(endIndex); + found = true; + break; + } + } + + if (!found) { + throw new Error('No matching code block found'); + } + + return content; +} + +/** + * Find code block position after CODE_REF comment + */ +export function findCodeBlockPosition( + content: string, + commentMatch: string +): { start: number; end: number; language: string } | null { + const commentIndex = content.indexOf(commentMatch); + if (commentIndex === -1) { + return null; + } + + const commentEnd = content.indexOf('-->', commentIndex); + if (commentEnd === -1) { + return null; + } + + // Search for code block within 500 characters after comment + const searchStart = commentEnd + 3; + const searchWindow = content.substring(searchStart, searchStart + 500); + + const codeBlockMatch = /```([\w]*)\n([\s\S]*?)```/.exec(searchWindow); + if (!codeBlockMatch) { + return null; + } + + return { + start: searchStart + codeBlockMatch.index, + end: searchStart + codeBlockMatch.index + codeBlockMatch[0].length, + language: codeBlockMatch[1] || 'typescript', + }; +} + +/** + * Move CODE_REF comment before code block + * + * @param content Markdown file content + * @param commentMatch CODE_REF comment text + * @param codeBlockStart Code block start position + * @returns Modified markdown content + */ +export function moveCodeRefCommentBeforeCodeBlock( + content: string, + commentMatch: string, + codeBlockStart: number +): string { + // 1. Identify CODE_REF comment position + const commentIndex = content.indexOf(commentMatch); + if (commentIndex === -1) { + throw new Error(`CODE_REF comment not found: ${commentMatch}`); + } + + const commentEnd = content.indexOf('-->', commentIndex); + if (commentEnd === -1) { + throw new Error('CODE_REF comment end tag not found'); + } + + // 2. Determine removal range (including surrounding newlines) + let removalStart = commentIndex; + let removalEnd = commentEnd + 3; + + // If newline exists before comment, remove it too + if (commentIndex > 0 && content[commentIndex - 1] === '\n') { + removalStart = commentIndex - 1; + } + + // Remove newlines after comment (max 2) + let newlinesAfter = 0; + while (removalEnd < content.length && content[removalEnd] === '\n' && newlinesAfter < 2) { + removalEnd++; + newlinesAfter++; + } + + // 3. Remove comment + const contentWithoutComment = content.substring(0, removalStart) + content.substring(removalEnd); + + // 4. Adjust position (shift due to removal) + const removedLength = removalEnd - removalStart; + const adjustedCodeBlockStart = + codeBlockStart > commentIndex ? codeBlockStart - removedLength : codeBlockStart; + + // 5. Insert before code block + const beforeCodeBlock = contentWithoutComment.substring(0, adjustedCodeBlockStart); + const afterCodeBlock = contentWithoutComment.substring(adjustedCodeBlockStart); + + // Add appropriate newline before comment + const needsNewlineBefore = beforeCodeBlock.length > 0 && !beforeCodeBlock.endsWith('\n\n'); + const prefix = needsNewlineBefore ? '\n' : ''; + + // One newline between comment and code block + const commentWithNewline = `${prefix}${commentMatch}\n`; + + return beforeCodeBlock + commentWithNewline + afterCodeBlock; +} diff --git a/src/utils/markdown.test.ts b/src/utils/markdown.test.ts new file mode 100644 index 0000000..f4d3d39 --- /dev/null +++ b/src/utils/markdown.test.ts @@ -0,0 +1,373 @@ +import { describe, expect, it } from '@jest/globals'; + +import { + associateCodeBlocksWithRefs, + extractCodeBlockAfterComment, + normalizeCode, +} from '@/utils/markdown'; +import type { CodeRef } from '@/utils/types'; + +describe('markdown.utils', () => { + describe('extractCodeBlockAfterComment', () => { + it('CODE_REFの直後にコードブロックがある場合、正しく抽出できること', () => { + const content = ` + +\`\`\`typescript +export function example() { + return 'test'; +} +\`\`\``; + + const commentIndex = content.indexOf(' + +これは説明文です。 + +\`\`\`typescript +const x = 1; +\`\`\``; + + const commentIndex = content.indexOf(' + +これは説明文だけです。`; + + const commentIndex = content.indexOf(' + +\`\`\` +const y = 2; +\`\`\``; + + const commentIndex = content.indexOf(' + +\`\`\`javascript +const z = 3; +\`\`\``; + + const commentIndex = content.indexOf(' + +\`\`\`typescript +\`\`\``; + + const commentIndex = content.indexOf('${filler}\`\`\`typescript +const x = 1; +\`\`\``; + + const commentIndex = content.indexOf(' + +\`\`\`typescript +const a = 1; +\`\`\` + + + +\`\`\`typescript +const b = 2; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + docLineNumber: 3, + }, + { + fullMatch: '', + refPath: 'src/b.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + docLineNumber: 7, + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(2); + expect(result[0].codeBlock).toBe('const a = 1;\n'); + expect(result[0].docLineNumber).toBe(3); + expect(result[0].codeBlockStartOffset).toBe( + content.indexOf('') + ); + expect(result[1].codeBlock).toBe('const b = 2;\n'); + expect(result[1].docLineNumber).toBe(7); + expect(result[1].codeBlockStartOffset).toBe( + content.indexOf('') + ); + }); + + it('コードブロックがない場合、codeBlockプロパティが追加されないこと', () => { + const content = ` + +これは説明文だけです。`; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0].codeBlock).toBeUndefined(); + expect(result[0].codeBlockStartOffset).toBeUndefined(); + }); + + it('fullMatchが見つからない場合、元のrefをそのまま返すこと', () => { + const content = ` + +\`\`\`typescript +const other = 1; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', // contentに存在しない + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual(refs[0]); // 変更されていない + }); + + it('CODE_REFとコードブロックの間に見出しがある場合、codeBlockプロパティが追加されないこと', () => { + const content = ` + +#### これは見出しです + +\`\`\`typescript +const a = 1; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0].codeBlock).toBeUndefined(); + expect(result[0].codeBlockStartOffset).toBeUndefined(); + }); + + it('CODE_REFとコードブロックの間に段落がある場合、codeBlockプロパティが追加されないこと', () => { + const content = ` + +これは説明文です。 + +\`\`\`typescript +const a = 1; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0].codeBlock).toBeUndefined(); + expect(result[0].codeBlockStartOffset).toBeUndefined(); + }); + + it('CODE_REFの直後(空行のみ)にコードブロックがある場合、codeBlockプロパティが追加されること', () => { + const content = ` + +\`\`\`typescript +const a = 1; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0].codeBlock).toBe('const a = 1;\n'); + }); + + it('CODE_REFの直後(空行なし)にコードブロックがある場合、codeBlockプロパティが追加されること', () => { + const content = ` +\`\`\`typescript +const a = 1; +\`\`\``; + + const refs: CodeRef[] = [ + { + fullMatch: '', + refPath: 'src/a.ts', + startLine: 1, + endLine: 5, + docFile: 'test.md', + }, + ]; + + const result = associateCodeBlocksWithRefs(content, refs); + + expect(result).toHaveLength(1); + expect(result[0].codeBlock).toBe('const a = 1;\n'); + }); + }); + + describe('normalizeCode', () => { + it('空白のみが異なるコードを正規化できること', () => { + const code1 = ' const x = 1; '; + const code2 = 'const x = 1;'; + + expect(normalizeCode(code1)).toBe(normalizeCode(code2)); + expect(normalizeCode(code1)).toBe('constx=1;'); + }); + + it('Windows改行とUnix改行を統一すること', () => { + const codeWindows = 'line1\r\nline2\r\nline3'; + const codeUnix = 'line1\nline2\nline3'; + + expect(normalizeCode(codeWindows)).toBe(normalizeCode(codeUnix)); + expect(normalizeCode(codeUnix)).toBe('line1line2line3'); + }); + + it('改行とインデントをすべて削除すること', () => { + const code = ` line1 + line2 + line3 `; + + const result = normalizeCode(code); + expect(result).toBe('line1line2line3'); + }); + + it('連続する空白をすべて削除すること', () => { + const code = 'const x = 1;'; + const expected = 'constx=1;'; + + expect(normalizeCode(code)).toBe(expected); + }); + + it('すべての空白を削除すること', () => { + const code = '\n\nconst x = 1;\n\n'; + const expected = 'constx=1;'; + + expect(normalizeCode(code)).toBe(expected); + }); + + it('空行もすべて削除すること', () => { + const code = `line1 + +line3`; + const result = normalizeCode(code); + + expect(result).toBe('line1line3'); + }); + + it('タブとスペースの違いを吸収すること', () => { + const codeWithTabs = '\tconst\tx\t=\t1;'; + const codeWithSpaces = ' const x = 1;'; + + expect(normalizeCode(codeWithTabs)).toBe(normalizeCode(codeWithSpaces)); + expect(normalizeCode(codeWithTabs)).toBe('constx=1;'); + }); + + it('空文字列の場合、空文字列を返すこと', () => { + expect(normalizeCode('')).toBe(''); + }); + + it('空行のみの場合、空文字列を返すこと', () => { + expect(normalizeCode('\n\n\n')).toBe(''); + }); + + it('括弧の内側の改行も削除すること', () => { + const code1 = 'func(\n arg1,\n arg2\n)'; + const code2 = 'func(arg1,arg2)'; + + expect(normalizeCode(code1)).toBe(normalizeCode(code2)); + expect(normalizeCode(code1)).toBe('func(arg1,arg2)'); + }); + }); +}); diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts new file mode 100644 index 0000000..5e9f936 --- /dev/null +++ b/src/utils/markdown.ts @@ -0,0 +1,102 @@ +/** + * マークダウンドキュメント処理ユーティリティ + */ + +import type { CodeRef } from '@/utils/types'; + +/** + * CODE_REFコメントの後にあるコードブロックを抽出する + * + * @param content マークダウンファイルの内容 + * @param commentIndex CODE_REFコメントの開始位置 + * @returns コードブロックの内容、見つからない場合はnull + * + * **厳格なルール**: CODE_REFコメントとコードブロックの間に、空行以外のテキスト + * (見出し、段落、リストなど)がある場合は、コードブロックを関連付けません。 + */ +export function extractCodeBlockAfterComment(content: string, commentIndex: number): string | null { + // コメント終了位置(-->)を見つける + const commentEndMarker = '-->'; + const commentEnd = content.indexOf(commentEndMarker, commentIndex); + + if (commentEnd === -1) { + return null; + } + + // コメント終了後から5000文字以内でコードブロックを検索 + const searchStart = commentEnd + commentEndMarker.length; + const searchWindow = content.substring(searchStart, searchStart + 5000); + + // コードブロックのパターン: ```language\ncode\n``` + // 言語識別子はオプション(typescript, javascript, など) + // 言語識別子の後に空白があってもマッチする + const codeBlockPattern = /```[\w]*\s*\n([\s\S]*?)```/; + const match = codeBlockPattern.exec(searchWindow); + + if (!match) { + return null; + } + + // コードブロックの開始位置を取得 + const codeBlockStart = match.index; + + // コメント終了後からコードブロック開始までの間のテキストを取得 + const textBetween = searchWindow.substring(0, codeBlockStart); + + // 空行以外のテキストがあるかチェック + // 空行、空白のみの行は許容する + const hasNonEmptyContent = textBetween.split('\n').some((line) => line.trim() !== ''); + + if (hasNonEmptyContent) { + // コメントとコードブロックの間に他のテキストがある場合はnullを返す + return null; + } + + return match[1]; +} + +/** + * CODE_REFの配列にコードブロックを関連付ける + * + * @param content マークダウンファイルの内容 + * @param refs CODE_REFの配列 + * @returns コードブロックが関連付けられたCODE_REFの配列 + */ +export function associateCodeBlocksWithRefs(content: string, refs: CodeRef[]): CodeRef[] { + return refs.map((ref) => { + // codeBlockStartOffsetが設定されている場合はそれを使用、なければfullMatchから検索 + const commentIndex = ref.codeBlockStartOffset ?? content.indexOf(ref.fullMatch); + + if (commentIndex === -1) { + // fullMatchが見つからない場合(通常は発生しないはず) + return ref; + } + + // コードブロックを抽出 + const codeBlock = extractCodeBlockAfterComment(content, commentIndex); + + if (codeBlock !== null) { + return { + ...ref, + codeBlock, + codeBlockStartOffset: commentIndex, + }; + } + + return ref; + }); +} + +/** + * コードを正規化する(空白の違いを吸収) + * + * すべての空白文字を削除することで、インデント、改行位置、 + * スペースの数などの違いを完全に無視します。 + * + * @param code 正規化するコード + * @returns 正規化されたコード(空白なし) + */ +export function normalizeCode(code: string): string { + // すべての空白文字(改行、タブ、スペースなど)を削除 + return code.replace(/\s+/g, ''); +} diff --git a/src/utils/prompt.test.ts b/src/utils/prompt.test.ts new file mode 100644 index 0000000..cbdc324 --- /dev/null +++ b/src/utils/prompt.test.ts @@ -0,0 +1,377 @@ +import * as fs from 'fs'; +import * as readline from 'readline'; + +import { jest } from '@jest/globals'; + +import { + askQuestion, + askSelectOption, + askYesNo, + createPromptInterface, + displayFixPreview, +} from '@/utils/prompt'; +import type { FixAction } from '@/utils/types'; + +// モジュールをモック +jest.mock('fs'); +jest.mock('readline'); + +const mockedFs = fs as jest.Mocked; +const mockedReadline = readline as jest.Mocked; + +describe('prompt', () => { + let mockRl: jest.Mocked; + + beforeEach(() => { + jest.clearAllMocks(); + + // readline.Interface のモックを作成 + mockRl = { + question: jest.fn(), + close: jest.fn(), + on: jest.fn(), + } as any; + }); + + describe('createPromptInterface', () => { + it('readline.Interfaceを作成すること', () => { + mockedReadline.createInterface.mockReturnValue(mockRl as any); + + const result = createPromptInterface(); + + expect(result).toBe(mockRl); + expect(mockedReadline.createInterface).toHaveBeenCalledWith({ + input: process.stdin, + output: process.stdout, + }); + }); + }); + + describe('askQuestion', () => { + it('質問して回答を取得すること', async () => { + const answer = 'test answer'; + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)(answer); + }); + + const result = await askQuestion(mockRl, 'What is your name?'); + + expect(result).toBe(answer); + expect(mockRl.question).toHaveBeenCalledWith('What is your name?', expect.any(Function)); + }); + + it('空の回答も正しく処理すること', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)(''); + }); + + const result = await askQuestion(mockRl, 'Optional question?'); + + expect(result).toBe(''); + }); + }); + + describe('askYesNo', () => { + it('yesの回答でtrueを返すこと', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('y'); + }); + + const result = await askYesNo(mockRl, 'Continue?'); + + expect(result).toBe(true); + }); + + it('Yesの回答でtrueを返すこと(大文字小文字を区別しない)', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('Yes'); + }); + + const result = await askYesNo(mockRl, 'Continue?'); + + expect(result).toBe(true); + }); + + it('noの回答でfalseを返すこと', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('n'); + }); + + const result = await askYesNo(mockRl, 'Continue?'); + + expect(result).toBe(false); + }); + + it('空の回答でデフォルト値を返すこと(デフォルトfalse)', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)(''); + }); + + const result = await askYesNo(mockRl, 'Continue?', false); + + expect(result).toBe(false); + }); + + it('空の回答でデフォルト値を返すこと(デフォルトtrue)', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)(''); + }); + + const result = await askYesNo(mockRl, 'Continue?', true); + + expect(result).toBe(true); + }); + + it('質問文にデフォルト値が含まれること(デフォルトfalse)', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('y'); + }); + + await askYesNo(mockRl, 'Continue?', false); + + expect(mockRl.question).toHaveBeenCalledWith('Continue? (y/N): ', expect.any(Function)); + }); + + it('質問文にデフォルト値が含まれること(デフォルトtrue)', async () => { + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('n'); + }); + + await askYesNo(mockRl, 'Continue?', true); + + expect(mockRl.question).toHaveBeenCalledWith('Continue? (Y/n): ', expect.any(Function)); + }); + }); + + describe('askSelectOption', () => { + it('有効なオプションを選択できること', async () => { + const options = ['Option 1', 'Option 2', 'Option 3']; + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + + mockRl.question.mockImplementation((_q, callback) => { + (callback as any)('2'); + }); + + const result = await askSelectOption(mockRl, options, 'Choose an option:'); + + expect(result).toBe(1); // 0-based index + expect(consoleLogSpy).toHaveBeenCalledWith('\nChoose an option:'); + expect(consoleLogSpy).toHaveBeenCalledWith(' 1) Option 1'); + expect(consoleLogSpy).toHaveBeenCalledWith(' 2) Option 2'); + expect(consoleLogSpy).toHaveBeenCalledWith(' 3) Option 3'); + + consoleLogSpy.mockRestore(); + }); + + it('無効な選択の後、有効な選択ができること', async () => { + const options = ['Option 1', 'Option 2']; + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + + let callCount = 0; + mockRl.question.mockImplementation((_q, callback) => { + callCount++; + if (callCount === 1) { + (callback as any)('5'); // 無効な選択 + } else { + (callback as any)('1'); // 有効な選択 + } + }); + + const result = await askSelectOption(mockRl, options, 'Choose:'); + + expect(result).toBe(0); + expect(consoleLogSpy).toHaveBeenCalledWith('❌ Invalid selection. Please try again.'); + + consoleLogSpy.mockRestore(); + }); + + it('0を入力した場合、再入力を求めること', async () => { + const options = ['Option 1', 'Option 2']; + const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + + let callCount = 0; + mockRl.question.mockImplementation((_q, callback) => { + callCount++; + if (callCount === 1) { + (callback as any)('0'); // 無効 + } else { + (callback as any)('1'); // 有効 + } + }); + + const result = await askSelectOption(mockRl, options, 'Choose:'); + + expect(result).toBe(0); + expect(consoleLogSpy).toHaveBeenCalledWith('❌ Invalid selection. Please try again.'); + + consoleLogSpy.mockRestore(); + }); + }); + + describe('displayFixPreview', () => { + let consoleLogSpy: any; + const testProjectRoot = '/tmp/test-project'; + + beforeEach(() => { + consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleLogSpy.mockRestore(); + }); + + it('UPDATE_LINE_NUMBERSタイプの修正プレビューを表示すること', () => { + const action: FixAction = { + type: 'UPDATE_LINE_NUMBERS', + error: { + type: 'CODE_LOCATION_MISMATCH', + message: 'Code location mismatch', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + }, + description: 'Update line numbers', + preview: 'Preview text', + newStartLine: 15, + newEndLine: 25, + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Update line numbers'); + }); + + it('INSERT_CODE_BLOCKタイプの修正プレビューを表示すること', () => { + const action: FixAction = { + type: 'INSERT_CODE_BLOCK', + error: { + type: 'INSERT_CODE_BLOCK', + message: 'Insert code block', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + }, + description: 'Insert code block', + preview: 'Preview text', + newCodeBlock: 'const x = 1;', + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Insert code block'); + expect(consoleLogSpy).toHaveBeenCalledWith('\x1b[32m+ Insert code block:\x1b[0m'); + }); + + it('CODE_CONTENT_MISMATCHタイプの修正プレビューを表示すること', () => { + const action: FixAction = { + type: 'REPLACE_CODE_BLOCK', + error: { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + expectedCode: 'const x = 1;', + actualCode: 'const y = 2;', + }, + description: 'Replace code block', + preview: 'Preview text', + newCodeBlock: 'const y = 2;', + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Replace code block'); + }); + + it('CODE_LOCATION_MISMATCHタイプの修正プレビューを表示すること(コードブロックなし)', () => { + const action: FixAction = { + type: 'UPDATE_LINE_NUMBERS', + error: { + type: 'CODE_LOCATION_MISMATCH', + message: 'Code location mismatch', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + }, + description: 'Update line numbers', + preview: 'Simple preview text', + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Update line numbers'); + expect(consoleLogSpy).toHaveBeenCalledWith('Simple preview text'); + }); + + it('REPLACE_CODE_BLOCKタイプの修正プレビューを表示すること', () => { + const action: FixAction = { + type: 'REPLACE_CODE_BLOCK', + error: { + type: 'REPLACE_CODE_BLOCK', + message: 'Replace code block', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + expectedCode: 'old code', + actualCode: 'new code', + }, + description: 'Replace code block', + preview: 'Preview text', + newCodeBlock: 'new code', + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Replace code block'); + }); + + it('UPDATE_END_LINEタイプの修正プレビューを表示すること', () => { + mockedFs.existsSync.mockReturnValue(true); + + const action: FixAction = { + type: 'UPDATE_END_LINE', + error: { + type: 'UPDATE_END_LINE', + message: 'Update end line', + ref: { + fullMatch: '', + refPath: 'src/test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + }, + description: 'Update end line number', + preview: 'Preview text', + newStartLine: 10, + newEndLine: 25, + newCodeBlock: 'const x = 1;\nconst y = 2;\nconst z = 3;', + }; + + displayFixPreview(action, testProjectRoot); + + expect(consoleLogSpy).toHaveBeenCalledWith('\nChanges: Update end line number'); + }); + }); +}); diff --git a/src/utils/prompt.ts b/src/utils/prompt.ts new file mode 100644 index 0000000..c959afd --- /dev/null +++ b/src/utils/prompt.ts @@ -0,0 +1,189 @@ +/** + * Interactive CLI prompt utility + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as readline from 'readline'; + +import { extractLinesFromFile } from '@/utils/code-comparison'; +import { displayCodeDiff, displayLineRangeDiff } from '@/utils/diff-display'; +import type { FixAction } from '@/utils/types'; + +/** + * Create readline interface + */ +export function createPromptInterface(): readline.Interface { + return readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); +} + +/** + * Yes/No question + */ +export async function askYesNo( + rl: readline.Interface, + question: string, + defaultValue = false +): Promise { + const defaultText = defaultValue ? 'Y/n' : 'y/N'; + const answer = await askQuestion(rl, `${question} (${defaultText}): `); + + if (!answer.trim()) { + return defaultValue; + } + + return answer.toLowerCase().startsWith('y'); +} + +/** + * Ask question and get answer + */ +export function askQuestion(rl: readline.Interface, question: string): Promise { + return new Promise((resolve) => { + rl.question(question, (answer) => { + resolve(answer); + }); + }); +} + +/** + * Select from multiple options + * @returns Index of selected option (0-based) + */ +export async function askSelectOption( + rl: readline.Interface, + options: string[], + message: string +): Promise { + console.log(`\n${message}`); + options.forEach((opt, idx) => { + console.log(` ${idx + 1}) ${opt}`); + }); + + while (true) { + const answer = await askQuestion(rl, `Select (1-${options.length}): `); + + const selection = parseInt(answer.trim(), 10); + if (selection >= 1 && selection <= options.length) { + return selection - 1; + } + + console.log('❌ Invalid selection. Please try again.'); + } +} + +/** + * Display fix preview + */ +export function displayFixPreview(action: FixAction, projectRoot: string): void { + console.log(`\nChanges: ${action.description}`); + + // Display colored diff based on error type + const { error } = action; + const absolutePath = path.resolve(projectRoot, error.ref.refPath); + + switch (error.type) { + case 'CODE_LOCATION_MISMATCH': { + // Display line number diff + if (error.ref.codeBlock && action.newStartLine && action.newEndLine) { + // If code block exists, get actual code + const actualCode = extractLinesFromFile( + absolutePath, + action.newStartLine, + action.newEndLine + ); + + const diff = displayLineRangeDiff( + actualCode, + { + start: error.ref.startLine!, + end: error.ref.endLine!, + }, + { + start: action.newStartLine, + end: action.newEndLine, + } + ); + console.log(diff); + } else { + // Simple preview if no code block + console.log(action.preview); + } + break; + } + + case 'CODE_CONTENT_MISMATCH': { + // Display code content diff + if (error.expectedCode && action.newCodeBlock) { + const diff = displayCodeDiff(error.expectedCode, action.newCodeBlock); + console.log(diff); + } else { + console.log(action.preview); + } + break; + } + + case 'INSERT_CODE_BLOCK': { + // For new insertion, simply display the code to be inserted + if (action.newCodeBlock) { + console.log('\x1b[32m+ Insert code block:\x1b[0m'); + console.log('\x1b[2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m'); + const lines = action.newCodeBlock.split('\n'); + lines.forEach((line) => { + console.log(`\x1b[32m+ ${line}\x1b[0m`); + }); + console.log('\x1b[2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m'); + } else { + console.log(action.preview); + } + break; + } + + case 'REPLACE_CODE_BLOCK': { + // For code block replacement, same processing as CODE_CONTENT_MISMATCH + if (error.expectedCode && action.newCodeBlock) { + const diff = displayCodeDiff(error.expectedCode, action.newCodeBlock); + console.log(diff); + } else { + console.log(action.preview); + } + break; + } + + case 'UPDATE_LINE_NUMBERS': + case 'UPDATE_END_LINE': { + // For line number update, display comment change + const oldComment = error.ref.fullMatch; + const newComment = ``; + + console.log('\x1b[2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m'); + console.log(`\x1b[31m- ${oldComment}\x1b[0m`); + console.log(`\x1b[32m+ ${newComment}\x1b[0m`); + console.log('\x1b[2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m'); + + // If code block also exists, display content + if (action.newCodeBlock && fs.existsSync(absolutePath)) { + console.log('\nCode block content:'); + const lines = action.newCodeBlock.split('\n').slice(0, 10); // First 10 lines + lines.forEach((line) => { + console.log(` ${line}`); + }); + if (action.newCodeBlock.split('\n').length > 10) { + console.log(' ...'); + } + } + break; + } + + default: { + // For other cases, display default preview + if (action.preview) { + console.log(action.preview); + } + break; + } + } +} diff --git a/src/utils/styles.test.ts b/src/utils/styles.test.ts new file mode 100644 index 0000000..bd6ca7d --- /dev/null +++ b/src/utils/styles.test.ts @@ -0,0 +1,463 @@ +/** + * Tests for styling utilities + */ + +import { formatFixOptions } from './styles'; +import type { FixAction, CodeRefError } from './types'; + +describe('styles', () => { + // Save original environment variable + let originalNoColor: string | undefined; + + beforeEach(() => { + originalNoColor = process.env.NO_COLOR; + }); + + afterEach(() => { + // Restore original environment variable + if (originalNoColor === undefined) { + delete process.env.NO_COLOR; + } else { + process.env.NO_COLOR = originalNoColor; + } + }); + + describe('formatFixOptions', () => { + it('should format multiple options correctly', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 20, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace code block with entire symbol', + preview: '```typescript\nfunction foo() {\n return 42;\n}\n```', + }, + { + type: 'UPDATE_LINE_NUMBERS', + error: mockError, + description: 'Update line numbers only', + preview: 'Lines: 10-25', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain option headers + expect(result).toContain('Option 1'); + expect(result).toContain('Option 2'); + + // Should contain descriptions + expect(result).toContain('Replace code block with entire symbol'); + expect(result).toContain('Update line numbers only'); + + // Should contain box drawing characters + expect(result).toContain('┌'); + expect(result).toContain('└'); + expect(result).toContain('│'); + }); + + it('should display short preview as is', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace with short code', + preview: '```typescript\nfunction foo() {\n return 42;\n}\n```', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain all lines of the preview + expect(result).toContain('function foo()'); + expect(result).toContain('return 42'); + + // Should NOT contain truncation message + expect(result).not.toContain('more lines'); + }); + + it('should truncate long preview (>20 lines)', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 1, + endLine: 30, + docFile: 'test.md', + }, + }; + + // Create a preview with 25 lines + const longPreview = Array.from({ length: 25 }, (_, i) => `line ${i + 1}`).join('\n'); + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace with long code', + preview: longPreview, + }, + ]; + + const result = formatFixOptions(options); + + // Should contain first few lines + expect(result).toContain('line 1'); + expect(result).toContain('line 15'); + + // Should contain truncation message + expect(result).toContain('10 more lines'); + + // Should NOT contain all lines + expect(result).not.toContain('line 25'); + }); + + it('should handle special characters properly', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace with special chars', + preview: '```typescript\nconst str = "Hello ";\nconst regex = /\\d+/;\n```', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain special characters + expect(result).toContain('Hello '); + expect(result).toContain('/\\d+/'); + }); + + it('should extract and display line information for range', () => { + const mockError: CodeRefError = { + type: 'UPDATE_LINE_NUMBERS', + message: 'Line numbers need update', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 25, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'UPDATE_LINE_NUMBERS', + error: mockError, + description: 'Update line numbers', + preview: 'Lines: 10-25\nSome code here', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain formatted line information + expect(result).toContain('Lines: 10-25 (16 lines)'); + }); + + it('should extract and display line information for single line', () => { + const mockError: CodeRefError = { + type: 'UPDATE_LINE_NUMBERS', + message: 'Line numbers need update', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 10, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'UPDATE_LINE_NUMBERS', + error: mockError, + description: 'Update line number', + preview: 'Line: 10\nSome code here', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain formatted line information for single line + expect(result).toContain('Line: 10'); + }); + + it('should handle empty preview', () => { + const mockError: CodeRefError = { + type: 'UPDATE_LINE_NUMBERS', + message: 'Line numbers need update', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'UPDATE_LINE_NUMBERS', + error: mockError, + description: 'Update line numbers', + preview: '', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain option description + expect(result).toContain('Update line numbers'); + // Should contain box drawing + expect(result).toContain('┌'); + // Should NOT contain Preview section + expect(result).not.toContain('Preview:'); + }); + + it('should handle preview without line information', () => { + const mockError: CodeRefError = { + type: 'REPLACE_CODE_BLOCK', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace code block', + preview: '```typescript\nfunction test() {}\n```', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain option description + expect(result).toContain('Replace code block'); + // Should contain preview + expect(result).toContain('Preview:'); + expect(result).toContain('function test()'); + // Should NOT contain line info (no "Lines:" or "Line:" in preview) + expect(result).not.toMatch(/Lines?: \d+/); + }); + }); + + describe('color disabled environment', () => { + it('should not output color codes when NO_COLOR=1', () => { + // Set NO_COLOR environment variable + process.env.NO_COLOR = '1'; + + // Force re-import to pick up environment variable + // Note: This is tricky in Node.js, so we'll just check that chalk respects NO_COLOR + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Test option', + preview: 'function foo() {}', + }, + ]; + + const result = formatFixOptions(options); + + // chalk should automatically detect NO_COLOR and disable colors + // We just verify the function doesn't crash and returns text + expect(result).toContain('Option 1'); + expect(result).toContain('Test option'); + }); + }); + + describe('syntax highlighting', () => { + it('should highlight code block markers as dim', () => { + const mockError: CodeRefError = { + type: 'REPLACE_CODE_BLOCK', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace code', + preview: '```typescript\ncode here\n```', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain code block markers + expect(result).toContain('```typescript'); + expect(result).toContain('```'); + }); + + it('should highlight keywords in code', () => { + const mockError: CodeRefError = { + type: 'REPLACE_CODE_BLOCK', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Replace code', + preview: + 'function test() {\n const x = 42;\n return x;\n}\nexport class Foo {}\ninterface Bar {}', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain code with keywords + expect(result).toContain('function'); + expect(result).toContain('const'); + expect(result).toContain('return'); + expect(result).toContain('export'); + expect(result).toContain('class'); + expect(result).toContain('interface'); + }); + }); + + describe('box drawing', () => { + it('should contain box drawing characters', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'Test option', + preview: 'Some code', + }, + ]; + + const result = formatFixOptions(options); + + // Should contain box drawing characters + expect(result).toContain('┌'); + expect(result).toContain('─'); + expect(result).toContain('└'); + expect(result).toContain('│'); + }); + + it('should visually separate each option', () => { + const mockError: CodeRefError = { + type: 'CODE_CONTENT_MISMATCH', + message: 'Code content mismatch', + ref: { + fullMatch: '', + refPath: 'test.ts', + startLine: 10, + endLine: 15, + docFile: 'test.md', + }, + }; + + const options: FixAction[] = [ + { + type: 'REPLACE_CODE_BLOCK', + error: mockError, + description: 'First option', + preview: 'Code 1', + }, + { + type: 'UPDATE_LINE_NUMBERS', + error: mockError, + description: 'Second option', + preview: 'Code 2', + }, + ]; + + const result = formatFixOptions(options); + + // Each option should have its own box + const topLeftCount = (result.match(/┌/g) || []).length; + const bottomLeftCount = (result.match(/└/g) || []).length; + + expect(topLeftCount).toBe(2); // Two options = two top-left corners + expect(bottomLeftCount).toBe(2); // Two options = two bottom-left corners + }); + }); +}); diff --git a/src/utils/styles.ts b/src/utils/styles.ts new file mode 100644 index 0000000..faad7f4 --- /dev/null +++ b/src/utils/styles.ts @@ -0,0 +1,185 @@ +/** + * Styling utilities for CLI output using chalk + */ + +import chalk from 'chalk'; +import type { FixAction } from './types'; + +/** + * Color scheme (consistent with diff-display.ts and new additions) + */ +const colors = { + // Existing colors from diff-display.ts + success: chalk.green, + error: chalk.red, + dim: chalk.dim, + + // New colors for fix options + primary: chalk.cyan.bold, // Option numbers + muted: chalk.gray, // Supplementary info + code: chalk.yellow, // Code blocks +}; + +/** + * Box drawing characters + */ +const box = { + topLeft: '┌', + topRight: '┐', + bottomLeft: '└', + bottomRight: '┘', + horizontal: '─', + vertical: '│', +}; + +/** + * Maximum number of lines to show in preview + */ +const MAX_PREVIEW_LINES = 20; + +/** + * Number of lines to show when truncating + */ +const TRUNCATE_SHOW_LINES = 15; + +/** + * Format fix options for display + * @param options Array of fix actions to display + * @returns Formatted string with styled options + */ +export function formatFixOptions(options: FixAction[]): string { + const output: string[] = []; + + options.forEach((option, index) => { + const optionNumber = index + 1; + + // Option header with box drawing + const headerText = ` Option ${optionNumber} `; + const headerLine = box.horizontal.repeat(60 - headerText.length); + output.push(colors.dim(` ${box.topLeft}${box.horizontal}${headerText}${headerLine}`)); + + // Option description + output.push(colors.dim(` ${box.vertical} `) + colors.primary(option.description)); + + // Extract line information from preview if available + const lineInfo = extractLineInfo(option.preview); + if (lineInfo) { + output.push(colors.dim(` ${box.vertical} `) + colors.muted(lineInfo)); + } + + // Preview section + if (option.preview) { + output.push(colors.dim(` ${box.vertical}`)); + output.push(colors.dim(` ${box.vertical} `) + colors.muted('Preview:')); + + const previewLines = formatPreview(option.preview); + previewLines.forEach((line) => { + output.push(colors.dim(` ${box.vertical} `) + line); + }); + } + + // Bottom border + output.push(colors.dim(` ${box.bottomLeft}${box.horizontal.repeat(60)}`)); + + // Add spacing between options (except after the last one) + if (index < options.length - 1) { + output.push(''); + } + }); + + return output.join('\n'); +} + +/** + * Extract line information from preview text + * @param preview Preview text + * @returns Line information string or null + */ +function extractLineInfo(preview: string): string | null { + // Look for patterns like "Lines: 10-25" or "Line: 10" + const regex = /Lines?: (\d+)-?(\d+)?/i; + const linesMatch = regex.exec(preview); + if (linesMatch) { + const start = linesMatch[1]; + const end = linesMatch[2]; + if (end) { + const lineCount = parseInt(end) - parseInt(start) + 1; + return `Lines: ${start}-${end} (${lineCount} lines)`; + } else { + return `Line: ${start}`; + } + } + return null; +} + +/** + * Format preview text with truncation and syntax highlighting + * @param preview Preview text + * @returns Array of formatted lines + */ +function formatPreview(preview: string): string[] { + const lines = preview.split('\n'); + const output: string[] = []; + + // Truncate if too long + if (lines.length > MAX_PREVIEW_LINES) { + const truncatedLines = lines.slice(0, TRUNCATE_SHOW_LINES); + const remainingCount = lines.length - TRUNCATE_SHOW_LINES; + + truncatedLines.forEach((line) => { + output.push(formatCodeLine(line)); + }); + + output.push(colors.muted(`... (${remainingCount} more lines)`)); + } else { + lines.forEach((line) => { + output.push(formatCodeLine(line)); + }); + } + + return output; +} + +/** + * Format a single code line with basic syntax highlighting + * @param line Code line + * @returns Formatted line + */ +function formatCodeLine(line: string): string { + // Apply basic syntax highlighting + // Detect code block markers + if (line.trim().startsWith('```')) { + return colors.dim(line); + } + + // Detect keywords (basic highlighting) + const keywords = [ + 'function', + 'const', + 'let', + 'var', + 'class', + 'interface', + 'type', + 'export', + 'import', + 'return', + 'if', + 'else', + 'for', + 'while', + ]; + + let formattedLine = line; + keywords.forEach((keyword) => { + const regex = new RegExp(`\\b(${keyword})\\b`, 'g'); + formattedLine = formattedLine.replace(regex, chalk.cyan('$1')); + }); + + // Highlight strings (simple patterns for single and double quotes) + formattedLine = formattedLine.replace(/'[^']*'/g, (match) => colors.code(match)); + formattedLine = formattedLine.replace(/"[^"]*"/g, (match) => colors.code(match)); + formattedLine = formattedLine.replace(/`[^`]*`/g, (match) => colors.code(match)); + + return formattedLine; +} diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..bf5ea21 --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,143 @@ +/** + * スクリプト共通の型定義 + */ + +/** + * コード参照情報 + */ +export interface CodeRef { + fullMatch: string; + refPath: string; + startLine: number | null; + endLine: number | null; + docFile: string; + docLineNumber?: number; // ドキュメント内のCODE_REFコメントの行番号 + codeBlock?: string; // ドキュメント内のコードブロック内容 + codeBlockStartOffset?: number; // コードブロックの開始位置 + // シンボル指定機能の追加フィールド + symbolPath?: string; // "functionName" or "ClassName#methodName" + className?: string; // "ClassName" (if specified) + memberName?: string; // "methodName" or "functionName" +} + +/** + * コード参照エラー + */ +export interface CodeRefError { + type: + | 'FILE_NOT_FOUND' + | 'LINE_OUT_OF_RANGE' + | 'INVALID_LINE_NUMBER' + | 'INVALID_RANGE' + | 'READ_ERROR' + | 'PATH_TRAVERSAL' + | 'CODE_CONTENT_MISMATCH' // 指定行のコードが異なる + | 'CODE_LOCATION_MISMATCH' // コードは一致するが行数が異なる + | 'CODE_BLOCK_MISSING' // CODE_REFの後にコードブロックがない + | 'INSERT_CODE_BLOCK' // CODE_REFの後にコードブロックを挿入する必要がある + | 'REPLACE_CODE_BLOCK' // CODE_REFの後のコードブロックを置換する必要がある + | 'UPDATE_LINE_NUMBERS' // CODE_REFの行番号を更新する必要がある + | 'UPDATE_END_LINE' // CODE_REFの終了行番号を更新する必要がある + | 'SYMBOL_NOT_FOUND' // 指定されたシンボルが見つからない + | 'MULTIPLE_SYMBOLS_FOUND' // 同名のシンボルが複数存在 + | 'SYMBOL_RANGE_MISMATCH' // シンボルの範囲と指定行番号が一致しない + | 'NOT_TYPESCRIPT_FILE'; // TypeScript/JavaScriptファイル以外 + message: string; + ref: CodeRef; + suggestedLines?: { start: number; end: number }; // CODE_LOCATION_MISMATCH用 + actualCode?: string; // CODE_CONTENT_MISMATCH用 + expectedCode?: string; // CODE_CONTENT_MISMATCH用 + // シンボル指定機能の追加フィールド + foundSymbols?: SymbolMatch[]; // MULTIPLE_SYMBOLS_FOUND用 + suggestedSymbol?: SymbolMatch; // SYMBOL_RANGE_MISMATCH用 +} + +/** + * Git実行オプション + */ +export interface GitExecOptions { + cwd?: string; + encoding?: BufferEncoding; +} + +/** + * パターンマッチング結果 + */ +export interface PatternMatchResult { + matched: boolean; + pattern?: string; +} + +/** + * 修正操作タイプ + */ +export type FixType = + | 'UPDATE_LINE_NUMBERS' // CODE_LOCATION_MISMATCH + | 'INSERT_CODE_BLOCK' // CODE_BLOCK_MISSING + | 'REPLACE_CODE_BLOCK' // CODE_CONTENT_MISMATCH + | 'UPDATE_END_LINE' // LINE_OUT_OF_RANGE + | 'UPDATE_SYMBOL_RANGE' // SYMBOL_RANGE_MISMATCH + | 'MOVE_CODE_REF_COMMENT'; // CODE_BLOCK_MISSING (with existing block) + +/** + * 修正アクション詳細 + */ +export interface FixAction { + type: FixType; + error: CodeRefError; + description: string; + preview: string; + newStartLine?: number; + newEndLine?: number; + newCodeBlock?: string; + matchOptions?: { start: number; end: number }[]; + codeBlockPosition?: { + start: number; + end: number; + language: string; + content: string; + }; +} + +/** + * 修正結果 + */ +export interface FixResult { + success: boolean; + action: FixAction; + error?: string; + backupPath?: string; +} + +/** + * CLIオプション + */ +export interface FixOptions { + dryRun: boolean; + auto: boolean; + noBackup: boolean; + verbose: boolean; +} + +/** + * スコープ拡張されたマッチ情報 + */ +export interface ExpandedMatch { + start: number; + end: number; + confidence: 'high' | 'medium' | 'low'; + expansionType?: 'ast' | 'heuristic' | 'none'; + scopeType?: 'function' | 'class' | 'interface' | 'type' | 'const' | 'unknown'; +} + +/** + * シンボル検索のマッチ情報 + */ +export interface SymbolMatch { + className?: string; // クラス名(クラスメソッドの場合) + memberName: string; // メソッド名または関数名 + startLine: number; // 開始行番号(JSDocコメント含む) + endLine: number; // 終了行番号 + scopeType: 'function' | 'class' | 'method' | 'const' | 'let' | 'var'; // スコープタイプ + confidence: 'high' | 'medium' | 'low'; // 信頼度 +} diff --git a/tsconfig.json b/tsconfig.json index 1fc5d06..16c06f8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,10 @@ "checkJs": false, "outDir": "./dist", "rootDir": "./src", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + }, "removeComments": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, @@ -22,5 +26,5 @@ "sourceMap": true }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "test"] + "exclude": ["node_modules", "dist"] } diff --git a/tsconfig.scripts.json b/tsconfig.scripts.json new file mode 100644 index 0000000..d694daa --- /dev/null +++ b/tsconfig.scripts.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./scripts", + "outDir": "./dist/scripts", + "noEmit": true + }, + "include": ["scripts/**/*"], + "exclude": ["node_modules", "dist"] +}