From a55a153f4e11d0b65e13bd067bcf696eceba1693 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 12:57:20 -0700 Subject: [PATCH 1/8] chore(deps): upgrade to TypeScript 6, resolve all npm audit advisories Bump TypeScript 5.9 -> 6.0.3, the highest major supported by @typescript-eslint v8 (peer range >=4.8.4 <6.1.0). TypeScript 7 is deliberately skipped: it falls outside that range, so linting would run against an unsupported compiler. TS 6 turns deprecated `baseUrl` into error TS5101, which broke the tsup DTS build - tsup hardcodes `baseUrl: compilerOptions.baseUrl || "."` in its dts pipeline and there is no released fix. Set `ignoreDeprecations` to "6.0" to restore the build; it is a no-op here since no `paths` are configured. Add overrides to clear 4 high-severity advisories in transitive deps of @huggingface/transformers, which pins all of them below the fixed versions with no upstream release available: sharp ^0.34.5 -> ^0.35.3 libvips CVEs (GHSA-f88m-g3jw-g9cj) adm-zip 0.5.x -> ^0.6.0 GHSA-xcpc-8h2w-3j85 Pin esbuild to 0.27.2 for GHSA-g7r4-m6w7-qqqr. tsup requires ^0.27.0 and every version in 0.27.3 - 0.28.0 is affected, leaving 0.27.2 as the only safe in-range choice. This must be an override rather than a lockfile pin because package-lock.json is gitignored, so a fresh install would otherwise re-resolve a vulnerable version. Also applies the pending in-range dev dependency updates (eslint, prettier, vitest, typescript-eslint, @types/node). Verified: npm audit reports 0 vulnerabilities after deleting the lockfile and reinstalling; typecheck, lint, build (incl. DTS), 801 tests and the live ONNX smoke test all pass. --- .agents-docs/AGENTS-development-commands.md | 18 ++++++++++++++++++ package.json | 21 +++++++++++++-------- tsconfig.json | 3 ++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.agents-docs/AGENTS-development-commands.md b/.agents-docs/AGENTS-development-commands.md index b3ac060..409c8a9 100644 --- a/.agents-docs/AGENTS-development-commands.md +++ b/.agents-docs/AGENTS-development-commands.md @@ -33,3 +33,21 @@ npx vitest tests/clustering # watch a directory - `dist/` is the published artifact; `files` in `package.json` ships `dist`, `README.md`, `LICENSE`. - `@huggingface/transformers` is an **optional peer dependency** — present in `devDependencies` for local-provider tests but never required for consumers who don't use the local ONNX provider. - Engines: Node `>=18`. + +## Dependency overrides + +`package.json` declares `overrides` to pull two transitive dependencies of `@huggingface/transformers` above the versions it pins, resolving high-severity advisories that have no upstream fix: + +| Override | Pinned by transformers | Advisory | +|---|---|---| +| `sharp: ^0.35.3` | `^0.34.5` | libvips CVEs (GHSA-f88m-g3jw-g9cj) | +| `adm-zip: ^0.6.0` | `0.5.x` via `onnxruntime-node@1.24.3` | GHSA-xcpc-8h2w-3j85 | + +Both are outside the ranges transformers declares, so **re-run `npm run smoke` after changing them** — it exercises `onnxruntime-node` end-to-end across three models and is the only guard that these overrides remain safe. + +`esbuild` is also pinned to exactly `0.27.2`. tsup declares `esbuild: ^0.27.0`, and every version from `0.27.3` up to `0.28.0` is covered by GHSA-g7r4-m6w7-qqqr, so `0.27.2` is the only in-range version without the advisory. The override (rather than a lockfile pin) is required because `package-lock.json` is gitignored — a fresh `npm install` would otherwise resolve a vulnerable `0.27.x`. Remove the pin once tsup widens its range to `^0.28.1` or later. + +## TypeScript version constraints + +- The project is on **TypeScript 6.0.x**. Do not bump to TypeScript 7 yet: `@typescript-eslint/*` v8 declares `typescript: ">=4.8.4 <6.1.0"`, so `npm run lint` would run against an unsupported compiler. +- `tsconfig.json` sets `"ignoreDeprecations": "6.0"`. This is **required for `npm run build`**, not for our own config — tsup's dts pipeline hardcodes `baseUrl: compilerOptions.baseUrl || "."` (`node_modules/tsup/dist/rollup.js`), and TS 6 raises `TS5101` for deprecated `baseUrl`. Removing the flag breaks the DTS build. It can be dropped once tsup stops injecting `baseUrl`. diff --git a/package.json b/package.json index b569506..16654d0 100644 --- a/package.json +++ b/package.json @@ -65,16 +65,21 @@ "optional": true } }, + "overrides": { + "sharp": "^0.35.3", + "adm-zip": "^0.6.0", + "esbuild": "0.27.2" + }, "devDependencies": { "@huggingface/transformers": "^4.2.0", - "@types/node": "^25.9.1", - "@typescript-eslint/eslint-plugin": "^8.60.0", - "@typescript-eslint/parser": "^8.60.0", - "@vitest/coverage-v8": "^4.1.7", - "eslint": "^10.4.0", - "prettier": "^3.8.3", + "@types/node": "^25.9.5", + "@typescript-eslint/eslint-plugin": "^8.67.0", + "@typescript-eslint/parser": "^8.67.0", + "@vitest/coverage-v8": "^4.1.10", + "eslint": "^10.8.1", + "prettier": "^3.9.6", "tsup": "^8.5.1", - "typescript": "^5.9.3", - "vitest": "^4.1.7" + "typescript": "^6.0.3", + "vitest": "^4.1.10" } } diff --git a/tsconfig.json b/tsconfig.json index 0935853..e592dec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "rootDir": "src", "esModuleInterop": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "ignoreDeprecations": "6.0" }, "include": ["src"] } From cd2068cb33eff2ac194d20b3925f78b470492417 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 12:57:42 -0700 Subject: [PATCH 2/8] fix(scripts): make npm run format work on Windows The format script wrapped its globs in single quotes. POSIX shells strip these, but cmd and PowerShell do not, so prettier received the literal string 'src/**/*.ts', matched zero files, and exited 0 - the script was a silent no-op for anyone developing on Windows. Switching to escaped double quotes works on every platform, since both POSIX shells and cmd strip them. Add a format:check counterpart so the same globs can be enforced without writing, suitable for CI. Without it this failure mode can recur unnoticed. --- .agents-docs/AGENTS-development-commands.md | 3 +++ package.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.agents-docs/AGENTS-development-commands.md b/.agents-docs/AGENTS-development-commands.md index 409c8a9..6143673 100644 --- a/.agents-docs/AGENTS-development-commands.md +++ b/.agents-docs/AGENTS-development-commands.md @@ -11,6 +11,9 @@ | `npm run test:coverage` | Vitest with v8 coverage (`text` + `lcov`). | | `npm run lint` | ESLint over `src/`. | | `npm run format` | Prettier write over `src/**/*.ts` and `tests/**/*.ts`. | +| `npm run format:check` | Prettier check (no writes) — use in CI. | + +Glob arguments in the `format` scripts must use **double** quotes. Single quotes are not stripped by `cmd`/PowerShell, so prettier receives the literal glob, matches nothing, and the script silently succeeds as a no-op on Windows. ## Running a single test diff --git a/package.json b/package.json index 16654d0..04051a1 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "test:watch": "vitest", "test:coverage": "vitest run --coverage", "lint": "eslint src/", - "format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts'", + "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", + "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"", "smoke": "npm run build && node scripts/smoke-test.mjs" }, "license": "Apache-2.0", From 2fb21415bef1c023435b233e832e3d0c98694dd4 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 12:57:56 -0700 Subject: [PATCH 3/8] style: apply Prettier config across src and tests No functional changes - formatting only. Because npm run format silently matched nothing on Windows (fixed in the previous commit), 90 files had been formatted with Prettier's defaults (printWidth 80, trailingComma "all") rather than the repo's .prettierrc (printWidth 100, trailingComma "es5"). This applies the configured style, mostly unwrapping call signatures and argument lists that fit within 100 columns. Verified: lint, typecheck, build and all 801 tests pass unchanged. --- src/aggregation/average.ts | 12 ++--- src/aggregation/combine.ts | 2 +- src/aggregation/index.ts | 8 ++- src/clustering/cluster.ts | 39 ++++++++------ src/clustering/hdbscan.ts | 22 ++++---- src/clustering/metrics.ts | 15 ++---- src/clustering/operations.ts | 9 ++-- src/clustering/optimal.ts | 33 ++++++------ src/clustering/presets.ts | 2 +- src/clustering/statistics.ts | 6 +-- src/eval/metrics.ts | 11 ++-- src/internal/clustering.ts | 5 +- src/internal/concurrency.ts | 7 +-- src/internal/metrics.ts | 4 +- src/internal/validation.ts | 6 +-- src/math/dimensions.ts | 12 +++-- src/math/projection.ts | 4 +- src/models/manager.ts | 7 +-- src/models/registry.ts | 12 ++--- src/pipeline/pipeline.ts | 4 +- src/pipeline/rate-limiter.ts | 7 +-- src/providers/cohere.ts | 35 +++--------- src/providers/factory.ts | 2 +- src/providers/google.ts | 29 ++++------ src/providers/middleware.ts | 10 +--- src/providers/openai-compatible.ts | 25 +++------ src/providers/shared.ts | 14 ++--- src/quantization/calibration.ts | 8 +-- src/quantization/hamming.ts | 10 +--- src/quantization/info.ts | 2 +- src/quantization/quantize.ts | 22 ++++++-- src/search/fusion.ts | 5 +- src/search/hnsw.ts | 23 ++++---- src/search/matrix.ts | 5 +- src/search/mmr.ts | 2 +- src/search/pairwise.ts | 6 +-- src/search/rank.ts | 2 +- src/search/rerank.ts | 5 +- src/search/search-index.ts | 12 +++-- src/search/threshold.ts | 6 +-- src/search/topk.ts | 6 +-- src/storage/cache.ts | 2 +- src/storage/serialize.ts | 13 ++--- src/store/embedding-store.ts | 24 ++++----- src/text/chunking.ts | 10 ++-- src/text/markdown.ts | 8 ++- tests/aggregation/average.test.ts | 11 ++-- tests/aggregation/batch-incremental.test.ts | 24 ++++----- tests/aggregation/combine.test.ts | 10 +--- tests/aggregation/pooling.test.ts | 14 ++++- tests/clustering/cluster.test.ts | 54 ++++++++++++++----- tests/clustering/cohesion.test.ts | 31 ++++++++--- tests/clustering/hdbscan.test.ts | 31 +++++------ tests/clustering/incremental.test.ts | 8 ++- tests/clustering/metrics.test.ts | 4 +- tests/clustering/operations.test.ts | 30 +++++++---- tests/clustering/presets.test.ts | 12 ++++- tests/clustering/redistribution.test.ts | 14 ++--- tests/edge-cases.test.ts | 13 ++--- tests/eval/metrics.test.ts | 12 ++++- .../integration/embed-cluster-search.test.ts | 7 ++- .../integration/incremental-pipeline.test.ts | 4 +- tests/integration/v03-features.test.ts | 36 ++++++++++--- tests/internal/clustering.test.ts | 28 ++++++++-- tests/internal/validation.test.ts | 20 +++++-- tests/math/dimensions.test.ts | 25 ++++++--- tests/models/manager.test.ts | 17 +++--- tests/models/registry.test.ts | 23 ++++++-- tests/pipeline/checkpoint.test.ts | 2 +- tests/pipeline/pipeline.test.ts | 4 +- tests/providers/cohere.test.ts | 5 +- tests/providers/factory.test.ts | 16 +++--- tests/providers/google.test.ts | 19 ++----- tests/providers/middleware.test.ts | 9 +++- tests/providers/shared.test.ts | 27 +++++----- tests/providers/timeout.test.ts | 2 +- tests/quantization/calibration.test.ts | 6 ++- tests/search/filter.test.ts | 8 +-- tests/search/hnsw.test.ts | 2 +- tests/search/mmr.test.ts | 8 +-- tests/search/pairwise.test.ts | 15 ++++-- tests/search/rank.test.ts | 6 +-- tests/search/rerank.test.ts | 8 ++- tests/search/threshold.test.ts | 10 ++-- tests/search/topk.test.ts | 6 +-- tests/search/validation.test.ts | 27 +++++++--- tests/storage/serialize-v2.test.ts | 5 +- tests/store/embedding-store.test.ts | 2 +- tests/text/create-tokenizer.test.ts | 4 +- tests/text/markdown.test.ts | 11 ++-- 90 files changed, 631 insertions(+), 512 deletions(-) diff --git a/src/aggregation/average.ts b/src/aggregation/average.ts index 996bf81..7d4cdbd 100644 --- a/src/aggregation/average.ts +++ b/src/aggregation/average.ts @@ -74,7 +74,7 @@ export function weightedAverage(embeddings: Vector[], weights: number[]): Float3 validateEmbeddings(embeddings); if (embeddings.length !== weights.length) { throw new ValidationError( - `Embeddings length (${embeddings.length}) must match weights length (${weights.length})`, + `Embeddings length (${embeddings.length}) must match weights length (${weights.length})` ); } const dim = embeddings[0].length; @@ -117,12 +117,10 @@ export function weightedAverage(embeddings: Vector[], weights: number[]): Float3 export function incrementalAverage( currentAvg: Vector, newEmbedding: Vector, - count: number, + count: number ): Float32Array { if (currentAvg.length !== newEmbedding.length) { - throw new ValidationError( - `Dimension mismatch: ${currentAvg.length} vs ${newEmbedding.length}`, - ); + throw new ValidationError(`Dimension mismatch: ${currentAvg.length} vs ${newEmbedding.length}`); } const dim = currentAvg.length; const result = new Float32Array(dim); @@ -150,7 +148,7 @@ export function incrementalAverage( export function batchIncrementalAverage( currentAvg: Vector, newEmbeddings: Vector[], - count: number, + count: number ): Float32Array { if (newEmbeddings.length === 0) { throw new ValidationError('newEmbeddings array must be non-empty'); @@ -159,7 +157,7 @@ export function batchIncrementalAverage( for (let i = 0; i < newEmbeddings.length; i++) { if (newEmbeddings[i].length !== dim) { throw new ValidationError( - `Dimension mismatch: expected ${dim}, got ${newEmbeddings[i].length} at index ${i}`, + `Dimension mismatch: expected ${dim}, got ${newEmbeddings[i].length} at index ${i}` ); } } diff --git a/src/aggregation/combine.ts b/src/aggregation/combine.ts index def1a42..c1c9f65 100644 --- a/src/aggregation/combine.ts +++ b/src/aggregation/combine.ts @@ -23,7 +23,7 @@ export async function combineEmbeddings( options?: { aggregate?: (embeddings: Vector[]) => Float32Array; embedOptions?: EmbedOptions; - }, + } ): Promise { if (texts.length === 0) { throw new ValidationError('texts array must be non-empty'); diff --git a/src/aggregation/index.ts b/src/aggregation/index.ts index 1408bd8..83c2fe8 100644 --- a/src/aggregation/index.ts +++ b/src/aggregation/index.ts @@ -1,3 +1,9 @@ -export { averageEmbeddings, weightedAverage, incrementalAverage, batchIncrementalAverage, centroid } from './average'; +export { + averageEmbeddings, + weightedAverage, + incrementalAverage, + batchIncrementalAverage, + centroid, +} from './average'; export { maxPooling, minPooling } from './pooling'; export { combineEmbeddings } from './combine'; diff --git a/src/clustering/cluster.ts b/src/clustering/cluster.ts index 07478cd..ac750d3 100644 --- a/src/clustering/cluster.ts +++ b/src/clustering/cluster.ts @@ -15,10 +15,7 @@ const DEFAULT_CONFIG: Required = { shuffleSeed: 42, }; -function findMostSimilarPair( - clusters: Cluster[], - metric: SimilarityMetric, -): [number, number] { +function findMostSimilarPair(clusters: Cluster[], metric: SimilarityMetric): [number, number] { let bestI = 0; let bestJ = 1; let bestSim = -Infinity; @@ -37,9 +34,7 @@ function findMostSimilarPair( function mergeTwoClusters(a: Cluster, b: Cluster, metric: SimilarityMetric): Cluster { const members = [...a.members, ...b.members]; - const labels = a.labels || b.labels - ? [...(a.labels || []), ...(b.labels || [])] - : undefined; + const labels = a.labels || b.labels ? [...(a.labels || []), ...(b.labels || [])] : undefined; return { centroid: computeCentroid(members), members, @@ -60,13 +55,13 @@ function mergeTwoClusters(a: Cluster, b: Cluster, metric: SimilarityMetric): Clu export function clusterEmbeddings( embeddings: Vector[], config?: ClusteringConfig, - labels?: string[], + labels?: string[] ): Cluster[] { if (embeddings.length === 0) return []; if (labels && labels.length !== embeddings.length) { throw new ValidationError( - `Labels length (${labels.length}) must match embeddings length (${embeddings.length})`, + `Labels length (${labels.length}) must match embeddings length (${embeddings.length})` ); } @@ -78,7 +73,15 @@ export function clusterEmbeddings( if (cfg.minClusterSize < 0) { throw new ValidationError(`minClusterSize must be >= 0, got ${cfg.minClusterSize}`); } - const { similarityThreshold, minClusterSize, maxClusters, metric, assignmentStrategy, shuffle, shuffleSeed } = cfg; + const { + similarityThreshold, + minClusterSize, + maxClusters, + metric, + assignmentStrategy, + shuffle, + shuffleSeed, + } = cfg; // Convert all inputs to Float32Array up front const float32Embeddings = embeddings.map((e) => toFloat32(e)); @@ -192,13 +195,15 @@ export function clusterEmbeddings( allLabels.push(...cluster.labels); } } - result = [{ - centroid: computeCentroid(allMembers), - members: allMembers, - labels: hasLabels ? allLabels : undefined, - size: allMembers.length, - cohesion: 1.0, - }]; + result = [ + { + centroid: computeCentroid(allMembers), + members: allMembers, + labels: hasLabels ? allLabels : undefined, + size: allMembers.length, + cohesion: 1.0, + }, + ]; } else { result = []; } diff --git a/src/clustering/hdbscan.ts b/src/clustering/hdbscan.ts index 77bcc92..c7334ec 100644 --- a/src/clustering/hdbscan.ts +++ b/src/clustering/hdbscan.ts @@ -69,9 +69,9 @@ interface MSTEdge { /** An entry in the condensed tree. */ interface CondensedTreeEntry { - parent: number; // condensed cluster ID - child: number; // point index (if childSize===1) or condensed cluster ID - lambda: number; // 1/distance at which this separation happens + parent: number; // condensed cluster ID + child: number; // point index (if childSize===1) or condensed cluster ID + lambda: number; // 1/distance at which this separation happens childSize: number; // 1 for individual point, >1 for sub-cluster } @@ -107,7 +107,11 @@ function computeDistanceMatrix(embeddings: Vector[], distFn: DistanceFn): Float6 return matrix; } -function computeCoreDistances(distMatrix: Float64Array, n: number, minSamples: number): Float64Array { +function computeCoreDistances( + distMatrix: Float64Array, + n: number, + minSamples: number +): Float64Array { const coreDistances = new Float64Array(n); const k = Math.min(minSamples, n - 1); @@ -125,7 +129,7 @@ function computeCoreDistances(distMatrix: Float64Array, n: number, minSamples: n function computeMutualReachabilityMatrix( distMatrix: Float64Array, coreDistances: Float64Array, - n: number, + n: number ): Float64Array { const mrd = new Float64Array(n * n); for (let i = 0; i < n; i++) { @@ -231,7 +235,7 @@ class UnionFind { function buildCondensedTree( edges: MSTEdge[], n: number, - minClusterSize: number, + minClusterSize: number ): CondensedTreeEntry[] { // Step 1: Build single-linkage hierarchy const uf = new UnionFind(n); @@ -331,9 +335,7 @@ function buildCondensedTree( * * Returns the set of point indices for each selected cluster. */ -function extractClusters( - condensedTree: CondensedTreeEntry[], -): number[][] { +function extractClusters(condensedTree: CondensedTreeEntry[]): number[][] { if (condensedTree.length === 0) return []; // Identify all condensed cluster IDs @@ -508,7 +510,7 @@ export function hdbscan(embeddings: Vector[], options: HDBSCANOptions = {}): HDB // Validate labels length if (inputLabels && inputLabels.length !== embeddings.length) { throw new ValidationError( - `Labels length (${inputLabels.length}) must match embeddings length (${embeddings.length})`, + `Labels length (${inputLabels.length}) must match embeddings length (${embeddings.length})` ); } diff --git a/src/clustering/metrics.ts b/src/clustering/metrics.ts index 3d49a38..bc3a749 100644 --- a/src/clustering/metrics.ts +++ b/src/clustering/metrics.ts @@ -29,10 +29,7 @@ import { computePairwiseCohesion } from '../internal/clustering'; * const quality = cohesionScore(cluster); * if (quality < 0.7) console.warn('Cluster is loosely grouped'); */ -export function cohesionScore( - cluster: Cluster, - metric: SimilarityMetric = 'cosine', -): number { +export function cohesionScore(cluster: Cluster, metric: SimilarityMetric = 'cosine'): number { return computePairwiseCohesion(cluster.members, metric); } @@ -66,10 +63,7 @@ export function cohesionScore( * if (quality < 0.7) console.warn('Loose cluster detected'); * } */ -export function centroidCohesion( - cluster: Cluster, - metric: SimilarityMetric = 'cosine', -): number { +export function centroidCohesion(cluster: Cluster, metric: SimilarityMetric = 'cosine'): number { if (cluster.members.length <= 1) return 1.0; let totalSim = 0; @@ -115,10 +109,7 @@ export function centroidCohesion( * console.log(`threshold=${threshold}: silhouette=${silhouetteScore(clusters).toFixed(3)}`); * } */ -export function silhouetteScore( - clusters: Cluster[], - metric: SimilarityMetric = 'cosine', -): number { +export function silhouetteScore(clusters: Cluster[], metric: SimilarityMetric = 'cosine'): number { if (clusters.length <= 1) return 0; let totalSilhouette = 0; diff --git a/src/clustering/operations.ts b/src/clustering/operations.ts index d3090cc..f6d99cf 100644 --- a/src/clustering/operations.ts +++ b/src/clustering/operations.ts @@ -48,7 +48,7 @@ import { computeCentroid, computePairwiseCohesion } from '../internal/clustering export function assignToCluster( embedding: Vector, clusters: Cluster[], - options?: { metric?: SimilarityMetric; threshold?: number }, + options?: { metric?: SimilarityMetric; threshold?: number } ): { clusterIndex: number; similarity: number } { const metric = options?.metric ?? 'cosine'; const threshold = options?.threshold ?? 0; @@ -97,13 +97,10 @@ export function assignToCluster( export function mergeClusters( a: Cluster, b: Cluster, - metric: SimilarityMetric = 'cosine', + metric: SimilarityMetric = 'cosine' ): Cluster { const members = [...a.members, ...b.members]; - const labels = - a.labels || b.labels - ? [...(a.labels || []), ...(b.labels || [])] - : undefined; + const labels = a.labels || b.labels ? [...(a.labels || []), ...(b.labels || [])] : undefined; return { centroid: computeCentroid(members), diff --git a/src/clustering/optimal.ts b/src/clustering/optimal.ts index 3972ea4..b186e03 100644 --- a/src/clustering/optimal.ts +++ b/src/clustering/optimal.ts @@ -17,7 +17,7 @@ function meanSilhouette( embeddings: Vector[], assignments: number[], k: number, - metric: SimilarityMetric, + metric: SimilarityMetric ): number { if (k <= 1) return 0; @@ -71,7 +71,7 @@ function withinClusterSumOfDistances( embeddings: Vector[], assignments: number[], k: number, - metric: SimilarityMetric, + metric: SimilarityMetric ): number { // Compute centroid for each cluster const clusterMembers: Vector[][] = Array.from({ length: k }, () => []); @@ -93,19 +93,19 @@ function withinClusterSumOfDistances( /** * Runs clustering for a given k and returns flat assignment array. */ -function clusterAndAssign( - embeddings: Vector[], - k: number, - metric: SimilarityMetric, -): number[] { +function clusterAndAssign(embeddings: Vector[], k: number, metric: SimilarityMetric): number[] { // Use labels to track original indices through clustering const indexLabels = embeddings.map((_, i) => String(i)); - const clusters = clusterEmbeddings(embeddings, { - maxClusters: k, - minClusterSize: 1, - similarityThreshold: 0, - metric, - }, indexLabels); + const clusters = clusterEmbeddings( + embeddings, + { + maxClusters: k, + minClusterSize: 1, + similarityThreshold: 0, + metric, + }, + indexLabels + ); // Build assignment map using labels (original indices) const assignments = new Array(embeddings.length).fill(0); @@ -133,10 +133,7 @@ function clusterAndAssign( * @param options - Configuration: minK, maxK, metric, method * @returns The optimal k value */ -export function findOptimalK( - embeddings: Vector[], - options?: OptimalKOptions, -): number { +export function findOptimalK(embeddings: Vector[], options?: OptimalKOptions): number { const method = options?.method ?? 'silhouette'; const metric = options?.metric ?? 'cosine'; const minK = options?.minK ?? 2; @@ -196,7 +193,7 @@ export function findOptimalK( */ export function silhouetteByK( embeddings: Vector[], - options?: Omit, + options?: Omit ): Array<{ k: number; silhouette: number }> { const metric = options?.metric ?? 'cosine'; const minK = options?.minK ?? 2; diff --git a/src/clustering/presets.ts b/src/clustering/presets.ts index f72cbc2..9cada04 100644 --- a/src/clustering/presets.ts +++ b/src/clustering/presets.ts @@ -67,7 +67,7 @@ export const CLUSTERING_PRESETS = { * const clusters = clusterEmbeddings(embeddings, getPreset('legacy')); */ export function getPreset( - name: 'high-precision' | 'balanced' | 'performance' | 'legacy', + name: 'high-precision' | 'balanced' | 'performance' | 'legacy' ): ClusteringConfig { switch (name) { case 'high-precision': diff --git a/src/clustering/statistics.ts b/src/clustering/statistics.ts index 109db81..1bc884c 100644 --- a/src/clustering/statistics.ts +++ b/src/clustering/statistics.ts @@ -10,7 +10,7 @@ import { computeScore, computeDistance } from '../internal/metrics'; */ export function clusterStats( cluster: { centroid: Vector; members: Vector[] }, - metric: SimilarityMetric = 'cosine', + metric: SimilarityMetric = 'cosine' ): ClusterStats { const { centroid, members } = cluster; @@ -65,7 +65,7 @@ export function clusterStats( */ export function detectOutliers( cluster: { centroid: Vector; members: Vector[] }, - options?: { threshold?: number; metric?: SimilarityMetric }, + options?: { threshold?: number; metric?: SimilarityMetric } ): number[] { const metric = options?.metric ?? 'cosine'; const threshold = options?.threshold ?? 2; @@ -100,7 +100,7 @@ export function detectOutliers( export function centroidDrift( oldCentroid: Vector, newCentroid: Vector, - metric: SimilarityMetric = 'cosine', + metric: SimilarityMetric = 'cosine' ): number { return computeDistance(oldCentroid, newCentroid, metric); } diff --git a/src/eval/metrics.ts b/src/eval/metrics.ts index fbd4af5..86b6db5 100644 --- a/src/eval/metrics.ts +++ b/src/eval/metrics.ts @@ -16,7 +16,7 @@ export function recallAtK( retrieved: string[], relevant: Set | string[], - k?: number, + k?: number ): number { const relevantSet = relevant instanceof Set ? relevant : new Set(relevant); if (relevantSet.size === 0 || retrieved.length === 0) return 0; @@ -43,7 +43,7 @@ export function recallAtK( export function ndcg( retrieved: string[], relevanceScores: Map | Record, - k?: number, + k?: number ): number { if (retrieved.length === 0) return 0; @@ -87,10 +87,7 @@ export function ndcg( * @param relevant - Set or array of relevant item identifiers * @returns MRR score in [0, 1] */ -export function mrr( - retrieved: string[], - relevant: Set | string[], -): number { +export function mrr(retrieved: string[], relevant: Set | string[]): number { const relevantSet = relevant instanceof Set ? relevant : new Set(relevant); if (relevantSet.size === 0 || retrieved.length === 0) return 0; @@ -113,7 +110,7 @@ export function mrr( */ export function meanAveragePrecision( retrieved: string[], - relevant: Set | string[], + relevant: Set | string[] ): number { const relevantSet = relevant instanceof Set ? relevant : new Set(relevant); if (relevantSet.size === 0 || retrieved.length === 0) return 0; diff --git a/src/internal/clustering.ts b/src/internal/clustering.ts index fdcd37a..1f966f7 100644 --- a/src/internal/clustering.ts +++ b/src/internal/clustering.ts @@ -23,10 +23,7 @@ export function computeCentroid(members: Vector[]): Float32Array { * Computes the average pairwise similarity among members. * @internal */ -export function computePairwiseCohesion( - members: Vector[], - metric: SimilarityMetric, -): number { +export function computePairwiseCohesion(members: Vector[], metric: SimilarityMetric): number { if (members.length <= 1) return 1.0; let totalSim = 0; let pairs = 0; diff --git a/src/internal/concurrency.ts b/src/internal/concurrency.ts index 8474a80..a05d596 100644 --- a/src/internal/concurrency.ts +++ b/src/internal/concurrency.ts @@ -9,7 +9,7 @@ */ export async function promisePool( tasks: Array<() => Promise>, - maxConcurrency: number, + maxConcurrency: number ): Promise { const results = new Array(tasks.length); let nextIndex = 0; @@ -27,10 +27,7 @@ export async function promisePool( } } - const workers = Array.from( - { length: Math.min(maxConcurrency, tasks.length) }, - () => runNext(), - ); + const workers = Array.from({ length: Math.min(maxConcurrency, tasks.length) }, () => runNext()); await Promise.all(workers); return results; diff --git a/src/internal/metrics.ts b/src/internal/metrics.ts index 52ea1db..6f969ff 100644 --- a/src/internal/metrics.ts +++ b/src/internal/metrics.ts @@ -20,7 +20,7 @@ export function computeScore(a: Vector, b: Vector, metric: SimilarityMetric): nu case 'manhattan': return 1 / (1 + manhattanDistance(a, b)); default: - throw new ValidationError(`Unknown similarity metric: ${(metric as string)}`); + throw new ValidationError(`Unknown similarity metric: ${metric as string}`); } } @@ -41,6 +41,6 @@ export function computeDistance(a: Vector, b: Vector, metric: SimilarityMetric): case 'manhattan': return manhattanDistance(a, b); default: - throw new ValidationError(`Unknown similarity metric: ${(metric as string)}`); + throw new ValidationError(`Unknown similarity metric: ${metric as string}`); } } diff --git a/src/internal/validation.ts b/src/internal/validation.ts index 28c0382..a290ffb 100644 --- a/src/internal/validation.ts +++ b/src/internal/validation.ts @@ -41,14 +41,14 @@ export function validateVectorPair( a: Vector, b: Vector, nameA = 'Vector A', - nameB = 'Vector B', + nameB = 'Vector B' ): void { if (a.length === 0 || b.length === 0) { throw new ValidationError('Vectors must be non-empty'); } if (a.length !== b.length) { throw new DimensionMismatchError( - `Dimension mismatch: ${nameA} has ${a.length} dimensions, ${nameB} has ${b.length}`, + `Dimension mismatch: ${nameA} has ${a.length} dimensions, ${nameB} has ${b.length}` ); } } @@ -65,7 +65,7 @@ export function validateEmbeddings(embeddings: Vector[], name = 'Embeddings'): v for (let i = 1; i < embeddings.length; i++) { if (embeddings[i].length !== dim) { throw new DimensionMismatchError( - `Dimension mismatch: expected ${dim}, got ${embeddings[i].length} at index ${i}`, + `Dimension mismatch: expected ${dim}, got ${embeddings[i].length} at index ${i}` ); } } diff --git a/src/math/dimensions.ts b/src/math/dimensions.ts index 55fde75..ad9385d 100644 --- a/src/math/dimensions.ts +++ b/src/math/dimensions.ts @@ -52,7 +52,11 @@ export function truncateDimensions( } // Check if input is a batch (array of vectors) - if (Array.isArray(input) && input.length > 0 && (Array.isArray(input[0]) || input[0] instanceof Float32Array)) { + if ( + Array.isArray(input) && + input.length > 0 && + (Array.isArray(input[0]) || input[0] instanceof Float32Array) + ) { const batch = input as Vector[]; return batch.map((v) => truncateSingle(v, targetDims)); } @@ -62,9 +66,7 @@ export function truncateDimensions( function truncateSingle(v: Vector, targetDims: number): Float32Array { if (targetDims > v.length) { - throw new ValidationError( - `targetDims (${targetDims}) exceeds vector length (${v.length})` - ); + throw new ValidationError(`targetDims (${targetDims}) exceeds vector length (${v.length})`); } let result: Float32Array; if (v instanceof Float32Array) { @@ -92,7 +94,7 @@ function truncateSingle(v: Vector, targetDims: number): Float32Array { */ export function validateDimensions( embeddings: Vector[], - expectedDim?: number, + expectedDim?: number ): { valid: boolean; dimension: number; mismatches: number[] } { if (embeddings.length === 0) { return { valid: true, dimension: expectedDim ?? 0, mismatches: [] }; diff --git a/src/math/projection.ts b/src/math/projection.ts index 0b90640..97a30c0 100644 --- a/src/math/projection.ts +++ b/src/math/projection.ts @@ -52,11 +52,11 @@ function gaussianRandom(rng: () => number): number { export function createRandomProjection( sourceDims: number, targetDims: number, - options?: { seed?: number }, + options?: { seed?: number } ): RandomProjector { if (targetDims > sourceDims) { throw new ValidationError( - `targetDims (${targetDims}) must not exceed sourceDims (${sourceDims})`, + `targetDims (${targetDims}) must not exceed sourceDims (${sourceDims})` ); } if (sourceDims <= 0 || targetDims <= 0) { diff --git a/src/models/manager.ts b/src/models/manager.ts index f0acc22..addd14e 100644 --- a/src/models/manager.ts +++ b/src/models/manager.ts @@ -49,7 +49,7 @@ export function getModelInfo(modelId: string): ModelInfo | undefined { */ export async function downloadModel( modelId: string, - options?: { cacheDir?: string }, + options?: { cacheDir?: string } ): Promise { const transformers = await import('@huggingface/transformers'); const cacheDir = options?.cacheDir ?? getDefaultCacheDir(); @@ -108,10 +108,7 @@ export async function listModels(cacheDir?: string): Promise { * @example * await deleteModel('Xenova/all-MiniLM-L6-v2'); */ -export async function deleteModel( - modelId: string, - cacheDir?: string, -): Promise { +export async function deleteModel(modelId: string, cacheDir?: string): Promise { const fs = await import('node:fs/promises'); const path = await import('node:path'); const dir = cacheDir ?? getDefaultCacheDir(); diff --git a/src/models/registry.ts b/src/models/registry.ts index c911f9c..bd29196 100644 --- a/src/models/registry.ts +++ b/src/models/registry.ts @@ -102,8 +102,7 @@ export const MODEL_REGISTRY: Record = { id: 'onnx-community/gte-multilingual-base', dimensions: 768, maxTokens: 8192, - description: - 'GTE multilingual base (70+ languages), long-context, Matryoshka-truncatable', + description: 'GTE multilingual base (70+ languages), long-context, Matryoshka-truncatable', size: '305M', pooling: 'cls', }, @@ -298,10 +297,7 @@ export const MODEL_REGISTRY: Record = { * @example * registerModel({ id: 'my-org/custom-model', dimensions: 512, maxTokens: 1024, description: 'Custom model' }); */ -export function registerModel( - info: ModelInfo, - options?: { overwrite?: boolean }, -): void { +export function registerModel(info: ModelInfo, options?: { overwrite?: boolean }): void { if (!info.id || typeof info.id !== 'string') { throw new ValidationError('Model id must be a non-empty string'); } @@ -310,7 +306,7 @@ export function registerModel( } if (MODEL_REGISTRY[info.id] && !options?.overwrite) { throw new ValidationError( - `Model "${info.id}" is already registered. Pass { overwrite: true } to replace it.`, + `Model "${info.id}" is already registered. Pass { overwrite: true } to replace it.` ); } MODEL_REGISTRY[info.id] = info; @@ -326,7 +322,7 @@ export function registerModel( * getRecommendedModel('multilingual'); // multilingual-MiniLM-L12-v2 */ export function getRecommendedModel( - useCase: 'speed' | 'balanced' | 'quality' | 'multilingual', + useCase: 'speed' | 'balanced' | 'quality' | 'multilingual' ): ModelInfo { switch (useCase) { case 'speed': diff --git a/src/pipeline/pipeline.ts b/src/pipeline/pipeline.ts index dadfaa1..710397f 100644 --- a/src/pipeline/pipeline.ts +++ b/src/pipeline/pipeline.ts @@ -83,7 +83,7 @@ class Semaphore { */ export function createEmbeddingPipeline( provider: EmbeddingProvider, - options: PipelineOptions = {}, + options: PipelineOptions = {} ): EmbeddingPipeline { const { batchSize = 100, @@ -207,7 +207,7 @@ export function createEmbeddingPipeline( async function saveCheckpoint( checkpoint: CheckpointAdapter, completedIds: Set, - totalProcessed: number, + totalProcessed: number ): Promise { await checkpoint.save({ completedIds: [...completedIds], diff --git a/src/pipeline/rate-limiter.ts b/src/pipeline/rate-limiter.ts index b6474cc..d85cf19 100644 --- a/src/pipeline/rate-limiter.ts +++ b/src/pipeline/rate-limiter.ts @@ -52,12 +52,9 @@ export class TokenBucketRateLimiter { this.requestTokens = Math.min( this.maxRequests, - this.requestTokens + this.maxRequests * fraction, - ); - this.tokenBucket = Math.min( - this.maxTokens, - this.tokenBucket + this.maxTokens * fraction, + this.requestTokens + this.maxRequests * fraction ); + this.tokenBucket = Math.min(this.maxTokens, this.tokenBucket + this.maxTokens * fraction); this.lastRefill = now; } diff --git a/src/providers/cohere.ts b/src/providers/cohere.ts index 19cae1e..6be6862 100644 --- a/src/providers/cohere.ts +++ b/src/providers/cohere.ts @@ -1,9 +1,4 @@ -import type { - EmbeddingProvider, - EmbeddingResult, - EmbedOptions, - CohereConfig, -} from '../types'; +import type { EmbeddingProvider, EmbeddingResult, EmbedOptions, CohereConfig } from '../types'; import { ProviderError, ValidationError } from '../types'; import { toFloat32 } from '../internal/vector-utils'; import { retryWithBackoff, autoBatch, createTimeoutSignal, wrapTimeoutError } from './shared'; @@ -33,7 +28,7 @@ export function createCohereProvider(config: CohereConfig): EmbeddingProvider { async function embedBatch( batch: string[], inputType: string, - signal?: AbortSignal, + signal?: AbortSignal ): Promise<{ embeddings: number[][]; tokens: number }> { const fetchSignal = createTimeoutSignal(config.timeout, signal); const result = await retryWithBackoff( @@ -60,32 +55,21 @@ export function createCohereProvider(config: CohereConfig): EmbeddingProvider { } catch { body = response.statusText; } - throw new ProviderError( - `HTTP ${response.status}: ${body}`, - 'cohere', - response.status, - ); + throw new ProviderError(`HTTP ${response.status}: ${body}`, 'cohere', response.status); } try { - return await response.json() as CohereEmbeddingResponse; + return (await response.json()) as CohereEmbeddingResponse; } catch { - throw new ProviderError( - 'Failed to parse API response', - 'cohere', - response.status, - ); + throw new ProviderError('Failed to parse API response', 'cohere', response.status); } }, retryConfig, - signal, + signal ); if (!result.embeddings?.float || !Array.isArray(result.embeddings.float)) { - throw new ProviderError( - 'Unexpected API response structure from cohere', - 'cohere', - ); + throw new ProviderError('Unexpected API response structure from cohere', 'cohere'); } const embeddings = result.embeddings.float; @@ -97,10 +81,7 @@ export function createCohereProvider(config: CohereConfig): EmbeddingProvider { name: 'cohere', dimensions: config.dimensions ?? null, - async embed( - input: string | string[], - options?: EmbedOptions, - ): Promise { + async embed(input: string | string[], options?: EmbedOptions): Promise { const inputs = Array.isArray(input) ? input : [input]; for (const text of inputs) { if (text.trim().length === 0) { diff --git a/src/providers/factory.ts b/src/providers/factory.ts index eab2284..fd6c28a 100644 --- a/src/providers/factory.ts +++ b/src/providers/factory.ts @@ -32,7 +32,7 @@ const ALIAS_BASE_URLS: Record = { */ export function createProvider( type: T, - config: ProviderConfigMap[T], + config: ProviderConfigMap[T] ): EmbeddingProvider { switch (type) { case 'local': diff --git a/src/providers/google.ts b/src/providers/google.ts index 053acb7..decc75e 100644 --- a/src/providers/google.ts +++ b/src/providers/google.ts @@ -23,9 +23,7 @@ const MAX_BATCH_SIZE = 5; * }); * const result = await provider.embed('hello world'); */ -export function createGoogleVertexProvider( - config: GoogleVertexConfig, -): EmbeddingProvider { +export function createGoogleVertexProvider(config: GoogleVertexConfig): EmbeddingProvider { const location = config.location ?? DEFAULT_LOCATION; const model = config.model ?? DEFAULT_MODEL; const retryConfig = config.retry ?? { maxRetries: 3, baseDelay: 1000, maxDelay: 30000 }; @@ -41,7 +39,7 @@ export function createGoogleVertexProvider( async function embedBatch( batch: string[], - signal?: AbortSignal, + signal?: AbortSignal ): Promise<{ embeddings: number[][] }> { const token = await resolveToken(); const fetchSignal = createTimeoutSignal(config.timeout, signal); @@ -70,34 +68,28 @@ export function createGoogleVertexProvider( throw new ProviderError( `HTTP ${response.status}: ${body}`, 'google-vertex', - response.status, + response.status ); } try { - return await response.json() as GoogleVertexEmbeddingResponse; + return (await response.json()) as GoogleVertexEmbeddingResponse; } catch { - throw new ProviderError( - 'Failed to parse API response', - 'google-vertex', - response.status, - ); + throw new ProviderError('Failed to parse API response', 'google-vertex', response.status); } }, retryConfig, - signal, + signal ); if (!Array.isArray(result.predictions) || !result.predictions[0]?.embeddings?.values) { throw new ProviderError( 'Unexpected API response structure from google-vertex', - 'google-vertex', + 'google-vertex' ); } - const embeddings = result.predictions.map( - (p) => p.embeddings.values, - ); + const embeddings = result.predictions.map((p) => p.embeddings.values); return { embeddings }; } @@ -105,10 +97,7 @@ export function createGoogleVertexProvider( name: 'google-vertex', dimensions: config.retry ? null : null, - async embed( - input: string | string[], - options?: EmbedOptions, - ): Promise { + async embed(input: string | string[], options?: EmbedOptions): Promise { const inputs = Array.isArray(input) ? input : [input]; for (const text of inputs) { if (text.trim().length === 0) { diff --git a/src/providers/middleware.ts b/src/providers/middleware.ts index 7b68332..9ed24cd 100644 --- a/src/providers/middleware.ts +++ b/src/providers/middleware.ts @@ -8,10 +8,7 @@ import { createLRUCache } from '../storage/cache'; * @param options - Cache configuration (maxSize, ttl, hashFunction) * @returns A new EmbeddingProvider that caches results from the wrapped provider */ -export function withCache( - provider: EmbeddingProvider, - options?: CacheOptions, -): EmbeddingProvider { +export function withCache(provider: EmbeddingProvider, options?: CacheOptions): EmbeddingProvider { const cache = createLRUCache({ maxSize: options?.maxSize ?? 1000, ttl: options?.ttl, @@ -31,10 +28,7 @@ export function withCache( name: provider.name, dimensions: provider.dimensions, - async embed( - input: string | string[], - options?: EmbedOptions, - ): Promise { + async embed(input: string | string[], options?: EmbedOptions): Promise { const inputs = Array.isArray(input) ? input : [input]; const key = buildKey(inputs, options?.dimensions); diff --git a/src/providers/openai-compatible.ts b/src/providers/openai-compatible.ts index bc19d4e..8f7a17a 100644 --- a/src/providers/openai-compatible.ts +++ b/src/providers/openai-compatible.ts @@ -33,9 +33,7 @@ function extractProviderName(baseUrl: string): string { * }); * const result = await provider.embed(['hello', 'world']); */ -export function createOpenAICompatibleProvider( - config: OpenAICompatibleConfig, -): EmbeddingProvider { +export function createOpenAICompatibleProvider(config: OpenAICompatibleConfig): EmbeddingProvider { const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL; const maxBatchSize = config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE; const retryConfig = config.retry ?? { maxRetries: 3, baseDelay: 1000, maxDelay: 30000 }; @@ -44,7 +42,7 @@ export function createOpenAICompatibleProvider( async function embedBatch( batch: string[], - signal?: AbortSignal, + signal?: AbortSignal ): Promise<{ embeddings: number[][]; tokens: number }> { const fetchSignal = createTimeoutSignal(config.timeout, signal); const result = await retryWithBackoff( @@ -77,28 +75,24 @@ export function createOpenAICompatibleProvider( throw new ProviderError( `HTTP ${response.status}: ${body}`, providerName, - response.status, + response.status ); } try { - return await response.json() as OpenAIEmbeddingResponse; + return (await response.json()) as OpenAIEmbeddingResponse; } catch { - throw new ProviderError( - 'Failed to parse API response', - providerName, - response.status, - ); + throw new ProviderError('Failed to parse API response', providerName, response.status); } }, retryConfig, - signal, + signal ); if (!Array.isArray(result.data) || !result.data[0]?.embedding) { throw new ProviderError( 'Unexpected API response structure from openai-compatible', - providerName, + providerName ); } @@ -111,10 +105,7 @@ export function createOpenAICompatibleProvider( name: providerName, dimensions: config.dimensions ?? null, - async embed( - input: string | string[], - options?: EmbedOptions, - ): Promise { + async embed(input: string | string[], options?: EmbedOptions): Promise { const inputs = Array.isArray(input) ? input : [input]; for (const text of inputs) { if (text.trim().length === 0) { diff --git a/src/providers/shared.ts b/src/providers/shared.ts index 39f8a5e..3dbe259 100644 --- a/src/providers/shared.ts +++ b/src/providers/shared.ts @@ -10,7 +10,7 @@ const DEFAULT_TIMEOUT = 30000; */ export function createTimeoutSignal( timeoutMs: number | undefined, - userSignal?: AbortSignal, + userSignal?: AbortSignal ): AbortSignal { const timeout = timeoutMs ?? DEFAULT_TIMEOUT; const timeoutSignal = AbortSignal.timeout(timeout); @@ -25,15 +25,11 @@ export function createTimeoutSignal( * Wraps a provider operation and converts timeout DOMException into ProviderError. * @internal */ -export function wrapTimeoutError( - error: unknown, - providerName: string, - timeoutMs?: number, -): never { +export function wrapTimeoutError(error: unknown, providerName: string, timeoutMs?: number): never { if (error instanceof DOMException && error.name === 'TimeoutError') { throw new ProviderError( `Request timed out after ${timeoutMs ?? DEFAULT_TIMEOUT}ms`, - providerName, + providerName ); } throw error; @@ -59,7 +55,7 @@ function isRetryable(error: unknown): boolean { export async function retryWithBackoff( fn: () => Promise, config: RetryConfig, - signal?: AbortSignal, + signal?: AbortSignal ): Promise { const maxRetries = config.maxRetries ?? 3; const baseDelay = config.baseDelay ?? 1000; @@ -114,7 +110,7 @@ export async function autoBatch( inputs: string[], batchSize: number, fn: (batch: string[]) => Promise, - config?: BatchConfig, + config?: BatchConfig ): Promise { if (inputs.length === 0) return []; diff --git a/src/quantization/calibration.ts b/src/quantization/calibration.ts index bf553bd..713c415 100644 --- a/src/quantization/calibration.ts +++ b/src/quantization/calibration.ts @@ -68,12 +68,12 @@ export function calibrate(embeddings: Vector[]): QuantizationCalibration { */ export function calibratedQuantize( embedding: Vector, - calibration: QuantizationCalibration, + calibration: QuantizationCalibration ): Uint8Array { const v = toFloat32(embedding); if (v.length !== calibration.dimensions) { throw new ValidationError( - `Embedding dimensions (${v.length}) do not match calibration dimensions (${calibration.dimensions})`, + `Embedding dimensions (${v.length}) do not match calibration dimensions (${calibration.dimensions})` ); } @@ -100,11 +100,11 @@ export function calibratedQuantize( */ export function calibratedDequantize( data: Uint8Array, - calibration: QuantizationCalibration, + calibration: QuantizationCalibration ): Float32Array { if (data.length !== calibration.dimensions) { throw new ValidationError( - `Data dimensions (${data.length}) do not match calibration dimensions (${calibration.dimensions})`, + `Data dimensions (${data.length}) do not match calibration dimensions (${calibration.dimensions})` ); } diff --git a/src/quantization/hamming.ts b/src/quantization/hamming.ts index 0c6c630..210819b 100644 --- a/src/quantization/hamming.ts +++ b/src/quantization/hamming.ts @@ -19,9 +19,7 @@ for (let i = 0; i < 256; i++) { */ export function hammingDistance(a: Uint8Array, b: Uint8Array): number { if (a.length !== b.length) { - throw new ValidationError( - `Vector length mismatch: ${a.length} vs ${b.length}`, - ); + throw new ValidationError(`Vector length mismatch: ${a.length} vs ${b.length}`); } let distance = 0; for (let i = 0; i < a.length; i++) { @@ -42,10 +40,6 @@ export function hammingDistance(a: Uint8Array, b: Uint8Array): number { * @returns Similarity in range [0, 1] * @throws {ValidationError} If vectors have different lengths */ -export function hammingSimilarity( - a: Uint8Array, - b: Uint8Array, - dimensions: number, -): number { +export function hammingSimilarity(a: Uint8Array, b: Uint8Array, dimensions: number): number { return 1 - hammingDistance(a, b) / dimensions; } diff --git a/src/quantization/info.ts b/src/quantization/info.ts index 5f28dff..ba714b9 100644 --- a/src/quantization/info.ts +++ b/src/quantization/info.ts @@ -14,7 +14,7 @@ type QuantizationType = 'fp16' | 'int8' | 'uint8' | 'binary'; export function estimateMemorySavings( dimensions: number, count: number, - quantizationType: QuantizationType, + quantizationType: QuantizationType ): { originalBytes: number; quantizedBytes: number; diff --git a/src/quantization/quantize.ts b/src/quantization/quantize.ts index 171c09d..c370d70 100644 --- a/src/quantization/quantize.ts +++ b/src/quantization/quantize.ts @@ -85,7 +85,7 @@ export function quantize(embedding: Vector, type: 'uint8'): Uint8Array; export function quantize(embedding: Vector, type: 'binary'): Uint8Array; export function quantize( embedding: Vector, - type: QuantizationType, + type: QuantizationType ): Float32Array | Int8Array | Uint8Array { switch (type) { case 'fp16': { @@ -138,14 +138,26 @@ export function quantize( * @example * dequantize(new Int8Array([64, -38, 102]), 'int8'); // [0.504, -0.299, 0.803] */ -export function dequantize(data: Float32Array, type: 'fp16', originalDimension?: number): Float32Array; +export function dequantize( + data: Float32Array, + type: 'fp16', + originalDimension?: number +): Float32Array; export function dequantize(data: Int8Array, type: 'int8', originalDimension?: number): Float32Array; -export function dequantize(data: Uint8Array, type: 'uint8', originalDimension?: number): Float32Array; -export function dequantize(data: Uint8Array, type: 'binary', originalDimension?: number): Float32Array; +export function dequantize( + data: Uint8Array, + type: 'uint8', + originalDimension?: number +): Float32Array; +export function dequantize( + data: Uint8Array, + type: 'binary', + originalDimension?: number +): Float32Array; export function dequantize( data: Float32Array | Int8Array | Uint8Array, type: QuantizationType, - originalDimension?: number, + originalDimension?: number ): Float32Array { switch (type) { case 'fp16': { diff --git a/src/search/fusion.ts b/src/search/fusion.ts index 0c40f1c..7c89b12 100644 --- a/src/search/fusion.ts +++ b/src/search/fusion.ts @@ -13,10 +13,7 @@ import { ValidationError } from '../types'; * @returns Merged list sorted by fused RRF score descending * @throws {ValidationError} If lists is empty array of non-arrays */ -export function fuseRankedLists( - lists: RankedItem[][], - options?: { k?: number }, -): RankedItem[] { +export function fuseRankedLists(lists: RankedItem[][], options?: { k?: number }): RankedItem[] { if (lists.length === 0) return []; const k = options?.k ?? 60; diff --git a/src/search/hnsw.ts b/src/search/hnsw.ts index 5fc3617..72a2837 100644 --- a/src/search/hnsw.ts +++ b/src/search/hnsw.ts @@ -226,10 +226,7 @@ export class HNSWIndex { } /** Search for the most similar items to a query vector. */ - search( - query: Vector, - options?: HNSWSearchOptions, - ): Array { + search(query: Vector, options?: HNSWSearchOptions): Array { const topK = options?.topK ?? 10; const efSearch = options?.efSearch ?? 50; const filter = options?.filter; @@ -353,11 +350,15 @@ export class HNSWIndex { } const nodeSize = - 4 + idBytes.length + // id - 4 + metaBytes.length + // metadata - 4 + vectorBytes + // vector + 4 + + idBytes.length + // id + 4 + + metaBytes.length + // metadata + 4 + + vectorBytes + // vector 4 + // level - 4 + layerByteLen; // layers + 4 + + layerByteLen; // layers const buf = new ArrayBuffer(nodeSize); const view = new DataView(buf); @@ -380,7 +381,9 @@ export class HNSWIndex { // Vector view.setUint32(offset, vectorBytes, true); offset += 4; - new Uint8Array(buf, offset, vectorBytes).set(new Uint8Array(node.vector.buffer, node.vector.byteOffset, vectorBytes)); + new Uint8Array(buf, offset, vectorBytes).set( + new Uint8Array(node.vector.buffer, node.vector.byteOffset, vectorBytes) + ); offset += vectorBytes; // Level @@ -591,7 +594,7 @@ export class HNSWIndex { query: Float32Array, entryPointIdx: number, ef: number, - layer: number, + layer: number ): ScoredIndex[] { const entryNode = this.nodes[entryPointIdx]; if (!entryNode || !entryNode.neighbors.has(layer)) { diff --git a/src/search/matrix.ts b/src/search/matrix.ts index 47f327a..b7b2b7a 100644 --- a/src/search/matrix.ts +++ b/src/search/matrix.ts @@ -18,10 +18,7 @@ import type { SearchOptions, Vector } from '../types'; * @returns A symmetric NxN matrix of similarity scores * @throws {ValidationError} If the embeddings array is empty */ -export function similarityMatrix( - embeddings: Vector[], - options?: SearchOptions, -): number[][] { +export function similarityMatrix(embeddings: Vector[], options?: SearchOptions): number[][] { if (embeddings.length === 0) { throw new ValidationError('Embeddings array must be non-empty'); } diff --git a/src/search/mmr.ts b/src/search/mmr.ts index b5473ec..f8393fa 100644 --- a/src/search/mmr.ts +++ b/src/search/mmr.ts @@ -14,7 +14,7 @@ export function mmrSearch( query: Vector, corpus: Vector[], k: number, - options?: MMROptions, + options?: MMROptions ): Array<{ index: number; score: number }> { const metric = options?.metric ?? 'cosine'; const lambda = options?.lambda ?? 0.5; diff --git a/src/search/pairwise.ts b/src/search/pairwise.ts index 274ffd4..a8fd721 100644 --- a/src/search/pairwise.ts +++ b/src/search/pairwise.ts @@ -13,11 +13,11 @@ import type { SimilarityMetric, Vector } from '../types'; export function pairwiseSimilarity( listA: Vector[], listB: Vector[], - metric: SimilarityMetric = 'cosine', + metric: SimilarityMetric = 'cosine' ): number[] { if (listA.length !== listB.length) { throw new ValidationError( - `Lists must have the same length, got ${listA.length} and ${listB.length}`, + `Lists must have the same length, got ${listA.length} and ${listB.length}` ); } @@ -25,7 +25,7 @@ export function pairwiseSimilarity( for (let i = 0; i < listA.length; i++) { if (listA[i].length !== listB[i].length) { throw new DimensionMismatchError( - `Dimension mismatch at index ${i}: ${listA[i].length} vs ${listB[i].length}`, + `Dimension mismatch at index ${i}: ${listA[i].length} vs ${listB[i].length}` ); } scores[i] = computeScore(listA[i], listB[i], metric); diff --git a/src/search/rank.ts b/src/search/rank.ts index eddb5a8..66f2ef5 100644 --- a/src/search/rank.ts +++ b/src/search/rank.ts @@ -12,7 +12,7 @@ import type { SearchOptions, SearchResult, Vector } from '../types'; export function rankBySimilarity( query: Vector, corpus: Vector[], - options?: SearchOptions, + options?: SearchOptions ): SearchResult[] { if (corpus.length === 0) return []; return topK(query, corpus, corpus.length, options); diff --git a/src/search/rerank.ts b/src/search/rerank.ts index 43e95b6..9e71fa3 100644 --- a/src/search/rerank.ts +++ b/src/search/rerank.ts @@ -15,7 +15,7 @@ export function rerankResults( options?: { metric?: SimilarityMetric; weights?: { original?: number; rerank?: number }; - }, + } ): Array<{ index: number; score: number }> { const metric = options?.metric ?? 'cosine'; const originalWeight = options?.weights?.original ?? 0.5; @@ -23,7 +23,8 @@ export function rerankResults( const reranked = results.map((result) => ({ index: result.index, - score: originalWeight * result.score + rerankWeight * computeScore(query, result.embedding, metric), + score: + originalWeight * result.score + rerankWeight * computeScore(query, result.embedding, metric), })); reranked.sort((a, b) => b.score - a.score); diff --git a/src/search/search-index.ts b/src/search/search-index.ts index 7733156..e6948ac 100644 --- a/src/search/search-index.ts +++ b/src/search/search-index.ts @@ -23,11 +23,17 @@ export class SearchIndex { /** Add a single item. Overwrites if ID already exists. */ add(id: string, embedding: Vector, metadata?: Record): void { - this.items.set(id, { id, embedding: toFloat32(embedding), ...(metadata !== undefined ? { metadata } : {}) }); + this.items.set(id, { + id, + embedding: toFloat32(embedding), + ...(metadata !== undefined ? { metadata } : {}), + }); } /** Add multiple items at once. */ - addBatch(items: Array<{ id: string; embedding: Vector; metadata?: Record }>): void { + addBatch( + items: Array<{ id: string; embedding: Vector; metadata?: Record }> + ): void { for (const item of items) { this.add(item.id, item.embedding, item.metadata); } @@ -45,7 +51,7 @@ export class SearchIndex { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean; - }, + } ): Array { const topK = options?.topK ?? 10; const threshold = options?.threshold; diff --git a/src/search/threshold.ts b/src/search/threshold.ts index 333deec..ad57a41 100644 --- a/src/search/threshold.ts +++ b/src/search/threshold.ts @@ -24,7 +24,7 @@ export function aboveThreshold( query: Vector, corpus: Vector[], threshold: number, - options?: SearchOptions, + options?: SearchOptions ): SearchResult[] { if (!Number.isFinite(threshold)) { throw new ValidationError(`threshold must be a finite number, got ${threshold}`); @@ -36,7 +36,7 @@ export function aboveThreshold( if (corpus.length > 0 && query.length !== corpus[0].length) { throw new DimensionMismatchError( - `Query dimension (${query.length}) does not match corpus dimension (${corpus[0].length})`, + `Query dimension (${query.length}) does not match corpus dimension (${corpus[0].length})` ); } @@ -70,7 +70,7 @@ export function aboveThreshold( export function deduplicate( embeddings: Vector[], threshold: number, - options?: SearchOptions, + options?: SearchOptions ): { embeddings: Float32Array[]; indices: number[]; labels?: string[] } { const metric = options?.metric ?? 'cosine'; const labels = options?.labels; diff --git a/src/search/topk.ts b/src/search/topk.ts index 107d53e..514aef0 100644 --- a/src/search/topk.ts +++ b/src/search/topk.ts @@ -24,7 +24,7 @@ export function topK( query: Vector, corpus: Vector[], k: number, - options?: SearchOptions, + options?: SearchOptions ): SearchResult[] { if (k < 1 || !Number.isInteger(k)) { throw new ValidationError(`k must be a positive integer, got ${k}`); @@ -36,7 +36,7 @@ export function topK( if (corpus.length > 0 && query.length !== corpus[0].length) { throw new DimensionMismatchError( - `Query dimension (${query.length}) does not match corpus dimension (${corpus[0].length})`, + `Query dimension (${query.length}) does not match corpus dimension (${corpus[0].length})` ); } @@ -104,7 +104,7 @@ export function topKMulti( queries: Vector[], corpus: Vector[], k: number, - options?: SearchOptions, + options?: SearchOptions ): SearchResult[][] { return queries.map((query) => topK(query, corpus, k, options)); } diff --git a/src/storage/cache.ts b/src/storage/cache.ts index 26f8ebe..056b035 100644 --- a/src/storage/cache.ts +++ b/src/storage/cache.ts @@ -99,7 +99,7 @@ export function createLRUCache(options?: CacheOptions): CacheProvider { */ export async function warmCache( cache: CacheProvider, - data: Array<{ key: string; value: Float32Array[] }>, + data: Array<{ key: string; value: Float32Array[] }> ): Promise { for (const entry of data) { await cache.set(entry.key, entry.value); diff --git a/src/storage/serialize.ts b/src/storage/serialize.ts index 678cbd9..2301dd7 100644 --- a/src/storage/serialize.ts +++ b/src/storage/serialize.ts @@ -135,7 +135,7 @@ function fromBinaryV2(data: Uint8Array): DeserializeResult { offset += 4; const decoder = new TextDecoder(); metadata = JSON.parse( - decoder.decode(new Uint8Array(data.buffer, data.byteOffset + offset, metadataLength)), + decoder.decode(new Uint8Array(data.buffer, data.byteOffset + offset, metadataLength)) ); offset += metadataLength; } @@ -169,16 +169,14 @@ function fromBinaryV2(data: Uint8Array): DeserializeResult { export function serialize( embeddings: Vector[], format: SerializeFormat, - metadata?: SerializationMetadata, + metadata?: SerializationMetadata ): string | Uint8Array { validateEmbeddings(embeddings); switch (format) { case 'json': // For JSON, convert Float32Array to regular arrays for proper JSON serialization - return JSON.stringify(embeddings.map((e) => - e instanceof Float32Array ? Array.from(e) : e, - )); + return JSON.stringify(embeddings.map((e) => (e instanceof Float32Array ? Array.from(e) : e))); case 'binary': return toBinaryV2(embeddings, metadata); @@ -200,10 +198,7 @@ export function serialize( * @param format - Input format: 'json', 'binary', or 'base64' * @returns Object with Float32Array[] embeddings and optional metadata (v2 only) */ -export function deserialize( - data: string | Uint8Array, - format: SerializeFormat, -): DeserializeResult { +export function deserialize(data: string | Uint8Array, format: SerializeFormat): DeserializeResult { switch (format) { case 'json': { const parsed: number[][] = JSON.parse(data as string); diff --git a/src/store/embedding-store.ts b/src/store/embedding-store.ts index 949e7cd..f2eaeac 100644 --- a/src/store/embedding-store.ts +++ b/src/store/embedding-store.ts @@ -1,8 +1,4 @@ -import type { - EmbeddingStoreConfig, - StoredItem, - Vector, -} from '../types'; +import type { EmbeddingStoreConfig, StoredItem, Vector } from '../types'; import { SearchIndex } from '../search/search-index'; import { withCache } from '../providers/middleware'; @@ -17,16 +13,18 @@ export interface EmbeddingStore { /** Embed text and store the result with an ID and optional metadata. */ add(id: string, text: string, metadata?: Record): Promise; /** Batch embed and store multiple items. */ - addBatch(items: Array<{ id: string; text: string; metadata?: Record }>): Promise; + addBatch( + items: Array<{ id: string; text: string; metadata?: Record }> + ): Promise; /** Embed query text and search for similar items. */ search( query: string, - options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean }, + options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean } ): Promise>; /** Search by a pre-computed embedding vector. */ searchByEmbedding( embedding: Vector, - options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean }, + options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean } ): Array; /** Remove an item by ID. */ remove(id: string): boolean; @@ -52,9 +50,7 @@ export interface EmbeddingStore { * const results = await store.search('greeting', { topK: 5 }); */ export function createEmbeddingStore(config: EmbeddingStoreConfig): EmbeddingStore { - const provider = config.cache - ? withCache(config.provider, config.cache) - : config.provider; + const provider = config.cache ? withCache(config.provider, config.cache) : config.provider; const index = new SearchIndex({ metric: config.metric }); @@ -65,7 +61,7 @@ export function createEmbeddingStore(config: EmbeddingStoreConfig): EmbeddingSto }, async addBatch( - items: Array<{ id: string; text: string; metadata?: Record }>, + items: Array<{ id: string; text: string; metadata?: Record }> ): Promise { const texts = items.map((item) => item.text); const result = await provider.embed(texts); @@ -76,7 +72,7 @@ export function createEmbeddingStore(config: EmbeddingStoreConfig): EmbeddingSto async search( query: string, - options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean }, + options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean } ): Promise> { const result = await provider.embed(query); return index.search(result.embeddings[0], options); @@ -84,7 +80,7 @@ export function createEmbeddingStore(config: EmbeddingStoreConfig): EmbeddingSto searchByEmbedding( embedding: Vector, - options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean }, + options?: { topK?: number; threshold?: number; filter?: (item: StoredItem) => boolean } ): Array { return index.search(embedding, options); }, diff --git a/src/text/chunking.ts b/src/text/chunking.ts index fb27973..b863150 100644 --- a/src/text/chunking.ts +++ b/src/text/chunking.ts @@ -12,7 +12,7 @@ export function chunkByTokenCount( text: string, maxTokens: number, - options?: { overlap?: number; separator?: string }, + options?: { overlap?: number; separator?: string } ): string[] { if (!text) return []; @@ -51,19 +51,17 @@ export function chunkByTokenCount( */ export function chunkBySentence( text: string, - options?: { maxTokens?: number; overlap?: number }, + options?: { maxTokens?: number; overlap?: number } ): string[] { if (!text) return []; if (typeof Intl === 'undefined' || !('Segmenter' in Intl)) { - throw new Error( - 'chunkBySentence requires Intl.Segmenter (Node 16+ or modern browser)', - ); + throw new Error('chunkBySentence requires Intl.Segmenter (Node 16+ or modern browser)'); } const segmenter = new Intl.Segmenter(undefined, { granularity: 'sentence' }); const sentences = Array.from(segmenter.segment(text), (s) => s.segment).filter( - (s) => s.trim().length > 0, + (s) => s.trim().length > 0 ); if (sentences.length === 0) return []; diff --git a/src/text/markdown.ts b/src/text/markdown.ts index 88d08b5..7ac945f 100644 --- a/src/text/markdown.ts +++ b/src/text/markdown.ts @@ -192,7 +192,7 @@ function splitSentences(text: string): string[] { */ export function chunkByStructure( text: string, - options?: { maxTokens?: number; overlap?: number }, + options?: { maxTokens?: number; overlap?: number } ): StructuredChunk[] { if (!text || text.trim().length === 0) return []; @@ -278,7 +278,11 @@ export function chunkByStructure( if (estimateTokens(candidate) > overlap) break; overlapText = candidate; } - currentOffset = block.offset + text.indexOf(currentText, currentOffset - block.offset) + currentText.length - overlapText.length; + currentOffset = + block.offset + + text.indexOf(currentText, currentOffset - block.offset) + + currentText.length - + overlapText.length; currentText = overlapText; } else { currentOffset = block.offset + block.text.indexOf(sentence, currentText.length); diff --git a/tests/aggregation/average.test.ts b/tests/aggregation/average.test.ts index e574c05..d984cd2 100644 --- a/tests/aggregation/average.test.ts +++ b/tests/aggregation/average.test.ts @@ -39,7 +39,12 @@ describe('averageEmbeddings', () => { }); it('throws ValidationError for mixed dimensions', () => { - expect(() => averageEmbeddings([[1, 2], [1, 2, 3]])).toThrow(ValidationError); + expect(() => + averageEmbeddings([ + [1, 2], + [1, 2, 3], + ]) + ).toThrow(ValidationError); }); it('returns the vector itself for a single input', () => { @@ -78,7 +83,7 @@ describe('weightedAverage', () => { [10, 20], [1, 2], ], - [0, 1], + [0, 1] ); expect(result).toBeInstanceOf(Float32Array); expect(Array.from(result)).toEqual([1, 2]); @@ -90,7 +95,7 @@ describe('weightedAverage', () => { [0, 0], [10, 10], ], - [1, 3], + [1, 3] ); // (0*1 + 10*3) / (1+3) = 7.5 expect(result).toBeInstanceOf(Float32Array); diff --git a/tests/aggregation/batch-incremental.test.ts b/tests/aggregation/batch-incremental.test.ts index d78a4ed..9707529 100644 --- a/tests/aggregation/batch-incremental.test.ts +++ b/tests/aggregation/batch-incremental.test.ts @@ -1,17 +1,10 @@ import { describe, it, expect } from 'vitest'; -import { - batchIncrementalAverage, - averageEmbeddings, -} from '../../src/aggregation/average'; +import { batchIncrementalAverage, averageEmbeddings } from '../../src/aggregation/average'; import { ValidationError } from '../../src/types'; describe('batchIncrementalAverage', () => { it('produces result equivalent to full re-average for a batch of 10 vectors', () => { - const vectors = Array.from({ length: 10 }, (_, i) => [ - Math.sin(i), - Math.cos(i), - i * 0.1, - ]); + const vectors = Array.from({ length: 10 }, (_, i) => [Math.sin(i), Math.cos(i), i * 0.1]); const fullAvg = averageEmbeddings(vectors); // Incrementally: start with first vector, then batch the remaining 9 @@ -43,14 +36,19 @@ describe('batchIncrementalAverage', () => { it('throws ValidationError for dimension mismatch across batch items', () => { expect(() => - batchIncrementalAverage([1, 2, 3], [[4, 5, 6], [7, 8]], 1), + batchIncrementalAverage( + [1, 2, 3], + [ + [4, 5, 6], + [7, 8], + ], + 1 + ) ).toThrow(ValidationError); }); it('throws ValidationError when batch item dimension mismatches currentAvg', () => { - expect(() => - batchIncrementalAverage([1, 2], [[3, 4, 5]], 1), - ).toThrow(ValidationError); + expect(() => batchIncrementalAverage([1, 2], [[3, 4, 5]], 1)).toThrow(ValidationError); }); it('returns the blended value for a single-item batch', () => { diff --git a/tests/aggregation/combine.test.ts b/tests/aggregation/combine.test.ts index 04e32f7..98097e9 100644 --- a/tests/aggregation/combine.test.ts +++ b/tests/aggregation/combine.test.ts @@ -17,10 +17,7 @@ function createMockProvider(embeddings: Float32Array[]): EmbeddingProvider { describe('combineEmbeddings', () => { it('should combine multiple texts into single averaged embedding', async () => { - const provider = createMockProvider([ - new Float32Array([1, 2, 3]), - new Float32Array([3, 4, 5]), - ]); + const provider = createMockProvider([new Float32Array([1, 2, 3]), new Float32Array([3, 4, 5])]); const result = await combineEmbeddings(['hello', 'world'], provider); @@ -30,10 +27,7 @@ describe('combineEmbeddings', () => { }); it('should use custom aggregation function', async () => { - const provider = createMockProvider([ - new Float32Array([1, 2, 3]), - new Float32Array([4, 5, 6]), - ]); + const provider = createMockProvider([new Float32Array([1, 2, 3]), new Float32Array([4, 5, 6])]); // Use max pooling as custom aggregation const maxPool = (embeddings: (number[] | Float32Array)[]): Float32Array => { diff --git a/tests/aggregation/pooling.test.ts b/tests/aggregation/pooling.test.ts index 8b966cd..d39c19a 100644 --- a/tests/aggregation/pooling.test.ts +++ b/tests/aggregation/pooling.test.ts @@ -24,7 +24,12 @@ describe('maxPooling', () => { }); it('throws ValidationError for mixed dimensions', () => { - expect(() => maxPooling([[1, 2], [1, 2, 3]])).toThrow(ValidationError); + expect(() => + maxPooling([ + [1, 2], + [1, 2, 3], + ]) + ).toThrow(ValidationError); }); it('handles negative values correctly', () => { @@ -65,7 +70,12 @@ describe('minPooling', () => { }); it('throws ValidationError for mixed dimensions', () => { - expect(() => minPooling([[1, 2], [1, 2, 3]])).toThrow(ValidationError); + expect(() => + minPooling([ + [1, 2], + [1, 2, 3], + ]) + ).toThrow(ValidationError); }); it('handles negative values correctly', () => { diff --git a/tests/clustering/cluster.test.ts b/tests/clustering/cluster.test.ts index 372b5ee..cd43875 100644 --- a/tests/clustering/cluster.test.ts +++ b/tests/clustering/cluster.test.ts @@ -26,9 +26,15 @@ describe('clusterEmbeddings', () => { it('separates orthogonal embeddings into different clusters', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], [1, 0, 0], - [0, 1, 0], [0, 1, 0], [0, 1, 0], - [0, 0, 1], [0, 0, 1], [0, 0, 1], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], ]; const clusters = clusterEmbeddings(embeddings, { similarityThreshold: 0.5, @@ -40,8 +46,12 @@ describe('clusterEmbeddings', () => { it('high threshold creates more clusters', () => { // Slightly different vectors that are similar but not identical const embeddings = [ - [1, 0, 0], [0.99, 0.1, 0], [0.98, 0.15, 0], - [0, 1, 0], [0.1, 0.99, 0], [0.05, 0.98, 0], + [1, 0, 0], + [0.99, 0.1, 0], + [0.98, 0.15, 0], + [0, 1, 0], + [0.1, 0.99, 0], + [0.05, 0.98, 0], ]; const looseThreshold = clusterEmbeddings(embeddings, { similarityThreshold: 0.5, @@ -56,7 +66,11 @@ describe('clusterEmbeddings', () => { it('redistributes small clusters into nearest valid cluster', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], [0, 1, 0], // only 1 member — too small, redistributed to nearest valid cluster ]; const clusters = clusterEmbeddings(embeddings, { @@ -70,10 +84,14 @@ describe('clusterEmbeddings', () => { it('caps clusters at maxClusters by merging most similar', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], - [0, 1, 0], [0, 1, 0], - [0, 0, 1], [0, 0, 1], - [0.7, 0.7, 0], [0.7, 0.7, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 1, 0], + [0, 0, 1], + [0, 0, 1], + [0.7, 0.7, 0], + [0.7, 0.7, 0], ]; const clusters = clusterEmbeddings(embeddings, { similarityThreshold: 0.5, @@ -85,8 +103,12 @@ describe('clusterEmbeddings', () => { it('supports custom metric option', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], [1, 0, 0], - [0, 1, 0], [0, 1, 0], [0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], ]; const clusters = clusterEmbeddings(embeddings, { similarityThreshold: 0.5, @@ -118,8 +140,12 @@ describe('clusterEmbeddings', () => { it('populates cohesion field on each cluster', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], [1, 0, 0], - [0, 1, 0], [0, 1, 0], [0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], ]; const clusters = clusterEmbeddings(embeddings, { similarityThreshold: 0.5, diff --git a/tests/clustering/cohesion.test.ts b/tests/clustering/cohesion.test.ts index f73cb2b..3e7f934 100644 --- a/tests/clustering/cohesion.test.ts +++ b/tests/clustering/cohesion.test.ts @@ -3,10 +3,12 @@ import { centroidCohesion } from '../../src/clustering/metrics'; import type { Cluster, SimilarityMetric } from '../../src/types'; function makeCluster(members: number[][], centroid?: number[]): Cluster { - const c = centroid ?? members.reduce( - (acc, m) => acc.map((v, i) => v + m[i] / members.length), - new Array(members[0].length).fill(0), - ); + const c = + centroid ?? + members.reduce( + (acc, m) => acc.map((v, i) => v + m[i] / members.length), + new Array(members[0].length).fill(0) + ); return { centroid: c, members, size: members.length, cohesion: 0 }; } @@ -38,7 +40,10 @@ describe('centroidCohesion', () => { }); it('works with euclidean metric', () => { - const members = [[1, 0], [1.1, 0.1]]; + const members = [ + [1, 0], + [1.1, 0.1], + ]; const cluster = makeCluster(members); const score = centroidCohesion(cluster, 'euclidean'); expect(typeof score).toBe('number'); @@ -46,7 +51,10 @@ describe('centroidCohesion', () => { }); it('works with dot metric', () => { - const members = [[1, 0], [0.9, 0.1]]; + const members = [ + [1, 0], + [0.9, 0.1], + ]; const cluster = makeCluster(members); const score = centroidCohesion(cluster, 'dot'); expect(typeof score).toBe('number'); @@ -54,7 +62,10 @@ describe('centroidCohesion', () => { }); it('works with manhattan metric', () => { - const members = [[1, 0], [0.9, 0.1]]; + const members = [ + [1, 0], + [0.9, 0.1], + ]; const cluster = makeCluster(members); const score = centroidCohesion(cluster, 'manhattan'); expect(typeof score).toBe('number'); @@ -62,7 +73,11 @@ describe('centroidCohesion', () => { }); it('returns 1.0 for all identical members (cosine)', () => { - const members = [[0.5, 0.5], [0.5, 0.5], [0.5, 0.5]]; + const members = [ + [0.5, 0.5], + [0.5, 0.5], + [0.5, 0.5], + ]; const cluster = makeCluster(members); expect(centroidCohesion(cluster, 'cosine')).toBeCloseTo(1.0, 5); }); diff --git a/tests/clustering/hdbscan.test.ts b/tests/clustering/hdbscan.test.ts index 31754f6..985c49f 100644 --- a/tests/clustering/hdbscan.test.ts +++ b/tests/clustering/hdbscan.test.ts @@ -10,7 +10,7 @@ function makeCluster2D( cy: number, count: number, spread: number, - seed: number, + seed: number ): number[][] { const pts: number[][] = []; let s = seed; @@ -32,7 +32,7 @@ function makeRing2D( radius: number, count: number, noise: number, - seed: number, + seed: number ): number[][] { const pts: number[][] = []; let s = seed; @@ -106,7 +106,10 @@ describe('hdbscan — noise detection', () => { // Use multiple clusters + outliers so the outliers don't get absorbed const cluster1 = makeCluster2D(0, 0, 15, 0.3, 1); const cluster2 = makeCluster2D(10, 0, 15, 0.3, 2); - const outliers: number[][] = [[100, 100], [-100, -100]]; + const outliers: number[][] = [ + [100, 100], + [-100, -100], + ]; const embeddings = [...cluster1, ...cluster2, ...outliers]; const result = hdbscan(embeddings, { minClusterSize: 5 }); @@ -117,7 +120,10 @@ describe('hdbscan — noise detection', () => { it('noise points are NOT in any cluster members', () => { const cluster = makeCluster2D(0, 0, 20, 0.3, 1); - const outliers: number[][] = [[100, 100], [-100, -100]]; + const outliers: number[][] = [ + [100, 100], + [-100, -100], + ]; const embeddings = [...cluster, ...outliers]; const result = hdbscan(embeddings, { minClusterSize: 5 }); @@ -178,10 +184,7 @@ describe('hdbscan — edge cases', () => { }); it('minSamples affects core distance computation', () => { - const embeddings = [ - ...makeCluster2D(0, 0, 20, 0.5, 1), - ...makeCluster2D(10, 0, 20, 0.5, 2), - ]; + const embeddings = [...makeCluster2D(0, 0, 20, 0.5, 1), ...makeCluster2D(10, 0, 20, 0.5, 2)]; const result1 = hdbscan(embeddings, { minClusterSize: 5, minSamples: 3 }); const result2 = hdbscan(embeddings, { minClusterSize: 5, minSamples: 15 }); // Different minSamples should potentially produce different results @@ -195,10 +198,7 @@ describe('hdbscan — edge cases', () => { describe('hdbscan — label preservation', () => { it('preserves string labels in cluster members', () => { - const embeddings = [ - ...makeCluster2D(0, 0, 10, 0.3, 1), - ...makeCluster2D(10, 0, 10, 0.3, 2), - ]; + const embeddings = [...makeCluster2D(0, 0, 10, 0.3, 1), ...makeCluster2D(10, 0, 10, 0.3, 2)]; const labels = embeddings.map((_, i) => `point-${i}`); const result = hdbscan(embeddings, { minClusterSize: 5, labels }); @@ -230,7 +230,7 @@ describe('hdbscan — label preservation', () => { it('throws ValidationError for labels length mismatch', () => { const embeddings = makeCluster2D(0, 0, 10, 0.3, 1); expect(() => hdbscan(embeddings, { minClusterSize: 5, labels: ['a', 'b'] })).toThrow( - ValidationError, + ValidationError ); }); }); @@ -239,10 +239,7 @@ describe('hdbscan — label preservation', () => { describe('hdbscan — metric support', () => { it('default metric is euclidean', () => { - const embeddings = [ - ...makeCluster2D(0, 0, 15, 0.3, 1), - ...makeCluster2D(10, 0, 15, 0.3, 2), - ]; + const embeddings = [...makeCluster2D(0, 0, 15, 0.3, 1), ...makeCluster2D(10, 0, 15, 0.3, 2)]; const defaultResult = hdbscan(embeddings, { minClusterSize: 5 }); const euclideanResult = hdbscan(embeddings, { minClusterSize: 5, metric: 'euclidean' }); expect(defaultResult.labels).toEqual(euclideanResult.labels); diff --git a/tests/clustering/incremental.test.ts b/tests/clustering/incremental.test.ts index 12f6cbe..c34a501 100644 --- a/tests/clustering/incremental.test.ts +++ b/tests/clustering/incremental.test.ts @@ -71,8 +71,12 @@ describe('IncrementalClusterer', () => { it('addBatch adds multiple embeddings', () => { const clusterer = new IncrementalClusterer({ similarityThreshold: 0.8 }); clusterer.addBatch( - [[1, 0, 0], [0.99, 0.01, 0], [0, 1, 0]], - ['a', 'b', 'c'], + [ + [1, 0, 0], + [0.99, 0.01, 0], + [0, 1, 0], + ], + ['a', 'b', 'c'] ); expect(clusterer.size).toBe(3); const clusters = clusterer.getClusters(); diff --git a/tests/clustering/metrics.test.ts b/tests/clustering/metrics.test.ts index 4984e86..f928148 100644 --- a/tests/clustering/metrics.test.ts +++ b/tests/clustering/metrics.test.ts @@ -122,9 +122,7 @@ describe('silhouetteScore', () => { [0.45, 0.55, 0], ]), ]; - expect(silhouetteScore(overlapping)).toBeLessThan( - silhouetteScore(wellSeparated), - ); + expect(silhouetteScore(overlapping)).toBeLessThan(silhouetteScore(wellSeparated)); }); it('works with euclidean metric', () => { diff --git a/tests/clustering/operations.test.ts b/tests/clustering/operations.test.ts index b276a78..a87f343 100644 --- a/tests/clustering/operations.test.ts +++ b/tests/clustering/operations.test.ts @@ -2,10 +2,7 @@ import { describe, it, expect } from 'vitest'; import { assignToCluster, mergeClusters } from '../../src/clustering/operations'; import type { Cluster } from '../../src/types'; -function makeCluster( - members: number[][], - labels?: string[], -): Cluster { +function makeCluster(members: number[][], labels?: string[]): Cluster { const dims = members[0].length; const centroid = new Array(dims).fill(0); for (const m of members) { @@ -17,8 +14,14 @@ function makeCluster( describe('assignToCluster', () => { const clusters: Cluster[] = [ - makeCluster([[1, 0, 0], [0.95, 0.05, 0]]), - makeCluster([[0, 1, 0], [0.05, 0.95, 0]]), + makeCluster([ + [1, 0, 0], + [0.95, 0.05, 0], + ]), + makeCluster([ + [0, 1, 0], + [0.05, 0.95, 0], + ]), ]; it('assigns embedding to most similar cluster', () => { @@ -54,7 +57,10 @@ describe('assignToCluster', () => { describe('mergeClusters', () => { it('combines members from both clusters', () => { - const a = makeCluster([[1, 0, 0], [0.9, 0.1, 0]]); + const a = makeCluster([ + [1, 0, 0], + [0.9, 0.1, 0], + ]); const b = makeCluster([[0.8, 0.2, 0]]); const merged = mergeClusters(a, b); expect(merged.members).toHaveLength(3); @@ -71,8 +77,14 @@ describe('mergeClusters', () => { }); it('updates cohesion', () => { - const a = makeCluster([[1, 0, 0], [0.99, 0.01, 0]]); - const b = makeCluster([[0, 1, 0], [0.01, 0.99, 0]]); + const a = makeCluster([ + [1, 0, 0], + [0.99, 0.01, 0], + ]); + const b = makeCluster([ + [0, 1, 0], + [0.01, 0.99, 0], + ]); const merged = mergeClusters(a, b); expect(typeof merged.cohesion).toBe('number'); // Merged disparate clusters should have lower cohesion diff --git a/tests/clustering/presets.test.ts b/tests/clustering/presets.test.ts index 5170a23..f60f586 100644 --- a/tests/clustering/presets.test.ts +++ b/tests/clustering/presets.test.ts @@ -46,8 +46,16 @@ describe('getPreset', () => { it('presets work with clusterEmbeddings', () => { const embeddings = [ - [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], - [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], ]; const preset = getPreset('balanced'); const clusters = clusterEmbeddings(embeddings, preset); diff --git a/tests/clustering/redistribution.test.ts b/tests/clustering/redistribution.test.ts index cdad627..a78db99 100644 --- a/tests/clustering/redistribution.test.ts +++ b/tests/clustering/redistribution.test.ts @@ -14,8 +14,8 @@ describe('cluster redistribution edge cases', () => { ]; const clusters = clusterEmbeddings(embeddings, { - similarityThreshold: 0.95, // very high so each gets its own cluster initially - minClusterSize: 10, // larger than total count, forcing all-small path + similarityThreshold: 0.95, // very high so each gets its own cluster initially + minClusterSize: 10, // larger than total count, forcing all-small path maxClusters: 5, }); @@ -27,9 +27,9 @@ describe('cluster redistribution edge cases', () => { it('preserves all members after redistribution', () => { const embeddings = [ [1, 0, 0], - [0.99, 0.01, 0], // close to first - [0.98, 0.02, 0], // close to first - [0, 1, 0], // outlier, will be in small cluster + [0.99, 0.01, 0], // close to first + [0.98, 0.02, 0], // close to first + [0, 1, 0], // outlier, will be in small cluster ]; const clusters = clusterEmbeddings(embeddings, { @@ -52,7 +52,7 @@ describe('cluster redistribution edge cases', () => { const clusters = clusterEmbeddings(embeddings, { similarityThreshold: 0.99, // very high — likely one per cluster - minClusterSize: 1, // no redistribution needed + minClusterSize: 1, // no redistribution needed maxClusters: 10, }); @@ -68,7 +68,7 @@ describe('cluster redistribution edge cases', () => { [1, 0, 0], [0.98, 0.02, 0], [0.97, 0.03, 0], - [0, 0, 1], // outlier + [0, 0, 1], // outlier ]; const clusters = clusterEmbeddings(embeddings, { diff --git a/tests/edge-cases.test.ts b/tests/edge-cases.test.ts index 4448ceb..57e25d5 100644 --- a/tests/edge-cases.test.ts +++ b/tests/edge-cases.test.ts @@ -1,11 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { - cosineSimilarity, - topK, - aboveThreshold, - normalize, - ValidationError, -} from '../src'; +import { cosineSimilarity, topK, aboveThreshold, normalize, ValidationError } from '../src'; import { validateVector } from '../src/internal/validation'; describe('edge cases', () => { @@ -87,7 +81,10 @@ describe('edge cases', () => { }); it('k equal to corpus.length returns all items', () => { - const corpus = [[1, 0], [0, 1]]; + const corpus = [ + [1, 0], + [0, 1], + ]; const results = topK([1, 0], corpus, 2); expect(results).toHaveLength(2); }); diff --git a/tests/eval/metrics.test.ts b/tests/eval/metrics.test.ts index 698735c..8310f89 100644 --- a/tests/eval/metrics.test.ts +++ b/tests/eval/metrics.test.ts @@ -47,8 +47,16 @@ describe('ndcg', () => { // = 0/1 + 1/1.585 + 3/2 + 7/2.322 = 0 + 0.6309 + 1.5 + 3.0145 = 5.1454 // IDCG (perfect): (2^3-1)/log2(2) + (2^2-1)/log2(3) + (2^1-1)/log2(4) + (2^0-1)/log2(5) // = 7/1 + 3/1.585 + 1/2 + 0/2.322 = 7 + 1.8928 + 0.5 + 0 = 9.3928 - const dcg = 0 / Math.log2(2) + (Math.pow(2, 1) - 1) / Math.log2(3) + (Math.pow(2, 2) - 1) / Math.log2(4) + (Math.pow(2, 3) - 1) / Math.log2(5); - const idcg = (Math.pow(2, 3) - 1) / Math.log2(2) + (Math.pow(2, 2) - 1) / Math.log2(3) + (Math.pow(2, 1) - 1) / Math.log2(4) + 0; + const dcg = + 0 / Math.log2(2) + + (Math.pow(2, 1) - 1) / Math.log2(3) + + (Math.pow(2, 2) - 1) / Math.log2(4) + + (Math.pow(2, 3) - 1) / Math.log2(5); + const idcg = + (Math.pow(2, 3) - 1) / Math.log2(2) + + (Math.pow(2, 2) - 1) / Math.log2(3) + + (Math.pow(2, 1) - 1) / Math.log2(4) + + 0; const expected = dcg / idcg; const result = ndcg(retrieved, scores); expect(result).toBeCloseTo(expected); diff --git a/tests/integration/embed-cluster-search.test.ts b/tests/integration/embed-cluster-search.test.ts index 42381b5..d52540b 100644 --- a/tests/integration/embed-cluster-search.test.ts +++ b/tests/integration/embed-cluster-search.test.ts @@ -21,7 +21,12 @@ function createMockProvider(): EmbeddingProvider { if (text.startsWith('food')) return [0.1, 0.0, 0.0, 0.9]; // Default: hash-based const hash = text.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0); - return [(hash % 10) / 10, ((hash * 3) % 10) / 10, ((hash * 7) % 10) / 10, ((hash * 13) % 10) / 10]; + return [ + (hash % 10) / 10, + ((hash * 3) % 10) / 10, + ((hash * 7) % 10) / 10, + ((hash * 13) % 10) / 10, + ]; }); return { embeddings, model: 'mock', dimensions: 4 }; }, diff --git a/tests/integration/incremental-pipeline.test.ts b/tests/integration/incremental-pipeline.test.ts index d9edb36..dd5a0f2 100644 --- a/tests/integration/incremental-pipeline.test.ts +++ b/tests/integration/incremental-pipeline.test.ts @@ -12,7 +12,7 @@ describe('integration: IncrementalClusterer pipeline', () => { // Group A: near [1, 0, 0] const groupA = [ [0.95, 0.05, 0.0], - [0.90, 0.10, 0.0], + [0.9, 0.1, 0.0], [0.92, 0.08, 0.0], [0.88, 0.12, 0.0], [0.93, 0.07, 0.0], @@ -21,7 +21,7 @@ describe('integration: IncrementalClusterer pipeline', () => { // Group B: near [0, 1, 0] const groupB = [ [0.05, 0.95, 0.0], - [0.10, 0.90, 0.0], + [0.1, 0.9, 0.0], [0.08, 0.92, 0.0], [0.12, 0.88, 0.0], [0.07, 0.93, 0.0], diff --git a/tests/integration/v03-features.test.ts b/tests/integration/v03-features.test.ts index 30b0fa5..f9c189a 100644 --- a/tests/integration/v03-features.test.ts +++ b/tests/integration/v03-features.test.ts @@ -22,12 +22,26 @@ function createMockProvider(dims = 8): EmbeddingProvider { const vec = new Float32Array(dims); // Create separable embeddings based on topic prefixes const lower = text.toLowerCase(); - if (lower.includes('machine learning') || lower.includes('neural') || lower.includes('ai')) { - vec[0] = 0.9; vec[1] = 0.3; vec[2] = 0.1; - } else if (lower.includes('cooking') || lower.includes('recipe') || lower.includes('pasta')) { - vec[3] = 0.9; vec[4] = 0.3; vec[5] = 0.1; + if ( + lower.includes('machine learning') || + lower.includes('neural') || + lower.includes('ai') + ) { + vec[0] = 0.9; + vec[1] = 0.3; + vec[2] = 0.1; + } else if ( + lower.includes('cooking') || + lower.includes('recipe') || + lower.includes('pasta') + ) { + vec[3] = 0.9; + vec[4] = 0.3; + vec[5] = 0.1; } else if (lower.includes('space') || lower.includes('planet') || lower.includes('nasa')) { - vec[0] = 0.1; vec[1] = 0.9; vec[6] = 0.3; + vec[0] = 0.1; + vec[1] = 0.9; + vec[6] = 0.3; } else { // Hash-based fallback let h = 0; @@ -78,7 +92,9 @@ describe('integration: embed → HNSW → search → evaluate', () => { expect(hnsw.size).toBe(texts.length); // Query for AI-related content - const { embeddings: [queryVec] } = await provider.embed('ai and machine learning'); + const { + embeddings: [queryVec], + } = await provider.embed('ai and machine learning'); // HNSW search const hnswResults = hnsw.search(queryVec, { topK: 3 }); @@ -203,7 +219,9 @@ describe('integration: pipeline → store → search', () => { } // Search - const { embeddings: [queryVec] } = await provider.embed('ai and machine learning'); + const { + embeddings: [queryVec], + } = await provider.embed('ai and machine learning'); const results = index.search(queryVec, { topK: 2 }); expect(results.length).toBe(2); @@ -258,7 +276,9 @@ A recipe for making delicious Italian pasta from scratch. } // Search for AI-related content - const { embeddings: [queryVec] } = await provider.embed('neural network ai learning'); + const { + embeddings: [queryVec], + } = await provider.embed('neural network ai learning'); const results = index.search(queryVec, { topK: 3 }); expect(results.length).toBeGreaterThan(0); diff --git a/tests/internal/clustering.test.ts b/tests/internal/clustering.test.ts index 767eb55..9e6b46f 100644 --- a/tests/internal/clustering.test.ts +++ b/tests/internal/clustering.test.ts @@ -3,7 +3,10 @@ import { computeCentroid, computePairwiseCohesion } from '../../src/internal/clu describe('computeCentroid', () => { it('computes mean of known vectors', () => { - const centroid = computeCentroid([[1, 0], [0, 1]]); + const centroid = computeCentroid([ + [1, 0], + [0, 1], + ]); expect(centroid).toBeInstanceOf(Float32Array); expect(Array.from(centroid)).toEqual([0.5, 0.5]); }); @@ -15,7 +18,11 @@ describe('computeCentroid', () => { }); it('computes centroid of three vectors', () => { - const centroid = computeCentroid([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); + const centroid = computeCentroid([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]); expect(centroid).toBeInstanceOf(Float32Array); expect(Array.from(centroid)).toEqual([4, 5, 6]); }); @@ -23,12 +30,25 @@ describe('computeCentroid', () => { describe('computePairwiseCohesion', () => { it('returns 1.0 for identical vectors with cosine metric', () => { - const cohesion = computePairwiseCohesion([[1, 0], [1, 0], [1, 0]], 'cosine'); + const cohesion = computePairwiseCohesion( + [ + [1, 0], + [1, 0], + [1, 0], + ], + 'cosine' + ); expect(cohesion).toBeCloseTo(1.0); }); it('returns 0.0 for orthogonal vectors with cosine metric', () => { - const cohesion = computePairwiseCohesion([[1, 0], [0, 1]], 'cosine'); + const cohesion = computePairwiseCohesion( + [ + [1, 0], + [0, 1], + ], + 'cosine' + ); expect(cohesion).toBeCloseTo(0.0); }); diff --git a/tests/internal/validation.test.ts b/tests/internal/validation.test.ts index aaced77..18ffb45 100644 --- a/tests/internal/validation.test.ts +++ b/tests/internal/validation.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from 'vitest'; -import { validateVector, validateVectorPair, validateEmbeddings } from '../../src/internal/validation'; +import { + validateVector, + validateVectorPair, + validateEmbeddings, +} from '../../src/internal/validation'; import { ValidationError, DimensionMismatchError } from '../../src/types'; describe('validateVector', () => { @@ -57,7 +61,12 @@ describe('validateVectorPair', () => { describe('validateEmbeddings', () => { it('passes for valid embeddings', () => { - expect(() => validateEmbeddings([[1, 2], [3, 4]])).not.toThrow(); + expect(() => + validateEmbeddings([ + [1, 2], + [3, 4], + ]) + ).not.toThrow(); }); it('throws for empty array', () => { @@ -65,6 +74,11 @@ describe('validateEmbeddings', () => { }); it('throws DimensionMismatchError for mixed dimensions', () => { - expect(() => validateEmbeddings([[1, 2], [1, 2, 3]])).toThrow(DimensionMismatchError); + expect(() => + validateEmbeddings([ + [1, 2], + [1, 2, 3], + ]) + ).toThrow(DimensionMismatchError); }); }); diff --git a/tests/math/dimensions.test.ts b/tests/math/dimensions.test.ts index db3049d..66e6617 100644 --- a/tests/math/dimensions.test.ts +++ b/tests/math/dimensions.test.ts @@ -75,10 +75,7 @@ describe('truncateDimensions (batch)', () => { }); it('accepts Float32Array batch', () => { - const batch = [ - new Float32Array([1, 2, 3]), - new Float32Array([4, 5, 6]), - ]; + const batch = [new Float32Array([1, 2, 3]), new Float32Array([4, 5, 6])]; const result = truncateDimensions(batch, 2); expect(result).toHaveLength(2); expect(result[0]).toHaveLength(2); @@ -139,21 +136,35 @@ describe('truncateDimensions auto-normalize', () => { describe('validateDimensions', () => { it('returns valid for consistent dimensions', () => { - const result = validateDimensions([[1, 2], [3, 4], [5, 6]]); + const result = validateDimensions([ + [1, 2], + [3, 4], + [5, 6], + ]); expect(result.valid).toBe(true); expect(result.dimension).toBe(2); expect(result.mismatches).toEqual([]); }); it('returns invalid with correct mismatch indices', () => { - const result = validateDimensions([[1, 2], [3, 4, 5], [6, 7]]); + const result = validateDimensions([ + [1, 2], + [3, 4, 5], + [6, 7], + ]); expect(result.valid).toBe(false); expect(result.dimension).toBe(2); expect(result.mismatches).toEqual([1]); }); it('validates against expectedDim', () => { - const result = validateDimensions([[1, 2], [3, 4]], 3); + const result = validateDimensions( + [ + [1, 2], + [3, 4], + ], + 3 + ); expect(result.valid).toBe(false); expect(result.dimension).toBe(3); expect(result.mismatches).toEqual([0, 1]); diff --git a/tests/models/manager.test.ts b/tests/models/manager.test.ts index 92193ee..f99e7b8 100644 --- a/tests/models/manager.test.ts +++ b/tests/models/manager.test.ts @@ -47,7 +47,7 @@ describe('model manager', () => { expect(mockPipeline).toHaveBeenCalledWith( 'feature-extraction', 'Xenova/all-MiniLM-L12-v2', - expect.any(Object), + expect.any(Object) ); expect(typeof result).toBe('string'); }); @@ -60,7 +60,7 @@ describe('model manager', () => { expect(mockPipeline).toHaveBeenCalledWith( 'feature-extraction', 'Xenova/all-MiniLM-L6-v2', - expect.objectContaining({ cache_dir: '/custom/cache' }), + expect.objectContaining({ cache_dir: '/custom/cache' }) ); }); }); @@ -95,10 +95,10 @@ describe('model manager', () => { await deleteModel('Xenova/all-MiniLM-L12-v2', '/cache/dir'); - expect(mockRm).toHaveBeenCalledWith( - expect.stringContaining('Xenova--all-MiniLM-L12-v2'), - { recursive: true, force: true }, - ); + expect(mockRm).toHaveBeenCalledWith(expect.stringContaining('Xenova--all-MiniLM-L12-v2'), { + recursive: true, + force: true, + }); }); }); @@ -110,10 +110,7 @@ describe('model manager', () => { await listModels(); - expect(mockReaddir).toHaveBeenCalledWith( - '/my/custom/path', - expect.any(Object), - ); + expect(mockReaddir).toHaveBeenCalledWith('/my/custom/path', expect.any(Object)); // Reset for other tests setModelPath(''); diff --git a/tests/models/registry.test.ts b/tests/models/registry.test.ts index c282aee..e1e892c 100644 --- a/tests/models/registry.test.ts +++ b/tests/models/registry.test.ts @@ -36,22 +36,37 @@ describe('registerModel', () => { it('rejects empty model ID', () => { expect(() => - registerModel({ id: '', dimensions: 128, maxTokens: 256, description: 'bad' }), + registerModel({ id: '', dimensions: 128, maxTokens: 256, description: 'bad' }) ).toThrow(ValidationError); }); it('rejects non-positive dimensions', () => { expect(() => - registerModel({ id: 'test-org/another-model', dimensions: 0, maxTokens: 256, description: 'bad' }), + registerModel({ + id: 'test-org/another-model', + dimensions: 0, + maxTokens: 256, + description: 'bad', + }) ).toThrow(ValidationError); expect(() => - registerModel({ id: 'test-org/another-model', dimensions: -1, maxTokens: 256, description: 'bad' }), + registerModel({ + id: 'test-org/another-model', + dimensions: -1, + maxTokens: 256, + description: 'bad', + }) ).toThrow(ValidationError); }); it('rejects non-integer dimensions', () => { expect(() => - registerModel({ id: 'test-org/another-model', dimensions: 3.5, maxTokens: 256, description: 'bad' }), + registerModel({ + id: 'test-org/another-model', + dimensions: 3.5, + maxTokens: 256, + description: 'bad', + }) ).toThrow(ValidationError); }); }); diff --git a/tests/pipeline/checkpoint.test.ts b/tests/pipeline/checkpoint.test.ts index 18b3828..513336a 100644 --- a/tests/pipeline/checkpoint.test.ts +++ b/tests/pipeline/checkpoint.test.ts @@ -23,7 +23,7 @@ function createMockProvider(dims: number = 3): EmbeddingProvider & { callLog: st } function createInMemoryCheckpoint( - initialState: CheckpointState | null = null, + initialState: CheckpointState | null = null ): CheckpointAdapter & { saved: CheckpointState[]; loadCalls: number } { const adapter = { state: initialState, diff --git a/tests/pipeline/pipeline.test.ts b/tests/pipeline/pipeline.test.ts index 02bf2d4..75c2756 100644 --- a/tests/pipeline/pipeline.test.ts +++ b/tests/pipeline/pipeline.test.ts @@ -44,7 +44,9 @@ describe('batchIterator', () => { // ── Task 7.5: Pipeline End-to-End ─────────────────────────────────────────── -function createMockProvider(dims: number = 3): EmbeddingProvider & { callCount: number; concurrentCalls: number; maxConcurrent: number } { +function createMockProvider( + dims: number = 3 +): EmbeddingProvider & { callCount: number; concurrentCalls: number; maxConcurrent: number } { const provider = { name: 'mock', dimensions: dims, diff --git a/tests/providers/cohere.test.ts b/tests/providers/cohere.test.ts index 5a7ca51..36ad2e4 100644 --- a/tests/providers/cohere.test.ts +++ b/tests/providers/cohere.test.ts @@ -107,7 +107,10 @@ describe('createCohereProvider', () => { it('should parse embeddings.float response', async () => { const response = { embeddings: { - float: [[0.1, 0.2], [0.3, 0.4]], + float: [ + [0.1, 0.2], + [0.3, 0.4], + ], }, meta: { billed_units: { input_tokens: 8 } }, }; diff --git a/tests/providers/factory.test.ts b/tests/providers/factory.test.ts index f9e3adf..caa84c4 100644 --- a/tests/providers/factory.test.ts +++ b/tests/providers/factory.test.ts @@ -55,7 +55,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('voyage', { @@ -74,7 +74,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('mistral', { @@ -93,7 +93,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('jina', { @@ -112,7 +112,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('openrouter', { @@ -131,7 +131,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('together', { @@ -150,7 +150,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('fireworks', { @@ -169,7 +169,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('nomic', { @@ -188,7 +188,7 @@ describe('createProvider', () => { mockFetchResponse({ data: [{ embedding: [0.1], index: 0 }], usage: { total_tokens: 1 }, - }), + }) ); const provider = createProvider('mixedbread', { diff --git a/tests/providers/google.test.ts b/tests/providers/google.test.ts index 1f96d7d..69a7346 100644 --- a/tests/providers/google.test.ts +++ b/tests/providers/google.test.ts @@ -29,9 +29,7 @@ describe('createGoogleVertexProvider', () => { }; const successResponse = { - predictions: [ - { embeddings: { values: [0.1, 0.2, 0.3] } }, - ], + predictions: [{ embeddings: { values: [0.1, 0.2, 0.3] } }], }; it('should have correct provider name and dimensions', () => { @@ -48,7 +46,7 @@ describe('createGoogleVertexProvider', () => { const [url] = fetchSpy.mock.calls[0]; expect(url).toBe( - 'https://us-central1-aiplatform.googleapis.com/v1/projects/my-project/locations/us-central1/publishers/google/models/text-embedding-005:predict', + 'https://us-central1-aiplatform.googleapis.com/v1/projects/my-project/locations/us-central1/publishers/google/models/text-embedding-005:predict' ); }); @@ -105,14 +103,10 @@ describe('createGoogleVertexProvider', () => { }); await provider.embed('first'); - expect(fetchSpy.mock.calls[0][1].headers['Authorization']).toBe( - 'Bearer dynamic-token-1', - ); + expect(fetchSpy.mock.calls[0][1].headers['Authorization']).toBe('Bearer dynamic-token-1'); await provider.embed('second'); - expect(fetchSpy.mock.calls[1][1].headers['Authorization']).toBe( - 'Bearer dynamic-token-2', - ); + expect(fetchSpy.mock.calls[1][1].headers['Authorization']).toBe('Bearer dynamic-token-2'); expect(tokenFn).toHaveBeenCalledTimes(2); }); @@ -129,10 +123,7 @@ describe('createGoogleVertexProvider', () => { it('should handle batch input', async () => { const batchResponse = { - predictions: [ - { embeddings: { values: [0.1, 0.2] } }, - { embeddings: { values: [0.3, 0.4] } }, - ], + predictions: [{ embeddings: { values: [0.1, 0.2] } }, { embeddings: { values: [0.3, 0.4] } }], }; fetchSpy.mockResolvedValue(mockFetchResponse(batchResponse)); diff --git a/tests/providers/middleware.test.ts b/tests/providers/middleware.test.ts index 323756d..2c5a7fe 100644 --- a/tests/providers/middleware.test.ts +++ b/tests/providers/middleware.test.ts @@ -2,7 +2,9 @@ import { describe, it, expect, vi } from 'vitest'; import { withCache } from '../../src/providers/middleware'; import type { EmbeddingProvider, EmbeddingResult } from '../../src/types'; -function createMockProvider(embeddings: number[][] = [[0.1, 0.2, 0.3]]): EmbeddingProvider & { embedCalls: number } { +function createMockProvider( + embeddings: number[][] = [[0.1, 0.2, 0.3]] +): EmbeddingProvider & { embedCalls: number } { const mock = { name: 'mock', dimensions: 3, @@ -102,7 +104,10 @@ describe('withCache', () => { }); it('handles array input', async () => { - const provider = createMockProvider([[0.1, 0.2], [0.3, 0.4]]); + const provider = createMockProvider([ + [0.1, 0.2], + [0.3, 0.4], + ]); const cached = withCache(provider); await cached.embed(['hello', 'world']); diff --git a/tests/providers/shared.test.ts b/tests/providers/shared.test.ts index 6390034..0d48053 100644 --- a/tests/providers/shared.test.ts +++ b/tests/providers/shared.test.ts @@ -42,32 +42,26 @@ describe('retryWithBackoff', () => { }); it('should NOT retry on 4xx (except 429)', async () => { - const fn = vi - .fn() - .mockRejectedValue(new ProviderError('bad request', 'test', 400)); + const fn = vi.fn().mockRejectedValue(new ProviderError('bad request', 'test', 400)); - await expect( - retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 }), - ).rejects.toThrow('bad request'); + await expect(retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 })).rejects.toThrow( + 'bad request' + ); expect(fn).toHaveBeenCalledTimes(1); }); it('should NOT retry on non-ProviderError', async () => { const fn = vi.fn().mockRejectedValue(new Error('unknown')); - await expect( - retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 }), - ).rejects.toThrow('unknown'); + await expect(retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 })).rejects.toThrow('unknown'); expect(fn).toHaveBeenCalledTimes(1); }); it('should respect maxRetries', async () => { - const fn = vi - .fn() - .mockRejectedValue(new ProviderError('server error', 'test', 500)); + const fn = vi.fn().mockRejectedValue(new ProviderError('server error', 'test', 500)); await expect( - retryWithBackoff(fn, { maxRetries: 2, baseDelay: 1, maxDelay: 10 }), + retryWithBackoff(fn, { maxRetries: 2, baseDelay: 1, maxDelay: 10 }) ).rejects.toThrow('server error'); // Initial call + 2 retries = 3 total expect(fn).toHaveBeenCalledTimes(3); @@ -121,7 +115,7 @@ describe('retryWithBackoff', () => { }); await expect( - retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 }, controller.signal), + retryWithBackoff(fn, { maxRetries: 3, baseDelay: 1 }, controller.signal) ).rejects.toThrow(/abort/i); vi.restoreAllMocks(); @@ -171,7 +165,10 @@ describe('autoBatch', () => { }); const result = await autoBatch(['a', 'b'], 10, fn); - expect(result).toEqual([[1, 2], [1, 2]]); + expect(result).toEqual([ + [1, 2], + [1, 2], + ]); expect(fn).toHaveBeenCalledTimes(1); }); diff --git a/tests/providers/timeout.test.ts b/tests/providers/timeout.test.ts index 06903f7..0ccc48f 100644 --- a/tests/providers/timeout.test.ts +++ b/tests/providers/timeout.test.ts @@ -21,7 +21,7 @@ describe('provider timeouts', () => { init?.signal?.addEventListener('abort', () => { reject(init.signal!.reason); }); - }), + }) ); vi.stubGlobal('fetch', hangingFetch); } diff --git a/tests/quantization/calibration.test.ts b/tests/quantization/calibration.test.ts index a011c0e..b061b86 100644 --- a/tests/quantization/calibration.test.ts +++ b/tests/quantization/calibration.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from 'vitest'; -import { calibrate, calibratedQuantize, calibratedDequantize } from '../../src/quantization/calibration'; +import { + calibrate, + calibratedQuantize, + calibratedDequantize, +} from '../../src/quantization/calibration'; import { quantize, dequantize } from '../../src/quantization/quantize'; import { ValidationError } from '../../src/types'; diff --git a/tests/search/filter.test.ts b/tests/search/filter.test.ts index dd977eb..216dcd0 100644 --- a/tests/search/filter.test.ts +++ b/tests/search/filter.test.ts @@ -5,7 +5,7 @@ import { aboveThreshold } from '../../src/search/threshold'; describe('topK with filter', () => { const query = [1, 0, 0]; const corpus = [ - [1, 0, 0], // index 0 (even) + [1, 0, 0], // index 0 (even) [0.9, 0.1, 0], // index 1 (odd) [0.8, 0.2, 0], // index 2 (even) [0.7, 0.3, 0], // index 3 (odd) @@ -48,10 +48,10 @@ describe('topK with filter', () => { describe('aboveThreshold with filter', () => { const query = [1, 0, 0]; const corpus = [ - [1, 0, 0], // index 0, very high score + [1, 0, 0], // index 0, very high score [0.95, 0.05, 0], // index 1, high score - [0.9, 0.1, 0], // index 2, high score - [0, 1, 0], // index 3, low score + [0.9, 0.1, 0], // index 2, high score + [0, 1, 0], // index 3, low score ]; it('filters out even indices', () => { diff --git a/tests/search/hnsw.test.ts b/tests/search/hnsw.test.ts index e577e4e..fcf01ba 100644 --- a/tests/search/hnsw.test.ts +++ b/tests/search/hnsw.test.ts @@ -170,7 +170,7 @@ describe('HNSWIndex — search accuracy', () => { const bfIds = bfResults.map((r) => r.id); const overlap = bfIds.filter((id) => hnswIds.has(id)).length; expect(overlap).toBeGreaterThanOrEqual(3); - }, + } ); it('filter option restricts results', () => { diff --git a/tests/search/mmr.test.ts b/tests/search/mmr.test.ts index abb5ed1..c777e22 100644 --- a/tests/search/mmr.test.ts +++ b/tests/search/mmr.test.ts @@ -4,11 +4,11 @@ import { mmrSearch } from '../../src/search/mmr'; describe('mmrSearch', () => { const query = [1, 0, 0]; const corpus = [ - [1, 0, 0], // index 0: identical to query + [1, 0, 0], // index 0: identical to query [0.99, 0.01, 0], // index 1: very similar to query and index 0 - [0, 1, 0], // index 2: orthogonal/diverse - [0, 0, 1], // index 3: orthogonal/diverse - [-1, 0, 0], // index 4: opposite + [0, 1, 0], // index 2: orthogonal/diverse + [0, 0, 1], // index 3: orthogonal/diverse + [-1, 0, 0], // index 4: opposite ]; it('with lambda=1.0 matches topK ordering (pure relevance)', () => { diff --git a/tests/search/pairwise.test.ts b/tests/search/pairwise.test.ts index e3ee753..52bc15e 100644 --- a/tests/search/pairwise.test.ts +++ b/tests/search/pairwise.test.ts @@ -17,8 +17,14 @@ describe('pairwiseSimilarity', () => { }); it('computes element-wise similarity', () => { - const listA = [[1, 0, 0], [0, 1, 0]]; - const listB = [[0, 1, 0], [0, 1, 0]]; + const listA = [ + [1, 0, 0], + [0, 1, 0], + ]; + const listB = [ + [0, 1, 0], + [0, 1, 0], + ]; const scores = pairwiseSimilarity(listA, listB); expect(scores).toHaveLength(2); expect(scores[0]).toBeCloseTo(0); // orthogonal @@ -26,7 +32,10 @@ describe('pairwiseSimilarity', () => { }); it('throws ValidationError for different lengths', () => { - const listA = [[1, 0], [0, 1]]; + const listA = [ + [1, 0], + [0, 1], + ]; const listB = [[1, 0]]; expect(() => pairwiseSimilarity(listA, listB)).toThrow(ValidationError); }); diff --git a/tests/search/rank.test.ts b/tests/search/rank.test.ts index 488268a..ffe8465 100644 --- a/tests/search/rank.test.ts +++ b/tests/search/rank.test.ts @@ -4,10 +4,10 @@ import { rankBySimilarity } from '../../src/search/rank'; describe('rankBySimilarity', () => { const query = [1, 0, 0]; const corpus = [ - [0, 1, 0], // orthogonal - [1, 0, 0], // identical + [0, 1, 0], // orthogonal + [1, 0, 0], // identical [0.9, 0.1, 0], // very similar - [-1, 0, 0], // opposite + [-1, 0, 0], // opposite ]; it('ranks corpus by similarity descending', () => { diff --git a/tests/search/rerank.test.ts b/tests/search/rerank.test.ts index 361a7e1..8c5bb32 100644 --- a/tests/search/rerank.test.ts +++ b/tests/search/rerank.test.ts @@ -8,8 +8,8 @@ describe('rerankResults', () => { // Result at index 1 has high original score but low actual similarity // Result at index 0 has low original score but high actual similarity const results = [ - { index: 0, score: 0.1, embedding: [1, 0, 0] }, // low original, high cosine sim (1.0) - { index: 1, score: 0.9, embedding: [0, 1, 0] }, // high original, low cosine sim (0.0) + { index: 0, score: 0.1, embedding: [1, 0, 0] }, // low original, high cosine sim (1.0) + { index: 1, score: 0.9, embedding: [0, 1, 0] }, // high original, low cosine sim (0.0) ]; const reranked = rerankResults(results, query); @@ -34,9 +34,7 @@ describe('rerankResults', () => { }); it('default weights are 0.5/0.5', () => { - const results = [ - { index: 0, score: 0.6, embedding: [1, 0, 0] }, - ]; + const results = [{ index: 0, score: 0.6, embedding: [1, 0, 0] }]; const reranked = rerankResults(results, query); // 0.5 * 0.6 + 0.5 * 1.0 = 0.8 expect(reranked[0].score).toBeCloseTo(0.8); diff --git a/tests/search/threshold.test.ts b/tests/search/threshold.test.ts index 78728fa..9544102 100644 --- a/tests/search/threshold.test.ts +++ b/tests/search/threshold.test.ts @@ -4,10 +4,10 @@ import { aboveThreshold, deduplicate } from '../../src/search/threshold'; describe('aboveThreshold', () => { const query = [1, 0, 0]; const corpus = [ - [1, 0, 0], // similarity = 1.0 + [1, 0, 0], // similarity = 1.0 [0.9, 0.1, 0], // high similarity - [0, 1, 0], // similarity = 0.0 - [-1, 0, 0], // similarity = -1.0 + [0, 1, 0], // similarity = 0.0 + [-1, 0, 0], // similarity = -1.0 ]; it('returns embeddings with similarity >= threshold', () => { @@ -51,8 +51,8 @@ describe('deduplicate', () => { it('removes near-duplicate embeddings (keeps first)', () => { const embeddings = [ [1, 0, 0], - [1, 0, 0], // exact duplicate - [0, 1, 0], // unique + [1, 0, 0], // exact duplicate + [0, 1, 0], // unique ]; const result = deduplicate(embeddings, 0.99); expect(result.embeddings).toHaveLength(2); diff --git a/tests/search/topk.test.ts b/tests/search/topk.test.ts index 5493592..dad45c6 100644 --- a/tests/search/topk.test.ts +++ b/tests/search/topk.test.ts @@ -5,10 +5,10 @@ import { ValidationError } from '../../src/types'; describe('topK', () => { const query = [1, 0, 0]; const corpus = [ - [1, 0, 0], // identical to query - [0, 1, 0], // orthogonal + [1, 0, 0], // identical to query + [0, 1, 0], // orthogonal [0.9, 0.1, 0], // very similar - [-1, 0, 0], // opposite + [-1, 0, 0], // opposite ]; it('returns k most similar embeddings in descending order', () => { diff --git a/tests/search/validation.test.ts b/tests/search/validation.test.ts index 01cc0a0..6dce589 100644 --- a/tests/search/validation.test.ts +++ b/tests/search/validation.test.ts @@ -6,20 +6,30 @@ import { DimensionMismatchError, ValidationError } from '../../src/types'; describe('Search dimension validation', () => { const query3d = [1, 0, 0]; - const corpus2d = [[1, 0], [0, 1]]; + const corpus2d = [ + [1, 0], + [0, 1], + ]; it('topK throws DimensionMismatchError for mismatched query/corpus', () => { expect(() => topK(query3d, corpus2d, 1)).toThrow(DimensionMismatchError); - expect(() => topK(query3d, corpus2d, 1)).toThrow(/Query dimension \(3\) does not match corpus dimension \(2\)/); + expect(() => topK(query3d, corpus2d, 1)).toThrow( + /Query dimension \(3\) does not match corpus dimension \(2\)/ + ); }); it('aboveThreshold throws DimensionMismatchError for mismatched query/corpus', () => { expect(() => aboveThreshold(query3d, corpus2d, 0.5)).toThrow(DimensionMismatchError); - expect(() => aboveThreshold(query3d, corpus2d, 0.5)).toThrow(/Query dimension \(3\) does not match corpus dimension \(2\)/); + expect(() => aboveThreshold(query3d, corpus2d, 0.5)).toThrow( + /Query dimension \(3\) does not match corpus dimension \(2\)/ + ); }); it('similarityMatrix throws DimensionMismatchError for mixed dimensions', () => { - const mixed = [[1, 0, 0], [1, 0]]; + const mixed = [ + [1, 0, 0], + [1, 0], + ]; expect(() => similarityMatrix(mixed)).toThrow(DimensionMismatchError); }); @@ -36,7 +46,10 @@ describe('Search dimension validation', () => { describe('Search parameter validation', () => { const query = [1, 0, 0]; - const corpus = [[1, 0, 0], [0, 1, 0]]; + const corpus = [ + [1, 0, 0], + [0, 1, 0], + ]; it('topK throws ValidationError for k = 0', () => { expect(() => topK(query, corpus, 0)).toThrow(ValidationError); @@ -58,7 +71,9 @@ describe('Search parameter validation', () => { it('aboveThreshold throws ValidationError for Infinity threshold', () => { expect(() => aboveThreshold(query, corpus, Infinity)).toThrow(ValidationError); - expect(() => aboveThreshold(query, corpus, Infinity)).toThrow(/threshold must be a finite number/); + expect(() => aboveThreshold(query, corpus, Infinity)).toThrow( + /threshold must be a finite number/ + ); }); it('aboveThreshold throws ValidationError for NaN threshold', () => { diff --git a/tests/storage/serialize-v2.test.ts b/tests/storage/serialize-v2.test.ts index 80e6b60..419b8ad 100644 --- a/tests/storage/serialize-v2.test.ts +++ b/tests/storage/serialize-v2.test.ts @@ -66,7 +66,10 @@ describe('binary format v2', () => { describe('v1 backward compatibility', () => { it('deserializes v1 binary data (no version byte)', () => { // Manually craft v1 binary: [count:u32LE][dims:u32LE][float32LE * dims] per embedding - const embs = [[1.0, 2.0], [3.0, 4.0]]; + const embs = [ + [1.0, 2.0], + [3.0, 4.0], + ]; const totalBytes = 4 + embs.length * (4 + embs[0].length * 4); const buffer = new ArrayBuffer(totalBytes); const view = new DataView(buffer); diff --git a/tests/store/embedding-store.test.ts b/tests/store/embedding-store.test.ts index 5a98f9a..75ecf64 100644 --- a/tests/store/embedding-store.test.ts +++ b/tests/store/embedding-store.test.ts @@ -16,7 +16,7 @@ function createMockProvider(): EmbeddingProvider & { embedCalls: number } { // Produce deterministic embeddings based on text hash const embeddings = inputs.map((text) => { const hash = text.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0); - return [hash % 10 / 10, (hash * 7) % 10 / 10, (hash * 13) % 10 / 10]; + return [(hash % 10) / 10, ((hash * 7) % 10) / 10, ((hash * 13) % 10) / 10]; }); return { embeddings, model: 'mock', dimensions: 3 }; }, diff --git a/tests/text/create-tokenizer.test.ts b/tests/text/create-tokenizer.test.ts index 3811866..ca6cd71 100644 --- a/tests/text/create-tokenizer.test.ts +++ b/tests/text/create-tokenizer.test.ts @@ -34,7 +34,9 @@ describe('createTokenizer (real load)', () => { beforeAll(async () => { const { AutoTokenizer } = await import('@huggingface/transformers'); const autoTok = await AutoTokenizer.from_pretrained(MODEL); - reference = FIXED_STRINGS.map((s) => (autoTok(s) as { input_ids: { size: number } }).input_ids.size); + reference = FIXED_STRINGS.map( + (s) => (autoTok(s) as { input_ids: { size: number } }).input_ids.size + ); tok = createTokenizer(MODEL); await tok.load(); diff --git a/tests/text/markdown.test.ts b/tests/text/markdown.test.ts index 2a01077..70a83ff 100644 --- a/tests/text/markdown.test.ts +++ b/tests/text/markdown.test.ts @@ -225,8 +225,10 @@ Content another.`; describe('chunkByStructure — maxTokens and overlap', () => { it('respects maxTokens limit (no chunk exceeds it except code blocks)', () => { - const paragraphs = Array.from({ length: 10 }, (_, i) => - `Paragraph ${i} has several words that add up to a meaningful amount of content here.`, + const paragraphs = Array.from( + { length: 10 }, + (_, i) => + `Paragraph ${i} has several words that add up to a meaningful amount of content here.` ).join('\n\n'); const md = `# Section\n\n${paragraphs}`; const chunks = chunkByStructure(md, { maxTokens: 20 }); @@ -241,8 +243,9 @@ describe('chunkByStructure — maxTokens and overlap', () => { }); it('produces overlapping text when overlap option is set', () => { - const sentences = Array.from({ length: 20 }, (_, i) => - `Sentence number ${i} contains some words.`, + const sentences = Array.from( + { length: 20 }, + (_, i) => `Sentence number ${i} contains some words.` ).join(' '); const md = `# Section\n\n${sentences}`; const chunks = chunkByStructure(md, { maxTokens: 30, overlap: 10 }); From 2d7216b299c9c8a63c7358ed801912a485670576 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 13:02:47 -0700 Subject: [PATCH 4/8] chore: track package-lock.json The lockfile was gitignored, so every contributor and CI run resolved dependencies independently. Builds were not reproducible, and a compromised or regressed transitive patch release could land silently without any diff to review. This is especially load-bearing now that three `overrides` are the only thing holding transitive dependencies at non-vulnerable versions; without a lockfile there is no record of what actually got installed. Committing it does not affect consumers of the published package - npm ignores lockfiles from dependencies - so the usual argument for omitting it in a library does not apply here. It also enables `npm ci` in CI. --- .gitignore | 1 - package-lock.json | 5040 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 5040 insertions(+), 1 deletion(-) create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 630bb95..a740113 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules -package-lock.json dist coverage specs diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..504f69b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5040 @@ +{ + "name": "embedding-utils", + "version": "0.6.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "embedding-utils", + "version": "0.6.0", + "license": "Apache-2.0", + "devDependencies": { + "@huggingface/transformers": "^4.2.0", + "@types/node": "^25.9.5", + "@typescript-eslint/eslint-plugin": "^8.67.0", + "@typescript-eslint/parser": "^8.67.0", + "@vitest/coverage-v8": "^4.1.10", + "eslint": "^10.8.1", + "prettier": "^3.9.6", + "tsup": "^8.5.1", + "typescript": "^6.0.3", + "vitest": "^4.1.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@huggingface/transformers": "^4.2.0" + }, + "peerDependenciesMeta": { + "@huggingface/transformers": { + "optional": true + } + } + }, + "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" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@huggingface/jinja": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.6.tgz", + "integrity": "sha512-MyMWyLnjqo+KRJYSH7oWNbsOn5onuIvfXYPcc0WOGxU0eHUV7oAYUoQTl2BMdu7ml+ea/bu11UM+EshbeHwtIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/tokenizers": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@huggingface/tokenizers/-/tokenizers-0.1.3.tgz", + "integrity": "sha512-8rF/RRT10u+kn7YuUbUg0OF30K8rjTc78aHpxT+qJ1uWSqxT1MHi8+9ltwYfkFYJzT/oS+qw3JVfHtNMGAdqyA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@huggingface/transformers": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-4.2.0.tgz", + "integrity": "sha512-8BRCoBMH0XsWaEIamuR0LrJGAfftgHAfb2Vrffy0VKlSAE/MnUJ5/h/zTfEP3fDIft+nk7TqB8xXEyABGitBjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.5.6", + "@huggingface/tokenizers": "^0.1.3", + "onnxruntime-node": "1.24.3", + "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c", + "sharp": "^0.34.5" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.144.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", + "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", + "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", + "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", + "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", + "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", + "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", + "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", + "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", + "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", + "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", + "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", + "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", + "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", + "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", + "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.10", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "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": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz", + "integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz", + "integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "adm-zip": "^0.5.16", + "global-agent": "^3.0.0", + "onnxruntime-common": "1.24.3" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.26.0-dev.20260416-b7804b056c", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.26.0-dev.20260416-b7804b056c.tgz", + "integrity": "sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.24.0-dev.20251116-b39e144322", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { + "version": "1.24.0-dev.20251116-b39e144322", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.0-dev.20251116-b39e144322.tgz", + "integrity": "sha512-BOoomdHYmNRL5r4iQ4bMvsl2t0/hzVQ3OM3PHD0gxeXu1PmggqBv3puZicEUVOA3AtHHYmqZtjMj9FOfGrATTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "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.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/protobufjs": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "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": ">=6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", + "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.144.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.4", + "@rolldown/binding-darwin-arm64": "1.2.4", + "@rolldown/binding-darwin-x64": "1.2.4", + "@rolldown/binding-freebsd-x64": "1.2.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", + "@rolldown/binding-linux-arm64-gnu": "1.2.4", + "@rolldown/binding-linux-arm64-musl": "1.2.4", + "@rolldown/binding-linux-ppc64-gnu": "1.2.4", + "@rolldown/binding-linux-s390x-gnu": "1.2.4", + "@rolldown/binding-linux-x64-gnu": "1.2.4", + "@rolldown/binding-linux-x64-musl": "1.2.4", + "@rolldown/binding-openharmony-arm64": "1.2.4", + "@rolldown/binding-win32-arm64-msvc": "1.2.4", + "@rolldown/binding-win32-x64-msvc": "1.2.4" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/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/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "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/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.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==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 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/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From b15fea62bc0be2d843798f806f19d96e3a25590e Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 13:05:34 -0700 Subject: [PATCH 5/8] fix(lint): lint tests/ and clear the dead code it surfaced eslint.config.mjs already declared `tests/**/*.ts` in its `files` glob, but `npm run lint` only ever passed `src/`, so no test file had been linted. Enabling it surfaced 34 errors. Relax two rules for tests/ only, since the defaults are wrong for test code rather than the code being wrong: - no-explicit-any: off. Mocking fetch, setTimeout and provider responses legitimately needs `any` (21 of the 34). - no-unused-vars: allow the `^_` prefix convention for intentionally unused args, which the suite was already using. The remaining 12 were genuine dead code: - Unused imports (SimilarityMetric, vi, beforeEach, afterEach). - Unused mock params, now `_`-prefixed to match the convention. - `originalSetTimeout`, captured to restore a global that vi.spyOn already restores. - A whole count=2 v1 buffer in serialize-v2.test.ts that the author's own comments explain was abandoned - count=2 is ambiguous with V2_VERSION - and replaced with the count=1 buffer below it. The explanation is kept; only the dead buffer is removed. - An EmbeddingStore the test creates and then deliberately bypasses in favour of SearchIndex, as its comment describes. Two were captured for a before/after comparison that was never written: - `beforeRebalance` in incremental.test.ts: completed as intended by asserting rebalance preserves total membership, an invariant the sibling integration test already checks. It passes. - `cohesionBefore` in incremental-pipeline.test.ts: removed rather than completed. Asserting cohesion does not decrease across a rebalance is not a guarantee the implementation makes, and the test already asserts every post-rebalance cluster has non-zero cohesion. Verified: lint, format:check and all 801 tests pass. --- eslint.config.mjs | 10 +++++++++ package.json | 2 +- tests/clustering/cohesion.test.ts | 2 +- tests/clustering/incremental.test.ts | 5 +++++ .../integration/incremental-pipeline.test.ts | 3 --- tests/integration/v03-features.test.ts | 4 ---- tests/pipeline/checkpoint.test.ts | 2 +- tests/providers/middleware.test.ts | 4 ++-- tests/providers/shared.test.ts | 1 - tests/storage/serialize-v2.test.ts | 22 ------------------- tests/store/embedding-store.test.ts | 2 +- 11 files changed, 21 insertions(+), 36 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 5f427b3..5218eb0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,4 +18,14 @@ export default [ ...tseslint.configs.recommended.rules, }, }, + { + files: ['tests/**/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + }, + }, ]; diff --git a/package.json b/package.json index 04051a1..ed49163 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage", - "lint": "eslint src/", + "lint": "eslint src/ tests/", "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"", "smoke": "npm run build && node scripts/smoke-test.mjs" diff --git a/tests/clustering/cohesion.test.ts b/tests/clustering/cohesion.test.ts index 3e7f934..b6e2b69 100644 --- a/tests/clustering/cohesion.test.ts +++ b/tests/clustering/cohesion.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { centroidCohesion } from '../../src/clustering/metrics'; -import type { Cluster, SimilarityMetric } from '../../src/types'; +import type { Cluster } from '../../src/types'; function makeCluster(members: number[][], centroid?: number[]): Cluster { const c = diff --git a/tests/clustering/incremental.test.ts b/tests/clustering/incremental.test.ts index c34a501..e7ae995 100644 --- a/tests/clustering/incremental.test.ts +++ b/tests/clustering/incremental.test.ts @@ -51,6 +51,11 @@ describe('IncrementalClusterer', () => { // Should still have reasonable clusters after rebalance expect(afterRebalance.length).toBeGreaterThanOrEqual(1); expect(afterRebalance.length).toBeLessThanOrEqual(2); + + // Rebalance redistributes members without dropping any + const membersBefore = beforeRebalance.reduce((sum, c) => sum + c.size, 0); + const membersAfter = afterRebalance.reduce((sum, c) => sum + c.size, 0); + expect(membersAfter).toBe(membersBefore); }); it('getStats returns per-cluster statistics', () => { diff --git a/tests/integration/incremental-pipeline.test.ts b/tests/integration/incremental-pipeline.test.ts index dd5a0f2..0a52a2d 100644 --- a/tests/integration/incremental-pipeline.test.ts +++ b/tests/integration/incremental-pipeline.test.ts @@ -38,9 +38,6 @@ describe('integration: IncrementalClusterer pipeline', () => { const beforeRebalance = clusterer.getClusters(); expect(beforeRebalance.length).toBeGreaterThanOrEqual(2); - // Record cohesion before rebalance - const cohesionBefore = beforeRebalance.map((c) => c.cohesion); - // Rebalance clusterer.rebalance(); diff --git a/tests/integration/v03-features.test.ts b/tests/integration/v03-features.test.ts index f9c189a..9eaa4ca 100644 --- a/tests/integration/v03-features.test.ts +++ b/tests/integration/v03-features.test.ts @@ -3,7 +3,6 @@ import { HNSWIndex } from '../../src/search/hnsw'; import { hdbscan } from '../../src/clustering/hdbscan'; import { recallAtK, ndcg } from '../../src/eval/metrics'; import { topK } from '../../src/search/topk'; -import { createEmbeddingStore } from '../../src/store/embedding-store'; import { createEmbeddingPipeline } from '../../src/pipeline/pipeline'; import { chunkByStructure } from '../../src/text/markdown'; import { SearchIndex } from '../../src/search/search-index'; @@ -208,9 +207,6 @@ describe('integration: pipeline → store → search', () => { expect(emb).toBeInstanceOf(Float32Array); } - // Store the pipeline output in an EmbeddingStore (using searchByEmbedding) - const store = createEmbeddingStore({ provider, metric: 'cosine' }); - // Add items using their pre-computed embeddings via the underlying SearchIndex // (EmbeddingStore.add calls the provider, so we use a direct approach) const index = new SearchIndex({ metric: 'cosine' }); diff --git a/tests/pipeline/checkpoint.test.ts b/tests/pipeline/checkpoint.test.ts index 513336a..8eb0483 100644 --- a/tests/pipeline/checkpoint.test.ts +++ b/tests/pipeline/checkpoint.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { createEmbeddingPipeline } from '../../src/pipeline/pipeline'; import type { CheckpointAdapter, CheckpointState } from '../../src/pipeline/checkpoint'; import type { EmbeddingProvider, EmbeddingResult } from '../../src/types'; diff --git a/tests/providers/middleware.test.ts b/tests/providers/middleware.test.ts index 2c5a7fe..6bf8a97 100644 --- a/tests/providers/middleware.test.ts +++ b/tests/providers/middleware.test.ts @@ -9,7 +9,7 @@ function createMockProvider( name: 'mock', dimensions: 3, embedCalls: 0, - async embed(input: string | string[]): Promise { + async embed(_input: string | string[]): Promise { mock.embedCalls++; return { embeddings, @@ -93,7 +93,7 @@ describe('withCache', () => { it('supports custom hashFunction', async () => { const provider = createMockProvider(); - const hashFn = vi.fn((key: string) => 'fixed-hash'); + const hashFn = vi.fn((_key: string) => 'fixed-hash'); const cached = withCache(provider, { hashFunction: hashFn }); await cached.embed('hello'); diff --git a/tests/providers/shared.test.ts b/tests/providers/shared.test.ts index 0d48053..8f3fa3a 100644 --- a/tests/providers/shared.test.ts +++ b/tests/providers/shared.test.ts @@ -69,7 +69,6 @@ describe('retryWithBackoff', () => { it('should increase delays exponentially', async () => { const delays: number[] = []; - const originalSetTimeout = globalThis.setTimeout; vi.spyOn(globalThis, 'setTimeout').mockImplementation((fn: any, delay?: number) => { if (delay !== undefined && delay > 0) delays.push(delay); // Execute immediately diff --git a/tests/storage/serialize-v2.test.ts b/tests/storage/serialize-v2.test.ts index 419b8ad..61a02ca 100644 --- a/tests/storage/serialize-v2.test.ts +++ b/tests/storage/serialize-v2.test.ts @@ -66,28 +66,6 @@ describe('binary format v2', () => { describe('v1 backward compatibility', () => { it('deserializes v1 binary data (no version byte)', () => { // Manually craft v1 binary: [count:u32LE][dims:u32LE][float32LE * dims] per embedding - const embs = [ - [1.0, 2.0], - [3.0, 4.0], - ]; - const totalBytes = 4 + embs.length * (4 + embs[0].length * 4); - const buffer = new ArrayBuffer(totalBytes); - const view = new DataView(buffer); - let offset = 0; - - view.setUint32(offset, embs.length, true); // count = 2 - offset += 4; - - for (const emb of embs) { - view.setUint32(offset, emb.length, true); // dims = 2 - offset += 4; - for (const val of emb) { - view.setFloat32(offset, val, true); - offset += 4; - } - } - - const v1Data = new Uint8Array(buffer); // First byte of v1 data is part of count (uint32LE for 2 = [2, 0, 0, 0]) // which is also 2 — same as V2_VERSION. But that's only when count=2. // For v1 with count != 2, the first byte differs. diff --git a/tests/store/embedding-store.test.ts b/tests/store/embedding-store.test.ts index 75ecf64..17a1fef 100644 --- a/tests/store/embedding-store.test.ts +++ b/tests/store/embedding-store.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { createEmbeddingStore } from '../../src/store/embedding-store'; import type { EmbeddingProvider, EmbeddingResult } from '../../src/types'; From 460247bffd5da94a515598a7c6fb31284a4769e7 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 13:09:33 -0700 Subject: [PATCH 6/8] ci: add GitHub Actions workflow The repository had no CI, so nothing enforced the checks that already existed. Formatting had silently drifted across 90 files and tests were never linted, both of which went unnoticed for exactly this reason. Three jobs: verify format:check, lint, typecheck, test and build across Node 20, 22 and 24. engines-floor builds on Node 24, then runs the built dist on Node 18. audit npm audit against the committed lockfile. The verify matrix floor is Node 20 rather than the `engines.node` floor of 18, because the dev toolchain cannot run on 18 - vitest 4 requires ^20 || ^22 || >=24 and eslint 10 requires ^20.19 || ^22.13 || >=24. Rather than drop the `>=18` claim or ship a knowingly red matrix entry, the engines-floor job verifies the claim the way it actually matters: the published artifact has no runtime dependencies, so scripts/engines-check.mjs imports both the ESM and CJS builds on Node 18 and asserts real behaviour from cosineSimilarity, dotProduct and topK. Keep it in step with engines.node. Installs use --ignore-scripts to skip the onnxruntime-node and sharp postinstalls, which fetch hundreds of MB of native binaries that nothing in CI needs; verified locally that a clean `npm ci --ignore-scripts` passes every step. `npm run smoke` is deliberately excluded since it downloads models from HuggingFace, and stays a local check. audit is gated at --audit-level=high so moderate findings in dev-only transitive dependencies cannot block a merge, and also runs weekly on a schedule so new advisories surface on their own rather than failing an unrelated pull request. --- .github/workflows/ci.yml | 87 +++++++++++++++++++++++++++++++++++++++ package.json | 3 +- scripts/engines-check.mjs | 42 +++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 scripts/engines-check.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cf84a2a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: CI + +on: + push: + branches: [master, develop] + pull_request: + schedule: + # Weekly, so new advisories surface on their own schedule instead of + # failing an unrelated pull request. + - cron: '0 6 * * 1' + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: verify (node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # The floor is 20, not the `engines.node` floor of 18, because the dev + # toolchain requires it: vitest 4 needs ^20 || ^22 || >=24 and eslint 10 + # needs ^20.19 || ^22.13 || >=24. The `engines` claim is covered by the + # engines-floor job below instead. + node: ['20', '22', '24'] + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: ${{ matrix.node }} + cache: npm + + # --ignore-scripts skips the onnxruntime-node and sharp postinstalls, + # which download hundreds of MB of native binaries. Nothing below needs + # them: the suite mocks providers, and the local ONNX provider is only + # exercised by `npm run smoke`, which is deliberately not run here since + # it downloads models from HuggingFace. + - run: npm ci --ignore-scripts + + - run: npm run format:check + - run: npm run lint + - run: npm run typecheck + - run: npm test + - run: npm run build + + engines-floor: + name: engines floor (node 18) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + # Build on a modern Node, since the toolchain cannot run on 18... + - uses: actions/setup-node@v7 + with: + node-version: '24' + cache: npm + - run: npm ci --ignore-scripts + - run: npm run build + + # ...then run the built artifact on the oldest Node that engines.node + # claims to support. The package has no runtime dependencies, so this + # needs nothing installed. Keep this in step with engines.node. + - uses: actions/setup-node@v7 + with: + node-version: '18' + - run: npm run engines:check + + audit: + name: audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: '24' + cache: npm + + # Audits the committed lockfile. Fails on high and critical only; moderate + # and below in dev-only transitive dependencies should not block a merge. + - run: npm audit --audit-level=high diff --git a/package.json b/package.json index ed49163..96941d9 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "lint": "eslint src/ tests/", "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"", - "smoke": "npm run build && node scripts/smoke-test.mjs" + "smoke": "npm run build && node scripts/smoke-test.mjs", + "engines:check": "node scripts/engines-check.mjs" }, "license": "Apache-2.0", "peerDependencies": { diff --git a/scripts/engines-check.mjs b/scripts/engines-check.mjs new file mode 100644 index 0000000..587628d --- /dev/null +++ b/scripts/engines-check.mjs @@ -0,0 +1,42 @@ +// Verifies the built dist/ actually runs on the oldest Node major that +// package.json's `engines.node` claims to support. +// +// This exists because the dev toolchain (vitest, eslint) requires Node >=20, +// so the CI matrix cannot exercise Node 18 the normal way. Only the published +// artifact needs to work there, and it has no runtime dependencies - so this +// script imports dist/ directly and needs nothing installed. +// +// Run with: node scripts/engines-check.mjs + +import { createRequire } from 'node:module'; +import { readFileSync } from 'node:fs'; +import assert from 'node:assert/strict'; + +const require = createRequire(import.meta.url); +const { engines, version } = JSON.parse(readFileSync('./package.json', 'utf8')); + +const a = [1, 0, 0]; +const b = [0.6, 0.8, 0]; + +function check(label, mod) { + assert.equal(typeof mod.cosineSimilarity, 'function', `${label}: cosineSimilarity missing`); + assert.equal(typeof mod.dotProduct, 'function', `${label}: dotProduct missing`); + assert.equal(typeof mod.topK, 'function', `${label}: topK missing`); + + // 1*0.6 + 0*0.8 + 0*0 = 0.6, and both inputs are unit vectors. + assert.ok(Math.abs(mod.cosineSimilarity(a, b) - 0.6) < 1e-6, `${label}: cosineSimilarity wrong`); + assert.ok(Math.abs(mod.dotProduct(a, b) - 0.6) < 1e-6, `${label}: dotProduct wrong`); + + const ranked = mod.topK(a, [b, [1, 0, 0], [0, 1, 0]], 2); + assert.equal(ranked.length, 2, `${label}: topK returned ${ranked.length} results, expected 2`); + assert.equal(ranked[0].index, 1, `${label}: topK did not rank the identical vector first`); + + console.log(` ok ${label}`); +} + +console.log(`embedding-utils ${version} on Node ${process.version} (engines: ${engines.node})`); + +check('esm (dist/index.js)', await import('../dist/index.js')); +check('cjs (dist/index.cjs)', require('../dist/index.cjs')); + +console.log('All engines checks passed.'); From 3ed7401e7b7efd03f17c1abd1cfb0976c86732b6 Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 13:11:44 -0700 Subject: [PATCH 7/8] docs: record tooling constraints and override removal conditions Documents the decisions made across the preceding commits, and corrects statements that they invalidated - notably the note claiming package-lock.json is gitignored, which is no longer true. - Give each of the three overrides an explicit "remove when" condition, so none of them silently outlives its reason. They force versions outside ranges their dependents declare, which is only defensible for as long as the advisory has no upstream fix. - Warn that `npm test` does not cover the sharp and adm-zip overrides: the suite mocks providers, so it stays green even if the local ONNX provider breaks. `npm run smoke` is the only guard. - Explain why esbuild has to be an override rather than a lockfile entry. - Describe the CI jobs, why the verify matrix floor is Node 20 while engines.node is >=18, and why installs pass --ignore-scripts. - Note the tests-only eslint relaxations, and that scripts/*.mjs is covered by neither eslint nor prettier. - Add an Unreleased CHANGELOG section stating plainly that none of this changes the public API or runtime behavior. --- .agents-docs/AGENTS-development-commands.md | 50 ++++++++++++++++----- AGENTS.md | 4 +- CHANGELOG.md | 13 ++++++ 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/.agents-docs/AGENTS-development-commands.md b/.agents-docs/AGENTS-development-commands.md index 6143673..947c664 100644 --- a/.agents-docs/AGENTS-development-commands.md +++ b/.agents-docs/AGENTS-development-commands.md @@ -9,12 +9,16 @@ | `npm test` | Runs the full Vitest suite once (`vitest run`). | | `npm run test:watch` | Vitest in watch mode. | | `npm run test:coverage` | Vitest with v8 coverage (`text` + `lcov`). | -| `npm run lint` | ESLint over `src/`. | +| `npm run lint` | ESLint over `src/` **and** `tests/`. | | `npm run format` | Prettier write over `src/**/*.ts` and `tests/**/*.ts`. | -| `npm run format:check` | Prettier check (no writes) — use in CI. | +| `npm run format:check` | Prettier check (no writes) — used in CI. | +| `npm run smoke` | Builds, then runs the live local-ONNX provider test (`scripts/smoke-test.mjs`). Downloads models from HuggingFace, so it is local-only and not run in CI. | +| `npm run engines:check` | Runs the built `dist/` (ESM + CJS) and asserts real behaviour, to verify the `engines.node` floor. See CI below. | Glob arguments in the `format` scripts must use **double** quotes. Single quotes are not stripped by `cmd`/PowerShell, so prettier receives the literal glob, matches nothing, and the script silently succeeds as a no-op on Windows. +`eslint.config.mjs` relaxes two rules for `tests/**` only: `no-explicit-any` is off (mocking `fetch`/`setTimeout`/provider responses legitimately needs `any`) and `no-unused-vars` honours the `^_` prefix for intentionally unused arguments. Neither relaxation applies to `src/`. Scripts under `scripts/` are `.mjs` and are covered by neither tool. + ## Running a single test Vitest passes through file/name filters: @@ -35,20 +39,46 @@ npx vitest tests/clustering # watch a directory - `dist/` is the published artifact; `files` in `package.json` ships `dist`, `README.md`, `LICENSE`. - `@huggingface/transformers` is an **optional peer dependency** — present in `devDependencies` for local-provider tests but never required for consumers who don't use the local ONNX provider. -- Engines: Node `>=18`. +- Engines: Node `>=18`. The dev toolchain itself needs `>=20`, so 18 is verified against the built artifact only — see CI below. +- `package-lock.json` **is tracked**. Use `npm ci` for reproducible installs; do not re-ignore it. + +## CI + +`.github/workflows/ci.yml` runs on pushes to `master`/`develop`, on every pull request, and weekly on a schedule. + +| Job | What it does | +|---|---| +| `verify` | `format:check`, `lint`, `typecheck`, `test`, `build` across Node 20, 22, 24. | +| `engines-floor` | Builds on Node 24, then runs `npm run engines:check` on Node 18. | +| `audit` | `npm audit --audit-level=high` against the committed lockfile. | + +Notes for anyone editing the workflow: + +- The `verify` matrix floor is **20, not 18**, because vitest 4 requires `^20 \|\| ^22 \|\| >=24` and eslint 10 requires `^20.19 \|\| ^22.13 \|\| >=24`. Node 18 cannot run the toolchain at all. `engines-floor` covers the `>=18` claim instead by exercising the built `dist/`, which has no runtime dependencies. If `engines.node` changes, update that job. +- Installs use `npm ci --ignore-scripts` to skip the `onnxruntime-node` and `sharp` postinstalls (hundreds of MB of native binaries). Nothing in CI needs them. If a future test does need real ONNX, drop `--ignore-scripts` for that job rather than adding it to `verify`. +- `audit` is gated at `high` so moderate findings in dev-only transitive dependencies don't block merges, and runs weekly so new advisories surface on their own schedule. ## Dependency overrides -`package.json` declares `overrides` to pull two transitive dependencies of `@huggingface/transformers` above the versions it pins, resolving high-severity advisories that have no upstream fix: +`package.json` declares three `overrides`. All exist to escape advisories that the declaring package pins below the fixed version, with no upstream release available. **These are temporary and should be removed as soon as the condition in the last column is met** — each one forces a version outside a range some dependency declares, so none should outlive its reason. + +| Override | Declared range it escapes | Advisory | Remove when | +|---|---|---|---| +| `sharp: ^0.35.3` | `^0.34.5`, by `@huggingface/transformers` | libvips CVEs (GHSA-f88m-g3jw-g9cj) | transformers depends on `sharp >=0.35.0` | +| `adm-zip: ^0.6.0` | `0.5.x`, via `onnxruntime-node@1.24.3` | GHSA-xcpc-8h2w-3j85 | `onnxruntime-node` depends on `adm-zip >=0.6.0` **and** transformers takes that version | +| `esbuild: 0.27.2` | `^0.27.0`, by `tsup` | GHSA-g7r4-m6w7-qqqr (affects `0.27.3` – `0.28.0`) | tsup widens to `^0.28.1` or later | + +Two things to know before touching them: -| Override | Pinned by transformers | Advisory | -|---|---|---| -| `sharp: ^0.35.3` | `^0.34.5` | libvips CVEs (GHSA-f88m-g3jw-g9cj) | -| `adm-zip: ^0.6.0` | `0.5.x` via `onnxruntime-node@1.24.3` | GHSA-xcpc-8h2w-3j85 | +- **`sharp` and `adm-zip` are not covered by `npm test`.** The unit suite mocks providers, so it will stay green even if these break the local ONNX provider. `npm run smoke` is the only guard — it exercises `onnxruntime-node` end-to-end across three models. Re-run it after any change here. +- **`esbuild` must be an override, not just a lockfile entry.** `^0.27.0` has no safe version above `0.27.2`, so anything that re-resolves the range (a fresh `npm install`, `npm update`, a dependency bump) would pull a vulnerable version back in. The pin is what makes it stick. -Both are outside the ranges transformers declares, so **re-run `npm run smoke` after changing them** — it exercises `onnxruntime-node` end-to-end across three models and is the only guard that these overrides remain safe. +Check whether any of these can go with: -`esbuild` is also pinned to exactly `0.27.2`. tsup declares `esbuild: ^0.27.0`, and every version from `0.27.3` up to `0.28.0` is covered by GHSA-g7r4-m6w7-qqqr, so `0.27.2` is the only in-range version without the advisory. The override (rather than a lockfile pin) is required because `package-lock.json` is gitignored — a fresh `npm install` would otherwise resolve a vulnerable `0.27.x`. Remove the pin once tsup widens its range to `^0.28.1` or later. +```bash +npm view @huggingface/transformers dependencies +npm view tsup dependencies.esbuild +``` ## TypeScript version constraints diff --git a/AGENTS.md b/AGENTS.md index e433178..5cae695 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,9 @@ The sections below are brief summaries. Each links to a detail file in `.agents- ## Development Commands -`npm run build` (tsup → dual ESM/CJS + dts), `npm test` (vitest run), `npm run test:watch`, `npm run test:coverage`, `npm run lint` (eslint), `npm run format` (prettier). Tests live in `tests/` mirroring `src/`. +`npm run build` (tsup → dual ESM/CJS + dts), `npm test` (vitest run), `npm run test:watch`, `npm run test:coverage`, `npm run lint` (eslint over `src/` + `tests/`), `npm run format` / `npm run format:check` (prettier), `npm run smoke` (live ONNX, local-only), `npm run engines:check`. Tests live in `tests/` mirroring `src/`. CI (`.github/workflows/ci.yml`) enforces all of these except `smoke`; `package-lock.json` is tracked, so use `npm ci`. + +Three temporary `overrides` in `package.json` (`sharp`, `adm-zip`, `esbuild`) hold transitive dependencies at non-vulnerable versions, and TypeScript is held at 6.x. Both have removal conditions documented — read the detail file before changing either. Details: [Development Commands](./.agents-docs/AGENTS-development-commands.md) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28cbfdb..ce8be08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [Unreleased] + +### Maintenance + +Tooling and dependency work only — **no changes to the public API or to runtime behavior**. Consumers are unaffected. + +- **TypeScript 6:** upgraded from 5.9 to 6.0.3, the highest major currently supported by `@typescript-eslint` v8 (`>=4.8.4 <6.1.0`). TypeScript 7 is deliberately deferred until typescript-eslint supports it. `tsconfig.json` now sets `"ignoreDeprecations": "6.0"`, which TS 6 requires because tsup's dts pipeline injects the now-deprecated `baseUrl`. +- **All npm audit advisories resolved (5 → 0).** Added `overrides` for `sharp` (libvips CVEs), `adm-zip`, and `esbuild`, each of which was pinned below its fixed version by a dependency with no upstream fix available. All three sat under `@huggingface/transformers`, the optional peer dependency, so consumers not using the local ONNX provider were never exposed. Every override is documented with a condition for its removal. +- **`package-lock.json` is now tracked.** It was previously gitignored, so installs were not reproducible and the `overrides` above had no recorded resolution. Use `npm ci`. +- **Fixed `npm run format`, which was a silent no-op on Windows.** Its globs were single-quoted, which `cmd`/PowerShell do not strip, so Prettier matched zero files and exited successfully. As a result 90 files had been formatted at Prettier's defaults rather than this project's `.prettierrc` (100 columns, `es5` trailing commas); the configured style is now applied throughout. Added `npm run format:check`. +- **`npm run lint` now covers `tests/` as well as `src/`.** `eslint.config.mjs` had always declared `tests/**`, but the script never passed it. Cleared the dead code this surfaced — unused imports, a vestigial `EmbeddingStore`, and an abandoned serialization buffer — and relaxed `no-explicit-any` for test files only. +- **Added CI** (`.github/workflows/ci.yml`): format, lint, typecheck, test and build across Node 20/22/24, plus an `engines-floor` job that runs the built `dist/` on Node 18 to verify the `engines.node` claim (the dev toolchain itself now requires Node >=20), and a weekly `npm audit`. + ## [0.6.0] - 2026-08-12 ### New Features From bcd0853604aa1f5994709ffcfc3ece75148daa5b Mon Sep 17 00:00:00 2001 From: Justin Parker Date: Fri, 14 Aug 2026 13:18:36 -0700 Subject: [PATCH 8/8] chore: cover scripts/ with lint and format, pin line endings to LF scripts/*.mjs was covered by neither eslint nor prettier, the last gap in the tooling. Add scripts/ to both, with a scripts-only eslint block: these files are plain ESM parsed by the default parser, so the typescript-eslint rules do not apply and only core no-unused-vars is enabled. Formatting them surfaced a second problem. scripts/engines-check.mjs showed as 84 changed lines for a 42-line file - every line - because it had been committed with CRLF while the rest of the tree is LF. Prettier normalized it, and `git ls-files --eol` found two more: .gitignore and .github/workflows/ci.yml. All three were added or edited in the preceding commits on Windows; the other 168 tracked files were already LF. Add .gitattributes with `* text=auto eol=lf` and renormalize. Prettier already enforces LF, but only for the extensions it covers, so it could never have caught .gitignore or ci.yml. A whole-file CRLF diff hides the real change during review, which is how this went unnoticed. Verified: format:check, lint, typecheck, 801 tests, engines:check and the live ONNX smoke test all pass; no CRLF remains in the index. --- .agents-docs/AGENTS-development-commands.md | 8 +- .gitattributes | 7 + .github/workflows/ci.yml | 174 ++++++++++---------- .gitignore | 10 +- CHANGELOG.md | 1 + eslint.config.mjs | 12 ++ package.json | 6 +- scripts/engines-check.mjs | 84 +++++----- scripts/smoke-test.mjs | 19 +-- 9 files changed, 169 insertions(+), 152 deletions(-) create mode 100644 .gitattributes diff --git a/.agents-docs/AGENTS-development-commands.md b/.agents-docs/AGENTS-development-commands.md index 947c664..ed742b9 100644 --- a/.agents-docs/AGENTS-development-commands.md +++ b/.agents-docs/AGENTS-development-commands.md @@ -9,15 +9,17 @@ | `npm test` | Runs the full Vitest suite once (`vitest run`). | | `npm run test:watch` | Vitest in watch mode. | | `npm run test:coverage` | Vitest with v8 coverage (`text` + `lcov`). | -| `npm run lint` | ESLint over `src/` **and** `tests/`. | -| `npm run format` | Prettier write over `src/**/*.ts` and `tests/**/*.ts`. | +| `npm run lint` | ESLint over `src/`, `tests/` and `scripts/`. | +| `npm run format` | Prettier write over `src/**/*.ts`, `tests/**/*.ts` and `scripts/**/*.mjs`. | | `npm run format:check` | Prettier check (no writes) — used in CI. | | `npm run smoke` | Builds, then runs the live local-ONNX provider test (`scripts/smoke-test.mjs`). Downloads models from HuggingFace, so it is local-only and not run in CI. | | `npm run engines:check` | Runs the built `dist/` (ESM + CJS) and asserts real behaviour, to verify the `engines.node` floor. See CI below. | Glob arguments in the `format` scripts must use **double** quotes. Single quotes are not stripped by `cmd`/PowerShell, so prettier receives the literal glob, matches nothing, and the script silently succeeds as a no-op on Windows. -`eslint.config.mjs` relaxes two rules for `tests/**` only: `no-explicit-any` is off (mocking `fetch`/`setTimeout`/provider responses legitimately needs `any`) and `no-unused-vars` honours the `^_` prefix for intentionally unused arguments. Neither relaxation applies to `src/`. Scripts under `scripts/` are `.mjs` and are covered by neither tool. +`eslint.config.mjs` relaxes two rules for `tests/**` only: `no-explicit-any` is off (mocking `fetch`/`setTimeout`/provider responses legitimately needs `any`) and `no-unused-vars` honours the `^_` prefix for intentionally unused arguments. Neither relaxation applies to `src/`. `scripts/**/*.mjs` is plain ESM parsed by the default parser, so only core rules apply there. + +`.gitattributes` pins the whole tree to `eol=lf` via `* text=auto eol=lf`. Files authored on Windows otherwise enter the index with CRLF, which produces a whole-file diff that buries the real change — this had already happened to three files. Prettier enforces LF too, but only for the extensions it covers, so the attribute is what protects `.yml`, `.json` and `.md`. ## Running a single test diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..682208b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Normalize text files to LF in the repository regardless of the authoring +# platform. Without this, files written on Windows land in the index with CRLF +# while the rest of the tree is LF, producing whole-file diffs that hide the +# real change. Prettier already enforces LF, but only for the files it covers. +* text=auto eol=lf + +*.jpg binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf84a2a..4f08a30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,87 +1,87 @@ -name: CI - -on: - push: - branches: [master, develop] - pull_request: - schedule: - # Weekly, so new advisories surface on their own schedule instead of - # failing an unrelated pull request. - - cron: '0 6 * * 1' - -permissions: - contents: read - -concurrency: - group: ci-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - verify: - name: verify (node ${{ matrix.node }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - # The floor is 20, not the `engines.node` floor of 18, because the dev - # toolchain requires it: vitest 4 needs ^20 || ^22 || >=24 and eslint 10 - # needs ^20.19 || ^22.13 || >=24. The `engines` claim is covered by the - # engines-floor job below instead. - node: ['20', '22', '24'] - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-node@v7 - with: - node-version: ${{ matrix.node }} - cache: npm - - # --ignore-scripts skips the onnxruntime-node and sharp postinstalls, - # which download hundreds of MB of native binaries. Nothing below needs - # them: the suite mocks providers, and the local ONNX provider is only - # exercised by `npm run smoke`, which is deliberately not run here since - # it downloads models from HuggingFace. - - run: npm ci --ignore-scripts - - - run: npm run format:check - - run: npm run lint - - run: npm run typecheck - - run: npm test - - run: npm run build - - engines-floor: - name: engines floor (node 18) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - # Build on a modern Node, since the toolchain cannot run on 18... - - uses: actions/setup-node@v7 - with: - node-version: '24' - cache: npm - - run: npm ci --ignore-scripts - - run: npm run build - - # ...then run the built artifact on the oldest Node that engines.node - # claims to support. The package has no runtime dependencies, so this - # needs nothing installed. Keep this in step with engines.node. - - uses: actions/setup-node@v7 - with: - node-version: '18' - - run: npm run engines:check - - audit: - name: audit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-node@v7 - with: - node-version: '24' - cache: npm - - # Audits the committed lockfile. Fails on high and critical only; moderate - # and below in dev-only transitive dependencies should not block a merge. - - run: npm audit --audit-level=high +name: CI + +on: + push: + branches: [master, develop] + pull_request: + schedule: + # Weekly, so new advisories surface on their own schedule instead of + # failing an unrelated pull request. + - cron: '0 6 * * 1' + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: verify (node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # The floor is 20, not the `engines.node` floor of 18, because the dev + # toolchain requires it: vitest 4 needs ^20 || ^22 || >=24 and eslint 10 + # needs ^20.19 || ^22.13 || >=24. The `engines` claim is covered by the + # engines-floor job below instead. + node: ['20', '22', '24'] + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: ${{ matrix.node }} + cache: npm + + # --ignore-scripts skips the onnxruntime-node and sharp postinstalls, + # which download hundreds of MB of native binaries. Nothing below needs + # them: the suite mocks providers, and the local ONNX provider is only + # exercised by `npm run smoke`, which is deliberately not run here since + # it downloads models from HuggingFace. + - run: npm ci --ignore-scripts + + - run: npm run format:check + - run: npm run lint + - run: npm run typecheck + - run: npm test + - run: npm run build + + engines-floor: + name: engines floor (node 18) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + # Build on a modern Node, since the toolchain cannot run on 18... + - uses: actions/setup-node@v7 + with: + node-version: '24' + cache: npm + - run: npm ci --ignore-scripts + - run: npm run build + + # ...then run the built artifact on the oldest Node that engines.node + # claims to support. The package has no runtime dependencies, so this + # needs nothing installed. Keep this in step with engines.node. + - uses: actions/setup-node@v7 + with: + node-version: '18' + - run: npm run engines:check + + audit: + name: audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: '24' + cache: npm + + # Audits the committed lockfile. Fails on high and critical only; moderate + # and below in dev-only transitive dependencies should not block a merge. + - run: npm audit --audit-level=high diff --git a/.gitignore b/.gitignore index a740113..fa04c29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -node_modules -dist -coverage -specs -specs--completed +node_modules +dist +coverage +specs +specs--completed diff --git a/CHANGELOG.md b/CHANGELOG.md index ce8be08..51ed2da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Tooling and dependency work only — **no changes to the public API or to runtim - **Fixed `npm run format`, which was a silent no-op on Windows.** Its globs were single-quoted, which `cmd`/PowerShell do not strip, so Prettier matched zero files and exited successfully. As a result 90 files had been formatted at Prettier's defaults rather than this project's `.prettierrc` (100 columns, `es5` trailing commas); the configured style is now applied throughout. Added `npm run format:check`. - **`npm run lint` now covers `tests/` as well as `src/`.** `eslint.config.mjs` had always declared `tests/**`, but the script never passed it. Cleared the dead code this surfaced — unused imports, a vestigial `EmbeddingStore`, and an abandoned serialization buffer — and relaxed `no-explicit-any` for test files only. - **Added CI** (`.github/workflows/ci.yml`): format, lint, typecheck, test and build across Node 20/22/24, plus an `engines-floor` job that runs the built `dist/` on Node 18 to verify the `engines.node` claim (the dev toolchain itself now requires Node >=20), and a weekly `npm audit`. +- **`scripts/` is now linted and formatted**, and `.gitattributes` pins the tree to LF line endings so files authored on Windows can no longer enter the index with CRLF. ## [0.6.0] - 2026-08-12 diff --git a/eslint.config.mjs b/eslint.config.mjs index 5218eb0..a66b82f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,4 +28,16 @@ export default [ ], }, }, + { + // Plain ESM, parsed by the default parser rather than the TypeScript one, + // so only core rules apply here. + files: ['scripts/**/*.mjs'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + rules: { + 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + }, + }, ]; diff --git a/package.json b/package.json index 96941d9..ce16100 100644 --- a/package.json +++ b/package.json @@ -52,9 +52,9 @@ "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage", - "lint": "eslint src/ tests/", - "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", - "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"", + "lint": "eslint src/ tests/ scripts/", + "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.mjs\"", + "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.mjs\"", "smoke": "npm run build && node scripts/smoke-test.mjs", "engines:check": "node scripts/engines-check.mjs" }, diff --git a/scripts/engines-check.mjs b/scripts/engines-check.mjs index 587628d..e0349e6 100644 --- a/scripts/engines-check.mjs +++ b/scripts/engines-check.mjs @@ -1,42 +1,42 @@ -// Verifies the built dist/ actually runs on the oldest Node major that -// package.json's `engines.node` claims to support. -// -// This exists because the dev toolchain (vitest, eslint) requires Node >=20, -// so the CI matrix cannot exercise Node 18 the normal way. Only the published -// artifact needs to work there, and it has no runtime dependencies - so this -// script imports dist/ directly and needs nothing installed. -// -// Run with: node scripts/engines-check.mjs - -import { createRequire } from 'node:module'; -import { readFileSync } from 'node:fs'; -import assert from 'node:assert/strict'; - -const require = createRequire(import.meta.url); -const { engines, version } = JSON.parse(readFileSync('./package.json', 'utf8')); - -const a = [1, 0, 0]; -const b = [0.6, 0.8, 0]; - -function check(label, mod) { - assert.equal(typeof mod.cosineSimilarity, 'function', `${label}: cosineSimilarity missing`); - assert.equal(typeof mod.dotProduct, 'function', `${label}: dotProduct missing`); - assert.equal(typeof mod.topK, 'function', `${label}: topK missing`); - - // 1*0.6 + 0*0.8 + 0*0 = 0.6, and both inputs are unit vectors. - assert.ok(Math.abs(mod.cosineSimilarity(a, b) - 0.6) < 1e-6, `${label}: cosineSimilarity wrong`); - assert.ok(Math.abs(mod.dotProduct(a, b) - 0.6) < 1e-6, `${label}: dotProduct wrong`); - - const ranked = mod.topK(a, [b, [1, 0, 0], [0, 1, 0]], 2); - assert.equal(ranked.length, 2, `${label}: topK returned ${ranked.length} results, expected 2`); - assert.equal(ranked[0].index, 1, `${label}: topK did not rank the identical vector first`); - - console.log(` ok ${label}`); -} - -console.log(`embedding-utils ${version} on Node ${process.version} (engines: ${engines.node})`); - -check('esm (dist/index.js)', await import('../dist/index.js')); -check('cjs (dist/index.cjs)', require('../dist/index.cjs')); - -console.log('All engines checks passed.'); +// Verifies the built dist/ actually runs on the oldest Node major that +// package.json's `engines.node` claims to support. +// +// This exists because the dev toolchain (vitest, eslint) requires Node >=20, +// so the CI matrix cannot exercise Node 18 the normal way. Only the published +// artifact needs to work there, and it has no runtime dependencies - so this +// script imports dist/ directly and needs nothing installed. +// +// Run with: node scripts/engines-check.mjs + +import { createRequire } from 'node:module'; +import { readFileSync } from 'node:fs'; +import assert from 'node:assert/strict'; + +const require = createRequire(import.meta.url); +const { engines, version } = JSON.parse(readFileSync('./package.json', 'utf8')); + +const a = [1, 0, 0]; +const b = [0.6, 0.8, 0]; + +function check(label, mod) { + assert.equal(typeof mod.cosineSimilarity, 'function', `${label}: cosineSimilarity missing`); + assert.equal(typeof mod.dotProduct, 'function', `${label}: dotProduct missing`); + assert.equal(typeof mod.topK, 'function', `${label}: topK missing`); + + // 1*0.6 + 0*0.8 + 0*0 = 0.6, and both inputs are unit vectors. + assert.ok(Math.abs(mod.cosineSimilarity(a, b) - 0.6) < 1e-6, `${label}: cosineSimilarity wrong`); + assert.ok(Math.abs(mod.dotProduct(a, b) - 0.6) < 1e-6, `${label}: dotProduct wrong`); + + const ranked = mod.topK(a, [b, [1, 0, 0], [0, 1, 0]], 2); + assert.equal(ranked.length, 2, `${label}: topK returned ${ranked.length} results, expected 2`); + assert.equal(ranked[0].index, 1, `${label}: topK did not rank the identical vector first`); + + console.log(` ok ${label}`); +} + +console.log(`embedding-utils ${version} on Node ${process.version} (engines: ${engines.node})`); + +check('esm (dist/index.js)', await import('../dist/index.js')); +check('cjs (dist/index.cjs)', require('../dist/index.cjs')); + +console.log('All engines checks passed.'); diff --git a/scripts/smoke-test.mjs b/scripts/smoke-test.mjs index 8faefbd..f8a4057 100644 --- a/scripts/smoke-test.mjs +++ b/scripts/smoke-test.mjs @@ -63,20 +63,19 @@ for (const [modelId, precision] of models) { const info = getModelInfo(modelId); const expectedPooling = info?.pooling ?? 'mean'; const expectedDims = info?.dimensions; - const hasPrefixes = - !!info?.prefixes && info.prefixes.document !== info.prefixes.query; + const hasPrefixes = !!info?.prefixes && info.prefixes.document !== info.prefixes.query; console.log( `▶ ${modelId}` + (precision ? ` [${precision}]` : '') + ` (registry: ${expectedDims ?? '?'}d, pooling=${expectedPooling}` + (info ? '' : ', NOT in registry') + - `)`, + `)` ); try { const provider = createLocalProvider( - precision ? { model: modelId, precision } : { model: modelId }, + precision ? { model: modelId, precision } : { model: modelId } ); const docRes = await provider.embed(DOC, { inputType: 'document' }); @@ -87,9 +86,7 @@ for (const [modelId, precision] of models) { if (docRes.dimensions === expectedDims) { pass(`dimensions = ${docRes.dimensions}`); } else { - fail( - `dimensions mismatch: got ${docRes.dimensions}, registry says ${expectedDims}`, - ); + fail(`dimensions mismatch: got ${docRes.dimensions}, registry says ${expectedDims}`); } } else { console.log(` • dimensions = ${docRes.dimensions} (no registry value)`); @@ -108,14 +105,12 @@ for (const [modelId, precision] of models) { // bit-identical vectors for the same input. const reuseStart = Date.now(); const provider2 = createLocalProvider( - precision ? { model: modelId, precision } : { model: modelId }, + precision ? { model: modelId, precision } : { model: modelId } ); const docRes2 = await provider2.embed(DOC, { inputType: 'document' }); const reuseMs = Date.now() - reuseStart; const docVec2 = docRes2.embeddings[0]; - const identical = - docVec2.length === docVec.length && - docVec2.every((v, i) => v === docVec[i]); + const identical = docVec2.length === docVec.length && docVec2.every((v, i) => v === docVec[i]); if (identical) { pass(`shared-pipeline reuse: identical vectors (2nd provider embed took ${reuseMs}ms)`); } else { @@ -132,7 +127,7 @@ for (const [modelId, precision] of models) { const prefixDelta = 1 - cosine(queryRes.embeddings[0], queryAsDoc.embeddings[0]); if (prefixDelta > 1e-4) { pass( - `prefixes applied (query/doc encodings differ by ${prefixDelta.toExponential(2)}; query~doc sim=${sim.toFixed(3)})`, + `prefixes applied (query/doc encodings differ by ${prefixDelta.toExponential(2)}; query~doc sim=${sim.toFixed(3)})` ); } else { fail('prefixes do not appear to be applied (query == doc encoding)');