diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 476c2c1..b91ecc5 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,90 +7,90 @@ on: jobs: test: runs-on: ubuntu-latest - + strategy: matrix: node-version: [20.19.0, 21.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: Build project - run: npm run build - - - name: Run tests - run: npm test - - - name: Run tests with coverage - run: npm run test:coverage - - - name: Publish Test Report - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './ctrf/*.json' - summary-report: true - github-report: true - failed-report: true - flaky-report: true - insights-report: true - fail-rate-report: true - flaky-rate-report: true - slowest-report: true - previous-results-report: true - upload-artifact: true - artifact-name: ctrf-test-report-${{ matrix.node-version }} - - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - if: always() + - 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: Build project + run: npm run build + + - name: Run tests + run: npm test + + - name: Run tests with coverage + run: npm run test:coverage + + - name: Publish Test Report + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: "./ctrf/*.json" + summary-report: true + github-report: true + failed-report: true + flaky-report: true + insights-report: true + fail-rate-report: true + flaky-rate-report: true + slowest-report: true + previous-results-report: true + upload-artifact: true + artifact-name: ctrf-test-report-${{ matrix.node-version }} + + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: always() lint: runs-on: ubuntu-latest - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20.19.0' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Type check - run: npx tsc --noEmit + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.19.0" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Type check + run: npx tsc --noEmit security: runs-on: ubuntu-latest - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20.19.0' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Run security audit - run: npm audit --audit-level=moderate - - - name: Check for known vulnerabilities - run: npx audit-ci --moderate + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.19.0" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run security audit + run: npm audit --audit-level=moderate + + - name: Check for known vulnerabilities + run: npx audit-ci --moderate diff --git a/.gitignore b/.gitignore index c4374df..61b0646 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ final scripts coverage dist -docs -node_modules \ No newline at end of file +node_modules +ctrf \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7003896 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +dist/ +node_modules/ +coverage/ +templates/ +reports/ +community-reports/ +.github/ +.exlintrc.js +README.md +docs/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e505faf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false, + "printWidth": 80, + "bracketSpacing": true, + "arrowParens": "avoid" +} diff --git a/README.md b/README.md index fb1a204..ac37028 100644 --- a/README.md +++ b/README.md @@ -27,188 +27,28 @@ Explore more integrations ## Installation ```sh -npm install ctrf@0.0.13-next.0 +npm install ctrf ``` +## API reference + +See [API reference](./docs) for more details. + ## TypeScript Types The library exports comprehensive TypeScript types for working with CTRF reports: ```typescript -import type { Report, Test, Insights } from 'ctrf'; +import type { Report, Test, Insights } from "ctrf"; function analyzeReport(report: Report): void { const flakyTests = report.results.tests.filter((test: Test) => test.flaky); const insights = report.insights as Insights; - + console.log(`Flaky rate: ${insights?.flakyRate.current}`); } ``` -## API Reference - -### Reading Reports - -#### `readSingleReport(filePath: string): Report` - -Reads and parses a single CTRF report file from a specified file path. - -**Parameters:** -- `filePath` - Path to the JSON file containing the CTRF report - -**Returns:** The parsed `Report` object - -**Throws:** Error if the file does not exist, is not valid JSON, or does not conform to the CTRF report structure - -**Example:** -```typescript -import { readSingleReport } from 'ctrf'; - -const report = readSingleReport('./test-results.json'); -console.log(`Found ${report.results.summary.tests} tests`); -``` - -#### `readReportsFromDirectory(directoryPath: string): Report[]` - -Reads all CTRF report files from a given directory. - -**Parameters:** -- `directoryPath` - Path to the directory containing JSON files - -**Returns:** Array of parsed `Report` objects - -**Throws:** Error if the directory does not exist or no valid CTRF reports are found - -**Example:** -```typescript -import { readReportsFromDirectory } from 'ctrf'; - -const reports = readReportsFromDirectory('./test-reports/'); -console.log(`Loaded ${reports.length} reports`); -``` - -#### `readReportsFromGlobPattern(pattern: string): Report[]` - -Reads all CTRF report files matching a glob pattern. - -**Parameters:** -- `pattern` - The glob pattern to match files (e.g., `ctrf/*.json`) - -**Returns:** Array of parsed `Report` objects - -**Throws:** Error if no valid CTRF reports are found - -**Example:** -```typescript -import { readReportsFromGlobPattern } from 'ctrf'; - -const reports = readReportsFromGlobPattern('reports/**/*.json'); -console.log(`Found ${reports.length} reports matching pattern`); -``` - -### Merging Reports - -#### `mergeReports(reports: Report[]): Report` - -Merges multiple CTRF reports into a single report. Combines test results, summaries, and metadata from all input reports. - -**Parameters:** -- `reports` - Array of CTRF report objects to be merged - -**Returns:** The merged CTRF report object - -**Throws:** Error if no reports are provided for merging - -**Example:** -```typescript -import { mergeReports, readReportsFromDirectory } from 'ctrf'; - -const reports = readReportsFromDirectory('./test-reports/'); -const mergedReport = mergeReports(reports); -console.log(`Merged ${reports.length} reports into one`); -``` - -### Report Enrichment & Insights - -#### `enrichReportWithInsights(currentReport: Report, previousReports?: Report[], baseline?: number | string): Report` - -Enriches a CTRF report with comprehensive insights by analyzing current and historical test data. Calculates metrics like flaky rates, failure rates, and performance trends. - -**Parameters:** -- `currentReport` - The current CTRF report to enrich -- `previousReports` - Array of historical CTRF reports (ordered newest to oldest, optional) -- `baseline` - Optional baseline specification: - - `undefined`: Use most recent previous report (default) - - `number`: Use report at this index in previousReports array (0 = most recent) - - `string`: Use report with specific timestamp ID - -**Returns:** The current report enriched with insights - -**Example:** -```typescript -import { enrichReportWithInsights, readSingleReport } from 'ctrf'; - -const currentReport = readSingleReport('./current-report.json'); -const previousReports = readReportsFromDirectory('./historical-reports/'); - -const enrichedReport = enrichReportWithInsights(currentReport, previousReports); -console.log(`Flaky rate: ${enrichedReport.insights?.flakyRate.current}`); -``` - -### Storing Previous Results - -#### `storePreviousResults(currentReport: Report, previousReports: Report[]): Report` - -Stores previous test run results in the current report's metadata. Extracts key metrics from historical reports for trend analysis. - -**Parameters:** -- `currentReport` - The current CTRF report to enrich with previous results -- `previousReports` - Array of previous CTRF reports to extract metrics from - -**Returns:** The current report with previousResults populated in the `extra` field - -**Example:** -```typescript -import { storePreviousResults, readSingleReport } from 'ctrf'; - -const currentReport = readSingleReport('./current-report.json'); -const previousReports = readReportsFromDirectory('./historical-reports/'); - -const reportWithHistory = storePreviousResults(currentReport, previousReports); -console.log(`Stored ${reportWithHistory.extra?.previousResults?.length} previous results`); -``` - -### Utility Functions - -#### `isTestFlaky(test: Test): boolean` - -Determines if a test is flaky based on its retries and status. - -**Parameters:** -- `test` - The CTRF test to evaluate - -**Returns:** `true` if the test is considered flaky, otherwise `false` - -#### `formatAsPercentage(ratio: number, decimals?: number): string` - -Formats a ratio (0-1) as a percentage string for display. - -**Parameters:** -- `ratio` - The ratio to format (0-1) -- `decimals` - Number of decimal places (default: 2) - -**Returns:** Formatted percentage string (e.g., "25.50%") - -#### `formatInsightsMetricAsPercentage(metric: InsightsMetric, decimals?: number): object` - -Formats an InsightsMetric as percentage strings for display. - -**Parameters:** -- `metric` - The insights metric to format -- `decimals` - Number of decimal places (default: 2) - -**Returns:** Object with formatted percentage strings for current, previous, and change values - ## What is CTRF? CTRF is a universal JSON test report schema that addresses the lack of a standardized format for JSON test reports. diff --git a/ctrf/ctrf-report.json b/ctrf/ctrf-report.json deleted file mode 100644 index ddf8251..0000000 --- a/ctrf/ctrf-report.json +++ /dev/null @@ -1,393 +0,0 @@ -{ - "results": { - "tool": { - "name": "jest" - }, - "summary": { - "tests": 34, - "passed": 34, - "failed": 0, - "pending": 0, - "skipped": 0, - "other": 0, - "start": 1753734090944, - "stop": 1753734092321 - }, - "tests": [ - { - "name": "read-reports readSingleReport should read and parse a valid CTRF report file", - "duration": 3, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readSingleReport" - }, - { - "name": "read-reports readSingleReport should throw error when file does not exist", - "duration": 12, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readSingleReport" - }, - { - "name": "read-reports readSingleReport should throw error when file is not valid JSON", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readSingleReport" - }, - { - "name": "read-reports readSingleReport should throw error when file is not a valid CTRF report", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readSingleReport" - }, - { - "name": "read-reports readReportsFromDirectory should read valid CTRF reports from directory", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromDirectory" - }, - { - "name": "read-reports readReportsFromDirectory should throw error when directory does not exist", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromDirectory" - }, - { - "name": "read-reports readReportsFromDirectory should throw error when no valid CTRF reports are found", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromDirectory" - }, - { - "name": "read-reports readReportsFromDirectory should skip invalid files and warn about them", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromDirectory" - }, - { - "name": "read-reports readReportsFromGlobPattern should read valid CTRF reports from glob pattern", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromGlobPattern" - }, - { - "name": "read-reports readReportsFromGlobPattern should throw error when no files match the pattern", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromGlobPattern" - }, - { - "name": "read-reports readReportsFromGlobPattern should throw error when no valid CTRF reports are found", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/read-reports.test.ts", - "retries": 0, - "flaky": false, - "suite": "read-reports.test.ts > read-reports > readReportsFromGlobPattern" - }, - { - "name": "run-insights utility functions isTestFlaky should return true for explicitly flaky tests", - "duration": 2, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > isTestFlaky" - }, - { - "name": "run-insights utility functions isTestFlaky should return true for passed tests with retries", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > isTestFlaky" - }, - { - "name": "run-insights utility functions isTestFlaky should return false for failed tests with retries", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > isTestFlaky" - }, - { - "name": "run-insights utility functions isTestFlaky should return false for passed tests without retries", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > isTestFlaky" - }, - { - "name": "run-insights utility functions isTestFlaky should return false for passed tests without retries property", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > isTestFlaky" - }, - { - "name": "run-insights utility functions formatAsPercentage should format ratio as percentage with default 2 decimals", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatAsPercentage" - }, - { - "name": "run-insights utility functions formatAsPercentage should format ratio as percentage with custom decimals", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatAsPercentage" - }, - { - "name": "run-insights utility functions formatAsPercentage should handle edge cases", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatAsPercentage" - }, - { - "name": "run-insights utility functions formatInsightsMetricAsPercentage should format all metric values as percentages", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatInsightsMetricAsPercentage" - }, - { - "name": "run-insights utility functions formatInsightsMetricAsPercentage should handle negative change values", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatInsightsMetricAsPercentage" - }, - { - "name": "run-insights utility functions formatInsightsMetricAsPercentage should handle zero change values", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > run-insights utility functions > formatInsightsMetricAsPercentage" - }, - { - "name": "enrichReportWithInsights - Main API basic functionality should enrich a report with run-level insights when no previous reports", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > basic functionality" - }, - { - "name": "enrichReportWithInsights - Main API basic functionality should add test-level insights to each test", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > basic functionality" - }, - { - "name": "enrichReportWithInsights - Main API baseline comparison functionality should compare against previous report by default", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > baseline comparison functionality" - }, - { - "name": "enrichReportWithInsights - Main API baseline comparison functionality should handle baseline by index", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > baseline comparison functionality" - }, - { - "name": "enrichReportWithInsights - Main API baseline comparison functionality should handle baseline by report ID", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > baseline comparison functionality" - }, - { - "name": "enrichReportWithInsights - Main API baseline comparison functionality should handle invalid baseline gracefully", - "duration": 24, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > baseline comparison functionality" - }, - { - "name": "enrichReportWithInsights - Main API added/removed tests tracking should track tests added since baseline", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > added/removed tests tracking" - }, - { - "name": "enrichReportWithInsights - Main API added/removed tests tracking should track tests removed since baseline", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > added/removed tests tracking" - }, - { - "name": "enrichReportWithInsights - Main API added/removed tests tracking should handle single report no added/removed tests", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > added/removed tests tracking" - }, - { - "name": "enrichReportWithInsights - Main API edge cases should handle empty current report", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > edge cases" - }, - { - "name": "enrichReportWithInsights - Main API edge cases should handle invalid current report", - "duration": 0, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > edge cases" - }, - { - "name": "enrichReportWithInsights - Main API edge cases should handle multiple previous reports", - "duration": 1, - "status": "passed", - "rawStatus": "passed", - "type": "unit", - "filePath": "/Users/matthew/projects/personal/ctrf/ctrf-js-common/src/__tests__/run-insights.test.ts", - "retries": 0, - "flaky": false, - "suite": "run-insights.test.ts > enrichReportWithInsights - Main API > edge cases" - } - ] - } -} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..5587594 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,38 @@ +**CTRF v0.0.13** + +*** + +# CTRF v0.0.13 + +## Enumerations + +- [SortOrder](enumerations/SortOrder.md) + +## Interfaces + +- [Attachment](interfaces/Attachment.md) +- [Environment](interfaces/Environment.md) +- [Insights](interfaces/Insights.md) +- [InsightsMetric](interfaces/InsightsMetric.md) +- [Report](interfaces/Report.md) +- [Results](interfaces/Results.md) +- [RetryAttempt](interfaces/RetryAttempt.md) +- [Step](interfaces/Step.md) +- [Summary](interfaces/Summary.md) +- [Test](interfaces/Test.md) +- [TestInsights](interfaces/TestInsights.md) +- [Tool](interfaces/Tool.md) + +## Type Aliases + +- [TestStatus](type-aliases/TestStatus.md) + +## Functions + +- [enrichReportWithInsights](functions/enrichReportWithInsights.md) +- [mergeReports](functions/mergeReports.md) +- [readReportFromFile](functions/readReportFromFile.md) +- [readReportsFromDirectory](functions/readReportsFromDirectory.md) +- [readReportsFromGlobPattern](functions/readReportsFromGlobPattern.md) +- [sortReportsByTimestamp](functions/sortReportsByTimestamp.md) +- [storePreviousResults](functions/storePreviousResults.md) diff --git a/docs/enumerations/SortOrder.md b/docs/enumerations/SortOrder.md new file mode 100644 index 0000000..d4b8148 --- /dev/null +++ b/docs/enumerations/SortOrder.md @@ -0,0 +1,27 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / SortOrder + +# Enumeration: SortOrder + +Defined in: [src/methods/utilities/sort-reports.ts:6](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/utilities/sort-reports.ts#L6) + +Sort order options for timestamp-based sorting. + +## Enumeration Members + +### ASC + +> **ASC**: `"asc"` + +Defined in: [src/methods/utilities/sort-reports.ts:7](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/utilities/sort-reports.ts#L7) + +*** + +### DESC + +> **DESC**: `"desc"` + +Defined in: [src/methods/utilities/sort-reports.ts:8](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/utilities/sort-reports.ts#L8) diff --git a/docs/functions/enrichReportWithInsights.md b/docs/functions/enrichReportWithInsights.md new file mode 100644 index 0000000..1d9082c --- /dev/null +++ b/docs/functions/enrichReportWithInsights.md @@ -0,0 +1,37 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / enrichReportWithInsights + +# Function: enrichReportWithInsights() + +> **enrichReportWithInsights**(`currentReport`, `previousReports`, `baseline?`): [`Report`](../interfaces/Report.md) + +Defined in: [src/methods/run-insights.ts:818](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/run-insights.ts#L818) + +## Parameters + +### currentReport + +[`Report`](../interfaces/Report.md) + +The current CTRF report to enrich + +### previousReports + +[`Report`](../interfaces/Report.md)[] = `[]` + +Array of historical CTRF reports + +### baseline? + +[`Report`](../interfaces/Report.md) + +Optional baseline report to compare against. If not provided, no baseline comparisons are made. + +## Returns + +[`Report`](../interfaces/Report.md) + +The current report fully enriched with run-level insights, test-level insights, and baseline comparisons (if baseline provided) diff --git a/docs/functions/mergeReports.md b/docs/functions/mergeReports.md new file mode 100644 index 0000000..d0e3f0c --- /dev/null +++ b/docs/functions/mergeReports.md @@ -0,0 +1,27 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / mergeReports + +# Function: mergeReports() + +> **mergeReports**(`reports`): [`Report`](../interfaces/Report.md) + +Defined in: [src/methods/merge-reports.ts:10](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/merge-reports.ts#L10) + +Merges multiple CTRF reports into a single report. + +## Parameters + +### reports + +[`Report`](../interfaces/Report.md)[] + +Array of CTRF report objects to be merged. + +## Returns + +[`Report`](../interfaces/Report.md) + +The merged CTRF report object. diff --git a/docs/functions/readReportFromFile.md b/docs/functions/readReportFromFile.md new file mode 100644 index 0000000..1553831 --- /dev/null +++ b/docs/functions/readReportFromFile.md @@ -0,0 +1,31 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / readReportFromFile + +# Function: readReportFromFile() + +> **readReportFromFile**(`filePath`): [`Report`](../interfaces/Report.md) + +Defined in: [src/methods/read-reports.ts:13](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/read-reports.ts#L13) + +Reads a single CTRF report file from a specified path. + +## Parameters + +### filePath + +`string` + +Path to the JSON file containing the CTRF report. + +## Returns + +[`Report`](../interfaces/Report.md) + +The parsed `CtrfReport` object. + +## Throws + +If the file does not exist, is not a valid JSON, or does not conform to the `CtrfReport` structure. diff --git a/docs/functions/readReportsFromDirectory.md b/docs/functions/readReportsFromDirectory.md new file mode 100644 index 0000000..73fdc24 --- /dev/null +++ b/docs/functions/readReportsFromDirectory.md @@ -0,0 +1,31 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / readReportsFromDirectory + +# Function: readReportsFromDirectory() + +> **readReportsFromDirectory**(`directoryPath`): [`Report`](../interfaces/Report.md)[] + +Defined in: [src/methods/read-reports.ts:48](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/read-reports.ts#L48) + +Reads all CTRF report files from a given directory. + +## Parameters + +### directoryPath + +`string` + +Path to the directory containing JSON files. + +## Returns + +[`Report`](../interfaces/Report.md)[] + +An array of parsed `CtrfReport` objects. + +## Throws + +If the directory does not exist or no valid CTRF reports are found. diff --git a/docs/functions/readReportsFromGlobPattern.md b/docs/functions/readReportsFromGlobPattern.md new file mode 100644 index 0000000..f31cf0f --- /dev/null +++ b/docs/functions/readReportsFromGlobPattern.md @@ -0,0 +1,31 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / readReportsFromGlobPattern + +# Function: readReportsFromGlobPattern() + +> **readReportsFromGlobPattern**(`pattern`): [`Report`](../interfaces/Report.md)[] + +Defined in: [src/methods/read-reports.ts:95](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/read-reports.ts#L95) + +Reads all CTRF report files matching a glob pattern. + +## Parameters + +### pattern + +`string` + +The glob pattern to match files (e.g., ctrf/*.json). + +## Returns + +[`Report`](../interfaces/Report.md)[] + +An array of parsed `CtrfReport` objects. + +## Throws + +If no valid CTRF reports are found. diff --git a/docs/functions/sortReportsByTimestamp.md b/docs/functions/sortReportsByTimestamp.md new file mode 100644 index 0000000..87a14a4 --- /dev/null +++ b/docs/functions/sortReportsByTimestamp.md @@ -0,0 +1,52 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / sortReportsByTimestamp + +# Function: sortReportsByTimestamp() + +> **sortReportsByTimestamp**(`reports`, `order`): [`Report`](../interfaces/Report.md)[] + +Defined in: [src/methods/utilities/sort-reports.ts:38](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/utilities/sort-reports.ts#L38) + +Sorts CTRF reports by their timestamp. + +This function uses a fallback strategy for timestamp selection: +1. First tries to use `report.timestamp` if available +2. Falls back to `report.results.summary.stop` if `timestamp` is not available +3. Reports without any timestamp are sorted to the end of the array + +## Parameters + +### reports + +[`Report`](../interfaces/Report.md)[] + +Array of CTRF reports to sort + +### order + +[`SortOrder`](../enumerations/SortOrder.md) = `SortOrder.DESC` + +Sort order: SortOrder.DESC for newest first (default), SortOrder.ASC for oldest first + +## Returns + +[`Report`](../interfaces/Report.md)[] + +A new array with reports sorted by timestamp + +## Example + +```typescript +const unsortedReports = [report1, report2, report3]; + +const newestFirst = sortReportsByTimestamp(unsortedReports); +// newestFirst[0] will be the most recent report + +const oldestFirst = sortReportsByTimestamp(unsortedReports, SortOrder.ASC); +// oldestFirst[0] will be the oldest report + +const newestFirst = sortReportsByTimestamp(unsortedReports, SortOrder.DESC); +``` diff --git a/docs/functions/storePreviousResults.md b/docs/functions/storePreviousResults.md new file mode 100644 index 0000000..4c3e765 --- /dev/null +++ b/docs/functions/storePreviousResults.md @@ -0,0 +1,34 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / storePreviousResults + +# Function: storePreviousResults() + +> **storePreviousResults**(`currentReport`, `previousReports`): [`Report`](../interfaces/Report.md) + +Defined in: [src/methods/store-previous-results.ts:34](https://github.com/ctrf-io/ctrf-core-js/blob/main/src/methods/store-previous-results.ts#L34) + +Stores previous results in the current report's previousResults array. +Extracts key metrics from each previous report and adds them to the current report. + +## Parameters + +### currentReport + +[`Report`](../interfaces/Report.md) + +The current CTRF report to enrich with previous results + +### previousReports + +[`Report`](../interfaces/Report.md)[] + +Array of previous CTRF reports to extract metrics from + +## Returns + +[`Report`](../interfaces/Report.md) + +The current report with previousResults populated diff --git a/docs/interfaces/Attachment.md b/docs/interfaces/Attachment.md new file mode 100644 index 0000000..89aa66e --- /dev/null +++ b/docs/interfaces/Attachment.md @@ -0,0 +1,41 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Attachment + +# Interface: Attachment + +Defined in: [types/ctrf.d.ts:100](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L100) + +## Properties + +### contentType + +> **contentType**: `string` + +Defined in: [types/ctrf.d.ts:102](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L102) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:104](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L104) + +*** + +### name + +> **name**: `string` + +Defined in: [types/ctrf.d.ts:101](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L101) + +*** + +### path + +> **path**: `string` + +Defined in: [types/ctrf.d.ts:103](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L103) diff --git a/docs/interfaces/Environment.md b/docs/interfaces/Environment.md new file mode 100644 index 0000000..01af5ed --- /dev/null +++ b/docs/interfaces/Environment.md @@ -0,0 +1,137 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Environment + +# Interface: Environment + +Defined in: [types/ctrf.d.ts:69](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L69) + +## Properties + +### appName? + +> `optional` **appName**: `string` + +Defined in: [types/ctrf.d.ts:71](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L71) + +*** + +### appVersion? + +> `optional` **appVersion**: `string` + +Defined in: [types/ctrf.d.ts:72](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L72) + +*** + +### branchName? + +> `optional` **branchName**: `string` + +Defined in: [types/ctrf.d.ts:80](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L80) + +*** + +### buildId? + +> `optional` **buildId**: `string` + +Defined in: [types/ctrf.d.ts:73](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L73) + +*** + +### buildName? + +> `optional` **buildName**: `string` + +Defined in: [types/ctrf.d.ts:74](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L74) + +*** + +### buildNumber? + +> `optional` **buildNumber**: `number` + +Defined in: [types/ctrf.d.ts:75](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L75) + +*** + +### buildUrl? + +> `optional` **buildUrl**: `string` + +Defined in: [types/ctrf.d.ts:76](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L76) + +*** + +### commit? + +> `optional` **commit**: `string` + +Defined in: [types/ctrf.d.ts:79](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L79) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:85](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L85) + +*** + +### osPlatform? + +> `optional` **osPlatform**: `string` + +Defined in: [types/ctrf.d.ts:81](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L81) + +*** + +### osRelease? + +> `optional` **osRelease**: `string` + +Defined in: [types/ctrf.d.ts:82](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L82) + +*** + +### osVersion? + +> `optional` **osVersion**: `string` + +Defined in: [types/ctrf.d.ts:83](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L83) + +*** + +### reportName? + +> `optional` **reportName**: `string` + +Defined in: [types/ctrf.d.ts:70](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L70) + +*** + +### repositoryName? + +> `optional` **repositoryName**: `string` + +Defined in: [types/ctrf.d.ts:77](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L77) + +*** + +### repositoryUrl? + +> `optional` **repositoryUrl**: `string` + +Defined in: [types/ctrf.d.ts:78](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L78) + +*** + +### testEnvironment? + +> `optional` **testEnvironment**: `string` + +Defined in: [types/ctrf.d.ts:84](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L84) diff --git a/docs/interfaces/Insights.md b/docs/interfaces/Insights.md new file mode 100644 index 0000000..b474e7f --- /dev/null +++ b/docs/interfaces/Insights.md @@ -0,0 +1,73 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Insights + +# Interface: Insights + +Defined in: [types/ctrf.d.ts:123](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L123) + +## Properties + +### averageRunDuration? + +> `optional` **averageRunDuration**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:128](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L128) + +*** + +### averageTestDuration? + +> `optional` **averageTestDuration**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:130](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L130) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:131](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L131) + +*** + +### failRate? + +> `optional` **failRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:126](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L126) + +*** + +### flakyRate? + +> `optional` **flakyRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:127](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L127) + +*** + +### p95RunDuration? + +> `optional` **p95RunDuration**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:129](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L129) + +*** + +### passRate? + +> `optional` **passRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:125](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L125) + +*** + +### runsAnalyzed? + +> `optional` **runsAnalyzed**: `number` + +Defined in: [types/ctrf.d.ts:124](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L124) diff --git a/docs/interfaces/InsightsMetric.md b/docs/interfaces/InsightsMetric.md new file mode 100644 index 0000000..911497f --- /dev/null +++ b/docs/interfaces/InsightsMetric.md @@ -0,0 +1,33 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / InsightsMetric + +# Interface: InsightsMetric + +Defined in: [types/ctrf.d.ts:144](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L144) + +## Properties + +### baseline + +> **baseline**: `number` + +Defined in: [types/ctrf.d.ts:146](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L146) + +*** + +### change + +> **change**: `number` + +Defined in: [types/ctrf.d.ts:147](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L147) + +*** + +### current + +> **current**: `number` + +Defined in: [types/ctrf.d.ts:145](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L145) diff --git a/docs/interfaces/Report.md b/docs/interfaces/Report.md new file mode 100644 index 0000000..b01829f --- /dev/null +++ b/docs/interfaces/Report.md @@ -0,0 +1,81 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Report + +# Interface: Report + +Defined in: [types/ctrf.d.ts:1](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L1) + +## Properties + +### baseline? + +> `optional` **baseline**: `Baseline` + +Defined in: [types/ctrf.d.ts:9](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L9) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:10](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L10) + +*** + +### generatedBy? + +> `optional` **generatedBy**: `string` + +Defined in: [types/ctrf.d.ts:6](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L6) + +*** + +### insights? + +> `optional` **insights**: [`Insights`](Insights.md) + +Defined in: [types/ctrf.d.ts:8](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L8) + +*** + +### reportFormat + +> **reportFormat**: `"CTRF"` + +Defined in: [types/ctrf.d.ts:2](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L2) + +*** + +### reportId? + +> `optional` **reportId**: `string` + +Defined in: [types/ctrf.d.ts:4](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L4) + +*** + +### results + +> **results**: [`Results`](Results.md) + +Defined in: [types/ctrf.d.ts:7](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L7) + +*** + +### specVersion + +> **specVersion**: `` `${number}.${number}.${number}` `` + +Defined in: [types/ctrf.d.ts:3](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L3) + +*** + +### timestamp? + +> `optional` **timestamp**: `string` + +Defined in: [types/ctrf.d.ts:5](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L5) diff --git a/docs/interfaces/Results.md b/docs/interfaces/Results.md new file mode 100644 index 0000000..b87bc82 --- /dev/null +++ b/docs/interfaces/Results.md @@ -0,0 +1,49 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Results + +# Interface: Results + +Defined in: [types/ctrf.d.ts:13](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L13) + +## Properties + +### environment? + +> `optional` **environment**: [`Environment`](Environment.md) + +Defined in: [types/ctrf.d.ts:17](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L17) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:18](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L18) + +*** + +### summary + +> **summary**: [`Summary`](Summary.md) + +Defined in: [types/ctrf.d.ts:15](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L15) + +*** + +### tests + +> **tests**: [`Test`](Test.md)[] + +Defined in: [types/ctrf.d.ts:16](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L16) + +*** + +### tool + +> **tool**: [`Tool`](Tool.md) + +Defined in: [types/ctrf.d.ts:14](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L14) diff --git a/docs/interfaces/RetryAttempt.md b/docs/interfaces/RetryAttempt.md new file mode 100644 index 0000000..fd4d236 --- /dev/null +++ b/docs/interfaces/RetryAttempt.md @@ -0,0 +1,113 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / RetryAttempt + +# Interface: RetryAttempt + +Defined in: [types/ctrf.d.ts:107](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L107) + +## Properties + +### attachments? + +> `optional` **attachments**: [`Attachment`](Attachment.md)[] + +Defined in: [types/ctrf.d.ts:119](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L119) + +*** + +### attempt + +> **attempt**: `number` + +Defined in: [types/ctrf.d.ts:108](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L108) + +*** + +### duration? + +> `optional` **duration**: `number` + +Defined in: [types/ctrf.d.ts:110](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L110) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:120](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L120) + +*** + +### line? + +> `optional` **line**: `number` + +Defined in: [types/ctrf.d.ts:113](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L113) + +*** + +### message? + +> `optional` **message**: `string` + +Defined in: [types/ctrf.d.ts:111](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L111) + +*** + +### snippet? + +> `optional` **snippet**: `string` + +Defined in: [types/ctrf.d.ts:114](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L114) + +*** + +### start? + +> `optional` **start**: `number` + +Defined in: [types/ctrf.d.ts:117](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L117) + +*** + +### status + +> **status**: [`TestStatus`](../type-aliases/TestStatus.md) + +Defined in: [types/ctrf.d.ts:109](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L109) + +*** + +### stderr? + +> `optional` **stderr**: `string`[] + +Defined in: [types/ctrf.d.ts:116](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L116) + +*** + +### stdout? + +> `optional` **stdout**: `string`[] + +Defined in: [types/ctrf.d.ts:115](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L115) + +*** + +### stop? + +> `optional` **stop**: `number` + +Defined in: [types/ctrf.d.ts:118](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L118) + +*** + +### trace? + +> `optional` **trace**: `string` + +Defined in: [types/ctrf.d.ts:112](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L112) diff --git a/docs/interfaces/Step.md b/docs/interfaces/Step.md new file mode 100644 index 0000000..637c65a --- /dev/null +++ b/docs/interfaces/Step.md @@ -0,0 +1,33 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Step + +# Interface: Step + +Defined in: [types/ctrf.d.ts:94](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L94) + +## Properties + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:97](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L97) + +*** + +### name + +> **name**: `string` + +Defined in: [types/ctrf.d.ts:95](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L95) + +*** + +### status + +> **status**: [`TestStatus`](../type-aliases/TestStatus.md) + +Defined in: [types/ctrf.d.ts:96](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L96) diff --git a/docs/interfaces/Summary.md b/docs/interfaces/Summary.md new file mode 100644 index 0000000..847ce34 --- /dev/null +++ b/docs/interfaces/Summary.md @@ -0,0 +1,105 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Summary + +# Interface: Summary + +Defined in: [types/ctrf.d.ts:21](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L21) + +## Properties + +### duration + +> **duration**: `number` + +Defined in: [types/ctrf.d.ts:32](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L32) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:33](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L33) + +*** + +### failed + +> **failed**: `number` + +Defined in: [types/ctrf.d.ts:24](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L24) + +*** + +### flaky + +> **flaky**: `number` + +Defined in: [types/ctrf.d.ts:28](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L28) + +*** + +### other + +> **other**: `number` + +Defined in: [types/ctrf.d.ts:27](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L27) + +*** + +### passed + +> **passed**: `number` + +Defined in: [types/ctrf.d.ts:23](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L23) + +*** + +### pending + +> **pending**: `number` + +Defined in: [types/ctrf.d.ts:26](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L26) + +*** + +### skipped + +> **skipped**: `number` + +Defined in: [types/ctrf.d.ts:25](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L25) + +*** + +### start + +> **start**: `number` + +Defined in: [types/ctrf.d.ts:30](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L30) + +*** + +### stop + +> **stop**: `number` + +Defined in: [types/ctrf.d.ts:31](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L31) + +*** + +### suites? + +> `optional` **suites**: `number` + +Defined in: [types/ctrf.d.ts:29](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L29) + +*** + +### tests + +> **tests**: `number` + +Defined in: [types/ctrf.d.ts:22](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L22) diff --git a/docs/interfaces/Test.md b/docs/interfaces/Test.md new file mode 100644 index 0000000..f3dad27 --- /dev/null +++ b/docs/interfaces/Test.md @@ -0,0 +1,249 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Test + +# Interface: Test + +Defined in: [types/ctrf.d.ts:36](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L36) + +## Properties + +### ai? + +> `optional` **ai**: `string` + +Defined in: [types/ctrf.d.ts:48](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L48) + +*** + +### attachments? + +> `optional` **attachments**: [`Attachment`](Attachment.md)[] + +Defined in: [types/ctrf.d.ts:59](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L59) + +*** + +### browser? + +> `optional` **browser**: `string` + +Defined in: [types/ctrf.d.ts:60](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L60) + +*** + +### device? + +> `optional` **device**: `string` + +Defined in: [types/ctrf.d.ts:61](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L61) + +*** + +### duration + +> **duration**: `number` + +Defined in: [types/ctrf.d.ts:40](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L40) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:66](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L66) + +*** + +### filePath? + +> `optional` **filePath**: `string` + +Defined in: [types/ctrf.d.ts:52](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L52) + +*** + +### flaky? + +> `optional` **flaky**: `boolean` + +Defined in: [types/ctrf.d.ts:55](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L55) + +*** + +### id? + +> `optional` **id**: `string` + +Defined in: [types/ctrf.d.ts:37](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L37) + +*** + +### insights? + +> `optional` **insights**: [`TestInsights`](TestInsights.md) + +Defined in: [types/ctrf.d.ts:65](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L65) + +*** + +### line? + +> `optional` **line**: `number` + +Defined in: [types/ctrf.d.ts:47](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L47) + +*** + +### message? + +> `optional` **message**: `string` + +Defined in: [types/ctrf.d.ts:44](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L44) + +*** + +### name + +> **name**: `string` + +Defined in: [types/ctrf.d.ts:38](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L38) + +*** + +### parameters? + +> `optional` **parameters**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:63](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L63) + +*** + +### rawStatus? + +> `optional` **rawStatus**: `string` + +Defined in: [types/ctrf.d.ts:49](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L49) + +*** + +### retries? + +> `optional` **retries**: `number` + +Defined in: [types/ctrf.d.ts:53](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L53) + +*** + +### retryAttempts? + +> `optional` **retryAttempts**: [`RetryAttempt`](RetryAttempt.md)[] + +Defined in: [types/ctrf.d.ts:54](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L54) + +*** + +### screenshot? + +> `optional` **screenshot**: `string` + +Defined in: [types/ctrf.d.ts:62](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L62) + +*** + +### snippet? + +> `optional` **snippet**: `string` + +Defined in: [types/ctrf.d.ts:46](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L46) + +*** + +### start? + +> `optional` **start**: `number` + +Defined in: [types/ctrf.d.ts:41](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L41) + +*** + +### status + +> **status**: [`TestStatus`](../type-aliases/TestStatus.md) + +Defined in: [types/ctrf.d.ts:39](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L39) + +*** + +### stderr? + +> `optional` **stderr**: `string`[] + +Defined in: [types/ctrf.d.ts:57](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L57) + +*** + +### stdout? + +> `optional` **stdout**: `string`[] + +Defined in: [types/ctrf.d.ts:56](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L56) + +*** + +### steps? + +> `optional` **steps**: [`Step`](Step.md)[] + +Defined in: [types/ctrf.d.ts:64](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L64) + +*** + +### stop? + +> `optional` **stop**: `number` + +Defined in: [types/ctrf.d.ts:42](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L42) + +*** + +### suite? + +> `optional` **suite**: `string` + +Defined in: [types/ctrf.d.ts:43](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L43) + +*** + +### tags? + +> `optional` **tags**: `string`[] + +Defined in: [types/ctrf.d.ts:50](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L50) + +*** + +### threadId? + +> `optional` **threadId**: `string` + +Defined in: [types/ctrf.d.ts:58](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L58) + +*** + +### trace? + +> `optional` **trace**: `string` + +Defined in: [types/ctrf.d.ts:45](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L45) + +*** + +### type? + +> `optional` **type**: `string` + +Defined in: [types/ctrf.d.ts:51](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L51) diff --git a/docs/interfaces/TestInsights.md b/docs/interfaces/TestInsights.md new file mode 100644 index 0000000..f803806 --- /dev/null +++ b/docs/interfaces/TestInsights.md @@ -0,0 +1,65 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / TestInsights + +# Interface: TestInsights + +Defined in: [types/ctrf.d.ts:134](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L134) + +## Properties + +### averageTestDuration? + +> `optional` **averageTestDuration**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:138](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L138) + +*** + +### executedInRuns? + +> `optional` **executedInRuns**: `number` + +Defined in: [types/ctrf.d.ts:140](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L140) + +*** + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:141](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L141) + +*** + +### failRate? + +> `optional` **failRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:136](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L136) + +*** + +### flakyRate? + +> `optional` **flakyRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:137](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L137) + +*** + +### p95TestDuration? + +> `optional` **p95TestDuration**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:139](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L139) + +*** + +### passRate? + +> `optional` **passRate**: [`InsightsMetric`](InsightsMetric.md) + +Defined in: [types/ctrf.d.ts:135](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L135) diff --git a/docs/interfaces/Tool.md b/docs/interfaces/Tool.md new file mode 100644 index 0000000..2599a01 --- /dev/null +++ b/docs/interfaces/Tool.md @@ -0,0 +1,33 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / Tool + +# Interface: Tool + +Defined in: [types/ctrf.d.ts:88](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L88) + +## Properties + +### extra? + +> `optional` **extra**: `Record`\<`string`, `unknown`\> + +Defined in: [types/ctrf.d.ts:91](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L91) + +*** + +### name + +> **name**: `string` + +Defined in: [types/ctrf.d.ts:89](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L89) + +*** + +### version? + +> `optional` **version**: `string` + +Defined in: [types/ctrf.d.ts:90](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L90) diff --git a/docs/type-aliases/TestStatus.md b/docs/type-aliases/TestStatus.md new file mode 100644 index 0000000..a7e6fa4 --- /dev/null +++ b/docs/type-aliases/TestStatus.md @@ -0,0 +1,11 @@ +[**CTRF v0.0.13**](../README.md) + +*** + +[CTRF](../README.md) / TestStatus + +# Type Alias: TestStatus + +> **TestStatus** = `"passed"` \| `"failed"` \| `"skipped"` \| `"pending"` \| `"other"` + +Defined in: [types/ctrf.d.ts:160](https://github.com/ctrf-io/ctrf-core-js/blob/main/types/ctrf.d.ts#L160) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..2ebe1b2 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,35 @@ +// @ts-check +import js from '@eslint/js' +import typescript from 'typescript-eslint' + +export default [ + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'coverage/**', + 'docs/**', + 'ctrf/**', + '*.js', + '*.mjs', + 'src/test-utils/**', + 'scripts/**', + 'examples/**', + 'final/**', + 'src/__tests__/**', + 'src/test-utils/**', + 'src/test-utils/**', + '**/*.test.ts', + 'src/cli.ts', + ], + }, + js.configs.recommended, + ...typescript.configs.recommended, + { + files: ['**/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': 'error', + }, + }, +] diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 8f6d0a7..0000000 --- a/jest.config.js +++ /dev/null @@ -1,24 +0,0 @@ -export default { - preset: 'ts-jest/presets/default-esm', - extensionsToTreatAsEsm: ['.ts'], - testEnvironment: 'node', - testMatch: [ - '**/__tests__/**/*.test.ts', - '**/?(*.)+(spec|test).ts' - ], - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts' - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - transform: { - '^.+\\.ts$': ['ts-jest', { - useESM: true - }] - }, - reporters: [ - 'default', - ['jest-ctrf-json-reporter', {}], - ], -}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6dbd3f2..0185cf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ctrf", - "version": "0.0.13-next.0", + "version": "0.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ctrf", - "version": "0.0.13-next.0", + "version": "0.0.13", "license": "MIT", "dependencies": { "glob": "^11.0.3", @@ -17,16 +17,18 @@ "ctrf": "dist/cli.js" }, "devDependencies": { - "@types/jest": "^30.0.0", + "@d2t/vitest-ctrf-json-reporter": "^1.2.0", + "@eslint/js": "^9.32.0", "@types/node": "^20.12.7", "@types/yargs": "^17.0.32", - "jest": "^30.0.4", - "jest-ctrf-json-reporter": "^0.0.9", - "jest-environment-node": "^30.0.4", - "ts-jest": "^29.4.0", - "tsx": "^4.19.2", - "typedoc": "^0.28.8", - "typescript": "^5.8.3" + "@vitest/coverage-v8": "^3.2.4", + "eslint": "^9.32.0", + "prettier": "^3.5.3", + "typedoc": "^0.28.9", + "typedoc-plugin-markdown": "^4.8.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vitest": "^3.2.4" }, "engines": { "node": ">=20.19.0" @@ -37,6 +39,7 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -45,153 +48,12 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/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, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -201,28 +63,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -232,6 +73,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.28.0" }, @@ -242,265 +84,12 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" @@ -510,41 +99,21 @@ } }, "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@emnapi/core": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", - "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.3", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", - "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "license": "MIT", + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", - "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "node_modules/@d2t/vitest-ctrf-json-reporter": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@d2t/vitest-ctrf-json-reporter/-/vitest-ctrf-json-reporter-1.2.0.tgz", + "integrity": "sha512-CL+vAIdavJ3rr1IBE0lv5NvUtxigNTnNDo99hLMlgMkbOpzMvhV3EksQQt3CjweVVnPuTbiSqXXyqAKA7JINjg==", "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.6", @@ -962,473 +531,338 @@ "node": ">=18" } }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.8.1.tgz", - "integrity": "sha512-HVZW+8pxoOExr5ZMPK15U79jQAZTO/S6i5byQyyZGjtNj+qaYd82cizTncwFzTQgiLo8uUBym6vh+/1tfJklTw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, + "license": "MIT", "dependencies": { - "@shikijs/engine-oniguruma": "^3.8.1", - "@shikijs/langs": "^3.8.1", - "@shikijs/themes": "^3.8.1", - "@shikijs/types": "^3.8.1", - "@shikijs/vscode-textmate": "^10.0.2" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": "20 || >=22" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "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==", - "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" - }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": "^12.0.0 || ^14.0.0 || >=16.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/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, + "license": "Apache-2.0", "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" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.4.tgz", - "integrity": "sha512-tMLCDvBJBwPqMm4OAiuKm2uF5y5Qe26KgcMn+nrDSWpEW+eeFmqA0iO4zJfL16GP7gE3bUUQ3hIuUJ22AqVRnw==", - "dev": true, - "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.0.2", - "jest-util": "30.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.4.tgz", - "integrity": "sha512-MWScSO9GuU5/HoWjpXAOBs6F/iobvK1XlioelgOM9St7S0Z5WTI9kjCQLPeo4eQRRYusyLW25/J7J5lbFkrYXw==", - "dev": true, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@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.0.2", - "jest-config": "30.0.4", - "jest-haste-map": "30.0.2", - "jest-message-util": "30.0.2", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-resolve-dependencies": "30.0.4", - "jest-runner": "30.0.4", - "jest-runtime": "30.0.4", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "jest-watcher": "30.0.4", - "micromatch": "^4.0.8", - "pretty-format": "30.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "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/@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, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@jest/environment": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.4.tgz", - "integrity": "sha512-5NT+sr7ZOb8wW7C4r7wOKnRQ8zmRWQT2gW4j73IXAKp5/PX1Z8MCStBLQDYfIG3n1Sw0NRfYGdp0iIPVooBAFQ==", + "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": "ISC", "dependencies": { - "@jest/fake-timers": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-mock": "30.0.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "*" } }, - "node_modules/@jest/expect": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.4.tgz", - "integrity": "sha512-Z/DL7t67LBHSX4UzDyeYKqOxE/n7lbrrgEwWM3dGiH5Dgn35nk+YtgzKudmfIrBI8DRRrKYY5BCo3317HZV1Fw==", + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, - "dependencies": { - "expect": "30.0.4", - "jest-snapshot": "30.0.4" - }, + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/expect-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.4.tgz", - "integrity": "sha512-EgXecHDNfANeqOkcak0DxsoVI4qkDUsR7n/Lr2vtmTBjwLPBnnPOF71S11Q8IObWzxm2QgQoY6f9hzrRD3gHRA==", + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jest/get-type": "30.0.1" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/fake-timers": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.4.tgz", - "integrity": "sha512-qZ7nxOcL5+gwBO6LErvwVy5k06VsX/deqo2XnVUSTV0TNC9lrg8FC3dARbi+5lmrr5VyX5drragK+xLcOjvjYw==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "30.0.1", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-util": "30.0.2" + "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.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "Python-2.0" }, - "node_modules/@jest/globals": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.4.tgz", - "integrity": "sha512-avyZuxEHF2EUhFF6NEWVdxkRRV6iXXcIES66DLhuLlU7lXhtFG/ySq/a8SRZmEJSsLkNAFX6z6mm8KWyXe9OEA==", + "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", "dependencies": { - "@jest/environment": "30.0.4", - "@jest/expect": "30.0.4", - "@jest/types": "30.0.1", - "jest-mock": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "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/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" + "argparse": "^2.0.1" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@jest/reporters": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.4.tgz", - "integrity": "sha512-6ycNmP0JSJEEys1FbIzHtjl9BP0tOZ/KN6iMeAKrdvGmUsa1qfRdlQRUDKJ4P84hJ3xHw1yTqJt4fvPNHhyE+g==", + "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": "ISC", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@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.0.2", - "jest-util": "30.0.2", - "jest-worker": "30.0.2", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" + "brace-expansion": "^1.1.7" }, "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": "*" } }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, - "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" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://eslint.org/donate" } }, - "node_modules/@jest/reporters/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/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/reporters/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 - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/reporters/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/@gerrit0/mini-shiki": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.9.2.tgz", + "integrity": "sha512-Tvsj+AOO4Z8xLRJK900WkyfxHsZQu+Zm1//oT1w443PO6RiYMoq/4NGOhaNuZoUMYsjKIAPVQ6eOFMddj6yphQ==", "dev": true, + "license": "MIT", "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" + "@shikijs/engine-oniguruma": "^3.9.2", + "@shikijs/langs": "^3.9.2", + "@shikijs/themes": "^3.9.2", + "@shikijs/types": "^3.9.2", + "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@jest/schemas": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.1.tgz", - "integrity": "sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==", + "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, - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.18.0" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.4.tgz", - "integrity": "sha512-BEpX8M/Y5lG7MI3fmiO+xCnacOrVsnbqVrcDZIT8aSGkKV1w2WwvRQxSWw5SIS8ozg7+h8tSj5EO1Riqqxcdag==", + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.18.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/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/test-result": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.4.tgz", - "integrity": "sha512-Mfpv8kjyKTHqsuu9YugB6z1gcdB3TSSOaKlehtVaiNlClMkEHY+5ZqCY2CrEE3ntpBMlstX/ShDAf84HKWsyIw==", + "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, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/types": "30.0.1", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "20 || >=22" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.4.tgz", - "integrity": "sha512-bj6ePmqi4uxAE8EHE0Slmk5uBYd9Vd/PcVt06CsBxzH4bbA8nGsI1YbXl/NH+eii4XRtyrRx+Cikub0x8H4vDg==", - "dev": true, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", "dependencies": { - "@jest/test-result": "30.0.4", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "slash": "^3.0.0" + "@isaacs/balanced-match": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "20 || >=22" } }, - "node_modules/@jest/transform": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.4.tgz", - "integrity": "sha512-atvy4hRph/UxdCIBp+UB2jhEA/jJiUeGZ7QPgBi9jUUKNgi3WEoMXGNG7zbbELG2+88PMabUNCDchmqgJy3ELg==", - "dev": true, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.0.1", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", - "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.0.2", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.2", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" + "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.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" } }, - "node_modules/@jest/types": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.1.tgz", - "integrity": "sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==", + "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, - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.1", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, + "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { @@ -1436,6 +870,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -1446,6 +881,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -1461,2528 +897,1955 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "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, - "optional": true, + "license": "MIT", "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=14" + "node": ">= 8" } }, - "node_modules/@pkgr/core": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "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", "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" + "node": ">= 8" } }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.8.1.tgz", - "integrity": "sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==", + "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": { - "@shikijs/types": "3.8.1", - "@shikijs/vscode-textmate": "^10.0.2" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@shikijs/langs": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.8.1.tgz", - "integrity": "sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==", + "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, - "dependencies": { - "@shikijs/types": "3.8.1" + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" } }, - "node_modules/@shikijs/themes": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.8.1.tgz", - "integrity": "sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@shikijs/types": "3.8.1" - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@shikijs/types": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.8.1.tgz", - "integrity": "sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==", - "dev": true, - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.37", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.37.tgz", - "integrity": "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", - "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/node": { - "version": "20.19.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", - "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", - "dev": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true - }, - "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" - ], + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], "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==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "android" + "darwin" ] }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ - "arm64" + "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ - "x64" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "freebsd" ] }, - "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==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "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==", + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", "cpu": [ - "ppc64" + "loong64" ], "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==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ - "riscv64" + "ppc64" ], "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==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", "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==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ - "s390x" + "riscv64" ], "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==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ - "x64" + "s390x" ], "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==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "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==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ - "wasm32" + "x64" ], "dev": true, + "license": "MIT", "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } + "os": [ + "linux" + ] }, - "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==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "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==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "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==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.9.2.tgz", + "integrity": "sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "@shikijs/types": "3.9.2", + "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node_modules/@shikijs/langs": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.9.2.tgz", + "integrity": "sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.9.2" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@shikijs/themes": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.9.2.tgz", + "integrity": "sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "@shikijs/types": "3.9.2" } }, - "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/@shikijs/types": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.9.2.tgz", + "integrity": "sha512-/M5L0Uc2ljyn2jKvj4Yiah7ow/W+DJSglVafvWAJ/b8AZDeeRAdMu3c2riDzB7N42VD+jSnWxeP9AKtd4TfYVw==", "dev": true, + "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" }, - "node_modules/babel-jest": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.4.tgz", - "integrity": "sha512-UjG2j7sAOqsp2Xua1mS/e+ekddkSu3wpf4nZUSvXNHuVWdaOUXQ77+uyjJLDE9i0atm5x4kds8K9yb5lRsRtcA==", + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/transform": "30.0.4", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" + "@types/deep-eql": "*" } }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", - "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", - "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@types/unist": "*" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, - "node_modules/babel-preset-jest": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", - "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "node_modules/@types/node": { + "version": "20.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", + "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" + "undici-types": "~6.21.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "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", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "@types/yargs-parser": "*" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "bin": { - "browserslist": "cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "peerDependencies": { + "@typescript-eslint/parser": "^8.39.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 4" } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/@typescript-eslint/parser": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", "dev": true, + "license": "MIT", "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=6" + "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/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", - "dev": true - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "MIT", "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=20" + "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/cliui/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" - }, - "node_modules/cliui/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==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "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/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "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": "ISC", "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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==" - }, - "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 - }, - "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 - }, - "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/@typescript-eslint/utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", + "dev": true, + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" }, "engines": { - "node": ">= 8" + "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/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "node_modules/@vitest/coverage-v8": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { - "babel-plugin-macros": { + "@vitest/browser": { "optional": true } } }, - "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/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, + "license": "MIT", "dependencies": { - "jake": "^10.8.5" + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, - "bin": { - "ejs": "bin/cli.js" + "funding": { + "url": "https://opencollective.com/vitest" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/electron-to-chromium": { - "version": "1.5.182", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", - "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", - "dev": true - }, - "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/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "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==" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, - "engines": { - "node": ">=0.12" + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-arrayish": "^0.2.1" + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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==", + "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": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "acorn": "bin/acorn" }, "engines": { - "node": ">=4" + "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/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "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": { - "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" + "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-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "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/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/expect": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.4.tgz", - "integrity": "sha512-dDLGjnP2cKbEppxVICxI/Uf4YemmGMPNy0QytCbfafbpYk9AFQsxb8Uyrxii0RPK7FWgLGlSem+07WirwS3cFQ==", + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.0.4", - "@jest/get-type": "30.0.1", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-util": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@jridgewell/trace-mapping": "^0.3.29", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" } }, - "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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/fb-watchman": { + "node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "bser": "2.1.1" + "balanced-match": "^1.0.0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "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/chai": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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 - }, - "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/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">= 16" } }, - "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==", + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=20" } }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "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, - "engines": { - "node": ">=8.0.0" - } + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" }, - "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, + "node_modules/cliui/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==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": "20 || >=22" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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 - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "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, - "engines": { - "node": ">=10.17.0" + "url": "https://github.com/chalk/wrap-ansi?sponsor=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==", - "dev": true, + "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==", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">=7.0.0" } }, - "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 + "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==" }, - "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "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==", - "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, - "engines": { - "node": ">=6" - } - }, - "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, - "engines": { - "node": ">=0.12.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==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "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, "engines": { - "node": ">=8" + "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==", + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "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" + "ms": "^2.1.3" }, "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=6.0" }, - "engines": { - "node": ">=10" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "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/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, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "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/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "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==" }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/jake/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/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "license": "MIT" }, - "node_modules/jest": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.4.tgz", - "integrity": "sha512-9QE0RS4WwTj/TtTC4h/eFVmFAhGNVerSB9XpJh8sqaXlP73ILcPcZ7JWjjEtJJe2m8QyBLKKfPQuK+3F+Xij/g==", + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", "dev": true, - "dependencies": { - "@jest/core": "30.0.4", - "@jest/types": "30.0.1", - "import-local": "^3.2.0", - "jest-cli": "30.0.4" - }, + "hasInstallScript": true, "bin": { - "jest": "bin/jest.js" + "esbuild": "bin/esbuild" }, "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.0.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.2.tgz", - "integrity": "sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==", - "dev": true, - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.0.2", - "p-limit": "^3.1.0" + "node": ">=18" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" } }, - "node_modules/jest-circus": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.4.tgz", - "integrity": "sha512-o6UNVfbXbmzjYgmVPtSQrr5xFZCtkDZGdTlptYvGFSN80RuOOlTe73djvMrs+QAuSERZWcHBNIOMH+OEqvjWuw==", - "dev": true, - "dependencies": { - "@jest/environment": "30.0.4", - "@jest/expect": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.0.2", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-runtime": "30.0.4", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "p-limit": "^3.1.0", - "pretty-format": "30.0.2", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6" } }, - "node_modules/jest-cli": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.4.tgz", - "integrity": "sha512-3dOrP3zqCWBkjoVG1zjYJpD9143N9GUCbwaF2pFF5brnIgRLHmKcCIw+83BvF1LxggfMWBA0gxkn6RuQVuRhIQ==", + "node_modules/eslint": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/core": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "yargs": "^17.7.2" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "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": { - "jest": "bin/jest.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "jiti": "*" }, "peerDependenciesMeta": { - "node-notifier": { + "jiti": { "optional": true } } }, - "node_modules/jest-cli/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/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, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-cli/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 - }, - "node_modules/jest-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "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": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/jest-cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/eslint/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": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.4.tgz", - "integrity": "sha512-3dzbO6sh34thAGEjJIW0fgT0GA0EVlkski6ZzMcbW6dzhenylXAE/Mj2MI4HonroWbkKc6wU6bLVQ8dvBSZ9lA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.4", - "@jest/types": "30.0.1", - "babel-jest": "30.0.4", - "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.0.4", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.4", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-runner": "30.0.4", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^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": ">=10" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/eslint/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": { - "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" + "p-locate": "^5.0.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/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/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": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "brace-expansion": "^1.1.7" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": "*" } }, - "node_modules/jest-config/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 - }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/eslint/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": { - "brace-expansion": "^2.0.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/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/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-ctrf-json-reporter": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/jest-ctrf-json-reporter/-/jest-ctrf-json-reporter-0.0.9.tgz", - "integrity": "sha512-mxuUPZ/npUEoPyjE/vHQhdNVhuxV3NzdIPIWjoreW4rMisaDKlD7x4oERU4HEC9mlI7dQkwcOHo9BJzGIgieKw==", - "dev": true - }, - "node_modules/jest-diff": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.4.tgz", - "integrity": "sha512-TSjceIf6797jyd+R64NXqicttROD+Qf98fex7CowmlSn7f8+En0da1Dglwr1AXxDtVizoxXYZBlUQwNhoOXkNw==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "pretty-format": "30.0.2" + "estraverse": "^5.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10" } }, - "node_modules/jest-docblock": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", - "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "detect-newline": "^3.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4.0" } }, - "node_modules/jest-each": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.2.tgz", - "integrity": "sha512-ZFRsTpe5FUWFQ9cWTMguCaiA6kkW5whccPy9JjD1ezxh+mJeqmz8naL8Fl/oSbNJv3rgB0x87WBIkA5CObIUZQ==", + "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, - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.1", - "chalk": "^4.1.2", - "jest-util": "30.0.2", - "pretty-format": "30.0.2" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4.0" } }, - "node_modules/jest-environment-node": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.4.tgz", - "integrity": "sha512-p+rLEzC2eThXqiNh9GHHTC0OW5Ca4ZfcURp7scPjYBcmgpR9HG6750716GuUipYf2AcThU3k20B31USuiaaIEg==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/environment": "30.0.4", - "@jest/fake-timers": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-mock": "30.0.2", - "jest-util": "30.0.2", - "jest-validate": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@types/estree": "^1.0.0" } }, - "node_modules/jest-haste-map": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.2.tgz", - "integrity": "sha512-telJBKpNLeCb4MaX+I5k496556Y2FiKR/QLZc0+MGBYl4k3OO0472drlV2LUe7c1Glng5HuAu+5GLYp//GpdOQ==", + "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, - "dependencies": { - "@jest/types": "30.0.1", - "@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.0.2", - "jest-worker": "30.0.2", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "node": ">=0.10.0" } }, - "node_modules/jest-leak-detector": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.2.tgz", - "integrity": "sha512-U66sRrAYdALq+2qtKffBLDWsQ/XoNNs2Lcr83sc9lvE/hEpNafJlq2lXCPUBMNqamMECNxSIekLfe69qg4KMIQ==", + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, - "dependencies": { - "@jest/get-type": "30.0.1", - "pretty-format": "30.0.2" - }, + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.0.0" } }, - "node_modules/jest-matcher-utils": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.4.tgz", - "integrity": "sha512-ubCewJ54YzeAZ2JeHHGVoU+eDIpQFsfPQs0xURPWoNiO42LGJ+QGgfSf+hFIRplkZDkhH5MOvuxHKXRTUU3dUQ==", + "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": { - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "jest-diff": "30.0.4", - "pretty-format": "30.0.2" + "@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": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8.6.0" } }, - "node_modules/jest-message-util": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.2.tgz", - "integrity": "sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==", + "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": "ISC", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.1", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jest-mock": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.2.tgz", - "integrity": "sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==", + "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 + }, + "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.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { - "@jest/types": "30.0.1", - "@types/node": "*", - "jest-util": "30.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "reusify": "^1.0.4" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "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, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "engines": { + "node": ">=16.0.0" } }, - "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/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, + "dependencies": { + "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-resolve": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.2.tgz", - "integrity": "sha512-q/XT0XQvRemykZsvRopbG6FQUT6/ra+XV6rPijyjT6D0msOyCvR2A5PlWZLd+fH0U8XWKZfDiAgrUNDNX2BkCw==", + "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": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.2", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.2", - "jest-validate": "30.0.2", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" + "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-resolve-dependencies": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.4.tgz", - "integrity": "sha512-EQBYow19B/hKr4gUTn+l8Z+YLlP2X0IoPyp0UydOtrcPbIOYzJ8LKdFd+yrbwztPQvmlBFUwGPPEzHH1bAvFAw==", + "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==", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.0.4" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runner": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.4.tgz", - "integrity": "sha512-mxY0vTAEsowJwvFJo5pVivbCpuu6dgdXRmt3v3MXjBxFly7/lTk3Td0PaMyGOeNQUFmSuGEsGYqhbn7PA9OekQ==", + "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, - "dependencies": { - "@jest/console": "30.0.4", - "@jest/environment": "30.0.4", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.4", - "jest-haste-map": "30.0.2", - "jest-leak-detector": "30.0.2", - "jest-message-util": "30.0.2", - "jest-resolve": "30.0.2", - "jest-runtime": "30.0.4", - "jest-util": "30.0.2", - "jest-watcher": "30.0.4", - "jest-worker": "30.0.2", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "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-runtime": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.4.tgz", - "integrity": "sha512-tUQrZ8+IzoZYIHoPDQEB4jZoPyzBjLjq7sk0KVyd5UPRjRDOsN7o6UlvaGF8ddpGsjznl9PW+KRgWqCNO+Hn7w==", + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@jest/environment": "30.0.4", - "@jest/fake-timers": "30.0.4", - "@jest/globals": "30.0.4", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "@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.0.2", - "jest-message-util": "30.0.2", - "jest-mock": "30.0.2", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.2", - "jest-snapshot": "30.0.4", - "jest-util": "30.0.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "resolve-pkg-maps": "^1.0.0" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime/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/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": { - "@isaacs/cliui": "^8.0.2" + "is-glob": "^4.0.3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "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" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-runtime/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 + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/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": { - "brace-expansion": "^2.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-runtime/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/import-fresh/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, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/jest-snapshot": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.4.tgz", - "integrity": "sha512-S/8hmSkeUib8WRUq9pWEb5zMfsOjiYWDWzFzKnjX7eDyKKgimsu9hcmsUEg8a7dPAw8s/FacxsXquq71pDgPjQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "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.0.4", - "@jest/get-type": "30.0.1", - "@jest/snapshot-utils": "30.0.4", - "@jest/transform": "30.0.4", - "@jest/types": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0", - "chalk": "^4.1.2", - "expect": "30.0.4", - "graceful-fs": "^4.2.11", - "jest-diff": "30.0.4", - "jest-matcher-utils": "30.0.4", - "jest-message-util": "30.0.2", - "jest-util": "30.0.2", - "pretty-format": "30.0.2", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.8.19" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "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, - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "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==", + "engines": { + "node": ">=8" } }, - "node_modules/jest-util": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.2.tgz", - "integrity": "sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==", + "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": { - "@jest/types": "30.0.1", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "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, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=0.12.0" } }, - "node_modules/jest-validate": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.2.tgz", - "integrity": "sha512-noOvul+SFER4RIvNAwGn6nmV2fXqBq67j+hKGHKGFCmK4ks/Iy1FSrqQNBLGKlu4ZZIRL6Kg1U72N1nxuRCrGQ==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "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, - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.1", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.0.2" - }, + "license": "BSD-3-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "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/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" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-watcher": { - "version": "30.0.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.4.tgz", - "integrity": "sha512-YESbdHDs7aQOCSSKffG8jXqOKFqw4q4YqR+wHYpR5GWEQioGvL0BfbcjvKIvPEM0XGfsfJrka7jJz3Cc3gI4VQ==", + "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": { - "@jest/test-result": "30.0.4", - "@jest/types": "30.0.1", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.0.2", - "string-length": "^4.0.2" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest-worker": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.2.tgz", - "integrity": "sha512-RN1eQmx7qSLFA+o9pfJKlqViwL5wt+OL3Vff/A+/cPsmuw7NPwfgl33AP+/agRmHzPOFgXviRycR9kYwlcRQXg==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.2", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.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==", - "dev": true, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dependencies": { - "has-flag": "^4.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, "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 + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "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, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } + "license": "MIT" }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "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, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } + "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 + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "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, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" + "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==", + "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": ">=6" + "node": ">= 0.8.0" } }, - "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 - }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -3992,23 +2855,19 @@ "uc.micro": "^2.0.0" } }, - "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/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, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } + "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 + "node_modules/loupe": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "dev": true, + "license": "MIT" }, "node_modules/lru-cache": { "version": "11.1.0", @@ -4024,11 +2883,34 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "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" }, @@ -4039,33 +2921,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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 - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -4095,11 +2950,15 @@ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, - "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 + "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", @@ -4114,15 +2973,6 @@ "node": ">=8.6" } }, - "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, - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", @@ -4151,19 +3001,23 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/napi-postinstall": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", - "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "bin": { - "napi-postinstall": "lib/cli.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/natural-compare": { @@ -4172,135 +3026,55 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "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 - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "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==", + "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": { - "mimic-fn": "^2.1.0" + "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": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/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==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "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, - "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==" }, - "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/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": { - "@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" + "callsites": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/path-exists": { @@ -4312,15 +3086,6 @@ "node": ">=8" } }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4344,6 +3109,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4362,51 +3144,69 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "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/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": ">= 6" + "node": "^10 || ^12 || >=14" } }, - "node_modules/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/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, - "dependencies": { - "find-up": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/pretty-format": { - "version": "30.0.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.2.tgz", - "integrity": "sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==", + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, - "dependencies": { - "@jest/schemas": "30.0.1", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", + "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", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, "node_modules/punycode.js": { @@ -4418,74 +3218,124 @@ "node": ">=6" } }, - "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==", + "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": "individual", - "url": "https://github.com/sponsors/dubzzz" + "type": "github", + "url": "https://github.com/sponsors/feross" }, { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true + "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, + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } }, - "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/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { + "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "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/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, + "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "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": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "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": { + "queue-microtask": "^1.2.2" } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -4507,6 +3357,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -4518,85 +3375,29 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/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, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/string-width": { "version": "5.1.2", @@ -4686,24 +3487,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4716,6 +3499,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4728,193 +3524,219 @@ "node": ">=8" } }, - "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, + "license": "ISC", "dependencies": { - "@pkgr/core": "^0.2.4" + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "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://opencollective.com/synckit" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/test-exclude/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": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/test-exclude/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/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/test-exclude/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": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "license": "MIT" }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "fdir": "^6.4.4", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=8.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/ts-jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, + "license": "MIT", "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" + "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { + "picomatch": { "optional": true } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=14.0.0" } }, - "node_modules/ts-jest/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/tinyspy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "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==", + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "optional": true + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } }, "node_modules/tsx": { "version": "4.20.3", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -4929,34 +3751,27 @@ "fsevents": "~2.3.3" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8.0" } }, "node_modules/typedoc": { - "version": "0.28.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.8.tgz", - "integrity": "sha512-16GfLopc8icHfdvqZDqdGBoS2AieIRP2rpf9mU+MgN+gGLyEQvAO0QgOa6NJ5QNmQi0LFrDY9in4F2fUNKgJKA==", + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.9.tgz", + "integrity": "sha512-aw45vwtwOl3QkUAmWCnLV9QW1xY+FSX2zzlit4MAfE99wX+Jij4ycnpbAWgBXsRrxmfs9LaYktg/eX5Bpthd3g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@gerrit0/mini-shiki": "^3.7.0", + "@gerrit0/mini-shiki": "^3.9.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", @@ -4970,7 +3785,20 @@ "pnpm": ">= 10" }, "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.0.tgz", + "integrity": "sha512-BQqXnT9PETe6WEFf8bcsvvGEGQHbwTo/BFyY+RUIsSB05Y0Wn56iF+fK1PY2OKJJIhV4kp4dp7osaP9Bm5a0Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.28.x" } }, "node_modules/typedoc/node_modules/minimatch": { @@ -5001,6 +3829,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", + "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.0", + "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -5013,91 +3865,226 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "hasInstallScript": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "dev": true, + "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://opencollective.com/unrs-resolver" + "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "jiti": { + "optional": true }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } - ], + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { - "update-browserslist-db": "cli.js" + "vite-node": "vite-node.mjs" }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", "peerDependencies": { - "browserslist": ">= 4.21.0" + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/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/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=10.12.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "dependencies": { - "makeerror": "1.0.12" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/which": { @@ -5114,6 +4101,33 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -5198,25 +4212,6 @@ "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5225,12 +4220,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/yaml": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", diff --git a/package.json b/package.json index f62a4f1..fa261a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ctrf", - "version": "0.0.13-next.0", + "version": "0.0.13", "description": "Common library for working with CTRF reports", "type": "module", "main": "dist/index.js", @@ -12,24 +12,26 @@ "node": ">=20.19.0" }, "scripts": { - "build": "tsc", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "test:e2e": "jest --testPathPattern=e2e", - "enrich-reports": "tsx scripts/enrich-reports.ts", + "build": "tsc -p .", + "build:check": "tsc -p . -noEmit", + "build:watch": "tsc -p . --watch", + "clean": "rm -rf dist && rm -rf coverage && rm -rf ctrf", + "test": "vitest run", + "test:coverage": "vitest run --coverage", + "lint": "eslint . --ext .ts --fix", + "lint:check": "eslint . --ext .ts", + "format": "prettier --write .", + "format:check": "prettier --check .", "docs": "typedoc", - "docs:watch": "typedoc --watch" + "docs:watch": "typedoc --watch", + "all": "npm run build:check && npm run test:coverage && npm run lint && npm run format && npm run docs && npm run build" }, "files": [ "dist/", "types/", "README.md" ], - "repository": { - "type": "git", - "url": "https://github.com/ctrf-io/ctrf-cli" - }, + "repository": "github:ctrf-io/ctrf-core-js", "homepage": "https://ctrf.io", "author": "Matthew Thomas", "license": "MIT", @@ -39,15 +41,17 @@ "yargs": "^18.0.0" }, "devDependencies": { - "@types/jest": "^30.0.0", + "@d2t/vitest-ctrf-json-reporter": "^1.2.0", + "@eslint/js": "^9.32.0", "@types/node": "^20.12.7", "@types/yargs": "^17.0.32", - "jest": "^30.0.4", - "jest-ctrf-json-reporter": "^0.0.9", - "jest-environment-node": "^30.0.4", - "ts-jest": "^29.4.0", - "tsx": "^4.19.2", - "typedoc": "^0.28.8", - "typescript": "^5.8.3" + "@vitest/coverage-v8": "^3.2.4", + "eslint": "^9.32.0", + "prettier": "^3.5.3", + "typedoc": "^0.28.9", + "typedoc-plugin-markdown": "^4.8.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vitest": "^3.2.4" } } diff --git a/src/__tests__/read-reports.test.ts b/src/__tests__/read-reports.test.ts deleted file mode 100644 index c4b611e..0000000 --- a/src/__tests__/read-reports.test.ts +++ /dev/null @@ -1,235 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { readSingleReport, readReportsFromDirectory, readReportsFromGlobPattern } from '../methods/read-reports'; -import { Report } from '../../types/ctrf'; - -// Mock fs module for testing -jest.mock('fs'); -jest.mock('glob'); - -const mockFs = fs as jest.Mocked; -const mockGlob = require('glob') as jest.Mocked; - -describe('read-reports', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('readSingleReport', () => { - const validCtrfReport: Report = { - reportFormat: 'ctrf', - specVersion: '1.0.0', - results: { - tool: { name: 'jest' }, - summary: { - tests: 1, - passed: 1, - failed: 0, - skipped: 0, - pending: 0, - other: 0, - start: Date.now(), - stop: Date.now() + 1000 - }, - tests: [ - { - name: 'test1', - status: 'passed', - duration: 100 - } - ] - } - }; - - it('should read and parse a valid CTRF report file', () => { - const filePath = '/path/to/report.json'; - mockFs.existsSync.mockReturnValue(true); - mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)); - - const result = readSingleReport(filePath); - - expect(result).toEqual(validCtrfReport); - expect(mockFs.readFileSync).toHaveBeenCalledWith(path.resolve(filePath), 'utf8'); - }); - - it('should throw error when file does not exist', () => { - const filePath = '/path/to/nonexistent.json'; - mockFs.existsSync.mockReturnValue(false); - - expect(() => readSingleReport(filePath)).toThrow('JSON file not found: /path/to/nonexistent.json'); - }); - - it('should throw error when file is not valid JSON', () => { - const filePath = '/path/to/invalid.json'; - mockFs.existsSync.mockReturnValue(true); - mockFs.readFileSync.mockReturnValue('invalid json'); - - expect(() => readSingleReport(filePath)).toThrow(/Failed to read or parse the file/); - }); - - it('should throw error when file is not a valid CTRF report', () => { - const filePath = '/path/to/invalid-ctrf.json'; - mockFs.existsSync.mockReturnValue(true); - mockFs.readFileSync.mockReturnValue(JSON.stringify({ invalid: 'data' })); - - expect(() => readSingleReport(filePath)).toThrow(/is not a valid CTRF report/); - }); - }); - - describe('readReportsFromDirectory', () => { - it('should read valid CTRF reports from directory', () => { - const directoryPath = '/path/to/reports'; - const validCtrfReport: Report = { - reportFormat: 'ctrf', - specVersion: '1.0.0', - results: { - tool: { name: 'jest' }, - summary: { - tests: 1, - passed: 1, - failed: 0, - skipped: 0, - pending: 0, - other: 0, - start: Date.now(), - stop: Date.now() + 1000 - }, - tests: [ - { - name: 'test1', - status: 'passed', - duration: 100 - } - ] - } - }; - - mockFs.existsSync.mockReturnValue(true); - mockFs.readdirSync.mockReturnValue(['report1.json', 'report2.json', 'not-json.txt'] as any); - mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)); - - const result = readReportsFromDirectory(directoryPath); - - expect(result).toHaveLength(2); - expect(result[0]).toEqual(validCtrfReport); - expect(result[1]).toEqual(validCtrfReport); - }); - - it('should throw error when directory does not exist', () => { - const directoryPath = '/path/to/nonexistent'; - mockFs.existsSync.mockReturnValue(false); - - expect(() => readReportsFromDirectory(directoryPath)).toThrow(/does not exist/); - }); - - it('should throw error when no valid CTRF reports are found', () => { - const directoryPath = '/path/to/empty'; - mockFs.existsSync.mockReturnValue(true); - mockFs.readdirSync.mockReturnValue(['not-json.txt'] as any); - - expect(() => readReportsFromDirectory(directoryPath)).toThrow(/No valid CTRF reports found/); - }); - - it('should skip invalid files and warn about them', () => { - const directoryPath = '/path/to/mixed'; - const validCtrfReport: Report = { - reportFormat: 'ctrf', - specVersion: '1.0.0', - results: { - tool: { name: 'jest' }, - summary: { - tests: 1, - passed: 1, - failed: 0, - skipped: 0, - pending: 0, - other: 0, - start: Date.now(), - stop: Date.now() + 1000 - }, - tests: [ - { - name: 'test1', - status: 'passed', - duration: 100 - } - ] - } - }; - - mockFs.existsSync.mockReturnValue(true); - mockFs.readdirSync.mockReturnValue(['valid.json', 'invalid.json'] as any); - mockFs.readFileSync - .mockReturnValueOnce(JSON.stringify(validCtrfReport)) - .mockReturnValueOnce(JSON.stringify({ invalid: 'data' })); - - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - const result = readReportsFromDirectory(directoryPath); - - expect(result).toHaveLength(1); - expect(result[0]).toEqual(validCtrfReport); - expect(consoleSpy).toHaveBeenCalledWith('Skipping invalid CTRF report file: invalid.json'); - - consoleSpy.mockRestore(); - }); - }); - - describe('readReportsFromGlobPattern', () => { - it('should read valid CTRF reports from glob pattern', () => { - const pattern = 'reports/*.json'; - const validCtrfReport: Report = { - reportFormat: 'ctrf', - specVersion: '1.0.0', - results: { - tool: { name: 'jest' }, - summary: { - tests: 1, - passed: 1, - failed: 0, - skipped: 0, - pending: 0, - other: 0, - start: Date.now(), - stop: Date.now() + 1000 - }, - tests: [ - { - name: 'test1', - status: 'passed', - duration: 100 - } - ] - } - }; - - mockGlob.sync.mockReturnValue(['report1.json', 'report2.json']); - mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)); - - const result = readReportsFromGlobPattern(pattern); - - expect(result).toHaveLength(2); - expect(result[0]).toEqual(validCtrfReport); - expect(result[1]).toEqual(validCtrfReport); - }); - - it('should throw error when no files match the pattern', () => { - const pattern = 'nonexistent/*.json'; - mockGlob.sync.mockReturnValue([]); - - expect(() => readReportsFromGlobPattern(pattern)).toThrow(/No files found matching the pattern/); - }); - - it('should throw error when no valid CTRF reports are found', () => { - const pattern = 'invalid/*.json'; - mockGlob.sync.mockReturnValue(['invalid.json']); - mockFs.readFileSync.mockReturnValue(JSON.stringify({ invalid: 'data' })); - - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - - expect(() => readReportsFromGlobPattern(pattern)).toThrow(/No valid CTRF reports found/); - - consoleSpy.mockRestore(); - }); - }); -}); \ No newline at end of file diff --git a/src/__tests__/run-insights.test.ts b/src/__tests__/run-insights.test.ts deleted file mode 100644 index eacba73..0000000 --- a/src/__tests__/run-insights.test.ts +++ /dev/null @@ -1,371 +0,0 @@ -// Test the public API and utility functions -import { - isTestFlaky, - formatAsPercentage, - formatInsightsMetricAsPercentage, - enrichReportWithInsights -} from '../methods/run-insights'; - -import { Report, Test, InsightsMetric } from '../../types/ctrf.js'; -import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from '../constants'; - -// Mock data helpers -const createMockTest = (overrides: Partial = {}): Test => ({ - name: 'test-name', - status: 'passed', - duration: 100, - ...overrides -}); - -const createMockReport = (tests: Test[] = [], overrides: Partial = {}): Report => ({ - reportFormat: CTRF_REPORT_FORMAT, - specVersion: CTRF_SPEC_VERSION, - results: { - tool: { name: 'jest' }, - summary: { - tests: tests.length, - passed: tests.filter(t => t.status === 'passed').length, - failed: tests.filter(t => t.status === 'failed').length, - skipped: tests.filter(t => t.status === 'skipped').length, - pending: tests.filter(t => t.status === 'pending').length, - other: tests.filter(t => t.status === 'other').length, - start: Date.now(), - stop: Date.now() + 1000 - }, - tests - }, - ...overrides -}); - -describe('run-insights utility functions', () => { - describe('isTestFlaky', () => { - it('should return true for explicitly flaky tests', () => { - const test = createMockTest({ flaky: true }); - expect(isTestFlaky(test)).toBe(true); - }); - - it('should return true for passed tests with retries', () => { - const test = createMockTest({ - status: 'passed', - retries: 2 - }); - expect(isTestFlaky(test)).toBe(true); - }); - - it('should return false for failed tests with retries', () => { - const test = createMockTest({ - status: 'failed', - retries: 2 - }); - expect(isTestFlaky(test)).toBe(false); - }); - - it('should return false for passed tests without retries', () => { - const test = createMockTest({ - status: 'passed', - retries: 0 - }); - expect(isTestFlaky(test)).toBe(false); - }); - - it('should return false for passed tests without retries property', () => { - const test = createMockTest({ status: 'passed' }); - expect(isTestFlaky(test)).toBe(false); - }); - }); - - describe('formatAsPercentage', () => { - it('should format ratio as percentage with default 2 decimals', () => { - expect(formatAsPercentage(0.25)).toBe('25.00%'); - expect(formatAsPercentage(0.5)).toBe('50.00%'); - expect(formatAsPercentage(0.75)).toBe('75.00%'); - expect(formatAsPercentage(1)).toBe('100.00%'); - }); - - it('should format ratio as percentage with custom decimals', () => { - expect(formatAsPercentage(0.25, 1)).toBe('25.0%'); - expect(formatAsPercentage(0.25, 0)).toBe('25%'); - expect(formatAsPercentage(0.25, 3)).toBe('25.000%'); - }); - - it('should handle edge cases', () => { - expect(formatAsPercentage(0)).toBe('0.00%'); - expect(formatAsPercentage(0.00001)).toBe('0.00%'); - expect(formatAsPercentage(0.999)).toBe('99.90%'); - }); - }); - - describe('formatInsightsMetricAsPercentage', () => { - it('should format all metric values as percentages', () => { - const metric: InsightsMetric = { - current: 0.25, - previous: 0.2, - change: 0.05 - }; - - const result = formatInsightsMetricAsPercentage(metric); - - expect(result).toEqual({ - current: '25.00%', - previous: '20.00%', - change: '+5.00%' - }); - }); - - it('should handle negative change values', () => { - const metric: InsightsMetric = { - current: 0.15, - previous: 0.2, - change: -0.05 - }; - - const result = formatInsightsMetricAsPercentage(metric); - - expect(result).toEqual({ - current: '15.00%', - previous: '20.00%', - change: '-5.00%' - }); - }); - - it('should handle zero change values', () => { - const metric: InsightsMetric = { - current: 0.2, - previous: 0.2, - change: 0 - }; - - const result = formatInsightsMetricAsPercentage(metric); - - expect(result).toEqual({ - current: '20.00%', - previous: '20.00%', - change: '+0.00%' - }); - }); - }); -}); - -describe('enrichReportWithInsights - Main API', () => { - describe('basic functionality', () => { - it('should enrich a report with run-level insights when no previous reports', () => { - const tests = [ - createMockTest({ name: 'test1', status: 'passed', duration: 100 }), - createMockTest({ name: 'test2', status: 'failed', duration: 200 }), - createMockTest({ name: 'test3', status: 'passed', retries: 1, flaky: true, duration: 150 }) - ]; - const currentReport = createMockReport(tests); - - const result = enrichReportWithInsights(currentReport, []); - - expect(result.insights).toBeDefined(); - expect(result.insights!.flakyRate.current).toBeGreaterThan(0); - expect(result.insights!.failRate.current).toBeGreaterThan(0); - expect(result.insights!.averageTestDuration.current).toBe(150); // (100+200+150)/3 - expect(result.insights!.reportsAnalyzed).toBe(1); - - // Should have no baseline comparison (previous/change should be 0) - expect(result.insights!.flakyRate.previous).toBe(0); - expect(result.insights!.flakyRate.change).toBe(0); - }); - - it('should add test-level insights to each test', () => { - const tests = [ - createMockTest({ name: 'test1', status: 'passed', duration: 100 }), - createMockTest({ name: 'test2', status: 'failed', duration: 200 }) - ]; - const currentReport = createMockReport(tests); - - const result = enrichReportWithInsights(currentReport, []); - - expect(result.results.tests[0].insights).toBeDefined(); - expect(result.results.tests[1].insights).toBeDefined(); - - expect(result.results.tests[0].insights!.failRate.current).toBe(0); - expect(result.results.tests[1].insights!.failRate.current).toBe(1); - expect(result.results.tests[0].insights!.averageTestDuration.current).toBe(100); - expect(result.results.tests[1].insights!.averageTestDuration.current).toBe(200); - }); - }); - - describe('baseline comparison functionality', () => { - it('should compare against previous report by default', () => { - const currentTests = [ - createMockTest({ name: 'test1', status: 'passed', duration: 100 }), - createMockTest({ name: 'test2', status: 'passed', duration: 200 }) - ]; - const previousTests = [ - createMockTest({ name: 'test1', status: 'failed', duration: 150 }), - createMockTest({ name: 'test2', status: 'failed', duration: 250 }) - ]; - - const currentReport = createMockReport(currentTests, { - results: { - ...createMockReport(currentTests).results, - summary: { ...createMockReport(currentTests).results.summary, start: 2000 } - } - }); - const previousReport = createMockReport(previousTests, { - results: { - ...createMockReport(previousTests).results, - summary: { ...createMockReport(previousTests).results.summary, start: 1000 } - } - }); - - const result = enrichReportWithInsights(currentReport, [previousReport]); - - expect(result.insights).toBeDefined(); - expect(result.insights!.failRate.current).toBe(0.5); // Overall: 2 passed + 2 failed = 50% fail rate - expect(result.insights!.failRate.previous).toBe(1); // Previous: all failed - expect(result.insights!.failRate.change).toBe(-0.5); // Improvement of 50% - expect(result.insights!.reportsAnalyzed).toBe(2); - }); - - it('should handle baseline by index', () => { - const currentTests = [createMockTest({ name: 'test1', status: 'passed' })]; - const previous1Tests = [createMockTest({ name: 'test1', status: 'failed' })]; - const previous2Tests = [createMockTest({ name: 'test1', status: 'passed' })]; - - const currentReport = createMockReport(currentTests); - const previousReport1 = createMockReport(previous1Tests); - const previousReport2 = createMockReport(previous2Tests); - - // Use index 1 (second previous report) - const result = enrichReportWithInsights(currentReport, [previousReport1, previousReport2], 1); - - expect(result.insights!.failRate.current).toBe(0.3333); // 1 passed + 1 failed + 1 passed = 1/3 fail rate - expect(result.insights!.failRate.previous).toBe(0); // previous2 had no failures - expect(result.insights!.failRate.change).toBe(0.3333); - }); - - it('should handle baseline by report ID', () => { - const currentTests = [createMockTest({ name: 'test1', status: 'passed' })]; - const previousTests = [createMockTest({ name: 'test1', status: 'failed' })]; - - const currentReport = createMockReport(currentTests); - const previousReport = createMockReport(previousTests, { - results: { - ...createMockReport(previousTests).results, - summary: { ...createMockReport(previousTests).results.summary, start: 12345 } - } - }); - - const result = enrichReportWithInsights(currentReport, [previousReport], "12345"); - - expect(result.insights!.failRate.current).toBe(0.5); // 1 passed + 1 failed = 50% fail rate - expect(result.insights!.failRate.previous).toBe(1); - expect(result.insights!.failRate.change).toBe(-0.5); - }); - - it('should handle invalid baseline gracefully', () => { - const currentTests = [createMockTest({ name: 'test1', status: 'passed' })]; - const previousTests = [createMockTest({ name: 'test1', status: 'failed' })]; - - const currentReport = createMockReport(currentTests); - const previousReport = createMockReport(previousTests); - - // Invalid index - const result1 = enrichReportWithInsights(currentReport, [previousReport], 999); - expect(result1.insights!.failRate.previous).toBe(0); - expect(result1.insights!.failRate.change).toBe(0); - - // Invalid ID - const result2 = enrichReportWithInsights(currentReport, [previousReport], "invalid-id"); - expect(result2.insights!.failRate.previous).toBe(0); - expect(result2.insights!.failRate.change).toBe(0); - }); - }); - - describe('added/removed tests tracking', () => { - it('should track tests added since baseline', () => { - const currentTests = [ - createMockTest({ name: 'test1', status: 'passed' }), - createMockTest({ name: 'test2', status: 'passed' }), // New test - createMockTest({ name: 'test3', status: 'passed' }) // New test - ]; - const previousTests = [ - createMockTest({ name: 'test1', status: 'passed' }) - ]; - - const currentReport = createMockReport(currentTests); - const previousReport = createMockReport(previousTests); - - const result = enrichReportWithInsights(currentReport, [previousReport]); - - expect(result.insights!.extra!.testsAdded).toHaveLength(2); - expect((result.insights!.extra!.testsAdded as any[])[0].name).toBe('test2'); - expect((result.insights!.extra!.testsAdded as any[])[1].name).toBe('test3'); - }); - - it('should track tests removed since baseline', () => { - const currentTests = [ - createMockTest({ name: 'test1', status: 'passed' }) - ]; - const previousTests = [ - createMockTest({ name: 'test1', status: 'passed' }), - createMockTest({ name: 'test2', status: 'passed' }), // Removed test - createMockTest({ name: 'test3', status: 'passed' }) // Removed test - ]; - - const currentReport = createMockReport(currentTests); - const previousReport = createMockReport(previousTests); - - const result = enrichReportWithInsights(currentReport, [previousReport]); - - expect(result.insights!.extra!.testsRemoved).toHaveLength(2); - expect((result.insights!.extra!.testsRemoved as any[])[0].name).toBe('test2'); - expect((result.insights!.extra!.testsRemoved as any[])[1].name).toBe('test3'); - }); - - it('should handle single report no added/removed tests', () => { - const currentTests = [createMockTest({ name: 'test1', status: 'passed' })]; - - const currentReport = createMockReport(currentTests); - const result = enrichReportWithInsights(currentReport, []); - - expect(result.insights!.extra!.testsAdded).toBeUndefined(); - expect(result.insights!.extra!.testsRemoved).toBeUndefined(); - }); - }); - - describe('edge cases', () => { - it('should handle empty current report', () => { - const currentReport = createMockReport([]); - const previousReport = createMockReport([createMockTest()]); - - const result = enrichReportWithInsights(currentReport, [previousReport]); - - expect(result.insights).toBeDefined(); - expect(result.insights!.failRate.current).toBe(0); // Previous report has 1 test, current has 0, so 0 failed overall - expect(result.insights!.averageTestDuration.current).toBe(100); // Previous report had 1 test with 100ms duration - }); - - it('should handle invalid current report', () => { - const invalidReport = { results: null } as any; - - const result = enrichReportWithInsights(invalidReport, []); - - expect(result).toBe(invalidReport); // Should return unchanged - }); - - it('should handle multiple previous reports', () => { - const currentTests = [createMockTest({ name: 'test1', status: 'passed' })]; - const previous1Tests = [createMockTest({ name: 'test1', status: 'failed' })]; - const previous2Tests = [createMockTest({ name: 'test1', status: 'passed' })]; - const previous3Tests = [createMockTest({ name: 'test1', status: 'failed' })]; - - const currentReport = createMockReport(currentTests); - const previousReports = [ - createMockReport(previous1Tests), - createMockReport(previous2Tests), - createMockReport(previous3Tests) - ]; - - const result = enrichReportWithInsights(currentReport, previousReports); - - expect(result.insights!.reportsAnalyzed).toBe(4); // Current + 3 previous - expect(result.insights!.failRate.current).toBeDefined(); - expect(result.insights!.failRate.previous).toBeDefined(); - }); - }); -}); \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts index 2ccf88d..3564acf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,15 +1,15 @@ #!/usr/bin/env node -import yargs from 'yargs/yargs'; -import { hideBin } from 'yargs/helpers'; -import { mergeReports } from './merge.js'; -import { identifyFlakyTests } from './flaky.js'; +import yargs from 'yargs/yargs' +import { hideBin } from 'yargs/helpers' +import { mergeReports } from './merge.js' +import { identifyFlakyTests } from './flaky.js' const argv = yargs(hideBin(process.argv)) .command( 'merge ', 'Merge CTRF reports into a single report', - (yargs) => { + yargs => { return yargs .positional('directory', { describe: 'Directory of the CTRF reports', @@ -32,27 +32,30 @@ const argv = yargs(hideBin(process.argv)) describe: 'Keep existing reports after merging', type: 'boolean', default: false, - }); + }) }, - async (argv) => { - await mergeReports(argv.directory as string, argv.output as string, argv['output-dir'] as string, argv['keep-reports'] as boolean); + async argv => { + await mergeReports( + argv.directory as string, + argv.output as string, + argv['output-dir'] as string, + argv['keep-reports'] as boolean + ) } ) .command( 'flaky ', 'Identify flaky tests from a CTRF report file', - (yargs) => { - return yargs - .positional('file', { - describe: 'CTRF report file', - type: 'string', - demandOption: true, - }); + yargs => { + return yargs.positional('file', { + describe: 'CTRF report file', + type: 'string', + demandOption: true, + }) }, - async (argv) => { - await identifyFlakyTests(argv.file as string); + async argv => { + await identifyFlakyTests(argv.file as string) } ) .help() - .demandCommand(1, 'You need at least one command before moving on') - .argv; \ No newline at end of file + .demandCommand(1, 'You need at least one command before moving on').argv diff --git a/src/constants.ts b/src/constants.ts index e9c18a7..8970989 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,2 +1,2 @@ -export const CTRF_REPORT_FORMAT = 'ctrf'; -export const CTRF_SPEC_VERSION = '0.0.0'; \ No newline at end of file +export const CTRF_REPORT_FORMAT = 'CTRF' +export const CTRF_SPEC_VERSION = '0.0.0' diff --git a/src/flaky.ts b/src/flaky.ts index 108e48f..a095736 100644 --- a/src/flaky.ts +++ b/src/flaky.ts @@ -1,29 +1,32 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'fs' +import path from 'path' +import { Test } from '../types/ctrf.js' export async function identifyFlakyTests(filePath: string) { try { - const resolvedFilePath = path.resolve(filePath); + const resolvedFilePath = path.resolve(filePath) if (!fs.existsSync(resolvedFilePath)) { - console.error(`The file ${resolvedFilePath} does not exist.`); - return; + console.error(`The file ${resolvedFilePath} does not exist.`) + return } - const fileContent = fs.readFileSync(resolvedFilePath, 'utf8'); - const report = JSON.parse(fileContent); + const fileContent = fs.readFileSync(resolvedFilePath, 'utf8') + const report = JSON.parse(fileContent) - const flakyTests = report.results.tests.filter((test: any) => test.flaky === true); + const flakyTests = report.results.tests.filter( + (test: Test) => test.flaky === true + ) if (flakyTests.length > 0) { - console.log(`Found ${flakyTests.length} flaky test(s):`); - flakyTests.forEach((test: any) => { - console.log(`- Test Name: ${test.name}, Retries: ${test.retries}`); - }); + console.log(`Found ${flakyTests.length} flaky test(s):`) + flakyTests.forEach((test: Test) => { + console.log(`- Test Name: ${test.name}, Retries: ${test.retries}`) + }) } else { - console.log(`No flaky tests found in ${resolvedFilePath}.`); + console.log(`No flaky tests found in ${resolvedFilePath}.`) } } catch (error) { - console.error('Error identifying flaky tests:', error); + console.error('Error identifying flaky tests:', error) } } diff --git a/src/index.ts b/src/index.ts index 2a99cfd..abbf7ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,13 @@ -export { mergeReports } from './methods/merge-reports.js'; -export { readReportsFromDirectory } from './methods/read-reports.js'; -export { readReportsFromGlobPattern } from './methods/read-reports.js'; -export { - enrichReportWithInsights, -} from './methods/run-insights.js'; -export { storePreviousResults } from './methods/store-previous-results.js'; +export { mergeReports } from './methods/merge-reports.js' +export { readReportsFromDirectory } from './methods/read-reports.js' +export { readReportsFromGlobPattern } from './methods/read-reports.js' +export { enrichReportWithInsights } from './methods/run-insights.js' +export { + sortReportsByTimestamp, + SortOrder, +} from './methods/utilities/sort-reports.js' +export { storePreviousResults } from './methods/store-previous-results.js' +export { readReportFromFile } from './methods/read-reports.js' export type { Report, @@ -15,10 +18,9 @@ export type { Tool, Step, Attachment, - RetryAttempts, + RetryAttempt, Insights, TestInsights, InsightsMetric, - TestState -} from '../types/ctrf.js'; - + TestStatus, +} from '../types/ctrf.js' diff --git a/src/merge.ts b/src/merge.ts index cad75d6..38ba3d9 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -1,92 +1,108 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'fs' +import path from 'path' -export async function mergeReports(directory: string, output: string, outputDir: string, keepReports: boolean) { - try { - const directoryPath = path.resolve(directory); - const outputFileName = output; - const resolvedOutputDir = outputDir ? path.resolve(outputDir) : directoryPath; - const outputPath = path.join(resolvedOutputDir, outputFileName); +export async function mergeReports( + directory: string, + output: string, + outputDir: string, + keepReports: boolean +) { + try { + const directoryPath = path.resolve(directory) + const outputFileName = output + const resolvedOutputDir = outputDir + ? path.resolve(outputDir) + : directoryPath + const outputPath = path.join(resolvedOutputDir, outputFileName) - console.log("Merging CTRF reports..."); + console.log('Merging CTRF reports...') - const files = fs.readdirSync(directoryPath); + const files = fs.readdirSync(directoryPath) - files.forEach((file) => { - console.log('Found file:', file); - }); + files.forEach(file => { + console.log('Found file:', file) + }) - const ctrfReportFiles = files.filter((file) => { - try { - if (path.extname(file) !== '.json') { - console.log(`Skipping non-CTRF file: ${file}`); - return false; - } - const filePath = path.join(directoryPath, file); - const fileContent = fs.readFileSync(filePath, 'utf8'); - const jsonData = JSON.parse(fileContent); - if (!jsonData.hasOwnProperty('results')) { - console.log(`Skipping non-CTRF file: ${file}`); - return false; - } - return true; - } catch (error) { - console.error(`Error reading JSON file '${file}':`, error); - return false; - } - }); - - if (ctrfReportFiles.length === 0) { - console.log('No CTRF reports found in the specified directory.'); - return; + const ctrfReportFiles = files.filter(file => { + try { + if (path.extname(file) !== '.json') { + console.log(`Skipping non-CTRF file: ${file}`) + return false } - - if (!fs.existsSync(resolvedOutputDir)) { - fs.mkdirSync(resolvedOutputDir, { recursive: true }); - console.log(`Created output directory: ${resolvedOutputDir}`); + const filePath = path.join(directoryPath, file) + const fileContent = fs.readFileSync(filePath, 'utf8') + const jsonData = JSON.parse(fileContent) + if (!('results' in jsonData)) { + console.log(`Skipping non-CTRF file: ${file}`) + return false } + return true + } catch (error) { + console.error(`Error reading JSON file '${file}':`, error) + return false + } + }) + + if (ctrfReportFiles.length === 0) { + console.log('No CTRF reports found in the specified directory.') + return + } - const mergedReport = ctrfReportFiles - .map((file) => { - console.log("Merging report:", file); - const filePath = path.join(directoryPath, file); - const fileContent = fs.readFileSync(filePath, 'utf8'); - return JSON.parse(fileContent); - }) - .reduce((acc, curr) => { - if (!acc.results) { - return curr; - } + if (!fs.existsSync(resolvedOutputDir)) { + fs.mkdirSync(resolvedOutputDir, { recursive: true }) + console.log(`Created output directory: ${resolvedOutputDir}`) + } - acc.results.summary.tests += curr.results.summary.tests; - acc.results.summary.passed += curr.results.summary.passed; - acc.results.summary.failed += curr.results.summary.failed; - acc.results.summary.skipped += curr.results.summary.skipped; - acc.results.summary.pending += curr.results.summary.pending; - acc.results.summary.other += curr.results.summary.other; + const mergedReport = ctrfReportFiles + .map(file => { + console.log('Merging report:', file) + const filePath = path.join(directoryPath, file) + const fileContent = fs.readFileSync(filePath, 'utf8') + return JSON.parse(fileContent) + }) + .reduce( + (acc, curr) => { + if (!acc.results) { + return curr + } - acc.results.tests.push(...curr.results.tests); + acc.results.summary.tests += curr.results.summary.tests + acc.results.summary.passed += curr.results.summary.passed + acc.results.summary.failed += curr.results.summary.failed + acc.results.summary.skipped += curr.results.summary.skipped + acc.results.summary.pending += curr.results.summary.pending + acc.results.summary.other += curr.results.summary.other - acc.results.summary.start = Math.min(acc.results.summary.start, curr.results.summary.start); - acc.results.summary.stop = Math.max(acc.results.summary.stop, curr.results.summary.stop); + acc.results.tests.push(...curr.results.tests) - return acc; - }, { results: null }); + acc.results.summary.start = Math.min( + acc.results.summary.start, + curr.results.summary.start + ) + acc.results.summary.stop = Math.max( + acc.results.summary.stop, + curr.results.summary.stop + ) - fs.writeFileSync(outputPath, JSON.stringify(mergedReport, null, 2)); + return acc + }, + { results: null } + ) - if (!keepReports) { - ctrfReportFiles.forEach((file) => { - const filePath = path.join(directoryPath, file); - if (file !== outputFileName) { - fs.unlinkSync(filePath); - } - }); - } + fs.writeFileSync(outputPath, JSON.stringify(mergedReport, null, 2)) - console.log('CTRF reports merged successfully.'); - console.log(`Merged report saved to: ${outputPath}`); - } catch (error) { - console.error('Error merging CTRF reports:', error); + if (!keepReports) { + ctrfReportFiles.forEach(file => { + const filePath = path.join(directoryPath, file) + if (file !== outputFileName) { + fs.unlinkSync(filePath) + } + }) } + + console.log('CTRF reports merged successfully.') + console.log(`Merged report saved to: ${outputPath}`) + } catch (error) { + console.error('Error merging CTRF reports:', error) + } } diff --git a/src/methods/merge-reports.ts b/src/methods/merge-reports.ts index 3f19f55..c306fb8 100644 --- a/src/methods/merge-reports.ts +++ b/src/methods/merge-reports.ts @@ -1,5 +1,5 @@ -import { Report, Summary } from "../../types/ctrf.js"; -import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from "../constants.js"; +import { Report, Summary } from '../../types/ctrf.js' +import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from '../constants.js' /** * Merges multiple CTRF reports into a single report. @@ -9,7 +9,7 @@ import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from "../constants.js"; */ export function mergeReports(reports: Report[]): Report { if (!reports || reports.length === 0) { - throw new Error('No reports provided for merging.'); + throw new Error('No reports provided for merging.') } const mergedReport: Report = { @@ -20,50 +20,50 @@ export function mergeReports(reports: Report[]): Report { summary: initializeEmptySummary(), tests: [], }, - }; + } - reports.forEach((report) => { - const { summary, tests, environment, extra } = report.results; + reports.forEach(report => { + const { summary, tests, environment, extra } = report.results - mergedReport.results.summary.tests += summary.tests; - mergedReport.results.summary.passed += summary.passed; - mergedReport.results.summary.failed += summary.failed; - mergedReport.results.summary.skipped += summary.skipped; - mergedReport.results.summary.pending += summary.pending; - mergedReport.results.summary.other += summary.other; + mergedReport.results.summary.tests += summary.tests + mergedReport.results.summary.passed += summary.passed + mergedReport.results.summary.failed += summary.failed + mergedReport.results.summary.skipped += summary.skipped + mergedReport.results.summary.pending += summary.pending + mergedReport.results.summary.other += summary.other if (summary.suites !== undefined) { mergedReport.results.summary.suites = - (mergedReport.results.summary.suites || 0) + summary.suites; + (mergedReport.results.summary.suites || 0) + summary.suites } mergedReport.results.summary.start = Math.min( mergedReport.results.summary.start, summary.start - ); + ) mergedReport.results.summary.stop = Math.max( mergedReport.results.summary.stop, summary.stop - ); + ) - mergedReport.results.tests.push(...tests); + mergedReport.results.tests.push(...tests) if (environment) { mergedReport.results.environment = { ...mergedReport.results.environment, ...environment, - }; + } } if (extra) { mergedReport.results.extra = { ...mergedReport.results.extra, ...extra, - }; + } } - }); + }) - return mergedReport; + return mergedReport } /** @@ -79,7 +79,9 @@ function initializeEmptySummary(): Summary { skipped: 0, pending: 0, other: 0, + flaky: 0, + duration: 0, start: Number.MAX_SAFE_INTEGER, stop: 0, - }; + } } diff --git a/src/methods/read-reports.test.ts b/src/methods/read-reports.test.ts new file mode 100644 index 0000000..5d4f621 --- /dev/null +++ b/src/methods/read-reports.test.ts @@ -0,0 +1,271 @@ +import fs from 'fs' +import path from 'path' +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { + readReportFromFile, + readReportsFromDirectory, + readReportsFromGlobPattern, +} from './read-reports' +import { Report } from '../../types/ctrf' + +// Mock fs module for testing +vi.mock('fs') +vi.mock('glob') + +const mockFs = vi.mocked(fs) +const mockGlob = vi.mocked(await import('glob')) + +describe('read-reports', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('readSingleReport', () => { + const validCtrfReport: Report = { + reportFormat: 'CTRF', + specVersion: '1.0.0', + results: { + tool: { name: 'jest' }, + summary: { + tests: 1, + passed: 1, + failed: 0, + skipped: 0, + pending: 0, + other: 0, + flaky: 0, + start: Date.now(), + stop: Date.now() + 1000, + duration: 1000, + }, + tests: [ + { + name: 'test1', + status: 'passed', + duration: 100, + }, + ], + }, + } + + it('should read and parse a valid CTRF report file', () => { + const filePath = '/path/to/report.json' + mockFs.existsSync.mockReturnValue(true) + mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)) + + const result = readReportFromFile(filePath) + + expect(result).toEqual(validCtrfReport) + expect(mockFs.readFileSync).toHaveBeenCalledWith( + path.resolve(filePath), + 'utf8' + ) + }) + + it('should throw error when file does not exist', () => { + const filePath = '/path/to/nonexistent.json' + mockFs.existsSync.mockReturnValue(false) + + expect(() => readReportFromFile(filePath)).toThrow( + 'JSON file not found: /path/to/nonexistent.json' + ) + }) + + it('should throw error when file is not valid JSON', () => { + const filePath = '/path/to/invalid.json' + mockFs.existsSync.mockReturnValue(true) + mockFs.readFileSync.mockReturnValue('invalid json') + + expect(() => readReportFromFile(filePath)).toThrow( + /Failed to read or parse the file/ + ) + }) + + it('should throw error when file is not a valid CTRF report', () => { + const filePath = '/path/to/invalid-ctrf.json' + mockFs.existsSync.mockReturnValue(true) + mockFs.readFileSync.mockReturnValue(JSON.stringify({ invalid: 'data' })) + + expect(() => readReportFromFile(filePath)).toThrow( + /is not a valid CTRF report/ + ) + }) + }) + + describe('readReportsFromDirectory', () => { + it('should read valid CTRF reports from directory', () => { + const directoryPath = '/path/to/reports' + const validCtrfReport: Report = { + reportFormat: 'CTRF', + specVersion: '1.0.0', + results: { + tool: { name: 'jest' }, + summary: { + tests: 1, + passed: 1, + failed: 0, + skipped: 0, + pending: 0, + other: 0, + flaky: 0, + start: Date.now(), + stop: Date.now() + 1000, + duration: 1000, + }, + tests: [ + { + name: 'test1', + status: 'passed', + duration: 100, + }, + ], + }, + } + + mockFs.existsSync.mockReturnValue(true) + mockFs.readdirSync.mockReturnValue([ + 'report1.json', + 'report2.json', + 'not-json.txt', + ] as any) + mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)) + + const result = readReportsFromDirectory(directoryPath) + + expect(result).toHaveLength(2) + expect(result[0]).toEqual(validCtrfReport) + expect(result[1]).toEqual(validCtrfReport) + }) + + it('should throw error when directory does not exist', () => { + const directoryPath = '/path/to/nonexistent' + mockFs.existsSync.mockReturnValue(false) + + expect(() => readReportsFromDirectory(directoryPath)).toThrow( + /does not exist/ + ) + }) + + it('should throw error when no valid CTRF reports are found', () => { + const directoryPath = '/path/to/empty' + mockFs.existsSync.mockReturnValue(true) + mockFs.readdirSync.mockReturnValue(['not-json.txt'] as any) + + expect(() => readReportsFromDirectory(directoryPath)).toThrow( + /No valid CTRF reports found/ + ) + }) + + it('should skip invalid files and warn about them', () => { + const directoryPath = '/path/to/mixed' + const validCtrfReport: Report = { + reportFormat: 'CTRF', + specVersion: '1.0.0', + results: { + tool: { name: 'jest' }, + summary: { + tests: 1, + passed: 1, + failed: 0, + skipped: 0, + pending: 0, + other: 0, + flaky: 0, + start: Date.now(), + stop: Date.now() + 1000, + duration: 1000, + }, + tests: [ + { + name: 'test1', + status: 'passed', + duration: 100, + }, + ], + }, + } + + mockFs.existsSync.mockReturnValue(true) + mockFs.readdirSync.mockReturnValue(['valid.json', 'invalid.json'] as any) + mockFs.readFileSync + .mockReturnValueOnce(JSON.stringify(validCtrfReport)) + .mockReturnValueOnce(JSON.stringify({ invalid: 'data' })) + + const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + const result = readReportsFromDirectory(directoryPath) + + expect(result).toHaveLength(1) + expect(result[0]).toEqual(validCtrfReport) + expect(consoleSpy).toHaveBeenCalledWith( + 'Skipping invalid CTRF report file: invalid.json' + ) + + consoleSpy.mockRestore() + }) + }) + + describe('readReportsFromGlobPattern', () => { + it('should read valid CTRF reports from glob pattern', () => { + const pattern = 'reports/*.json' + const validCtrfReport: Report = { + reportFormat: 'CTRF', + specVersion: '1.0.0', + results: { + tool: { name: 'jest' }, + summary: { + tests: 1, + passed: 1, + failed: 0, + skipped: 0, + pending: 0, + other: 0, + flaky: 0, + start: Date.now(), + stop: Date.now() + 1000, + duration: 1000, + }, + tests: [ + { + name: 'test1', + status: 'passed', + duration: 100, + }, + ], + }, + } + + mockGlob.sync.mockReturnValue(['report1.json', 'report2.json']) + mockFs.readFileSync.mockReturnValue(JSON.stringify(validCtrfReport)) + + const result = readReportsFromGlobPattern(pattern) + + expect(result).toHaveLength(2) + expect(result[0]).toEqual(validCtrfReport) + expect(result[1]).toEqual(validCtrfReport) + }) + + it('should throw error when no files match the pattern', () => { + const pattern = 'nonexistent/*.json' + mockGlob.sync.mockReturnValue([]) + + expect(() => readReportsFromGlobPattern(pattern)).toThrow( + /No files found matching the pattern/ + ) + }) + + it('should throw error when no valid CTRF reports are found', () => { + const pattern = 'invalid/*.json' + mockGlob.sync.mockReturnValue(['invalid.json']) + mockFs.readFileSync.mockReturnValue(JSON.stringify({ invalid: 'data' })) + + const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + expect(() => readReportsFromGlobPattern(pattern)).toThrow( + /No valid CTRF reports found/ + ) + + consoleSpy.mockRestore() + }) + }) +}) diff --git a/src/methods/read-reports.ts b/src/methods/read-reports.ts index 36debe1..20d3d6f 100644 --- a/src/methods/read-reports.ts +++ b/src/methods/read-reports.ts @@ -1,7 +1,7 @@ -import fs from 'fs'; -import path from 'path'; -import { Report } from '../../types/ctrf.js'; -import { glob } from 'glob'; +import fs from 'fs' +import path from 'path' +import { Report } from '../../types/ctrf.js' +import { glob } from 'glob' /** * Reads a single CTRF report file from a specified path. @@ -10,32 +10,33 @@ import { glob } from 'glob'; * @returns The parsed `CtrfReport` object. * @throws If the file does not exist, is not a valid JSON, or does not conform to the `CtrfReport` structure. */ -export function readSingleReport(filePath: string): Report { - if (!fs.existsSync(filePath)) { - throw new Error(`JSON file not found: ${filePath}`) - } - const resolvedPath = path.resolve(filePath); - - if (!fs.existsSync(resolvedPath)) { - throw new Error(`The file '${resolvedPath}' does not exist.`); - } +export function readReportFromFile(filePath: string): Report { + if (!fs.existsSync(filePath)) { + throw new Error(`JSON file not found: ${filePath}`) + } + const resolvedPath = path.resolve(filePath) - try { - const content = fs.readFileSync(resolvedPath, 'utf8'); + if (!fs.existsSync(resolvedPath)) { + throw new Error(`The file '${resolvedPath}' does not exist.`) + } - const parsed = JSON.parse(content); + try { + const content = fs.readFileSync(resolvedPath, 'utf8') - if (!isCtrfReport(parsed)) { - throw new Error(`The file '${resolvedPath}' is not a valid CTRF report.`); - } + const parsed = JSON.parse(content) - return parsed as Report; - } catch (error) { - const errorMessage = (error as any).message || 'Unknown error'; - throw new Error(`Failed to read or parse the file '${resolvedPath}': ${errorMessage}`); + if (!isCtrfReport(parsed)) { + throw new Error(`The file '${resolvedPath}' is not a valid CTRF report.`) } -} + return parsed as Report + } catch (error) { + const errorMessage = (error as Error).message || 'Unknown error' + throw new Error( + `Failed to read or parse the file '${resolvedPath}': ${errorMessage}` + ) + } +} /** * Reads all CTRF report files from a given directory. @@ -45,82 +46,85 @@ export function readSingleReport(filePath: string): Report { * @throws If the directory does not exist or no valid CTRF reports are found. */ export function readReportsFromDirectory(directoryPath: string): Report[] { - directoryPath = path.resolve(directoryPath); + directoryPath = path.resolve(directoryPath) - if (!fs.existsSync(directoryPath)) { - throw new Error(`The directory '${directoryPath}' does not exist.`); - } + if (!fs.existsSync(directoryPath)) { + throw new Error(`The directory '${directoryPath}' does not exist.`) + } - const files = fs.readdirSync(directoryPath); - - const reports: Report[] = files - .filter((file) => path.extname(file) === '.json') - .map((file) => { - const filePath = path.join(directoryPath, file); - try { - const content = fs.readFileSync(filePath, 'utf8'); - const parsed = JSON.parse(content); - - if (!isCtrfReport(parsed)) { - console.warn(`Skipping invalid CTRF report file: ${file}`); - return null; - } - - return parsed as Report; - } catch (error) { - console.warn(`Failed to read or parse file '${file}':`, error); - return null; - } - }) - .filter((report): report is Report => report !== null); - - if (reports.length === 0) { - throw new Error(`No valid CTRF reports found in the directory '${directoryPath}'.`); - } + const files = fs.readdirSync(directoryPath) - return reports; + const reports: Report[] = files + .filter(file => path.extname(file) === '.json') + .map(file => { + const filePath = path.join(directoryPath, file) + try { + const content = fs.readFileSync(filePath, 'utf8') + const parsed = JSON.parse(content) + + if (!isCtrfReport(parsed)) { + console.warn(`Skipping invalid CTRF report file: ${file}`) + return null + } + + return parsed as Report + } catch (error) { + console.warn(`Failed to read or parse file '${file}':`, error) + return null + } + }) + .filter((report): report is Report => report !== null) + + if (reports.length === 0) { + throw new Error( + `No valid CTRF reports found in the directory '${directoryPath}'.` + ) + } + + return reports } /** * Reads all CTRF report files matching a glob pattern. * * @param pattern The glob pattern to match files (e.g., ctrf/*.json). -* @returns An array of parsed `CtrfReport` objects. -* @throws If no valid CTRF reports are found. -*/ + * @returns An array of parsed `CtrfReport` objects. + * @throws If no valid CTRF reports are found. + */ export function readReportsFromGlobPattern(pattern: string): Report[] { - const files = glob.sync(pattern); - - if (files.length === 0) { - throw new Error(`No files found matching the pattern '${pattern}'.`); - } - - const reports: Report[] = files - .map((file) => { - try { + const files = glob.sync(pattern) - const content = fs.readFileSync(file, 'utf8'); - const parsed = JSON.parse(content); + if (files.length === 0) { + throw new Error(`No files found matching the pattern '${pattern}'.`) + } - if (!isCtrfReport(parsed)) { - console.warn(`Skipping invalid CTRF report file: ${file}`); - return null; - } + const reports: Report[] = files + .map(file => { + try { + const content = fs.readFileSync(file, 'utf8') + const parsed = JSON.parse(content) - return parsed as Report; - } catch (error) { - console.warn(`Failed to read or parse file '${file}':`, error); - return null; - } - }) - .filter((report): report is Report => report !== null); - - if (reports.length === 0) { - throw new Error(`No valid CTRF reports found matching the pattern '${pattern}'.`); - } + if (!isCtrfReport(parsed)) { + console.warn(`Skipping invalid CTRF report file: ${file}`) + return null + } - return reports; + return parsed as Report + } catch (error) { + console.warn(`Failed to read or parse file '${file}':`, error) + return null + } + }) + .filter((report): report is Report => report !== null) + + if (reports.length === 0) { + throw new Error( + `No valid CTRF reports found matching the pattern '${pattern}'.` + ) + } + + return reports } /** @@ -129,13 +133,15 @@ export function readReportsFromGlobPattern(pattern: string): Report[] { * @param obj The object to validate. * @returns `true` if the object matches the `CtrfReport` type; otherwise, `false`. */ + +// eslint-disable-next-line @typescript-eslint/no-explicit-any function isCtrfReport(obj: any): obj is Report { - return ( - obj && - typeof obj === 'object' && - obj.results && - Array.isArray(obj.results.tests) && - typeof obj.results.summary === 'object' && - typeof obj.results.tool === 'object' - ); + return ( + obj && + typeof obj === 'object' && + obj.results && + Array.isArray(obj.results.tests) && + typeof obj.results.summary === 'object' && + typeof obj.results.tool === 'object' + ) } diff --git a/src/methods/run-insights.test.ts b/src/methods/run-insights.test.ts new file mode 100644 index 0000000..57e9eb2 --- /dev/null +++ b/src/methods/run-insights.test.ts @@ -0,0 +1,436 @@ +import { describe, it, expect } from 'vitest' +import { + isTestFlaky, + formatAsPercentage, + formatInsightsMetricAsPercentage, + enrichReportWithInsights, +} from './run-insights' + +import { Report, Test, InsightsMetric } from '../../types/ctrf.js' +import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from '../constants' + +const createMockTest = (overrides: Partial = {}): Test => ({ + name: 'test-name', + status: 'passed', + duration: 100, + ...overrides, +}) + +const createMockReport = ( + tests: Test[] = [], + overrides: Partial = {} +): Report => ({ + reportFormat: CTRF_REPORT_FORMAT, + specVersion: CTRF_SPEC_VERSION, + results: { + tool: { name: 'jest' }, + summary: { + tests: tests.length, + passed: tests.filter(t => t.status === 'passed').length, + failed: tests.filter(t => t.status === 'failed').length, + skipped: tests.filter(t => t.status === 'skipped').length, + pending: tests.filter(t => t.status === 'pending').length, + other: tests.filter(t => t.status === 'other').length, + flaky: tests.filter(t => t.flaky === true).length, + start: Date.now(), + stop: Date.now() + 1000, + duration: 1000, + }, + tests, + }, + ...overrides, +}) + +describe('run-insights utility functions', () => { + describe('isTestFlaky', () => { + it('should return true for explicitly flaky tests', () => { + const test = createMockTest({ flaky: true }) + expect(isTestFlaky(test)).toBe(true) + }) + + it('should return true for passed tests with retries', () => { + const test = createMockTest({ + status: 'passed', + retries: 2, + }) + expect(isTestFlaky(test)).toBe(true) + }) + + it('should return false for failed tests with retries', () => { + const test = createMockTest({ + status: 'failed', + retries: 2, + }) + expect(isTestFlaky(test)).toBe(false) + }) + + it('should return false for passed tests without retries', () => { + const test = createMockTest({ + status: 'passed', + retries: 0, + }) + expect(isTestFlaky(test)).toBe(false) + }) + + it('should return false for passed tests without retries property', () => { + const test = createMockTest({ status: 'passed' }) + expect(isTestFlaky(test)).toBe(false) + }) + }) + + describe('formatAsPercentage', () => { + it('should format ratio as percentage with default 2 decimals', () => { + expect(formatAsPercentage(0.25)).toBe('25.00%') + expect(formatAsPercentage(0.5)).toBe('50.00%') + expect(formatAsPercentage(0.75)).toBe('75.00%') + expect(formatAsPercentage(1)).toBe('100.00%') + }) + + it('should format ratio as percentage with custom decimals', () => { + expect(formatAsPercentage(0.25, 1)).toBe('25.0%') + expect(formatAsPercentage(0.25, 0)).toBe('25%') + expect(formatAsPercentage(0.25, 3)).toBe('25.000%') + }) + + it('should handle edge cases', () => { + expect(formatAsPercentage(0)).toBe('0.00%') + expect(formatAsPercentage(0.00001)).toBe('0.00%') + expect(formatAsPercentage(0.999)).toBe('99.90%') + }) + }) + + describe('formatInsightsMetricAsPercentage', () => { + it('should format all metric values as percentages', () => { + const metric: InsightsMetric = { + current: 0.25, + baseline: 0.2, + change: 0.05, + } + + const result = formatInsightsMetricAsPercentage(metric) + + expect(result).toEqual({ + current: '25.00%', + baseline: '20.00%', + change: '+5.00%', + }) + }) + + it('should handle negative change values', () => { + const metric: InsightsMetric = { + current: 0.15, + baseline: 0.2, + change: -0.05, + } + + const result = formatInsightsMetricAsPercentage(metric) + + expect(result).toEqual({ + current: '15.00%', + baseline: '20.00%', + change: '-5.00%', + }) + }) + + it('should handle zero change values', () => { + const metric: InsightsMetric = { + current: 0.2, + baseline: 0.2, + change: 0, + } + + const result = formatInsightsMetricAsPercentage(metric) + + expect(result).toEqual({ + current: '20.00%', + baseline: '20.00%', + change: '+0.00%', + }) + }) + }) +}) + +describe('enrichReportWithInsights - Main API', () => { + describe('basic functionality', () => { + it('should enrich a report with run-level insights when no baseline reports', () => { + const tests = [ + createMockTest({ name: 'test1', status: 'passed', duration: 100 }), + createMockTest({ name: 'test2', status: 'failed', duration: 200 }), + createMockTest({ + name: 'test3', + status: 'passed', + retries: 1, + flaky: true, + duration: 150, + }), + ] + const currentReport = createMockReport(tests) + + const result = enrichReportWithInsights(currentReport, []) + + expect(result.insights).toBeDefined() + expect(result.insights!.flakyRate?.current).toBeGreaterThan(0) + expect(result.insights!.failRate?.current).toBeGreaterThan(0) + expect(result.insights!.averageTestDuration?.current).toBe(150) // (100+200+150)/3 + expect(result.insights!.runsAnalyzed).toBe(1) + + expect(result.insights!.flakyRate?.baseline).toBe(0) + expect(result.insights!.flakyRate?.change).toBe(0) + }) + + it('should add test-level insights to each test', () => { + const tests = [ + createMockTest({ name: 'test1', status: 'passed', duration: 100 }), + createMockTest({ name: 'test2', status: 'failed', duration: 200 }), + ] + const currentReport = createMockReport(tests) + + const result = enrichReportWithInsights(currentReport, []) + + expect(result.results.tests[0].insights).toBeDefined() + expect(result.results.tests[1].insights).toBeDefined() + + expect(result.results.tests[0].insights!.failRate?.current).toBe(0) + expect(result.results.tests[1].insights!.failRate?.current).toBe(1) + expect( + result.results.tests[0].insights!.averageTestDuration?.current + ).toBe(100) + expect( + result.results.tests[1].insights!.averageTestDuration?.current + ).toBe(200) + }) + }) + + describe('baseline comparison functionality', () => { + it('should compare against previous report by default', () => { + const currentTests = [ + createMockTest({ name: 'test1', status: 'passed', duration: 100 }), + createMockTest({ name: 'test2', status: 'passed', duration: 200 }), + ] + const previousTests = [ + createMockTest({ name: 'test1', status: 'failed', duration: 150 }), + createMockTest({ name: 'test2', status: 'failed', duration: 250 }), + ] + + const currentReport = createMockReport(currentTests, { + results: { + ...createMockReport(currentTests).results, + summary: { + ...createMockReport(currentTests).results.summary, + start: 2000, + }, + }, + }) + const previousReport = createMockReport(previousTests, { + results: { + ...createMockReport(previousTests).results, + summary: { + ...createMockReport(previousTests).results.summary, + start: 1000, + }, + }, + }) + + const result = enrichReportWithInsights( + currentReport, + [previousReport], + previousReport + ) + + expect(result.insights).toBeDefined() + expect(result.insights!.failRate?.current).toBe(0.5) // Overall: 2 passed + 2 failed = 50% fail rate + expect(result.insights!.failRate?.baseline).toBe(1) // Previous: all failed + expect(result.insights!.failRate?.change).toBe(-0.5) // Improvement of 50% + expect(result.insights!.runsAnalyzed).toBe(2) + }) + + it('should handle baseline by index', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + const previous1Tests = [ + createMockTest({ name: 'test1', status: 'failed' }), + ] + const previous2Tests = [ + createMockTest({ name: 'test1', status: 'passed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReport1 = createMockReport(previous1Tests) + const previousReport2 = createMockReport(previous2Tests) + + // Use previousReport2 as baseline (second previous report) + const result = enrichReportWithInsights( + currentReport, + [previousReport1, previousReport2], + previousReport2 + ) + + expect(result.insights!.failRate?.current).toBe(0.3333) // 1 passed + 1 failed + 1 passed = 1/3 fail rate + expect(result.insights!.failRate?.baseline).toBe(0) // previous2 had no failures + expect(result.insights!.failRate?.change).toBe(0.3333) + }) + + it('should handle baseline by report ID', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + const previousTests = [ + createMockTest({ name: 'test1', status: 'failed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReport = createMockReport(previousTests, { + results: { + ...createMockReport(previousTests).results, + summary: { + ...createMockReport(previousTests).results.summary, + start: 12345, + }, + }, + }) + + const result = enrichReportWithInsights( + currentReport, + [previousReport], + previousReport + ) + + expect(result.insights!.failRate?.current).toBe(0.5) + expect(result.insights!.failRate?.baseline).toBe(1) + expect(result.insights!.failRate?.change).toBe(-0.5) + }) + + it('should handle invalid baseline gracefully', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + const previousTests = [ + createMockTest({ name: 'test1', status: 'failed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReport = createMockReport(previousTests) + + const invalidBaseline = createMockReport([], { results: null } as any) + const result1 = enrichReportWithInsights( + currentReport, + [previousReport], + invalidBaseline + ) + expect(result1.insights!.failRate?.baseline).toBe(0) + expect(result1.insights!.failRate?.change).toBe(0) + + const result2 = enrichReportWithInsights(currentReport, [previousReport]) + expect(result2.insights!.failRate?.baseline).toBe(0) + expect(result2.insights!.failRate?.change).toBe(0) + }) + }) + + describe('added/removed tests tracking', () => { + it.skip('should track tests added since baseline', () => { + const currentTests = [ + createMockTest({ name: 'test1', status: 'passed' }), + createMockTest({ name: 'test2', status: 'passed' }), + createMockTest({ name: 'test3', status: 'passed' }), + ] + const previousTests = [ + createMockTest({ name: 'test1', status: 'passed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReport = createMockReport(previousTests) + + const result = enrichReportWithInsights( + currentReport, + [previousReport], + previousReport + ) + + expect(result.insights!.extra!.testsAdded).toHaveLength(2) + expect((result.insights!.extra!.testsAdded as any[])[0].name).toBe( + 'test2' + ) + expect((result.insights!.extra!.testsAdded as any[])[1].name).toBe( + 'test3' + ) + }) + + it.skip('should track tests removed since baseline', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + const previousTests = [ + createMockTest({ name: 'test1', status: 'passed' }), + createMockTest({ name: 'test2', status: 'passed' }), + createMockTest({ name: 'test3', status: 'passed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReport = createMockReport(previousTests) + + const result = enrichReportWithInsights( + currentReport, + [previousReport], + previousReport + ) + + expect(result.insights!.extra!.testsRemoved).toHaveLength(2) + expect((result.insights!.extra!.testsRemoved as any[])[0].name).toBe( + 'test2' + ) + expect((result.insights!.extra!.testsRemoved as any[])[1].name).toBe( + 'test3' + ) + }) + + it.skip('should handle single report no added/removed tests', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + + const currentReport = createMockReport(currentTests) + const result = enrichReportWithInsights(currentReport, []) + + expect(result.insights!.extra!.testsAdded).toBeUndefined() + expect(result.insights!.extra!.testsRemoved).toBeUndefined() + }) + }) + + describe('edge cases', () => { + it('should handle empty current report', () => { + const currentReport = createMockReport([]) + const previousReport = createMockReport([createMockTest()]) + + const result = enrichReportWithInsights(currentReport, [previousReport]) + + expect(result.insights).toBeDefined() + expect(result.insights!.failRate?.current).toBe(0) + expect(result.insights!.averageTestDuration?.current).toBe(100) + }) + + it('should handle invalid current report', () => { + const invalidReport = { results: null } as any + + const result = enrichReportWithInsights(invalidReport, []) + + expect(result).toBe(invalidReport) + }) + + it('should handle multiple previous reports', () => { + const currentTests = [createMockTest({ name: 'test1', status: 'passed' })] + const previous1Tests = [ + createMockTest({ name: 'test1', status: 'failed' }), + ] + const previous2Tests = [ + createMockTest({ name: 'test1', status: 'passed' }), + ] + const previous3Tests = [ + createMockTest({ name: 'test1', status: 'failed' }), + ] + + const currentReport = createMockReport(currentTests) + const previousReports = [ + createMockReport(previous1Tests), + createMockReport(previous2Tests), + createMockReport(previous3Tests), + ] + + const result = enrichReportWithInsights(currentReport, previousReports) + + expect(result.insights!.runsAnalyzed).toBe(4) + expect(result.insights!.failRate?.current).toBeDefined() + expect(result.insights!.failRate?.baseline).toBeDefined() + }) + }) +}) diff --git a/src/methods/run-insights.ts b/src/methods/run-insights.ts index 281da22..8388f78 100644 --- a/src/methods/run-insights.ts +++ b/src/methods/run-insights.ts @@ -1,4 +1,11 @@ -import { InsightsMetric, Report, Test, TestInsights, Insights } from "../../types/ctrf.js" +import { + InsightsMetric, + Report, + Test, + TestInsights, + Insights, +} from '../../types/ctrf.js' +import { sortReportsByTimestamp } from './utilities/sort-reports.js' export interface SimplifiedTestData { name: string @@ -6,10 +13,6 @@ export interface SimplifiedTestData { filePath?: string } -// ======================================== -// UTILITY FUNCTIONS -// ======================================== - /** * Utility function that determines if a test is flaky based on its retries and status. * @@ -31,7 +34,10 @@ export function isTestFlaky(test: Test): boolean { * @param decimals - Number of decimal places (default: 2) * @returns Formatted percentage string (e.g., "25.50%") */ -export function formatAsPercentage(ratio: number, decimals: number = 2): string { +export function formatAsPercentage( + ratio: number, + decimals: number = 2 +): string { return `${(ratio * 100).toFixed(decimals)}%` } @@ -45,14 +51,34 @@ export function formatAsPercentage(ratio: number, decimals: number = 2): string export function formatInsightsMetricAsPercentage( metric: InsightsMetric, decimals: number = 2 -): { current: string; previous: string; change: string } { +): { current: string; baseline: string; change: string } { return { current: formatAsPercentage(metric.current, decimals), - previous: formatAsPercentage(metric.previous, decimals), - change: `${metric.change >= 0 ? '+' : ''}${formatAsPercentage(metric.change, decimals)}` + baseline: formatAsPercentage(metric.baseline, decimals), + change: `${metric.change >= 0 ? '+' : ''}${formatAsPercentage(metric.change, decimals)}`, } } +/** + * Utility function that calculates percent fractional change between current and baseline values. + * Used for duration metrics where the change should be expressed as a ratio (e.g., 0.25 = 25% increase). + * + * @param current - The current value + * @param baseline - The baseline value to compare against + * @param decimals - Number of decimal places (default: 4) + * @returns Percent fractional change, or 0 if baseline is 0 + */ +export function calculatePercentChange( + current: number, + baseline: number, + decimals: number = 4 +): number { + if (baseline === 0) { + return 0 + } + return Number(((current - baseline) / baseline).toFixed(decimals)) +} + /** * Calculates the 95th percentile from an array of numbers. * @@ -61,10 +87,10 @@ export function formatInsightsMetricAsPercentage( */ function calculateP95(values: number[]): number { if (values.length === 0) return 0 - + const sorted = [...values].sort((a, b) => a - b) const index = Math.ceil(sorted.length * 0.95) - 1 - + return Number(sorted[Math.max(0, index)].toFixed(2)) } @@ -80,22 +106,22 @@ function validateReportForInsights(report: Report): boolean { * Key distinction: "Attempts" include retries, "Results" are final test outcomes only. */ interface AggregatedRunMetrics { - // ATTEMPT METRICS (includes retries) - totalAttempts: number // Total test executions including retries (e.g., test fails 2x then passes = 3 attempts) - totalAttemptsFailed: number // Total failed test executions including retries (e.g., 2 failed attempts + 1 final pass = 2 failed attempts) - totalAttemptsFlaky: number // Total retry attempts for flaky tests only, failed before the final result was passed (e.g., flaky test with 2 retries = 2 flaky attempts) - - // RESULT METRICS (final test outcomes only, no retries counted) - totalResults: number // Total number of tests executed (each test counted once regardless of retries) - totalResultsFailed: number // Number of tests with final status "failed" (1 per test regardless of retry count) - totalResultsPassed: number // Number of tests with final status "passed" (1 per test regardless of retry count) - totalResultsSkipped: number // Number of tests with final status "skipped/pending/other" (1 per test) - totalResultsFlaky: number // Number of tests marked as flaky (1 per test regardless of retry count) - - // OTHER METRICS - totalResultsDuration: number // Total duration of all final test results (retries not included in duration) - reportsAnalyzed: number // Total number of reports analyzed - } + // ATTEMPT METRICS (includes retries) + totalAttempts: number // Total test executions including retries (e.g., test fails 2x then passes = 3 attempts) + totalAttemptsFailed: number // Total failed test executions including retries (e.g., 2 failed attempts + 1 final pass = 2 failed attempts) + totalAttemptsFlaky: number // Total retry attempts for flaky tests only, failed before the final result was passed (e.g., flaky test with 2 retries = 2 flaky attempts) + + // RESULT METRICS (final test outcomes only, no retries counted) + totalResults: number // Total number of tests executed (each test counted once regardless of retries) + totalResultsFailed: number // Number of tests with final status "failed" (1 per test regardless of retry count) + totalResultsPassed: number // Number of tests with final status "passed" (1 per test regardless of retry count) + totalResultsSkipped: number // Number of tests with final status "skipped/pending/other" (1 per test) + totalResultsFlaky: number // Number of tests marked as flaky (1 per test regardless of retry count) + + // OTHER METRICS + totalResultsDuration: number // Total duration of all final test results (retries not included in duration) + reportsAnalyzed: number // Total number of reports analyzed +} /** * Aggregated run metrics for a single test across multiple reports, @@ -139,7 +165,7 @@ function aggregateTestMetricsAcrossReports( totalResultsDuration: 0, appearsInRuns: 0, reportsAnalyzed: 0, - durations: [] + durations: [], }) } @@ -221,14 +247,10 @@ function consolidateTestMetricsToRunMetrics( totalResultsFlaky, totalAttemptsFlaky, totalResultsDuration, - reportsAnalyzed: metricsMap.size + reportsAnalyzed: metricsMap.size, } } -// ======================================== -// INSIGHT Flaky Rate FUNCTIONS -// ======================================== - /** * Calculates overall flaky rate from consolidated run metrics. * Flaky rate = (failed attempts from flaky tests) / (total attempts) as ratio 0-1 @@ -240,33 +262,11 @@ function calculateFlakyRateFromMetrics( return 0 } - return Number((runMetrics.totalAttemptsFlaky / runMetrics.totalAttempts).toFixed(4)) -} - -/** - * Internal helper function that calculates flaky rate insights across all reports (current + all previous). - * - * @param currentReport - The current CTRF report - * @param previousReports - Array of historical CTRF reports - * @returns InsightsMetric with current value calculated across all reports - */ -function calculateInsightFlakyRateCurrent( - currentReport: Report, - previousReports: Report[] -): InsightsMetric { - const allReports = [currentReport, ...previousReports] - - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - const current = calculateFlakyRateFromMetrics(runMetrics) - - return { current, previous: 0, change: 0 } + return Number( + (runMetrics.totalAttemptsFlaky / runMetrics.totalAttempts).toFixed(4) + ) } -// ======================================== -// INSIGHT Fail Rate FUNCTIONS -// ======================================== - /** * Calculates overall fail rate from consolidated run metrics. * Fail rate = (totalResultsFailed / totalResults) as ratio 0-1 @@ -278,71 +278,27 @@ function calculateFailRateFromMetrics( return 0 } - return Number((runMetrics.totalResultsFailed / runMetrics.totalResults).toFixed(4)) -} - -/** - * Internal helper function that calculates fail rate insights across all reports (current + all previous). - * - * @param currentReport - The current CTRF report - * @param previousReports - Array of historical CTRF reports - * @returns InsightsMetric with current value calculated across all reports - */ -function calculateFailRateInsight( - currentReport: Report, - previousReports: Report[] -): InsightsMetric { - const allReports = [currentReport, ...previousReports] - - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - const current = calculateFailRateFromMetrics(runMetrics) - - return { current, previous: 0, change: 0 } + return Number( + (runMetrics.totalResultsFailed / runMetrics.totalResults).toFixed(4) + ) } -// ======================================== -// INSIGHT Skipped Rate FUNCTIONS -// ======================================== - /** - * Calculates overall skipped rate from consolidated run metrics. - * Skipped rate = (totalResultsSkipped / totalResults) as ratio 0-1 + * Calculates overall pass rate from consolidated run metrics. + * Pass rate = (totalResultsPassed / totalResults) as ratio 0-1 */ -function calculateSkippedRateFromMetrics( +function calculatePassRateFromMetrics( runMetrics: AggregatedRunMetrics ): number { if (runMetrics.totalResults === 0) { return 0 } - return Number((runMetrics.totalResultsSkipped / runMetrics.totalResults).toFixed(4)) -} - -/** - * Internal helper function that calculates skipped rate insights across all reports (current + all previous). - * - * @param currentReport - The current CTRF report - * @param previousReports - Array of historical CTRF reports - * @returns InsightsMetric with current value calculated across all reports - */ -function calculateSkippedRateInsight( - currentReport: Report, - previousReports: Report[] -): InsightsMetric { - const allReports = [currentReport, ...previousReports] - - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - const current = calculateSkippedRateFromMetrics(runMetrics) - - return { current, previous: 0, change: 0 } + return Number( + (runMetrics.totalResultsPassed / runMetrics.totalResults).toFixed(4) + ) } -// ======================================== -// INSIGHT Average Test Duration FUNCTIONS -// ======================================== - /** * Calculates average test duration from consolidated run metrics. * Average test duration = (totalDuration / totalResults) @@ -354,33 +310,11 @@ function calculateAverageTestDurationFromMetrics( return 0 } - return Number((runMetrics.totalResultsDuration / runMetrics.totalResults).toFixed(2)) -} - -/** - * Internal helper function that calculates average test duration insights across all reports (current + all previous). - * - * @param currentReport - The current CTRF report - * @param previousReports - Array of historical CTRF reports - * @returns InsightsMetric with current value calculated across all reports - */ -function calculateAverageTestDurationInsight( - currentReport: Report, - previousReports: Report[] -): InsightsMetric { - const allReports = [currentReport, ...previousReports] - - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - const current = calculateAverageTestDurationFromMetrics(runMetrics) - - return { current, previous: 0, change: 0 } + return Number( + (runMetrics.totalResultsDuration / runMetrics.totalResults).toFixed(2) + ) } -// ======================================== -// INSIGHT Average Run Duration FUNCTIONS -// ======================================== - /** * Calculates average run duration from consolidated run metrics. * Average run duration = (totalDuration / reportsAnalyzed) @@ -392,34 +326,34 @@ function calculateAverageRunDurationFromMetrics( return 0 } - return Number((runMetrics.totalResultsDuration / runMetrics.reportsAnalyzed).toFixed(2)) + return Number( + (runMetrics.totalResultsDuration / runMetrics.reportsAnalyzed).toFixed(2) + ) } /** - * Internal helper function that calculates average run duration insights across all reports (current + all previous). + * Calculates p95 run duration from a collection of reports. + * Uses the actual run duration (summary.stop - summary.start) for each report. * - * @param currentReport - The current CTRF report - * @param previousReports - Array of historical CTRF reports - * @returns InsightsMetric with current value calculated across all reports - */ -function calculateAverageRunDurationInsight( - currentReport: Report, - previousReports: Report[] -): InsightsMetric { - const allReports = [currentReport, ...previousReports] - - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - - const current = calculateAverageRunDurationFromMetrics(runMetrics) + * @param reports - Array of CTRF reports to calculate p95 from + * @returns The 95th percentile run duration + */ +function calculateP95RunDurationFromReports(reports: Report[]): number { + const runDurations: number[] = [] + + for (const report of reports) { + if (validateReportForInsights(report) && report.results?.summary) { + const { start, stop } = report.results.summary + if (start && stop && stop > start) { + const runDuration = stop - start + runDurations.push(runDuration) + } + } + } - return { current, previous: 0, change: 0 } + return calculateP95(runDurations) } -// ======================================== -// INSIGHT Current FUNCTIONS -// ======================================== - /** * Internal helper function that recursively calculates insights for each report based on all reports that came before it chronologically. * Only sets the `current` field for each report - `previous` and `change` are calculated later. @@ -428,192 +362,61 @@ function calculateAverageRunDurationInsight( * @param index - Current index being processed (default: 0) * @returns The reports array with insights populated for each report */ -function calculateRunInsights( - reports: Report[], - index: number = 0 -): Report[] { +function calculateRunInsights(reports: Report[], index: number = 0): Report[] { if (index >= reports.length) { return reports } const currentReport = reports[index] - const previousReports = reports.slice(index + 1) // Reports that came before this one in time + const previousReports = reports.slice(index + 1) const allReportsUpToThisPoint = [currentReport, ...previousReports] const testMetrics = aggregateTestMetricsAcrossReports(allReportsUpToThisPoint) const runMetrics = consolidateTestMetricsToRunMetrics(testMetrics) - const { reportsAnalyzed, ...relevantMetrics } = runMetrics - + const { ...relevantMetrics } = runMetrics + currentReport.insights = { + passRate: { + current: calculatePassRateFromMetrics(runMetrics), + baseline: 0, + change: 0, + }, flakyRate: { current: calculateFlakyRateFromMetrics(runMetrics), - previous: 0, - change: 0 + baseline: 0, + change: 0, }, failRate: { current: calculateFailRateFromMetrics(runMetrics), - previous: 0, - change: 0 - }, - skippedRate: { - current: calculateSkippedRateFromMetrics(runMetrics), - previous: 0, - change: 0 + baseline: 0, + change: 0, }, averageTestDuration: { current: calculateAverageTestDurationFromMetrics(runMetrics), - previous: 0, - change: 0 + baseline: 0, + change: 0, }, averageRunDuration: { current: calculateAverageRunDurationFromMetrics(runMetrics), - previous: 0, - change: 0 + baseline: 0, + change: 0, }, - reportsAnalyzed: allReportsUpToThisPoint.length, - extra: relevantMetrics + p95RunDuration: { + current: calculateP95RunDurationFromReports(allReportsUpToThisPoint), + baseline: 0, + change: 0, + }, + runsAnalyzed: allReportsUpToThisPoint.length, + extra: relevantMetrics, } return calculateRunInsights(reports, index + 1) } -// ======================================== -// TEST-LEVEL INSIGHTS FUNCTIONS -// ======================================== - -/** - * Calculates test-level flaky rate for a specific test. - */ -function calculateTestFlakyRate( - testName: string, - testMetrics: AggregatedTestMetrics -): InsightsMetric { - const current = testMetrics.totalAttempts === 0 ? 0 : - Number((testMetrics.totalAttemptsFlaky / testMetrics.totalAttempts).toFixed(4)) - - return { current, previous: 0, change: 0 } -} - -/** - * Calculates test-level fail rate for a specific test. - */ -function calculateTestFailRate( - testName: string, - testMetrics: AggregatedTestMetrics -): InsightsMetric { - const current = testMetrics.totalResults === 0 ? 0 : - Number((testMetrics.totalResultsFailed / testMetrics.totalResults).toFixed(4)) - return { current, previous: 0, change: 0 } -} - -/** - * Calculates test-level skipped rate for a specific test. - */ -function calculateTestSkippedRate( - testName: string, - testMetrics: AggregatedTestMetrics -): InsightsMetric { - const current = testMetrics.totalResults === 0 ? 0 : - Number((testMetrics.totalResultsSkipped / testMetrics.totalResults).toFixed(4)) - - return { current, previous: 0, change: 0 } -} - -/** - * Calculates test-level average duration for a specific test. - */ -function calculateTestAverageDuration( - testName: string, - testMetrics: AggregatedTestMetrics -): InsightsMetric { - const current = testMetrics.totalResults === 0 ? 0 : - Number((testMetrics.totalResultsDuration / testMetrics.totalResults).toFixed(2)) - - return { current, previous: 0, change: 0 } -} - -/** - * Calculates test-level p95 duration for a specific test. - */ -function calculateTestP95Duration( - testName: string, - testMetrics: AggregatedTestMetrics -): InsightsMetric { - const current = calculateP95(testMetrics.durations) - - return { current, previous: 0, change: 0 } -} - -/** - * Calculates test-level insights for a specific test. - */ -function calculateTestInsights( - testName: string, - testMetrics: AggregatedTestMetrics, -): TestInsights { - const { appearsInRuns, reportsAnalyzed, ...relevantMetrics } = testMetrics - - return { - flakyRate: calculateTestFlakyRate(testName, testMetrics), - failRate: calculateTestFailRate(testName, testMetrics), - skippedRate: calculateTestSkippedRate(testName, testMetrics), - averageTestDuration: calculateTestAverageDuration(testName, testMetrics), - p95Duration: calculateTestP95Duration(testName, testMetrics), - appearsInRuns: testMetrics.appearsInRuns, - extra: relevantMetrics - } -} - -/** - * Internal helper function that adds test-level insights to all tests in the current report. - * - * @param currentReport - The current CTRF report to add insights to - * @param previousReports - Array of historical CTRF reports - * @returns The current report with test-level insights added to each test - */ -function addTestInsightsToCurrentReport( - currentReport: Report, - previousReports: Report[] -): Report { - if (!validateReportForInsights(currentReport)) { - return currentReport - } - - const allReports = [currentReport, ...previousReports] - const testMetrics = aggregateTestMetricsAcrossReports(allReports) - - const reportWithInsights: Report = { - ...currentReport, - results: { - ...currentReport.results, - tests: currentReport.results.tests.map(test => { - const testName = test.name - const metrics = testMetrics.get(testName) - - if (metrics) { - const testInsights = calculateTestInsights(testName, metrics) - return { - ...test, - insights: testInsights - } - } - - return test - }) - } - } - - return reportWithInsights -} - -// ======================================== -// BASELINE INSIGHTS FUNCTIONS -// ======================================== - /** * Calculates test-level insights with baseline comparison for a specific test. - * + * * @param testName - Name of the test * @param currentTestMetrics - Current aggregated test metrics * @param baselineTestMetrics - Baseline aggregated test metrics (optional) @@ -624,64 +427,121 @@ function calculateTestInsightsWithBaseline( currentTestMetrics: AggregatedTestMetrics, baselineTestMetrics?: AggregatedTestMetrics ): TestInsights { - const currentFlakyRate = currentTestMetrics.totalAttempts === 0 ? 0 : - Number((currentTestMetrics.totalAttemptsFlaky / currentTestMetrics.totalAttempts).toFixed(4)) - const currentFailRate = currentTestMetrics.totalResults === 0 ? 0 : - Number((currentTestMetrics.totalResultsFailed / currentTestMetrics.totalResults).toFixed(4)) - const currentSkippedRate = currentTestMetrics.totalResults === 0 ? 0 : - Number((currentTestMetrics.totalResultsSkipped / currentTestMetrics.totalResults).toFixed(4)) - const currentAverageDuration = currentTestMetrics.totalResults === 0 ? 0 : - Number((currentTestMetrics.totalResultsDuration / currentTestMetrics.totalResults).toFixed(2)) + const currentPassRate = + currentTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + currentTestMetrics.totalResultsPassed / + currentTestMetrics.totalResults + ).toFixed(4) + ) + const currentFlakyRate = + currentTestMetrics.totalAttempts === 0 + ? 0 + : Number( + ( + currentTestMetrics.totalAttemptsFlaky / + currentTestMetrics.totalAttempts + ).toFixed(4) + ) + const currentFailRate = + currentTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + currentTestMetrics.totalResultsFailed / + currentTestMetrics.totalResults + ).toFixed(4) + ) + const currentAverageDuration = + currentTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + currentTestMetrics.totalResultsDuration / + currentTestMetrics.totalResults + ).toFixed(2) + ) const currentP95Duration = calculateP95(currentTestMetrics.durations) + let baselinePassRate = 0 let baselineFlakyRate = 0 let baselineFailRate = 0 - let baselineSkippedRate = 0 let baselineAverageDuration = 0 let baselineP95Duration = 0 if (baselineTestMetrics) { - baselineFlakyRate = baselineTestMetrics.totalAttempts === 0 ? 0 : - Number((baselineTestMetrics.totalAttemptsFlaky / baselineTestMetrics.totalAttempts).toFixed(4)) - baselineFailRate = baselineTestMetrics.totalResults === 0 ? 0 : - Number((baselineTestMetrics.totalResultsFailed / baselineTestMetrics.totalResults).toFixed(4)) - baselineSkippedRate = baselineTestMetrics.totalResults === 0 ? 0 : - Number((baselineTestMetrics.totalResultsSkipped / baselineTestMetrics.totalResults).toFixed(4)) - baselineAverageDuration = baselineTestMetrics.totalResults === 0 ? 0 : - Number((baselineTestMetrics.totalResultsDuration / baselineTestMetrics.totalResults).toFixed(2)) + baselinePassRate = + baselineTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + baselineTestMetrics.totalResultsPassed / + baselineTestMetrics.totalResults + ).toFixed(4) + ) + baselineFlakyRate = + baselineTestMetrics.totalAttempts === 0 + ? 0 + : Number( + ( + baselineTestMetrics.totalAttemptsFlaky / + baselineTestMetrics.totalAttempts + ).toFixed(4) + ) + baselineFailRate = + baselineTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + baselineTestMetrics.totalResultsFailed / + baselineTestMetrics.totalResults + ).toFixed(4) + ) + baselineAverageDuration = + baselineTestMetrics.totalResults === 0 + ? 0 + : Number( + ( + baselineTestMetrics.totalResultsDuration / + baselineTestMetrics.totalResults + ).toFixed(2) + ) baselineP95Duration = calculateP95(baselineTestMetrics.durations) } - const { appearsInRuns, reportsAnalyzed, ...relevantMetrics } = currentTestMetrics + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { durations: _durations, ...relevantMetrics } = currentTestMetrics return { + passRate: { + current: currentPassRate, + baseline: baselinePassRate, + change: Number((currentPassRate - baselinePassRate).toFixed(4)), + }, flakyRate: { current: currentFlakyRate, - previous: baselineFlakyRate, - change: Number((currentFlakyRate - baselineFlakyRate).toFixed(4)) + baseline: baselineFlakyRate, + change: Number((currentFlakyRate - baselineFlakyRate).toFixed(4)), }, failRate: { current: currentFailRate, - previous: baselineFailRate, - change: Number((currentFailRate - baselineFailRate).toFixed(4)) - }, - skippedRate: { - current: currentSkippedRate, - previous: baselineSkippedRate, - change: Number((currentSkippedRate - baselineSkippedRate).toFixed(4)) + baseline: baselineFailRate, + change: Number((currentFailRate - baselineFailRate).toFixed(4)), }, averageTestDuration: { current: currentAverageDuration, - previous: baselineAverageDuration, - change: Number((currentAverageDuration - baselineAverageDuration).toFixed(2)) + baseline: baselineAverageDuration, + change: currentAverageDuration - baselineAverageDuration, }, - p95Duration: { + p95TestDuration: { current: currentP95Duration, - previous: baselineP95Duration, - change: Number((currentP95Duration - baselineP95Duration).toFixed(2)) + baseline: baselineP95Duration, + change: currentP95Duration - baselineP95Duration, }, - appearsInRuns: currentTestMetrics.appearsInRuns, - extra: relevantMetrics + executedInRuns: currentTestMetrics.appearsInRuns, + extra: relevantMetrics, } } @@ -707,13 +567,16 @@ function addTestInsightsWithBaselineToCurrentReport( let baselineTestMetrics: Map | undefined if (baselineReport && validateReportForInsights(baselineReport)) { - const baselineIndex = previousReports.findIndex(report => - report.results?.summary?.start === baselineReport.results?.summary?.start + const baselineIndex = previousReports.findIndex( + report => + report.results?.summary?.start === + baselineReport.results?.summary?.start ) - + if (baselineIndex >= 0) { const reportsUpToBaseline = previousReports.slice(baselineIndex) - baselineTestMetrics = aggregateTestMetricsAcrossReports(reportsUpToBaseline) + baselineTestMetrics = + aggregateTestMetricsAcrossReports(reportsUpToBaseline) } } @@ -724,19 +587,23 @@ function addTestInsightsWithBaselineToCurrentReport( tests: currentReport.results.tests.map(test => { const testName = test.name const currentMetrics = currentTestMetrics.get(testName) - + if (currentMetrics) { const baselineMetrics = baselineTestMetrics?.get(testName) - const testInsights = calculateTestInsightsWithBaseline(testName, currentMetrics, baselineMetrics) + const testInsights = calculateTestInsightsWithBaseline( + testName, + currentMetrics, + baselineMetrics + ) return { ...test, - insights: testInsights + insights: testInsights, } } - + return test - }) - } + }), + }, } return reportWithInsights @@ -763,33 +630,59 @@ function calculateReportInsightsBaseline( } return { + passRate: { + current: currentInsights?.passRate?.current ?? 0, + baseline: previousInsights?.passRate?.current ?? 0, + change: Number( + ( + (currentInsights?.passRate?.current ?? 0) - + (previousInsights?.passRate?.current ?? 0) + ).toFixed(4) + ), + }, flakyRate: { - current: currentInsights.flakyRate.current, - previous: previousInsights.flakyRate.current, - change: Number((currentInsights.flakyRate.current - previousInsights.flakyRate.current).toFixed(4)) + current: currentInsights?.flakyRate?.current ?? 0, + baseline: previousInsights?.flakyRate?.current ?? 0, + change: Number( + ( + (currentInsights?.flakyRate?.current ?? 0) - + (previousInsights?.flakyRate?.current ?? 0) + ).toFixed(4) + ), }, failRate: { - current: currentInsights.failRate.current, - previous: previousInsights.failRate.current, - change: Number((currentInsights.failRate.current - previousInsights.failRate.current).toFixed(4)) - }, - skippedRate: { - current: currentInsights.skippedRate.current, - previous: previousInsights.skippedRate.current, - change: Number((currentInsights.skippedRate.current - previousInsights.skippedRate.current).toFixed(4)) + current: currentInsights?.failRate?.current ?? 0, + baseline: previousInsights?.failRate?.current ?? 0, + change: Number( + ( + (currentInsights?.failRate?.current ?? 0) - + (previousInsights?.failRate?.current ?? 0) + ).toFixed(4) + ), }, averageTestDuration: { - current: currentInsights.averageTestDuration.current, - previous: previousInsights.averageTestDuration.current, - change: Number((currentInsights.averageTestDuration.current - previousInsights.averageTestDuration.current).toFixed(2)) + current: currentInsights?.averageTestDuration?.current ?? 0, + baseline: previousInsights?.averageTestDuration?.current ?? 0, + change: + (currentInsights?.averageTestDuration?.current ?? 0) - + (previousInsights?.averageTestDuration?.current ?? 0), }, averageRunDuration: { - current: currentInsights.averageRunDuration.current, - previous: previousInsights.averageRunDuration.current, - change: Number((currentInsights.averageRunDuration.current - previousInsights.averageRunDuration.current).toFixed(2)) + current: currentInsights?.averageRunDuration?.current ?? 0, + baseline: previousInsights?.averageRunDuration?.current ?? 0, + change: + (currentInsights?.averageRunDuration?.current ?? 0) - + (previousInsights?.averageRunDuration?.current ?? 0), + }, + p95RunDuration: { + current: currentInsights?.p95RunDuration?.current ?? 0, + baseline: previousInsights?.p95RunDuration?.current ?? 0, + change: + (currentInsights?.p95RunDuration?.current ?? 0) - + (previousInsights?.p95RunDuration?.current ?? 0), }, - reportsAnalyzed: currentInsights.reportsAnalyzed, - extra: currentInsights.extra + runsAnalyzed: currentInsights?.runsAnalyzed ?? 0, + extra: currentInsights.extra, } } @@ -805,17 +698,24 @@ function getTestsRemovedSinceBaseline( currentReport: Report, baselineReport: Report ): SimplifiedTestData[] { - if (!validateReportForInsights(currentReport) || !validateReportForInsights(baselineReport)) { + if ( + !validateReportForInsights(currentReport) || + !validateReportForInsights(baselineReport) + ) { return [] } - const currentTestNames = new Set(currentReport.results.tests.map(test => test.name)) - const removedTests = baselineReport.results.tests.filter(test => !currentTestNames.has(test.name)) + const currentTestNames = new Set( + currentReport.results.tests.map(test => test.name) + ) + const removedTests = baselineReport.results.tests.filter( + test => !currentTestNames.has(test.name) + ) return removedTests.map(test => ({ name: test.name, suite: test.suite, - filePath: test.filePath + filePath: test.filePath, })) } @@ -828,20 +728,27 @@ function getTestsRemovedSinceBaseline( * @returns Array of CtrfTest objects that were added since baseline */ function getTestsAddedSinceBaseline( - currentReport: Report, + currentReport: Report, baselineReport: Report ): SimplifiedTestData[] { - if (!validateReportForInsights(currentReport) || !validateReportForInsights(baselineReport)) { + if ( + !validateReportForInsights(currentReport) || + !validateReportForInsights(baselineReport) + ) { return [] } - const baselineTestNames = new Set(baselineReport.results.tests.map(test => test.name)) - const addedTests = currentReport.results.tests.filter(test => !baselineTestNames.has(test.name)) + const baselineTestNames = new Set( + baselineReport.results.tests.map(test => test.name) + ) + const addedTests = currentReport.results.tests.filter( + test => !baselineTestNames.has(test.name) + ) return addedTests.map(test => ({ name: test.name, suite: test.suite, - filePath: test.filePath + filePath: test.filePath, })) } @@ -859,14 +766,17 @@ function setTestsRemovedToInsights( currentReport: Report, baselineReport: Report ): Insights { - const removedTests = getTestsRemovedSinceBaseline(currentReport, baselineReport) - + const removedTests = getTestsRemovedSinceBaseline( + currentReport, + baselineReport + ) + return { ...insights, extra: { ...insights.extra, - testsRemoved: removedTests - } + testsRemoved: removedTests, + }, } } @@ -885,13 +795,13 @@ function setTestsAddedToInsights( baselineReport: Report ): Insights { const addedTests = getTestsAddedSinceBaseline(currentReport, baselineReport) - + return { ...insights, extra: { ...insights.extra, - testsAdded: addedTests - } + testsAdded: addedTests, + }, } } @@ -899,103 +809,66 @@ function setTestsAddedToInsights( // MAIN CONSUMER API FUNCTION // ======================================== -/** - * Helper function to find the baseline report based on the baseline parameter. - * - * @param reportsWithRunInsights - Array of reports with run insights (current first, then previous) - * @param baseline - Baseline specification (undefined, number, or string) - * @returns The baseline report to use for comparison, or null if not found - */ -function findBaselineReport( - reportsWithRunInsights: Report[], - baseline?: number | string -): Report | null { - if (baseline === undefined) { - return reportsWithRunInsights[1] || null - } - - if (typeof baseline === 'number') { - const targetIndex = baseline + 1 - if (targetIndex < reportsWithRunInsights.length) { - return reportsWithRunInsights[targetIndex] - } - console.warn(`Baseline index ${baseline} is out of range. Available previous reports: ${reportsWithRunInsights.length - 1}`) - return null - } - - if (typeof baseline === 'string') { - const report = reportsWithRunInsights.find( - report => report.results?.summary?.start?.toString() === baseline - ) - if (!report) { - console.warn(`No report found with start timestamp ID: ${baseline}`) - return null - } - return report - } - - return null -} - /** * @param currentReport - The current CTRF report to enrich - * @param previousReports - Array of historical CTRF reports (ordered newest to oldest) - * @param baseline - Optional baseline specification: - * - undefined: Use most recent previous report (default) - * - number: Use report at this index in previousReports array (0 = most recent) - * - string: Use reportId - * @returns The current report fully enriched with run-level insights, test-level insights, and baseline comparisons + * @param previousReports - Array of historical CTRF reports + * @param baseline - Optional baseline report to compare against. If not provided, no baseline comparisons are made. + * @returns The current report fully enriched with run-level insights, test-level insights, and baseline comparisons (if baseline provided) */ export function enrichReportWithInsights( - currentReport: Report, + currentReport: Report, previousReports: Report[] = [], - baseline?: number | string + baseline?: Report ): Report { if (!validateReportForInsights(currentReport)) { console.warn('Current report is not valid for insights calculation') return currentReport } - const allReports = [currentReport, ...previousReports] + const sortedPreviousReports = sortReportsByTimestamp(previousReports) + + const allReports = [currentReport, ...sortedPreviousReports] const reportsWithRunInsights = calculateRunInsights([...allReports]) - - const baselineReport = previousReports.length > 0 ? - findBaselineReport(reportsWithRunInsights, baseline) : null - - const currentReportWithRunInsights = reportsWithRunInsights[0] // Current report is first - const currentReportWithTestInsights = addTestInsightsWithBaselineToCurrentReport( - currentReportWithRunInsights, - previousReports, - baselineReport || undefined - ) - if (previousReports.length === 0) { - return currentReportWithTestInsights - } + const currentReportWithRunInsights = reportsWithRunInsights[0] + const currentReportWithTestInsights = + addTestInsightsWithBaselineToCurrentReport( + currentReportWithRunInsights, + sortedPreviousReports, + baseline + ) - if (!baselineReport) { + if (!baseline) { return currentReportWithTestInsights } - + let baselineInsights = calculateReportInsightsBaseline( currentReportWithTestInsights, - baselineReport + baseline ) baselineInsights = setTestsAddedToInsights( baselineInsights, currentReportWithTestInsights, - baselineReport + baseline ) - + baselineInsights = setTestsRemovedToInsights( baselineInsights, currentReportWithTestInsights, - baselineReport + baseline ) + // Remove testsAdded and testsRemoved as they're not part of the official schema yet + if (baselineInsights.extra?.testsAdded) { + delete baselineInsights.extra.testsAdded + } + if (baselineInsights.extra?.testsRemoved) { + delete baselineInsights.extra.testsRemoved + } + return { ...currentReportWithTestInsights, - insights: baselineInsights + insights: baselineInsights, } -} \ No newline at end of file +} diff --git a/src/methods/store-previous-results.ts b/src/methods/store-previous-results.ts index a0fdf31..1d68f93 100644 --- a/src/methods/store-previous-results.ts +++ b/src/methods/store-previous-results.ts @@ -1,67 +1,72 @@ // This should be added to library, as not standard CTRF thing -import { Environment, Report } from '../../types/ctrf.js'; +import { Environment, Report } from '../../types/ctrf.js' /** * Interface for a previous result entry stored in the current report */ interface PreviousResult { - start: number; - stop: number; - buildId?: string; + start: number + stop: number + buildId?: string buildName?: string - buildNumber?: string + buildNumber?: number buildUrl?: string - result: string; - tests: number; - passed: number; - failed: number; - skipped: number; - flaky: number; - other: number; - duration: number; - environment?: Environment; + result: string + tests: number + passed: number + failed: number + skipped: number + flaky: number + other: number + duration: number + environment?: Environment } /** * Stores previous results in the current report's previousResults array. * Extracts key metrics from each previous report and adds them to the current report. - * + * * @param currentReport The current CTRF report to enrich with previous results * @param previousReports Array of previous CTRF reports to extract metrics from * @returns The current report with previousResults populated */ export function storePreviousResults( - currentReport: Report, + currentReport: Report, previousReports: Report[] ): Report { if (!currentReport || !Array.isArray(previousReports)) { - throw new Error('Invalid input: currentReport must be a valid CTRF report and previousReports must be an array'); + throw new Error( + 'Invalid input: currentReport must be a valid CTRF report and previousReports must be an array' + ) } if (!currentReport.extra) { - currentReport.extra = {}; + currentReport.extra = {} } - const previousResults: PreviousResult[] = previousReports.map((report) => { + const previousResults: PreviousResult[] = previousReports.map(report => { if (!report.results || !report.results.summary) { - throw new Error('Invalid previous report: missing results or summary'); + throw new Error('Invalid previous report: missing results or summary') } - const summary = report.results.summary; - const tests = report.results.tests || []; - - const flakyCount = tests.filter(test => test.flaky === true).length; - - const duration = summary.stop - summary.start; - - let result = 'passed'; + const summary = report.results.summary + const tests = report.results.tests || [] + + const flakyCount = tests.filter(test => test.flaky === true).length + + const duration = summary.stop - summary.start + + let result = 'passed' if (summary.failed > 0) { - result = 'failed'; - } else if ((summary.skipped > 0 || summary.pending > 0 || summary.other > 0) && summary.passed === 0) { - result = 'skipped'; + result = 'failed' + } else if ( + (summary.skipped > 0 || summary.pending > 0 || summary.other > 0) && + summary.passed === 0 + ) { + result = 'skipped' } else if (summary.tests === 0) { - result = 'empty'; + result = 'empty' } return { @@ -79,13 +84,13 @@ export function storePreviousResults( flaky: flakyCount, other: summary.other, duration, - environment: report.results.environment - }; - }); + environment: report.results.environment, + } + }) - previousResults.sort((a, b) => a.start - b.start); + previousResults.sort((a, b) => a.start - b.start) - currentReport.extra.previousResults = previousResults; + currentReport.extra.previousResults = previousResults - return currentReport; + return currentReport } diff --git a/src/methods/utilities/sort-reports.ts b/src/methods/utilities/sort-reports.ts new file mode 100644 index 0000000..58b0c19 --- /dev/null +++ b/src/methods/utilities/sort-reports.ts @@ -0,0 +1,54 @@ +import { Report } from '../../../types/ctrf' + +/** + * Sort order options for timestamp-based sorting. + */ +export enum SortOrder { + ASC = 'asc', + DESC = 'desc', +} + +/** + * Sorts CTRF reports by their timestamp. + * + * This function uses a fallback strategy for timestamp selection: + * 1. First tries to use `report.timestamp` if available + * 2. Falls back to `report.results.summary.stop` if `timestamp` is not available + * 3. Reports without any timestamp are sorted to the end of the array + * + * @param reports - Array of CTRF reports to sort + * @param order - Sort order: SortOrder.DESC for newest first (default), SortOrder.ASC for oldest first + * @returns A new array with reports sorted by timestamp + * + * @example + * ```typescript + * const unsortedReports = [report1, report2, report3]; + * + * const newestFirst = sortReportsByTimestamp(unsortedReports); + * // newestFirst[0] will be the most recent report + * + * const oldestFirst = sortReportsByTimestamp(unsortedReports, SortOrder.ASC); + * // oldestFirst[0] will be the oldest report + * + + * const newestFirst = sortReportsByTimestamp(unsortedReports, SortOrder.DESC); + * ``` + * + */ +export function sortReportsByTimestamp( + reports: Report[], + order: SortOrder = SortOrder.DESC +): Report[] { + return [...reports].sort((a, b) => { + const aTimestamp = a.timestamp || a.results?.summary?.stop + const bTimestamp = b.timestamp || b.results?.summary?.stop + + if (!aTimestamp && !bTimestamp) return 0 + if (!aTimestamp) return 1 + if (!bTimestamp) return -1 + + const timeDiff = + new Date(bTimestamp).getTime() - new Date(aTimestamp).getTime() + return order === SortOrder.ASC ? -timeDiff : timeDiff + }) +} diff --git a/src/__tests__/test-helpers/report-enrichment-helper.ts b/src/test-utils/report-enrichment-helper.ts similarity index 64% rename from src/__tests__/test-helpers/report-enrichment-helper.ts rename to src/test-utils/report-enrichment-helper.ts index 875a576..99eb672 100644 --- a/src/__tests__/test-helpers/report-enrichment-helper.ts +++ b/src/test-utils/report-enrichment-helper.ts @@ -1,9 +1,12 @@ import * as fs from 'fs' import * as path from 'path' import * as os from 'os' -import { enrichReportsWithInsights, EnrichmentConfig } from '../../../scripts/enrich-reports' -import { Report, Test } from '../../../types/ctrf.js' -import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from '../../constants' +import { + enrichReportsWithInsights, + EnrichmentConfig, +} from '../../scripts/enrich-reports' +import { Report, Test } from '../../types/ctrf.js' +import { CTRF_REPORT_FORMAT, CTRF_SPEC_VERSION } from '../constants' /** * Test helper for creating and testing report enrichment scenarios @@ -25,7 +28,7 @@ export class ReportEnrichmentTestHelper { name: 'test-name', status: 'passed', duration: 100, - ...overrides + ...overrides, } } @@ -48,12 +51,16 @@ export class ReportEnrichmentTestHelper { failed: tests.filter(t => t.status === 'failed').length, skipped: tests.filter(t => t.status === 'skipped').length, pending: tests.filter(t => t.status === 'pending').length, - other: tests.filter(t => !['passed', 'failed', 'skipped', 'pending'].includes(t.status)).length, + other: tests.filter( + t => !['passed', 'failed', 'skipped', 'pending'].includes(t.status) + ).length, + flaky: tests.filter(t => t.flaky === true).length, + duration: tests.reduce((sum, t) => sum + t.duration, 0), start: startTime, - stop: startTime + 5000 + stop: startTime + 5000, }, - tests - } + tests, + }, } } @@ -61,7 +68,10 @@ export class ReportEnrichmentTestHelper { * Add a report to the test scenario */ addReport(report: Report, filename?: string): string { - const reportPath = path.join(this.tempDir, filename || `report-${this.reportPaths.length + 1}.json`) + const reportPath = path.join( + this.tempDir, + filename || `report-${this.reportPaths.length + 1}.json` + ) fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)) this.reportPaths.push(reportPath) return reportPath @@ -70,14 +80,17 @@ export class ReportEnrichmentTestHelper { /** * Add multiple reports with timestamps spaced apart */ - addReportsWithTimestamps(reportsData: Array<{ tests: Test[], toolName?: string }>, intervalMs: number = 10000): string[] { + addReportsWithTimestamps( + reportsData: Array<{ tests: Test[]; toolName?: string }>, + intervalMs: number = 10000 + ): string[] { const baseTime = Date.now() const paths: string[] = [] reportsData.forEach((reportData, index) => { const report = this.createMockReport( reportData.tests, - baseTime - (index * intervalMs), // Earlier reports have smaller timestamps + baseTime - index * intervalMs, // Earlier reports have smaller timestamps reportData.toolName ) const path = this.addReport(report, `report-${index + 1}.json`) @@ -96,33 +109,73 @@ export class ReportEnrichmentTestHelper { reportPaths: string[] } { const currentTests = [ - this.createMockTest({ name: 'stable-test', status: 'passed', duration: 100 }), - this.createMockTest({ name: 'flaky-test', status: 'passed', retries: 2, flaky: true, duration: 150 }), - this.createMockTest({ name: 'failing-test', status: 'failed', duration: 200 }) + this.createMockTest({ + name: 'stable-test', + status: 'passed', + duration: 100, + }), + this.createMockTest({ + name: 'flaky-test', + status: 'passed', + retries: 2, + flaky: true, + duration: 150, + }), + this.createMockTest({ + name: 'failing-test', + status: 'failed', + duration: 200, + }), ] const previousTests1 = [ - this.createMockTest({ name: 'stable-test', status: 'passed', duration: 95 }), - this.createMockTest({ name: 'flaky-test', status: 'failed', duration: 180 }), - this.createMockTest({ name: 'failing-test', status: 'passed', duration: 190 }) + this.createMockTest({ + name: 'stable-test', + status: 'passed', + duration: 95, + }), + this.createMockTest({ + name: 'flaky-test', + status: 'failed', + duration: 180, + }), + this.createMockTest({ + name: 'failing-test', + status: 'passed', + duration: 190, + }), ] const previousTests2 = [ - this.createMockTest({ name: 'stable-test', status: 'passed', duration: 105 }), - this.createMockTest({ name: 'flaky-test', status: 'passed', retries: 1, flaky: true, duration: 160 }), - this.createMockTest({ name: 'failing-test', status: 'failed', duration: 210 }) + this.createMockTest({ + name: 'stable-test', + status: 'passed', + duration: 105, + }), + this.createMockTest({ + name: 'flaky-test', + status: 'passed', + retries: 1, + flaky: true, + duration: 160, + }), + this.createMockTest({ + name: 'failing-test', + status: 'failed', + duration: 210, + }), ] const reportPaths = this.addReportsWithTimestamps([ { tests: currentTests }, { tests: previousTests1 }, - { tests: previousTests2 } + { tests: previousTests2 }, ]) const currentReport = this.createMockReport(currentTests) const previousReports = [ this.createMockReport(previousTests1, Date.now() - 10000), - this.createMockReport(previousTests2, Date.now() - 20000) + this.createMockReport(previousTests2, Date.now() - 20000), ] return { currentReport, previousReports, reportPaths } @@ -131,11 +184,14 @@ export class ReportEnrichmentTestHelper { /** * Run enrichment on the collected reports */ - async enrichReports(outputFilename: string = 'enriched-report.json', verbose: boolean = false): Promise { + async enrichReports( + outputFilename: string = 'enriched-report.json', + verbose: boolean = false + ): Promise { const config: EnrichmentConfig = { inputReports: this.reportPaths, outputPath: path.join(this.tempDir, outputFilename), - verbose + verbose, } return await enrichReportsWithInsights(config) @@ -187,7 +243,7 @@ export async function testReportEnrichment( outputPath?: string ): Promise { const helper = new ReportEnrichmentTestHelper() - + try { // Add all reports reports.forEach((report, index) => { @@ -196,9 +252,9 @@ export async function testReportEnrichment( // Run enrichment const result = await helper.enrichReports(outputPath, false) - + return result } finally { helper.cleanup() } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 4f317f4..005ab5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2023", "module": "ESNext", - "moduleResolution": "node", + "moduleResolution": "bundler", "strict": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, @@ -14,11 +14,10 @@ }, "include": ["src/**/*"], "exclude": [ - "examples/**/*", - "scripts/**/*", - "final/**/*", "src/__tests__/**/*", "dist/**/*", - "node_modules/**/*" + "node_modules/**/*", + "src/test-utils/**/*", + "scripts/**/*" ] } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..d318266 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["vitest/globals", "node"], + "moduleResolution": "bundler", + "allowImportingTsExtensions": false, + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["dist/**/*", "node_modules/**/*"] +} diff --git a/typedoc.json b/typedoc.json index 5fa0a21..633d598 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,20 +1,17 @@ { "entryPoints": ["src/index.ts"], "out": "docs", - "exclude": [ - "src/__tests__/**/*", - "src/**/*.test.ts", - "src/**/*.spec.ts" - ], + "exclude": ["src/__tests__/**/*", "src/**/*.test.ts", "src/**/*.spec.ts"], "excludePrivate": true, "excludeProtected": true, "excludeExternals": true, "includeVersion": true, "categorizeByGroup": true, - "categoryOrder": ["Core", "Methods", "*"], - "readme": "README.md", - "theme": "default", - "name": "CTRF JavaScript Common Library", + "categoryOrder": ["Methods", "*"], + "readme": "none", + "plugin": ["typedoc-plugin-markdown"], + "name": "CTRF ", "githubPages": false, - "gitRevision": "main" -} \ No newline at end of file + "gitRevision": "main", + "cleanOutputDir": true +} diff --git a/types/ctrf.d.ts b/types/ctrf.d.ts index 2ad45a2..cfebc82 100644 --- a/types/ctrf.d.ts +++ b/types/ctrf.d.ts @@ -1,11 +1,12 @@ export interface Report { - reportFormat: string - specVersion: string + reportFormat: 'CTRF' + specVersion: `${number}.${number}.${number}` reportId?: string - timestamp?: number + timestamp?: string generatedBy?: string results: Results insights?: Insights + baseline?: Baseline extra?: Record } @@ -24,16 +25,18 @@ export interface Summary { skipped: number pending: number other: number + flaky?: number suites?: number start: number stop: number + duration?: number extra?: Record } export interface Test { id?: string name: string - status: TestState + status: TestStatus duration: number start?: number stop?: number @@ -48,12 +51,12 @@ export interface Test { type?: string filePath?: string retries?: number + retryAttempts?: RetryAttempt[] flaky?: boolean stdout?: string[] stderr?: string[] threadId?: string attachments?: Attachment[] - retryAttempts?: RetryAttempts[] browser?: string device?: string screenshot?: string @@ -67,17 +70,17 @@ export interface Environment { reportName?: string appName?: string appVersion?: string - osPlatform?: string - osRelease?: string - osVersion?: string buildId?: string buildName?: string - buildNumber?: string + buildNumber?: number buildUrl?: string repositoryName?: string repositoryUrl?: string commit?: string branchName?: string + osPlatform?: string + osRelease?: string + osVersion?: string testEnvironment?: string extra?: Record } @@ -90,7 +93,7 @@ export interface Tool { export interface Step { name: string - status: TestState + status: TestStatus extra?: Record } @@ -98,54 +101,61 @@ export interface Attachment { name: string contentType: string path: string + extra?: Record } -export interface RetryAttempts { - retry: number; - status: TestState; - rawStatus?: string; - duration?: number; - message?: string; - trace?: string; - snippet?: string; - line?: number; - stdout?: string[]; - stderr?: string[]; - start?: number; - stop?: number; - attachments?: Attachment[]; - extra?: Record; +export interface RetryAttempt { + attempt: number + status: TestStatus + duration?: number + message?: string + trace?: string + line?: number + snippet?: string + stdout?: string[] + stderr?: string[] + start?: number + stop?: number + attachments?: Attachment[] + extra?: Record } export interface Insights { - flakyRate: InsightsMetric - failRate: InsightsMetric - skippedRate: InsightsMetric - averageTestDuration: InsightsMetric - averageRunDuration: InsightsMetric - reportsAnalyzed: number + runsAnalyzed?: number + passRate?: InsightsMetric + failRate?: InsightsMetric + flakyRate?: InsightsMetric + averageRunDuration?: InsightsMetric + p95RunDuration?: InsightsMetric + averageTestDuration?: InsightsMetric extra?: Record } export interface TestInsights { - flakyRate: InsightsMetric - failRate: InsightsMetric - skippedRate: InsightsMetric - averageTestDuration: InsightsMetric - p95Duration: InsightsMetric - appearsInRuns: number + passRate?: InsightsMetric + failRate?: InsightsMetric + flakyRate?: InsightsMetric + averageTestDuration?: InsightsMetric + p95TestDuration?: InsightsMetric + executedInRuns?: number extra?: Record } export interface InsightsMetric { current: number - previous: number + baseline: number change: number } -export type TestState = - | 'passed' - | 'failed' - | 'skipped' - | 'pending' - | 'other' +export interface Baseline { + reportId: string + source?: string + timestamp?: string + commit?: string + buildName?: string + buildNumber?: number + buildUrl?: string + extra?: Record +} + +export type TestStatus = 'passed' | 'failed' | 'skipped' | 'pending' | 'other' diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..4ccca2f --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + reporters: ['default', '@d2t/vitest-ctrf-json-reporter'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: [ + 'node_modules/', + 'dist/', + 'coverage/', + '**/*.d.ts', + '**/*.test.ts', + '**/*.spec.ts', + 'src/test-utils/**', + 'ctrf/', + ], + }, + environment: 'node', + globals: false, + include: ['src/**/*.{test,spec}.{js,ts}'], + exclude: ['node_modules/', 'dist/', 'coverage/', 'src/test-utils/**'], + }, +})