sensemaking 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/chunk/extract.d.cts +2 -0
- package/dist/cjs/chunk/extract.d.ts +2 -0
- package/dist/cjs/chunk/extract.js +139 -0
- package/dist/cjs/chunk/extract.js.map +1 -0
- package/dist/cjs/chunk/group.d.cts +4 -0
- package/dist/cjs/chunk/group.d.ts +4 -0
- package/dist/cjs/chunk/group.js +489 -0
- package/dist/cjs/chunk/group.js.map +1 -0
- package/dist/cjs/chunk/index.d.cts +8 -0
- package/dist/cjs/chunk/index.d.ts +8 -0
- package/dist/cjs/chunk/index.js +44 -0
- package/dist/cjs/chunk/index.js.map +1 -0
- package/dist/cjs/chunk/parse.d.cts +2 -0
- package/dist/cjs/chunk/parse.d.ts +2 -0
- package/dist/cjs/chunk/parse.js +71 -0
- package/dist/cjs/chunk/parse.js.map +1 -0
- package/dist/cjs/chunk/types.d.cts +19 -0
- package/dist/cjs/chunk/types.d.ts +19 -0
- package/dist/cjs/chunk/types.js +5 -0
- package/dist/cjs/chunk/types.js.map +1 -0
- package/dist/cjs/chunk/version.d.cts +1 -0
- package/dist/cjs/chunk/version.d.ts +1 -0
- package/dist/cjs/chunk/version.js +14 -0
- package/dist/cjs/chunk/version.js.map +1 -0
- package/dist/cjs/cli/map.js +5 -5
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +3 -3
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +9 -8
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +5 -5
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +5 -5
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +5 -5
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.d.cts +1 -1
- package/dist/cjs/cli/shared.d.ts +1 -1
- package/dist/cjs/cli/shared.js +3 -3
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/status.js +19 -19
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/commands/map.d.cts +1 -1
- package/dist/cjs/commands/map.d.ts +1 -1
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/peek.d.cts +1 -1
- package/dist/cjs/commands/peek.d.ts +1 -1
- package/dist/cjs/commands/peek.js +13 -6
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/scope.js +1 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.d.cts +1 -1
- package/dist/cjs/commands/search.d.ts +1 -1
- package/dist/cjs/commands/search.js +3 -3
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.js +1 -1
- package/dist/cjs/commands/signals.js.map +1 -1
- package/dist/cjs/config/access.d.cts +3 -1
- package/dist/cjs/config/access.d.ts +3 -1
- package/dist/cjs/config/access.js +11 -11
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +1 -0
- package/dist/cjs/config/index.d.ts +1 -0
- package/dist/cjs/config/index.js +4 -0
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/types.d.cts +1 -0
- package/dist/cjs/config/types.d.ts +1 -0
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.d.cts +1 -0
- package/dist/cjs/config/validate.d.ts +1 -0
- package/dist/cjs/config/validate.js +8 -1
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/db/open.d.cts +1 -1
- package/dist/cjs/db/open.d.ts +1 -1
- package/dist/cjs/db/open.js +4 -4
- package/dist/cjs/db/open.js.map +1 -1
- package/dist/cjs/db/reconcile.js +8 -8
- package/dist/cjs/db/reconcile.js.map +1 -1
- package/dist/cjs/{sql-functions.js → db/sql-functions.js} +1 -1
- package/dist/cjs/db/sql-functions.js.map +1 -0
- package/dist/cjs/embed/distribution.d.cts +4 -0
- package/dist/cjs/embed/distribution.d.ts +4 -0
- package/dist/cjs/embed/distribution.js +30 -0
- package/dist/cjs/embed/distribution.js.map +1 -0
- package/dist/cjs/embed/langfit.d.cts +0 -3
- package/dist/cjs/embed/langfit.d.ts +0 -3
- package/dist/cjs/embed/langfit.js +53 -96
- package/dist/cjs/embed/langfit.js.map +1 -1
- package/dist/cjs/embed/languages.js.map +1 -1
- package/dist/cjs/embed/query.js +6 -6
- package/dist/cjs/embed/query.js.map +1 -1
- package/dist/cjs/embed/static.js +8 -35
- package/dist/cjs/embed/static.js.map +1 -1
- package/dist/cjs/features/embed.d.cts +2 -5
- package/dist/cjs/features/embed.d.ts +2 -5
- package/dist/cjs/features/embed.js +36 -58
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/{fences.js → features/fences.js} +1 -1
- package/dist/cjs/features/fences.js.map +1 -0
- package/dist/cjs/features/links.js +1 -1
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js +1 -1
- package/dist/cjs/features/rank.js.map +1 -1
- package/dist/cjs/features/sections.js +4 -3
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js +1 -1
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/features/types.d.cts +4 -2
- package/dist/cjs/features/types.d.ts +4 -2
- package/dist/cjs/graph/graph.js.map +1 -0
- package/dist/cjs/graph/traverse.js.map +1 -0
- package/dist/cjs/index.d.cts +4 -4
- package/dist/cjs/index.d.ts +4 -4
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output/column-hint.js.map +1 -0
- package/dist/cjs/output/output.js.map +1 -0
- package/dist/cjs/output/progress.js.map +1 -0
- package/dist/cjs/{search-error.js → output/search-error.js} +1 -1
- package/dist/cjs/output/search-error.js.map +1 -0
- package/dist/cjs/scan/frontmatter.d.cts +13 -0
- package/dist/cjs/scan/frontmatter.d.ts +13 -0
- package/dist/cjs/scan/frontmatter.js +229 -0
- package/dist/cjs/scan/frontmatter.js.map +1 -0
- package/dist/cjs/scan/index.d.cts +25 -0
- package/dist/cjs/scan/index.d.ts +25 -0
- package/dist/cjs/scan/index.js +107 -0
- package/dist/cjs/scan/index.js.map +1 -0
- package/dist/cjs/scan/list.d.cts +12 -0
- package/dist/cjs/scan/list.d.ts +12 -0
- package/dist/cjs/scan/list.js +154 -0
- package/dist/cjs/scan/list.js.map +1 -0
- package/dist/cjs/text/segment.d.cts +3 -0
- package/dist/cjs/text/segment.d.ts +3 -0
- package/dist/cjs/{segment.js → text/segment.js} +17 -14
- package/dist/cjs/text/segment.js.map +1 -0
- package/dist/cjs/text/strip.d.cts +3 -0
- package/dist/cjs/text/strip.d.ts +3 -0
- package/dist/cjs/text/strip.js +32 -0
- package/dist/cjs/text/strip.js.map +1 -0
- package/dist/esm/chunk/extract.d.ts +2 -0
- package/dist/esm/chunk/extract.js +114 -0
- package/dist/esm/chunk/extract.js.map +1 -0
- package/dist/esm/chunk/group.d.ts +4 -0
- package/dist/esm/chunk/group.js +253 -0
- package/dist/esm/chunk/group.js.map +1 -0
- package/dist/esm/chunk/index.d.ts +8 -0
- package/dist/esm/chunk/index.js +16 -0
- package/dist/esm/chunk/index.js.map +1 -0
- package/dist/esm/chunk/parse.d.ts +2 -0
- package/dist/esm/chunk/parse.js +63 -0
- package/dist/esm/chunk/parse.js.map +1 -0
- package/dist/esm/chunk/types.d.ts +19 -0
- package/dist/esm/chunk/types.js +2 -0
- package/dist/esm/chunk/types.js.map +1 -0
- package/dist/esm/chunk/version.d.ts +1 -0
- package/dist/esm/chunk/version.js +3 -0
- package/dist/esm/chunk/version.js.map +1 -0
- package/dist/esm/cli/map.js +2 -2
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js +2 -2
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js +4 -3
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js +2 -2
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js +2 -2
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js +2 -2
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.d.ts +1 -1
- package/dist/esm/cli/shared.js +3 -3
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/status.js +3 -3
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/commands/map.d.ts +1 -1
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/peek.d.ts +1 -1
- package/dist/esm/commands/peek.js +8 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/scope.js +1 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.d.ts +1 -1
- package/dist/esm/commands/search.js +3 -3
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.js +1 -1
- package/dist/esm/commands/signals.js.map +1 -1
- package/dist/esm/config/access.d.ts +3 -1
- package/dist/esm/config/access.js +9 -9
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +1 -0
- package/dist/esm/config/index.js +1 -0
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/types.d.ts +1 -0
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.d.ts +1 -0
- package/dist/esm/config/validate.js +8 -2
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/db/open.d.ts +1 -1
- package/dist/esm/db/open.js +5 -5
- package/dist/esm/db/open.js.map +1 -1
- package/dist/esm/db/reconcile.js +4 -4
- package/dist/esm/db/reconcile.js.map +1 -1
- package/dist/esm/{sql-functions.js → db/sql-functions.js} +1 -1
- package/dist/esm/db/sql-functions.js.map +1 -0
- package/dist/esm/embed/distribution.d.ts +4 -0
- package/dist/esm/embed/distribution.js +11 -0
- package/dist/esm/embed/distribution.js.map +1 -0
- package/dist/esm/embed/langfit.d.ts +0 -3
- package/dist/esm/embed/langfit.js +8 -8
- package/dist/esm/embed/langfit.js.map +1 -1
- package/dist/esm/embed/languages.js.map +1 -1
- package/dist/esm/embed/query.js +5 -5
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/embed/static.js +6 -2
- package/dist/esm/embed/static.js.map +1 -1
- package/dist/esm/features/embed.d.ts +2 -5
- package/dist/esm/features/embed.js +34 -32
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/{fences.js → features/fences.js} +1 -1
- package/dist/esm/features/fences.js.map +1 -0
- package/dist/esm/features/links.js +1 -1
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/rank.js +1 -1
- package/dist/esm/features/rank.js.map +1 -1
- package/dist/esm/features/sections.js +4 -3
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js +1 -1
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/features/types.d.ts +4 -2
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/graph/graph.js.map +1 -0
- package/dist/esm/graph/traverse.js.map +1 -0
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output/column-hint.js.map +1 -0
- package/dist/esm/output/output.js.map +1 -0
- package/dist/esm/output/progress.js.map +1 -0
- package/dist/esm/{search-error.js → output/search-error.js} +1 -1
- package/dist/esm/output/search-error.js.map +1 -0
- package/dist/esm/scan/frontmatter.d.ts +13 -0
- package/dist/esm/{scan.js → scan/frontmatter.js} +4 -123
- package/dist/esm/scan/frontmatter.js.map +1 -0
- package/dist/esm/scan/index.d.ts +25 -0
- package/dist/esm/scan/index.js +58 -0
- package/dist/esm/scan/index.js.map +1 -0
- package/dist/esm/scan/list.d.ts +12 -0
- package/dist/esm/scan/list.js +58 -0
- package/dist/esm/scan/list.js.map +1 -0
- package/dist/esm/text/segment.d.ts +3 -0
- package/dist/esm/{segment.js → text/segment.js} +3 -3
- package/dist/esm/text/segment.js.map +1 -0
- package/dist/esm/text/strip.d.ts +3 -0
- package/dist/esm/text/strip.js +12 -0
- package/dist/esm/text/strip.js.map +1 -0
- package/package.json +15 -2
- package/schema.json +8 -1
- package/skills/sense/SKILL.md +1 -1
- package/skills/sense-setup/SKILL.md +3 -3
- package/dist/cjs/column-hint.js.map +0 -1
- package/dist/cjs/fences.js.map +0 -1
- package/dist/cjs/graph.js.map +0 -1
- package/dist/cjs/output.js.map +0 -1
- package/dist/cjs/progress.js.map +0 -1
- package/dist/cjs/scan.d.cts +0 -35
- package/dist/cjs/scan.d.ts +0 -35
- package/dist/cjs/scan.js +0 -436
- package/dist/cjs/scan.js.map +0 -1
- package/dist/cjs/search-error.js.map +0 -1
- package/dist/cjs/segment.d.cts +0 -2
- package/dist/cjs/segment.d.ts +0 -2
- package/dist/cjs/segment.js.map +0 -1
- package/dist/cjs/sql-functions.js.map +0 -1
- package/dist/cjs/traverse.js.map +0 -1
- package/dist/esm/column-hint.js.map +0 -1
- package/dist/esm/fences.js.map +0 -1
- package/dist/esm/graph.js.map +0 -1
- package/dist/esm/output.js.map +0 -1
- package/dist/esm/progress.js.map +0 -1
- package/dist/esm/scan.d.ts +0 -35
- package/dist/esm/scan.js.map +0 -1
- package/dist/esm/search-error.js.map +0 -1
- package/dist/esm/segment.d.ts +0 -2
- package/dist/esm/segment.js.map +0 -1
- package/dist/esm/sql-functions.js.map +0 -1
- package/dist/esm/traverse.js.map +0 -1
- /package/dist/cjs/{sql-functions.d.cts → db/sql-functions.d.cts} +0 -0
- /package/dist/cjs/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
- /package/dist/cjs/{fences.d.cts → features/fences.d.cts} +0 -0
- /package/dist/cjs/{fences.d.ts → features/fences.d.ts} +0 -0
- /package/dist/cjs/{graph.d.cts → graph/graph.d.cts} +0 -0
- /package/dist/cjs/{graph.d.ts → graph/graph.d.ts} +0 -0
- /package/dist/cjs/{graph.js → graph/graph.js} +0 -0
- /package/dist/cjs/{traverse.d.cts → graph/traverse.d.cts} +0 -0
- /package/dist/cjs/{traverse.d.ts → graph/traverse.d.ts} +0 -0
- /package/dist/cjs/{traverse.js → graph/traverse.js} +0 -0
- /package/dist/cjs/{column-hint.d.cts → output/column-hint.d.cts} +0 -0
- /package/dist/cjs/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
- /package/dist/cjs/{column-hint.js → output/column-hint.js} +0 -0
- /package/dist/cjs/{output.d.cts → output/output.d.cts} +0 -0
- /package/dist/cjs/{output.d.ts → output/output.d.ts} +0 -0
- /package/dist/cjs/{output.js → output/output.js} +0 -0
- /package/dist/cjs/{progress.d.cts → output/progress.d.cts} +0 -0
- /package/dist/cjs/{progress.d.ts → output/progress.d.ts} +0 -0
- /package/dist/cjs/{progress.js → output/progress.js} +0 -0
- /package/dist/cjs/{search-error.d.cts → output/search-error.d.cts} +0 -0
- /package/dist/cjs/{search-error.d.ts → output/search-error.d.ts} +0 -0
- /package/dist/esm/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
- /package/dist/esm/{fences.d.ts → features/fences.d.ts} +0 -0
- /package/dist/esm/{graph.d.ts → graph/graph.d.ts} +0 -0
- /package/dist/esm/{graph.js → graph/graph.js} +0 -0
- /package/dist/esm/{traverse.d.ts → graph/traverse.d.ts} +0 -0
- /package/dist/esm/{traverse.js → graph/traverse.js} +0 -0
- /package/dist/esm/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
- /package/dist/esm/{column-hint.js → output/column-hint.js} +0 -0
- /package/dist/esm/{output.d.ts → output/output.d.ts} +0 -0
- /package/dist/esm/{output.js → output/output.js} +0 -0
- /package/dist/esm/{progress.d.ts → output/progress.d.ts} +0 -0
- /package/dist/esm/{progress.js → output/progress.js} +0 -0
- /package/dist/esm/{search-error.d.ts → output/search-error.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/query.ts"],"sourcesContent":["import { join } from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Chunk } from '../features/embed.ts';\nimport { embed } from '../features/embed.ts';\nimport { progress } from '../progress.ts';\nimport { parseFile } from '../scan.ts';\nimport { checkLanguageFit } from './langfit.ts';\nimport { getProvider } from './registry.ts';\n\n// Storage lever fixed by the bake-off (BENCHMARKING.md): int8 at 256 dims is\n// quality-free vs f32-512 when fused. Queries stay f32 at the same dims.\nconst STORE_DIMS = 256;\n// Seed chunks that participate in a `related` scan; see similarNotes for the measurement.\nconst TARGET_CHUNK_CAP = 16;\n\n// Slice + re-normalize (Matryoshka); optionally round through int8 storage. Exported for\n// benchmark/lib/embed.mjs, which scores the same lever math offline.\nexport function toStore(full: Float32Array, dims: number, int8: boolean): { v: Float32Array; scale: number } {\n const v = new Float32Array(dims);\n let norm = 0;\n for (let d = 0; d < dims; d++) norm += full[d] * full[d];\n norm = Math.sqrt(norm) + 1e-32;\n for (let d = 0; d < dims; d++) v[d] = full[d] / norm;\n if (!int8) return { v, scale: 1 };\n let max = 0;\n for (let d = 0; d < dims; d++) max = Math.max(max, Math.abs(v[d]));\n return { v, scale: max / 127 || 1 };\n}\n\n// Embed rows whose vector is NULL, re-deriving chunk text from the files through the\n// same parse + chunker that stored the rows.\nexport async function embedPending(db: DatabaseSync, cfg: Config, baseDir: string): Promise<void> {\n const provider = await getProvider(cfg); // throws EMBED_DISABLED before touching the table\n const dirty = db.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk').all() as Array<{ path: string; chunk: number }>;\n if (dirty.length === 0) return;\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n\n const byPath = new Map<string, number[]>();\n for (const row of dirty) {\n const list = byPath.get(row.path) ?? [];\n list.push(row.chunk);\n byPath.set(row.path, list);\n }\n\n const jobs: Array<{ path: string; chunk: number; text: string }> = [];\n for (const [path, chunkIdxs] of byPath) {\n let chunks: Chunk[];\n try {\n // presets/embed are irrelevant here -- re-deriving chunk text for a doc that already\n // has embeddings rows means the tree had an embedding model at reconcile time.\n chunks = parseFile({ relPath: path, absPath: join(baseDir, path), mtimeMs: 0, ctimeMs: 0, size: 0, presets: [], embed: true }, [embed]).doc.extracted.embed as Chunk[];\n } catch {\n continue; // vanished since reconcile; the next reconcile removes its rows\n }\n for (const idx of chunkIdxs) if (chunks[idx]) jobs.push({ path, chunk: idx, text: chunks[idx].text });\n }\n\n // Over the exact texts about to be embedded, before any of them are: a mismatch fails\n // this run loudly instead of quietly storing vectors from the wrong model.\n await checkLanguageFit(\n db,\n provider,\n jobs.map((j) => j.text)\n );\n\n const update = db.prepare('UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?');\n // The lazy build is the one long silence a first search hits (measured 23s at\n // 26k notes); progress makes it distinguishable from a hang.\n const report = progress('embedding chunks', jobs.length);\n for (let i = 0; i < jobs.length; i += provider.batchCap) {\n const batch = jobs.slice(i, i + provider.batchCap);\n const vectors = await provider.embedDocuments(batch.map((j) => j.text));\n db.exec('BEGIN');\n try {\n batch.forEach((job, j) => {\n const { v, scale } = toStore(vectors[j], storeDims, true);\n const q = new Int8Array(storeDims);\n for (let d = 0; d < storeDims; d++) q[d] = Math.round(v[d] / scale);\n update.run(scale, Buffer.from(q.buffer), job.path, job.chunk);\n });\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n report.tick(Math.min(i + provider.batchCap, jobs.length));\n }\n report.finish();\n}\n\n// Dequantised int8 dot products land a little either side of a true cosine, so an identical\n// pair prints 1.001 and undermines a column whose whole job is being a bounded number.\nfunction asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\n// Best chunk per file by cosine, its line range riding along; FTS5 operators are stripped as\n// lexical syntax. Similarity comes back because the fused score cannot express match quality,\n// and nearest-neighbour search always returns a neighbour however far away.\nexport async function semanticCandidates(db: DatabaseSync, cfg: Config, terms: string, fetch: number, allowed?: Set<string>): Promise<Array<{ path: string; lines: string; similarity: number }>> {\n const baseDir = (cfg as Partial<ResolvedConfig>).baseDir;\n if (!baseDir) throw new SenseError('EMBED_MODEL', 'semantic expansion needs a config with baseDir (use loadConfig/open)');\n await embedPending(db, cfg, baseDir);\n\n const provider = await getProvider(cfg);\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n const text = (terms.match(/[\\p{L}\\p{N}]+/gu) ?? []).filter((t) => !['AND', 'OR', 'NOT', 'NEAR'].includes(t)).join(' ');\n const { v: qv } = toStore(await provider.embedQuery(text), storeDims, false);\n\n const rows = db.prepare('SELECT \"path\", start_line, end_line, scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{\n path: string;\n start_line: number;\n end_line: number;\n scale: number;\n vector: Uint8Array;\n }>;\n\n const best = new Map<string, { score: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n let dot = 0;\n for (let d = 0; d < q.length; d++) dot += q[d] * qv[d];\n const score = dot * row.scale;\n const existing = best.get(row.path);\n if (!existing || score > existing.score) best.set(row.path, { score, lines: `L${row.start_line}-${row.end_line}` });\n }\n return [...best.entries()]\n .sort((a, b) => b[1].score - a[1].score)\n .slice(0, fetch)\n .map(([path, b]) => ({ path, lines: b.lines, similarity: asCosine(b.score) }));\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport function hasEmbedding(db: DatabaseSync, path: string): boolean {\n const row = db.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1').get(path) as { ok: number } | undefined;\n return row !== undefined;\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors. Reads only what is stored, so it stays sync.\nexport function similarNotes(db: DatabaseSync, _cfg: Config, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Array<{ path: string; similarity: number }> {\n // Cost is target_chunks x stored_chunks, so a heading-dense seed multiplies a full-corpus\n // scan (12.7s at 201 chunks/note). Sample evenly, so late sections still get a vote.\n const targetRows = db.prepare('SELECT scale, vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk').all(path) as Array<{ scale: number; vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n const step = Math.max(1, Math.ceil(targetRows.length / TARGET_CHUNK_CAP));\n const target = targetRows.filter((_, i) => i % step === 0).map((row) => ({ v: new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength), scale: row.scale }));\n\n const rows = db.prepare('SELECT \"path\", scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{ path: string; scale: number; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = dot * t.scale * row.scale;\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, opts.k)\n .map(([p, score]) => ({ path: p, similarity: asCosine(score) }));\n}\n"],"names":["embedPending","hasEmbedding","semanticCandidates","similarNotes","toStore","STORE_DIMS","TARGET_CHUNK_CAP","full","dims","int8","v","Float32Array","norm","d","Math","sqrt","scale","max","abs","db","cfg","baseDir","provider","dirty","storeDims","byPath","row","list","jobs","path","chunkIdxs","chunks","idx","update","report","i","batch","vectors","slice","batchCap","embedDocuments","map","j","text","exec","forEach","job","q","Int8Array","round","run","Buffer","from","buffer","chunk","err","tick","min","length","getProvider","prepare","all","Map","get","push","set","parseFile","relPath","absPath","join","mtimeMs","ctimeMs","size","presets","embed","doc","extracted","checkLanguageFit","progress","finish","asCosine","score","terms","fetch","allowed","qv","rows","best","dot","existing","SenseError","match","filter","t","includes","embedQuery","has","vector","byteOffset","byteLength","lines","start_line","end_line","entries","sort","a","b","similarity","undefined","_cfg","opts","targetRows","step","ceil","target","_","exclude","other","len","k","p"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/query.ts"],"sourcesContent":["import { join } from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Chunk } from '../features/embed.ts';\nimport { embed } from '../features/embed.ts';\nimport { progress } from '../output/progress.ts';\nimport { parseFile } from '../scan/index.ts';\nimport { checkLanguageFit } from './langfit.ts';\nimport { getProvider } from './registry.ts';\n\n// Storage lever fixed by the bake-off (benchmark/reports/2026-08-13-static-model-bakeoff.md):\n// int8 at 256 dims is quality-free vs f32-512 when fused. Queries stay f32 at the same dims.\nconst STORE_DIMS = 256;\n// Seed chunks that participate in a `related` scan; see similarNotes for the measurement.\nconst TARGET_CHUNK_CAP = 16;\n\n// Slice + re-normalize (Matryoshka); optionally round through int8 storage. Exported for\n// benchmark/lib/embed.mjs, which scores the same lever math offline.\nexport function toStore(full: Float32Array, dims: number, int8: boolean): { v: Float32Array; scale: number } {\n const v = new Float32Array(dims);\n let norm = 0;\n for (let d = 0; d < dims; d++) norm += full[d] * full[d];\n norm = Math.sqrt(norm) + 1e-32;\n for (let d = 0; d < dims; d++) v[d] = full[d] / norm;\n if (!int8) return { v, scale: 1 };\n let max = 0;\n for (let d = 0; d < dims; d++) max = Math.max(max, Math.abs(v[d]));\n return { v, scale: max / 127 || 1 };\n}\n\n// Embed rows whose vector is NULL, re-deriving chunk text from the files through the\n// same parse + chunker that stored the rows.\nexport async function embedPending(db: DatabaseSync, cfg: Config, baseDir: string): Promise<void> {\n const provider = await getProvider(cfg); // throws EMBED_DISABLED before touching the table\n const dirty = db.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk').all() as Array<{ path: string; chunk: number }>;\n if (dirty.length === 0) return;\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n\n const byPath = new Map<string, number[]>();\n for (const row of dirty) {\n const list = byPath.get(row.path) ?? [];\n list.push(row.chunk);\n byPath.set(row.path, list);\n }\n\n const jobs: Array<{ path: string; chunk: number; text: string }> = [];\n for (const [path, chunkIdxs] of byPath) {\n let chunks: Chunk[];\n try {\n // presets/embed are irrelevant here -- re-deriving chunk text for a doc that already\n // has embeddings rows means the tree had an embedding model at reconcile time.\n chunks = parseFile({ relPath: path, absPath: join(baseDir, path), mtimeMs: 0, ctimeMs: 0, size: 0, presets: [], embed: true }, [embed], cfg).doc.extracted.embed as Chunk[];\n } catch {\n continue; // vanished since reconcile; the next reconcile removes its rows\n }\n for (const idx of chunkIdxs) if (chunks[idx]) jobs.push({ path, chunk: idx, text: chunks[idx].text });\n }\n\n // Over the exact texts about to be embedded, before any of them are: a mismatch fails\n // this run loudly instead of quietly storing vectors from the wrong model.\n await checkLanguageFit(\n db,\n provider,\n jobs.map((j) => j.text)\n );\n\n const update = db.prepare('UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?');\n // The lazy build is the one long silence a first search hits (measured 23s at\n // 26k notes); progress makes it distinguishable from a hang.\n const report = progress('embedding chunks', jobs.length);\n for (let i = 0; i < jobs.length; i += provider.batchCap) {\n const batch = jobs.slice(i, i + provider.batchCap);\n const vectors = await provider.embedDocuments(batch.map((j) => j.text));\n db.exec('BEGIN');\n try {\n batch.forEach((job, j) => {\n const { v, scale } = toStore(vectors[j], storeDims, true);\n const q = new Int8Array(storeDims);\n for (let d = 0; d < storeDims; d++) q[d] = Math.round(v[d] / scale);\n update.run(scale, Buffer.from(q.buffer), job.path, job.chunk);\n });\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n report.tick(Math.min(i + provider.batchCap, jobs.length));\n }\n report.finish();\n}\n\n// Dequantised int8 dot products land a little either side of a true cosine, so an identical\n// pair prints 1.001 and undermines a column whose whole job is being a bounded number.\nfunction asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\n// Best chunk per file by cosine, its line range riding along; FTS5 operators are stripped as\n// lexical syntax. Similarity comes back because the fused score cannot express match quality,\n// and nearest-neighbour search always returns a neighbour however far away.\nexport async function semanticCandidates(db: DatabaseSync, cfg: Config, terms: string, fetch: number, allowed?: Set<string>): Promise<Array<{ path: string; lines: string; similarity: number }>> {\n const baseDir = (cfg as Partial<ResolvedConfig>).baseDir;\n if (!baseDir) throw new SenseError('EMBED_MODEL', 'semantic expansion needs a config with baseDir (use loadConfig/open)');\n await embedPending(db, cfg, baseDir);\n\n const provider = await getProvider(cfg);\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n const text = (terms.match(/[\\p{L}\\p{N}]+/gu) ?? []).filter((t) => !['AND', 'OR', 'NOT', 'NEAR'].includes(t)).join(' ');\n const { v: qv } = toStore(await provider.embedQuery(text), storeDims, false);\n\n const rows = db.prepare('SELECT \"path\", start_line, end_line, scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{\n path: string;\n start_line: number;\n end_line: number;\n scale: number;\n vector: Uint8Array;\n }>;\n\n const best = new Map<string, { score: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n let dot = 0;\n for (let d = 0; d < q.length; d++) dot += q[d] * qv[d];\n const score = dot * row.scale;\n const existing = best.get(row.path);\n if (!existing || score > existing.score) best.set(row.path, { score, lines: `L${row.start_line}-${row.end_line}` });\n }\n return [...best.entries()]\n .sort((a, b) => b[1].score - a[1].score)\n .slice(0, fetch)\n .map(([path, b]) => ({ path, lines: b.lines, similarity: asCosine(b.score) }));\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport function hasEmbedding(db: DatabaseSync, path: string): boolean {\n const row = db.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1').get(path) as { ok: number } | undefined;\n return row !== undefined;\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors. Reads only what is stored, so it stays sync.\nexport function similarNotes(db: DatabaseSync, _cfg: Config, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Array<{ path: string; similarity: number }> {\n // Cost is target_chunks x stored_chunks, so a heading-dense seed multiplies a full-corpus\n // scan (12.7s at 201 chunks/note). Sample evenly, so late sections still get a vote.\n const targetRows = db.prepare('SELECT scale, vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk').all(path) as Array<{ scale: number; vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n const step = Math.max(1, Math.ceil(targetRows.length / TARGET_CHUNK_CAP));\n const target = targetRows.filter((_, i) => i % step === 0).map((row) => ({ v: new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength), scale: row.scale }));\n\n const rows = db.prepare('SELECT \"path\", scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{ path: string; scale: number; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = dot * t.scale * row.scale;\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, opts.k)\n .map(([p, score]) => ({ path: p, similarity: asCosine(score) }));\n}\n"],"names":["embedPending","hasEmbedding","semanticCandidates","similarNotes","toStore","STORE_DIMS","TARGET_CHUNK_CAP","full","dims","int8","v","Float32Array","norm","d","Math","sqrt","scale","max","abs","db","cfg","baseDir","provider","dirty","storeDims","byPath","row","list","jobs","path","chunkIdxs","chunks","idx","update","report","i","batch","vectors","slice","batchCap","embedDocuments","map","j","text","exec","forEach","job","q","Int8Array","round","run","Buffer","from","buffer","chunk","err","tick","min","length","getProvider","prepare","all","Map","get","push","set","parseFile","relPath","absPath","join","mtimeMs","ctimeMs","size","presets","embed","doc","extracted","checkLanguageFit","progress","finish","asCosine","score","terms","fetch","allowed","qv","rows","best","dot","existing","SenseError","match","filter","t","includes","embedQuery","has","vector","byteOffset","byteLength","lines","start_line","end_line","entries","sort","a","b","similarity","undefined","_cfg","opts","targetRows","step","ceil","target","_","exclude","other","len","k","p"],"mappings":"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"}
|
package/dist/cjs/embed/static.js
CHANGED
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "staticProvider", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
var _nodefs = require("node:fs");
|
|
12
|
+
var _nodemodule = /*#__PURE__*/ _interop_require_default(require("node:module"));
|
|
12
13
|
var _nodepath = require("node:path");
|
|
13
14
|
var _errorsts = require("../errors.js");
|
|
14
15
|
var _storets = require("./store.js");
|
|
@@ -46,35 +47,10 @@ function _async_to_generator(fn) {
|
|
|
46
47
|
});
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
var cacheBabelInterop = new WeakMap();
|
|
52
|
-
var cacheNodeInterop = new WeakMap();
|
|
53
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
54
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
55
|
-
})(nodeInterop);
|
|
56
|
-
}
|
|
57
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
58
|
-
if (!nodeInterop && obj && obj.__esModule) return obj;
|
|
59
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") return {
|
|
50
|
+
function _interop_require_default(obj) {
|
|
51
|
+
return obj && obj.__esModule ? obj : {
|
|
60
52
|
default: obj
|
|
61
53
|
};
|
|
62
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
63
|
-
if (cache && cache.has(obj)) return cache.get(obj);
|
|
64
|
-
var newObj = {
|
|
65
|
-
__proto__: null
|
|
66
|
-
};
|
|
67
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
68
|
-
for(var key in obj){
|
|
69
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
70
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
71
|
-
if (desc && (desc.get || desc.set)) Object.defineProperty(newObj, key, desc);
|
|
72
|
-
else newObj[key] = obj[key];
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
newObj.default = obj;
|
|
76
|
-
if (cache) cache.set(obj, newObj);
|
|
77
|
-
return newObj;
|
|
78
54
|
}
|
|
79
55
|
function _iterable_to_array_limit(arr, i) {
|
|
80
56
|
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
@@ -214,6 +190,10 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
214
190
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
215
191
|
}
|
|
216
192
|
var BATCH_CAP = 64;
|
|
193
|
+
// @huggingface/tokenizers is a 540K static import; a top-level import put it on every command's
|
|
194
|
+
// module graph regardless of whether the semantic path ran, shipped as the 0.17.0 regression.
|
|
195
|
+
// Deferred to a synchronous require inside staticProvider so only the embed path pays for it.
|
|
196
|
+
var _require = typeof require === 'undefined' ? _nodemodule.default.createRequire(require("url").pathToFileURL(__filename).toString()) : require;
|
|
217
197
|
function staticProvider(model) {
|
|
218
198
|
return _async_to_generator(function() {
|
|
219
199
|
var _ref, _tokenizerJson_model, _tokenizerJson_model_vocab, _tokenizerJson_model1, dir, languages, cached, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, file, raw, headerLen, header, entry, spec, dims, dataStart, matrix, tokenizerJson, Tokenizer, tok, unkId;
|
|
@@ -320,14 +300,7 @@ function staticProvider(model) {
|
|
|
320
300
|
dataStart = raw.byteOffset + 8 + headerLen + spec.data_offsets[0];
|
|
321
301
|
matrix = dataStart % 4 === 0 ? new Float32Array(raw.buffer, dataStart, spec.shape[0] * dims) : new Float32Array(raw.buffer.slice(dataStart, dataStart + spec.shape[0] * dims * 4));
|
|
322
302
|
tokenizerJson = JSON.parse((0, _nodefs.readFileSync)((0, _nodepath.join)(dir, 'tokenizer.json'), 'utf8'));
|
|
323
|
-
|
|
324
|
-
4,
|
|
325
|
-
Promise.resolve().then(function() {
|
|
326
|
-
return /*#__PURE__*/ _interop_require_wildcard(require("@huggingface/tokenizers"));
|
|
327
|
-
})
|
|
328
|
-
];
|
|
329
|
-
case 4:
|
|
330
|
-
Tokenizer = _state.sent().Tokenizer;
|
|
303
|
+
Tokenizer = _require('@huggingface/tokenizers').Tokenizer;
|
|
331
304
|
tok = new Tokenizer(tokenizerJson, {});
|
|
332
305
|
unkId = (_ref = (_tokenizerJson_model1 = tokenizerJson.model) === null || _tokenizerJson_model1 === void 0 ? void 0 : (_tokenizerJson_model_vocab = _tokenizerJson_model1.vocab) === null || _tokenizerJson_model_vocab === void 0 ? void 0 : _tokenizerJson_model_vocab[(_tokenizerJson_model = tokenizerJson.model) === null || _tokenizerJson_model === void 0 ? void 0 : _tokenizerJson_model.unk_token]) !== null && _ref !== void 0 ? _ref : -1;
|
|
333
306
|
// Symmetric model: document and query embedding are the same call.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/static.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { SenseError } from '../errors.ts';\nimport { downloadModel, isDownloadable, MODEL_FILENAMES, MODEL_FILES, modelDir, readLanguages } from './store.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 64;\n\n// Model2Vec safetensors + pure-JS tokenizer. Encode convention from model2vec/model.py: no\n// special tokens, drop unk ids, mean-pool, L2-normalize.\nexport async function staticProvider(model: string): Promise<EmbedProvider> {\n let dir: string;\n let languages: string[] | undefined;\n if (isDownloadable(model)) {\n // Naming a HF id is consent: fetch whatever is missing now, announcing it on stderr.\n dir = await downloadModel(model, (file, into) => console.error(`sense: fetching ${model}/${file} into ${into}`));\n // Cached beside the repo; a local model directory has no card to read, so it has none.\n const cached = readLanguages(model);\n languages = cached && cached.length > 0 ? cached : undefined;\n } else {\n dir = modelDir(model);\n for (const file of MODEL_FILES) {\n if (!existsSync(join(dir, file))) {\n throw new SenseError('EMBED_MODEL_MISSING', `embed model ${model} is not available (looked in ${dir}); expected ${MODEL_FILENAMES} in that directory`);\n }\n }\n }\n\n const raw = readFileSync(join(dir, 'model.safetensors'));\n const headerLen = Number(raw.readBigUInt64LE(0));\n const header = JSON.parse(raw.subarray(8, 8 + headerLen).toString('utf8')) as Record<string, { dtype: string; shape: number[]; data_offsets: number[] }>;\n const entry = Object.entries(header).find(([k]) => k !== '__metadata__');\n if (!entry || entry[1].dtype !== 'F32') throw new SenseError('EMBED_MODEL', `${model}: expected an F32 safetensors matrix`);\n const spec = entry[1];\n const dims = spec.shape[1];\n const dataStart = raw.byteOffset + 8 + headerLen + spec.data_offsets[0];\n const matrix = dataStart % 4 === 0 ? new Float32Array(raw.buffer, dataStart, spec.shape[0] * dims) : new Float32Array(raw.buffer.slice(dataStart, dataStart + spec.shape[0] * dims * 4));\n\n const tokenizerJson = JSON.parse(readFileSync(join(dir, 'tokenizer.json'), 'utf8'));\n
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/static.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport Module from 'node:module';\nimport { join } from 'node:path';\nimport { SenseError } from '../errors.ts';\nimport { downloadModel, isDownloadable, MODEL_FILENAMES, MODEL_FILES, modelDir, readLanguages } from './store.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 64;\n\n// @huggingface/tokenizers is a 540K static import; a top-level import put it on every command's\n// module graph regardless of whether the semantic path ran, shipped as the 0.17.0 regression.\n// Deferred to a synchronous require inside staticProvider so only the embed path pays for it.\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Model2Vec safetensors + pure-JS tokenizer. Encode convention from model2vec/model.py: no\n// special tokens, drop unk ids, mean-pool, L2-normalize.\nexport async function staticProvider(model: string): Promise<EmbedProvider> {\n let dir: string;\n let languages: string[] | undefined;\n if (isDownloadable(model)) {\n // Naming a HF id is consent: fetch whatever is missing now, announcing it on stderr.\n dir = await downloadModel(model, (file, into) => console.error(`sense: fetching ${model}/${file} into ${into}`));\n // Cached beside the repo; a local model directory has no card to read, so it has none.\n const cached = readLanguages(model);\n languages = cached && cached.length > 0 ? cached : undefined;\n } else {\n dir = modelDir(model);\n for (const file of MODEL_FILES) {\n if (!existsSync(join(dir, file))) {\n throw new SenseError('EMBED_MODEL_MISSING', `embed model ${model} is not available (looked in ${dir}); expected ${MODEL_FILENAMES} in that directory`);\n }\n }\n }\n\n const raw = readFileSync(join(dir, 'model.safetensors'));\n const headerLen = Number(raw.readBigUInt64LE(0));\n const header = JSON.parse(raw.subarray(8, 8 + headerLen).toString('utf8')) as Record<string, { dtype: string; shape: number[]; data_offsets: number[] }>;\n const entry = Object.entries(header).find(([k]) => k !== '__metadata__');\n if (!entry || entry[1].dtype !== 'F32') throw new SenseError('EMBED_MODEL', `${model}: expected an F32 safetensors matrix`);\n const spec = entry[1];\n const dims = spec.shape[1];\n const dataStart = raw.byteOffset + 8 + headerLen + spec.data_offsets[0];\n const matrix = dataStart % 4 === 0 ? new Float32Array(raw.buffer, dataStart, spec.shape[0] * dims) : new Float32Array(raw.buffer.slice(dataStart, dataStart + spec.shape[0] * dims * 4));\n\n const tokenizerJson = JSON.parse(readFileSync(join(dir, 'tokenizer.json'), 'utf8'));\n const { Tokenizer } = _require('@huggingface/tokenizers') as typeof import('@huggingface/tokenizers');\n const tok = new Tokenizer(tokenizerJson, {});\n const unkId = tokenizerJson.model?.vocab?.[tokenizerJson.model?.unk_token] ?? -1;\n\n function one(text: string): Float32Array {\n // The tokenizer yields undefined (not the unk id) for tokens outside the vocab; an\n // undefined id would index the matrix at NaN and poison the whole mean-pool -- and the\n // int8 conversion then stores the NaN vector as all zeros, silently. Keep integers only.\n const ids = (tok.encode(text, { add_special_tokens: false }).ids as number[]).filter((id) => Number.isInteger(id) && id !== unkId);\n const v = new Float32Array(dims);\n if (ids.length === 0) return v;\n for (const id of ids) {\n const off = id * dims;\n for (let d = 0; d < dims; d++) v[d] += matrix[off + d];\n }\n let norm = 0;\n for (let d = 0; d < dims; d++) {\n v[d] /= ids.length;\n norm += v[d] * v[d];\n }\n norm = Math.sqrt(norm) + 1e-32;\n for (let d = 0; d < dims; d++) v[d] /= norm;\n return v;\n }\n\n // Symmetric model: document and query embedding are the same call.\n return { id: `static:${model}`, dims, batchCap: BATCH_CAP, languages, embedDocuments: async (texts) => texts.map(one), embedQuery: async (text) => one(text) };\n}\n"],"names":["staticProvider","BATCH_CAP","_require","require","Module","createRequire","model","tokenizerJson","dir","languages","cached","file","raw","headerLen","header","entry","spec","dims","dataStart","matrix","Tokenizer","tok","unkId","one","text","ids","encode","add_special_tokens","filter","id","Number","isInteger","v","Float32Array","length","off","d","norm","Math","sqrt","isDownloadable","downloadModel","into","console","error","readLanguages","undefined","modelDir","MODEL_FILES","existsSync","join","SenseError","MODEL_FILENAMES","readFileSync","readBigUInt64LE","JSON","parse","subarray","toString","Object","entries","find","k","dtype","shape","byteOffset","data_offsets","buffer","slice","vocab","unk_token","batchCap","embedDocuments","texts","map","embedQuery"],"mappings":"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"}
|
|
@@ -8,82 +8,46 @@ Object.defineProperty(exports, "embed", {
|
|
|
8
8
|
return embed;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
function _array_without_holes(arr) {
|
|
18
|
-
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
19
|
-
}
|
|
20
|
-
function _iterable_to_array(iter) {
|
|
21
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
|
|
22
|
-
return Array.from(iter);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function _non_iterable_spread() {
|
|
26
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
27
|
-
}
|
|
28
|
-
function _to_consumable_array(arr) {
|
|
29
|
-
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
30
|
-
}
|
|
31
|
-
function _unsupported_iterable_to_array(o, minLen) {
|
|
32
|
-
if (!o) return;
|
|
33
|
-
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
34
|
-
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
35
|
-
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
36
|
-
if (n === "Map" || n === "Set") return Array.from(n);
|
|
37
|
-
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
38
|
-
}
|
|
39
|
-
// Deterministic, so embed time can re-derive text from stored line ranges. Heading-delimited,
|
|
40
|
-
// preamble kept, whole body when no headings; the title/summary prefix mirrors bm25 weighting.
|
|
11
|
+
var _indexts = require("../chunk/index.js");
|
|
12
|
+
var _indexts1 = require("../config/index.js");
|
|
13
|
+
var _identityts = require("../embed/identity.js");
|
|
14
|
+
// Deterministic, so embed time can re-derive text from stored line ranges; the title/summary
|
|
15
|
+
// prefix mirrors bm25 field weighting, and an empty chunk is dropped before the prefix is added.
|
|
41
16
|
//
|
|
42
17
|
// Chunks the body, not the raw file, with `offset` shifting line numbers back onto the raw
|
|
43
18
|
// file so a range stays a direct Read range (sections is 1-indexed over raw too). Chunking raw
|
|
44
19
|
// put the frontmatter block in its own leading chunk on every note with a heading, which made
|
|
45
20
|
// `lines` point at YAML and made frontmatter-only notes near-identical to each other. It also
|
|
46
21
|
// disagreed with FTS, which indexes the body alone.
|
|
47
|
-
function chunksOf(body, search) {
|
|
48
|
-
var offset = arguments.length >
|
|
49
|
-
var lines = body.split('\n');
|
|
50
|
-
var starts = [];
|
|
51
|
-
var fence = (0, _fencests.fenceTracker)();
|
|
52
|
-
for(var i = 0; i < lines.length; i++){
|
|
53
|
-
if (fence.feed(lines[i])) continue;
|
|
54
|
-
if (!fence.inFence && /^#{1,6} +/.test(lines[i])) starts.push(i + 1);
|
|
55
|
-
}
|
|
56
|
-
var bounds = starts.length === 0 ? [
|
|
57
|
-
1
|
|
58
|
-
] : starts[0] > 1 ? [
|
|
59
|
-
1
|
|
60
|
-
].concat(_to_consumable_array(starts)) : starts;
|
|
22
|
+
function chunksOf(blocks, body, search) {
|
|
23
|
+
var offset = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 0, chunkTokens = arguments.length > 4 ? arguments[4] : void 0;
|
|
61
24
|
var prefix = [
|
|
62
25
|
search === null || search === void 0 ? void 0 : search.title,
|
|
63
26
|
search === null || search === void 0 ? void 0 : search.summary
|
|
64
27
|
].filter(Boolean).join('\n');
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
startLine:
|
|
73
|
-
endLine:
|
|
74
|
-
text: prefix ? "".concat(prefix, "\n").concat(text) : text
|
|
75
|
-
}
|
|
28
|
+
// chunk()'s default text mode is 'raw' (D9), the shipped choice. A hardcoded option change
|
|
29
|
+
// here must bump src/chunk/version.ts's CHUNK_VERSION -- the signature for every chunker
|
|
30
|
+
// input not already carried in config.
|
|
31
|
+
return (0, _indexts.chunkFromBlocks)(blocks, body, chunkTokens !== undefined ? {
|
|
32
|
+
targetTokens: chunkTokens
|
|
33
|
+
} : undefined).map(function(c) {
|
|
34
|
+
return {
|
|
35
|
+
startLine: c.startLine + offset,
|
|
36
|
+
endLine: c.endLine + offset,
|
|
37
|
+
text: prefix ? "".concat(prefix, "\n").concat(c.text) : c.text
|
|
38
|
+
};
|
|
76
39
|
});
|
|
77
|
-
return chunks;
|
|
78
40
|
}
|
|
79
41
|
var embed = {
|
|
80
42
|
name: 'embed',
|
|
81
43
|
schema: function schema(db) {
|
|
82
44
|
db.exec('CREATE TABLE IF NOT EXISTS embeddings ("path" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector BLOB, PRIMARY KEY ("path", chunk))');
|
|
83
45
|
},
|
|
84
|
-
extract: function extract(raw, body, search) {
|
|
46
|
+
extract: function extract(raw, body, search, _data, cfg, blocks) {
|
|
47
|
+
var _cfg_embed;
|
|
85
48
|
// Lines the frontmatter occupies, so body line 1 maps back to its raw line number.
|
|
86
|
-
|
|
49
|
+
// blocks comes from parseFile's shared parse; falls back to parsing body for a direct call.
|
|
50
|
+
return chunksOf(blocks !== null && blocks !== void 0 ? blocks : (0, _indexts.parse)(body), body, search, raw.split('\n').length - body.split('\n').length, cfg === null || cfg === void 0 ? void 0 : (_cfg_embed = cfg.embed) === null || _cfg_embed === void 0 ? void 0 : _cfg_embed.chunkTokens);
|
|
87
51
|
},
|
|
88
52
|
remove: function remove(db, path) {
|
|
89
53
|
db.prepare('DELETE FROM embeddings WHERE "path" = ?').run(path);
|
|
@@ -99,6 +63,20 @@ var embed = {
|
|
|
99
63
|
},
|
|
100
64
|
enabledForFile: function enabledForFile(_cfg, file) {
|
|
101
65
|
return file.embed;
|
|
66
|
+
},
|
|
67
|
+
// Provider/model/chunk-version/identity, keyed so a rebuild notice can name it "embed
|
|
68
|
+
// settings". Driven by embedConfig(cfg) directly (a model can be configured with no preset
|
|
69
|
+
// yet using vectors), not by whether this feature is itself active.
|
|
70
|
+
signature: function signature(cfg) {
|
|
71
|
+
var e = (0, _indexts1.embedConfig)(cfg);
|
|
72
|
+
if (!e) return 'embed:off';
|
|
73
|
+
// Weight identity from identity.ts, no network: a static model's resolved sha or local
|
|
74
|
+
// size+mtime, appended once known so changed weights re-embed.
|
|
75
|
+
var identity = e.provider === 'static' ? (0, _identityts.modelIdentity)(e.model) : undefined;
|
|
76
|
+
// W3b: chunkTokens rides the version token, since it's the one owner lever the chunker
|
|
77
|
+
// itself takes -- changing or clearing it must rebuild exactly like a chunker version bump.
|
|
78
|
+
var chunkVersion = e.chunkTokens !== undefined ? "".concat(_indexts.CHUNK_VERSION, ":").concat(e.chunkTokens) : _indexts.CHUNK_VERSION;
|
|
79
|
+
return "embed:".concat(e.provider, ":").concat(e.model, ":").concat(chunkVersion).concat(identity !== undefined ? "@".concat(identity) : '');
|
|
102
80
|
}
|
|
103
81
|
};
|
|
104
82
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/embed.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/embed.ts"],"sourcesContent":["import type { Block, Chunk } from '../chunk/index.ts';\nimport { CHUNK_VERSION, chunkFromBlocks, parse } from '../chunk/index.ts';\nimport { embedConfig } from '../config/index.ts';\nimport { modelIdentity } from '../embed/identity.ts';\nimport type { Feature } from './types.ts';\n\n// int8 vectors with a per-vector scale, NULL vector = not yet embedded. Reconcile writes dirty\n// rows; embedding tops up on the next search, so staleness costs recall. Providers, the vector\n// fill, and the search-time helpers live under src/embed/. Block/group structure (D1-D5) lives\n// in src/chunk; this keeps only the title/summary prefix and the empty-chunk drop (D6).\n\nexport type { Chunk };\n\n// Deterministic, so embed time can re-derive text from stored line ranges; the title/summary\n// prefix mirrors bm25 field weighting, and an empty chunk is dropped before the prefix is added.\n//\n// Chunks the body, not the raw file, with `offset` shifting line numbers back onto the raw\n// file so a range stays a direct Read range (sections is 1-indexed over raw too). Chunking raw\n// put the frontmatter block in its own leading chunk on every note with a heading, which made\n// `lines` point at YAML and made frontmatter-only notes near-identical to each other. It also\n// disagreed with FTS, which indexes the body alone.\nfunction chunksOf(blocks: Block[], body: string, search?: { title: string; summary: string }, offset = 0, chunkTokens?: number): Chunk[] {\n const prefix = [search?.title, search?.summary].filter(Boolean).join('\\n');\n // chunk()'s default text mode is 'raw' (D9), the shipped choice. A hardcoded option change\n // here must bump src/chunk/version.ts's CHUNK_VERSION -- the signature for every chunker\n // input not already carried in config.\n return chunkFromBlocks(blocks, body, chunkTokens !== undefined ? { targetTokens: chunkTokens } : undefined).map((c) => ({\n startLine: c.startLine + offset,\n endLine: c.endLine + offset,\n text: prefix ? `${prefix}\\n${c.text}` : c.text,\n }));\n}\n\nexport const embed: Feature = {\n name: 'embed',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS embeddings (\"path\" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector BLOB, PRIMARY KEY (\"path\", chunk))`);\n },\n extract(raw, body, search, _data, cfg, blocks) {\n // Lines the frontmatter occupies, so body line 1 maps back to its raw line number.\n // blocks comes from parseFile's shared parse; falls back to parsing body for a direct call.\n return chunksOf(blocks ?? parse(body), body, search, raw.split('\\n').length - body.split('\\n').length, cfg?.embed?.chunkTokens);\n },\n remove(db, path) {\n db.prepare('DELETE FROM embeddings WHERE \"path\" = ?').run(path);\n },\n // A tree with no embedding model never had extract() run for the doc (db.ts's per-file\n // filter skips it), so extracted is undefined here -- store nothing, i.e. no rows.\n store(db, path, extracted) {\n if (!extracted) return;\n const insert = db.prepare('INSERT INTO embeddings (\"path\", chunk, start_line, end_line, scale, vector) VALUES (?, ?, ?, ?, NULL, NULL)');\n (extracted as Chunk[]).forEach((c, idx) => insert.run(path, idx, c.startLine, c.endLine));\n },\n enabledForFile(_cfg, file) {\n return file.embed;\n },\n // Provider/model/chunk-version/identity, keyed so a rebuild notice can name it \"embed\n // settings\". Driven by embedConfig(cfg) directly (a model can be configured with no preset\n // yet using vectors), not by whether this feature is itself active.\n signature(cfg) {\n const e = embedConfig(cfg);\n if (!e) return 'embed:off';\n // Weight identity from identity.ts, no network: a static model's resolved sha or local\n // size+mtime, appended once known so changed weights re-embed.\n const identity = e.provider === 'static' ? modelIdentity(e.model) : undefined;\n // W3b: chunkTokens rides the version token, since it's the one owner lever the chunker\n // itself takes -- changing or clearing it must rebuild exactly like a chunker version bump.\n const chunkVersion = e.chunkTokens !== undefined ? `${CHUNK_VERSION}:${e.chunkTokens}` : CHUNK_VERSION;\n return `embed:${e.provider}:${e.model}:${chunkVersion}${identity !== undefined ? `@${identity}` : ''}`;\n },\n};\n"],"names":["embed","chunksOf","blocks","body","search","offset","chunkTokens","prefix","title","summary","filter","Boolean","join","chunkFromBlocks","undefined","targetTokens","map","c","startLine","endLine","text","name","schema","db","exec","extract","raw","_data","cfg","parse","split","length","remove","path","prepare","run","store","extracted","insert","forEach","idx","enabledForFile","_cfg","file","signature","e","embedConfig","identity","provider","modelIdentity","model","chunkVersion","CHUNK_VERSION"],"mappings":";;;;+BAiCaA;;;eAAAA;;;uBAhCyC;wBAC1B;0BACE;AAU9B,6FAA6F;AAC7F,iGAAiG;AACjG,EAAE;AACF,2FAA2F;AAC3F,+FAA+F;AAC/F,8FAA8F;AAC9F,8FAA8F;AAC9F,oDAAoD;AACpD,SAASC,SAASC,MAAe,EAAEC,IAAY,EAAEC,MAA2C;QAAEC,SAAAA,iEAAS,GAAGC;IACxG,IAAMC,SAAS;QAACH,mBAAAA,6BAAAA,OAAQI,KAAK;QAAEJ,mBAAAA,6BAAAA,OAAQK,OAAO;KAAC,CAACC,MAAM,CAACC,SAASC,IAAI,CAAC;IACrE,2FAA2F;IAC3F,yFAAyF;IACzF,uCAAuC;IACvC,OAAOC,IAAAA,wBAAe,EAACX,QAAQC,MAAMG,gBAAgBQ,YAAY;QAAEC,cAAcT;IAAY,IAAIQ,WAAWE,GAAG,CAAC,SAACC;eAAO;YACtHC,WAAWD,EAAEC,SAAS,GAAGb;YACzBc,SAASF,EAAEE,OAAO,GAAGd;YACrBe,MAAMb,SAAS,AAAC,GAAaU,OAAXV,QAAO,MAAW,OAAPU,EAAEG,IAAI,IAAKH,EAAEG,IAAI;QAChD;;AACF;AAEO,IAAMpB,QAAiB;IAC5BqB,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACPA,GAAGC,IAAI,CAAC;IACV;IACAC,SAAAA,SAAAA,QAAQC,GAAG,EAAEvB,IAAI,EAAEC,MAAM,EAAEuB,KAAK,EAAEC,GAAG,EAAE1B,MAAM;YAG4D0B;QAFvG,mFAAmF;QACnF,4FAA4F;QAC5F,OAAO3B,SAASC,mBAAAA,oBAAAA,SAAU2B,IAAAA,cAAK,EAAC1B,OAAOA,MAAMC,QAAQsB,IAAII,KAAK,CAAC,MAAMC,MAAM,GAAG5B,KAAK2B,KAAK,CAAC,MAAMC,MAAM,EAAEH,gBAAAA,2BAAAA,aAAAA,IAAK5B,KAAK,cAAV4B,iCAAAA,WAAYtB,WAAW;IAChI;IACA0B,QAAAA,SAAAA,OAAOT,EAAE,EAAEU,IAAI;QACbV,GAAGW,OAAO,CAAC,2CAA2CC,GAAG,CAACF;IAC5D;IACA,uFAAuF;IACvF,mFAAmF;IACnFG,OAAAA,SAAAA,MAAMb,EAAE,EAAEU,IAAI,EAAEI,SAAS;QACvB,IAAI,CAACA,WAAW;QAChB,IAAMC,SAASf,GAAGW,OAAO,CAAC;QACzBG,UAAsBE,OAAO,CAAC,SAACtB,GAAGuB;mBAAQF,OAAOH,GAAG,CAACF,MAAMO,KAAKvB,EAAEC,SAAS,EAAED,EAAEE,OAAO;;IACzF;IACAsB,gBAAAA,SAAAA,eAAeC,IAAI,EAAEC,IAAI;QACvB,OAAOA,KAAK3C,KAAK;IACnB;IACA,sFAAsF;IACtF,2FAA2F;IAC3F,oEAAoE;IACpE4C,WAAAA,SAAAA,UAAUhB,GAAG;QACX,IAAMiB,IAAIC,IAAAA,qBAAW,EAAClB;QACtB,IAAI,CAACiB,GAAG,OAAO;QACf,uFAAuF;QACvF,+DAA+D;QAC/D,IAAME,WAAWF,EAAEG,QAAQ,KAAK,WAAWC,IAAAA,yBAAa,EAACJ,EAAEK,KAAK,IAAIpC;QACpE,uFAAuF;QACvF,4FAA4F;QAC5F,IAAMqC,eAAeN,EAAEvC,WAAW,KAAKQ,YAAY,AAAC,GAAmB+B,OAAjBO,sBAAa,EAAC,KAAiB,OAAdP,EAAEvC,WAAW,IAAK8C,sBAAa;QACtG,OAAO,AAAC,SAAsBP,OAAdA,EAAEG,QAAQ,EAAC,KAAcG,OAAXN,EAAEK,KAAK,EAAC,KAAkBH,OAAfI,cAA4D,OAA7CJ,aAAajC,YAAY,AAAC,IAAY,OAATiC,YAAa;IACpG;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column
|
|
2
2
|
// 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see
|
|
3
|
-
// test/unit/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself
|
|
3
|
+
// test/unit/features/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself
|
|
4
4
|
// contain a backtick (spec rule). A closer is a run of the SAME character, length >= the
|
|
5
5
|
// opener's, holding nothing but trailing spaces after the run.
|
|
6
6
|
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/fences.ts"],"sourcesContent":["// Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column\n// 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see\n// test/unit/features/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself\n// contain a backtick (spec rule). A closer is a run of the SAME character, length >= the\n// opener's, holding nothing but trailing spaces after the run.\n\nexport interface FenceTracker {\n feed(line: string): boolean; // true iff this line is a fence delimiter (open or close)\n readonly inFence: boolean;\n}\n\nconst BACKTICK_OPEN = /^(`{3,})(.*)$/;\nconst TILDE_OPEN = /^(~{3,})(.*)$/;\n\nexport function fenceTracker(): FenceTracker {\n let inFence = false;\n let fenceChar = '';\n let fenceLen = 0;\n return {\n feed(line: string): boolean {\n if (!inFence) {\n const bt = BACKTICK_OPEN.exec(line);\n if (bt && !bt[2].includes('`')) {\n inFence = true;\n fenceChar = '`';\n fenceLen = bt[1].length;\n return true;\n }\n const td = TILDE_OPEN.exec(line);\n if (td) {\n inFence = true;\n fenceChar = '~';\n fenceLen = td[1].length;\n return true;\n }\n return false;\n }\n const run = fenceChar === '`' ? /^(`+)(.*)$/ : /^(~+)(.*)$/;\n const m = run.exec(line);\n if (m && m[1].length >= fenceLen && m[2].trim() === '') {\n inFence = false;\n return true;\n }\n return false;\n },\n get inFence() {\n return inFence;\n },\n };\n}\n\n// Masks <!-- ... --> spans that may cross line boundaries, one line at a time so callers can\n// interleave it with their own per-line state. In isolation an unclosed <!-- stays open forever\n// (mask() has no notion of a line boundary that should kill it); maskRegions() below is what\n// calls close() at the point an unclosed comment actually dies -- a blank line at paragraph\n// level, or its enclosing HTML block's end.\nexport interface CommentTracker {\n mask(line: string): string;\n close(): void; // force-ends an unclosed comment (paragraph blank line, or its HTML block ending)\n readonly inComment: boolean;\n}\n\nexport function commentTracker(): CommentTracker {\n let inComment = false;\n return {\n mask(line: string): string {\n let out = '';\n let i = 0;\n while (i < line.length) {\n if (inComment) {\n const end = line.indexOf('-->', i);\n if (end === -1) {\n out += ' '.repeat(line.length - i);\n i = line.length;\n } else {\n out += ' '.repeat(end + 3 - i);\n i = end + 3;\n inComment = false;\n }\n continue;\n }\n const start = line.indexOf('<!--', i);\n if (start === -1) {\n out += line.slice(i);\n i = line.length;\n } else {\n out += `${line.slice(i, start)} `;\n i = start + 4;\n inComment = true;\n }\n }\n return out;\n },\n close() {\n inComment = false;\n },\n get inComment() {\n return inComment;\n },\n };\n}\n\n// CommonMark's HTML-block type-6 list (fixed by the spec, not a drifting enumeration): a line\n// starting with an open or close tag of one of these, at column 0 (leading whitespace/`>`\n// blockquote markers tolerated), opens a block that swallows following lines -- tags, links,\n// and comments alike -- until a blank line closes it.\nconst HTML_BLOCK_TAGS = new Set([\n 'address',\n 'article',\n 'aside',\n 'base',\n 'basefont',\n 'blockquote',\n 'body',\n 'caption',\n 'center',\n 'col',\n 'colgroup',\n 'dd',\n 'details',\n 'dialog',\n 'dir',\n 'div',\n 'dl',\n 'dt',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'frame',\n 'frameset',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hr',\n 'html',\n 'iframe',\n 'legend',\n 'li',\n 'link',\n 'main',\n 'menu',\n 'menuitem',\n 'nav',\n 'noframes',\n 'ol',\n 'optgroup',\n 'option',\n 'p',\n 'param',\n 'search',\n 'section',\n 'summary',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'title',\n 'tr',\n 'track',\n 'ul',\n]);\n// Type-1 blocks (script/pre/style/textarea): closes on the line holding the matching end tag,\n// not on a blank line, and that line is the last one swallowed.\nconst HTML_PRE_TAGS = new Set(['script', 'pre', 'style', 'textarea']);\nconst HTML_PRE_CLOSE_RE = /<\\/(?:script|pre|style|textarea)>/i; // any of the four closers ends the block, not only the tag that opened it\n// An opening or closing tag at column 0, tag name captured for the lookups above.\nconst HTML_BLOCK_OPEN_RE = /^<\\/?([a-zA-Z][a-zA-Z0-9]*)(?:[ \\t]|\\/?>|$)/;\nconst BLANK_LINE_RE = /^[ \\t>]*$/; // whitespace/blockquote-markers only -- a paragraph break, and what ends a type-6 block\n\n// Body with fenced-code, HTML-block, and <!-- --> comment regions replaced by spaces (newlines\n// preserved, so line numbers and offsets survive) plus inline code spans masked. The single\n// region masker for tags/links: fence and HTML-block delimiter+content lines are opaque, an\n// unclosed comment dies at its containing block's end (or the next blank line at paragraph\n// level -- never silently to end of file), and a closed comment masks exactly its span.\n// Bodies without a backtick/tilde/`<` fast-path out untouched.\nexport function maskRegions(body: string): string {\n if (!/[`~<]/.test(body)) return body;\n const fence = fenceTracker();\n const comment = commentTracker();\n let inHtmlBlock = false;\n let htmlBlockClose: RegExp | null = null; // set while inside a type-1 (script/pre/style/textarea) block\n return body\n .split('\\n')\n .map((line) => {\n if (inHtmlBlock) {\n if (htmlBlockClose) {\n if (htmlBlockClose.test(line)) {\n inHtmlBlock = false;\n htmlBlockClose = null;\n }\n return ' '.repeat(line.length);\n }\n if (BLANK_LINE_RE.test(line)) {\n // Block content never feeds the comment tracker, so an inner <!-- was only ever\n // masked text; nothing to close here.\n inHtmlBlock = false;\n return line;\n }\n return ' '.repeat(line.length);\n }\n if (!comment.inComment) {\n const isDelim = fence.feed(line);\n if (isDelim || fence.inFence) return ' '.repeat(line.length);\n }\n const uncommented = comment.inComment || line.includes('<!--') ? comment.mask(line) : line;\n if (comment.inComment && BLANK_LINE_RE.test(line)) comment.close(); // paragraph-level: dies at the next blank line\n // Indented or blockquoted HTML blocks still swallow their content in Obsidian, so the\n // opener test runs after stripping leading whitespace and > markers.\n const stripped = uncommented.replace(/^[ \\t>]*/, '');\n if (stripped[0] === '<') {\n const openMatch = HTML_BLOCK_OPEN_RE.exec(stripped);\n if (openMatch) {\n const tagName = openMatch[1].toLowerCase();\n const isClosingTag = stripped[1] === '/';\n if (!isClosingTag && HTML_PRE_TAGS.has(tagName)) {\n // The end condition can be met on the opening line itself: <script>x</script>\n // is a one-line block and the next line parses (Obsidian-verified).\n if (!HTML_PRE_CLOSE_RE.test(stripped.slice(openMatch[0].length))) {\n inHtmlBlock = true;\n htmlBlockClose = HTML_PRE_CLOSE_RE;\n }\n return ' '.repeat(line.length);\n }\n if (HTML_BLOCK_TAGS.has(tagName)) {\n inHtmlBlock = true;\n return ' '.repeat(line.length);\n }\n }\n }\n // Inline code spans hide their content too: `[[x]]` in prose is not a link.\n return uncommented.includes('`') ? maskCodeSpans(uncommented) : uncommented;\n })\n .join('\\n');\n}\n\n// A code span opens on a run of N backticks and closes at the next run of exactly N -- a\n// shorter or longer run in between is literal text, not a delimiter (CommonMark code spans).\n// Masked with spaces so column positions and tag-boundary whitespace are unaffected.\nexport function maskCodeSpans(line: string): string {\n let out = '';\n let i = 0;\n while (i < line.length) {\n if (line[i] !== '`') {\n out += line[i];\n i++;\n continue;\n }\n let j = i;\n while (line[j] === '`') j++;\n const n = j - i;\n let k = j;\n let closeStart = -1;\n let closeEnd = -1;\n while (k < line.length) {\n if (line[k] !== '`') {\n k++;\n continue;\n }\n let m = k;\n while (line[m] === '`') m++;\n if (m - k === n) {\n closeStart = k;\n closeEnd = m;\n break;\n }\n k = m;\n }\n if (closeStart >= 0) {\n out += ' '.repeat(closeEnd - i);\n i = closeEnd;\n } else {\n out += line.slice(i, j);\n i = j;\n }\n }\n return out;\n}\n"],"names":["commentTracker","fenceTracker","maskCodeSpans","maskRegions","BACKTICK_OPEN","TILDE_OPEN","inFence","fenceChar","fenceLen","feed","line","bt","exec","includes","length","td","run","m","trim","inComment","mask","out","i","end","indexOf","repeat","start","slice","close","HTML_BLOCK_TAGS","Set","HTML_PRE_TAGS","HTML_PRE_CLOSE_RE","HTML_BLOCK_OPEN_RE","BLANK_LINE_RE","body","test","fence","comment","inHtmlBlock","htmlBlockClose","split","map","isDelim","uncommented","stripped","replace","openMatch","tagName","toLowerCase","isClosingTag","has","join","j","n","k","closeStart","closeEnd"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ _export(exports, {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
var _posix = /*#__PURE__*/ _interop_require_default(require("node:path/posix"));
|
|
20
|
-
var _fencests = require("
|
|
20
|
+
var _fencests = require("./fences.js");
|
|
21
21
|
function _array_like_to_array(arr, len) {
|
|
22
22
|
if (len == null || len > arr.length) len = arr.length;
|
|
23
23
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { maskRegions } from '../fences.ts';\nimport type { FileStat } from '../scan.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds), internal markdown links, and\n// frontmatter values that are exactly a wikilink (\"[[X]]\"; mid-string and ![[...]] forms are\n// not links there -- Obsidian's rule, probe-verified).\n// One rule for [[inner]] text wherever it appears: alias stripped after |, a leading #\n// keeps the anchor as the target (a same-note self-link needs a heading name), otherwise\n// the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: Obsidian gives markdown links full\n // linkpath resolution, so \\](Zektor) resolves like [[Zektor]] and \\](#anchor) is a\n // self-edge. External URLs (a scheme anywhere survives the malformed double-paren case)\n // and titled links (dest stops at whitespace) are skipped.\n // A space in the destination is only valid ahead of a quoted title; a domain-shaped first\n // segment (www.example.com/...) is external even without a scheme.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (shortest path wins on ties -- verified against a\n// cold-loaded Obsidian vault on 6+ real collision pairs; byBase's lists are pre-sorted that way).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n // Self-edges (same-note anchors) are excluded here so PageRank mass is not self-recycled --\n // Obsidian's own graph view hides self-loops too. The rows themselves stay in the table for\n // backlinks/peek.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n // A same-note anchor's dst is always its own src, never another file's basename, so it\n // gets no target_base -- SQL's `IN` never matches NULL, keeping it out of\n // resolveIncremental's cross-file collectIn('target_base', ...) entirely.\n for (const { target, embed } of targets) insert.run(path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","collect","collectIn","column","keys","i","chunk","slice","placeholders","reparsed","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":";;;;;;;;;;;QA6KgBA;eAAAA;;QAwCHC;eAAAA;;;4DArNK;wBAEU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAI5B,0FAA0F;AAC1F,iFAAiF;AACjF,+FAA+F;AAC/F,uFAAuF;AAEvF,wFAAwF;AACxF,6FAA6F;AAC7F,uDAAuD;AACvD,uFAAuF;AACvF,yFAAyF;AACzF,4CAA4C;AAC5C,SAASC,mBAAmBC,KAAa;IACvC,IAAMC,cAAcD,MAAME,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,IAAI;IAC5C,IAAIF,YAAYG,UAAU,CAAC,MAAM,OAAOH,YAAYI,MAAM,GAAG,IAAIJ,cAAc;IAC/E,IAAMK,SAASL,YAAYC,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,IAAI;IAC7C,OAAOG,UAAU;AACnB;AAEA,SAASC,QAAQC,IAAY,EAAEC,OAAe,EAAEC,OAA4C,EAAEC,IAA8B;IAC1H,IAAMC,OAAO,YAAYC,IAAI,CAACJ,WAAWK,IAAAA,qBAAW,EAACL,WAAWA;IAChE,IAAMM,OAAO,IAAIC;IACjB,IAAMC,UAAqD,EAAE;IAC7D,IAAMC,MAAM,aAACZ,QAAgBa;QAC3B,IAAMC,MAAM,AAAC,GAAaD,OAAXb,QAAO,MAAsB,OAAlBa,QAAQ,MAAM;QACxC,IAAIJ,KAAKM,GAAG,CAACD,MAAM;QACnBL,KAAKG,GAAG,CAACE;QACTH,QAAQK,IAAI,CAAC;YAAEhB,QAAAA;YAAQa,OAAAA;QAAM;IAC/B;QAGK,kCAAA,2BAAA;;QAFL,uFAAuF;QACvF,6DAA6D;QAC7D,QAAK,YAAWP,KAAKW,QAAQ,CAAC,sCAAzB,SAAA,6BAAA,QAAA,yBAAA,iCAA4C;YAA5C,IAAMC,IAAN;gBAE2BA;YAD9B,IAAMlB,SAASP,mBAAmByB,CAAC,CAAC,EAAE;YACtC,IAAIlB,QAAQY,IAAIZ,QAAQM,IAAI,CAAC,EAACY,WAAAA,EAAEC,KAAK,cAAPD,sBAAAA,WAAW,KAAK,EAAE,KAAK;QACvD;;QAHK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QAUA,mCAAA,4BAAA;;QANL,sFAAsF;QACtF,mFAAmF;QACnF,wFAAwF;QACxF,2DAA2D;QAC3D,0FAA0F;QAC1F,mEAAmE;QACnE,QAAK,aAAWZ,KAAKW,QAAQ,CAAC,iGAAzB,UAAA,8BAAA,SAAA,0BAAA,kCAAuG;YAAvG,IAAMC,KAAN;YACH,IAAME,OAAOF,EAAC,CAAC,EAAE,CAACrB,IAAI;YACtB,qFAAqF;YACrF,oFAAoF;YACpF,IAAI,CAACuB,QAAQA,KAAKC,QAAQ,CAAC,QAAQ;YACnC,IAAI,oCAAoCd,IAAI,CAACa,OAAO;YACpD,IAAMpB,UAASoB,KAAKtB,UAAU,CAAC,OAAOsB,OAAOA,KAAKxB,KAAK,CAAC,IAAI,CAAC,EAAE;YAC/D,IAAII,SAAQY,IAAIZ,SAAQkB,EAAC,CAAC,EAAE,KAAK;QACnC;;QARK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;QASA,mCAAA,4BAAA;;QAAL,QAAK,aAAeI,OAAOC,MAAM,CAAClB,iBAAAA,kBAAAA,OAAQ,CAAC,uBAAtC,UAAA,8BAAA,SAAA,0BAAA,kCAA0C;YAA1C,IAAMmB,QAAN;gBACE,mCAAA,4BAAA;;gBAAL,QAAK,aAAcC,CAAAA,MAAMC,OAAO,CAACF,SAASA,QAAQ;oBAACA;iBAAM,AAAD,sBAAnD,UAAA,8BAAA,SAAA,0BAAA,kCAAsD;oBAAtD,IAAMG,OAAN;oBACH,IAAI,OAAOA,SAAS,UAAU;oBAC9B,IAAMT,KAAI,iBAAiBU,IAAI,CAACD,KAAK9B,IAAI;oBACzC,IAAI,CAACqB,IAAG;oBACR,IAAMlB,UAASP,mBAAmByB,EAAC,CAAC,EAAE;oBACtC,IAAIlB,SAAQY,IAAIZ,SAAQ;gBAC1B;;gBANK;gBAAA;;;yBAAA,8BAAA;wBAAA;;;wBAAA;8BAAA;;;;QAOP;;QARK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IASL,OAAOW;AACT;AAEA,SAASkB,YAAY7B,MAAc;IACjC,OAAOA,OAAO8B,OAAO,CAAC,OAAO,KAAKA,OAAO,CAAC,SAAS;AACrD;AAEA,SAASC,QAAQC,IAAY;IAC3B,OAAOC,cAAK,CAACC,QAAQ,CAACF,MAAMF,OAAO,CAAC,UAAU,IAAIK,WAAW;AAC/D;AAEA,uFAAuF;AACvF,0FAA0F;AAC1F,kGAAkG;AAClG,SAASC,cAAcC,GAAW,EAAErC,MAAc,EAAEsC,OAAoB,EAAEC,MAA6B;IACrG,4FAA4F;IAC5F,IAAIvC,OAAOF,UAAU,CAAC,MAAM,OAAOuC;IACnC,IAAMG,QAAQX,YAAY7B;IAC1B,IAAMyC,UAAUR,cAAK,CAACS,SAAS,CAACT,cAAK,CAACU,IAAI,CAACV,cAAK,CAACW,OAAO,CAACP,MAAMG;IAC/D,gBAAwB,QAAA;QAACA;QAAQ,GAAQ,OAANA,OAAM;QAAMC;QAAU,GAAU,OAARA,SAAQ;KAAK,OAAhD,mBAAkD;YAA/DI,YAAa;QACtB,IAAIP,QAAQvB,GAAG,CAAC8B,YAAY,OAAOA;IACrC;IACA,IAAMC,aAAaP,OAAOQ,GAAG,CAAChB,QAAQS;IACtC,IAAI,CAACM,YAAY,OAAO;IACxB,0FAA0F;IAC1F,+DAA+D;IAC/D,OAAOA,WAAWzB,QAAQ,CAACgB,OAAOA,MAAMS,UAAU,CAAC,EAAE;AACvD;AAEA,6FAA6F;AAC7F,yFAAyF;AACzF,SAASE,YAAYC,KAAiB;IACpC,IAAMV,SAAS,IAAIW;IACnB,IAAMC,QAAQF,MAAMG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;OAAEC,IAAI,CAAC,SAACC,GAAGC;eAAMD,EAAEzD,MAAM,GAAG0D,EAAE1D,MAAM,IAAKyD,CAAAA,IAAIC,IAAI,CAAC,IAAID,IAAIC,IAAI,IAAI,CAAA;;QACrG,kCAAA,2BAAA;;QAAL,QAAK,YAAcN,0BAAd,SAAA,6BAAA,QAAA,yBAAA,iCAAqB;YAArB,IAAMnB,OAAN;YACH,IAAMlB,MAAMiB,QAAQC;YACpB,IAAM0B,OAAOnB,OAAOQ,GAAG,CAACjC;YACxB,IAAI4C,MAAMA,KAAK1C,IAAI,CAACgB;iBACfO,OAAOoB,GAAG,CAAC7C,KAAK;gBAACkB;aAAK;QAC7B;;QALK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAML,OAAOO;AACT;AASA,4FAA4F;AAC5F,0FAA0F;AAC1F,0BAA0B;AAC1B,SAASqB,YAAYC,EAAgB,EAAEC,IAAe,EAAExB,OAAoB,EAAEC,MAA6B;IACzG,IAAMwB,SAASF,GAAGG,OAAO,CAAC;IAC1B,IAAIC,UAAU;QACT,kCAAA,2BAAA;;QAAL,QAAK,YAAaH,yBAAb,SAAA,6BAAA,QAAA,yBAAA,iCAAmB;YAAnB,IAAMI,MAAN;YACH,IAAMC,MAAM/B,cAAc8B,IAAI7B,GAAG,EAAE6B,IAAIlE,MAAM,EAAEsC,SAASC;YACxD,IAAI4B,QAAQD,IAAIC,GAAG,EAAE;gBACnBJ,OAAOK,GAAG,CAACD,KAAKD,IAAI7B,GAAG,EAAE6B,IAAIlE,MAAM;gBACnCiE,UAAU;YACZ;QACF;;QANK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAOL,OAAOA;AACT;AAEA,yFAAyF;AACzF,+EAA+E;AAC/E,SAASI,WAAWR,EAAgB,EAAEZ,KAAiB;IACrD,IAAMX,UAAU,IAAI5B,IAAIuC,MAAMG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IAClD,IAAMf,SAASS,YAAYC;IAC3B,IAAMa,OAAOD,GAAGG,OAAO,CAAC,6CAA6CM,GAAG;IACxE,OAAOV,YAAYC,IAAIC,MAAMxB,SAASC;AACxC;AAEA,yFAAyF;AACzF,yFAAyF;AACzF,kEAAkE;AAClE,SAASgC,mBAAmBV,EAAgB,EAAEW,KAAqB;IACjE,IAAMlC,UAAU,IAAI5B,IAAI8D,MAAMvB,KAAK,CAACG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IACxD,IAAMf,SAASS,YAAYwB,MAAMvB,KAAK;IAEtC,IAAMwB,mBAAmB,IAAI/D;QACxB,kCAAA,2BAAA;;QAAL,QAAK,YAAW8D,MAAME,KAAK,qBAAtB,SAAA,6BAAA,QAAA,yBAAA;YAAA,IAAMC,IAAN;YAAwBF,iBAAiB7D,GAAG,CAACmB,QAAQ4C;;;QAArD;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QACA,mCAAA,4BAAA;;QAAL,QAAK,aAAWH,MAAMI,QAAQ,qBAAzB,UAAA,8BAAA,SAAA,0BAAA;YAAA,IAAMD,KAAN;YAA2BF,iBAAiB7D,GAAG,CAACmB,QAAQ4C;;;QAAxD;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAEL,+EAA+E;IAC/E,IAAM7B,aAAa,IAAII;IACvB,IAAM2B,UAAU,iBAACf;YACV,kCAAA,2BAAA;;YAAL,QAAK,YAAaA,yBAAb,SAAA,6BAAA,QAAA,yBAAA;gBAAA,IAAMI,MAAN;gBAAmBpB,WAAWa,GAAG,CAAC,AAAC,GAAcO,OAAZA,IAAI7B,GAAG,EAAC,MAAmB6B,OAAfA,IAAIlE,MAAM,EAAC,MAAc,OAAVkE,IAAIrD,KAAK,GAAIqD;;;YAA7E;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACA,qFAAqF;IACrF,6CAA6C;IAC7C,IAAMY,YAAY,mBAACC,QAAgBC;QACjC,IAAK,IAAIC,IAAI,GAAGA,IAAID,KAAKjF,MAAM,EAAEkF,KAAK,IAAK;gBAGjCpB;YAFR,IAAMqB,QAAQF,KAAKG,KAAK,CAACF,GAAGA,IAAI;YAChC,IAAMG,eAAeF,MAAM9B,GAAG,CAAC;uBAAM;eAAKT,IAAI,CAAC;YAC/CkC,QAAQhB,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,mDAAgEoB,OAAdL,QAAO,SAAoB,OAAbK,cAAa,OAAId,GAAG,OAAhGT,aAAiG,qBAAGqB;QAC9G;IACF;IAEAJ,UAAU,OAAON,MAAMa,QAAQ;IAC/BP,UAAU,eAAgB,qBAAGL;IAC7BK,UAAU,OAAON,MAAMI,QAAQ;IAE/B,OAAOhB,YAAYC,IAAK,qBAAGf,WAAWvB,MAAM,KAAKe,SAASC;AAC5D;AAGO,SAAShD,UAAUsE,EAAgB;IACxC,0FAA0F;IAC1F,yFAAyF;IACzF,yFAAyF;IACzF,0FAA0F;IAC1F,iFAAiF;IACjF,4FAA4F;IAC5F,4FAA4F;IAC5F,kBAAkB;IAClB,IAAMyB,MAAM;IAIZ,OAAO,AAACzB,GAAGG,OAAO,CAACsB,KAAKhB,GAAG,GAA2ClB,GAAG,CAAC,SAACmC;eAAM;YAACA,EAAElD,GAAG;YAAEkD,EAAEpB,GAAG;SAAC;;AACjG;AAEA,4FAA4F;AAC5F,+FAA+F;AAC/F,IAAMqB,mBAAmB,IAAIC;AAE7B,SAASC,cAAclB,KAAqB,EAAExC,IAAY;IACxD,IAAI2B,MAAM6B,iBAAiBzC,GAAG,CAACyB;IAC/B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIjD;QACV8E,iBAAiB7B,GAAG,CAACa,OAAOb;IAC9B;IACAA,IAAI/C,GAAG,CAACoB;AACV;AAEA,qFAAqF;AACrF,IAAM2D,eAAe,IAAIF;AACzB,SAASG,YAAYpB,KAAqB;IACxC,IAAIb,MAAMgC,aAAa5C,GAAG,CAACyB;IAC3B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIjD,IAAI8D,MAAMI,QAAQ;QAC5Be,aAAahC,GAAG,CAACa,OAAOb;IAC1B;IACA,OAAOA;AACT;AAEO,IAAMnE,QAAiB;IAC5BqG,MAAM;IACNC,QAAAA,SAAAA,OAAOjC,EAAE;QACPA,GAAGjC,IAAI,CAAC;QACRiC,GAAGjC,IAAI,CAAC;QACRiC,GAAGjC,IAAI,CAAC;IACV;IACA3B,SAAAA;IACA,gFAAgF;IAChF,uFAAuF;IACvF,kFAAkF;IAClF8F,QAAAA,SAAAA,OAAOlC,EAAE,EAAE7B,IAAI,EAAEwC,KAAK;QACpB,IAAI,CAACoB,YAAYpB,OAAOzD,GAAG,CAACiB,OAAO;QACnC,IAAMgE,SAASnC,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACpC;QACjE,IAAIiE,OAAOD,OAAOE,OAAO,IAAI,GAAGR,cAAclB,OAAOxC;IACvD;IACAmE,OAAAA,SAAAA,MAAMtC,EAAE,EAAE7B,IAAI,EAAEoE,SAAS,EAAE5B,KAAK;QAC9B,IAAM6B,UAAUD;QAChB,2FAA2F;QAC3F,oEAAoE;QACpE,IAAIE;QACJ,IAAID,QAAQtG,MAAM,KAAK,GAAG;YACxBuG,QAAQzC,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACpC;QAC5D,OAAO;gBAGG6B;YAFR,IAAMuB,eAAeiB,QAAQjD,GAAG,CAAC;uBAAM;eAAKT,IAAI,CAAC;YACjD,IAAMqC,OAAOqB,QAAQjD,GAAG,CAAC,SAACmD;uBAAM,AAAC,GAAeA,OAAbA,EAAEvG,MAAM,EAAC,MAAwB,OAApBuG,EAAE1F,KAAK,GAAG,MAAM;;YAChEyF,QAAQzC,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,4EAAwF,OAAboB,cAAa,OAAIhB,GAAG,OAA3GP,aAAAA;gBAA4G7B;aAAc,CAA1H6B,OAAkH,qBAAGmB;QAC/H;QACA,IAAIiB,OAAOK,MAAMJ,OAAO,IAAI,GAAGR,cAAclB,OAAOxC;QACpD,oFAAoF;QACpF,sEAAsE;QACtE,IAAMwE,SAAS3C,GAAGG,OAAO,CAAC;YAIrB,kCAAA,2BAAA;;YAHL,uFAAuF;YACvF,0EAA0E;YAC1E,0EAA0E;YAC1E,QAAK,YAA2BqC,4BAA3B,SAAA,6BAAA,QAAA,yBAAA;gBAAA,kBAAA,aAAQrG,qBAAAA,QAAQa,oBAAAA;gBAAoB2F,OAAOpC,GAAG,CAACpC,MAAMhC,QAAQA,OAAOF,UAAU,CAAC,OAAO,OAAOiC,QAAQF,YAAY7B,UAAUa,QAAQ,IAAI;;;YAAvI;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACA4F,gBAAAA,SAAAA,eAAe5C,EAAE,EAAEW,KAAK;;YAIEgB;QAHxB,mFAAmF;QACnF,+EAA+E;QAC/E,iEAAiE;QACjE,IAAMkB,iBAAiB,UAAClB,wBAAAA,iBAAiBzC,GAAG,CAACyB,oBAArBgB,4CAAAA,sBAA6BmB,IAAI,uCAAI,KAAK;QAElE,IAAMC,QAAQpC,MAAMa,QAAQ,CAACtF,MAAM,GAAGyE,MAAMI,QAAQ,CAAC7E,MAAM;QAC3D,+EAA+E;QAC/E,iFAAiF;QACjF,IAAM8G,QAAQrC,MAAMvB,KAAK,CAAClD,MAAM,KAAK,KAAK6G,QAAQ,MAAMpC,MAAMvB,KAAK,CAAClD,MAAM;QAE1E,IAAM+G,aAAaD,QAAQxC,WAAWR,IAAIW,MAAMvB,KAAK,IAAIsB,mBAAmBV,IAAIW;QAChFA,MAAMuC,YAAY,GAAGD,cAAcJ;IACrC;AACF"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileStat } from '../scan/index.ts';\nimport { maskRegions } from './fences.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds), internal markdown links, and\n// frontmatter values that are exactly a wikilink (\"[[X]]\"; mid-string and ![[...]] forms are\n// not links there -- Obsidian's rule, probe-verified).\n// One rule for [[inner]] text wherever it appears: alias stripped after |, a leading #\n// keeps the anchor as the target (a same-note self-link needs a heading name), otherwise\n// the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: Obsidian gives markdown links full\n // linkpath resolution, so \\](Zektor) resolves like [[Zektor]] and \\](#anchor) is a\n // self-edge. External URLs (a scheme anywhere survives the malformed double-paren case)\n // and titled links (dest stops at whitespace) are skipped.\n // A space in the destination is only valid ahead of a quoted title; a domain-shaped first\n // segment (www.example.com/...) is external even without a scheme.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (shortest path wins on ties -- verified against a\n// cold-loaded Obsidian vault on 6+ real collision pairs; byBase's lists are pre-sorted that way).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n // Self-edges (same-note anchors) are excluded here so PageRank mass is not self-recycled --\n // Obsidian's own graph view hides self-loops too. The rows themselves stay in the table for\n // backlinks/peek.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n // A same-note anchor's dst is always its own src, never another file's basename, so it\n // gets no target_base -- SQL's `IN` never matches NULL, keeping it out of\n // resolveIncremental's cross-file collectIn('target_base', ...) entirely.\n for (const { target, embed } of targets) insert.run(path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","collect","collectIn","column","keys","i","chunk","slice","placeholders","reparsed","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":";;;;;;;;;;;QA6KgBA;eAAAA;;QAwCHC;eAAAA;;;4DArNK;wBAGU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG5B,0FAA0F;AAC1F,iFAAiF;AACjF,+FAA+F;AAC/F,uFAAuF;AAEvF,wFAAwF;AACxF,6FAA6F;AAC7F,uDAAuD;AACvD,uFAAuF;AACvF,yFAAyF;AACzF,4CAA4C;AAC5C,SAASC,mBAAmBC,KAAa;IACvC,IAAMC,cAAcD,MAAME,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,IAAI;IAC5C,IAAIF,YAAYG,UAAU,CAAC,MAAM,OAAOH,YAAYI,MAAM,GAAG,IAAIJ,cAAc;IAC/E,IAAMK,SAASL,YAAYC,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,IAAI;IAC7C,OAAOG,UAAU;AACnB;AAEA,SAASC,QAAQC,IAAY,EAAEC,OAAe,EAAEC,OAA4C,EAAEC,IAA8B;IAC1H,IAAMC,OAAO,YAAYC,IAAI,CAACJ,WAAWK,IAAAA,qBAAW,EAACL,WAAWA;IAChE,IAAMM,OAAO,IAAIC;IACjB,IAAMC,UAAqD,EAAE;IAC7D,IAAMC,MAAM,aAACZ,QAAgBa;QAC3B,IAAMC,MAAM,AAAC,GAAaD,OAAXb,QAAO,MAAsB,OAAlBa,QAAQ,MAAM;QACxC,IAAIJ,KAAKM,GAAG,CAACD,MAAM;QACnBL,KAAKG,GAAG,CAACE;QACTH,QAAQK,IAAI,CAAC;YAAEhB,QAAAA;YAAQa,OAAAA;QAAM;IAC/B;QAGK,kCAAA,2BAAA;;QAFL,uFAAuF;QACvF,6DAA6D;QAC7D,QAAK,YAAWP,KAAKW,QAAQ,CAAC,sCAAzB,SAAA,6BAAA,QAAA,yBAAA,iCAA4C;YAA5C,IAAMC,IAAN;gBAE2BA;YAD9B,IAAMlB,SAASP,mBAAmByB,CAAC,CAAC,EAAE;YACtC,IAAIlB,QAAQY,IAAIZ,QAAQM,IAAI,CAAC,EAACY,WAAAA,EAAEC,KAAK,cAAPD,sBAAAA,WAAW,KAAK,EAAE,KAAK;QACvD;;QAHK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QAUA,mCAAA,4BAAA;;QANL,sFAAsF;QACtF,mFAAmF;QACnF,wFAAwF;QACxF,2DAA2D;QAC3D,0FAA0F;QAC1F,mEAAmE;QACnE,QAAK,aAAWZ,KAAKW,QAAQ,CAAC,iGAAzB,UAAA,8BAAA,SAAA,0BAAA,kCAAuG;YAAvG,IAAMC,KAAN;YACH,IAAME,OAAOF,EAAC,CAAC,EAAE,CAACrB,IAAI;YACtB,qFAAqF;YACrF,oFAAoF;YACpF,IAAI,CAACuB,QAAQA,KAAKC,QAAQ,CAAC,QAAQ;YACnC,IAAI,oCAAoCd,IAAI,CAACa,OAAO;YACpD,IAAMpB,UAASoB,KAAKtB,UAAU,CAAC,OAAOsB,OAAOA,KAAKxB,KAAK,CAAC,IAAI,CAAC,EAAE;YAC/D,IAAII,SAAQY,IAAIZ,SAAQkB,EAAC,CAAC,EAAE,KAAK;QACnC;;QARK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;QASA,mCAAA,4BAAA;;QAAL,QAAK,aAAeI,OAAOC,MAAM,CAAClB,iBAAAA,kBAAAA,OAAQ,CAAC,uBAAtC,UAAA,8BAAA,SAAA,0BAAA,kCAA0C;YAA1C,IAAMmB,QAAN;gBACE,mCAAA,4BAAA;;gBAAL,QAAK,aAAcC,CAAAA,MAAMC,OAAO,CAACF,SAASA,QAAQ;oBAACA;iBAAM,AAAD,sBAAnD,UAAA,8BAAA,SAAA,0BAAA,kCAAsD;oBAAtD,IAAMG,OAAN;oBACH,IAAI,OAAOA,SAAS,UAAU;oBAC9B,IAAMT,KAAI,iBAAiBU,IAAI,CAACD,KAAK9B,IAAI;oBACzC,IAAI,CAACqB,IAAG;oBACR,IAAMlB,UAASP,mBAAmByB,EAAC,CAAC,EAAE;oBACtC,IAAIlB,SAAQY,IAAIZ,SAAQ;gBAC1B;;gBANK;gBAAA;;;yBAAA,8BAAA;wBAAA;;;wBAAA;8BAAA;;;;QAOP;;QARK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IASL,OAAOW;AACT;AAEA,SAASkB,YAAY7B,MAAc;IACjC,OAAOA,OAAO8B,OAAO,CAAC,OAAO,KAAKA,OAAO,CAAC,SAAS;AACrD;AAEA,SAASC,QAAQC,IAAY;IAC3B,OAAOC,cAAK,CAACC,QAAQ,CAACF,MAAMF,OAAO,CAAC,UAAU,IAAIK,WAAW;AAC/D;AAEA,uFAAuF;AACvF,0FAA0F;AAC1F,kGAAkG;AAClG,SAASC,cAAcC,GAAW,EAAErC,MAAc,EAAEsC,OAAoB,EAAEC,MAA6B;IACrG,4FAA4F;IAC5F,IAAIvC,OAAOF,UAAU,CAAC,MAAM,OAAOuC;IACnC,IAAMG,QAAQX,YAAY7B;IAC1B,IAAMyC,UAAUR,cAAK,CAACS,SAAS,CAACT,cAAK,CAACU,IAAI,CAACV,cAAK,CAACW,OAAO,CAACP,MAAMG;IAC/D,gBAAwB,QAAA;QAACA;QAAQ,GAAQ,OAANA,OAAM;QAAMC;QAAU,GAAU,OAARA,SAAQ;KAAK,OAAhD,mBAAkD;YAA/DI,YAAa;QACtB,IAAIP,QAAQvB,GAAG,CAAC8B,YAAY,OAAOA;IACrC;IACA,IAAMC,aAAaP,OAAOQ,GAAG,CAAChB,QAAQS;IACtC,IAAI,CAACM,YAAY,OAAO;IACxB,0FAA0F;IAC1F,+DAA+D;IAC/D,OAAOA,WAAWzB,QAAQ,CAACgB,OAAOA,MAAMS,UAAU,CAAC,EAAE;AACvD;AAEA,6FAA6F;AAC7F,yFAAyF;AACzF,SAASE,YAAYC,KAAiB;IACpC,IAAMV,SAAS,IAAIW;IACnB,IAAMC,QAAQF,MAAMG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;OAAEC,IAAI,CAAC,SAACC,GAAGC;eAAMD,EAAEzD,MAAM,GAAG0D,EAAE1D,MAAM,IAAKyD,CAAAA,IAAIC,IAAI,CAAC,IAAID,IAAIC,IAAI,IAAI,CAAA;;QACrG,kCAAA,2BAAA;;QAAL,QAAK,YAAcN,0BAAd,SAAA,6BAAA,QAAA,yBAAA,iCAAqB;YAArB,IAAMnB,OAAN;YACH,IAAMlB,MAAMiB,QAAQC;YACpB,IAAM0B,OAAOnB,OAAOQ,GAAG,CAACjC;YACxB,IAAI4C,MAAMA,KAAK1C,IAAI,CAACgB;iBACfO,OAAOoB,GAAG,CAAC7C,KAAK;gBAACkB;aAAK;QAC7B;;QALK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAML,OAAOO;AACT;AASA,4FAA4F;AAC5F,0FAA0F;AAC1F,0BAA0B;AAC1B,SAASqB,YAAYC,EAAgB,EAAEC,IAAe,EAAExB,OAAoB,EAAEC,MAA6B;IACzG,IAAMwB,SAASF,GAAGG,OAAO,CAAC;IAC1B,IAAIC,UAAU;QACT,kCAAA,2BAAA;;QAAL,QAAK,YAAaH,yBAAb,SAAA,6BAAA,QAAA,yBAAA,iCAAmB;YAAnB,IAAMI,MAAN;YACH,IAAMC,MAAM/B,cAAc8B,IAAI7B,GAAG,EAAE6B,IAAIlE,MAAM,EAAEsC,SAASC;YACxD,IAAI4B,QAAQD,IAAIC,GAAG,EAAE;gBACnBJ,OAAOK,GAAG,CAACD,KAAKD,IAAI7B,GAAG,EAAE6B,IAAIlE,MAAM;gBACnCiE,UAAU;YACZ;QACF;;QANK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAOL,OAAOA;AACT;AAEA,yFAAyF;AACzF,+EAA+E;AAC/E,SAASI,WAAWR,EAAgB,EAAEZ,KAAiB;IACrD,IAAMX,UAAU,IAAI5B,IAAIuC,MAAMG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IAClD,IAAMf,SAASS,YAAYC;IAC3B,IAAMa,OAAOD,GAAGG,OAAO,CAAC,6CAA6CM,GAAG;IACxE,OAAOV,YAAYC,IAAIC,MAAMxB,SAASC;AACxC;AAEA,yFAAyF;AACzF,yFAAyF;AACzF,kEAAkE;AAClE,SAASgC,mBAAmBV,EAAgB,EAAEW,KAAqB;IACjE,IAAMlC,UAAU,IAAI5B,IAAI8D,MAAMvB,KAAK,CAACG,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IACxD,IAAMf,SAASS,YAAYwB,MAAMvB,KAAK;IAEtC,IAAMwB,mBAAmB,IAAI/D;QACxB,kCAAA,2BAAA;;QAAL,QAAK,YAAW8D,MAAME,KAAK,qBAAtB,SAAA,6BAAA,QAAA,yBAAA;YAAA,IAAMC,IAAN;YAAwBF,iBAAiB7D,GAAG,CAACmB,QAAQ4C;;;QAArD;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QACA,mCAAA,4BAAA;;QAAL,QAAK,aAAWH,MAAMI,QAAQ,qBAAzB,UAAA,8BAAA,SAAA,0BAAA;YAAA,IAAMD,KAAN;YAA2BF,iBAAiB7D,GAAG,CAACmB,QAAQ4C;;;QAAxD;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAEL,+EAA+E;IAC/E,IAAM7B,aAAa,IAAII;IACvB,IAAM2B,UAAU,iBAACf;YACV,kCAAA,2BAAA;;YAAL,QAAK,YAAaA,yBAAb,SAAA,6BAAA,QAAA,yBAAA;gBAAA,IAAMI,MAAN;gBAAmBpB,WAAWa,GAAG,CAAC,AAAC,GAAcO,OAAZA,IAAI7B,GAAG,EAAC,MAAmB6B,OAAfA,IAAIlE,MAAM,EAAC,MAAc,OAAVkE,IAAIrD,KAAK,GAAIqD;;;YAA7E;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACA,qFAAqF;IACrF,6CAA6C;IAC7C,IAAMY,YAAY,mBAACC,QAAgBC;QACjC,IAAK,IAAIC,IAAI,GAAGA,IAAID,KAAKjF,MAAM,EAAEkF,KAAK,IAAK;gBAGjCpB;YAFR,IAAMqB,QAAQF,KAAKG,KAAK,CAACF,GAAGA,IAAI;YAChC,IAAMG,eAAeF,MAAM9B,GAAG,CAAC;uBAAM;eAAKT,IAAI,CAAC;YAC/CkC,QAAQhB,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,mDAAgEoB,OAAdL,QAAO,SAAoB,OAAbK,cAAa,OAAId,GAAG,OAAhGT,aAAiG,qBAAGqB;QAC9G;IACF;IAEAJ,UAAU,OAAON,MAAMa,QAAQ;IAC/BP,UAAU,eAAgB,qBAAGL;IAC7BK,UAAU,OAAON,MAAMI,QAAQ;IAE/B,OAAOhB,YAAYC,IAAK,qBAAGf,WAAWvB,MAAM,KAAKe,SAASC;AAC5D;AAGO,SAAShD,UAAUsE,EAAgB;IACxC,0FAA0F;IAC1F,yFAAyF;IACzF,yFAAyF;IACzF,0FAA0F;IAC1F,iFAAiF;IACjF,4FAA4F;IAC5F,4FAA4F;IAC5F,kBAAkB;IAClB,IAAMyB,MAAM;IAIZ,OAAO,AAACzB,GAAGG,OAAO,CAACsB,KAAKhB,GAAG,GAA2ClB,GAAG,CAAC,SAACmC;eAAM;YAACA,EAAElD,GAAG;YAAEkD,EAAEpB,GAAG;SAAC;;AACjG;AAEA,4FAA4F;AAC5F,+FAA+F;AAC/F,IAAMqB,mBAAmB,IAAIC;AAE7B,SAASC,cAAclB,KAAqB,EAAExC,IAAY;IACxD,IAAI2B,MAAM6B,iBAAiBzC,GAAG,CAACyB;IAC/B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIjD;QACV8E,iBAAiB7B,GAAG,CAACa,OAAOb;IAC9B;IACAA,IAAI/C,GAAG,CAACoB;AACV;AAEA,qFAAqF;AACrF,IAAM2D,eAAe,IAAIF;AACzB,SAASG,YAAYpB,KAAqB;IACxC,IAAIb,MAAMgC,aAAa5C,GAAG,CAACyB;IAC3B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIjD,IAAI8D,MAAMI,QAAQ;QAC5Be,aAAahC,GAAG,CAACa,OAAOb;IAC1B;IACA,OAAOA;AACT;AAEO,IAAMnE,QAAiB;IAC5BqG,MAAM;IACNC,QAAAA,SAAAA,OAAOjC,EAAE;QACPA,GAAGjC,IAAI,CAAC;QACRiC,GAAGjC,IAAI,CAAC;QACRiC,GAAGjC,IAAI,CAAC;IACV;IACA3B,SAAAA;IACA,gFAAgF;IAChF,uFAAuF;IACvF,kFAAkF;IAClF8F,QAAAA,SAAAA,OAAOlC,EAAE,EAAE7B,IAAI,EAAEwC,KAAK;QACpB,IAAI,CAACoB,YAAYpB,OAAOzD,GAAG,CAACiB,OAAO;QACnC,IAAMgE,SAASnC,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACpC;QACjE,IAAIiE,OAAOD,OAAOE,OAAO,IAAI,GAAGR,cAAclB,OAAOxC;IACvD;IACAmE,OAAAA,SAAAA,MAAMtC,EAAE,EAAE7B,IAAI,EAAEoE,SAAS,EAAE5B,KAAK;QAC9B,IAAM6B,UAAUD;QAChB,2FAA2F;QAC3F,oEAAoE;QACpE,IAAIE;QACJ,IAAID,QAAQtG,MAAM,KAAK,GAAG;YACxBuG,QAAQzC,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACpC;QAC5D,OAAO;gBAGG6B;YAFR,IAAMuB,eAAeiB,QAAQjD,GAAG,CAAC;uBAAM;eAAKT,IAAI,CAAC;YACjD,IAAMqC,OAAOqB,QAAQjD,GAAG,CAAC,SAACmD;uBAAM,AAAC,GAAeA,OAAbA,EAAEvG,MAAM,EAAC,MAAwB,OAApBuG,EAAE1F,KAAK,GAAG,MAAM;;YAChEyF,QAAQzC,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,4EAAwF,OAAboB,cAAa,OAAIhB,GAAG,OAA3GP,aAAAA;gBAA4G7B;aAAc,CAA1H6B,OAAkH,qBAAGmB;QAC/H;QACA,IAAIiB,OAAOK,MAAMJ,OAAO,IAAI,GAAGR,cAAclB,OAAOxC;QACpD,oFAAoF;QACpF,sEAAsE;QACtE,IAAMwE,SAAS3C,GAAGG,OAAO,CAAC;YAIrB,kCAAA,2BAAA;;YAHL,uFAAuF;YACvF,0EAA0E;YAC1E,0EAA0E;YAC1E,QAAK,YAA2BqC,4BAA3B,SAAA,6BAAA,QAAA,yBAAA;gBAAA,kBAAA,aAAQrG,qBAAAA,QAAQa,oBAAAA;gBAAoB2F,OAAOpC,GAAG,CAACpC,MAAMhC,QAAQA,OAAOF,UAAU,CAAC,OAAO,OAAOiC,QAAQF,YAAY7B,UAAUa,QAAQ,IAAI;;;YAAvI;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACA4F,gBAAAA,SAAAA,eAAe5C,EAAE,EAAEW,KAAK;;YAIEgB;QAHxB,mFAAmF;QACnF,+EAA+E;QAC/E,iEAAiE;QACjE,IAAMkB,iBAAiB,UAAClB,wBAAAA,iBAAiBzC,GAAG,CAACyB,oBAArBgB,4CAAAA,sBAA6BmB,IAAI,uCAAI,KAAK;QAElE,IAAMC,QAAQpC,MAAMa,QAAQ,CAACtF,MAAM,GAAGyE,MAAMI,QAAQ,CAAC7E,MAAM;QAC3D,+EAA+E;QAC/E,iFAAiF;QACjF,IAAM8G,QAAQrC,MAAMvB,KAAK,CAAClD,MAAM,KAAK,KAAK6G,QAAQ,MAAMpC,MAAMvB,KAAK,CAAClD,MAAM;QAE1E,IAAM+G,aAAaD,QAAQxC,WAAWR,IAAIW,MAAMvB,KAAK,IAAIsB,mBAAmBV,IAAIW;QAChFA,MAAMuC,YAAY,GAAGD,cAAcJ;IACrC;AACF"}
|
|
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "rank", {
|
|
|
8
8
|
return rank;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
var _graphts = require("../graph.js");
|
|
11
|
+
var _graphts = require("../graph/graph.js");
|
|
12
12
|
var _linksts = require("./links.js");
|
|
13
13
|
function _array_like_to_array(arr, len) {
|
|
14
14
|
if (len == null || len > arr.length) len = arr.length;
|