sensemaking 0.25.1 → 0.26.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 (316) hide show
  1. package/README.md +13 -9
  2. package/dist/cjs/cli/index.d.cts +1 -1
  3. package/dist/cjs/cli/index.d.ts +1 -1
  4. package/dist/cjs/cli/index.js +1 -1
  5. package/dist/cjs/cli/index.js.map +1 -1
  6. package/dist/cjs/cli/path.js +5 -29
  7. package/dist/cjs/cli/path.js.map +1 -1
  8. package/dist/cjs/cli/related.js +4 -3
  9. package/dist/cjs/cli/related.js.map +1 -1
  10. package/dist/cjs/commands/index.d.cts +6 -3
  11. package/dist/cjs/commands/index.d.ts +6 -3
  12. package/dist/cjs/commands/index.js +4 -0
  13. package/dist/cjs/commands/index.js.map +1 -1
  14. package/dist/cjs/commands/map.d.cts +14 -5
  15. package/dist/cjs/commands/map.d.ts +14 -5
  16. package/dist/cjs/commands/map.js +42 -35
  17. package/dist/cjs/commands/map.js.map +1 -1
  18. package/dist/cjs/commands/path.d.cts +14 -0
  19. package/dist/cjs/commands/path.d.ts +14 -0
  20. package/dist/cjs/commands/path.js +209 -0
  21. package/dist/cjs/commands/path.js.map +1 -0
  22. package/dist/cjs/commands/peek.d.cts +16 -1
  23. package/dist/cjs/commands/peek.d.ts +16 -1
  24. package/dist/cjs/commands/peek.js +71 -33
  25. package/dist/cjs/commands/peek.js.map +1 -1
  26. package/dist/cjs/commands/related.d.cts +16 -3
  27. package/dist/cjs/commands/related.d.ts +16 -3
  28. package/dist/cjs/commands/related.js +131 -98
  29. package/dist/cjs/commands/related.js.map +1 -1
  30. package/dist/cjs/commands/scope.d.cts +1 -0
  31. package/dist/cjs/commands/scope.d.ts +1 -0
  32. package/dist/cjs/commands/scope.js +149 -0
  33. package/dist/cjs/commands/scope.js.map +1 -1
  34. package/dist/cjs/commands/search.d.cts +26 -3
  35. package/dist/cjs/commands/search.d.ts +26 -3
  36. package/dist/cjs/commands/search.js +211 -71
  37. package/dist/cjs/commands/search.js.map +1 -1
  38. package/dist/cjs/commands/signals.d.cts +4 -3
  39. package/dist/cjs/commands/signals.d.ts +4 -3
  40. package/dist/cjs/commands/signals.js +13 -4
  41. package/dist/cjs/commands/signals.js.map +1 -1
  42. package/dist/cjs/commands/snapshot.d.cts +3 -0
  43. package/dist/cjs/commands/snapshot.d.ts +3 -0
  44. package/dist/cjs/commands/snapshot.js +180 -0
  45. package/dist/cjs/commands/snapshot.js.map +1 -0
  46. package/dist/cjs/embed/cohere.js +41 -16
  47. package/dist/cjs/embed/cohere.js.map +1 -1
  48. package/dist/cjs/embed/endpoint.d.cts +2 -0
  49. package/dist/cjs/embed/endpoint.d.ts +2 -0
  50. package/dist/cjs/embed/endpoint.js +30 -0
  51. package/dist/cjs/embed/endpoint.js.map +1 -0
  52. package/dist/cjs/embed/handoff.d.cts +7 -2
  53. package/dist/cjs/embed/handoff.d.ts +7 -2
  54. package/dist/cjs/embed/handoff.js +10 -0
  55. package/dist/cjs/embed/handoff.js.map +1 -1
  56. package/dist/cjs/embed/http.d.cts +18 -1
  57. package/dist/cjs/embed/http.d.ts +18 -1
  58. package/dist/cjs/embed/http.js +239 -40
  59. package/dist/cjs/embed/http.js.map +1 -1
  60. package/dist/cjs/embed/openai.js +39 -17
  61. package/dist/cjs/embed/openai.js.map +1 -1
  62. package/dist/cjs/embed/query.d.cts +9 -3
  63. package/dist/cjs/embed/query.d.ts +9 -3
  64. package/dist/cjs/embed/query.js +257 -90
  65. package/dist/cjs/embed/query.js.map +1 -1
  66. package/dist/cjs/embed/registry.d.cts +2 -2
  67. package/dist/cjs/embed/registry.d.ts +2 -2
  68. package/dist/cjs/embed/registry.js +36 -4
  69. package/dist/cjs/embed/registry.js.map +1 -1
  70. package/dist/cjs/embed/static.js +6 -2
  71. package/dist/cjs/embed/static.js.map +1 -1
  72. package/dist/cjs/embed/store.js +42 -77
  73. package/dist/cjs/embed/store.js.map +1 -1
  74. package/dist/cjs/embed/types.d.cts +5 -2
  75. package/dist/cjs/embed/types.d.ts +5 -2
  76. package/dist/cjs/embed/types.js.map +1 -1
  77. package/dist/cjs/features/embed.js +290 -43
  78. package/dist/cjs/features/embed.js.map +1 -1
  79. package/dist/cjs/graph/traverse.d.cts +2 -2
  80. package/dist/cjs/graph/traverse.d.ts +2 -2
  81. package/dist/cjs/graph/traverse.js +15 -5
  82. package/dist/cjs/graph/traverse.js.map +1 -1
  83. package/dist/cjs/index.d.cts +2 -2
  84. package/dist/cjs/index.d.ts +2 -2
  85. package/dist/cjs/index.js +6 -0
  86. package/dist/cjs/index.js.map +1 -1
  87. package/dist/cjs/output/output.d.cts +4 -36
  88. package/dist/cjs/output/output.d.ts +4 -36
  89. package/dist/cjs/output/output.js +1 -2
  90. package/dist/cjs/output/output.js.map +1 -1
  91. package/dist/cjs/scan/pool.d.cts +2 -1
  92. package/dist/cjs/scan/pool.d.ts +2 -1
  93. package/dist/cjs/scan/pool.js +116 -27
  94. package/dist/cjs/scan/pool.js.map +1 -1
  95. package/dist/cjs/scan/reparse.d.cts +1 -0
  96. package/dist/cjs/scan/reparse.d.ts +1 -0
  97. package/dist/cjs/scan/reparse.js +75 -21
  98. package/dist/cjs/scan/reparse.js.map +1 -1
  99. package/dist/cjs/store/builder.d.cts +11 -21
  100. package/dist/cjs/store/builder.d.ts +11 -21
  101. package/dist/cjs/store/builder.js +1077 -438
  102. package/dist/cjs/store/builder.js.map +1 -1
  103. package/dist/cjs/store/duckdb/connection.js +16 -2
  104. package/dist/cjs/store/duckdb/connection.js.map +1 -1
  105. package/dist/cjs/store/duckdb/open.d.cts +1 -1
  106. package/dist/cjs/store/duckdb/open.d.ts +1 -1
  107. package/dist/cjs/store/duckdb/open.js +73 -65
  108. package/dist/cjs/store/duckdb/open.js.map +1 -1
  109. package/dist/cjs/store/duckdb/reconcile.js +533 -15
  110. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  111. package/dist/cjs/store/embed-scope.js +19 -9
  112. package/dist/cjs/store/embed-scope.js.map +1 -1
  113. package/dist/cjs/store/index.d.cts +6 -0
  114. package/dist/cjs/store/index.d.ts +6 -0
  115. package/dist/cjs/store/index.js +8 -4
  116. package/dist/cjs/store/index.js.map +1 -1
  117. package/dist/cjs/store/open.d.cts +6 -1
  118. package/dist/cjs/store/open.d.ts +6 -1
  119. package/dist/cjs/store/open.js +230 -307
  120. package/dist/cjs/store/open.js.map +1 -1
  121. package/dist/cjs/store/readiness.d.cts +5 -0
  122. package/dist/cjs/store/readiness.d.ts +5 -0
  123. package/dist/cjs/store/readiness.js +39 -0
  124. package/dist/cjs/store/readiness.js.map +1 -0
  125. package/dist/cjs/store/reconcile.d.cts +26 -1
  126. package/dist/cjs/store/reconcile.d.ts +26 -1
  127. package/dist/cjs/store/reconcile.js +670 -576
  128. package/dist/cjs/store/reconcile.js.map +1 -1
  129. package/dist/cjs/store/signature.d.cts +1 -0
  130. package/dist/cjs/store/signature.d.ts +1 -0
  131. package/dist/cjs/store/signature.js +9 -0
  132. package/dist/cjs/store/signature.js.map +1 -1
  133. package/dist/cjs/store/sqlite/open.d.cts +1 -1
  134. package/dist/cjs/store/sqlite/open.d.ts +1 -1
  135. package/dist/cjs/store/sqlite/open.js +84 -90
  136. package/dist/cjs/store/sqlite/open.js.map +1 -1
  137. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  138. package/dist/cjs/store/sqlite/store.js +63 -60
  139. package/dist/cjs/store/sqlite/store.js.map +1 -1
  140. package/dist/cjs/store/sqlite/vectors.d.cts +2 -2
  141. package/dist/cjs/store/sqlite/vectors.d.ts +2 -2
  142. package/dist/cjs/store/sqlite/vectors.js +8 -4
  143. package/dist/cjs/store/sqlite/vectors.js.map +1 -1
  144. package/dist/cjs/store/stages.js.map +1 -1
  145. package/dist/cjs/store/turso/connection.d.cts +1 -0
  146. package/dist/cjs/store/turso/connection.d.ts +1 -0
  147. package/dist/cjs/store/turso/connection.js +319 -94
  148. package/dist/cjs/store/turso/connection.js.map +1 -1
  149. package/dist/cjs/store/turso/open.d.cts +1 -1
  150. package/dist/cjs/store/turso/open.d.ts +1 -1
  151. package/dist/cjs/store/turso/open.js +70 -74
  152. package/dist/cjs/store/turso/open.js.map +1 -1
  153. package/dist/cjs/store/turso/store.js +25 -23
  154. package/dist/cjs/store/turso/store.js.map +1 -1
  155. package/dist/cjs/store/turso/vectors.d.cts +2 -2
  156. package/dist/cjs/store/turso/vectors.d.ts +2 -2
  157. package/dist/cjs/store/turso/vectors.js +26 -16
  158. package/dist/cjs/store/turso/vectors.js.map +1 -1
  159. package/dist/cjs/store/types.d.cts +4 -1
  160. package/dist/cjs/store/types.d.ts +4 -1
  161. package/dist/cjs/store/types.js.map +1 -1
  162. package/dist/cjs/store/vectors.d.cts +14 -1
  163. package/dist/cjs/store/vectors.d.ts +14 -1
  164. package/dist/cjs/store/vectors.js +61 -0
  165. package/dist/cjs/store/vectors.js.map +1 -1
  166. package/dist/cjs/watch.d.cts +6 -0
  167. package/dist/cjs/watch.d.ts +6 -0
  168. package/dist/cjs/watch.js +42 -10
  169. package/dist/cjs/watch.js.map +1 -1
  170. package/dist/cjs/workers/parse.d.cts +4 -3
  171. package/dist/cjs/workers/parse.d.ts +4 -3
  172. package/dist/cjs/workers/parse.js +19 -17
  173. package/dist/cjs/workers/parse.js.map +1 -1
  174. package/dist/esm/cli/index.d.ts +1 -1
  175. package/dist/esm/cli/index.js +1 -1
  176. package/dist/esm/cli/index.js.map +1 -1
  177. package/dist/esm/cli/path.js +4 -10
  178. package/dist/esm/cli/path.js.map +1 -1
  179. package/dist/esm/cli/related.js +5 -3
  180. package/dist/esm/cli/related.js.map +1 -1
  181. package/dist/esm/commands/index.d.ts +6 -3
  182. package/dist/esm/commands/index.js +1 -0
  183. package/dist/esm/commands/index.js.map +1 -1
  184. package/dist/esm/commands/map.d.ts +14 -5
  185. package/dist/esm/commands/map.js +2 -0
  186. package/dist/esm/commands/map.js.map +1 -1
  187. package/dist/esm/commands/path.d.ts +14 -0
  188. package/dist/esm/commands/path.js +33 -0
  189. package/dist/esm/commands/path.js.map +1 -0
  190. package/dist/esm/commands/peek.d.ts +16 -1
  191. package/dist/esm/commands/peek.js +35 -9
  192. package/dist/esm/commands/peek.js.map +1 -1
  193. package/dist/esm/commands/related.d.ts +16 -3
  194. package/dist/esm/commands/related.js +45 -28
  195. package/dist/esm/commands/related.js.map +1 -1
  196. package/dist/esm/commands/scope.d.ts +1 -0
  197. package/dist/esm/commands/scope.js +58 -0
  198. package/dist/esm/commands/scope.js.map +1 -1
  199. package/dist/esm/commands/search.d.ts +26 -3
  200. package/dist/esm/commands/search.js +85 -18
  201. package/dist/esm/commands/search.js.map +1 -1
  202. package/dist/esm/commands/signals.d.ts +4 -3
  203. package/dist/esm/commands/signals.js +11 -4
  204. package/dist/esm/commands/signals.js.map +1 -1
  205. package/dist/esm/commands/snapshot.d.ts +3 -0
  206. package/dist/esm/commands/snapshot.js +25 -0
  207. package/dist/esm/commands/snapshot.js.map +1 -0
  208. package/dist/esm/embed/cohere.js +35 -10
  209. package/dist/esm/embed/cohere.js.map +1 -1
  210. package/dist/esm/embed/endpoint.d.ts +2 -0
  211. package/dist/esm/embed/endpoint.js +11 -0
  212. package/dist/esm/embed/endpoint.js.map +1 -0
  213. package/dist/esm/embed/handoff.d.ts +7 -2
  214. package/dist/esm/embed/handoff.js +7 -0
  215. package/dist/esm/embed/handoff.js.map +1 -1
  216. package/dist/esm/embed/http.d.ts +18 -1
  217. package/dist/esm/embed/http.js +137 -17
  218. package/dist/esm/embed/http.js.map +1 -1
  219. package/dist/esm/embed/openai.js +34 -12
  220. package/dist/esm/embed/openai.js.map +1 -1
  221. package/dist/esm/embed/query.d.ts +9 -3
  222. package/dist/esm/embed/query.js +98 -26
  223. package/dist/esm/embed/query.js.map +1 -1
  224. package/dist/esm/embed/registry.d.ts +2 -2
  225. package/dist/esm/embed/registry.js +34 -5
  226. package/dist/esm/embed/registry.js.map +1 -1
  227. package/dist/esm/embed/static.js +10 -2
  228. package/dist/esm/embed/static.js.map +1 -1
  229. package/dist/esm/embed/store.js +30 -21
  230. package/dist/esm/embed/store.js.map +1 -1
  231. package/dist/esm/embed/types.d.ts +5 -2
  232. package/dist/esm/embed/types.js.map +1 -1
  233. package/dist/esm/features/embed.js +82 -21
  234. package/dist/esm/features/embed.js.map +1 -1
  235. package/dist/esm/graph/traverse.d.ts +2 -2
  236. package/dist/esm/graph/traverse.js +4 -2
  237. package/dist/esm/graph/traverse.js.map +1 -1
  238. package/dist/esm/index.d.ts +2 -2
  239. package/dist/esm/index.js +1 -1
  240. package/dist/esm/index.js.map +1 -1
  241. package/dist/esm/output/output.d.ts +4 -36
  242. package/dist/esm/output/output.js +1 -2
  243. package/dist/esm/output/output.js.map +1 -1
  244. package/dist/esm/scan/pool.d.ts +2 -1
  245. package/dist/esm/scan/pool.js +80 -23
  246. package/dist/esm/scan/pool.js.map +1 -1
  247. package/dist/esm/scan/reparse.d.ts +1 -0
  248. package/dist/esm/scan/reparse.js +40 -12
  249. package/dist/esm/scan/reparse.js.map +1 -1
  250. package/dist/esm/store/builder.d.ts +11 -21
  251. package/dist/esm/store/builder.js +337 -133
  252. package/dist/esm/store/builder.js.map +1 -1
  253. package/dist/esm/store/duckdb/connection.js +9 -1
  254. package/dist/esm/store/duckdb/connection.js.map +1 -1
  255. package/dist/esm/store/duckdb/open.d.ts +1 -1
  256. package/dist/esm/store/duckdb/open.js +13 -9
  257. package/dist/esm/store/duckdb/open.js.map +1 -1
  258. package/dist/esm/store/duckdb/reconcile.js +145 -10
  259. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  260. package/dist/esm/store/embed-scope.js +16 -6
  261. package/dist/esm/store/embed-scope.js.map +1 -1
  262. package/dist/esm/store/index.d.ts +6 -0
  263. package/dist/esm/store/index.js +10 -4
  264. package/dist/esm/store/index.js.map +1 -1
  265. package/dist/esm/store/open.d.ts +6 -1
  266. package/dist/esm/store/open.js +77 -87
  267. package/dist/esm/store/open.js.map +1 -1
  268. package/dist/esm/store/readiness.d.ts +5 -0
  269. package/dist/esm/store/readiness.js +11 -0
  270. package/dist/esm/store/readiness.js.map +1 -0
  271. package/dist/esm/store/reconcile.d.ts +26 -1
  272. package/dist/esm/store/reconcile.js +142 -120
  273. package/dist/esm/store/reconcile.js.map +1 -1
  274. package/dist/esm/store/signature.d.ts +1 -0
  275. package/dist/esm/store/signature.js +8 -0
  276. package/dist/esm/store/signature.js.map +1 -1
  277. package/dist/esm/store/sqlite/open.d.ts +1 -1
  278. package/dist/esm/store/sqlite/open.js +12 -12
  279. package/dist/esm/store/sqlite/open.js.map +1 -1
  280. package/dist/esm/store/sqlite/reconcile.js +2 -2
  281. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  282. package/dist/esm/store/sqlite/store.js +21 -18
  283. package/dist/esm/store/sqlite/store.js.map +1 -1
  284. package/dist/esm/store/sqlite/vectors.d.ts +2 -2
  285. package/dist/esm/store/sqlite/vectors.js +7 -5
  286. package/dist/esm/store/sqlite/vectors.js.map +1 -1
  287. package/dist/esm/store/stages.js.map +1 -1
  288. package/dist/esm/store/turso/connection.d.ts +1 -0
  289. package/dist/esm/store/turso/connection.js +97 -28
  290. package/dist/esm/store/turso/connection.js.map +1 -1
  291. package/dist/esm/store/turso/open.d.ts +1 -1
  292. package/dist/esm/store/turso/open.js +9 -8
  293. package/dist/esm/store/turso/open.js.map +1 -1
  294. package/dist/esm/store/turso/store.js +16 -14
  295. package/dist/esm/store/turso/store.js.map +1 -1
  296. package/dist/esm/store/turso/vectors.d.ts +2 -2
  297. package/dist/esm/store/turso/vectors.js +27 -17
  298. package/dist/esm/store/turso/vectors.js.map +1 -1
  299. package/dist/esm/store/types.d.ts +4 -1
  300. package/dist/esm/store/types.js.map +1 -1
  301. package/dist/esm/store/vectors.d.ts +14 -1
  302. package/dist/esm/store/vectors.js +55 -0
  303. package/dist/esm/store/vectors.js.map +1 -1
  304. package/dist/esm/watch.d.ts +6 -0
  305. package/dist/esm/watch.js +40 -14
  306. package/dist/esm/watch.js.map +1 -1
  307. package/dist/esm/workers/parse.d.ts +4 -3
  308. package/dist/esm/workers/parse.js +19 -17
  309. package/dist/esm/workers/parse.js.map +1 -1
  310. package/package.json +1 -1
  311. package/skills/sense/SKILL.md +4 -2
  312. package/skills/sense/references/stores/duckdb.md +1 -1
  313. package/skills/sense-setup/SKILL.md +2 -2
  314. package/skills/sense-setup/references/embeddings.md +1 -1
  315. package/skills/sense-setup/references/store-benchmarks.md +5 -5
  316. package/skills/sense-setup/references/stores/duckdb.md +1 -1
@@ -27,6 +27,7 @@ export interface ReconcileDialect {
27
27
  beginMode(): string;
28
28
  checkColumnLimit(count: number): void;
29
29
  addColumns(conn: Connection, names: string[]): Promise<void>;
30
+ updateFrontmatter?(conn: Connection, writableColumns: string[], rows: unknown[][]): Promise<void>;
30
31
  reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, cfg: Config): Promise<void>;
31
32
  recordDuration?(conn: Connection, ms: number): Promise<void>;
32
33
  }
@@ -45,7 +46,9 @@ export interface OpenDialect<Handle> {
45
46
  observational?: boolean;
46
47
  }): Promise<void>;
47
48
  isLocked?(err: Error): boolean;
48
- ensureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;
49
+ ensureCoreSchema(handle: Handle, conn: Connection): Promise<void>;
50
+ ensureFeatureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;
51
+ prepareConnection?(handle: Handle, conn: Connection, cfg: Config): Promise<void>;
49
52
  setDerivedBusyTimeout?(handle: Handle, conn: Connection, ms: number): Promise<void>;
50
53
  prepareLexical?(conn: Connection): Promise<void>;
51
54
  assertLexicalReady?(conn: Connection): Promise<void>;
@@ -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, ResolvedConfig, StoreName } from '../config/index.ts';\nimport type { ReconcileDelta } from '../features/types.ts';\nimport type { ParsedDoc } from '../scan/index.ts';\n\n// 'vectors': the store's VectorStore is functionally implemented, not present-but-inert; checked\n// at open (index.ts) before a config with an \"embed\" block is allowed onto a store lacking it.\n// 'segment': the engine can run segment() (Intl.Segmenter grapheme clustering) as a SQL function;\n// turso's client cannot register it and has no rewrite for it, unlike has()/basename(), which now\n// work everywhere. An array, not a bare union, so a runtime check reads the same list the type does.\nexport const CAPABILITY_NAMES = ['vectors', 'segment'] 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 // Bulk-inserts rows that cannot conflict, through a path that binds no per-value parameters.\n // Optional: a store that has nothing faster than its own INSERT omits it and callers fall back\n // (appendRows in shared.ts). `columns` names the values each row carries; a table column the\n // caller does not write takes its default.\n appendRows?(table: string, columns: string[], rows: 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, options: { existingOnly: boolean; observational: boolean }): 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, options?: { observational?: boolean }): 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 // Persistent lexical preparation belongs to build, while no-build calls only the readiness\n // check. SQLite and Turso maintain lexical state during reconcile and omit both hooks.\n prepareLexical?(conn: Connection): Promise<void>;\n assertLexicalReady?(conn: Connection): Promise<void>;\n createStore(handle: Handle, conn: Connection, cfg: ResolvedConfig, options: { observational: boolean }): 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\n// A ranked match, nothing more. Snippets are cut from the document above the store\n// (commands/search.ts), identically for every engine, so no store carries them.\nexport interface LexicalHit {\n path: string;\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 snippet, 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;;;;;+BAW/EA;;;eAAAA;;;AAAN,IAAMA,mBAAmB;IAAC;IAAW;CAAU"}
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// 'vectors': the store's VectorStore is functionally implemented, not present-but-inert; checked\n// at open (index.ts) before a config with an \"embed\" block is allowed onto a store lacking it.\n// 'segment': the engine can run segment() (Intl.Segmenter grapheme clustering) as a SQL function;\n// turso's client cannot register it and has no rewrite for it, unlike has()/basename(), which now\n// work everywhere. An array, not a bare union, so a runtime check reads the same list the type does.\nexport const CAPABILITY_NAMES = ['vectors', 'segment'] 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 // Bulk-inserts rows that cannot conflict, through a path that binds no per-value parameters.\n // Optional: a store that has nothing faster than its own INSERT omits it and callers fall back\n // (appendRows in shared.ts). `columns` names the values each row carries; a table column the\n // caller does not write takes its default.\n appendRows?(table: string, columns: string[], rows: 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 // Replaces this engine's existing-row frontmatter upsert, inside the caller's\n // publication transaction. Preserve non-writable columns and repair missing rows.\n updateFrontmatter?(conn: Connection, writableColumns: string[], rows: unknown[][]): Promise<void>;\n // Reconciles searchable content for `touched` paths and parsed `docs`; `delta` carries the tree\n // state a 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, options: { existingOnly: boolean; observational: boolean }): 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, options?: { observational?: boolean }): 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 // Minimal, configuration-independent schema needed before the builder can capture a planning\n // snapshot. This bootstrap may commit independently because no feature generation is published.\n ensureCoreSchema(handle: Handle, conn: Connection): Promise<void>;\n // Configuration-dependent persistent DDL. The builder invokes this only inside its fenced write\n // transaction, so a failed feature enable cannot leave catalog changes under the old signature.\n ensureFeatureSchema(handle: Handle, conn: Connection, cfg: Config): Promise<void>;\n // Connection-local preparation with no durable cache effect (DuckDB's TEMP vector stage).\n prepareConnection?(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 // Persistent lexical preparation belongs to build, while no-build calls only the readiness\n // check. SQLite and Turso maintain lexical state during reconcile and omit both hooks.\n prepareLexical?(conn: Connection): Promise<void>;\n assertLexicalReady?(conn: Connection): Promise<void>;\n createStore(handle: Handle, conn: Connection, cfg: ResolvedConfig, options: { observational: boolean }): 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\n// A ranked match, nothing more. Snippets are cut from the document above the store\n// (commands/search.ts), identically for every engine, so no store carries them.\nexport interface LexicalHit {\n path: string;\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 snippet, 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;;;;;+BAW/EA;;;eAAAA;;;AAAN,IAAMA,mBAAmB;IAAC;IAAW;CAAU"}
@@ -1,4 +1,16 @@
1
- import type { Connection } from './types.js';
1
+ import type { Connection, VectorStore } from './types.js';
2
+ export declare const NATIVE_VECTOR_SCOPE_TABLE = "_vector_scope";
3
+ interface NativeVectorScopeOwner {
4
+ bind(vectors: VectorStore): void;
5
+ activeFor(allowed: Set<string> | undefined): boolean;
6
+ }
7
+ interface NativeVectorScopeReservation {
8
+ activate(): void;
9
+ deactivate(): void;
10
+ invalidate(): void;
11
+ }
12
+ export declare function createNativeVectorScopeOwner(): NativeVectorScopeOwner;
13
+ export declare function reserveNativeVectorScope(vectors: VectorStore, allowed: Set<string>): NativeVectorScopeReservation | null;
2
14
  export declare const TARGET_CHUNK_CAP = 16;
3
15
  export declare function sampleEvenly<T>(rows: T[], cap?: number): T[];
4
16
  export declare function asCosine(score: number): number;
@@ -14,3 +26,4 @@ export declare function pendingRows(conn: Connection): Promise<Array<{
14
26
  chunk: number;
15
27
  }>>;
16
28
  export declare function hasVectorRow(conn: Connection, path: string): Promise<boolean>;
29
+ export {};
@@ -1,4 +1,16 @@
1
- import type { Connection } from './types.js';
1
+ import type { Connection, VectorStore } from './types.js';
2
+ export declare const NATIVE_VECTOR_SCOPE_TABLE = "_vector_scope";
3
+ interface NativeVectorScopeOwner {
4
+ bind(vectors: VectorStore): void;
5
+ activeFor(allowed: Set<string> | undefined): boolean;
6
+ }
7
+ interface NativeVectorScopeReservation {
8
+ activate(): void;
9
+ deactivate(): void;
10
+ invalidate(): void;
11
+ }
12
+ export declare function createNativeVectorScopeOwner(): NativeVectorScopeOwner;
13
+ export declare function reserveNativeVectorScope(vectors: VectorStore, allowed: Set<string>): NativeVectorScopeReservation | null;
2
14
  export declare const TARGET_CHUNK_CAP = 16;
3
15
  export declare function sampleEvenly<T>(rows: T[], cap?: number): T[];
4
16
  export declare function asCosine(score: number): number;
@@ -14,3 +26,4 @@ export declare function pendingRows(conn: Connection): Promise<Array<{
14
26
  chunk: number;
15
27
  }>>;
16
28
  export declare function hasVectorRow(conn: Connection, path: string): Promise<boolean>;
29
+ export {};
@@ -9,6 +9,9 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
+ get NATIVE_VECTOR_SCOPE_TABLE () {
13
+ return NATIVE_VECTOR_SCOPE_TABLE;
14
+ },
12
15
  get TARGET_CHUNK_CAP () {
13
16
  return TARGET_CHUNK_CAP;
14
17
  },
@@ -18,12 +21,18 @@ _export(exports, {
18
21
  get compareVectorScores () {
19
22
  return compareVectorScores;
20
23
  },
24
+ get createNativeVectorScopeOwner () {
25
+ return createNativeVectorScopeOwner;
26
+ },
21
27
  get hasVectorRow () {
22
28
  return hasVectorRow;
23
29
  },
24
30
  get pendingRows () {
25
31
  return pendingRows;
26
32
  },
33
+ get reserveNativeVectorScope () {
34
+ return reserveNativeVectorScope;
35
+ },
27
36
  get sampleEvenly () {
28
37
  return sampleEvenly;
29
38
  }
@@ -153,6 +162,58 @@ function _ts_generator(thisArg, body) {
153
162
  };
154
163
  }
155
164
  }
165
+ var NATIVE_VECTOR_SCOPE_TABLE = '_vector_scope';
166
+ var nativeVectorScopeOwners = new WeakMap();
167
+ function createNativeVectorScopeOwner() {
168
+ var owner = {};
169
+ return {
170
+ bind: function bind(vectors) {
171
+ if (owner.vectors) throw new Error('native vector scope owner is already bound');
172
+ if (nativeVectorScopeOwners.has(vectors)) throw new Error('VectorStore is already bound to a native vector scope owner');
173
+ owner.vectors = vectors;
174
+ nativeVectorScopeOwners.set(vectors, owner);
175
+ },
176
+ activeFor: function activeFor(allowed) {
177
+ var _owner_scope;
178
+ return allowed !== undefined && ((_owner_scope = owner.scope) === null || _owner_scope === void 0 ? void 0 : _owner_scope.phase) === 'active' && owner.scope.allowed === allowed;
179
+ }
180
+ };
181
+ }
182
+ function reserveNativeVectorScope(vectors, allowed) {
183
+ var owner = nativeVectorScopeOwners.get(vectors);
184
+ if (!owner) return null;
185
+ if (owner.scope) throw new Error('native vector scope operation is already active for this Store');
186
+ var token = {};
187
+ owner.scope = {
188
+ token: token,
189
+ allowed: allowed,
190
+ phase: 'materializing'
191
+ };
192
+ var valid = true;
193
+ var current = function current() {
194
+ var _owner_scope;
195
+ if (!valid || ((_owner_scope = owner.scope) === null || _owner_scope === void 0 ? void 0 : _owner_scope.token) !== token) throw new Error('native vector scope reservation is no longer valid');
196
+ return owner.scope;
197
+ };
198
+ return {
199
+ activate: function activate() {
200
+ var scope = current();
201
+ if (scope.phase !== 'materializing') throw new Error("native vector scope cannot activate from ".concat(scope.phase));
202
+ scope.phase = 'active';
203
+ },
204
+ deactivate: function deactivate() {
205
+ var scope = current();
206
+ if (scope.phase === 'cleaning') throw new Error('native vector scope cleanup already started');
207
+ scope.phase = 'cleaning';
208
+ },
209
+ invalidate: function invalidate() {
210
+ var _owner_scope;
211
+ if (!valid) return;
212
+ if (((_owner_scope = owner.scope) === null || _owner_scope === void 0 ? void 0 : _owner_scope.token) === token) owner.scope = undefined;
213
+ valid = false;
214
+ }
215
+ };
216
+ }
156
217
  var TARGET_CHUNK_CAP = 16;
157
218
  function sampleEvenly(rows) {
158
219
  var cap = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : TARGET_CHUNK_CAP;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/vectors.ts"],"sourcesContent":["import type { Connection } from './types.ts';\n\n// Seed chunks that participate in a `related` scan. Cost is target_chunks x stored_chunks, so a\n// heading-dense seed multiplies a full-corpus scan (12.7s at 201 chunks/note unsampled).\nexport const TARGET_CHUNK_CAP = 16;\n\n// Evenly samples down to at most `cap` rows, so late sections of a long note still get a vote\n// instead of being cut off by a fixed prefix.\nexport function sampleEvenly<T>(rows: T[], cap: number = TARGET_CHUNK_CAP): T[] {\n const step = Math.max(1, Math.ceil(rows.length / cap));\n return rows.filter((_, i) => i % step === 0);\n}\n\n// Every store computes a real cosine; clamp float error at the ends and round for the public score.\nexport function asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\n// Rank by the computed cosine before rounding for display. Bytewise path order resolves only an\n// exact score tie, so two distinguishable similarities never cross a caller's result boundary.\nexport function compareVectorScores(a: { path: string; score: number }, b: { path: string; score: number }): number {\n const byScore = b.score - a.score;\n if (byScore !== 0) return byScore;\n return Buffer.compare(Buffer.from(a.path), Buffer.from(b.path));\n}\n\nexport async function pendingRows(conn: Connection): Promise<Array<{ path: string; chunk: number }>> {\n const stmt = await conn.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk');\n return (await stmt.all()) as Array<{ path: string; chunk: number }>;\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport async function hasVectorRow(conn: Connection, path: string): Promise<boolean> {\n const stmt = await conn.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1');\n const row = (await stmt.get(path)) as { ok: number } | undefined;\n return row !== undefined;\n}\n"],"names":["TARGET_CHUNK_CAP","asCosine","compareVectorScores","hasVectorRow","pendingRows","sampleEvenly","rows","cap","step","Math","max","ceil","length","filter","_","i","score","round","min","a","b","byScore","Buffer","compare","from","path","conn","stmt","prepare","all","row","get","undefined"],"mappings":";;;;;;;;;;;QAIaA;eAAAA;;QAUGC;eAAAA;;QAMAC;eAAAA;;QAaMC;eAAAA;;QAPAC;eAAAA;;QAlBNC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAJT,IAAML,mBAAmB;AAIzB,SAASK,aAAgBC,IAAS;QAAEC,MAAAA,iEAAcP;IACvD,IAAMQ,OAAOC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACL,KAAKM,MAAM,GAAGL;IACjD,OAAOD,KAAKO,MAAM,CAAC,SAACC,GAAGC;eAAMA,IAAIP,SAAS;;AAC5C;AAGO,SAASP,SAASe,KAAa;IACpC,OAAOP,KAAKQ,KAAK,CAACR,KAAKS,GAAG,CAAC,GAAGT,KAAKC,GAAG,CAAC,CAAC,GAAGM,UAAU,QAAQ;AAC/D;AAIO,SAASd,oBAAoBiB,CAAkC,EAAEC,CAAkC;IACxG,IAAMC,UAAUD,EAAEJ,KAAK,GAAGG,EAAEH,KAAK;IACjC,IAAIK,YAAY,GAAG,OAAOA;IAC1B,OAAOC,OAAOC,OAAO,CAACD,OAAOE,IAAI,CAACL,EAAEM,IAAI,GAAGH,OAAOE,IAAI,CAACJ,EAAEK,IAAI;AAC/D;AAEO,SAAerB,YAAYsB,IAAgB;;YAC1CC;;;;oBAAO;;wBAAMD,KAAKE,OAAO,CAAC;;;oBAA1BD,OAAO;oBACL;;wBAAMA,KAAKE,GAAG;;;oBAAtB;;wBAAQ;;;;IACV;;AAIO,SAAe1B,aAAauB,IAAgB,EAAED,IAAY;;YACzDE,MACAG;;;;oBADO;;wBAAMJ,KAAKE,OAAO,CAAC;;;oBAA1BD,OAAO;oBACA;;wBAAMA,KAAKI,GAAG,CAACN;;;oBAAtBK,MAAO;oBACb;;wBAAOA,QAAQE;;;;IACjB"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/vectors.ts"],"sourcesContent":["import type { Connection, VectorStore } from './types.ts';\n\nexport const NATIVE_VECTOR_SCOPE_TABLE = '_vector_scope';\n\ntype NativeVectorScopePhase = 'materializing' | 'active' | 'cleaning';\n\ninterface NativeVectorScopeState {\n token: object;\n allowed: Set<string>;\n phase: NativeVectorScopePhase;\n}\n\ninterface NativeVectorScopeOwnerState {\n vectors?: VectorStore;\n scope?: NativeVectorScopeState;\n}\n\ninterface NativeVectorScopeOwner {\n bind(vectors: VectorStore): void;\n activeFor(allowed: Set<string> | undefined): boolean;\n}\n\ninterface NativeVectorScopeReservation {\n activate(): void;\n deactivate(): void;\n invalidate(): void;\n}\n\nconst nativeVectorScopeOwners = new WeakMap<VectorStore, NativeVectorScopeOwnerState>();\n\n// One owner is constructed beside one native store connection, then bound to that connection's\n// VectorStore wrapper. The adapter asks its own owner for a private boolean; no Set or reservation\n// can activate another Store, even when a caller reuses the same Set object across both.\nexport function createNativeVectorScopeOwner(): NativeVectorScopeOwner {\n const owner: NativeVectorScopeOwnerState = {};\n return {\n bind(vectors) {\n if (owner.vectors) throw new Error('native vector scope owner is already bound');\n if (nativeVectorScopeOwners.has(vectors)) throw new Error('VectorStore is already bound to a native vector scope owner');\n owner.vectors = vectors;\n nativeVectorScopeOwners.set(vectors, owner);\n },\n activeFor(allowed) {\n return allowed !== undefined && owner.scope?.phase === 'active' && owner.scope.allowed === allowed;\n },\n };\n}\n\nexport function reserveNativeVectorScope(vectors: VectorStore, allowed: Set<string>): NativeVectorScopeReservation | null {\n const owner = nativeVectorScopeOwners.get(vectors);\n if (!owner) return null;\n if (owner.scope) throw new Error('native vector scope operation is already active for this Store');\n\n const token = {};\n owner.scope = { token, allowed, phase: 'materializing' };\n let valid = true;\n\n const current = (): NativeVectorScopeState => {\n if (!valid || owner.scope?.token !== token) throw new Error('native vector scope reservation is no longer valid');\n return owner.scope;\n };\n\n return {\n activate() {\n const scope = current();\n if (scope.phase !== 'materializing') throw new Error(`native vector scope cannot activate from ${scope.phase}`);\n scope.phase = 'active';\n },\n deactivate() {\n const scope = current();\n if (scope.phase === 'cleaning') throw new Error('native vector scope cleanup already started');\n scope.phase = 'cleaning';\n },\n invalidate() {\n if (!valid) return;\n if (owner.scope?.token === token) owner.scope = undefined;\n valid = false;\n },\n };\n}\n\n// Seed chunks that participate in a `related` scan. Cost is target_chunks x stored_chunks, so a\n// heading-dense seed multiplies a full-corpus scan (12.7s at 201 chunks/note unsampled).\nexport const TARGET_CHUNK_CAP = 16;\n\n// Evenly samples down to at most `cap` rows, so late sections of a long note still get a vote\n// instead of being cut off by a fixed prefix.\nexport function sampleEvenly<T>(rows: T[], cap: number = TARGET_CHUNK_CAP): T[] {\n const step = Math.max(1, Math.ceil(rows.length / cap));\n return rows.filter((_, i) => i % step === 0);\n}\n\n// Every store computes a real cosine; clamp float error at the ends and round for the public score.\nexport function asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\n// Rank by the computed cosine before rounding for display. Bytewise path order resolves only an\n// exact score tie, so two distinguishable similarities never cross a caller's result boundary.\nexport function compareVectorScores(a: { path: string; score: number }, b: { path: string; score: number }): number {\n const byScore = b.score - a.score;\n if (byScore !== 0) return byScore;\n return Buffer.compare(Buffer.from(a.path), Buffer.from(b.path));\n}\n\nexport async function pendingRows(conn: Connection): Promise<Array<{ path: string; chunk: number }>> {\n const stmt = await conn.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk');\n return (await stmt.all()) as Array<{ path: string; chunk: number }>;\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport async function hasVectorRow(conn: Connection, path: string): Promise<boolean> {\n const stmt = await conn.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1');\n const row = (await stmt.get(path)) as { ok: number } | undefined;\n return row !== undefined;\n}\n"],"names":["NATIVE_VECTOR_SCOPE_TABLE","TARGET_CHUNK_CAP","asCosine","compareVectorScores","createNativeVectorScopeOwner","hasVectorRow","pendingRows","reserveNativeVectorScope","sampleEvenly","nativeVectorScopeOwners","WeakMap","owner","bind","vectors","Error","has","set","activeFor","allowed","undefined","scope","phase","get","token","valid","current","activate","deactivate","invalidate","rows","cap","step","Math","max","ceil","length","filter","_","i","score","round","min","a","b","byScore","Buffer","compare","from","path","conn","stmt","prepare","all","row"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAiFAC;eAAAA;;QAUGC;eAAAA;;QAMAC;eAAAA;;QAlEAC;eAAAA;;QA+EMC;eAAAA;;QAPAC;eAAAA;;QAzDNC;eAAAA;;QAuCAC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AArFT,IAAMR,4BAA4B;AA0BzC,IAAMS,0BAA0B,IAAIC;AAK7B,SAASN;IACd,IAAMO,QAAqC,CAAC;IAC5C,OAAO;QACLC,MAAAA,SAAAA,KAAKC,OAAO;YACV,IAAIF,MAAME,OAAO,EAAE,MAAM,IAAIC,MAAM;YACnC,IAAIL,wBAAwBM,GAAG,CAACF,UAAU,MAAM,IAAIC,MAAM;YAC1DH,MAAME,OAAO,GAAGA;YAChBJ,wBAAwBO,GAAG,CAACH,SAASF;QACvC;QACAM,WAAAA,SAAAA,UAAUC,OAAO;gBACiBP;YAAhC,OAAOO,YAAYC,aAAaR,EAAAA,eAAAA,MAAMS,KAAK,cAAXT,mCAAAA,aAAaU,KAAK,MAAK,YAAYV,MAAMS,KAAK,CAACF,OAAO,KAAKA;QAC7F;IACF;AACF;AAEO,SAASX,yBAAyBM,OAAoB,EAAEK,OAAoB;IACjF,IAAMP,QAAQF,wBAAwBa,GAAG,CAACT;IAC1C,IAAI,CAACF,OAAO,OAAO;IACnB,IAAIA,MAAMS,KAAK,EAAE,MAAM,IAAIN,MAAM;IAEjC,IAAMS,QAAQ,CAAC;IACfZ,MAAMS,KAAK,GAAG;QAAEG,OAAAA;QAAOL,SAAAA;QAASG,OAAO;IAAgB;IACvD,IAAIG,QAAQ;IAEZ,IAAMC,UAAU;YACAd;QAAd,IAAI,CAACa,SAASb,EAAAA,eAAAA,MAAMS,KAAK,cAAXT,mCAAAA,aAAaY,KAAK,MAAKA,OAAO,MAAM,IAAIT,MAAM;QAC5D,OAAOH,MAAMS,KAAK;IACpB;IAEA,OAAO;QACLM,UAAAA,SAAAA;YACE,IAAMN,QAAQK;YACd,IAAIL,MAAMC,KAAK,KAAK,iBAAiB,MAAM,IAAIP,MAAM,AAAC,4CAAuD,OAAZM,MAAMC,KAAK;YAC5GD,MAAMC,KAAK,GAAG;QAChB;QACAM,YAAAA,SAAAA;YACE,IAAMP,QAAQK;YACd,IAAIL,MAAMC,KAAK,KAAK,YAAY,MAAM,IAAIP,MAAM;YAChDM,MAAMC,KAAK,GAAG;QAChB;QACAO,YAAAA,SAAAA;gBAEMjB;YADJ,IAAI,CAACa,OAAO;YACZ,IAAIb,EAAAA,eAAAA,MAAMS,KAAK,cAAXT,mCAAAA,aAAaY,KAAK,MAAKA,OAAOZ,MAAMS,KAAK,GAAGD;YAChDK,QAAQ;QACV;IACF;AACF;AAIO,IAAMvB,mBAAmB;AAIzB,SAASO,aAAgBqB,IAAS;QAAEC,MAAAA,iEAAc7B;IACvD,IAAM8B,OAAOC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACL,KAAKM,MAAM,GAAGL;IACjD,OAAOD,KAAKO,MAAM,CAAC,SAACC,GAAGC;eAAMA,IAAIP,SAAS;;AAC5C;AAGO,SAAS7B,SAASqC,KAAa;IACpC,OAAOP,KAAKQ,KAAK,CAACR,KAAKS,GAAG,CAAC,GAAGT,KAAKC,GAAG,CAAC,CAAC,GAAGM,UAAU,QAAQ;AAC/D;AAIO,SAASpC,oBAAoBuC,CAAkC,EAAEC,CAAkC;IACxG,IAAMC,UAAUD,EAAEJ,KAAK,GAAGG,EAAEH,KAAK;IACjC,IAAIK,YAAY,GAAG,OAAOA;IAC1B,OAAOC,OAAOC,OAAO,CAACD,OAAOE,IAAI,CAACL,EAAEM,IAAI,GAAGH,OAAOE,IAAI,CAACJ,EAAEK,IAAI;AAC/D;AAEO,SAAe1C,YAAY2C,IAAgB;;YAC1CC;;;;oBAAO;;wBAAMD,KAAKE,OAAO,CAAC;;;oBAA1BD,OAAO;oBACL;;wBAAMA,KAAKE,GAAG;;;oBAAtB;;wBAAQ;;;;IACV;;AAIO,SAAe/C,aAAa4C,IAAgB,EAAED,IAAY;;YACzDE,MACAG;;;;oBADO;;wBAAMJ,KAAKE,OAAO,CAAC;;;oBAA1BD,OAAO;oBACA;;wBAAMA,KAAK5B,GAAG,CAAC0B;;;oBAAtBK,MAAO;oBACb;;wBAAOA,QAAQlC;;;;IACjB"}
@@ -12,11 +12,17 @@ export type WatchEvent = {
12
12
  type: 'reconcile-error';
13
13
  message: string;
14
14
  };
15
+ /** Options for the root {@link runWatch} API. */
15
16
  export interface WatchOptions {
16
17
  force?: boolean;
17
18
  onEvent?: (event: WatchEvent) => void;
19
+ /** Caller abort cancels provider work, skips final preparation, and resolves after cleanup. */
18
20
  signal?: AbortSignal;
19
21
  debounceMs?: number;
20
22
  heartbeatIntervalMs?: number;
21
23
  }
24
+ /**
25
+ * Runs in the foreground. Caller abort waits for active native work and cleanup; SIGINT/SIGTERM
26
+ * also run one final freshness pass. Cleanup failures reject either shutdown path.
27
+ */
22
28
  export declare function runWatch(cfg: ResolvedConfig, opts?: WatchOptions): Promise<void>;
@@ -12,11 +12,17 @@ export type WatchEvent = {
12
12
  type: 'reconcile-error';
13
13
  message: string;
14
14
  };
15
+ /** Options for the root {@link runWatch} API. */
15
16
  export interface WatchOptions {
16
17
  force?: boolean;
17
18
  onEvent?: (event: WatchEvent) => void;
19
+ /** Caller abort cancels provider work, skips final preparation, and resolves after cleanup. */
18
20
  signal?: AbortSignal;
19
21
  debounceMs?: number;
20
22
  heartbeatIntervalMs?: number;
21
23
  }
24
+ /**
25
+ * Runs in the foreground. Caller abort waits for active native work and cleanup; SIGINT/SIGTERM
26
+ * also run one final freshness pass. Cleanup failures reject either shutdown path.
27
+ */
22
28
  export declare function runWatch(cfg: ResolvedConfig, opts?: WatchOptions): Promise<void>;
package/dist/cjs/watch.js CHANGED
@@ -174,14 +174,23 @@ function _ts_generator(thisArg, body) {
174
174
  // Watch prepares the index for snapshot queries; filesystem events request debounced builds
175
175
  // and periodic reconciliation recovers missed notifications.
176
176
  var DEBOUNCE_MS = 200;
177
+ function isCallerAbort(signal, reason) {
178
+ return (signal === null || signal === void 0 ? void 0 : signal.aborted) === true && Object.is(reason, signal.reason);
179
+ }
180
+ function errorMessage(reason) {
181
+ return _instanceof(reason, Error) ? reason.message : String(reason);
182
+ }
177
183
  function runWatch(_0) {
178
184
  return _async_to_generator(function(cfg) {
179
- var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, _cfg_rootDir, _cfg_configDir, _opts_force, onEvent, debounceMs, reconcileIntervalMs, claimIntervalMs, rootDir, configDir, claimFailure, requestShutdown, claim, claimCloseStarted, closeClaim, watcher, startup, startupNotification, scheduleReconcile, stopping, _opts_signal, startupEvents, _ref, initialStore, dbPath, initialWarnings, initialParsed, initialTotal, _tmp, _ref1, store, parsed, warnings, _, _tmp1, running, queuedAlways, tick, startCycle, requestCycle, debounceTimer, reconcileTimer, err, closeError;
185
+ var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, _cfg_rootDir, _cfg_configDir, _opts_force, _opts_signal, onEvent, debounceMs, reconcileIntervalMs, claimIntervalMs, rootDir, configDir, claimFailure, requestShutdown, claim, claimCloseStarted, closeClaim, watcher, startup, startupNotification, scheduleReconcile, stopping, callerCanceled, _opts_signal1, _opts_signal2, _opts_signal3, startupEvents, _ref, initialStore, dbPath, initialWarnings, initialParsed, initialTotal, _tmp, _opts_signal4, _opts_signal5, _ref1, store, parsed, warnings, _, _tmp1, running, queuedAlways, tick, startCycle, requestCycle, debounceTimer, reconcileTimer, err, canceledByCaller, closeError;
180
186
  var _arguments = arguments;
181
187
  return _ts_generator(this, function(_state) {
182
188
  switch(_state.label){
183
189
  case 0:
184
190
  opts = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
191
+ if ((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted) return [
192
+ 2
193
+ ];
185
194
  onEvent = (_opts_onEvent = opts.onEvent) !== null && _opts_onEvent !== void 0 ? _opts_onEvent : function() {};
186
195
  debounceMs = (_opts_debounceMs = opts.debounceMs) !== null && _opts_debounceMs !== void 0 ? _opts_debounceMs : DEBOUNCE_MS;
187
196
  reconcileIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : _watchclaimts.WATCH_HEARTBEAT_INTERVAL_MS;
@@ -208,6 +217,7 @@ function runWatch(_0) {
208
217
  startup = true;
209
218
  startupNotification = false;
210
219
  stopping = false;
220
+ callerCanceled = false;
211
221
  _state.label = 2;
212
222
  case 2:
213
223
  _state.trys.push([
@@ -216,6 +226,7 @@ function runWatch(_0) {
216
226
  27,
217
227
  28
218
228
  ]);
229
+ (_opts_signal1 = opts.signal) === null || _opts_signal1 === void 0 ? void 0 : _opts_signal1.throwIfAborted();
219
230
  // Subscribe before the first build. Native watchers have no ready barrier, so startup also
220
231
  // performs an unconditional catch-up pass after the initial build.
221
232
  watcher = (0, _nodefs.watch)(rootDir, {
@@ -232,7 +243,9 @@ function runWatch(_0) {
232
243
  startupEvents = [];
233
244
  return [
234
245
  4,
235
- (0, _indexts.openStore)(cfg)
246
+ (0, _indexts.openStore)(cfg, {
247
+ signal: opts.signal
248
+ })
236
249
  ];
237
250
  case 3:
238
251
  _ref = _state.sent(), initialStore = _ref.store, dbPath = _ref.dbPath, initialWarnings = _ref.warnings, initialParsed = _ref.parsed;
@@ -280,6 +293,7 @@ function runWatch(_0) {
280
293
  7
281
294
  ];
282
295
  case 10:
296
+ (_opts_signal2 = opts.signal) === null || _opts_signal2 === void 0 ? void 0 : _opts_signal2.throwIfAborted();
283
297
  if (claimFailure) throw claimFailure;
284
298
  startupEvents.push({
285
299
  parsed: initialParsed,
@@ -289,9 +303,12 @@ function runWatch(_0) {
289
303
  _state.label = 11;
290
304
  case 11:
291
305
  startupNotification = false;
306
+ (_opts_signal4 = opts.signal) === null || _opts_signal4 === void 0 ? void 0 : _opts_signal4.throwIfAborted();
292
307
  return [
293
308
  4,
294
- (0, _indexts.openStore)(cfg)
309
+ (0, _indexts.openStore)(cfg, {
310
+ signal: opts.signal
311
+ })
295
312
  ];
296
313
  case 12:
297
314
  _ref1 = _state.sent(), store = _ref1.store, parsed = _ref1.parsed, warnings = _ref1.warnings;
@@ -336,10 +353,11 @@ function runWatch(_0) {
336
353
  7
337
354
  ];
338
355
  case 18:
356
+ (_opts_signal5 = opts.signal) === null || _opts_signal5 === void 0 ? void 0 : _opts_signal5.throwIfAborted();
339
357
  if (claimFailure) throw claimFailure;
340
358
  _state.label = 19;
341
359
  case 19:
342
- if (startupNotification && !((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted)) return [
360
+ if (startupNotification && !((_opts_signal3 = opts.signal) === null || _opts_signal3 === void 0 ? void 0 : _opts_signal3.aborted)) return [
343
361
  3,
344
362
  11
345
363
  ];
@@ -364,7 +382,9 @@ function runWatch(_0) {
364
382
  ]);
365
383
  return [
366
384
  4,
367
- (0, _indexts.openStore)(cfg)
385
+ (0, _indexts.openStore)(cfg, {
386
+ signal: opts.signal
387
+ })
368
388
  ];
369
389
  case 1:
370
390
  _ref = _state.sent(), store = _ref.store, parsed = _ref.parsed, warnings = _ref.warnings;
@@ -415,9 +435,12 @@ function runWatch(_0) {
415
435
  ];
416
436
  case 8:
417
437
  err = _state.sent();
438
+ if (isCallerAbort(opts.signal, err)) return [
439
+ 2
440
+ ];
418
441
  onEvent({
419
442
  type: 'reconcile-error',
420
- message: err.message
443
+ message: errorMessage(err)
421
444
  });
422
445
  return [
423
446
  3,
@@ -477,7 +500,9 @@ function runWatch(_0) {
477
500
  var _opts_signal;
478
501
  var shutdownReason = null;
479
502
  var shutdown = function shutdown(reason) {
503
+ var canceledByCaller = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
480
504
  shutdownReason !== null && shutdownReason !== void 0 ? shutdownReason : shutdownReason = reason !== null && reason !== void 0 ? reason : null;
505
+ callerCanceled || (callerCanceled = canceledByCaller);
481
506
  if (stopping) return;
482
507
  stopping = true;
483
508
  var shutdownTask = function() {
@@ -488,7 +513,7 @@ function runWatch(_0) {
488
513
  case 0:
489
514
  process.off('SIGINT', stopNormally);
490
515
  process.off('SIGTERM', stopNormally);
491
- (_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.removeEventListener('abort', stopNormally);
516
+ (_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.removeEventListener('abort', stopForCaller);
492
517
  clearInterval(reconcileTimer);
493
518
  if (debounceTimer) clearTimeout(debounceTimer);
494
519
  watcher === null || watcher === void 0 ? void 0 : watcher.close();
@@ -506,7 +531,7 @@ function runWatch(_0) {
506
531
  ,
507
532
  7
508
533
  ]);
509
- if (!!shutdownReason) return [
534
+ if (!(!shutdownReason && !callerCanceled)) return [
510
535
  3,
511
536
  5
512
537
  ];
@@ -574,14 +599,17 @@ function runWatch(_0) {
574
599
  var stopNormally = function stopNormally() {
575
600
  return shutdown();
576
601
  };
602
+ var stopForCaller = function stopForCaller() {
603
+ return shutdown(undefined, true);
604
+ };
577
605
  requestShutdown = shutdown;
578
606
  process.once('SIGINT', stopNormally);
579
607
  process.once('SIGTERM', stopNormally);
580
608
  if (claimFailure) shutdown(claimFailure);
581
- else if ((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted) shutdown();
609
+ else if ((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted) stopForCaller();
582
610
  else {
583
611
  var _opts_signal1;
584
- (_opts_signal1 = opts.signal) === null || _opts_signal1 === void 0 ? void 0 : _opts_signal1.addEventListener('abort', stopNormally, {
612
+ (_opts_signal1 = opts.signal) === null || _opts_signal1 === void 0 ? void 0 : _opts_signal1.addEventListener('abort', stopForCaller, {
585
613
  once: true
586
614
  });
587
615
  try {
@@ -628,6 +656,7 @@ function runWatch(_0) {
628
656
  ];
629
657
  case 22:
630
658
  err = _state.sent();
659
+ canceledByCaller = isCallerAbort(opts.signal, err);
631
660
  watcher === null || watcher === void 0 ? void 0 : watcher.close();
632
661
  if (claimCloseStarted) throw err;
633
662
  _state.label = 23;
@@ -655,6 +684,9 @@ function runWatch(_0) {
655
684
  closeError
656
685
  ], 'watch setup and claim cleanup both failed');
657
686
  case 26:
687
+ if (canceledByCaller) return [
688
+ 2
689
+ ];
658
690
  throw err;
659
691
  case 27:
660
692
  requestShutdown = undefined;
@@ -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 { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { shouldReconcileWatchNotification } from './lib/watch-notification.ts';\nimport { docCount, openStore } from './store/index.ts';\nimport { startWatchClaim, WATCH_HEARTBEAT_INTERVAL_MS } from './watch-claim.ts';\n\n// Watch prepares the index for snapshot queries; filesystem events request debounced builds\n// and periodic reconciliation recovers missed notifications.\nconst DEBOUNCE_MS = 200;\n\nexport type WatchEvent = { type: 'started'; rootDir: 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. DuckDB and Turso hand the cache file to\n// one process at a time, so every reconcile cycle opens and closes instead of holding an idle lock.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const reconcileIntervalMs = opts.heartbeatIntervalMs ?? WATCH_HEARTBEAT_INTERVAL_MS;\n if (!Number.isFinite(reconcileIntervalMs) || reconcileIntervalMs <= 0) throw new SenseError('CONFIG_INVALID', 'watch heartbeatIntervalMs must be a finite positive number');\n // Reconciliation may be parked for a caller, but live ownership must renew before fixed expiry.\n const claimIntervalMs = Math.min(reconcileIntervalMs, WATCH_HEARTBEAT_INTERVAL_MS);\n const rootDir = cfg.rootDir ?? cfg.baseDir;\n const configDir = cfg.configDir ?? rootDir;\n let claimFailure: Error | null = null;\n let requestShutdown: ((reason: Error) => void) | undefined;\n const claim = await startWatchClaim(configDir, opts.force ?? false, claimIntervalMs, (err) => {\n claimFailure ??= err;\n requestShutdown?.(err);\n });\n let claimCloseStarted = false;\n const closeClaim = (): Promise<void> => {\n claimCloseStarted = true;\n return claim.close();\n };\n let watcher: ReturnType<typeof fsWatch> | undefined;\n let startup = true;\n let startupNotification = false;\n let scheduleReconcile: (() => void) | undefined;\n let stopping = false;\n try {\n // Subscribe before the first build. Native watchers have no ready barrier, so startup also\n // performs an unconditional catch-up pass after the initial build.\n watcher = fsWatch(rootDir, { recursive: true }, (_event, filename) => {\n if (!shouldReconcileWatchNotification(filename)) return;\n if (startup) startupNotification = true;\n else scheduleReconcile?.();\n });\n watcher.on('error', (err) => {\n claimFailure ??= err;\n requestShutdown?.(err);\n });\n\n const startupEvents: Array<{ parsed: number; total: number; warnings: string[] }> = [];\n const { store: initialStore, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n let initialTotal = 0;\n try {\n // total is read before close: an event fires only once nothing is held open again.\n initialTotal = initialWarnings.length > 0 || initialParsed > 0 ? await docCount(initialStore) : 0;\n } finally {\n await initialStore.close();\n }\n if (claimFailure) throw claimFailure;\n\n startupEvents.push({ parsed: initialParsed, total: initialTotal, warnings: initialWarnings });\n // Always reopen once after subscribing, then drain notifications observed while startup was\n // in progress. Each pass closes before the next one starts, so no stores overlap.\n do {\n startupNotification = false;\n const { store, parsed, warnings } = await openStore(cfg);\n try {\n if (parsed > 0 || warnings.length > 0) startupEvents.push({ parsed, total: await docCount(store), warnings });\n } finally {\n await store.close();\n }\n if (claimFailure) throw claimFailure;\n } while (startupNotification && !opts.signal?.aborted);\n startup = false;\n\n // At most one open+reconcile+close cycle runs at a time; a trigger that arrives mid-cycle is\n // coalesced into a single rerun instead of opening a second, overlapping connection.\n let running: Promise<void> | null = null;\n let queuedAlways: boolean | null = null;\n\n const tick = async (alwaysEmit: boolean): Promise<void> => {\n try {\n const { store, parsed, warnings } = await openStore(cfg);\n try {\n if (alwaysEmit || parsed > 0 || warnings.length > 0) {\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n }\n } finally {\n await store.close();\n }\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n };\n\n const startCycle = (alwaysEmit: boolean) => {\n running = tick(alwaysEmit).then(() => {\n running = null;\n if (queuedAlways !== null && !stopping) {\n const again = queuedAlways;\n queuedAlways = null;\n startCycle(again);\n }\n });\n };\n\n const requestCycle = (alwaysEmit: boolean) => {\n if (stopping) return;\n if (running) {\n queuedAlways = queuedAlways || alwaysEmit;\n return;\n }\n startCycle(alwaysEmit);\n };\n\n let debounceTimer: NodeJS.Timeout | null = null;\n // Ignore our own state files, or cache and claim writes would retrigger reconciliation forever.\n // An unresolvable filename reconciles because parsing nothing costs less than missing a real edit.\n scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n requestCycle(true);\n }, debounceMs);\n };\n const reconcileTimer = setInterval(\n guardedTick(\n async () => requestCycle(false),\n () => stopping\n ),\n reconcileIntervalMs\n );\n\n return await new Promise<void>((resolveShutdown, rejectShutdown) => {\n let shutdownReason: Error | null = null;\n const shutdown = (reason?: Error) => {\n shutdownReason ??= reason ?? null;\n if (stopping) return;\n stopping = true;\n const shutdownTask = (async () => {\n process.off('SIGINT', stopNormally);\n process.off('SIGTERM', stopNormally);\n opts.signal?.removeEventListener('abort', stopNormally);\n clearInterval(reconcileTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher?.close();\n await running;\n let finalError: unknown;\n try {\n // A normal stop performs one final freshness pass. Ownership loss drains existing work\n // but starts nothing new because the replacement watcher now owns that responsibility.\n if (!shutdownReason) {\n const { store: finalStore } = await openStore(cfg);\n await finalStore.close();\n }\n } catch (err) {\n finalError = err;\n }\n try {\n await closeClaim();\n } catch (err) {\n if (finalError) throw new AggregateError([finalError, err], 'watch shutdown and claim cleanup both failed');\n throw err;\n }\n if (finalError) throw finalError;\n if (shutdownReason) throw shutdownReason;\n })();\n shutdownTask.then(resolveShutdown, rejectShutdown);\n };\n const stopNormally = () => shutdown();\n requestShutdown = shutdown;\n process.once('SIGINT', stopNormally);\n process.once('SIGTERM', stopNormally);\n if (claimFailure) shutdown(claimFailure);\n else if (opts.signal?.aborted) shutdown();\n else {\n opts.signal?.addEventListener('abort', stopNormally, { once: true });\n try {\n onEvent({ type: 'started', rootDir, dbPath });\n if (!stopping) {\n for (const event of startupEvents) {\n if (stopping) break;\n if (event.warnings.length > 0 || event.parsed > 0) onEvent({ type: 'reconciled', ...event });\n }\n }\n } catch (err) {\n shutdown(err instanceof Error ? err : new Error(String(err)));\n }\n }\n });\n } catch (err) {\n watcher?.close();\n if (claimCloseStarted) throw err;\n try {\n await closeClaim();\n } catch (closeError) {\n throw new AggregateError([err, closeError], 'watch setup and claim cleanup both failed');\n }\n throw err;\n } finally {\n requestShutdown = undefined;\n }\n}\n"],"names":["runWatch","DEBOUNCE_MS","cfg","opts","onEvent","debounceMs","reconcileIntervalMs","claimIntervalMs","rootDir","configDir","claimFailure","requestShutdown","claim","claimCloseStarted","closeClaim","watcher","startup","startupNotification","scheduleReconcile","stopping","startupEvents","initialStore","dbPath","initialWarnings","initialParsed","initialTotal","store","parsed","warnings","running","queuedAlways","tick","startCycle","requestCycle","debounceTimer","reconcileTimer","err","closeError","heartbeatIntervalMs","WATCH_HEARTBEAT_INTERVAL_MS","Number","isFinite","SenseError","Math","min","baseDir","startWatchClaim","force","close","fsWatch","recursive","_event","filename","shouldReconcileWatchNotification","on","openStore","length","docCount","push","total","signal","aborted","alwaysEmit","type","message","then","again","clearTimeout","setTimeout","setInterval","guardedTick","Promise","resolveShutdown","rejectShutdown","shutdownReason","shutdown","reason","shutdownTask","finalError","finalStore","process","off","stopNormally","removeEventListener","clearInterval","AggregateError","once","addEventListener","event","Error","String","undefined"],"mappings":"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"}
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 { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { shouldReconcileWatchNotification } from './lib/watch-notification.ts';\nimport { docCount, openStore } from './store/index.ts';\nimport { startWatchClaim, WATCH_HEARTBEAT_INTERVAL_MS } from './watch-claim.ts';\n\n// Watch prepares the index for snapshot queries; filesystem events request debounced builds\n// and periodic reconciliation recovers missed notifications.\nconst DEBOUNCE_MS = 200;\n\nexport type WatchEvent = { type: 'started'; rootDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\n/** Options for the root {@link runWatch} API. */\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n /** Caller abort cancels provider work, skips final preparation, and resolves after cleanup. */\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\nfunction isCallerAbort(signal: AbortSignal | undefined, reason: unknown): boolean {\n return signal?.aborted === true && Object.is(reason, signal.reason);\n}\n\nfunction errorMessage(reason: unknown): string {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\n/**\n * Runs in the foreground. Caller abort waits for active native work and cleanup; SIGINT/SIGTERM\n * also run one final freshness pass. Cleanup failures reject either shutdown path.\n */\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n if (opts.signal?.aborted) return;\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const reconcileIntervalMs = opts.heartbeatIntervalMs ?? WATCH_HEARTBEAT_INTERVAL_MS;\n if (!Number.isFinite(reconcileIntervalMs) || reconcileIntervalMs <= 0) throw new SenseError('CONFIG_INVALID', 'watch heartbeatIntervalMs must be a finite positive number');\n // Reconciliation may be parked for a caller, but live ownership must renew before fixed expiry.\n const claimIntervalMs = Math.min(reconcileIntervalMs, WATCH_HEARTBEAT_INTERVAL_MS);\n const rootDir = cfg.rootDir ?? cfg.baseDir;\n const configDir = cfg.configDir ?? rootDir;\n let claimFailure: Error | null = null;\n let requestShutdown: ((reason: Error) => void) | undefined;\n const claim = await startWatchClaim(configDir, opts.force ?? false, claimIntervalMs, (err) => {\n claimFailure ??= err;\n requestShutdown?.(err);\n });\n let claimCloseStarted = false;\n const closeClaim = (): Promise<void> => {\n claimCloseStarted = true;\n return claim.close();\n };\n let watcher: ReturnType<typeof fsWatch> | undefined;\n let startup = true;\n let startupNotification = false;\n let scheduleReconcile: (() => void) | undefined;\n let stopping = false;\n let callerCanceled = false;\n try {\n opts.signal?.throwIfAborted();\n // Subscribe before the first build. Native watchers have no ready barrier, so startup also\n // performs an unconditional catch-up pass after the initial build.\n watcher = fsWatch(rootDir, { recursive: true }, (_event, filename) => {\n if (!shouldReconcileWatchNotification(filename)) return;\n if (startup) startupNotification = true;\n else scheduleReconcile?.();\n });\n watcher.on('error', (err) => {\n claimFailure ??= err;\n requestShutdown?.(err);\n });\n\n const startupEvents: Array<{ parsed: number; total: number; warnings: string[] }> = [];\n const { store: initialStore, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg, { signal: opts.signal });\n let initialTotal = 0;\n try {\n // total is read before close: an event fires only once nothing is held open again.\n initialTotal = initialWarnings.length > 0 || initialParsed > 0 ? await docCount(initialStore) : 0;\n } finally {\n await initialStore.close();\n }\n opts.signal?.throwIfAborted();\n if (claimFailure) throw claimFailure;\n\n startupEvents.push({ parsed: initialParsed, total: initialTotal, warnings: initialWarnings });\n // Always reopen once after subscribing, then drain notifications observed while startup was\n // in progress. Each pass closes before the next one starts, so no stores overlap.\n do {\n startupNotification = false;\n opts.signal?.throwIfAborted();\n const { store, parsed, warnings } = await openStore(cfg, { signal: opts.signal });\n try {\n if (parsed > 0 || warnings.length > 0) startupEvents.push({ parsed, total: await docCount(store), warnings });\n } finally {\n await store.close();\n }\n opts.signal?.throwIfAborted();\n if (claimFailure) throw claimFailure;\n } while (startupNotification && !opts.signal?.aborted);\n startup = false;\n\n // At most one open+reconcile+close cycle runs at a time; a trigger that arrives mid-cycle is\n // coalesced into a single rerun instead of opening a second, overlapping connection.\n let running: Promise<void> | null = null;\n let queuedAlways: boolean | null = null;\n\n const tick = async (alwaysEmit: boolean): Promise<void> => {\n try {\n const { store, parsed, warnings } = await openStore(cfg, { signal: opts.signal });\n try {\n if (alwaysEmit || parsed > 0 || warnings.length > 0) {\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n }\n } finally {\n await store.close();\n }\n } catch (err) {\n if (isCallerAbort(opts.signal, err)) return;\n onEvent({ type: 'reconcile-error', message: errorMessage(err) });\n }\n };\n\n const startCycle = (alwaysEmit: boolean) => {\n running = tick(alwaysEmit).then(() => {\n running = null;\n if (queuedAlways !== null && !stopping) {\n const again = queuedAlways;\n queuedAlways = null;\n startCycle(again);\n }\n });\n };\n\n const requestCycle = (alwaysEmit: boolean) => {\n if (stopping) return;\n if (running) {\n queuedAlways = queuedAlways || alwaysEmit;\n return;\n }\n startCycle(alwaysEmit);\n };\n\n let debounceTimer: NodeJS.Timeout | null = null;\n // Ignore our own state files, or cache and claim writes would retrigger reconciliation forever.\n // An unresolvable filename reconciles because parsing nothing costs less than missing a real edit.\n scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n requestCycle(true);\n }, debounceMs);\n };\n const reconcileTimer = setInterval(\n guardedTick(\n async () => requestCycle(false),\n () => stopping\n ),\n reconcileIntervalMs\n );\n\n return await new Promise<void>((resolveShutdown, rejectShutdown) => {\n let shutdownReason: Error | null = null;\n const shutdown = (reason?: Error, canceledByCaller = false) => {\n shutdownReason ??= reason ?? null;\n callerCanceled ||= canceledByCaller;\n if (stopping) return;\n stopping = true;\n const shutdownTask = (async () => {\n process.off('SIGINT', stopNormally);\n process.off('SIGTERM', stopNormally);\n opts.signal?.removeEventListener('abort', stopForCaller);\n clearInterval(reconcileTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher?.close();\n await running;\n let finalError: unknown;\n try {\n // A normal stop performs one final freshness pass. Ownership loss drains existing work\n // but starts nothing new because the replacement watcher now owns that responsibility.\n if (!shutdownReason && !callerCanceled) {\n const { store: finalStore } = await openStore(cfg);\n await finalStore.close();\n }\n } catch (err) {\n finalError = err;\n }\n try {\n await closeClaim();\n } catch (err) {\n if (finalError) throw new AggregateError([finalError, err], 'watch shutdown and claim cleanup both failed');\n throw err;\n }\n if (finalError) throw finalError;\n if (shutdownReason) throw shutdownReason;\n })();\n shutdownTask.then(resolveShutdown, rejectShutdown);\n };\n const stopNormally = () => shutdown();\n const stopForCaller = () => shutdown(undefined, true);\n requestShutdown = shutdown;\n process.once('SIGINT', stopNormally);\n process.once('SIGTERM', stopNormally);\n if (claimFailure) shutdown(claimFailure);\n else if (opts.signal?.aborted) stopForCaller();\n else {\n opts.signal?.addEventListener('abort', stopForCaller, { once: true });\n try {\n onEvent({ type: 'started', rootDir, dbPath });\n if (!stopping) {\n for (const event of startupEvents) {\n if (stopping) break;\n if (event.warnings.length > 0 || event.parsed > 0) onEvent({ type: 'reconciled', ...event });\n }\n }\n } catch (err) {\n shutdown(err instanceof Error ? err : new Error(String(err)));\n }\n }\n });\n } catch (err) {\n const canceledByCaller = isCallerAbort(opts.signal, err);\n watcher?.close();\n if (claimCloseStarted) throw err;\n try {\n await closeClaim();\n } catch (closeError) {\n throw new AggregateError([err, closeError], 'watch setup and claim cleanup both failed');\n }\n if (canceledByCaller) return;\n throw err;\n } finally {\n requestShutdown = undefined;\n }\n}\n"],"names":["runWatch","DEBOUNCE_MS","isCallerAbort","signal","reason","aborted","Object","is","errorMessage","Error","message","String","cfg","opts","onEvent","debounceMs","reconcileIntervalMs","claimIntervalMs","rootDir","configDir","claimFailure","requestShutdown","claim","claimCloseStarted","closeClaim","watcher","startup","startupNotification","scheduleReconcile","stopping","callerCanceled","startupEvents","initialStore","dbPath","initialWarnings","initialParsed","initialTotal","store","parsed","warnings","running","queuedAlways","tick","startCycle","requestCycle","debounceTimer","reconcileTimer","err","canceledByCaller","closeError","heartbeatIntervalMs","WATCH_HEARTBEAT_INTERVAL_MS","Number","isFinite","SenseError","Math","min","baseDir","startWatchClaim","force","close","throwIfAborted","fsWatch","recursive","_event","filename","shouldReconcileWatchNotification","on","openStore","length","docCount","push","total","alwaysEmit","type","then","again","clearTimeout","setTimeout","setInterval","guardedTick","Promise","resolveShutdown","rejectShutdown","shutdownReason","shutdown","shutdownTask","finalError","finalStore","process","off","stopNormally","removeEventListener","stopForCaller","clearInterval","AggregateError","undefined","once","addEventListener","event"],"mappings":"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"}
@@ -6,8 +6,8 @@ export interface ParseWorkerData {
6
6
  cfg: Config;
7
7
  featureNames: FeatureName[];
8
8
  }
9
- export type ParseTask = FileStat;
10
- export type ParseTaskResult = {
9
+ export type ParseTask = FileStat[];
10
+ export type ParseFileResult = {
11
11
  ok: true;
12
12
  doc: ParsedDoc;
13
13
  warnings: string[];
@@ -16,4 +16,5 @@ export type ParseTaskResult = {
16
16
  ok: false;
17
17
  error: WorkerErrorPayload;
18
18
  };
19
- export default function parseTask(file: ParseTask): ParseTaskResult;
19
+ export type ParseTaskResult = ParseFileResult[];
20
+ export default function parseTask(files: ParseTask): ParseTaskResult;
@@ -6,8 +6,8 @@ export interface ParseWorkerData {
6
6
  cfg: Config;
7
7
  featureNames: FeatureName[];
8
8
  }
9
- export type ParseTask = FileStat;
10
- export type ParseTaskResult = {
9
+ export type ParseTask = FileStat[];
10
+ export type ParseFileResult = {
11
11
  ok: true;
12
12
  doc: ParsedDoc;
13
13
  warnings: string[];
@@ -16,4 +16,5 @@ export type ParseTaskResult = {
16
16
  ok: false;
17
17
  error: WorkerErrorPayload;
18
18
  };
19
- export default function parseTask(file: ParseTask): ParseTaskResult;
19
+ export type ParseTaskResult = ParseFileResult[];
20
+ export default function parseTask(files: ParseTask): ParseTaskResult;
@@ -25,22 +25,24 @@ var _Tinypool_workerData = _tinypool.default.workerData, cfg = _Tinypool_workerD
25
25
  var selected = _indexts.FEATURES.filter(function(feature) {
26
26
  return featureNames.includes(feature.name);
27
27
  });
28
- function parseTask(file) {
29
- try {
30
- var start = process.hrtime.bigint();
31
- var _parseFile = (0, _indexts1.parseFile)(file, (0, _reparsets.featuresForFile)(selected, cfg, file), cfg), doc = _parseFile.doc, warnings = _parseFile.warnings;
32
- var parseMs = Number(process.hrtime.bigint() - start) / 1e6;
33
- return {
34
- ok: true,
35
- doc: doc,
36
- warnings: warnings,
37
- parseMs: parseMs
38
- };
39
- } catch (err) {
40
- return {
41
- ok: false,
42
- error: (0, _workererrorts.serializeError)(err)
43
- };
44
- }
28
+ function parseTask(files) {
29
+ return files.map(function(file) {
30
+ try {
31
+ var start = process.hrtime.bigint();
32
+ var _parseFile = (0, _indexts1.parseFile)(file, (0, _reparsets.featuresForFile)(selected, cfg, file), cfg), doc = _parseFile.doc, warnings = _parseFile.warnings;
33
+ var parseMs = Number(process.hrtime.bigint() - start) / 1e6;
34
+ return {
35
+ ok: true,
36
+ doc: doc,
37
+ warnings: warnings,
38
+ parseMs: parseMs
39
+ };
40
+ } catch (err) {
41
+ return {
42
+ ok: false,
43
+ error: (0, _workererrorts.serializeError)(err)
44
+ };
45
+ }
46
+ });
45
47
  }
46
48
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }