sensemaking 0.9.4 → 0.10.0

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.
Files changed (156) hide show
  1. package/README.md +50 -100
  2. package/dist/cjs/cli/check.js +7 -13
  3. package/dist/cjs/cli/check.js.map +1 -1
  4. package/dist/cjs/cli/download.d.cts +3 -0
  5. package/dist/cjs/cli/download.d.ts +3 -0
  6. package/dist/cjs/cli/download.js +214 -0
  7. package/dist/cjs/cli/download.js.map +1 -0
  8. package/dist/cjs/cli/index.d.cts +6 -3
  9. package/dist/cjs/cli/index.d.ts +6 -3
  10. package/dist/cjs/cli/index.js +23 -5
  11. package/dist/cjs/cli/index.js.map +1 -1
  12. package/dist/cjs/cli/init.js +4 -1
  13. package/dist/cjs/cli/init.js.map +1 -1
  14. package/dist/cjs/cli/map.js +2 -2
  15. package/dist/cjs/cli/map.js.map +1 -1
  16. package/dist/cjs/cli/named.js +10 -18
  17. package/dist/cjs/cli/named.js.map +1 -1
  18. package/dist/cjs/cli/path.d.cts +3 -0
  19. package/dist/cjs/cli/path.d.ts +3 -0
  20. package/dist/cjs/cli/path.js +161 -0
  21. package/dist/cjs/cli/path.js.map +1 -0
  22. package/dist/cjs/cli/peek.js +3 -2
  23. package/dist/cjs/cli/peek.js.map +1 -1
  24. package/dist/cjs/cli/related.d.cts +3 -0
  25. package/dist/cjs/cli/related.d.ts +3 -0
  26. package/dist/cjs/cli/related.js +279 -0
  27. package/dist/cjs/cli/related.js.map +1 -0
  28. package/dist/cjs/cli/search.js +3 -7
  29. package/dist/cjs/cli/search.js.map +1 -1
  30. package/dist/cjs/cli/shared.d.cts +3 -1
  31. package/dist/cjs/cli/shared.d.ts +3 -1
  32. package/dist/cjs/cli/shared.js +28 -5
  33. package/dist/cjs/cli/shared.js.map +1 -1
  34. package/dist/cjs/cli/sql.d.cts +3 -0
  35. package/dist/cjs/cli/sql.d.ts +3 -0
  36. package/dist/cjs/cli/{query.js → sql.js} +4 -4
  37. package/dist/cjs/cli/sql.js.map +1 -0
  38. package/dist/cjs/cli/status.js +9 -2
  39. package/dist/cjs/cli/status.js.map +1 -1
  40. package/dist/cjs/cli.js +14 -15
  41. package/dist/cjs/cli.js.map +1 -1
  42. package/dist/cjs/commands.d.cts +12 -4
  43. package/dist/cjs/commands.d.ts +12 -4
  44. package/dist/cjs/commands.js +138 -58
  45. package/dist/cjs/commands.js.map +1 -1
  46. package/dist/cjs/config.d.cts +6 -4
  47. package/dist/cjs/config.d.ts +6 -4
  48. package/dist/cjs/config.js +137 -58
  49. package/dist/cjs/config.js.map +1 -1
  50. package/dist/cjs/db.js +14 -34
  51. package/dist/cjs/db.js.map +1 -1
  52. package/dist/cjs/errors.d.cts +1 -1
  53. package/dist/cjs/errors.d.ts +1 -1
  54. package/dist/cjs/errors.js.map +1 -1
  55. package/dist/cjs/features/embed.d.cts +14 -0
  56. package/dist/cjs/features/embed.d.ts +14 -0
  57. package/dist/cjs/features/embed.js +248 -54
  58. package/dist/cjs/features/embed.js.map +1 -1
  59. package/dist/cjs/features/links.js +7 -16
  60. package/dist/cjs/features/links.js.map +1 -1
  61. package/dist/cjs/features/rank.js +2 -6
  62. package/dist/cjs/features/rank.js.map +1 -1
  63. package/dist/cjs/index.d.cts +1 -1
  64. package/dist/cjs/index.d.ts +1 -1
  65. package/dist/cjs/index.js.map +1 -1
  66. package/dist/cjs/output.js +11 -14
  67. package/dist/cjs/output.js.map +1 -1
  68. package/dist/cjs/progress.js +2 -4
  69. package/dist/cjs/progress.js.map +1 -1
  70. package/dist/cjs/scan.d.cts +1 -0
  71. package/dist/cjs/scan.d.ts +1 -0
  72. package/dist/cjs/scan.js +67 -32
  73. package/dist/cjs/scan.js.map +1 -1
  74. package/dist/cjs/search-error.js +4 -6
  75. package/dist/cjs/search-error.js.map +1 -1
  76. package/dist/cjs/traverse.d.cts +8 -0
  77. package/dist/cjs/traverse.d.ts +8 -0
  78. package/dist/cjs/traverse.js +97 -0
  79. package/dist/cjs/traverse.js.map +1 -0
  80. package/dist/esm/cli/check.js +7 -13
  81. package/dist/esm/cli/check.js.map +1 -1
  82. package/dist/esm/cli/download.d.ts +3 -0
  83. package/dist/esm/cli/download.js +27 -0
  84. package/dist/esm/cli/download.js.map +1 -0
  85. package/dist/esm/cli/index.d.ts +6 -3
  86. package/dist/esm/cli/index.js +13 -7
  87. package/dist/esm/cli/index.js.map +1 -1
  88. package/dist/esm/cli/init.js +4 -1
  89. package/dist/esm/cli/init.js.map +1 -1
  90. package/dist/esm/cli/map.js +3 -2
  91. package/dist/esm/cli/map.js.map +1 -1
  92. package/dist/esm/cli/named.js +10 -16
  93. package/dist/esm/cli/named.js.map +1 -1
  94. package/dist/esm/cli/path.d.ts +3 -0
  95. package/dist/esm/cli/path.js +51 -0
  96. package/dist/esm/cli/path.js.map +1 -0
  97. package/dist/esm/cli/peek.js +4 -2
  98. package/dist/esm/cli/peek.js.map +1 -1
  99. package/dist/esm/cli/related.d.ts +3 -0
  100. package/dist/esm/cli/related.js +26 -0
  101. package/dist/esm/cli/related.js.map +1 -0
  102. package/dist/esm/cli/search.js +2 -5
  103. package/dist/esm/cli/search.js.map +1 -1
  104. package/dist/esm/cli/shared.d.ts +3 -1
  105. package/dist/esm/cli/shared.js +28 -5
  106. package/dist/esm/cli/shared.js.map +1 -1
  107. package/dist/esm/cli/sql.d.ts +3 -0
  108. package/dist/esm/cli/{query.js → sql.js} +4 -4
  109. package/dist/esm/cli/sql.js.map +1 -0
  110. package/dist/esm/cli/status.js +8 -1
  111. package/dist/esm/cli/status.js.map +1 -1
  112. package/dist/esm/cli.js +13 -10
  113. package/dist/esm/cli.js.map +1 -1
  114. package/dist/esm/commands.d.ts +12 -4
  115. package/dist/esm/commands.js +111 -68
  116. package/dist/esm/commands.js.map +1 -1
  117. package/dist/esm/config.d.ts +6 -4
  118. package/dist/esm/config.js +126 -75
  119. package/dist/esm/config.js.map +1 -1
  120. package/dist/esm/db.js +16 -38
  121. package/dist/esm/db.js.map +1 -1
  122. package/dist/esm/errors.d.ts +1 -1
  123. package/dist/esm/errors.js.map +1 -1
  124. package/dist/esm/features/embed.d.ts +14 -0
  125. package/dist/esm/features/embed.js +110 -35
  126. package/dist/esm/features/embed.js.map +1 -1
  127. package/dist/esm/features/links.js +8 -17
  128. package/dist/esm/features/links.js.map +1 -1
  129. package/dist/esm/features/rank.js +2 -6
  130. package/dist/esm/features/rank.js.map +1 -1
  131. package/dist/esm/features/types.js.map +1 -1
  132. package/dist/esm/index.d.ts +1 -1
  133. package/dist/esm/index.js.map +1 -1
  134. package/dist/esm/output.js +13 -16
  135. package/dist/esm/output.js.map +1 -1
  136. package/dist/esm/progress.js +2 -4
  137. package/dist/esm/progress.js.map +1 -1
  138. package/dist/esm/scan.d.ts +1 -0
  139. package/dist/esm/scan.js +41 -34
  140. package/dist/esm/scan.js.map +1 -1
  141. package/dist/esm/search-error.js +4 -6
  142. package/dist/esm/search-error.js.map +1 -1
  143. package/dist/esm/traverse.d.ts +8 -0
  144. package/dist/esm/traverse.js +71 -0
  145. package/dist/esm/traverse.js.map +1 -0
  146. package/package.json +18 -5
  147. package/schema.json +12 -16
  148. package/skills/sense/EXAMPLES.md +13 -22
  149. package/skills/sense/SKILL.md +65 -140
  150. package/skills/sense-setup/EXAMPLES.md +30 -46
  151. package/skills/sense-setup/SKILL.md +22 -83
  152. package/dist/cjs/cli/query.d.cts +0 -3
  153. package/dist/cjs/cli/query.d.ts +0 -3
  154. package/dist/cjs/cli/query.js.map +0 -1
  155. package/dist/esm/cli/query.d.ts +0 -3
  156. package/dist/esm/cli/query.js.map +0 -1
@@ -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 type { FileStat } from '../scan.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst): 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).\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds) plus relative markdown links to .md files.\nfunction extract(_raw: string, body: string): string[] {\n const targets = new Set<string>();\n for (const m of body.matchAll(/\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]*)?\\]\\]/g)) {\n const target = m[1].trim();\n if (target) targets.add(target);\n }\n for (const m of body.matchAll(/\\]\\(([^)]+\\.md)(?:#[^)]*)?\\)/g)) {\n const target = m[1].trim();\n if (target && !/^[a-z]+:\\/\\//i.test(target)) targets.add(target);\n }\n return [...targets];\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 (lexicographically first on ties).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\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 return byBase.get(baseKey(clean))?.[0] ?? null;\n}\n\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n for (const path of files.map((f) => f.relPath).sort()) {\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}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes.\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 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 affected: (a) links whose src was just\n// re-stored (their rows are fresh, dst reset to NULL by store()), (b) links whose target's\n// basename matches a file that appeared or disappeared this reconcile (resolution and\n// lexicographic-tie ambiguity hinge on exactly that set), and (c) links whose dst was a\n// path that just vanished. All three are indexed lookups (src is the links PK's leading\n// column; target_base and dst each have their own index), so this reads only the rows this\n// reconcile could plausibly have changed, not 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}`, 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 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 find's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n return (db.prepare('SELECT src, dst FROM links WHERE dst IS NOT NULL').all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// A removed path's outbound link rows are already gone by the time afterReconcile runs\n// (remove() below deletes them for vanished files AND for reparsed existing files), so\n// afterReconcile can't tell from the links table alone whether deleted rows carried edges.\n// remove() records that here, keyed on the reconcile's own delta object -- state dies with\n// the reconcile, including on ROLLBACK, and distinct reconciles never share it.\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, PRIMARY KEY (src, target))');\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 string[];\n // Stale rows (targets the new parse no longer contains) are real edge removals.\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 stale = db.prepare(`DELETE FROM links WHERE src = ? AND target NOT IN (${placeholders})`).run(path, ...targets);\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) VALUES (?, ?, ?, NULL) ON CONFLICT(src, target) DO UPDATE SET target_base = excluded.target_base');\n for (const target of targets) insert.run(path, target, baseKey(cleanTarget(target)));\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 // Fallback rule: correctness over cleverness once too much of the tree moved in one\n // reconcile -- a full pass is the one guaranteed to match resolveTarget's ambiguity\n // rules exactly. A cold build reparses ~every file, so it clears this threshold on its\n // own without a separate check.\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","extract","_raw","body","targets","Set","matchAll","m","target","trim","add","test","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","has","get","buildByBase","files","Map","map","f","relPath","sort","key","list","push","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","candidates","collect","collectIn","column","keys","i","length","chunk","slice","placeholders","reparsed","values","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","exec","remove","result","Number","changes","store","extracted","stale","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":"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"}
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.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst): 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).\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds) plus relative markdown links to .md files.\nfunction extract(_raw: string, body: string): string[] {\n const targets = new Set<string>();\n for (const m of body.matchAll(/\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]*)?\\]\\]/g)) {\n const target = m[1].trim();\n if (target) targets.add(target);\n }\n for (const m of body.matchAll(/\\]\\(([^)]+\\.md)(?:#[^)]*)?\\)/g)) {\n const target = m[1].trim();\n if (target && !/^[a-z]+:\\/\\//i.test(target)) targets.add(target);\n }\n return [...targets];\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 (lexicographically first on ties).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\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 return byBase.get(baseKey(clean))?.[0] ?? null;\n}\n\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n for (const path of files.map((f) => f.relPath).sort()) {\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}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes.\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 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}`, 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 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 return (db.prepare('SELECT src, dst FROM links WHERE dst IS NOT NULL').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, PRIMARY KEY (src, target))');\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 string[];\n // Stale rows (targets the new parse no longer contains) are real edge removals.\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 stale = db.prepare(`DELETE FROM links WHERE src = ? AND target NOT IN (${placeholders})`).run(path, ...targets);\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) VALUES (?, ?, ?, NULL) ON CONFLICT(src, target) DO UPDATE SET target_base = excluded.target_base');\n for (const target of targets) insert.run(path, target, baseKey(cleanTarget(target)));\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","extract","_raw","body","targets","Set","matchAll","m","target","trim","add","test","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","has","get","buildByBase","files","Map","map","f","relPath","sort","key","list","push","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","candidates","collect","collectIn","column","keys","i","length","chunk","slice","placeholders","reparsed","values","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","exec","remove","result","Number","changes","store","extracted","stale","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":";;;;;;;;;;;QAoHgBA;eAAAA;;QA4BHC;eAAAA;;;4DAhJK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKlB,mFAAmF;AACnF,iFAAiF;AACjF,wBAAwB;AAExB,qGAAqG;AACrG,SAASC,QAAQC,IAAY,EAAEC,IAAY;IACzC,IAAMC,UAAU,IAAIC;QACf,kCAAA,2BAAA;;QAAL,QAAK,YAAWF,KAAKG,QAAQ,CAAC,qEAAzB,SAAA,6BAAA,QAAA,yBAAA,iCAA2E;YAA3E,IAAMC,IAAN;YACH,IAAMC,SAASD,CAAC,CAAC,EAAE,CAACE,IAAI;YACxB,IAAID,QAAQJ,QAAQM,GAAG,CAACF;QAC1B;;QAHK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QAIA,mCAAA,4BAAA;;QAAL,QAAK,aAAWL,KAAKG,QAAQ,CAAC,qDAAzB,UAAA,8BAAA,SAAA,0BAAA,kCAA2D;YAA3D,IAAMC,KAAN;YACH,IAAMC,UAASD,EAAC,CAAC,EAAE,CAACE,IAAI;YACxB,IAAID,WAAU,CAAC,gBAAgBG,IAAI,CAACH,UAASJ,QAAQM,GAAG,CAACF;QAC3D;;QAHK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAIL,OAAQ,qBAAGJ;AACb;AAEA,SAASQ,YAAYJ,MAAc;IACjC,OAAOA,OAAOK,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,2EAA2E;AAC3E,SAASC,cAAcC,GAAW,EAAEZ,MAAc,EAAEa,OAAoB,EAAEC,MAA6B;;QAM9FA;IALP,IAAMC,QAAQX,YAAYJ;IAC1B,IAAMgB,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,QAAQQ,GAAG,CAACD,YAAY,OAAOA;IACrC;IACA,gBAAON,cAAAA,OAAOQ,GAAG,CAAChB,QAAQS,qBAAnBD,kCAAAA,WAA4B,CAAC,EAAE,uCAAI;AAC5C;AAEA,SAASS,YAAYC,KAAiB;IACpC,IAAMV,SAAS,IAAIW;QACd,kCAAA,2BAAA;;QAAL,QAAK,YAAcD,MAAME,GAAG,CAAC,SAACC;mBAAMA,EAAEC,OAAO;WAAEC,IAAI,uBAA9C,SAAA,6BAAA,QAAA,yBAAA,iCAAkD;YAAlD,IAAMtB,OAAN;YACH,IAAMuB,MAAMxB,QAAQC;YACpB,IAAMwB,OAAOjB,OAAOQ,GAAG,CAACQ;YACxB,IAAIC,MAAMA,KAAKC,IAAI,CAACzB;iBACfO,OAAOmB,GAAG,CAACH,KAAK;gBAACvB;aAAK;QAC7B;;QALK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAML,OAAOO;AACT;AAQA,iFAAiF;AACjF,SAASoB,YAAYC,EAAgB,EAAEC,IAAe,EAAEvB,OAAoB,EAAEC,MAA6B;IACzG,IAAMuB,SAASF,GAAGG,OAAO,CAAC;IAC1B,IAAIC,UAAU;QACT,kCAAA,2BAAA;;QAAL,QAAK,YAAaH,yBAAb,SAAA,6BAAA,QAAA,yBAAA,iCAAmB;YAAnB,IAAMI,MAAN;YACH,IAAMC,MAAM9B,cAAc6B,IAAI5B,GAAG,EAAE4B,IAAIxC,MAAM,EAAEa,SAASC;YACxD,IAAI2B,QAAQD,IAAIC,GAAG,EAAE;gBACnBJ,OAAOK,GAAG,CAACD,KAAKD,IAAI5B,GAAG,EAAE4B,IAAIxC,MAAM;gBACnCuC,UAAU;YACZ;QACF;;QANK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAOL,OAAOA;AACT;AAEA,yFAAyF;AACzF,+EAA+E;AAC/E,SAASI,WAAWR,EAAgB,EAAEX,KAAiB;IACrD,IAAMX,UAAU,IAAIhB,IAAI2B,MAAME,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IAClD,IAAMd,SAASS,YAAYC;IAC3B,IAAMY,OAAOD,GAAGG,OAAO,CAAC,sCAAsCM,GAAG;IACjE,OAAOV,YAAYC,IAAIC,MAAMvB,SAASC;AACxC;AAEA,yFAAyF;AACzF,yFAAyF;AACzF,kEAAkE;AAClE,SAAS+B,mBAAmBV,EAAgB,EAAEW,KAAqB;IACjE,IAAMjC,UAAU,IAAIhB,IAAIiD,MAAMtB,KAAK,CAACE,GAAG,CAAC,SAACC;eAAMA,EAAEC,OAAO;;IACxD,IAAMd,SAASS,YAAYuB,MAAMtB,KAAK;IAEtC,IAAMuB,mBAAmB,IAAIlD;QACxB,kCAAA,2BAAA;;QAAL,QAAK,YAAWiD,MAAME,KAAK,qBAAtB,SAAA,6BAAA,QAAA,yBAAA;YAAA,IAAMC,IAAN;YAAwBF,iBAAiB7C,GAAG,CAACI,QAAQ2C;;;QAArD;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QACA,mCAAA,4BAAA;;QAAL,QAAK,aAAWH,MAAMI,QAAQ,qBAAzB,UAAA,8BAAA,SAAA,0BAAA;YAAA,IAAMD,KAAN;YAA2BF,iBAAiB7C,GAAG,CAACI,QAAQ2C;;;QAAxD;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAEL,+EAA+E;IAC/E,IAAME,aAAa,IAAI1B;IACvB,IAAM2B,UAAU,iBAAChB;YACV,kCAAA,2BAAA;;YAAL,QAAK,YAAaA,yBAAb,SAAA,6BAAA,QAAA,yBAAA;gBAAA,IAAMI,MAAN;gBAAmBW,WAAWlB,GAAG,CAAC,AAAC,GAAcO,OAAZA,IAAI5B,GAAG,EAAC,MAAe,OAAX4B,IAAIxC,MAAM,GAAIwC;;;YAA/D;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACA,qFAAqF;IACrF,6CAA6C;IAC7C,IAAMa,YAAY,mBAACC,QAAgBC;QACjC,IAAK,IAAIC,IAAI,GAAGA,IAAID,KAAKE,MAAM,EAAED,KAAK,IAAK;gBAGjCrB;YAFR,IAAMuB,QAAQH,KAAKI,KAAK,CAACH,GAAGA,IAAI;YAChC,IAAMI,eAAeF,MAAMhC,GAAG,CAAC;uBAAM;eAAKR,IAAI,CAAC;YAC/CkC,QAAQjB,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,4CAAyDsB,OAAdN,QAAO,SAAoB,OAAbM,cAAa,OAAIhB,GAAG,OAAzFT,aAA0F,qBAAGuB;QACvG;IACF;IAEAL,UAAU,OAAOP,MAAMe,QAAQ;IAC/BR,UAAU,eAAgB,qBAAGN;IAC7BM,UAAU,OAAOP,MAAMI,QAAQ;IAE/B,OAAOhB,YAAYC,IAAK,qBAAGgB,WAAWW,MAAM,KAAKjD,SAASC;AAC5D;AAGO,SAASvB,UAAU4C,EAAgB;IACxC,OAAO,AAACA,GAAGG,OAAO,CAAC,oDAAoDM,GAAG,GAA2ClB,GAAG,CAAC,SAACqC;eAAM;YAACA,EAAEnD,GAAG;YAAEmD,EAAEtB,GAAG;SAAC;;AAChJ;AAEA,4FAA4F;AAC5F,+FAA+F;AAC/F,IAAMuB,mBAAmB,IAAIC;AAE7B,SAASC,cAAcpB,KAAqB,EAAEvC,IAAY;IACxD,IAAI0B,MAAM+B,iBAAiB1C,GAAG,CAACwB;IAC/B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIpC;QACVmE,iBAAiB/B,GAAG,CAACa,OAAOb;IAC9B;IACAA,IAAI/B,GAAG,CAACK;AACV;AAEA,qFAAqF;AACrF,IAAM4D,eAAe,IAAIF;AACzB,SAASG,YAAYtB,KAAqB;IACxC,IAAIb,MAAMkC,aAAa7C,GAAG,CAACwB;IAC3B,IAAI,CAACb,KAAK;QACRA,MAAM,IAAIpC,IAAIiD,MAAMI,QAAQ;QAC5BiB,aAAalC,GAAG,CAACa,OAAOb;IAC1B;IACA,OAAOA;AACT;AAEO,IAAMzC,QAAiB;IAC5B6E,MAAM;IACNC,QAAAA,SAAAA,OAAOnC,EAAE;QACPA,GAAGoC,IAAI,CAAC;QACRpC,GAAGoC,IAAI,CAAC;QACRpC,GAAGoC,IAAI,CAAC;IACV;IACA9E,SAAAA;IACA,gFAAgF;IAChF,uFAAuF;IACvF,kFAAkF;IAClF+E,QAAAA,SAAAA,OAAOrC,EAAE,EAAE5B,IAAI,EAAEuC,KAAK;QACpB,IAAI,CAACsB,YAAYtB,OAAOzB,GAAG,CAACd,OAAO;QACnC,IAAMkE,SAAStC,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACnC;QACjE,IAAImE,OAAOD,OAAOE,OAAO,IAAI,GAAGT,cAAcpB,OAAOvC;IACvD;IACAqE,OAAAA,SAAAA,MAAMzC,EAAE,EAAE5B,IAAI,EAAEsE,SAAS,EAAE/B,KAAK;QAC9B,IAAMlD,UAAUiF;QAChB,gFAAgF;QAChF,IAAIC;QACJ,IAAIlF,QAAQ6D,MAAM,KAAK,GAAG;YACxBqB,QAAQ3C,GAAGG,OAAO,CAAC,mCAAmCI,GAAG,CAACnC;QAC5D,OAAO;gBAEG4B;YADR,IAAMyB,eAAehE,QAAQ8B,GAAG,CAAC;uBAAM;eAAKR,IAAI,CAAC;YACjD4D,QAAQ3C,CAAAA,cAAAA,GAAGG,OAAO,CAAC,AAAC,sDAAkE,OAAbsB,cAAa,OAAIlB,GAAG,OAArFP,aAAAA;gBAAsF5B;aAAiB,CAAvG4B,OAA4F,qBAAGvC;QACzG;QACA,IAAI8E,OAAOI,MAAMH,OAAO,IAAI,GAAGT,cAAcpB,OAAOvC;QACpD,oFAAoF;QACpF,sEAAsE;QACtE,IAAMwE,SAAS5C,GAAGG,OAAO,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAgB1C,4BAAhB,SAAA,6BAAA,QAAA,yBAAA;gBAAA,IAAMI,SAAN;gBAAyB+E,OAAOrC,GAAG,CAACnC,MAAMP,QAAQM,QAAQF,YAAYJ;;;YAAtE;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;IACAgF,gBAAAA,SAAAA,eAAe7C,EAAE,EAAEW,KAAK;;YAIEkB;QAHxB,mFAAmF;QACnF,+EAA+E;QAC/E,iEAAiE;QACjE,IAAMiB,iBAAiB,UAACjB,wBAAAA,iBAAiB1C,GAAG,CAACwB,oBAArBkB,4CAAAA,sBAA6BkB,IAAI,uCAAI,KAAK;QAElE,IAAMC,QAAQrC,MAAMe,QAAQ,CAACJ,MAAM,GAAGX,MAAMI,QAAQ,CAACO,MAAM;QAC3D,+EAA+E;QAC/E,iFAAiF;QACjF,IAAM2B,QAAQtC,MAAMtB,KAAK,CAACiC,MAAM,KAAK,KAAK0B,QAAQ,MAAMrC,MAAMtB,KAAK,CAACiC,MAAM;QAE1E,IAAM4B,aAAaD,QAAQzC,WAAWR,IAAIW,MAAMtB,KAAK,IAAIqB,mBAAmBV,IAAIW;QAChFA,MAAMwC,YAAY,GAAGD,cAAcJ;IACrC;AACF"}
@@ -65,12 +65,8 @@ var rank = {
65
65
  })) db.exec('ALTER TABLE frontmatter ADD COLUMN "_rank" REAL');
66
66
  },
67
67
  afterReconcile: function afterReconcile(db, delta) {
68
- // links.afterReconcile runs first (registry order) and sets delta.linksChanged;
69
- // `undefined` (links off, or no opinion) recomputes, same as always -- only an
70
- // explicit `false` allows a skip. Rank also depends on the node set (every note is a
71
- // PageRank node, edges or not), so any add or vanish recomputes even with edges
72
- // untouched -- otherwise a new linkless note keeps _rank NULL and every other rank
73
- // stays normalized over the old count.
68
+ // Only an explicit linksChanged:false skips. Rank also depends on the node set, so any
69
+ // add or vanish recomputes -- otherwise a new linkless note keeps _rank NULL.
74
70
  if (delta.linksChanged === false && delta.added.length === 0 && delta.vanished.length === 0) return;
75
71
  var nodes = db.prepare('SELECT "path" FROM frontmatter').all().map(function(r) {
76
72
  return r.path;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/rank.ts"],"sourcesContent":["import { pagerank } from '../graph.ts';\nimport { linkEdges } from './links.ts';\nimport type { Feature } from './types.ts';\n\n// frontmatter._rank: PageRank over resolved links -- a static \"how load-bearing is this\n// note\" prior. Depends on links (enforced in config.featureEnabled).\n\nexport const rank: Feature = {\n name: 'rank',\n schema(db) {\n const columns = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n if (!columns.some((c) => c.name === '_rank')) db.exec('ALTER TABLE frontmatter ADD COLUMN \"_rank\" REAL');\n },\n afterReconcile(db, delta) {\n // links.afterReconcile runs first (registry order) and sets delta.linksChanged;\n // `undefined` (links off, or no opinion) recomputes, same as always -- only an\n // explicit `false` allows a skip. Rank also depends on the node set (every note is a\n // PageRank node, edges or not), so any add or vanish recomputes even with edges\n // untouched -- otherwise a new linkless note keeps _rank NULL and every other rank\n // stays normalized over the old count.\n if (delta.linksChanged === false && delta.added.length === 0 && delta.vanished.length === 0) return;\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const ranks = pagerank(nodes, linkEdges(db));\n const update = db.prepare('UPDATE frontmatter SET \"_rank\" = ? WHERE \"path\" = ?');\n for (const [path, value] of ranks) update.run(value, path);\n },\n};\n"],"names":["rank","name","schema","db","columns","prepare","all","some","c","exec","afterReconcile","delta","linksChanged","added","length","vanished","nodes","map","r","path","ranks","pagerank","linkEdges","update","value","run"],"mappings":";;;;+BAOaA;;;eAAAA;;;uBAPY;uBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMnB,IAAMA,OAAgB;IAC3BC,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACP,IAAMC,UAAUD,GAAGE,OAAO,CAAC,kCAAkCC,GAAG;QAChE,IAAI,CAACF,QAAQG,IAAI,CAAC,SAACC;mBAAMA,EAAEP,IAAI,KAAK;YAAUE,GAAGM,IAAI,CAAC;IACxD;IACAC,gBAAAA,SAAAA,eAAeP,EAAE,EAAEQ,KAAK;QACtB,gFAAgF;QAChF,+EAA+E;QAC/E,qFAAqF;QACrF,gFAAgF;QAChF,mFAAmF;QACnF,uCAAuC;QACvC,IAAIA,MAAMC,YAAY,KAAK,SAASD,MAAME,KAAK,CAACC,MAAM,KAAK,KAAKH,MAAMI,QAAQ,CAACD,MAAM,KAAK,GAAG;QAC7F,IAAME,QAAQ,AAACb,GAAGE,OAAO,CAAC,kCAAkCC,GAAG,GAA+BW,GAAG,CAAC,SAACC;mBAAMA,EAAEC,IAAI;;QAC/G,IAAMC,QAAQC,IAAAA,iBAAQ,EAACL,OAAOM,IAAAA,kBAAS,EAACnB;QACxC,IAAMoB,SAASpB,GAAGE,OAAO,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAuBe,0BAAvB,SAAA,6BAAA,QAAA,yBAAA;gBAAA,mCAAA,iBAAOD,uBAAMK;gBAAiBD,OAAOE,GAAG,CAACD,OAAOL;;;YAAhD;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/rank.ts"],"sourcesContent":["import { pagerank } from '../graph.ts';\nimport { linkEdges } from './links.ts';\nimport type { Feature } from './types.ts';\n\n// frontmatter._rank: PageRank over resolved links -- a static \"how load-bearing is this\n// note\" prior. Depends on links (enforced in config.featureEnabled).\n\nexport const rank: Feature = {\n name: 'rank',\n schema(db) {\n const columns = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n if (!columns.some((c) => c.name === '_rank')) db.exec('ALTER TABLE frontmatter ADD COLUMN \"_rank\" REAL');\n },\n afterReconcile(db, delta) {\n // Only an explicit linksChanged:false skips. Rank also depends on the node set, so any\n // add or vanish recomputes -- otherwise a new linkless note keeps _rank NULL.\n if (delta.linksChanged === false && delta.added.length === 0 && delta.vanished.length === 0) return;\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const ranks = pagerank(nodes, linkEdges(db));\n const update = db.prepare('UPDATE frontmatter SET \"_rank\" = ? WHERE \"path\" = ?');\n for (const [path, value] of ranks) update.run(value, path);\n },\n};\n"],"names":["rank","name","schema","db","columns","prepare","all","some","c","exec","afterReconcile","delta","linksChanged","added","length","vanished","nodes","map","r","path","ranks","pagerank","linkEdges","update","value","run"],"mappings":";;;;+BAOaA;;;eAAAA;;;uBAPY;uBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMnB,IAAMA,OAAgB;IAC3BC,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACP,IAAMC,UAAUD,GAAGE,OAAO,CAAC,kCAAkCC,GAAG;QAChE,IAAI,CAACF,QAAQG,IAAI,CAAC,SAACC;mBAAMA,EAAEP,IAAI,KAAK;YAAUE,GAAGM,IAAI,CAAC;IACxD;IACAC,gBAAAA,SAAAA,eAAeP,EAAE,EAAEQ,KAAK;QACtB,uFAAuF;QACvF,8EAA8E;QAC9E,IAAIA,MAAMC,YAAY,KAAK,SAASD,MAAME,KAAK,CAACC,MAAM,KAAK,KAAKH,MAAMI,QAAQ,CAACD,MAAM,KAAK,GAAG;QAC7F,IAAME,QAAQ,AAACb,GAAGE,OAAO,CAAC,kCAAkCC,GAAG,GAA+BW,GAAG,CAAC,SAACC;mBAAMA,EAAEC,IAAI;;QAC/G,IAAMC,QAAQC,IAAAA,iBAAQ,EAACL,OAAOM,IAAAA,kBAAS,EAACnB;QACxC,IAAMoB,SAASpB,GAAGE,OAAO,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAuBe,0BAAvB,SAAA,6BAAA,QAAA,yBAAA;gBAAA,mCAAA,iBAAOD,uBAAMK;gBAAiBD,OAAOE,GAAG,CAACD,OAAOL;;;YAAhD;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;AACF"}
@@ -1,6 +1,6 @@
1
1
  export type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands.js';
2
2
  export { mapTree, peek, presetCoverage, search } from './commands.js';
3
- export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch } from './config.js';
3
+ export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config.js';
4
4
  export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config.js';
5
5
  export type { OpenResult } from './db.js';
6
6
  export { open, rebuild } from './db.js';
@@ -1,6 +1,6 @@
1
1
  export type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands.js';
2
2
  export { mapTree, peek, presetCoverage, search } from './commands.js';
3
- export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch } from './config.js';
3
+ export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config.js';
4
4
  export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config.js';
5
5
  export type { OpenResult } from './db.js';
6
6
  export { open, rebuild } from './db.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/index.ts"],"sourcesContent":["// Public library API. Deliberately small: every export is a stability promise;\n// internals (feature registry, graph, scan, meta) stay module-private.\n\nexport type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands.ts';\nexport { mapTree, peek, presetCoverage, search } from './commands.ts';\nexport type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch } from './config.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config.ts';\nexport type { OpenResult } from './db.ts';\nexport { open, rebuild } from './db.ts';\nexport type { SenseErrorCode } from './errors.ts';\nexport { SenseError } from './errors.ts';\nexport type { Row } from './output.ts';\nexport { printRows } from './output.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["CONFIG_FILENAME","STATE_DIR","SUPPORTED_CONFIG_VERSION","SenseError","initConfig","loadConfig","mapTree","migrateConfig","open","peek","presetCoverage","printRows","rebuild","runWatch","search"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;;;;;;;;;;;;QAK9DA;eAAAA,yBAAe;;QAAyCC;eAAAA,mBAAS;;QAAEC;eAAAA,kCAAwB;;QAI3FC;eAAAA,oBAAU;;QAJOC;eAAAA,oBAAU;;QAAEC;eAAAA,oBAAU;;QAFvCC;eAAAA,mBAAO;;QAEkCC;eAAAA,uBAAa;;QAEtDC;eAAAA,UAAI;;QAJKC;eAAAA,gBAAI;;QAAEC;eAAAA,0BAAc;;QAQ7BC;eAAAA,mBAAS;;QAJHC;eAAAA,aAAO;;QAObC;eAAAA,iBAAQ;;QAXuBC;eAAAA,kBAAM;;;0BAAQ;wBAEsD;oBAE9E;wBAEH;wBAED;uBAGD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/index.ts"],"sourcesContent":["// Public library API. Deliberately small: every export is a stability promise;\n// internals (feature registry, graph, scan, meta) stay module-private.\n\nexport type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands.ts';\nexport { mapTree, peek, presetCoverage, search } from './commands.ts';\nexport type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config.ts';\nexport type { OpenResult } from './db.ts';\nexport { open, rebuild } from './db.ts';\nexport type { SenseErrorCode } from './errors.ts';\nexport { SenseError } from './errors.ts';\nexport type { Row } from './output.ts';\nexport { printRows } from './output.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["CONFIG_FILENAME","STATE_DIR","SUPPORTED_CONFIG_VERSION","SenseError","initConfig","loadConfig","mapTree","migrateConfig","open","peek","presetCoverage","printRows","rebuild","runWatch","search"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;;;;;;;;;;;;QAK9DA;eAAAA,yBAAe;;QAAyCC;eAAAA,mBAAS;;QAAEC;eAAAA,kCAAwB;;QAI3FC;eAAAA,oBAAU;;QAJOC;eAAAA,oBAAU;;QAAEC;eAAAA,oBAAU;;QAFvCC;eAAAA,mBAAO;;QAEkCC;eAAAA,uBAAa;;QAEtDC;eAAAA,UAAI;;QAJKC;eAAAA,gBAAI;;QAAEC;eAAAA,0BAAc;;QAQ7BC;eAAAA,mBAAS;;QAJHC;eAAAA,aAAO;;QAObC;eAAAA,iBAAQ;;QAXuBC;eAAAA,kBAAM;;;0BAAQ;wBAEsD;oBAE9E;wBAEH;wBAED;uBAGD"}
@@ -100,11 +100,8 @@ function printRows(rows, format) {
100
100
  console.warn("warning: result is ".concat(Math.round(rendered.length / 1000), " KB across ").concat(rows.length, " row(s); consider a LIMIT, fewer columns, or snippet() instead of whole values"));
101
101
  }
102
102
  }
103
- // Table output is for reading; a row wider than the terminal wraps into an unreadable
104
- // block (a `find` header measured 330 chars once `summary` is present, which drove one
105
- // user to pipe --format json through a script for every interactive query). Widest
106
- // columns give up space first, down to a floor, and only as far as the terminal needs.
107
- // --format json is never truncated: it is the machine-readable surface.
103
+ // Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns
104
+ // give up space first, down to a floor. --format json is never truncated.
108
105
  var MIN_COLUMN = 12;
109
106
  var TRUNCATED = '…';
110
107
  function fitWidths(widths, limit) {
@@ -156,7 +153,7 @@ function renderRows(rows, format) {
156
153
  ].concat(_to_consumable_array(cells.map(formatRow))).join('\n');
157
154
  }
158
155
  function featureOffHint(name) {
159
- return name === 'embed' ? 'no preset has semantic on' : "features.".concat(name);
156
+ return "features.".concat(name);
160
157
  }
161
158
  function featuresLine(features) {
162
159
  var off = features.off.length > 0 ? " \xb7 off: ".concat(features.off.map(function(name) {
@@ -236,15 +233,15 @@ function renderPeek(result) {
236
233
  } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');
237
234
  if (result.off.includes('links')) {
238
235
  lines.push('', 'links: off (features.links)');
239
- return lines.join('\n');
236
+ } else {
237
+ var linkLine = function linkLine(label, shown, total) {
238
+ if (total > 0) lines.push("".concat(label, " (").concat(total, "): ").concat(shown.join(', ')).concat(total > shown.length ? ", +".concat(total - shown.length, " more") : ''));
239
+ };
240
+ if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');
241
+ linkLine('links out', result.outbound, result.outboundTotal);
242
+ linkLine('unresolved', result.unresolved, result.unresolvedTotal);
243
+ linkLine('backlinks', result.backlinks, result.backlinksTotal);
240
244
  }
241
- var linkLine = function linkLine(label, shown, total) {
242
- if (total > 0) lines.push("".concat(label, " (").concat(total, "): ").concat(shown.join(', ')).concat(total > shown.length ? ", +".concat(total - shown.length, " more") : ''));
243
- };
244
- if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');
245
- linkLine('links out', result.outbound, result.outboundTotal);
246
- linkLine('unresolved', result.unresolved, result.unresolvedTotal);
247
- linkLine('backlinks', result.backlinks, result.backlinksTotal);
248
245
  return lines.join('\n');
249
246
  }
250
247
  /* 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/output.ts"],"sourcesContent":["// rows -> table (default) | json\n\nexport type Row = Record<string, unknown>;\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\nexport function printRows(rows: Row[], format: 'table' | 'json'): void {\n const rendered = renderRows(rows, format);\n console.log(rendered);\n if (rendered.length > OVERSIZE_BYTES) {\n console.warn(`warning: result is ${Math.round(rendered.length / 1000)} KB across ${rows.length} row(s); consider a LIMIT, fewer columns, or snippet() instead of whole values`);\n }\n}\n\n// Table output is for reading; a row wider than the terminal wraps into an unreadable\n// block (a `find` header measured 330 chars once `summary` is present, which drove one\n// user to pipe --format json through a script for every interactive query). Widest\n// columns give up space first, down to a floor, and only as far as the terminal needs.\n// --format json is never truncated: it is the machine-readable surface.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: 'table' | 'json', width = process.stdout.columns): string {\n if (format === 'json') return JSON.stringify(rows, null, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// `embed` is not a features-block key (it's derived from presets), so its off hint reads\n// differently from links/sections/rank's plain `features.<name>`.\nexport function featureOffHint(name: string): string {\n return name === 'embed' ? 'no preset has semantic on' : `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number }>): string[] {\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded`)];\n}\n\nexport function renderMap(result: { docs: { count: number; bytes: number }; fields: Row[]; fieldsTotal: number; features: { on: string[]; off: string[] }; presets: Array<{ name: string; files: number; embedded: number }>; hubs: Row[]; recent: Row[] }): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:', renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n return lines.join('\\n');\n }\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n return lines.join('\\n');\n}\n"],"names":["featureOffHint","featuresLine","presetsLines","printRows","renderMap","renderPeek","OVERSIZE_BYTES","cell","value","String","replace","rows","format","rendered","renderRows","console","log","length","warn","Math","round","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","process","stdout","columns","JSON","stringify","Object","keys","cells","map","row","col","natural","i","clip","w","padEnd","slice","formatRow","values","join","trimEnd","repeat","name","features","off","on","presets","p","files","embedded","result","parts","docs","count","bytes","fields","fieldsTotal","push","hubs","includes","recent","lines","path","tokens","entries","frontmatter","key","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output.ts"],"sourcesContent":["// rows -> table (default) | json\n\nexport type Row = Record<string, unknown>;\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\nexport function printRows(rows: Row[], format: 'table' | 'json'): void {\n const rendered = renderRows(rows, format);\n console.log(rendered);\n if (rendered.length > OVERSIZE_BYTES) {\n console.warn(`warning: result is ${Math.round(rendered.length / 1000)} KB across ${rows.length} row(s); consider a LIMIT, fewer columns, or snippet() instead of whole values`);\n }\n}\n\n// Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns\n// give up space first, down to a floor. --format json is never truncated.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: 'table' | 'json', width = process.stdout.columns): string {\n if (format === 'json') return JSON.stringify(rows, null, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// Names the config key that turns a feature back on. Only the three features-block toggles\n// reach here; `embed` has its own status line.\nexport function featureOffHint(name: string): string {\n return `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number }>): string[] {\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded`)];\n}\n\nexport function renderMap(result: { docs: { count: number; bytes: number }; fields: Row[]; fieldsTotal: number; features: { on: string[]; off: string[] }; presets: Array<{ name: string; files: number; embedded: number }>; hubs: Row[]; recent: Row[] }): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:', renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n } else {\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n }\n return lines.join('\\n');\n}\n"],"names":["featureOffHint","featuresLine","presetsLines","printRows","renderMap","renderPeek","OVERSIZE_BYTES","cell","value","String","replace","rows","format","rendered","renderRows","console","log","length","warn","Math","round","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","process","stdout","columns","JSON","stringify","Object","keys","cells","map","row","col","natural","i","clip","w","padEnd","slice","formatRow","values","join","trimEnd","repeat","name","features","off","on","presets","p","files","embedded","result","parts","docs","count","bytes","fields","fieldsTotal","push","hubs","includes","recent","lines","path","tokens","entries","frontmatter","key","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
@@ -8,10 +8,8 @@ Object.defineProperty(exports, "progress", {
8
8
  return progress;
9
9
  }
10
10
  });
11
- // Long-work progress on stderr only: stdout stays the parseable answer, so progress can
12
- // never corrupt --format json (the same channel warnings already use). TTY-aware: a human
13
- // terminal gets one self-overwriting line; a non-TTY consumer (agent, pipe, CI) gets at
14
- // most a few append-only lines -- an agent transcript gains lines, not redraw frames.
11
+ // Progress on stderr only, so it can never corrupt --format json. A TTY gets one
12
+ // self-overwriting line; a pipe gets a few append-only ones, not redraw frames.
15
13
  var NON_TTY_MILESTONES = 4;
16
14
  function progress(label, total) {
17
15
  var threshold = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 200;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/progress.ts"],"sourcesContent":["// Long-work progress on stderr only: stdout stays the parseable answer, so progress can\n// never corrupt --format json (the same channel warnings already use). TTY-aware: a human\n// terminal gets one self-overwriting line; a non-TTY consumer (agent, pipe, CI) gets at\n// most a few append-only lines -- an agent transcript gains lines, not redraw frames.\n\nconst NON_TTY_MILESTONES = 4;\n\nexport interface Progress {\n tick(done: number): void;\n finish(): void;\n}\n\n// `total` known upfront; `label` names the work (\"embedding notes\", \"reparsing files\").\n// Silent entirely below `threshold` -- short work needs no narration.\nexport function progress(label: string, total: number, threshold = 200): Progress {\n if (total < threshold) return { tick() {}, finish() {} };\n\n const started = Date.now();\n const isTTY = process.stderr.isTTY === true;\n let lastShown = 0;\n let wrote = false;\n\n const line = (done: number) => {\n const elapsed = (Date.now() - started) / 1000;\n // Estimate from observed rate, only once there is a rate to observe.\n const eta = done > 0 && done < total ? `, ~${Math.max(1, Math.round((elapsed / done) * (total - done)))}s left` : '';\n return `${label}: ${done}/${total}${eta}`;\n };\n\n return {\n tick(done: number) {\n if (isTTY) {\n process.stderr.write(`\\r${line(done)}`);\n wrote = true;\n return;\n }\n // Sparse milestones: first tick, then every 1/NON_TTY_MILESTONES of the total.\n if (done === lastShown) return;\n if (lastShown === 0 || done - lastShown >= total / NON_TTY_MILESTONES) {\n process.stderr.write(`${line(done)}\\n`);\n lastShown = done;\n wrote = true;\n }\n },\n finish() {\n if (!wrote) return;\n if (isTTY) process.stderr.write(`\\r${line(total)}\\n`);\n else if (lastShown < total) process.stderr.write(`${line(total)}\\n`);\n },\n };\n}\n"],"names":["progress","NON_TTY_MILESTONES","label","total","threshold","tick","finish","started","Date","now","isTTY","process","stderr","lastShown","wrote","line","done","elapsed","eta","Math","max","round","write"],"mappings":";;;;+BAcgBA;;;eAAAA;;;AAdhB,wFAAwF;AACxF,0FAA0F;AAC1F,wFAAwF;AACxF,sFAAsF;AAEtF,IAAMC,qBAAqB;AASpB,SAASD,SAASE,KAAa,EAAEC,KAAa;QAAEC,YAAAA,iEAAY;IACjE,IAAID,QAAQC,WAAW,OAAO;QAAEC,MAAAA,SAAAA,QAAQ;QAAGC,QAAAA,SAAAA,UAAU;IAAE;IAEvD,IAAMC,UAAUC,KAAKC,GAAG;IACxB,IAAMC,QAAQC,QAAQC,MAAM,CAACF,KAAK,KAAK;IACvC,IAAIG,YAAY;IAChB,IAAIC,QAAQ;IAEZ,IAAMC,OAAO,cAACC;QACZ,IAAMC,UAAU,AAACT,CAAAA,KAAKC,GAAG,KAAKF,OAAM,IAAK;QACzC,qEAAqE;QACrE,IAAMW,MAAMF,OAAO,KAAKA,OAAOb,QAAQ,AAAC,MAAgE,OAA3DgB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAAC,AAACJ,UAAUD,OAASb,CAAAA,QAAQa,IAAG,KAAK,YAAU;QAClH,OAAO,AAAC,GAAYA,OAAVd,OAAM,MAAYC,OAARa,MAAK,KAAWE,OAARf,OAAY,OAAJe;IACtC;IAEA,OAAO;QACLb,MAAAA,SAAAA,KAAKW,IAAY;YACf,IAAIN,OAAO;gBACTC,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,KAAe,OAAXP,KAAKC;gBAC/BF,QAAQ;gBACR;YACF;YACA,+EAA+E;YAC/E,IAAIE,SAASH,WAAW;YACxB,IAAIA,cAAc,KAAKG,OAAOH,aAAaV,QAAQF,oBAAoB;gBACrEU,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,GAAa,OAAXP,KAAKC,OAAM;gBACnCH,YAAYG;gBACZF,QAAQ;YACV;QACF;QACAR,QAAAA,SAAAA;YACE,IAAI,CAACQ,OAAO;YACZ,IAAIJ,OAAOC,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,KAAgB,OAAZP,KAAKZ,QAAO;iBAC5C,IAAIU,YAAYV,OAAOQ,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,GAAc,OAAZP,KAAKZ,QAAO;QAClE;IACF;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/progress.ts"],"sourcesContent":["// Progress on stderr only, so it can never corrupt --format json. A TTY gets one\n// self-overwriting line; a pipe gets a few append-only ones, not redraw frames.\n\nconst NON_TTY_MILESTONES = 4;\n\nexport interface Progress {\n tick(done: number): void;\n finish(): void;\n}\n\n// `total` known upfront; `label` names the work (\"embedding notes\", \"reparsing files\").\n// Silent entirely below `threshold` -- short work needs no narration.\nexport function progress(label: string, total: number, threshold = 200): Progress {\n if (total < threshold) return { tick() {}, finish() {} };\n\n const started = Date.now();\n const isTTY = process.stderr.isTTY === true;\n let lastShown = 0;\n let wrote = false;\n\n const line = (done: number) => {\n const elapsed = (Date.now() - started) / 1000;\n // Estimate from observed rate, only once there is a rate to observe.\n const eta = done > 0 && done < total ? `, ~${Math.max(1, Math.round((elapsed / done) * (total - done)))}s left` : '';\n return `${label}: ${done}/${total}${eta}`;\n };\n\n return {\n tick(done: number) {\n if (isTTY) {\n process.stderr.write(`\\r${line(done)}`);\n wrote = true;\n return;\n }\n // Sparse milestones: first tick, then every 1/NON_TTY_MILESTONES of the total.\n if (done === lastShown) return;\n if (lastShown === 0 || done - lastShown >= total / NON_TTY_MILESTONES) {\n process.stderr.write(`${line(done)}\\n`);\n lastShown = done;\n wrote = true;\n }\n },\n finish() {\n if (!wrote) return;\n if (isTTY) process.stderr.write(`\\r${line(total)}\\n`);\n else if (lastShown < total) process.stderr.write(`${line(total)}\\n`);\n },\n };\n}\n"],"names":["progress","NON_TTY_MILESTONES","label","total","threshold","tick","finish","started","Date","now","isTTY","process","stderr","lastShown","wrote","line","done","elapsed","eta","Math","max","round","write"],"mappings":";;;;+BAYgBA;;;eAAAA;;;AAZhB,iFAAiF;AACjF,gFAAgF;AAEhF,IAAMC,qBAAqB;AASpB,SAASD,SAASE,KAAa,EAAEC,KAAa;QAAEC,YAAAA,iEAAY;IACjE,IAAID,QAAQC,WAAW,OAAO;QAAEC,MAAAA,SAAAA,QAAQ;QAAGC,QAAAA,SAAAA,UAAU;IAAE;IAEvD,IAAMC,UAAUC,KAAKC,GAAG;IACxB,IAAMC,QAAQC,QAAQC,MAAM,CAACF,KAAK,KAAK;IACvC,IAAIG,YAAY;IAChB,IAAIC,QAAQ;IAEZ,IAAMC,OAAO,cAACC;QACZ,IAAMC,UAAU,AAACT,CAAAA,KAAKC,GAAG,KAAKF,OAAM,IAAK;QACzC,qEAAqE;QACrE,IAAMW,MAAMF,OAAO,KAAKA,OAAOb,QAAQ,AAAC,MAAgE,OAA3DgB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAAC,AAACJ,UAAUD,OAASb,CAAAA,QAAQa,IAAG,KAAK,YAAU;QAClH,OAAO,AAAC,GAAYA,OAAVd,OAAM,MAAYC,OAARa,MAAK,KAAWE,OAARf,OAAY,OAAJe;IACtC;IAEA,OAAO;QACLb,MAAAA,SAAAA,KAAKW,IAAY;YACf,IAAIN,OAAO;gBACTC,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,KAAe,OAAXP,KAAKC;gBAC/BF,QAAQ;gBACR;YACF;YACA,+EAA+E;YAC/E,IAAIE,SAASH,WAAW;YACxB,IAAIA,cAAc,KAAKG,OAAOH,aAAaV,QAAQF,oBAAoB;gBACrEU,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,GAAa,OAAXP,KAAKC,OAAM;gBACnCH,YAAYG;gBACZF,QAAQ;YACV;QACF;QACAR,QAAAA,SAAAA;YACE,IAAI,CAACQ,OAAO;YACZ,IAAIJ,OAAOC,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,KAAgB,OAAZP,KAAKZ,QAAO;iBAC5C,IAAIU,YAAYV,OAAOQ,QAAQC,MAAM,CAACU,KAAK,CAAC,AAAC,GAAc,OAAZP,KAAKZ,QAAO;QAClE;IACF;AACF"}
@@ -9,6 +9,7 @@ export interface FileStat {
9
9
  presets: string[];
10
10
  embed: boolean;
11
11
  }
12
+ export declare function toPosixPath(relPath: string, separator?: string): string;
12
13
  export declare function listFiles(cfg: Config, baseDir: string): FileStat[];
13
14
  export interface ParsedDoc {
14
15
  relPath: string;
@@ -9,6 +9,7 @@ export interface FileStat {
9
9
  presets: string[];
10
10
  embed: boolean;
11
11
  }
12
+ export declare function toPosixPath(relPath: string, separator?: string): string;
12
13
  export declare function listFiles(cfg: Config, baseDir: string): FileStat[];
13
14
  export interface ParsedDoc {
14
15
  relPath: string;
package/dist/cjs/scan.js CHANGED
@@ -17,11 +17,13 @@ _export(exports, {
17
17
  },
18
18
  get parseFile () {
19
19
  return parseFile;
20
+ },
21
+ get toPosixPath () {
22
+ return toPosixPath;
20
23
  }
21
24
  });
22
25
  var _nodefs = require("node:fs");
23
26
  var _nodepath = require("node:path");
24
- var _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
25
27
  var _removemarkdown = /*#__PURE__*/ _interop_require_default(require("remove-markdown"));
26
28
  var _yaml = require("yaml");
27
29
  var _configts = require("./config.js");
@@ -72,31 +74,41 @@ function normalizeText(value) {
72
74
  if (value === null || value === undefined) return '';
73
75
  return String(value).replace(/\s+/g, ' ').trim();
74
76
  }
75
- // Markdown is stripped at index time so snippets read as clean prose (also improves matching, e.g. `**bold**` indexes as `bold`).
76
- // remove-markdown doesn't cover Obsidian wikilinks or table scaffolding, so those are handled first/after.
77
+ // Keeps URL query strings, asset filenames, and HTML attributes out of the index: rare terms
78
+ // carry high IDF, so they outrank prose. remove-markdown misses wikilinks and tables.
77
79
  function stripText(value) {
78
80
  var withoutWikilinks = value.replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, '$2').replace(/\[\[([^\]]+)\]\]/g, '$1');
79
81
  var withoutMarkdown = (0, _removemarkdown.default)(withoutWikilinks);
80
82
  var withoutTables = withoutMarkdown.replace(/^\s*\|?[-\s|:]+\|\s*$/gm, '').replace(/\|/g, ' ');
81
83
  return normalizeText(withoutTables);
82
84
  }
85
+ function toPosixPath(relPath) {
86
+ var separator = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : _nodepath.sep;
87
+ return separator === '\\' ? relPath.split(separator).join('/') : relPath;
88
+ }
89
+ // Every command pays listFiles before it answers (the freshness check stats each file), so
90
+ // per-file work here is the hottest path in the package. Everything derivable from the config
91
+ // alone is computed once, above the loop.
92
+ var NO_THROW = {
93
+ throwIfNoEntry: false
94
+ };
83
95
  function listFiles(cfg, baseDir) {
84
96
  var coverage = new Map();
97
+ var posixNeeded = _nodepath.sep === '\\';
85
98
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
86
99
  try {
87
100
  for(var _iterator = (0, _configts.presetNames)(cfg)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
88
101
  var name = _step.value;
89
- var _preset_exclude;
90
102
  var preset = cfg.presets[name];
91
- var matched = _fastglob.default.sync(preset.include, {
92
- cwd: baseDir,
93
- ignore: (_preset_exclude = preset.exclude) !== null && _preset_exclude !== void 0 ? _preset_exclude : []
94
- });
95
103
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
96
104
  try {
97
- for(var _iterator1 = matched[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
98
- var relPath = _step1.value;
105
+ for(var _iterator1 = (0, _nodefs.globSync)(preset.include, {
106
+ cwd: baseDir,
107
+ exclude: preset.exclude
108
+ })[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
109
+ var matched = _step1.value;
99
110
  var _coverage_get;
111
+ var relPath = posixNeeded ? toPosixPath(matched) : matched;
100
112
  var set = (_coverage_get = coverage.get(relPath)) !== null && _coverage_get !== void 0 ? _coverage_get : new Set();
101
113
  set.add(name);
102
114
  coverage.set(relPath, set);
@@ -130,29 +142,52 @@ function listFiles(cfg, baseDir) {
130
142
  }
131
143
  }
132
144
  }
133
- var relPaths = _to_consumable_array(coverage.keys()).sort();
134
- return relPaths.map(function(relPath) {
135
- var absPath = (0, _nodepath.join)(baseDir, relPath);
136
- var st = (0, _nodefs.statSync)(absPath);
137
- var presets = _to_consumable_array(coverage.get(relPath)).sort();
138
- var embed = presets.some(function(name) {
139
- return (0, _configts.presetSemanticEnabled)(cfg, name);
140
- });
141
- return {
142
- relPath: relPath,
143
- absPath: absPath,
144
- mtimeMs: st.mtimeMs,
145
- size: st.size,
146
- presets: presets,
147
- embed: embed
148
- };
149
- });
145
+ // Which presets want vectors is a property of the config, not of any file.
146
+ var embedding = (0, _configts.embedEnabled)(cfg);
147
+ var semanticPresets = embedding ? new Set((0, _configts.presetNames)(cfg).filter(function(name) {
148
+ return (0, _configts.presetSemanticEnabled)(cfg, name);
149
+ })) : null;
150
+ var files = [];
151
+ var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
152
+ try {
153
+ for(var _iterator2 = _to_consumable_array(coverage.keys()).sort()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
154
+ var relPath1 = _step2.value;
155
+ var absPath = (0, _nodepath.join)(baseDir, relPath1); // join re-applies the platform separator for fs calls
156
+ // node:fs glob matches directories and dangling symlinks; fast-glob returned neither, so
157
+ // one stat filters both back out (throwIfNoEntry keeps a dangling link from throwing).
158
+ var st = (0, _nodefs.statSync)(absPath, NO_THROW);
159
+ if (!(st === null || st === void 0 ? void 0 : st.isFile())) continue;
160
+ var presets = _to_consumable_array(coverage.get(relPath1)).sort();
161
+ var embed = semanticPresets !== null && presets.some(function(name) {
162
+ return semanticPresets.has(name);
163
+ });
164
+ files.push({
165
+ relPath: relPath1,
166
+ absPath: absPath,
167
+ mtimeMs: st.mtimeMs,
168
+ size: st.size,
169
+ presets: presets,
170
+ embed: embed
171
+ });
172
+ }
173
+ } catch (err) {
174
+ _didIteratorError2 = true;
175
+ _iteratorError2 = err;
176
+ } finally{
177
+ try {
178
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
179
+ _iterator2.return();
180
+ }
181
+ } finally{
182
+ if (_didIteratorError2) {
183
+ throw _iteratorError2;
184
+ }
185
+ }
186
+ }
187
+ return files;
150
188
  }
151
- // SQLite storage classes follow the YAML scalar: strings -> TEXT, booleans and whole
152
- // numbers -> INTEGER, fractions -> REAL, everything else (lists, maps) -> JSON TEXT.
153
- // Booleans have no SQLite type of their own; 1/0 is the convention, so `WHERE flag = 1`
154
- // matches and `WHERE flag = 'true'` cannot -- `map` prints the observed type per field so
155
- // that mismatch is visible rather than a silent zero-row answer.
189
+ // Storage class follows the YAML scalar. Booleans store as 1/0, so `WHERE flag = 1` matches
190
+ // and `WHERE flag = 'true'` cannot; `map` prints observed types so the mismatch is visible.
156
191
  function mapValue(value) {
157
192
  if (value === null || value === undefined) return null;
158
193
  if (typeof value === 'boolean') return BigInt(value ? 1 : 0);
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan.ts"],"sourcesContent":["import { readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport fastGlob from 'fast-glob';\nimport removeMarkdown from 'remove-markdown';\nimport { parseDocument } from 'yaml';\nimport type { Config } from './config.ts';\nimport { presetNames, presetSemanticEnabled } from './config.ts';\nimport type { Feature } from './features/types.ts';\n\n// Filesystem -> rows. Pure data in, data + warnings out; db.ts does the SQL.\n\n// Reserved: colliding with these would clash with the `frontmatter` table's own columns or\n// the other tables (`content`, `links`, `sections`). Exported so db.ts's frontmatter upsert\n// can tell a feature-owned column (e.g. rank's `_rank`) apart from a parsed frontmatter\n// column and leave the former alone on reparse.\nexport const RESERVED_COLUMNS = new Set(['path', '_mtime', '_size', '_rank', 'content', 'links', 'sections']);\n\nfunction normalizeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\s+/g, ' ').trim();\n}\n\n// Markdown is stripped at index time so snippets read as clean prose (also improves matching, e.g. `**bold**` indexes as `bold`).\n// remove-markdown doesn't cover Obsidian wikilinks or table scaffolding, so those are handled first/after.\nfunction stripText(value: string): string {\n const withoutWikilinks = value.replace(/\\[\\[([^\\]|]+)\\|([^\\]]+)\\]\\]/g, '$2').replace(/\\[\\[([^\\]]+)\\]\\]/g, '$1');\n const withoutMarkdown = removeMarkdown(withoutWikilinks);\n const withoutTables = withoutMarkdown.replace(/^\\s*\\|?[-\\s|:]+\\|\\s*$/gm, '').replace(/\\|/g, ' ');\n return normalizeText(withoutTables);\n}\n\nexport interface FileStat {\n relPath: string;\n absPath: string;\n mtimeMs: number;\n size: number;\n presets: string[]; // every declared preset covering this file (>= 1; union, overlap allowed)\n embed: boolean; // true iff any covering preset has semantic !== false\n}\n\n// Presets are the file selection: each preset's include/exclude picks its files (fast-glob,\n// tsconfig-standard semantics, resolved relative to baseDir). Unlike the old layer model,\n// presets are views, not partitions -- they may overlap freely, and a file's set of covering\n// presets (not a single owner) is what indexing and embedding derive from. A preset whose\n// globs match nothing is valid. Files matched by no preset at all are simply not indexed.\nexport function listFiles(cfg: Config, baseDir: string): FileStat[] {\n const coverage = new Map<string, Set<string>>();\n for (const name of presetNames(cfg)) {\n const preset = cfg.presets[name];\n const matched = fastGlob.sync(preset.include, { cwd: baseDir, ignore: preset.exclude ?? [] });\n for (const relPath of matched) {\n const set = coverage.get(relPath) ?? new Set<string>();\n set.add(name);\n coverage.set(relPath, set);\n }\n }\n\n const relPaths = [...coverage.keys()].sort();\n return relPaths.map((relPath) => {\n const absPath = join(baseDir, relPath);\n const st = statSync(absPath);\n const presets = [...(coverage.get(relPath) as Set<string>)].sort();\n const embed = presets.some((name) => presetSemanticEnabled(cfg, name));\n return { relPath, absPath, mtimeMs: st.mtimeMs, size: st.size, presets, embed };\n });\n}\n\nexport interface ParsedDoc {\n relPath: string;\n mtimeMs: number;\n size: number;\n presets: string[];\n data: Record<string, string | number | bigint | null>;\n // title/summary are duplicated from frontmatter so bm25() can weight them above the body text.\n search: { title: string; summary: string; text: string };\n // Per-feature extraction results, keyed by feature name; features store them at reconcile.\n extracted: Record<string, unknown>;\n}\n\n// SQLite storage classes follow the YAML scalar: strings -> TEXT, booleans and whole\n// numbers -> INTEGER, fractions -> REAL, everything else (lists, maps) -> JSON TEXT.\n// Booleans have no SQLite type of their own; 1/0 is the convention, so `WHERE flag = 1`\n// matches and `WHERE flag = 'true'` cannot -- `map` prints the observed type per field so\n// that mismatch is visible rather than a silent zero-row answer.\nfunction mapValue(value: unknown): string | number | bigint | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return BigInt(value ? 1 : 0);\n if (typeof value === 'number') return Number.isSafeInteger(value) ? BigInt(value) : value;\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\n// The delimiter split is all this package used gray-matter for.\nfunction splitFrontmatter(raw: string): { fm: string | null; body: string } {\n const open = raw.match(/^---\\r?\\n/);\n if (!open) return { fm: null, body: raw };\n const rest = raw.slice(open[0].length);\n const close = rest.match(/^---\\r?(\\n|$)/m);\n if (!close || close.index === undefined) return { fm: null, body: raw };\n return { fm: rest.slice(0, close.index), body: rest.slice(close.index + close[0].length) };\n}\n\n// Lenient by design: parseDocument collects syntax errors as data and still yields values,\n// so Obsidian-style frontmatter (e.g. an alias starting with @) survives with a warning.\nfunction parseFrontmatter(relPath: string, fm: string, warnings: string[]): Record<string, unknown> {\n const doc = parseDocument(fm);\n if (doc.errors.length > 0) {\n warnings.push(`warning: ${relPath} frontmatter has ${doc.errors.length} syntax error(s) (${doc.errors[0].message.split('\\n')[0]}); parsed leniently`);\n }\n let data: unknown;\n try {\n data = doc.toJS();\n } catch (err) {\n warnings.push(`warning: ${relPath} has unparseable frontmatter (${(err as Error).message.split('\\n')[0]}); indexing without it`);\n return {};\n }\n if (data === null || data === undefined) return {};\n if (typeof data !== 'object' || Array.isArray(data)) {\n warnings.push(`warning: ${relPath} frontmatter is not a key-value mapping; ignoring it`);\n return {};\n }\n return data as Record<string, unknown>;\n}\n\nexport function parseFile(file: FileStat, extractors: Feature[] = []): { doc: ParsedDoc; warnings: string[] } {\n const raw = readFileSync(file.absPath, 'utf8');\n const warnings: string[] = [];\n\n const { fm, body: content } = splitFrontmatter(raw);\n const data = fm === null ? {} : parseFrontmatter(file.relPath, fm, warnings);\n const mapped: Record<string, string | number | bigint | null> = {};\n\n for (const key of Object.keys(data)) {\n if (RESERVED_COLUMNS.has(key)) {\n warnings.push(`warning: ${file.relPath} has a frontmatter key named \"${key}\", which is reserved; ignoring it`);\n continue;\n }\n mapped[key] = mapValue(data[key]);\n }\n\n // title/summary are plain YAML strings -- whitespace-collapse only;\n // the prose gets the full markdown strip.\n const search = { title: normalizeText(data.title), summary: normalizeText(data.summary), text: stripText(content) };\n\n return {\n doc: {\n relPath: file.relPath,\n mtimeMs: file.mtimeMs,\n size: file.size,\n presets: file.presets,\n data: mapped,\n search,\n extracted: Object.fromEntries(extractors.filter((f) => f.extract).map((f) => [f.name, f.extract?.(raw, content, search)])),\n },\n warnings,\n };\n}\n"],"names":["RESERVED_COLUMNS","listFiles","parseFile","Set","normalizeText","value","undefined","String","replace","trim","stripText","withoutWikilinks","withoutMarkdown","removeMarkdown","withoutTables","cfg","baseDir","coverage","Map","presetNames","name","preset","presets","matched","fastGlob","sync","include","cwd","ignore","exclude","relPath","set","get","add","relPaths","keys","sort","map","absPath","join","st","statSync","embed","some","presetSemanticEnabled","mtimeMs","size","mapValue","BigInt","Number","isSafeInteger","JSON","stringify","splitFrontmatter","raw","open","match","fm","body","rest","slice","length","close","index","parseFrontmatter","warnings","doc","parseDocument","errors","push","message","split","data","toJS","err","Array","isArray","file","extractors","readFileSync","content","mapped","Object","key","has","search","title","summary","text","extracted","fromEntries","filter","f","extract"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan.ts"],"sourcesContent":["import { globSync, readFileSync, statSync } from 'node:fs';\nimport { join, sep } from 'node:path';\nimport removeMarkdown from 'remove-markdown';\nimport { parseDocument } from 'yaml';\nimport type { Config } from './config.ts';\nimport { embedEnabled, presetNames, presetSemanticEnabled } from './config.ts';\nimport type { Feature } from './features/types.ts';\n\n// Filesystem -> rows. Pure data in, data + warnings out; db.ts does the SQL.\n\n// Frontmatter keys that would collide with table columns. Exported so db.ts's upsert can tell\n// a feature-owned column (`_rank`) from a parsed one and leave it alone on reparse.\nexport const RESERVED_COLUMNS = new Set(['path', '_mtime', '_size', '_rank', 'content', 'links', 'sections']);\n\nfunction normalizeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\s+/g, ' ').trim();\n}\n\n// Keeps URL query strings, asset filenames, and HTML attributes out of the index: rare terms\n// carry high IDF, so they outrank prose. remove-markdown misses wikilinks and tables.\nfunction stripText(value: string): string {\n const withoutWikilinks = value.replace(/\\[\\[([^\\]|]+)\\|([^\\]]+)\\]\\]/g, '$2').replace(/\\[\\[([^\\]]+)\\]\\]/g, '$1');\n const withoutMarkdown = removeMarkdown(withoutWikilinks);\n const withoutTables = withoutMarkdown.replace(/^\\s*\\|?[-\\s|:]+\\|\\s*$/gm, '').replace(/\\|/g, ' ');\n return normalizeText(withoutTables);\n}\n\nexport interface FileStat {\n relPath: string;\n absPath: string;\n mtimeMs: number;\n size: number;\n presets: string[]; // every declared preset covering this file (>= 1; union, overlap allowed)\n embed: boolean; // true iff a model is named and some covering preset has semantic on\n}\n\n// Presets are views, not partitions: they overlap freely, and a file's covering set (not one\n// owner) drives indexing. Globs resolve relative to baseDir; unmatched files are not indexed.\nexport function toPosixPath(relPath: string, separator: string = sep): string {\n return separator === '\\\\' ? relPath.split(separator).join('/') : relPath;\n}\n\n// Every command pays listFiles before it answers (the freshness check stats each file), so\n// per-file work here is the hottest path in the package. Everything derivable from the config\n// alone is computed once, above the loop.\nconst NO_THROW = { throwIfNoEntry: false } as const;\n\nexport function listFiles(cfg: Config, baseDir: string): FileStat[] {\n const coverage = new Map<string, Set<string>>();\n const posixNeeded = sep === '\\\\';\n for (const name of presetNames(cfg)) {\n const preset = cfg.presets[name];\n for (const matched of globSync(preset.include, { cwd: baseDir, exclude: preset.exclude })) {\n const relPath = posixNeeded ? toPosixPath(matched) : matched;\n const set = coverage.get(relPath) ?? new Set<string>();\n set.add(name);\n coverage.set(relPath, set);\n }\n }\n\n // Which presets want vectors is a property of the config, not of any file.\n const embedding = embedEnabled(cfg);\n const semanticPresets = embedding ? new Set(presetNames(cfg).filter((name) => presetSemanticEnabled(cfg, name))) : null;\n\n const files: FileStat[] = [];\n for (const relPath of [...coverage.keys()].sort()) {\n const absPath = join(baseDir, relPath); // join re-applies the platform separator for fs calls\n // node:fs glob matches directories and dangling symlinks; fast-glob returned neither, so\n // one stat filters both back out (throwIfNoEntry keeps a dangling link from throwing).\n const st = statSync(absPath, NO_THROW);\n if (!st?.isFile()) continue;\n const presets = [...(coverage.get(relPath) as Set<string>)].sort();\n const embed = semanticPresets !== null && presets.some((name) => semanticPresets.has(name));\n files.push({ relPath, absPath, mtimeMs: st.mtimeMs, size: st.size, presets, embed });\n }\n return files;\n}\n\nexport interface ParsedDoc {\n relPath: string;\n mtimeMs: number;\n size: number;\n presets: string[];\n data: Record<string, string | number | bigint | null>;\n // title/summary are duplicated from frontmatter so bm25() can weight them above the body text.\n search: { title: string; summary: string; text: string };\n // Per-feature extraction results, keyed by feature name; features store them at reconcile.\n extracted: Record<string, unknown>;\n}\n\n// Storage class follows the YAML scalar. Booleans store as 1/0, so `WHERE flag = 1` matches\n// and `WHERE flag = 'true'` cannot; `map` prints observed types so the mismatch is visible.\nfunction mapValue(value: unknown): string | number | bigint | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return BigInt(value ? 1 : 0);\n if (typeof value === 'number') return Number.isSafeInteger(value) ? BigInt(value) : value;\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\n// The delimiter split is all this package used gray-matter for.\nfunction splitFrontmatter(raw: string): { fm: string | null; body: string } {\n const open = raw.match(/^---\\r?\\n/);\n if (!open) return { fm: null, body: raw };\n const rest = raw.slice(open[0].length);\n const close = rest.match(/^---\\r?(\\n|$)/m);\n if (!close || close.index === undefined) return { fm: null, body: raw };\n return { fm: rest.slice(0, close.index), body: rest.slice(close.index + close[0].length) };\n}\n\n// Lenient by design: parseDocument collects syntax errors as data and still yields values,\n// so Obsidian-style frontmatter (e.g. an alias starting with @) survives with a warning.\nfunction parseFrontmatter(relPath: string, fm: string, warnings: string[]): Record<string, unknown> {\n const doc = parseDocument(fm);\n if (doc.errors.length > 0) {\n warnings.push(`warning: ${relPath} frontmatter has ${doc.errors.length} syntax error(s) (${doc.errors[0].message.split('\\n')[0]}); parsed leniently`);\n }\n let data: unknown;\n try {\n data = doc.toJS();\n } catch (err) {\n warnings.push(`warning: ${relPath} has unparseable frontmatter (${(err as Error).message.split('\\n')[0]}); indexing without it`);\n return {};\n }\n if (data === null || data === undefined) return {};\n if (typeof data !== 'object' || Array.isArray(data)) {\n warnings.push(`warning: ${relPath} frontmatter is not a key-value mapping; ignoring it`);\n return {};\n }\n return data as Record<string, unknown>;\n}\n\nexport function parseFile(file: FileStat, extractors: Feature[] = []): { doc: ParsedDoc; warnings: string[] } {\n const raw = readFileSync(file.absPath, 'utf8');\n const warnings: string[] = [];\n\n const { fm, body: content } = splitFrontmatter(raw);\n const data = fm === null ? {} : parseFrontmatter(file.relPath, fm, warnings);\n const mapped: Record<string, string | number | bigint | null> = {};\n\n for (const key of Object.keys(data)) {\n if (RESERVED_COLUMNS.has(key)) {\n warnings.push(`warning: ${file.relPath} has a frontmatter key named \"${key}\", which is reserved; ignoring it`);\n continue;\n }\n mapped[key] = mapValue(data[key]);\n }\n\n // title/summary are plain YAML strings -- whitespace-collapse only;\n // the prose gets the full markdown strip.\n const search = { title: normalizeText(data.title), summary: normalizeText(data.summary), text: stripText(content) };\n\n return {\n doc: {\n relPath: file.relPath,\n mtimeMs: file.mtimeMs,\n size: file.size,\n presets: file.presets,\n data: mapped,\n search,\n extracted: Object.fromEntries(extractors.filter((f) => f.extract).map((f) => [f.name, f.extract?.(raw, content, search)])),\n },\n warnings,\n };\n}\n"],"names":["RESERVED_COLUMNS","listFiles","parseFile","toPosixPath","Set","normalizeText","value","undefined","String","replace","trim","stripText","withoutWikilinks","withoutMarkdown","removeMarkdown","withoutTables","relPath","separator","sep","split","join","NO_THROW","throwIfNoEntry","cfg","baseDir","coverage","Map","posixNeeded","presetNames","name","preset","presets","globSync","include","cwd","exclude","matched","set","get","add","embedding","embedEnabled","semanticPresets","filter","presetSemanticEnabled","files","keys","sort","absPath","st","statSync","isFile","embed","some","has","push","mtimeMs","size","mapValue","BigInt","Number","isSafeInteger","JSON","stringify","splitFrontmatter","raw","open","match","fm","body","rest","slice","length","close","index","parseFrontmatter","warnings","doc","parseDocument","errors","message","data","toJS","err","Array","isArray","file","extractors","readFileSync","content","mapped","Object","key","search","title","summary","text","extracted","fromEntries","f","extract","map"],"mappings":"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"}
@@ -9,10 +9,8 @@ Object.defineProperty(exports, "searchError", {
9
9
  }
10
10
  });
11
11
  var _errorsts = require("./errors.js");
12
- // FTS5 reads these as operators, so a bare term containing one is a syntax error or --
13
- // worse -- a column filter: `end-to-end` parses as a filter on column `to`, and SQLite
14
- // reports `no such column: to`, which is a true statement about the parse and a false
15
- // one about the input. Both field reports on 0.6.0 misdiagnosed that message.
12
+ // FTS5 reads these as operators, so `end-to-end` parses as a filter on column `to` and errors
13
+ // `no such column: to` -- true about the parse, misleading about the input.
16
14
  var FTS5_OPERATORS = /[-'"/.:^*()]/;
17
15
  function searchError(err, terms, scope) {
18
16
  var _terms_match;
@@ -35,8 +33,8 @@ function searchError(err, terms, scope) {
35
33
  }).join(', '), " is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"").concat(suspects[0], "\"'. Searchable columns are title, summary, text."));
36
34
  }
37
35
  if (col !== undefined && scope !== undefined) {
38
- return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- the where condition (").concat(scope, ") references it; frontmatter columns are listed by sense query \"SELECT name FROM pragma_table_info('frontmatter')\"."));
36
+ return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- the where condition (").concat(scope, ") references it; frontmatter columns are listed by sense sql \"SELECT name FROM pragma_table_info('frontmatter')\"."));
39
37
  }
40
- return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense query (list them with pragma_table_info('frontmatter'))."));
38
+ return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense sql (list them with pragma_table_info('frontmatter'))."));
41
39
  }
42
40
  /* 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/search-error.ts"],"sourcesContent":["import { SenseError } from './errors.ts';\n\n// FTS5 reads these as operators, so a bare term containing one is a syntax error or --\n// worse -- a column filter: `end-to-end` parses as a filter on column `to`, and SQLite\n// reports `no such column: to`, which is a true statement about the parse and a false\n// one about the input. Both field reports on 0.6.0 misdiagnosed that message.\nconst FTS5_OPERATORS = /[-'\"/.:^*()]/;\n\nexport function searchError(err: Error, terms: string, scope?: string): Error {\n const message = err.message;\n if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;\n const suspects = (terms.match(/\\S+/g) ?? []).filter((t) => !t.startsWith('\"') && FTS5_OPERATORS.test(t));\n // Blame the terms only when the failing token actually came from one -- a typo'd column\n // in --where (or the tree's default scope) raises \"no such column\" through this same\n // statement, and naming a term for it would state a false fact about the input.\n const col = /no such column: (\\S+)/.exec(message)?.[1];\n const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t) => t.split(/[^\\p{L}\\p{N}]+/u).includes(col));\n if (fromTerms && suspects.length > 0) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the punctuation in ${suspects.map((t) => `\\`${t}\\``).join(', ')} is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"${suspects[0]}\"'. Searchable columns are title, summary, text.`);\n }\n if (col !== undefined && scope !== undefined) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the where condition (${scope}) references it; frontmatter columns are listed by sense query \"SELECT name FROM pragma_table_info('frontmatter')\".`);\n }\n return new SenseError('SEARCH_SYNTAX', `${message} -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense query (list them with pragma_table_info('frontmatter')).`);\n}\n"],"names":["searchError","FTS5_OPERATORS","err","terms","scope","message","test","suspects","match","filter","t","startsWith","col","exec","fromTerms","undefined","length","some","split","includes","SenseError","map","join"],"mappings":";;;;+BAQgBA;;;eAAAA;;;wBARW;AAE3B,uFAAuF;AACvF,uFAAuF;AACvF,sFAAsF;AACtF,8EAA8E;AAC9E,IAAMC,iBAAiB;AAEhB,SAASD,YAAYE,GAAU,EAAEC,KAAa,EAAEC,KAAc;QAGjDD;QAIN;IANZ,IAAME,UAAUH,IAAIG,OAAO;IAC3B,IAAI,CAAC,oDAAoDC,IAAI,CAACD,UAAU,OAAOH;IAC/E,IAAMK,WAAW,EAACJ,eAAAA,MAAMK,KAAK,CAAC,qBAAZL,0BAAAA,eAAuB,EAAE,EAAEM,MAAM,CAAC,SAACC;eAAM,CAACA,EAAEC,UAAU,CAAC,QAAQV,eAAeK,IAAI,CAACI;;IACrG,wFAAwF;IACxF,qFAAqF;IACrF,gFAAgF;IAChF,IAAME,OAAM,QAAA,wBAAwBC,IAAI,CAACR,sBAA7B,4BAAA,KAAuC,CAAC,EAAE;IACtD,IAAMS,YAAYF,QAAQG,YAAYR,SAASS,MAAM,GAAG,IAAIT,SAASU,IAAI,CAAC,SAACP;eAAMA,EAAEQ,KAAK,CAAC,8maAAmBC,QAAQ,CAACP;;IACrH,IAAIE,aAAaP,SAASS,MAAM,GAAG,GAAG;QACpC,OAAO,IAAII,oBAAU,CAAC,iBAAiB,AAAC,GAAmCb,OAAjCF,SAAQ,2BAAsJE,OAA7HA,SAASc,GAAG,CAAC,SAACX;mBAAM,AAAC,IAAM,OAAFA,GAAE;WAAKY,IAAI,CAAC,OAAM,qFAA8F,OAAZf,QAAQ,CAAC,EAAE,EAAC;IACtN;IACA,IAAIK,QAAQG,aAAaX,UAAUW,WAAW;QAC5C,OAAO,IAAIK,oBAAU,CAAC,iBAAiB,AAAC,GAAqChB,OAAnCC,SAAQ,6BAAiC,OAAND,OAAM;IACrF;IACA,OAAO,IAAIgB,oBAAU,CAAC,iBAAiB,AAAC,GAAU,OAARf,SAAQ;AACpD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/search-error.ts"],"sourcesContent":["import { SenseError } from './errors.ts';\n\n// FTS5 reads these as operators, so `end-to-end` parses as a filter on column `to` and errors\n// `no such column: to` -- true about the parse, misleading about the input.\nconst FTS5_OPERATORS = /[-'\"/.:^*()]/;\n\nexport function searchError(err: Error, terms: string, scope?: string): Error {\n const message = err.message;\n if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;\n const suspects = (terms.match(/\\S+/g) ?? []).filter((t) => !t.startsWith('\"') && FTS5_OPERATORS.test(t));\n // Blame the terms only when the failing token actually came from one -- a typo'd column\n // in --where (or the tree's default scope) raises \"no such column\" through this same\n // statement, and naming a term for it would state a false fact about the input.\n const col = /no such column: (\\S+)/.exec(message)?.[1];\n const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t) => t.split(/[^\\p{L}\\p{N}]+/u).includes(col));\n if (fromTerms && suspects.length > 0) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the punctuation in ${suspects.map((t) => `\\`${t}\\``).join(', ')} is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"${suspects[0]}\"'. Searchable columns are title, summary, text.`);\n }\n if (col !== undefined && scope !== undefined) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the where condition (${scope}) references it; frontmatter columns are listed by sense sql \"SELECT name FROM pragma_table_info('frontmatter')\".`);\n }\n return new SenseError('SEARCH_SYNTAX', `${message} -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense sql (list them with pragma_table_info('frontmatter')).`);\n}\n"],"names":["searchError","FTS5_OPERATORS","err","terms","scope","message","test","suspects","match","filter","t","startsWith","col","exec","fromTerms","undefined","length","some","split","includes","SenseError","map","join"],"mappings":";;;;+BAMgBA;;;eAAAA;;;wBANW;AAE3B,8FAA8F;AAC9F,4EAA4E;AAC5E,IAAMC,iBAAiB;AAEhB,SAASD,YAAYE,GAAU,EAAEC,KAAa,EAAEC,KAAc;QAGjDD;QAIN;IANZ,IAAME,UAAUH,IAAIG,OAAO;IAC3B,IAAI,CAAC,oDAAoDC,IAAI,CAACD,UAAU,OAAOH;IAC/E,IAAMK,WAAW,EAACJ,eAAAA,MAAMK,KAAK,CAAC,qBAAZL,0BAAAA,eAAuB,EAAE,EAAEM,MAAM,CAAC,SAACC;eAAM,CAACA,EAAEC,UAAU,CAAC,QAAQV,eAAeK,IAAI,CAACI;;IACrG,wFAAwF;IACxF,qFAAqF;IACrF,gFAAgF;IAChF,IAAME,OAAM,QAAA,wBAAwBC,IAAI,CAACR,sBAA7B,4BAAA,KAAuC,CAAC,EAAE;IACtD,IAAMS,YAAYF,QAAQG,YAAYR,SAASS,MAAM,GAAG,IAAIT,SAASU,IAAI,CAAC,SAACP;eAAMA,EAAEQ,KAAK,CAAC,8maAAmBC,QAAQ,CAACP;;IACrH,IAAIE,aAAaP,SAASS,MAAM,GAAG,GAAG;QACpC,OAAO,IAAII,oBAAU,CAAC,iBAAiB,AAAC,GAAmCb,OAAjCF,SAAQ,2BAAsJE,OAA7HA,SAASc,GAAG,CAAC,SAACX;mBAAM,AAAC,IAAM,OAAFA,GAAE;WAAKY,IAAI,CAAC,OAAM,qFAA8F,OAAZf,QAAQ,CAAC,EAAE,EAAC;IACtN;IACA,IAAIK,QAAQG,aAAaX,UAAUW,WAAW;QAC5C,OAAO,IAAIK,oBAAU,CAAC,iBAAiB,AAAC,GAAqChB,OAAnCC,SAAQ,6BAAiC,OAAND,OAAM;IACrF;IACA,OAAO,IAAIgB,oBAAU,CAAC,iBAAiB,AAAC,GAAU,OAARf,SAAQ;AACpD"}
@@ -0,0 +1,8 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ interface FindPathOptions {
3
+ directed?: boolean;
4
+ maxDepth?: number;
5
+ allowed?: Set<string>;
6
+ }
7
+ export declare function findPath(db: DatabaseSync, from: string, to: string, opts?: FindPathOptions): string[] | null;
8
+ export {};