sensemaking 0.19.2 → 0.21.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 (221) hide show
  1. package/README.md +0 -1
  2. package/dist/cjs/chunk/extract.d.cts +1 -0
  3. package/dist/cjs/chunk/extract.d.ts +1 -0
  4. package/dist/cjs/chunk/extract.js +23 -5
  5. package/dist/cjs/chunk/extract.js.map +1 -1
  6. package/dist/cjs/chunk/index.d.cts +1 -1
  7. package/dist/cjs/chunk/index.d.ts +1 -1
  8. package/dist/cjs/chunk/index.js +3 -0
  9. package/dist/cjs/chunk/index.js.map +1 -1
  10. package/dist/cjs/config/access.d.cts +0 -1
  11. package/dist/cjs/config/access.d.ts +0 -1
  12. package/dist/cjs/config/access.js +10 -17
  13. package/dist/cjs/config/access.js.map +1 -1
  14. package/dist/cjs/config/index.d.cts +1 -1
  15. package/dist/cjs/config/index.d.ts +1 -1
  16. package/dist/cjs/config/index.js +0 -3
  17. package/dist/cjs/config/index.js.map +1 -1
  18. package/dist/cjs/config/types.d.cts +0 -3
  19. package/dist/cjs/config/types.d.ts +0 -3
  20. package/dist/cjs/config/types.js.map +1 -1
  21. package/dist/cjs/config/validate.js +6 -24
  22. package/dist/cjs/config/validate.js.map +1 -1
  23. package/dist/cjs/errors.d.cts +1 -1
  24. package/dist/cjs/errors.d.ts +1 -1
  25. package/dist/cjs/errors.js.map +1 -1
  26. package/dist/cjs/features/links.js +4 -5
  27. package/dist/cjs/features/links.js.map +1 -1
  28. package/dist/cjs/features/sections.js +4 -1
  29. package/dist/cjs/features/sections.js.map +1 -1
  30. package/dist/cjs/scan/pool.d.cts +15 -0
  31. package/dist/cjs/scan/pool.d.ts +15 -0
  32. package/dist/cjs/scan/pool.js +263 -0
  33. package/dist/cjs/scan/pool.js.map +1 -0
  34. package/dist/cjs/scan/reparse.d.cts +2 -0
  35. package/dist/cjs/scan/reparse.d.ts +2 -0
  36. package/dist/cjs/scan/reparse.js +15 -76
  37. package/dist/cjs/scan/reparse.js.map +1 -1
  38. package/dist/cjs/store/builder.d.cts +19 -0
  39. package/dist/cjs/store/builder.d.ts +19 -0
  40. package/dist/cjs/store/builder.js +618 -0
  41. package/dist/cjs/store/builder.js.map +1 -0
  42. package/dist/cjs/store/duckdb/lexical.d.cts +1 -0
  43. package/dist/cjs/store/duckdb/lexical.d.ts +1 -0
  44. package/dist/cjs/store/duckdb/lexical.js +21 -5
  45. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  46. package/dist/cjs/store/duckdb/open.d.cts +9 -8
  47. package/dist/cjs/store/duckdb/open.d.ts +9 -8
  48. package/dist/cjs/store/duckdb/open.js +77 -123
  49. package/dist/cjs/store/duckdb/open.js.map +1 -1
  50. package/dist/cjs/store/duckdb/reconcile.js +34 -5
  51. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  52. package/dist/cjs/store/duckdb/store.d.cts +1 -2
  53. package/dist/cjs/store/duckdb/store.d.ts +1 -2
  54. package/dist/cjs/store/duckdb/store.js +1 -26
  55. package/dist/cjs/store/duckdb/store.js.map +1 -1
  56. package/dist/cjs/store/embed-scope.d.cts +2 -0
  57. package/dist/cjs/store/embed-scope.d.ts +2 -0
  58. package/dist/cjs/store/embed-scope.js +85 -0
  59. package/dist/cjs/store/embed-scope.js.map +1 -0
  60. package/dist/cjs/store/feature-scope.d.cts +8 -0
  61. package/dist/cjs/store/feature-scope.d.ts +8 -0
  62. package/dist/cjs/store/feature-scope.js +107 -0
  63. package/dist/cjs/store/feature-scope.js.map +1 -0
  64. package/dist/cjs/store/index.d.cts +8 -3
  65. package/dist/cjs/store/index.d.ts +8 -3
  66. package/dist/cjs/store/index.js +36 -7
  67. package/dist/cjs/store/index.js.map +1 -1
  68. package/dist/cjs/store/open.d.cts +15 -0
  69. package/dist/cjs/store/open.d.ts +15 -0
  70. package/dist/cjs/store/open.js +608 -0
  71. package/dist/cjs/store/open.js.map +1 -0
  72. package/dist/cjs/store/preset-scope.d.cts +3 -0
  73. package/dist/cjs/store/preset-scope.d.ts +3 -0
  74. package/dist/cjs/store/preset-scope.js +293 -0
  75. package/dist/cjs/store/preset-scope.js.map +1 -0
  76. package/dist/cjs/store/reconcile.d.cts +2 -1
  77. package/dist/cjs/store/reconcile.d.ts +2 -1
  78. package/dist/cjs/store/reconcile.js +175 -179
  79. package/dist/cjs/store/reconcile.js.map +1 -1
  80. package/dist/cjs/store/{sqlite/signature.d.cts → signature.d.cts} +0 -3
  81. package/dist/cjs/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
  82. package/dist/cjs/store/signature.js +164 -0
  83. package/dist/cjs/store/signature.js.map +1 -0
  84. package/dist/cjs/store/sqlite/lexical.d.cts +1 -1
  85. package/dist/cjs/store/sqlite/lexical.d.ts +1 -1
  86. package/dist/cjs/store/sqlite/lexical.js +2 -2
  87. package/dist/cjs/store/sqlite/lexical.js.map +1 -1
  88. package/dist/cjs/store/sqlite/open.d.cts +8 -9
  89. package/dist/cjs/store/sqlite/open.d.ts +8 -9
  90. package/dist/cjs/store/sqlite/open.js +82 -311
  91. package/dist/cjs/store/sqlite/open.js.map +1 -1
  92. package/dist/cjs/store/sqlite/reconcile.d.cts +1 -1
  93. package/dist/cjs/store/sqlite/reconcile.d.ts +1 -1
  94. package/dist/cjs/store/sqlite/reconcile.js +78 -16
  95. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  96. package/dist/cjs/store/sqlite/sql-functions.d.cts +1 -1
  97. package/dist/cjs/store/sqlite/sql-functions.d.ts +1 -1
  98. package/dist/cjs/store/sqlite/sql-functions.js +3 -3
  99. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
  100. package/dist/cjs/store/sqlite/store.d.cts +1 -2
  101. package/dist/cjs/store/sqlite/store.d.ts +1 -2
  102. package/dist/cjs/store/sqlite/store.js +2 -15
  103. package/dist/cjs/store/sqlite/store.js.map +1 -1
  104. package/dist/cjs/store/turso/open.d.cts +6 -11
  105. package/dist/cjs/store/turso/open.d.ts +6 -11
  106. package/dist/cjs/store/turso/open.js +85 -171
  107. package/dist/cjs/store/turso/open.js.map +1 -1
  108. package/dist/cjs/store/turso/reconcile.d.cts +2 -0
  109. package/dist/cjs/store/turso/reconcile.d.ts +2 -0
  110. package/dist/cjs/store/turso/reconcile.js +88 -8
  111. package/dist/cjs/store/turso/reconcile.js.map +1 -1
  112. package/dist/cjs/store/turso/store.d.cts +1 -2
  113. package/dist/cjs/store/turso/store.d.ts +1 -2
  114. package/dist/cjs/store/turso/store.js +1 -13
  115. package/dist/cjs/store/turso/store.js.map +1 -1
  116. package/dist/cjs/store/types.d.cts +16 -6
  117. package/dist/cjs/store/types.d.ts +16 -6
  118. package/dist/cjs/store/types.js.map +1 -1
  119. package/dist/cjs/text/strip.js +3 -6
  120. package/dist/cjs/text/strip.js.map +1 -1
  121. package/dist/cjs/watch.js +28 -16
  122. package/dist/cjs/watch.js.map +1 -1
  123. package/dist/esm/chunk/extract.d.ts +1 -0
  124. package/dist/esm/chunk/extract.js +10 -2
  125. package/dist/esm/chunk/extract.js.map +1 -1
  126. package/dist/esm/chunk/index.d.ts +1 -1
  127. package/dist/esm/chunk/index.js +1 -1
  128. package/dist/esm/chunk/index.js.map +1 -1
  129. package/dist/esm/config/access.d.ts +0 -1
  130. package/dist/esm/config/access.js +12 -17
  131. package/dist/esm/config/access.js.map +1 -1
  132. package/dist/esm/config/index.d.ts +1 -1
  133. package/dist/esm/config/index.js +1 -1
  134. package/dist/esm/config/index.js.map +1 -1
  135. package/dist/esm/config/types.d.ts +0 -3
  136. package/dist/esm/config/types.js.map +1 -1
  137. package/dist/esm/config/validate.js +6 -22
  138. package/dist/esm/config/validate.js.map +1 -1
  139. package/dist/esm/errors.d.ts +1 -1
  140. package/dist/esm/errors.js.map +1 -1
  141. package/dist/esm/features/links.js +3 -4
  142. package/dist/esm/features/links.js.map +1 -1
  143. package/dist/esm/features/sections.js +4 -1
  144. package/dist/esm/features/sections.js.map +1 -1
  145. package/dist/esm/scan/pool.d.ts +15 -0
  146. package/dist/esm/scan/pool.js +77 -0
  147. package/dist/esm/scan/pool.js.map +1 -0
  148. package/dist/esm/scan/reparse.d.ts +2 -0
  149. package/dist/esm/scan/reparse.js +11 -58
  150. package/dist/esm/scan/reparse.js.map +1 -1
  151. package/dist/esm/store/builder.d.ts +19 -0
  152. package/dist/esm/store/builder.js +163 -0
  153. package/dist/esm/store/builder.js.map +1 -0
  154. package/dist/esm/store/duckdb/lexical.d.ts +1 -0
  155. package/dist/esm/store/duckdb/lexical.js +22 -9
  156. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  157. package/dist/esm/store/duckdb/open.d.ts +9 -8
  158. package/dist/esm/store/duckdb/open.js +36 -58
  159. package/dist/esm/store/duckdb/open.js.map +1 -1
  160. package/dist/esm/store/duckdb/reconcile.js +15 -3
  161. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  162. package/dist/esm/store/duckdb/store.d.ts +1 -2
  163. package/dist/esm/store/duckdb/store.js +2 -11
  164. package/dist/esm/store/duckdb/store.js.map +1 -1
  165. package/dist/esm/store/embed-scope.d.ts +2 -0
  166. package/dist/esm/store/embed-scope.js +28 -0
  167. package/dist/esm/store/embed-scope.js.map +1 -0
  168. package/dist/esm/store/feature-scope.d.ts +8 -0
  169. package/dist/esm/store/feature-scope.js +49 -0
  170. package/dist/esm/store/feature-scope.js.map +1 -0
  171. package/dist/esm/store/index.d.ts +8 -3
  172. package/dist/esm/store/index.js +22 -7
  173. package/dist/esm/store/index.js.map +1 -1
  174. package/dist/esm/store/open.d.ts +15 -0
  175. package/dist/esm/store/open.js +187 -0
  176. package/dist/esm/store/open.js.map +1 -0
  177. package/dist/esm/store/preset-scope.d.ts +3 -0
  178. package/dist/esm/store/preset-scope.js +77 -0
  179. package/dist/esm/store/preset-scope.js.map +1 -0
  180. package/dist/esm/store/reconcile.d.ts +2 -1
  181. package/dist/esm/store/reconcile.js +33 -6
  182. package/dist/esm/store/reconcile.js.map +1 -1
  183. package/dist/esm/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
  184. package/dist/esm/store/signature.js +37 -0
  185. package/dist/esm/store/signature.js.map +1 -0
  186. package/dist/esm/store/sqlite/lexical.d.ts +1 -1
  187. package/dist/esm/store/sqlite/lexical.js +4 -4
  188. package/dist/esm/store/sqlite/lexical.js.map +1 -1
  189. package/dist/esm/store/sqlite/open.d.ts +8 -9
  190. package/dist/esm/store/sqlite/open.js +41 -147
  191. package/dist/esm/store/sqlite/open.js.map +1 -1
  192. package/dist/esm/store/sqlite/reconcile.d.ts +1 -1
  193. package/dist/esm/store/sqlite/reconcile.js +14 -14
  194. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  195. package/dist/esm/store/sqlite/sql-functions.d.ts +1 -1
  196. package/dist/esm/store/sqlite/sql-functions.js +3 -3
  197. package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
  198. package/dist/esm/store/sqlite/store.d.ts +1 -2
  199. package/dist/esm/store/sqlite/store.js +3 -9
  200. package/dist/esm/store/sqlite/store.js.map +1 -1
  201. package/dist/esm/store/turso/open.d.ts +6 -11
  202. package/dist/esm/store/turso/open.js +34 -78
  203. package/dist/esm/store/turso/open.js.map +1 -1
  204. package/dist/esm/store/turso/reconcile.d.ts +2 -0
  205. package/dist/esm/store/turso/reconcile.js +18 -3
  206. package/dist/esm/store/turso/reconcile.js.map +1 -1
  207. package/dist/esm/store/turso/store.d.ts +1 -2
  208. package/dist/esm/store/turso/store.js +2 -7
  209. package/dist/esm/store/turso/store.js.map +1 -1
  210. package/dist/esm/store/types.d.ts +16 -6
  211. package/dist/esm/store/types.js.map +1 -1
  212. package/dist/esm/text/strip.js +3 -4
  213. package/dist/esm/text/strip.js.map +1 -1
  214. package/dist/esm/watch.js +5 -3
  215. package/dist/esm/watch.js.map +1 -1
  216. package/package.json +4 -3
  217. package/schema.json +0 -11
  218. package/dist/cjs/store/sqlite/signature.js +0 -380
  219. package/dist/cjs/store/sqlite/signature.js.map +0 -1
  220. package/dist/esm/store/sqlite/signature.js +0 -58
  221. package/dist/esm/store/sqlite/signature.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/store.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\nimport type { Config } from '../../config/index.ts';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { reconcile } from '../reconcile.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { tursoDialect } from './reconcile.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// No 'snippets': fts_highlight returns the whole column, not a bounded window, so hits use\n// the caller's JS excerpt. No 'watch-concurrency': single-process by default, as on duckdb.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors']);\n\n// Shares one Connection instance (conn) with open()'s own reconcile call so transaction depth\n// (see transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: Database, conn: Connection, cfg: Config, baseDir: string): Store {\n return {\n name: 'turso',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n async reconcile() {\n return reconcile(conn, cfg, baseDir, tursoDialect);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n fieldStats: (columns, scopeWhere) => fieldStats(conn, columns, scopeWhere),\n },\n lexical: {\n query: (terms, opts) => queryLexical(conn, terms, opts),\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the\n // interface's per-call storeDims -- see vectors.ts's padded() for why a shorter vector is still correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(conn, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n async engineStatus() {\n // Read back rather than recomputed: this is what open() actually set (3x the largest\n // recorded reconcile, floored at 30s, capped at 10min). Turso's PRAGMA busy_timeout names its column \"busy_timeout\" (spike-verified), not \"timeout\" like real SQLite.\n const row = (await (await db.prepare('PRAGMA busy_timeout')).get()) as { busy_timeout: number };\n return { busy_timeout: `${row.busy_timeout}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)` };\n },\n raw: {\n async prepare(sql: string) {\n const stmt = await db.prepare(sql);\n stmt.safeIntegers(true); // int64 past 2^53 arrives as BigInt instead of losing precision\n return {\n columns: () => stmt.columns(),\n // sense sql streams through the client's own async generator.\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...params);\n },\n };\n },\n },\n async close() {\n await db.close();\n },\n };\n}\n"],"names":["CAPABILITIES","createStore","Set","db","conn","cfg","baseDir","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","withTransaction","reconcile","tursoDialect","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","STORE_DIMS","candidates","qv","_storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","raw","stmt","safeIntegers","iterate","params","close"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/store.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// No 'snippets': fts_highlight returns the whole column, not a bounded window, so hits use\n// the caller's JS excerpt. No 'watch-concurrency': single-process by default, as on duckdb.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors']);\n\n// Shares one Connection instance (conn) with the builder's own reconcile call so transaction depth\n// (see transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: Database, conn: Connection): Store {\n return {\n name: 'turso',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n fieldStats: (columns, scopeWhere) => fieldStats(conn, columns, scopeWhere),\n },\n lexical: {\n query: (terms, opts) => queryLexical(conn, terms, opts),\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the\n // interface's per-call storeDims -- see vectors.ts's padded() for why a shorter vector is still correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(conn, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n async engineStatus() {\n // Read back rather than recomputed: this is what open() actually set (3x the largest\n // recorded reconcile, floored at 30s, capped at 10min). Turso's PRAGMA busy_timeout names its column \"busy_timeout\" (spike-verified), not \"timeout\" like real SQLite.\n const row = (await (await db.prepare('PRAGMA busy_timeout')).get()) as { busy_timeout: number };\n return { busy_timeout: `${row.busy_timeout}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)` };\n },\n raw: {\n async prepare(sql: string) {\n const stmt = await db.prepare(sql);\n stmt.safeIntegers(true); // int64 past 2^53 arrives as BigInt instead of losing precision\n return {\n columns: () => stmt.columns(),\n // sense sql streams through the client's own async generator.\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...params);\n },\n };\n },\n },\n async close() {\n await db.close();\n },\n };\n}\n"],"names":["CAPABILITIES","createStore","Set","db","conn","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","withTransaction","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","STORE_DIMS","candidates","qv","_storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","raw","stmt","safeIntegers","iterate","params","close"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Config, StoreName } from '../config/index.js';
1
+ import type { Config, ResolvedConfig, StoreName } from '../config/index.js';
2
2
  import type { ReconcileDelta } from '../features/types.js';
3
3
  import type { ParsedDoc } from '../scan/index.js';
4
4
  export declare const CAPABILITY_NAMES: readonly ["phrases", "snippets", "watch-concurrency", "lexical", "vectors", "sql-functions"];
@@ -25,10 +25,24 @@ export interface Connection {
25
25
  export interface ReconcileDialect {
26
26
  beginMode(): string;
27
27
  checkColumnLimit(count: number): void;
28
- columnTypeSuffix(): string;
28
+ addColumns(conn: Connection, names: string[]): Promise<void>;
29
29
  reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, cfg: Config): Promise<void>;
30
30
  recordDuration?(conn: Connection, ms: number): Promise<void>;
31
31
  }
32
+ export interface OpenDialect<Handle> {
33
+ filename: string;
34
+ schemaVersion: string;
35
+ reconcileDialect: ReconcileDialect;
36
+ connect(dbPath: string, cfg: ResolvedConfig): Promise<{
37
+ handle: Handle;
38
+ conn: Connection;
39
+ }>;
40
+ close(handle: Handle): Promise<void>;
41
+ isLocked?(err: Error): boolean;
42
+ ensureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;
43
+ setDerivedBusyTimeout?(handle: Handle, conn: Connection, ms: number): Promise<void>;
44
+ createStore(handle: Handle, conn: Connection, cfg: ResolvedConfig): Store;
45
+ }
32
46
  export interface FieldStat {
33
47
  field: string;
34
48
  coverage: number;
@@ -96,10 +110,6 @@ export interface Store {
96
110
  prepare(sql: string): Promise<Statement>;
97
111
  runBatch(sql: string, paramRows: unknown[][]): Promise<void>;
98
112
  transaction<T>(fn: () => Promise<T>): Promise<T>;
99
- reconcile(): Promise<{
100
- parsed: number;
101
- warnings: string[];
102
- }>;
103
113
  docs: DocumentStore;
104
114
  lexical: LexicalIndex;
105
115
  vectors: VectorStore;
@@ -1,4 +1,4 @@
1
- import type { Config, StoreName } from '../config/index.js';
1
+ import type { Config, ResolvedConfig, StoreName } from '../config/index.js';
2
2
  import type { ReconcileDelta } from '../features/types.js';
3
3
  import type { ParsedDoc } from '../scan/index.js';
4
4
  export declare const CAPABILITY_NAMES: readonly ["phrases", "snippets", "watch-concurrency", "lexical", "vectors", "sql-functions"];
@@ -25,10 +25,24 @@ export interface Connection {
25
25
  export interface ReconcileDialect {
26
26
  beginMode(): string;
27
27
  checkColumnLimit(count: number): void;
28
- columnTypeSuffix(): string;
28
+ addColumns(conn: Connection, names: string[]): Promise<void>;
29
29
  reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, cfg: Config): Promise<void>;
30
30
  recordDuration?(conn: Connection, ms: number): Promise<void>;
31
31
  }
32
+ export interface OpenDialect<Handle> {
33
+ filename: string;
34
+ schemaVersion: string;
35
+ reconcileDialect: ReconcileDialect;
36
+ connect(dbPath: string, cfg: ResolvedConfig): Promise<{
37
+ handle: Handle;
38
+ conn: Connection;
39
+ }>;
40
+ close(handle: Handle): Promise<void>;
41
+ isLocked?(err: Error): boolean;
42
+ ensureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;
43
+ setDerivedBusyTimeout?(handle: Handle, conn: Connection, ms: number): Promise<void>;
44
+ createStore(handle: Handle, conn: Connection, cfg: ResolvedConfig): Store;
45
+ }
32
46
  export interface FieldStat {
33
47
  field: string;
34
48
  coverage: number;
@@ -96,10 +110,6 @@ export interface Store {
96
110
  prepare(sql: string): Promise<Statement>;
97
111
  runBatch(sql: string, paramRows: unknown[][]): Promise<void>;
98
112
  transaction<T>(fn: () => Promise<T>): Promise<T>;
99
- reconcile(): Promise<{
100
- parsed: number;
101
- warnings: string[];
102
- }>;
103
113
  docs: DocumentStore;
104
114
  lexical: LexicalIndex;
105
115
  vectors: VectorStore;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/types.ts"],"sourcesContent":["// The backing-store interface: a minimal portable statement surface (exec/prepare), plus\n// dedicated interfaces exactly where engines diverge (lexical index, vector scan, raw sql).\n\nimport type { Config, StoreName } from '../config/index.ts';\nimport type { ReconcileDelta } from '../features/types.ts';\nimport type { ParsedDoc } from '../scan/index.ts';\n\n// 'lexical'/'vectors': the store's LexicalIndex/VectorStore is functionally implemented, not\n// present-but-inert. 'sql-functions': the engine can register has/basename/segment as UDFs at all,\n// which turso's client cannot. The rest are finer FTS5-only behaviors; a missing one fails at open\n// or first use. An array, not a bare union, so a runtime check reads the same list the type does.\nexport const CAPABILITY_NAMES = ['phrases', 'snippets', 'watch-concurrency', 'lexical', 'vectors', 'sql-functions'] as const;\nexport type Capability = (typeof CAPABILITY_NAMES)[number];\n\nexport interface RunResult {\n changes: number | bigint;\n lastInsertRowid: number | bigint;\n}\n\n// A prepared statement's async surface: every supported engine crosses a real async boundary\n// for a query (DuckDB has no synchronous client), so run/get/all return Promises.\nexport interface Statement {\n run(...params: unknown[]): Promise<RunResult>;\n get(...params: unknown[]): Promise<unknown>;\n all(...params: unknown[]): Promise<unknown[]>;\n iterate(...params: unknown[]): AsyncIterable<unknown>;\n columns(): Array<{ name: string }>;\n setReadBigInts(enabled: boolean): void;\n}\n\n// Connection surface feature-owned SQL runs against inside a store's hot loops (schema,\n// reconcile), portable so a feature never imports an engine-specific client type.\nexport interface Connection {\n exec(sql: string): Promise<void>;\n prepare(sql: string): Promise<Statement>;\n runBatch(sql: string, paramRows: unknown[][]): Promise<void>;\n}\n\n// One reconcile algorithm (src/store/reconcile.ts), parameterised per engine. reconcileContent is\n// the load-bearing member: it lands every content-table change and owns its own multi-step\n// strategy (sqlite FTS5 incremental, duckdb combined delete/insert, turso incremental-or-rebuild).\nexport interface ReconcileDialect {\n // BEGIN mode for the whole reconcile: sqlite/turso 'BEGIN IMMEDIATE', duckdb 'BEGIN'.\n beginMode(): string;\n // Throws SenseError('COLUMN_LIMIT', ...) past this store's own column ceiling and reasoning.\n checkColumnLimit(count: number): void;\n // Type suffix for ALTER TABLE ADD COLUMN: '' for sqlite/turso, ' VARIANT' for duckdb.\n columnTypeSuffix(): string;\n // Deletes content rows for `touched`, inserts rows for `docs`; `delta` carries the tree state a\n // strategy may need. Must not open its own transaction, and must not return before its own\n // multi-step strategy (e.g. turso's DROP/rebuild) completes.\n reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, cfg: Config): Promise<void>;\n // Records this reconcile's write-transaction duration. sqlite/turso use it for open()'s derived\n // busy_timeout; duckdb has no such PRAGMA and omits it.\n recordDuration?(conn: Connection, ms: number): Promise<void>;\n}\n\nexport interface FieldStat {\n field: string;\n coverage: number;\n type: string;\n}\n\nexport interface DocumentStore {\n // Frontmatter column names (including internal ones; callers filter).\n columns(): Promise<string[]>;\n // Per-column coverage (non-null count) and observed type set, aggregated in one SQL query,\n // never one row per note. `scopeWhere` is a caller-built WHERE fragment, per LexicalQueryOptions.\n fieldStats(columns: string[], scopeWhere: string): Promise<FieldStat[]>;\n}\n\nexport interface LexicalHit {\n path: string;\n hit: string | null;\n}\n\nexport interface LexicalQueryOptions {\n whereJoin: string;\n whereCond: string;\n scopeCond: string;\n limit: number;\n}\n\nexport interface LexicalIndex {\n // Ranked word-match query with excerpt, scoped by the caller-built SQL fragments (the same\n // fragments narrowByWhere/materializeScope produce elsewhere).\n query(terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]>;\n}\n\nexport interface VectorCandidate {\n path: string;\n lines: string;\n similarity: number;\n}\n\nexport interface VectorSimilar {\n path: string;\n similarity: number;\n}\n\nexport interface VectorWriteRow {\n path: string;\n chunk: number;\n scale: number;\n vector: Buffer;\n}\n\nexport interface VectorStore {\n // Rows whose vector is NULL: never embedded, or added since.\n pending(): Promise<Array<{ path: string; chunk: number }>>;\n // One batch write per call (never per row) so a provider's embedding batch stays inside a\n // single store method.\n writeVectors(rows: VectorWriteRow[]): Promise<void>;\n candidates(queryVector: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>): Promise<VectorCandidate[]>;\n similar(path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Promise<VectorSimilar[]>;\n hasVector(path: string): Promise<boolean>;\n}\n\n// The `sense sql` passthrough: string in, streamed rows out. Each store registers the same\n// sense-supplied functions and applies its own read-bigints/error-translation behavior.\nexport interface RawStatement {\n columns(): Array<{ name: string }>;\n iterate(...params: unknown[]): AsyncIterable<unknown>;\n}\n\nexport interface SqlSession {\n prepare(sql: string): Promise<RawStatement>;\n}\n\nexport interface Store {\n readonly name: StoreName;\n readonly capabilities: ReadonlySet<Capability>;\n exec(sql: string): Promise<void>;\n prepare(sql: string): Promise<Statement>;\n // One crossing, N rows: every external write loop (search's candidate insert, graph ring's\n // temp-table writes) goes through this instead of looping over `run()`. Same contract as Connection.runBatch.\n runBatch(sql: string, paramRows: unknown[][]): Promise<void>;\n // Pins one snapshot across multi-statement reads; `map` and `peek` use it. A network-bound\n // write (search's embedding top-up) must stay outside it. Nesting joins the enclosing transaction.\n transaction<T>(fn: () => Promise<T>): Promise<T>;\n // The whole file-sync pass (parse changed files, run every feature hook, resolve links,\n // recompute rank) behind one method: per-file iteration never crosses the async boundary.\n reconcile(): Promise<{ parsed: number; warnings: string[] }>;\n docs: DocumentStore;\n lexical: LexicalIndex;\n vectors: VectorStore;\n raw: SqlSession;\n // Engine-level facts for `sense status` (e.g. a derived busy_timeout PRAGMA reading); each\n // store owns what it reports and how it is worded. Empty when there is nothing to report.\n engineStatus(): Promise<Record<string, string>>;\n close(): Promise<void>;\n}\n"],"names":["CAPABILITY_NAMES"],"mappings":"AAAA,yFAAyF;AACzF,4FAA4F;;;;;+BAU/EA;;;eAAAA;;;AAAN,IAAMA,mBAAmB;IAAC;IAAW;IAAY;IAAqB;IAAW;IAAW;CAAgB"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/types.ts"],"sourcesContent":["// The backing-store interface: a minimal portable statement surface (exec/prepare), plus\n// dedicated interfaces exactly where engines diverge (lexical index, vector scan, raw sql).\n\nimport type { Config, ResolvedConfig, StoreName } from '../config/index.ts';\nimport type { ReconcileDelta } from '../features/types.ts';\nimport type { ParsedDoc } from '../scan/index.ts';\n\n// 'lexical'/'vectors': the store's LexicalIndex/VectorStore is functionally implemented, not\n// present-but-inert. 'sql-functions': the engine can register has/basename/segment as UDFs at all,\n// which turso's client cannot. The rest are finer FTS5-only behaviors; a missing one fails at open\n// or first use. An array, not a bare union, so a runtime check reads the same list the type does.\nexport const CAPABILITY_NAMES = ['phrases', 'snippets', 'watch-concurrency', 'lexical', 'vectors', 'sql-functions'] as const;\nexport type Capability = (typeof CAPABILITY_NAMES)[number];\n\nexport interface RunResult {\n changes: number | bigint;\n lastInsertRowid: number | bigint;\n}\n\n// A prepared statement's async surface: every supported engine crosses a real async boundary\n// for a query (DuckDB has no synchronous client), so run/get/all return Promises.\nexport interface Statement {\n run(...params: unknown[]): Promise<RunResult>;\n get(...params: unknown[]): Promise<unknown>;\n all(...params: unknown[]): Promise<unknown[]>;\n iterate(...params: unknown[]): AsyncIterable<unknown>;\n columns(): Array<{ name: string }>;\n setReadBigInts(enabled: boolean): void;\n}\n\n// Connection surface feature-owned SQL runs against inside a store's hot loops (schema,\n// reconcile), portable so a feature never imports an engine-specific client type.\nexport interface Connection {\n exec(sql: string): Promise<void>;\n prepare(sql: string): Promise<Statement>;\n runBatch(sql: string, paramRows: unknown[][]): Promise<void>;\n}\n\n// One reconcile algorithm (src/store/reconcile.ts), parameterised per engine. reconcileContent is\n// the load-bearing member: it lands every content-table change and owns its own multi-step\n// strategy (sqlite FTS5 incremental, duckdb combined delete/insert, turso incremental-or-rebuild).\nexport interface ReconcileDialect {\n // BEGIN mode for the whole reconcile: sqlite/turso 'BEGIN IMMEDIATE', duckdb 'BEGIN'.\n beginMode(): string;\n // Throws SenseError('COLUMN_LIMIT', ...) past this store's own column ceiling and reasoning.\n checkColumnLimit(count: number): void;\n // Adds `names` to frontmatter, already filtered to columns this connection doesn't have yet.\n // sqlite/turso loop (their ADD COLUMN is metadata-only); duckdb issues one statement per call.\n addColumns(conn: Connection, names: string[]): Promise<void>;\n // Deletes content rows for `touched`, inserts rows for `docs`; `delta` carries the tree state a\n // strategy may need. Must not open its own transaction, and must not return before its own\n // multi-step strategy (e.g. turso's DROP/rebuild) completes.\n reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, cfg: Config): Promise<void>;\n // Records this reconcile's write-transaction duration. sqlite/turso use it for open()'s derived\n // busy_timeout; duckdb has no such PRAGMA and omits it.\n recordDuration?(conn: Connection, ms: number): Promise<void>;\n}\n\n// One open algorithm (src/store/open.ts), parameterised per engine. `Handle` is whatever this\n// store needs to close the connection and construct its Store (sqlite: {db}; duckdb:\n// {instance, duckdb}; turso: db) -- opaque to the shared orchestration, threaded through unchanged.\nexport interface OpenDialect<Handle> {\n // Cache filename under STATE_DIR, e.g. 'cache.db'.\n filename: string;\n // Cache shape version, independent of the config's own `version`; bumping it rebuilds an existing tree.\n schemaVersion: string;\n reconcileDialect: ReconcileDialect;\n // Opens the physical connection and applies pragmas due before any SQL runs (sqlite: busy_timeout\n // + WAL; turso: connect-time timeout; duckdb: none).\n connect(dbPath: string, cfg: ResolvedConfig): Promise<{ handle: Handle; conn: Connection }>;\n // Releases the handle, for both the rebuild-and-reopen branch and error cleanup on this attempt.\n close(handle: Handle): Promise<void>;\n // True when connect() failed because another process holds the cache file, which the orchestration\n // retries. Each engine words it differently, so the match is the dialect's own (`native-not-emulated`).\n // Absent for sqlite, whose file lock is shared and whose concurrent-open failure is a different defect.\n // Matching on message text is forced, not chosen: measured 2026-09-02, duckdb throws a plain Error\n // whose only own properties are stack and message, and turso sets code to the constant\n // 'GenericFailure' on every failure alike with rawCode undefined. Neither exposes anything a\n // predicate could switch on, so each dialect pins its engine's wordings and unit-tests them.\n isLocked?(err: Error): boolean;\n // Schema DDL beyond frontmatter/preset_files/meta (content table, feature hooks); sole owner of\n // whether it wraps itself in a write transaction (sqlite: yes, guards a cold-open ALTER race; duckdb/turso: no).\n ensureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;\n // Installs the derived busy_timeout PRAGMA right before reconcile (sqlite/turso); absent for\n // duckdb, which has no such PRAGMA.\n setDerivedBusyTimeout?(handle: Handle, conn: Connection, ms: number): Promise<void>;\n createStore(handle: Handle, conn: Connection, cfg: ResolvedConfig): Store;\n}\n\nexport interface FieldStat {\n field: string;\n coverage: number;\n type: string;\n}\n\nexport interface DocumentStore {\n // Frontmatter column names (including internal ones; callers filter).\n columns(): Promise<string[]>;\n // Per-column coverage (non-null count) and observed type set, aggregated in one SQL query,\n // never one row per note. `scopeWhere` is a caller-built WHERE fragment, per LexicalQueryOptions.\n fieldStats(columns: string[], scopeWhere: string): Promise<FieldStat[]>;\n}\n\nexport interface LexicalHit {\n path: string;\n hit: string | null;\n}\n\nexport interface LexicalQueryOptions {\n whereJoin: string;\n whereCond: string;\n scopeCond: string;\n limit: number;\n}\n\nexport interface LexicalIndex {\n // Ranked word-match query with excerpt, scoped by the caller-built SQL fragments (the same\n // fragments narrowByWhere/materializeScope produce elsewhere).\n query(terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]>;\n}\n\nexport interface VectorCandidate {\n path: string;\n lines: string;\n similarity: number;\n}\n\nexport interface VectorSimilar {\n path: string;\n similarity: number;\n}\n\nexport interface VectorWriteRow {\n path: string;\n chunk: number;\n scale: number;\n vector: Buffer;\n}\n\nexport interface VectorStore {\n // Rows whose vector is NULL: never embedded, or added since.\n pending(): Promise<Array<{ path: string; chunk: number }>>;\n // One batch write per call (never per row) so a provider's embedding batch stays inside a\n // single store method.\n writeVectors(rows: VectorWriteRow[]): Promise<void>;\n candidates(queryVector: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>): Promise<VectorCandidate[]>;\n similar(path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Promise<VectorSimilar[]>;\n hasVector(path: string): Promise<boolean>;\n}\n\n// The `sense sql` passthrough: string in, streamed rows out. Each store registers the same\n// sense-supplied functions and applies its own read-bigints/error-translation behavior.\nexport interface RawStatement {\n columns(): Array<{ name: string }>;\n iterate(...params: unknown[]): AsyncIterable<unknown>;\n}\n\nexport interface SqlSession {\n prepare(sql: string): Promise<RawStatement>;\n}\n\nexport interface Store {\n readonly name: StoreName;\n readonly capabilities: ReadonlySet<Capability>;\n exec(sql: string): Promise<void>;\n prepare(sql: string): Promise<Statement>;\n // One crossing, N rows: every external write loop (search's candidate insert, graph ring's\n // temp-table writes) goes through this instead of looping over `run()`. Same contract as Connection.runBatch.\n runBatch(sql: string, paramRows: unknown[][]): Promise<void>;\n // Pins one snapshot across multi-statement reads; `map` and `peek` use it. A network-bound\n // write (search's embedding top-up) must stay outside it. Nesting joins the enclosing transaction.\n transaction<T>(fn: () => Promise<T>): Promise<T>;\n docs: DocumentStore;\n lexical: LexicalIndex;\n vectors: VectorStore;\n raw: SqlSession;\n // Engine-level facts for `sense status` (e.g. a derived busy_timeout PRAGMA reading); each\n // store owns what it reports and how it is worded. Empty when there is nothing to report.\n engineStatus(): Promise<Record<string, string>>;\n close(): Promise<void>;\n}\n"],"names":["CAPABILITY_NAMES"],"mappings":"AAAA,yFAAyF;AACzF,4FAA4F;;;;;+BAU/EA;;;eAAAA;;;AAAN,IAAMA,mBAAmB;IAAC;IAAW;IAAY;IAAqB;IAAW;IAAW;CAAgB"}
@@ -19,12 +19,9 @@ _export(exports, {
19
19
  var _indexts = require("../chunk/index.js");
20
20
  var _frontmatterts = require("../scan/frontmatter.js");
21
21
  function textFromBlocks(blocks) {
22
- var text = blocks.map(function(block) {
23
- return (0, _indexts.extractText)(block.node);
24
- }).filter(function(s) {
25
- return s.length > 0;
26
- }).join('\n');
27
- return (0, _frontmatterts.normalizeText)(text);
22
+ return (0, _frontmatterts.normalizeText)((0, _indexts.extractTexts)(blocks.map(function(block) {
23
+ return block.node;
24
+ })));
28
25
  }
29
26
  function stripText(value) {
30
27
  return textFromBlocks((0, _indexts.parse)(value));
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/text/strip.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport { extractText, parse } from '../chunk/index.ts';\nimport { normalizeText } from '../scan/frontmatter.ts';\n\n// Flat prose from blocks a caller already parsed, via the same extractText the chunker uses\n// (src/chunk) -- words and vectors read one definition of \"the prose of this note\".\nexport function textFromBlocks(blocks: Block[]): string {\n const text = blocks\n .map((block) => extractText(block.node))\n .filter((s) => s.length > 0)\n .join('\\n');\n return normalizeText(text);\n}\n\n// Thin parse + delegate, for callers with no blocks of their own already.\nexport function stripText(value: string): string {\n return textFromBlocks(parse(value));\n}\n"],"names":["stripText","textFromBlocks","blocks","text","map","block","extractText","node","filter","s","length","join","normalizeText","value","parse"],"mappings":";;;;;;;;;;;QAegBA;eAAAA;;QATAC;eAAAA;;;uBALmB;6BACL;AAIvB,SAASA,eAAeC,MAAe;IAC5C,IAAMC,OAAOD,OACVE,GAAG,CAAC,SAACC;eAAUC,IAAAA,oBAAW,EAACD,MAAME,IAAI;OACrCC,MAAM,CAAC,SAACC;eAAMA,EAAEC,MAAM,GAAG;OACzBC,IAAI,CAAC;IACR,OAAOC,IAAAA,4BAAa,EAACT;AACvB;AAGO,SAASH,UAAUa,KAAa;IACrC,OAAOZ,eAAea,IAAAA,cAAK,EAACD;AAC9B"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/text/strip.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport { extractTexts, parse } from '../chunk/index.ts';\nimport { normalizeText } from '../scan/frontmatter.ts';\n\n// Flat prose from blocks a caller already parsed, via the same extractor the chunker uses\n// (src/chunk) -- words and vectors read one definition of \"the prose of this note\".\nexport function textFromBlocks(blocks: Block[]): string {\n return normalizeText(extractTexts(blocks.map((block) => block.node)));\n}\n\n// Thin parse + delegate, for callers with no blocks of their own already.\nexport function stripText(value: string): string {\n return textFromBlocks(parse(value));\n}\n"],"names":["stripText","textFromBlocks","blocks","normalizeText","extractTexts","map","block","node","value","parse"],"mappings":";;;;;;;;;;;QAWgBA;eAAAA;;QALAC;eAAAA;;;uBALoB;6BACN;AAIvB,SAASA,eAAeC,MAAe;IAC5C,OAAOC,IAAAA,4BAAa,EAACC,IAAAA,qBAAY,EAACF,OAAOG,GAAG,CAAC,SAACC;eAAUA,MAAMC,IAAI;;AACpE;AAGO,SAASP,UAAUQ,KAAa;IACrC,OAAOP,eAAeQ,IAAAA,cAAK,EAACD;AAC9B"}
package/dist/cjs/watch.js CHANGED
@@ -144,7 +144,7 @@ var HEARTBEAT_INTERVAL_MS = 5000;
144
144
  var STALE_HEARTBEAT_MS = 15000;
145
145
  function runWatch(_0) {
146
146
  return _async_to_generator(function(cfg) {
147
- var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, onEvent, debounceMs, heartbeatIntervalMs, _ref, store, dbPath, initialWarnings, initialParsed, baseDir, existingHeartbeat, age, _tmp, stopping, touchHeartbeat, debounceTimer, inFlight, scheduleReconcile, watcher, heartbeatTimer;
147
+ var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, onEvent, debounceMs, heartbeatIntervalMs, _ref, store, builder, dbPath, initialWarnings, initialParsed, baseDir, existingHeartbeat, age, _tmp, stopping, touchHeartbeat, debounceTimer, inFlight, scheduleReconcile, watcher, heartbeatTimer;
148
148
  var _arguments = arguments;
149
149
  return _ts_generator(this, function(_state) {
150
150
  switch(_state.label){
@@ -156,10 +156,10 @@ function runWatch(_0) {
156
156
  heartbeatIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : HEARTBEAT_INTERVAL_MS;
157
157
  return [
158
158
  4,
159
- (0, _indexts1.openStore)(cfg)
159
+ (0, _indexts1.openStoreForWatch)(cfg)
160
160
  ];
161
161
  case 1:
162
- _ref = _state.sent(), store = _ref.store, dbPath = _ref.dbPath, initialWarnings = _ref.warnings, initialParsed = _ref.parsed;
162
+ _ref = _state.sent(), store = _ref.store, builder = _ref.builder, dbPath = _ref.dbPath, initialWarnings = _ref.warnings, initialParsed = _ref.parsed;
163
163
  baseDir = cfg.baseDir;
164
164
  return [
165
165
  4,
@@ -169,21 +169,27 @@ function runWatch(_0) {
169
169
  existingHeartbeat = _state.sent();
170
170
  if (!(existingHeartbeat && !opts.force)) return [
171
171
  3,
172
- 4
172
+ 5
173
173
  ];
174
174
  age = Date.now() - Date.parse(existingHeartbeat);
175
175
  if (!(age >= 0 && age < STALE_HEARTBEAT_MS)) return [
176
176
  3,
177
- 4
177
+ 5
178
178
  ];
179
179
  return [
180
180
  4,
181
- store.close()
181
+ builder.close()
182
182
  ];
183
183
  case 3:
184
184
  _state.sent();
185
- throw new _errorsts.SenseError('WATCH_ACTIVE', "another watcher appears active (heartbeat ".concat(Math.round(age / 1000), "s ago); use --force to override"));
185
+ return [
186
+ 4,
187
+ store.close()
188
+ ];
186
189
  case 4:
190
+ _state.sent();
191
+ throw new _errorsts.SenseError('WATCH_ACTIVE', "another watcher appears active (heartbeat ".concat(Math.round(age / 1000), "s ago); use --force to override"));
192
+ case 5:
187
193
  onEvent({
188
194
  type: 'started',
189
195
  baseDir: baseDir,
@@ -191,7 +197,7 @@ function runWatch(_0) {
191
197
  });
192
198
  if (!(initialWarnings.length > 0 || initialParsed > 0)) return [
193
199
  3,
194
- 6
200
+ 7
195
201
  ];
196
202
  _tmp = {
197
203
  type: 'reconciled',
@@ -201,12 +207,12 @@ function runWatch(_0) {
201
207
  4,
202
208
  (0, _indexts1.docCount)(store)
203
209
  ];
204
- case 5:
210
+ case 6:
205
211
  onEvent.apply(void 0, [
206
212
  (_tmp.total = _state.sent(), _tmp.warnings = initialWarnings, _tmp)
207
213
  ]);
208
- _state.label = 6;
209
- case 6:
214
+ _state.label = 7;
215
+ case 7:
210
216
  stopping = false;
211
217
  touchHeartbeat = function touchHeartbeat() {
212
218
  return _async_to_generator(function() {
@@ -236,7 +242,7 @@ function runWatch(_0) {
236
242
  4,
237
243
  touchHeartbeat()
238
244
  ];
239
- case 7:
245
+ case 8:
240
246
  _state.sent();
241
247
  debounceTimer = null;
242
248
  // A reconcile that has already started owns the store, and on a bulk reparse a live worker
@@ -260,7 +266,7 @@ function runWatch(_0) {
260
266
  ]);
261
267
  return [
262
268
  4,
263
- store.reconcile()
269
+ builder.build()
264
270
  ];
265
271
  case 1:
266
272
  _ref = _state.sent(), parsed = _ref.parsed, warnings = _ref.warnings;
@@ -346,21 +352,27 @@ function runWatch(_0) {
346
352
  _state.sent();
347
353
  return [
348
354
  4,
349
- (0, _indexts1.setMeta)(store, 'watch_heartbeat', null)
355
+ builder.close()
350
356
  ];
351
357
  case 2:
352
358
  _state.sent();
353
359
  return [
354
360
  4,
355
- (0, _indexts1.setMeta)(store, 'watch_pid', null)
361
+ (0, _indexts1.setMeta)(store, 'watch_heartbeat', null)
356
362
  ];
357
363
  case 3:
358
364
  _state.sent();
359
365
  return [
360
366
  4,
361
- store.close()
367
+ (0, _indexts1.setMeta)(store, 'watch_pid', null)
362
368
  ];
363
369
  case 4:
370
+ _state.sent();
371
+ return [
372
+ 4,
373
+ store.close()
374
+ ];
375
+ case 5:
364
376
  _state.sent();
365
377
  resolveShutdown();
366
378
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, requireWatchConcurrency, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n requireWatchConcurrency(cfg);\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n // A reconcile that has already started owns the store, and on a bulk reparse a live worker\n // pool as well. Shutdown waits on this rather than closing the connection underneath it.\n let inFlight: Promise<void> | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n inFlight = (async () => {\n try {\n const { parsed, warnings } = await store.reconcile();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n } finally {\n inFlight = null;\n }\n })();\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever. An event with\n // an unresolvable filename (null, which fs.watch delivers under load) reconciles: parsing nothing costs less than missing a real edit.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await inFlight;\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["runWatch","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","dbPath","initialWarnings","initialParsed","baseDir","existingHeartbeat","age","stopping","touchHeartbeat","debounceTimer","inFlight","scheduleReconcile","watcher","heartbeatTimer","requireWatchConcurrency","openStore","warnings","parsed","getMeta","force","Date","now","parse","close","SenseError","Math","round","type","length","docCount","total","setMeta","toISOString","String","process","pid","clearTimeout","setTimeout","err","reconcile","message","fsWatch","recursive","_event","filename","startsWith","STATE_DIR","setInterval","guardedTick","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":";;;;+BAwBsBA;;;eAAAA;;;sBAxBW;uBAEP;wBACC;6BACC;wBACmD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE/E,uJAAuJ;AACvJ,IAAMC,cAAc;AACpB,IAAMC,wBAAwB;AAC9B,IAAMC,qBAAqB;AAcpB,SAAeH;wCAASI,GAAmB;YAAEC,MAElCA,eACGA,kBACSA,2BAFtBC,SACAC,YACAC,qBACsE,MAApEC,OAAOC,QAAkBC,iBAAyBC,eACpDC,SAEAC,mBAEEC,WAYJC,UACEC,gBAMFC,eAGAC,UACEC,mBAmBAC,SAIAC;;;;;oBAxD4CjB,OAAAA,oEAAqB,CAAC;oBACxEkB,IAAAA,iCAAuB,EAACnB;oBAClBE,WAAUD,gBAAAA,KAAKC,OAAO,cAAZD,2BAAAA,gBAAiB,YAAO;oBAClCE,cAAaF,mBAAAA,KAAKE,UAAU,cAAfF,8BAAAA,mBAAmBJ;oBAChCO,uBAAsBH,4BAAAA,KAAKG,mBAAmB,cAAxBH,uCAAAA,4BAA4BH;oBACoB;;wBAAMsB,IAAAA,mBAAS,EAACpB;;;oBAAhB,OAAA,eAApEK,QAAoE,KAApEA,OAAOC,SAA6D,KAA7DA,QAAkBC,kBAA2C,KAArDc,UAAmCb,gBAAkB,KAA1Bc;oBAC5Cb,UAAUT,IAAIS,OAAO;oBAED;;wBAAMc,IAAAA,iBAAO,EAAClB,OAAO;;;oBAAzCK,oBAAoB;yBACtBA,CAAAA,qBAAqB,CAACT,KAAKuB,KAAK,AAAD,GAA/Bd;;;;oBACIC,MAAMc,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAACjB;yBAChCC,CAAAA,OAAO,KAAKA,MAAMZ,kBAAiB,GAAnCY;;;;oBACF;;wBAAMN,MAAMuB,KAAK;;;oBAAjB;oBACA,MAAM,IAAIC,oBAAU,CAAC,gBAAgB,AAAC,6CAAmE,OAAvBC,KAAKC,KAAK,CAACpB,MAAM,OAAM;;oBAI7GT,QAAQ;wBAAE8B,MAAM;wBAAWvB,SAAAA;wBAASH,QAAAA;oBAAO;yBACvCC,CAAAA,gBAAgB0B,MAAM,GAAG,KAAKzB,gBAAgB,CAAA,GAA9CD;;;;;wBACQyB,MAAM;wBAAcV,QAAQd;;oBAAsB;;wBAAM0B,IAAAA,kBAAQ,EAAC7B;;;oBAA3EH;8BAAqDiC,QAAO,oBAAuBd,WAAUd;;;;oBAG3FK,WAAW;oBACTC,iBAAiB;;;;;wCACrB;;4CAAMuB,IAAAA,iBAAO,EAAC/B,OAAO,mBAAmB,IAAIoB,OAAOY,WAAW;;;wCAA9D;wCACA;;4CAAMD,IAAAA,iBAAO,EAAC/B,OAAO,aAAaiC,OAAOC,QAAQC,GAAG;;;wCAApD;;;;;;wBACF;;oBACA;;wBAAM3B;;;oBAAN;oBAEIC,gBAAuC;oBAC3C,2FAA2F;oBAC3F,yFAAyF;oBACrFC,WAAiC;oBAC/BC,oBAAoB;wBACxB,IAAIF,eAAe2B,aAAa3B;wBAChCA,gBAAgB4B,WAAW;4BACzB5B,gBAAgB;4BAChBC,WAAW,AAAC;;wCAEqB,MAArBO,QAAQD,gBAETsB;;;;;;;;;;gDAFsB;;oDAAMtC,MAAMuC,SAAS;;;gDAArB,OAAA,eAArBtB,SAAqB,KAArBA,QAAQD,WAAa,KAAbA;;oDACNW,MAAM;oDAAcV,QAAAA;;gDAAe;;oDAAMY,IAAAA,kBAAQ,EAAC7B;;;gDAA5DH;0DAAsCiC,QAAO,oBAAuBd,WAAAA;;;;;;;gDAC7DsB;gDACPzC,QAAQ;oDAAE8B,MAAM;oDAAmBa,SAAS,AAACF,IAAcE,OAAO;gDAAC;;;;;;gDAEnE9B,WAAW;;;;;;;;;;gCAEf;;wBACF,GAAGZ;oBACL;oBAEA,iGAAiG;oBACjG,uIAAuI;oBACjIc,UAAU6B,IAAAA,aAAO,EAACrC,SAAS;wBAAEsC,WAAW;oBAAK,GAAG,SAACC,QAAQC;wBAC7D,IAAI,OAAOA,aAAa,YAAYA,SAASC,UAAU,CAACC,kBAAS,GAAG;wBACpEnC;oBACF;oBACME,iBAAiBkC,YACrBC,IAAAA,0BAAW,EAACxC,gBAAgB;+BAAMD;wBAClCR;oBAGF;;wBAAO,IAAIkD,QAAc,SAACC;gCAoBpBtD,cACCA;4BApBL,oFAAoF;4BACpF,oFAAoF;4BACpF,IAAMuD,WAAW;;wCAKfvD;;;;gDAJA,IAAIW,UAAU;;;gDACdA,WAAW;gDACX2B,QAAQkB,GAAG,CAAC,UAAUD;gDACtBjB,QAAQkB,GAAG,CAAC,WAAWD;iDACvBvD,eAAAA,KAAKyD,MAAM,cAAXzD,mCAAAA,aAAa0D,mBAAmB,CAAC,SAASH;gDAC1CI,cAAc1C;gDACd,IAAIJ,eAAe2B,aAAa3B;gDAChCG,QAAQW,KAAK;gDACb;;oDAAMb;;;gDAAN;gDACA;;oDAAMqB,IAAAA,iBAAO,EAAC/B,OAAO,mBAAmB;;;gDAAxC;gDACA;;oDAAM+B,IAAAA,iBAAO,EAAC/B,OAAO,aAAa;;;gDAAlC;gDACA;;oDAAMA,MAAMuB,KAAK;;;gDAAjB;gDACA2B;;;;;;gCACF;;4BACAhB,QAAQsB,IAAI,CAAC,UAAUL;4BACvBjB,QAAQsB,IAAI,CAAC,WAAWL;4BACxB,KAAIvD,eAAAA,KAAKyD,MAAM,cAAXzD,mCAAAA,aAAa6D,OAAO,EAAEN;kCACrBvD,gBAAAA,KAAKyD,MAAM,cAAXzD,oCAAAA,cAAa8D,gBAAgB,CAAC,SAASP,UAAU;gCAAEK,MAAM;4BAAK;wBACrE;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStoreForWatch, requireWatchConcurrency, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n requireWatchConcurrency(cfg);\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, builder, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStoreForWatch(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await builder.close();\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n // A reconcile that has already started owns the store, and on a bulk reparse a live worker\n // pool as well. Shutdown waits on this rather than closing the connection underneath it.\n let inFlight: Promise<void> | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n inFlight = (async () => {\n try {\n const { parsed, warnings } = await builder.build();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n } finally {\n inFlight = null;\n }\n })();\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever. An event with\n // an unresolvable filename (null, which fs.watch delivers under load) reconciles: parsing nothing costs less than missing a real edit.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await inFlight;\n await builder.close();\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["runWatch","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","builder","dbPath","initialWarnings","initialParsed","baseDir","existingHeartbeat","age","stopping","touchHeartbeat","debounceTimer","inFlight","scheduleReconcile","watcher","heartbeatTimer","requireWatchConcurrency","openStoreForWatch","warnings","parsed","getMeta","force","Date","now","parse","close","SenseError","Math","round","type","length","docCount","total","setMeta","toISOString","String","process","pid","clearTimeout","setTimeout","err","build","message","fsWatch","recursive","_event","filename","startsWith","STATE_DIR","setInterval","guardedTick","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  import type { Nodes } from 'mdast';
2
2
  export declare function extractText(node: Nodes): string;
3
+ export declare function extractTexts(nodes: Nodes[]): string;
@@ -36,7 +36,7 @@ function embedText(inner) {
36
36
  return anchorText(pipeIdx === -1 ? inner : inner.slice(0, pipeIdx));
37
37
  }
38
38
  function resolveFlavor(text) {
39
- return text.replace(COMMENT_PAIR, '').replace(EMBED, (_, inner)=>embedText(inner)).replace(WIKILINK, (_, inner)=>wikilinkText(inner)).replace(BLOCK_ID, '');
39
+ return text.replace(HTML_COMMENT, '').replace(COMMENT_PAIR, '').replace(EMBED, (_, inner)=>embedText(inner)).replace(WIKILINK, (_, inner)=>wikilinkText(inner)).replace(BLOCK_ID, '');
40
40
  }
41
41
  // Sibling blocks (list items, blockquote content, footnote bodies) joined one per line;
42
42
  // empties dropped so a blank child never widens the gap between its neighbors.
@@ -108,7 +108,15 @@ const CODE_PLACEHOLDER = /\uE000(\d+)\uE000/g;
108
108
  // Plain text of one mdast node (or a whole tree): heading/list/table structure is kept as text,
109
109
  // markup (emphasis, link targets, task and callout markers) is dropped. Pure, synchronous.
110
110
  export function extractText(node) {
111
+ return extractTexts([
112
+ node
113
+ ]);
114
+ }
115
+ // Blocks resolved together: a blank line inside %%...%% splits it across blocks, and a per-block
116
+ // strip then never sees the closing %%. Code stays placeholder-held across all of them.
117
+ export function extractTexts(nodes) {
111
118
  const code = [];
112
- const resolved = resolveFlavor(extractNode(node, code));
119
+ const joined = nodes.map((n)=>extractNode(n, code)).filter((s)=>s.length > 0).join('\n');
120
+ const resolved = resolveFlavor(joined);
113
121
  return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i)=>code[Number(i)]);
114
122
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/extract.ts"],"sourcesContent":["import type { Nodes } from 'mdast';\n\n// mdast has no wikilink, embed, or %%comment%% node -- literal text resolved by regex passes\n// over non-code text (code/inlineCode is placeholder-held and spliced back verbatim, below).\nconst COMMENT_PAIR = /%%[\\s\\S]*?%%/g;\n// An embed always yields its target, whatever follows a pipe (a resize suffix, never an alias);\n// must run before the wikilink regex below, bang included, or that regex matches first.\nconst EMBED = /!\\[\\[([^\\]]+)\\]\\]/g;\nconst WIKILINK = /\\[\\[([^\\]]+)\\]\\]/g;\n// A trailing \" ^id\" is an Obsidian block anchor, valid only at a line's end; a caret elsewhere\n// (mid-line) is ordinary text and is left alone.\nconst BLOCK_ID = / \\^[A-Za-z0-9-]+$/gm;\n// A callout marker is only ever the first thing in a blockquote's text (Obsidian's grammar),\n// so this anchors to blockquote output alone -- never applied to prose in general.\nconst CALLOUT_MARKER = /^\\[!\\w[\\w-]*\\][+-]?[ \\t]?/;\n// An html node's value is raw HTML, block or inline: a full <!-- --> comment is dropped, any\n// remaining tags are stripped, and the text a browser would still render survives.\nconst HTML_COMMENT = /<!--[\\s\\S]*?-->/g;\nconst HTML_TAG = /<[^>]+>/g;\n\nfunction stripHtml(value: string): string {\n return value.replace(HTML_COMMENT, '').replace(HTML_TAG, '');\n}\n\n// A `#anchor` keeps its text (hash dropped), unlike links.ts's parseWikilinkInner (the resolver\n// authority), which discards it entirely -- replicated here since that return value doesn't fit.\nfunction anchorText(base: string): string {\n const hashIdx = base.indexOf('#');\n if (hashIdx === -1) return base.trim();\n const target = base.slice(0, hashIdx).trim();\n const anchor = base.slice(hashIdx + 1).trim();\n return target ? `${target} ${anchor}` : anchor;\n}\n\nfunction wikilinkText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return pipeIdx === -1 ? anchorText(inner) : inner.slice(pipeIdx + 1).trim();\n}\n\nfunction embedText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return anchorText(pipeIdx === -1 ? inner : inner.slice(0, pipeIdx));\n}\n\nfunction resolveFlavor(text: string): string {\n return text\n .replace(COMMENT_PAIR, '')\n .replace(EMBED, (_, inner: string) => embedText(inner))\n .replace(WIKILINK, (_, inner: string) => wikilinkText(inner))\n .replace(BLOCK_ID, '');\n}\n\n// Sibling blocks (list items, blockquote content, footnote bodies) joined one per line;\n// empties dropped so a blank child never widens the gap between its neighbors.\nfunction joinBlocks(nodes: Nodes[], code: string[]): string {\n return nodes\n .map((n) => extractNode(n, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n}\n\n// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the\n// source text nodes already carry whatever spacing separates them.\nfunction joinInline(nodes: Nodes[], code: string[]): string {\n return nodes.map((n) => extractNode(n, code)).join('');\n}\n\n// U+E000 (Private Use Area) never appears in real markdown text, so it is a collision-free\n// placeholder delimiter, spliced back to the literal code value after flavor resolution.\nfunction codePlaceholder(value: string, code: string[]): string {\n const idx = code.push(value) - 1;\n return `\\uE000${idx}\\uE000`;\n}\n\n// A GFM autolink's display text is its own target, missing only the scheme\n// mdast-util-gfm-autolink-literal fills into node.url (http:// for www., mailto: for an email).\nconst SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:\\/{0,2}/i;\nfunction isAutolink(url: string, text: string): boolean {\n return url === text || url.replace(SCHEME_PREFIX, '') === text;\n}\n\nfunction extractNode(node: Nodes, code: string[]): string {\n switch (node.type) {\n case 'root':\n case 'list':\n case 'listItem':\n case 'footnoteDefinition':\n return joinBlocks(node.children, code);\n case 'blockquote':\n return joinBlocks(node.children, code).replace(CALLOUT_MARKER, '');\n case 'link': {\n const text = joinInline(node.children, code);\n return isAutolink(node.url, text) ? '' : text;\n }\n case 'heading':\n case 'paragraph':\n case 'linkReference':\n case 'emphasis':\n case 'strong':\n case 'delete':\n return joinInline(node.children, code);\n case 'table':\n return node.children.map((n) => extractNode(n, code)).join('\\n');\n case 'tableRow':\n return node.children.map((n) => extractNode(n, code)).join(' ');\n case 'tableCell':\n return joinInline(node.children, code);\n case 'image':\n case 'imageReference':\n return node.alt ?? '';\n case 'text':\n return node.value;\n case 'inlineCode':\n case 'code':\n return codePlaceholder(node.value, code);\n case 'html':\n return stripHtml(node.value);\n case 'break':\n return ' ';\n default:\n return '';\n }\n}\n\nconst CODE_PLACEHOLDER = /\\uE000(\\d+)\\uE000/g;\n\n// Plain text of one mdast node (or a whole tree): heading/list/table structure is kept as text,\n// markup (emphasis, link targets, task and callout markers) is dropped. Pure, synchronous.\nexport function extractText(node: Nodes): string {\n const code: string[] = [];\n const resolved = resolveFlavor(extractNode(node, code));\n return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i: string) => code[Number(i)]);\n}\n"],"names":["COMMENT_PAIR","EMBED","WIKILINK","BLOCK_ID","CALLOUT_MARKER","HTML_COMMENT","HTML_TAG","stripHtml","value","replace","anchorText","base","hashIdx","indexOf","trim","target","slice","anchor","wikilinkText","inner","pipeIdx","embedText","resolveFlavor","text","_","joinBlocks","nodes","code","map","n","extractNode","filter","s","length","join","joinInline","codePlaceholder","idx","push","SCHEME_PREFIX","isAutolink","url","node","type","children","alt","CODE_PLACEHOLDER","extractText","resolved","i","Number"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/extract.ts"],"sourcesContent":["import type { Nodes } from 'mdast';\n\n// mdast has no wikilink, embed, or %%comment%% node -- literal text resolved by regex passes\n// over non-code text (code/inlineCode is placeholder-held and spliced back verbatim, below).\nconst COMMENT_PAIR = /%%[\\s\\S]*?%%/g;\n// An embed always yields its target, whatever follows a pipe (a resize suffix, never an alias);\n// must run before the wikilink regex below, bang included, or that regex matches first.\nconst EMBED = /!\\[\\[([^\\]]+)\\]\\]/g;\nconst WIKILINK = /\\[\\[([^\\]]+)\\]\\]/g;\n// A trailing \" ^id\" is an Obsidian block anchor, valid only at a line's end; a caret elsewhere\n// (mid-line) is ordinary text and is left alone.\nconst BLOCK_ID = / \\^[A-Za-z0-9-]+$/gm;\n// A callout marker is only ever the first thing in a blockquote's text (Obsidian's grammar),\n// so this anchors to blockquote output alone -- never applied to prose in general.\nconst CALLOUT_MARKER = /^\\[!\\w[\\w-]*\\][+-]?[ \\t]?/;\n// An html node's value is raw HTML, block or inline: a full <!-- --> comment is dropped, any\n// remaining tags are stripped, and the text a browser would still render survives.\nconst HTML_COMMENT = /<!--[\\s\\S]*?-->/g;\nconst HTML_TAG = /<[^>]+>/g;\n\nfunction stripHtml(value: string): string {\n return value.replace(HTML_COMMENT, '').replace(HTML_TAG, '');\n}\n\n// A `#anchor` keeps its text (hash dropped), unlike links.ts's parseWikilinkInner (the resolver\n// authority), which discards it entirely -- replicated here since that return value doesn't fit.\nfunction anchorText(base: string): string {\n const hashIdx = base.indexOf('#');\n if (hashIdx === -1) return base.trim();\n const target = base.slice(0, hashIdx).trim();\n const anchor = base.slice(hashIdx + 1).trim();\n return target ? `${target} ${anchor}` : anchor;\n}\n\nfunction wikilinkText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return pipeIdx === -1 ? anchorText(inner) : inner.slice(pipeIdx + 1).trim();\n}\n\nfunction embedText(inner: string): string {\n const pipeIdx = inner.indexOf('|');\n return anchorText(pipeIdx === -1 ? inner : inner.slice(0, pipeIdx));\n}\n\nfunction resolveFlavor(text: string): string {\n return text\n .replace(HTML_COMMENT, '')\n .replace(COMMENT_PAIR, '')\n .replace(EMBED, (_, inner: string) => embedText(inner))\n .replace(WIKILINK, (_, inner: string) => wikilinkText(inner))\n .replace(BLOCK_ID, '');\n}\n\n// Sibling blocks (list items, blockquote content, footnote bodies) joined one per line;\n// empties dropped so a blank child never widens the gap between its neighbors.\nfunction joinBlocks(nodes: Nodes[], code: string[]): string {\n return nodes\n .map((n) => extractNode(n, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n}\n\n// Inline content (headings, paragraphs, emphasis, links) concatenated with no separator: the\n// source text nodes already carry whatever spacing separates them.\nfunction joinInline(nodes: Nodes[], code: string[]): string {\n return nodes.map((n) => extractNode(n, code)).join('');\n}\n\n// U+E000 (Private Use Area) never appears in real markdown text, so it is a collision-free\n// placeholder delimiter, spliced back to the literal code value after flavor resolution.\nfunction codePlaceholder(value: string, code: string[]): string {\n const idx = code.push(value) - 1;\n return `\\uE000${idx}\\uE000`;\n}\n\n// A GFM autolink's display text is its own target, missing only the scheme\n// mdast-util-gfm-autolink-literal fills into node.url (http:// for www., mailto: for an email).\nconst SCHEME_PREFIX = /^[a-z][a-z0-9+.-]*:\\/{0,2}/i;\nfunction isAutolink(url: string, text: string): boolean {\n return url === text || url.replace(SCHEME_PREFIX, '') === text;\n}\n\nfunction extractNode(node: Nodes, code: string[]): string {\n switch (node.type) {\n case 'root':\n case 'list':\n case 'listItem':\n case 'footnoteDefinition':\n return joinBlocks(node.children, code);\n case 'blockquote':\n return joinBlocks(node.children, code).replace(CALLOUT_MARKER, '');\n case 'link': {\n const text = joinInline(node.children, code);\n return isAutolink(node.url, text) ? '' : text;\n }\n case 'heading':\n case 'paragraph':\n case 'linkReference':\n case 'emphasis':\n case 'strong':\n case 'delete':\n return joinInline(node.children, code);\n case 'table':\n return node.children.map((n) => extractNode(n, code)).join('\\n');\n case 'tableRow':\n return node.children.map((n) => extractNode(n, code)).join(' ');\n case 'tableCell':\n return joinInline(node.children, code);\n case 'image':\n case 'imageReference':\n return node.alt ?? '';\n case 'text':\n return node.value;\n case 'inlineCode':\n case 'code':\n return codePlaceholder(node.value, code);\n case 'html':\n return stripHtml(node.value);\n case 'break':\n return ' ';\n default:\n return '';\n }\n}\n\nconst CODE_PLACEHOLDER = /\\uE000(\\d+)\\uE000/g;\n\n// Plain text of one mdast node (or a whole tree): heading/list/table structure is kept as text,\n// markup (emphasis, link targets, task and callout markers) is dropped. Pure, synchronous.\nexport function extractText(node: Nodes): string {\n return extractTexts([node]);\n}\n\n// Blocks resolved together: a blank line inside %%...%% splits it across blocks, and a per-block\n// strip then never sees the closing %%. Code stays placeholder-held across all of them.\nexport function extractTexts(nodes: Nodes[]): string {\n const code: string[] = [];\n const joined = nodes\n .map((n) => extractNode(n, code))\n .filter((s) => s.length > 0)\n .join('\\n');\n const resolved = resolveFlavor(joined);\n return code.length === 0 ? resolved : resolved.replace(CODE_PLACEHOLDER, (_, i: string) => code[Number(i)]);\n}\n"],"names":["COMMENT_PAIR","EMBED","WIKILINK","BLOCK_ID","CALLOUT_MARKER","HTML_COMMENT","HTML_TAG","stripHtml","value","replace","anchorText","base","hashIdx","indexOf","trim","target","slice","anchor","wikilinkText","inner","pipeIdx","embedText","resolveFlavor","text","_","joinBlocks","nodes","code","map","n","extractNode","filter","s","length","join","joinInline","codePlaceholder","idx","push","SCHEME_PREFIX","isAutolink","url","node","type","children","alt","CODE_PLACEHOLDER","extractText","extractTexts","joined","resolved","i","Number"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import type { Block, Chunk, ChunkOptions } from './types.js';
2
2
  export declare function chunkFromBlocks(blocks: Block[], body: string, opts?: ChunkOptions): Chunk[];
3
3
  export declare function chunk(body: string, opts?: ChunkOptions): Chunk[];
4
- export { extractText } from './extract.js';
4
+ export { extractText, extractTexts } from './extract.js';
5
5
  export { DEFAULT_TARGET_TOKENS, estimateTokens } from './group.js';
6
6
  export { parse } from './parse.js';
7
7
  export type { Block, BlockType, Chunk, ChunkOptions } from './types.js';
@@ -10,7 +10,7 @@ export function chunkFromBlocks(blocks, body, opts) {
10
10
  export function chunk(body, opts) {
11
11
  return chunkFromBlocks(parse(body), body, opts);
12
12
  }
13
- export { extractText } from './extract.js';
13
+ export { extractText, extractTexts } from './extract.js';
14
14
  export { DEFAULT_TARGET_TOKENS, estimateTokens } from './group.js';
15
15
  export { parse } from './parse.js';
16
16
  export { CHUNK_VERSION } from './version.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/index.ts"],"sourcesContent":["import { group } from './group.ts';\nimport { parse } from './parse.ts';\nimport type { Block, Chunk, ChunkOptions } from './types.ts';\n\n// Groups blocks a caller already parsed (e.g. scan/index.ts, sharing one parse with the FTS\n// text path) against the same body they came from, per opts (D1/D3).\nexport function chunkFromBlocks(blocks: Block[], body: string, opts?: ChunkOptions): Chunk[] {\n return group(blocks, body, opts);\n}\n\n// Pure, deterministic function of file content; chunk semantics are version-stamped via\n// CHUNK_VERSION (./version.ts). Algorithm and evidence: BENCHMARKING.md, \"The chunking algorithm\".\nexport function chunk(body: string, opts?: ChunkOptions): Chunk[] {\n return chunkFromBlocks(parse(body), body, opts);\n}\n\nexport { extractText } from './extract.ts';\nexport { DEFAULT_TARGET_TOKENS, estimateTokens } from './group.ts';\nexport { parse } from './parse.ts';\nexport type { Block, BlockType, Chunk, ChunkOptions } from './types.ts';\nexport { CHUNK_VERSION } from './version.ts';\n"],"names":["group","parse","chunkFromBlocks","blocks","body","opts","chunk","extractText","DEFAULT_TARGET_TOKENS","estimateTokens","CHUNK_VERSION"],"mappings":"AAAA,SAASA,KAAK,QAAQ,aAAa;AACnC,SAASC,KAAK,QAAQ,aAAa;AAGnC,4FAA4F;AAC5F,qEAAqE;AACrE,OAAO,SAASC,gBAAgBC,MAAe,EAAEC,IAAY,EAAEC,IAAmB;IAChF,OAAOL,MAAMG,QAAQC,MAAMC;AAC7B;AAEA,wFAAwF;AACxF,mGAAmG;AACnG,OAAO,SAASC,MAAMF,IAAY,EAAEC,IAAmB;IACrD,OAAOH,gBAAgBD,MAAMG,OAAOA,MAAMC;AAC5C;AAEA,SAASE,WAAW,QAAQ,eAAe;AAC3C,SAASC,qBAAqB,EAAEC,cAAc,QAAQ,aAAa;AACnE,SAASR,KAAK,QAAQ,aAAa;AAEnC,SAASS,aAAa,QAAQ,eAAe"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/chunk/index.ts"],"sourcesContent":["import { group } from './group.ts';\nimport { parse } from './parse.ts';\nimport type { Block, Chunk, ChunkOptions } from './types.ts';\n\n// Groups blocks a caller already parsed (e.g. scan/index.ts, sharing one parse with the FTS\n// text path) against the same body they came from, per opts (D1/D3).\nexport function chunkFromBlocks(blocks: Block[], body: string, opts?: ChunkOptions): Chunk[] {\n return group(blocks, body, opts);\n}\n\n// Pure, deterministic function of file content; chunk semantics are version-stamped via\n// CHUNK_VERSION (./version.ts). Algorithm and evidence: BENCHMARKING.md, \"The chunking algorithm\".\nexport function chunk(body: string, opts?: ChunkOptions): Chunk[] {\n return chunkFromBlocks(parse(body), body, opts);\n}\n\nexport { extractText, extractTexts } from './extract.ts';\nexport { DEFAULT_TARGET_TOKENS, estimateTokens } from './group.ts';\nexport { parse } from './parse.ts';\nexport type { Block, BlockType, Chunk, ChunkOptions } from './types.ts';\nexport { CHUNK_VERSION } from './version.ts';\n"],"names":["group","parse","chunkFromBlocks","blocks","body","opts","chunk","extractText","extractTexts","DEFAULT_TARGET_TOKENS","estimateTokens","CHUNK_VERSION"],"mappings":"AAAA,SAASA,KAAK,QAAQ,aAAa;AACnC,SAASC,KAAK,QAAQ,aAAa;AAGnC,4FAA4F;AAC5F,qEAAqE;AACrE,OAAO,SAASC,gBAAgBC,MAAe,EAAEC,IAAY,EAAEC,IAAmB;IAChF,OAAOL,MAAMG,QAAQC,MAAMC;AAC7B;AAEA,wFAAwF;AACxF,mGAAmG;AACnG,OAAO,SAASC,MAAMF,IAAY,EAAEC,IAAmB;IACrD,OAAOH,gBAAgBD,MAAMG,OAAOA,MAAMC;AAC5C;AAEA,SAASE,WAAW,EAAEC,YAAY,QAAQ,eAAe;AACzD,SAASC,qBAAqB,EAAEC,cAAc,QAAQ,aAAa;AACnE,SAAST,KAAK,QAAQ,aAAa;AAEnC,SAASU,aAAa,QAAQ,eAAe"}
@@ -21,5 +21,4 @@ export declare function embedConfig(cfg: Config): {
21
21
  languages?: string[];
22
22
  chunkTokens?: number;
23
23
  } | null;
24
- export declare function contentTokenize(cfg: Config): string | undefined;
25
24
  export declare function featureSignature(cfg: Config, features: Feature[]): string;
@@ -68,17 +68,15 @@ export function embedConfig(cfg) {
68
68
  chunkTokens: e.chunkTokens
69
69
  };
70
70
  }
71
- // The configured FTS5 tokenizer, or undefined for the built-in default. Undefined (not the default
72
- // string) lets featureSignature omit the segment for trees that never set it, avoiding a spurious rebuild on upgrade.
73
- export function contentTokenize(cfg) {
74
- var _cfg_content;
75
- const tokenize = (_cfg_content = cfg.content) === null || _cfg_content === void 0 ? void 0 : _cfg_content.tokenize;
76
- return tokenize === undefined || tokenize.trim() === '' ? undefined : tokenize.trim();
77
- }
78
71
  // Cache key over everything indexing derives from, so any change to those inputs rebuilds.
79
- // Segment order is fixed (features list, then `features` in registry order, then tokenize, then presets) since reordering alone would fire a spurious rebuild; tokenize/presets stay here because no Feature module owns either.
72
+ // Segment order is fixed (one `feature:<name>:on|off` per FEATURE_NAMES entry but embed, then
73
+ // `features` in registry order, then presets) since reordering alone would fire a spurious
74
+ // rebuild; presets stay here because no Feature module owns it.
80
75
  export function featureSignature(cfg, features) {
81
- const globalPart = enabledFeatures(cfg).filter((name)=>name !== 'embed').join(',');
76
+ // One segment per toggle (not one lumped csv) so store/signature.ts's changedSignatureKeys can
77
+ // tell which feature moved -- store/feature-scope.ts routes a recognised toggle to a narrow
78
+ // per-table invalidation instead of the full clear.
79
+ const togglesPart = FEATURE_NAMES.filter((name)=>name !== 'embed').map((name)=>`feature:${name}:${featureEnabled(cfg, name) ? 'on' : 'off'}`);
82
80
  const featureParts = features.map((feature)=>{
83
81
  var _feature_signature;
84
82
  return (_feature_signature = feature.signature) === null || _feature_signature === void 0 ? void 0 : _feature_signature.call(feature, cfg);
@@ -97,12 +95,9 @@ export function featureSignature(cfg, features) {
97
95
  ].sort().join('+');
98
96
  return `preset:${name}:${include}:${exclude}:${presetHasSignal(cfg, name, 'vectors') ? 'on' : 'off'}`;
99
97
  }).join('|');
100
- const tokenize = contentTokenize(cfg);
101
- const parts = [
102
- `features:${globalPart}`,
103
- ...featureParts
104
- ];
105
- if (tokenize !== undefined) parts.push(`tokenize:${tokenize}`);
106
- parts.push(presetsPart);
107
- return parts.join('|');
98
+ return [
99
+ ...togglesPart,
100
+ ...featureParts,
101
+ presetsPart
102
+ ].join('|');
108
103
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/access.ts"],"sourcesContent":["import type { Feature } from '../features/types.ts';\nimport type { SignalName, SignalWeights } from './signals.ts';\nimport { type Config, type EmbedConfig, FEATURE_NAMES, type FeatureName, type StoreName } from './types.ts';\n\nexport function presetNames(cfg: Config): string[] {\n return Object.keys(cfg.presets);\n}\n\n// The backing store engine this tree uses; absent means the default.\nexport function storeName(cfg: Config): StoreName {\n return cfg.store ?? 'sqlite';\n}\n\n// Whether the tree names a model at all.\nexport function embedEnabled(cfg: Config): boolean {\n return typeof cfg.embed?.model === 'string' && cfg.embed.model.length > 0;\n}\n\n// A preset's effective signal weights: declared exhaustively when `signals` is present, else\n// every signal whose prerequisite holds at weight 1 (words always, links when on, vectors when a model is named).\nexport function presetSignals(cfg: Config, name: string): SignalWeights {\n const declared = cfg.presets[name]?.signals;\n if (declared) return declared;\n const signals: SignalWeights = { words: 1 };\n if (cfg.features?.links !== false) signals.links = 1;\n if (embedEnabled(cfg)) signals.vectors = 1;\n return signals;\n}\n\nexport function presetHasSignal(cfg: Config, name: string, signal: SignalName): boolean {\n return presetSignals(cfg, name)[signal] !== undefined;\n}\n\n// Whether embedding needs to run: a model is named AND some preset's effective signals include vectors.\nexport function anyPresetEmbeds(cfg: Config): boolean {\n return presetNames(cfg).some((name) => presetHasSignal(cfg, name, 'vectors'));\n}\n\n// Opt-out features (default on): absent block or key means enabled. `rank` additionally requires\n// `links`. `embed` is derived (not a features-block member): on iff some preset's signals include vectors.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n if (name === 'embed') return anyPresetEmbeds(cfg);\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Feature states, so \"off\" and \"empty\" stay distinguishable in output. `embed` is excluded:\n// it is not a features-block toggle, and `status` reports it on its own line.\nexport function featureStates(cfg: Config): { on: FeatureName[]; off: FeatureName[] } {\n const reported = FEATURE_NAMES.filter((name) => name !== 'embed');\n return {\n on: reported.filter((name) => featureEnabled(cfg, name)),\n off: reported.filter((name) => !featureEnabled(cfg, name)),\n };\n}\n\n// Resolved embed provider settings, or null when the config names no model. No fallback to\n// DEFAULT_EMBED_MODEL: that's a template constant `init` and the v4 migration write into the file, never a runtime default.\nexport function embedConfig(cfg: Config): { model: string; provider: 'static' | 'openai' | 'cohere'; url?: string; key?: string; languages?: string[]; chunkTokens?: number } | null {\n if (!embedEnabled(cfg)) return null;\n const e = cfg.embed as EmbedConfig;\n return { model: e.model as string, provider: e.provider ?? 'static', url: e.url, key: e.key, languages: e.languages, chunkTokens: e.chunkTokens };\n}\n\n// The configured FTS5 tokenizer, or undefined for the built-in default. Undefined (not the default\n// string) lets featureSignature omit the segment for trees that never set it, avoiding a spurious rebuild on upgrade.\nexport function contentTokenize(cfg: Config): string | undefined {\n const tokenize = cfg.content?.tokenize;\n return tokenize === undefined || tokenize.trim() === '' ? undefined : tokenize.trim();\n}\n\n// Cache key over everything indexing derives from, so any change to those inputs rebuilds.\n// Segment order is fixed (features list, then `features` in registry order, then tokenize, then presets) since reordering alone would fire a spurious rebuild; tokenize/presets stay here because no Feature module owns either.\nexport function featureSignature(cfg: Config, features: Feature[]): string {\n const globalPart = enabledFeatures(cfg)\n .filter((name) => name !== 'embed')\n .join(',');\n const featureParts = features.map((feature) => feature.signature?.(cfg)).filter((part): part is string => part !== undefined);\n // One keyed segment per preset so a rebuild notice can name exactly which preset moved.\n const presetsPart = [...presetNames(cfg)]\n .sort()\n .map((name) => {\n const p = cfg.presets[name];\n const include = [...p.include].sort().join('+');\n const exclude = [...(p.exclude ?? [])].sort().join('+');\n return `preset:${name}:${include}:${exclude}:${presetHasSignal(cfg, name, 'vectors') ? 'on' : 'off'}`;\n })\n .join('|');\n const tokenize = contentTokenize(cfg);\n const parts = [`features:${globalPart}`, ...featureParts];\n if (tokenize !== undefined) parts.push(`tokenize:${tokenize}`);\n parts.push(presetsPart);\n return parts.join('|');\n}\n"],"names":["FEATURE_NAMES","presetNames","cfg","Object","keys","presets","storeName","store","embedEnabled","embed","model","length","presetSignals","name","declared","signals","words","features","links","vectors","presetHasSignal","signal","undefined","anyPresetEmbeds","some","featureEnabled","enabled","enabledFeatures","filter","featureStates","reported","on","off","embedConfig","e","provider","url","key","languages","chunkTokens","contentTokenize","tokenize","content","trim","featureSignature","globalPart","join","featureParts","map","feature","signature","part","presetsPart","sort","p","include","exclude","parts","push"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/access.ts"],"sourcesContent":["import type { Feature } from '../features/types.ts';\nimport type { SignalName, SignalWeights } from './signals.ts';\nimport { type Config, type EmbedConfig, FEATURE_NAMES, type FeatureName, type StoreName } from './types.ts';\n\nexport function presetNames(cfg: Config): string[] {\n return Object.keys(cfg.presets);\n}\n\n// The backing store engine this tree uses; absent means the default.\nexport function storeName(cfg: Config): StoreName {\n return cfg.store ?? 'sqlite';\n}\n\n// Whether the tree names a model at all.\nexport function embedEnabled(cfg: Config): boolean {\n return typeof cfg.embed?.model === 'string' && cfg.embed.model.length > 0;\n}\n\n// A preset's effective signal weights: declared exhaustively when `signals` is present, else\n// every signal whose prerequisite holds at weight 1 (words always, links when on, vectors when a model is named).\nexport function presetSignals(cfg: Config, name: string): SignalWeights {\n const declared = cfg.presets[name]?.signals;\n if (declared) return declared;\n const signals: SignalWeights = { words: 1 };\n if (cfg.features?.links !== false) signals.links = 1;\n if (embedEnabled(cfg)) signals.vectors = 1;\n return signals;\n}\n\nexport function presetHasSignal(cfg: Config, name: string, signal: SignalName): boolean {\n return presetSignals(cfg, name)[signal] !== undefined;\n}\n\n// Whether embedding needs to run: a model is named AND some preset's effective signals include vectors.\nexport function anyPresetEmbeds(cfg: Config): boolean {\n return presetNames(cfg).some((name) => presetHasSignal(cfg, name, 'vectors'));\n}\n\n// Opt-out features (default on): absent block or key means enabled. `rank` additionally requires\n// `links`. `embed` is derived (not a features-block member): on iff some preset's signals include vectors.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n if (name === 'embed') return anyPresetEmbeds(cfg);\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Feature states, so \"off\" and \"empty\" stay distinguishable in output. `embed` is excluded:\n// it is not a features-block toggle, and `status` reports it on its own line.\nexport function featureStates(cfg: Config): { on: FeatureName[]; off: FeatureName[] } {\n const reported = FEATURE_NAMES.filter((name) => name !== 'embed');\n return {\n on: reported.filter((name) => featureEnabled(cfg, name)),\n off: reported.filter((name) => !featureEnabled(cfg, name)),\n };\n}\n\n// Resolved embed provider settings, or null when the config names no model. No fallback to\n// DEFAULT_EMBED_MODEL: that's a template constant `init` and the v4 migration write into the file, never a runtime default.\nexport function embedConfig(cfg: Config): { model: string; provider: 'static' | 'openai' | 'cohere'; url?: string; key?: string; languages?: string[]; chunkTokens?: number } | null {\n if (!embedEnabled(cfg)) return null;\n const e = cfg.embed as EmbedConfig;\n return { model: e.model as string, provider: e.provider ?? 'static', url: e.url, key: e.key, languages: e.languages, chunkTokens: e.chunkTokens };\n}\n\n// Cache key over everything indexing derives from, so any change to those inputs rebuilds.\n// Segment order is fixed (one `feature:<name>:on|off` per FEATURE_NAMES entry but embed, then\n// `features` in registry order, then presets) since reordering alone would fire a spurious\n// rebuild; presets stay here because no Feature module owns it.\nexport function featureSignature(cfg: Config, features: Feature[]): string {\n // One segment per toggle (not one lumped csv) so store/signature.ts's changedSignatureKeys can\n // tell which feature moved -- store/feature-scope.ts routes a recognised toggle to a narrow\n // per-table invalidation instead of the full clear.\n const togglesPart = FEATURE_NAMES.filter((name) => name !== 'embed').map((name) => `feature:${name}:${featureEnabled(cfg, name) ? 'on' : 'off'}`);\n const featureParts = features.map((feature) => feature.signature?.(cfg)).filter((part): part is string => part !== undefined);\n // One keyed segment per preset so a rebuild notice can name exactly which preset moved.\n const presetsPart = [...presetNames(cfg)]\n .sort()\n .map((name) => {\n const p = cfg.presets[name];\n const include = [...p.include].sort().join('+');\n const exclude = [...(p.exclude ?? [])].sort().join('+');\n return `preset:${name}:${include}:${exclude}:${presetHasSignal(cfg, name, 'vectors') ? 'on' : 'off'}`;\n })\n .join('|');\n return [...togglesPart, ...featureParts, presetsPart].join('|');\n}\n"],"names":["FEATURE_NAMES","presetNames","cfg","Object","keys","presets","storeName","store","embedEnabled","embed","model","length","presetSignals","name","declared","signals","words","features","links","vectors","presetHasSignal","signal","undefined","anyPresetEmbeds","some","featureEnabled","enabled","enabledFeatures","filter","featureStates","reported","on","off","embedConfig","e","provider","url","key","languages","chunkTokens","featureSignature","togglesPart","map","featureParts","feature","signature","part","presetsPart","sort","p","include","join","exclude"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export { anyPresetEmbeds, contentTokenize, embedConfig, embedEnabled, enabledFeatures, featureEnabled, featureSignature, featureStates, presetHasSignal, presetNames, presetSignals, storeName } from './access.js';
1
+ export { anyPresetEmbeds, embedConfig, embedEnabled, enabledFeatures, featureEnabled, featureSignature, featureStates, presetHasSignal, presetNames, presetSignals, storeName } from './access.js';
2
2
  export type { InitOverrides } from './load.js';
3
3
  export { findConfigPath, initConfig, loadConfig, migrateConfig } from './load.js';
4
4
  export { resolvePreset, resolveSearch } from './resolve.js';