sensemaking 0.18.3 → 0.19.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 (368) hide show
  1. package/README.md +22 -4
  2. package/dist/cjs/chunk/group.js +10 -23
  3. package/dist/cjs/chunk/group.js.map +1 -1
  4. package/dist/cjs/chunk/parse.js +2 -5
  5. package/dist/cjs/chunk/parse.js.map +1 -1
  6. package/dist/cjs/cli/index.js.map +1 -1
  7. package/dist/cjs/cli/shared.js +6 -10
  8. package/dist/cjs/cli/shared.js.map +1 -1
  9. package/dist/cjs/cli/status.js +44 -10
  10. package/dist/cjs/cli/status.js.map +1 -1
  11. package/dist/cjs/cli.js +2 -8
  12. package/dist/cjs/cli.js.map +1 -1
  13. package/dist/cjs/commands/map.js +1 -3
  14. package/dist/cjs/commands/map.js.map +1 -1
  15. package/dist/cjs/commands/peek.js.map +1 -1
  16. package/dist/cjs/commands/related.js +3 -5
  17. package/dist/cjs/commands/related.js.map +1 -1
  18. package/dist/cjs/commands/scope.js.map +1 -1
  19. package/dist/cjs/commands/search.js +6 -8
  20. package/dist/cjs/commands/search.js.map +1 -1
  21. package/dist/cjs/commands/signals.js.map +1 -1
  22. package/dist/cjs/config/access.js.map +1 -1
  23. package/dist/cjs/config/index.d.cts +1 -1
  24. package/dist/cjs/config/index.d.ts +1 -1
  25. package/dist/cjs/config/index.js +3 -0
  26. package/dist/cjs/config/index.js.map +1 -1
  27. package/dist/cjs/config/load.js +10 -24
  28. package/dist/cjs/config/load.js.map +1 -1
  29. package/dist/cjs/config/resolve.js.map +1 -1
  30. package/dist/cjs/config/signals.js +2 -3
  31. package/dist/cjs/config/signals.js.map +1 -1
  32. package/dist/cjs/config/types.d.cts +2 -1
  33. package/dist/cjs/config/types.d.ts +2 -1
  34. package/dist/cjs/config/types.js +8 -0
  35. package/dist/cjs/config/types.js.map +1 -1
  36. package/dist/cjs/config/validate.d.cts +1 -1
  37. package/dist/cjs/config/validate.d.ts +1 -1
  38. package/dist/cjs/config/validate.js +9 -10
  39. package/dist/cjs/config/validate.js.map +1 -1
  40. package/dist/cjs/embed/cohere.js +2 -2
  41. package/dist/cjs/embed/cohere.js.map +1 -1
  42. package/dist/cjs/embed/distribution.js +3 -3
  43. package/dist/cjs/embed/distribution.js.map +1 -1
  44. package/dist/cjs/embed/handoff.d.cts +3 -0
  45. package/dist/cjs/embed/handoff.d.ts +3 -0
  46. package/dist/cjs/embed/handoff.js +39 -0
  47. package/dist/cjs/embed/handoff.js.map +1 -0
  48. package/dist/cjs/embed/identity.d.cts +11 -10
  49. package/dist/cjs/embed/identity.d.ts +11 -10
  50. package/dist/cjs/embed/identity.js +30 -15
  51. package/dist/cjs/embed/identity.js.map +1 -1
  52. package/dist/cjs/embed/langfit.js +2 -3
  53. package/dist/cjs/embed/langfit.js.map +1 -1
  54. package/dist/cjs/embed/languages.js +2 -4
  55. package/dist/cjs/embed/languages.js.map +1 -1
  56. package/dist/cjs/embed/openai.js +2 -2
  57. package/dist/cjs/embed/openai.js.map +1 -1
  58. package/dist/cjs/embed/query.js +58 -20
  59. package/dist/cjs/embed/query.js.map +1 -1
  60. package/dist/cjs/embed/registry.js +1 -2
  61. package/dist/cjs/embed/registry.js.map +1 -1
  62. package/dist/cjs/embed/static.d.cts +1 -1
  63. package/dist/cjs/embed/static.d.ts +1 -1
  64. package/dist/cjs/embed/static.js +8 -10
  65. package/dist/cjs/embed/static.js.map +1 -1
  66. package/dist/cjs/embed/store.d.cts +2 -2
  67. package/dist/cjs/embed/store.d.ts +2 -2
  68. package/dist/cjs/embed/store.js +48 -34
  69. package/dist/cjs/embed/store.js.map +1 -1
  70. package/dist/cjs/embed/types.js +1 -3
  71. package/dist/cjs/embed/types.js.map +1 -1
  72. package/dist/cjs/errors.d.cts +1 -1
  73. package/dist/cjs/errors.d.ts +1 -1
  74. package/dist/cjs/errors.js.map +1 -1
  75. package/dist/cjs/features/embed.js +19 -17
  76. package/dist/cjs/features/embed.js.map +1 -1
  77. package/dist/cjs/features/fences.js +5 -10
  78. package/dist/cjs/features/fences.js.map +1 -1
  79. package/dist/cjs/features/links.js +20 -37
  80. package/dist/cjs/features/links.js.map +1 -1
  81. package/dist/cjs/features/sections.js +21 -19
  82. package/dist/cjs/features/sections.js.map +1 -1
  83. package/dist/cjs/features/tags.js +5 -8
  84. package/dist/cjs/features/tags.js.map +1 -1
  85. package/dist/cjs/graph/traverse.js +1 -2
  86. package/dist/cjs/graph/traverse.js.map +1 -1
  87. package/dist/cjs/index.d.cts +1 -1
  88. package/dist/cjs/index.d.ts +1 -1
  89. package/dist/cjs/index.js +3 -0
  90. package/dist/cjs/index.js.map +1 -1
  91. package/dist/cjs/output/column-hint.js +3 -6
  92. package/dist/cjs/output/column-hint.js.map +1 -1
  93. package/dist/cjs/output/output.js +7 -14
  94. package/dist/cjs/output/output.js.map +1 -1
  95. package/dist/cjs/output/search-error.js +2 -5
  96. package/dist/cjs/output/search-error.js.map +1 -1
  97. package/dist/cjs/scan/frontmatter.d.cts +1 -0
  98. package/dist/cjs/scan/frontmatter.d.ts +1 -0
  99. package/dist/cjs/scan/frontmatter.js +14 -18
  100. package/dist/cjs/scan/frontmatter.js.map +1 -1
  101. package/dist/cjs/scan/list.js +2 -3
  102. package/dist/cjs/scan/list.js.map +1 -1
  103. package/dist/cjs/scan/reparse.js +12 -26
  104. package/dist/cjs/scan/reparse.js.map +1 -1
  105. package/dist/cjs/scan/worker-error.js.map +1 -1
  106. package/dist/cjs/store/cache.d.cts +2 -0
  107. package/dist/cjs/store/cache.d.ts +2 -0
  108. package/dist/cjs/store/cache.js +20 -0
  109. package/dist/cjs/store/cache.js.map +1 -0
  110. package/dist/cjs/store/duckdb/batch.js +4 -16
  111. package/dist/cjs/store/duckdb/batch.js.map +1 -1
  112. package/dist/cjs/store/duckdb/connection.js +8 -20
  113. package/dist/cjs/store/duckdb/connection.js.map +1 -1
  114. package/dist/cjs/store/duckdb/fieldStats.js +2 -8
  115. package/dist/cjs/store/duckdb/fieldStats.js.map +1 -1
  116. package/dist/cjs/store/duckdb/lexical.js +21 -29
  117. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  118. package/dist/cjs/store/duckdb/native.js.map +1 -1
  119. package/dist/cjs/store/duckdb/open.js +12 -13
  120. package/dist/cjs/store/duckdb/open.js.map +1 -1
  121. package/dist/cjs/store/duckdb/reconcile.js +4 -11
  122. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  123. package/dist/cjs/store/duckdb/sql-functions.js +2 -4
  124. package/dist/cjs/store/duckdb/sql-functions.js.map +1 -1
  125. package/dist/cjs/store/duckdb/store.js +2 -3
  126. package/dist/cjs/store/duckdb/store.js.map +1 -1
  127. package/dist/cjs/store/duckdb/vectors.js +8 -18
  128. package/dist/cjs/store/duckdb/vectors.js.map +1 -1
  129. package/dist/cjs/store/index.d.cts +3 -2
  130. package/dist/cjs/store/index.d.ts +3 -2
  131. package/dist/cjs/store/index.js +7 -8
  132. package/dist/cjs/store/index.js.map +1 -1
  133. package/dist/cjs/store/native.js +6 -16
  134. package/dist/cjs/store/native.js.map +1 -1
  135. package/dist/cjs/store/sql-functions.js.map +1 -1
  136. package/dist/cjs/store/sqlite/connection.js +4 -8
  137. package/dist/cjs/store/sqlite/connection.js.map +1 -1
  138. package/dist/cjs/store/sqlite/fieldStats.js.map +1 -1
  139. package/dist/cjs/store/sqlite/lexical.js +3 -5
  140. package/dist/cjs/store/sqlite/lexical.js.map +1 -1
  141. package/dist/cjs/store/sqlite/open.d.cts +0 -1
  142. package/dist/cjs/store/sqlite/open.d.ts +0 -1
  143. package/dist/cjs/store/sqlite/open.js +22 -40
  144. package/dist/cjs/store/sqlite/open.js.map +1 -1
  145. package/dist/cjs/store/sqlite/reconcile.js +2 -3
  146. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  147. package/dist/cjs/store/sqlite/sql-functions.js +2 -4
  148. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
  149. package/dist/cjs/store/sqlite/store.js.map +1 -1
  150. package/dist/cjs/store/transaction.js +1 -3
  151. package/dist/cjs/store/transaction.js.map +1 -1
  152. package/dist/cjs/store/turso/connection.js +2 -6
  153. package/dist/cjs/store/turso/connection.js.map +1 -1
  154. package/dist/cjs/store/turso/fieldStats.js.map +1 -1
  155. package/dist/cjs/store/turso/lexical.d.cts +2 -0
  156. package/dist/cjs/store/turso/lexical.d.ts +2 -0
  157. package/dist/cjs/store/turso/lexical.js +347 -0
  158. package/dist/cjs/store/turso/lexical.js.map +1 -0
  159. package/dist/cjs/store/turso/native.js.map +1 -1
  160. package/dist/cjs/store/turso/open.d.cts +1 -1
  161. package/dist/cjs/store/turso/open.d.ts +1 -1
  162. package/dist/cjs/store/turso/open.js +81 -54
  163. package/dist/cjs/store/turso/open.js.map +1 -1
  164. package/dist/cjs/store/turso/reconcile.js +22 -22
  165. package/dist/cjs/store/turso/reconcile.js.map +1 -1
  166. package/dist/cjs/store/turso/store.js +18 -41
  167. package/dist/cjs/store/turso/store.js.map +1 -1
  168. package/dist/cjs/store/turso/vectors.d.cts +8 -0
  169. package/dist/cjs/store/turso/vectors.d.ts +8 -0
  170. package/dist/cjs/store/turso/vectors.js +435 -0
  171. package/dist/cjs/store/turso/vectors.js.map +1 -0
  172. package/dist/cjs/store/types.js +2 -3
  173. package/dist/cjs/store/types.js.map +1 -1
  174. package/dist/cjs/store/vectors.js.map +1 -1
  175. package/dist/cjs/text/segment.d.cts +1 -0
  176. package/dist/cjs/text/segment.d.ts +1 -0
  177. package/dist/cjs/text/segment.js +10 -14
  178. package/dist/cjs/text/segment.js.map +1 -1
  179. package/dist/cjs/watch.js +2 -5
  180. package/dist/cjs/watch.js.map +1 -1
  181. package/dist/cjs/workers/parse.js.map +1 -1
  182. package/dist/esm/chunk/group.js +10 -23
  183. package/dist/esm/chunk/group.js.map +1 -1
  184. package/dist/esm/chunk/parse.js +4 -8
  185. package/dist/esm/chunk/parse.js.map +1 -1
  186. package/dist/esm/cli/index.js +3 -5
  187. package/dist/esm/cli/index.js.map +1 -1
  188. package/dist/esm/cli/shared.js +12 -22
  189. package/dist/esm/cli/shared.js.map +1 -1
  190. package/dist/esm/cli/status.js +43 -9
  191. package/dist/esm/cli/status.js.map +1 -1
  192. package/dist/esm/cli.js +2 -8
  193. package/dist/esm/cli.js.map +1 -1
  194. package/dist/esm/commands/map.js +1 -3
  195. package/dist/esm/commands/map.js.map +1 -1
  196. package/dist/esm/commands/peek.js.map +1 -1
  197. package/dist/esm/commands/related.js +3 -5
  198. package/dist/esm/commands/related.js.map +1 -1
  199. package/dist/esm/commands/scope.js +3 -5
  200. package/dist/esm/commands/scope.js.map +1 -1
  201. package/dist/esm/commands/search.js +10 -16
  202. package/dist/esm/commands/search.js.map +1 -1
  203. package/dist/esm/commands/signals.js +3 -7
  204. package/dist/esm/commands/signals.js.map +1 -1
  205. package/dist/esm/config/access.js +8 -13
  206. package/dist/esm/config/access.js.map +1 -1
  207. package/dist/esm/config/index.d.ts +1 -1
  208. package/dist/esm/config/index.js +1 -1
  209. package/dist/esm/config/index.js.map +1 -1
  210. package/dist/esm/config/load.js +10 -24
  211. package/dist/esm/config/load.js.map +1 -1
  212. package/dist/esm/config/resolve.js +2 -3
  213. package/dist/esm/config/resolve.js.map +1 -1
  214. package/dist/esm/config/signals.js +3 -5
  215. package/dist/esm/config/signals.js.map +1 -1
  216. package/dist/esm/config/types.d.ts +2 -1
  217. package/dist/esm/config/types.js +8 -2
  218. package/dist/esm/config/types.js.map +1 -1
  219. package/dist/esm/config/validate.d.ts +1 -1
  220. package/dist/esm/config/validate.js +7 -10
  221. package/dist/esm/config/validate.js.map +1 -1
  222. package/dist/esm/embed/cohere.js +2 -2
  223. package/dist/esm/embed/cohere.js.map +1 -1
  224. package/dist/esm/embed/distribution.js +1 -1
  225. package/dist/esm/embed/distribution.js.map +1 -1
  226. package/dist/esm/embed/handoff.d.ts +3 -0
  227. package/dist/esm/embed/handoff.js +21 -0
  228. package/dist/esm/embed/handoff.js.map +1 -0
  229. package/dist/esm/embed/identity.d.ts +11 -10
  230. package/dist/esm/embed/identity.js +29 -26
  231. package/dist/esm/embed/identity.js.map +1 -1
  232. package/dist/esm/embed/langfit.js +2 -3
  233. package/dist/esm/embed/langfit.js.map +1 -1
  234. package/dist/esm/embed/languages.js +2 -4
  235. package/dist/esm/embed/languages.js.map +1 -1
  236. package/dist/esm/embed/openai.js +2 -2
  237. package/dist/esm/embed/openai.js.map +1 -1
  238. package/dist/esm/embed/query.js +21 -6
  239. package/dist/esm/embed/query.js.map +1 -1
  240. package/dist/esm/embed/registry.js +1 -2
  241. package/dist/esm/embed/registry.js.map +1 -1
  242. package/dist/esm/embed/static.d.ts +1 -1
  243. package/dist/esm/embed/static.js +8 -10
  244. package/dist/esm/embed/static.js.map +1 -1
  245. package/dist/esm/embed/store.d.ts +2 -2
  246. package/dist/esm/embed/store.js +28 -23
  247. package/dist/esm/embed/store.js.map +1 -1
  248. package/dist/esm/embed/types.js +1 -3
  249. package/dist/esm/embed/types.js.map +1 -1
  250. package/dist/esm/errors.d.ts +1 -1
  251. package/dist/esm/errors.js.map +1 -1
  252. package/dist/esm/features/embed.js +16 -16
  253. package/dist/esm/features/embed.js.map +1 -1
  254. package/dist/esm/features/fences.js +9 -19
  255. package/dist/esm/features/fences.js.map +1 -1
  256. package/dist/esm/features/links.js +24 -49
  257. package/dist/esm/features/links.js.map +1 -1
  258. package/dist/esm/features/sections.js +20 -20
  259. package/dist/esm/features/sections.js.map +1 -1
  260. package/dist/esm/features/tags.js +5 -8
  261. package/dist/esm/features/tags.js.map +1 -1
  262. package/dist/esm/features/types.js.map +1 -1
  263. package/dist/esm/graph/traverse.js +1 -2
  264. package/dist/esm/graph/traverse.js.map +1 -1
  265. package/dist/esm/index.d.ts +1 -1
  266. package/dist/esm/index.js +1 -1
  267. package/dist/esm/index.js.map +1 -1
  268. package/dist/esm/output/column-hint.js +3 -6
  269. package/dist/esm/output/column-hint.js.map +1 -1
  270. package/dist/esm/output/output.js +9 -20
  271. package/dist/esm/output/output.js.map +1 -1
  272. package/dist/esm/output/search-error.js +2 -5
  273. package/dist/esm/output/search-error.js.map +1 -1
  274. package/dist/esm/scan/frontmatter.d.ts +1 -0
  275. package/dist/esm/scan/frontmatter.js +13 -18
  276. package/dist/esm/scan/frontmatter.js.map +1 -1
  277. package/dist/esm/scan/list.js +2 -3
  278. package/dist/esm/scan/list.js.map +1 -1
  279. package/dist/esm/scan/reparse.js +16 -34
  280. package/dist/esm/scan/reparse.js.map +1 -1
  281. package/dist/esm/scan/worker-error.js.map +1 -1
  282. package/dist/esm/store/cache.d.ts +2 -0
  283. package/dist/esm/store/cache.js +11 -0
  284. package/dist/esm/store/cache.js.map +1 -0
  285. package/dist/esm/store/duckdb/batch.js +4 -16
  286. package/dist/esm/store/duckdb/batch.js.map +1 -1
  287. package/dist/esm/store/duckdb/connection.js +8 -20
  288. package/dist/esm/store/duckdb/connection.js.map +1 -1
  289. package/dist/esm/store/duckdb/fieldStats.js +4 -12
  290. package/dist/esm/store/duckdb/fieldStats.js.map +1 -1
  291. package/dist/esm/store/duckdb/lexical.js +29 -36
  292. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  293. package/dist/esm/store/duckdb/native.js +2 -7
  294. package/dist/esm/store/duckdb/native.js.map +1 -1
  295. package/dist/esm/store/duckdb/open.js +16 -23
  296. package/dist/esm/store/duckdb/open.js.map +1 -1
  297. package/dist/esm/store/duckdb/reconcile.js +4 -11
  298. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  299. package/dist/esm/store/duckdb/sql-functions.js +4 -9
  300. package/dist/esm/store/duckdb/sql-functions.js.map +1 -1
  301. package/dist/esm/store/duckdb/store.js +6 -14
  302. package/dist/esm/store/duckdb/store.js.map +1 -1
  303. package/dist/esm/store/duckdb/vectors.js +14 -31
  304. package/dist/esm/store/duckdb/vectors.js.map +1 -1
  305. package/dist/esm/store/index.d.ts +3 -2
  306. package/dist/esm/store/index.js +5 -6
  307. package/dist/esm/store/index.js.map +1 -1
  308. package/dist/esm/store/native.js +10 -25
  309. package/dist/esm/store/native.js.map +1 -1
  310. package/dist/esm/store/sql-functions.js +3 -5
  311. package/dist/esm/store/sql-functions.js.map +1 -1
  312. package/dist/esm/store/sqlite/connection.js +4 -8
  313. package/dist/esm/store/sqlite/connection.js.map +1 -1
  314. package/dist/esm/store/sqlite/fieldStats.js +2 -6
  315. package/dist/esm/store/sqlite/fieldStats.js.map +1 -1
  316. package/dist/esm/store/sqlite/lexical.js +5 -9
  317. package/dist/esm/store/sqlite/lexical.js.map +1 -1
  318. package/dist/esm/store/sqlite/open.d.ts +0 -1
  319. package/dist/esm/store/sqlite/open.js +21 -41
  320. package/dist/esm/store/sqlite/open.js.map +1 -1
  321. package/dist/esm/store/sqlite/reconcile.js +6 -13
  322. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  323. package/dist/esm/store/sqlite/sql-functions.js +2 -4
  324. package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
  325. package/dist/esm/store/sqlite/store.js +1 -2
  326. package/dist/esm/store/sqlite/store.js.map +1 -1
  327. package/dist/esm/store/transaction.js +2 -5
  328. package/dist/esm/store/transaction.js.map +1 -1
  329. package/dist/esm/store/turso/connection.js +2 -6
  330. package/dist/esm/store/turso/connection.js.map +1 -1
  331. package/dist/esm/store/turso/fieldStats.js +2 -5
  332. package/dist/esm/store/turso/fieldStats.js.map +1 -1
  333. package/dist/esm/store/turso/lexical.d.ts +2 -0
  334. package/dist/esm/store/turso/lexical.js +128 -0
  335. package/dist/esm/store/turso/lexical.js.map +1 -0
  336. package/dist/esm/store/turso/native.js +2 -7
  337. package/dist/esm/store/turso/native.js.map +1 -1
  338. package/dist/esm/store/turso/open.d.ts +1 -1
  339. package/dist/esm/store/turso/open.js +36 -29
  340. package/dist/esm/store/turso/open.js.map +1 -1
  341. package/dist/esm/store/turso/reconcile.js +22 -22
  342. package/dist/esm/store/turso/reconcile.js.map +1 -1
  343. package/dist/esm/store/turso/store.js +17 -28
  344. package/dist/esm/store/turso/store.js.map +1 -1
  345. package/dist/esm/store/turso/vectors.d.ts +8 -0
  346. package/dist/esm/store/turso/vectors.js +98 -0
  347. package/dist/esm/store/turso/vectors.js.map +1 -0
  348. package/dist/esm/store/types.js +2 -3
  349. package/dist/esm/store/types.js.map +1 -1
  350. package/dist/esm/store/vectors.js +2 -3
  351. package/dist/esm/store/vectors.js.map +1 -1
  352. package/dist/esm/text/segment.d.ts +1 -0
  353. package/dist/esm/text/segment.js +13 -21
  354. package/dist/esm/text/segment.js.map +1 -1
  355. package/dist/esm/watch.js +2 -5
  356. package/dist/esm/watch.js.map +1 -1
  357. package/dist/esm/workers/parse.js.map +1 -1
  358. package/package.json +3 -1
  359. package/schema.json +2 -2
  360. package/skills/sense/SKILL.md +2 -2
  361. package/skills/sense-setup/SKILL.md +3 -3
  362. package/dist/cjs/store/meta.d.cts +0 -3
  363. package/dist/cjs/store/meta.d.ts +0 -3
  364. package/dist/cjs/store/meta.js +0 -215
  365. package/dist/cjs/store/meta.js.map +0 -1
  366. package/dist/esm/store/meta.d.ts +0 -3
  367. package/dist/esm/store/meta.js +0 -17
  368. package/dist/esm/store/meta.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/connection.ts"],"sourcesContent":["import type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\n\n// Wraps node:sqlite's synchronous DatabaseSync in the async Connection/Statement shape every\n// store presents. Every method below does its real work synchronously and returns an already-\n// resolved promise, so reconcile's loop, the feature hooks, and runBatch never actually cross\n// an async boundary internally -- no regression against the pre-Store synchronous code.\nclass SqliteStatement implements Statement {\n private stmt: StatementSync;\n\n constructor(stmt: StatementSync) {\n this.stmt = stmt;\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n return this.stmt.run(...(params as Parameters<StatementSync['run']>));\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n return this.stmt.get(...(params as Parameters<StatementSync['get']>));\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n return this.stmt.all(...(params as Parameters<StatementSync['all']>));\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n yield* this.stmt.iterate(...(params as Parameters<StatementSync['iterate']>));\n }\n\n columns(): Array<{ name: string }> {\n return this.stmt.columns();\n }\n\n setReadBigInts(enabled: boolean): void {\n this.stmt.setReadBigInts(enabled);\n }\n}\n\nexport function createConnection(db: DatabaseSync): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n db.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new SqliteStatement(db.prepare(sql));\n },\n // Prepares once and loops synchronously: today's code, unchanged, wrapped as one async call\n // so features share one Connection contract with DuckDB. Joins the caller's transaction when\n // there is one (reconcile); otherwise opens its own, so a standalone batch (a vector write\n // outside any Store.transaction()) stays atomic and commits once, not once per row.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(conn, async () => {\n const stmt = db.prepare(sql);\n for (const row of paramRows) stmt.run(...(row as Parameters<StatementSync['run']>));\n });\n },\n };\n return conn;\n}\n"],"names":["createConnection","SqliteStatement","stmt","run","params","get","all","iterate","columns","setReadBigInts","enabled","db","conn","exec","sql","prepare","runBatch","paramRows","length","withTransaction","row"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/connection.ts"],"sourcesContent":["import type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\n\n// Wraps node:sqlite's synchronous DatabaseSync in the async Connection/Statement shape every store presents. Every method does its real\n// work synchronously and returns an already-resolved promise, so reconcile's loop, the feature hooks, and runBatch never cross an async boundary internally.\nclass SqliteStatement implements Statement {\n private stmt: StatementSync;\n\n constructor(stmt: StatementSync) {\n this.stmt = stmt;\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n return this.stmt.run(...(params as Parameters<StatementSync['run']>));\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n return this.stmt.get(...(params as Parameters<StatementSync['get']>));\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n return this.stmt.all(...(params as Parameters<StatementSync['all']>));\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n yield* this.stmt.iterate(...(params as Parameters<StatementSync['iterate']>));\n }\n\n columns(): Array<{ name: string }> {\n return this.stmt.columns();\n }\n\n setReadBigInts(enabled: boolean): void {\n this.stmt.setReadBigInts(enabled);\n }\n}\n\nexport function createConnection(db: DatabaseSync): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n db.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new SqliteStatement(db.prepare(sql));\n },\n // Prepares once and loops synchronously, wrapped as one async call so features share one\n // Connection contract with DuckDB. Joins the caller's transaction when there is one (reconcile); otherwise opens its own, so a standalone batch stays atomic and commits once, not per row.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(conn, async () => {\n const stmt = db.prepare(sql);\n for (const row of paramRows) stmt.run(...(row as Parameters<StatementSync['run']>));\n });\n },\n };\n return conn;\n}\n"],"names":["createConnection","SqliteStatement","stmt","run","params","get","all","iterate","columns","setReadBigInts","enabled","db","conn","exec","sql","prepare","runBatch","paramRows","length","withTransaction","row"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/fieldStats.ts"],"sourcesContent":["import { quoteIdent } from '../shared.ts';\nimport type { Connection, FieldStat } from '../types.ts';\n\n// One aggregate query, one result row: COUNT + GROUP_CONCAT(DISTINCT typeof()) per column, so\n// the query stays O(columns) rather than O(rows x columns). typeof()'s output already is the\n// shared vocabulary (integer/real/text -- mapValue() never binds a boolean or blob), so no\n// mapping step is needed here (contrast duckdb/fieldStats.ts). GROUP_CONCAT(DISTINCT ...)'s\n// order is not guaranteed sorted (spike-verified: 3+ distinct types come back in insertion\n// order), so the type set is sorted in JS.\nexport async function fieldStats(conn: Connection, columns: string[], scopeWhere: string): Promise<FieldStat[]> {\n const exprs = columns.map((name, i) => {\n const quoted = quoteIdent(name);\n return `COUNT(${quoted}) AS n${i}, GROUP_CONCAT(DISTINCT typeof(${quoted})) FILTER (WHERE ${quoted} IS NOT NULL) AS t${i}`;\n });\n const stmt = await conn.prepare(`SELECT ${exprs.join(', ')} FROM frontmatter ${scopeWhere}`);\n const row = (await stmt.get()) as Record<string, number | string | null>;\n return columns.map((name, i) => ({\n field: name,\n coverage: Number(row[`n${i}`] ?? 0),\n type: [...new Set(((row[`t${i}`] as string) ?? '').split(',').filter(Boolean))].sort().join(','),\n }));\n}\n"],"names":["fieldStats","conn","columns","scopeWhere","exprs","stmt","row","map","name","i","quoted","quoteIdent","prepare","join","get","field","coverage","Number","type","Set","split","filter","Boolean","sort"],"mappings":";;;;+BASsBA;;;eAAAA;;;wBATK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASpB,SAAeA,WAAWC,IAAgB,EAAEC,OAAiB,EAAEC,UAAkB;;YAChFC,OAIAC,MACAC;;;;oBALAF,QAAQF,QAAQK,GAAG,CAAC,SAACC,MAAMC;wBAC/B,IAAMC,SAASC,IAAAA,oBAAU,EAACH;wBAC1B,OAAO,AAAC,SAAuBC,OAAfC,QAAO,UAA2CA,OAAnCD,GAAE,mCAA2DC,OAA1BA,QAAO,qBAA8CD,OAA3BC,QAAO,sBAAsB,OAAFD;oBACzH;oBACa;;wBAAMR,KAAKW,OAAO,CAAC,AAAC,UAA8CT,OAArCC,MAAMS,IAAI,CAAC,OAAM,sBAA+B,OAAXV;;;oBAAzEE,OAAO;oBACA;;wBAAMA,KAAKS,GAAG;;;oBAArBR,MAAO;oBACb;;wBAAOJ,QAAQK,GAAG,CAAC,SAACC,MAAMC;gCAEPH,OACGA;mCAHW;gCAC/BS,OAAOP;gCACPQ,UAAUC,QAAOX,QAAAA,GAAG,CAAC,AAAC,IAAK,OAAFG,GAAI,cAAZH,mBAAAA,QAAgB;gCACjCY,MAAM,AAAC,qBAAG,IAAIC,IAAI,EAAEb,SAAAA,GAAG,CAAC,AAAC,IAAK,OAAFG,GAAI,cAAZH,oBAAAA,SAA2B,IAAIc,KAAK,CAAC,KAAKC,MAAM,CAACC,WAAWC,IAAI,GAAGV,IAAI,CAAC;4BAC9F;;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/fieldStats.ts"],"sourcesContent":["import { quoteIdent } from '../shared.ts';\nimport type { Connection, FieldStat } from '../types.ts';\n\n// One aggregate query per column (COUNT + GROUP_CONCAT(DISTINCT typeof())), O(columns) not O(rows x columns);\n// typeof()'s output is already the shared vocabulary, and GROUP_CONCAT's order is unsorted, so JS sorts the type set.\nexport async function fieldStats(conn: Connection, columns: string[], scopeWhere: string): Promise<FieldStat[]> {\n const exprs = columns.map((name, i) => {\n const quoted = quoteIdent(name);\n return `COUNT(${quoted}) AS n${i}, GROUP_CONCAT(DISTINCT typeof(${quoted})) FILTER (WHERE ${quoted} IS NOT NULL) AS t${i}`;\n });\n const stmt = await conn.prepare(`SELECT ${exprs.join(', ')} FROM frontmatter ${scopeWhere}`);\n const row = (await stmt.get()) as Record<string, number | string | null>;\n return columns.map((name, i) => ({\n field: name,\n coverage: Number(row[`n${i}`] ?? 0),\n type: [...new Set(((row[`t${i}`] as string) ?? '').split(',').filter(Boolean))].sort().join(','),\n }));\n}\n"],"names":["fieldStats","conn","columns","scopeWhere","exprs","stmt","row","map","name","i","quoted","quoteIdent","prepare","join","get","field","coverage","Number","type","Set","split","filter","Boolean","sort"],"mappings":";;;;+BAKsBA;;;eAAAA;;;wBALK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKpB,SAAeA,WAAWC,IAAgB,EAAEC,OAAiB,EAAEC,UAAkB;;YAChFC,OAIAC,MACAC;;;;oBALAF,QAAQF,QAAQK,GAAG,CAAC,SAACC,MAAMC;wBAC/B,IAAMC,SAASC,IAAAA,oBAAU,EAACH;wBAC1B,OAAO,AAAC,SAAuBC,OAAfC,QAAO,UAA2CA,OAAnCD,GAAE,mCAA2DC,OAA1BA,QAAO,qBAA8CD,OAA3BC,QAAO,sBAAsB,OAAFD;oBACzH;oBACa;;wBAAMR,KAAKW,OAAO,CAAC,AAAC,UAA8CT,OAArCC,MAAMS,IAAI,CAAC,OAAM,sBAA+B,OAAXV;;;oBAAzEE,OAAO;oBACA;;wBAAMA,KAAKS,GAAG;;;oBAArBR,MAAO;oBACb;;wBAAOJ,QAAQK,GAAG,CAAC,SAACC,MAAMC;gCAEPH,OACGA;mCAHW;gCAC/BS,OAAOP;gCACPQ,UAAUC,QAAOX,QAAAA,GAAG,CAAC,AAAC,IAAK,OAAFG,GAAI,cAAZH,mBAAAA,QAAgB;gCACjCY,MAAM,AAAC,qBAAG,IAAIC,IAAI,EAAEb,SAAAA,GAAG,CAAC,AAAC,IAAK,OAAFG,GAAI,cAAZH,oBAAAA,SAA2B,IAAIc,KAAK,CAAC,KAAKC,MAAM,CAACC,WAAWC,IAAI,GAAGV,IAAI,CAAC;4BAC9F;;;;;IACF"}
@@ -135,8 +135,7 @@ function _ts_generator(thisArg, body) {
135
135
  }
136
136
  }
137
137
  // snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc
138
- // (benchmark/reports/2026-08-23-hub-release-battery.md). Past this bound, hit stays NULL for
139
- // the caller's JS excerpt fallback.
138
+ // (benchmark/reports/2026-08-23-hub-release-battery.md); past this bound, hit stays NULL for the caller's JS excerpt fallback.
140
139
  var SNIPPET_BOUND = 16384;
141
140
  var WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';
142
141
  function queryLexical(conn, terms, opts, segmenting) {
@@ -147,9 +146,8 @@ function queryLexical(conn, terms, opts, segmenting) {
147
146
  case 0:
148
147
  whereJoin = opts.whereJoin, whereCond = opts.whereCond, scopeCond = opts.scopeCond, limit = opts.limit;
149
148
  query = segmenting ? (0, _segmentts.segmentMatch)(terms) : terms;
150
- // A run of pure unspaced-script punctuation segments to nothing searchable (segmentField
151
- // drops empty groups the same way): an empty MATCH string is an FTS5 syntax error, not zero
152
- // rows, so this returns the zero rows directly rather than issuing it.
149
+ // A run of pure unspaced-script punctuation segments to nothing searchable: an empty MATCH
150
+ // string is an FTS5 syntax error, not zero rows, so return zero rows directly rather than issuing it.
153
151
  if (query.trim() === '') return [
154
152
  2,
155
153
  []
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/lexical.ts"],"sourcesContent":["import { segmentMatch } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\n// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc\n// (benchmark/reports/2026-08-23-hub-release-battery.md). Past this bound, hit stays NULL for\n// the caller's JS excerpt fallback.\nconst SNIPPET_BOUND = 16_384;\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';\n\n// Ranked BM25 word-match query with excerpt, scoped by the caller-built SQL fragments.\n// `segmenting` mirrors the predicate that decided whether the `_seg` sidecars were populated\n// (contentTokenize(cfg) === undefined, see sqlite/store.ts): an unspaced-script run in `terms`\n// becomes a quoted grapheme phrase against those sidecars only when they exist.\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions, segmenting: boolean): Promise<LexicalHit[]> {\n const { whereJoin, whereCond, scopeCond, limit } = opts;\n const query = segmenting ? segmentMatch(terms) : terms;\n // A run of pure unspaced-script punctuation segments to nothing searchable (segmentField\n // drops empty groups the same way): an empty MATCH string is an FTS5 syntax error, not zero\n // rows, so this returns the zero rows directly rather than issuing it.\n if (query.trim() === '') return [];\n const sql = `SELECT content.path AS path, CASE WHEN length(content.text) <= ${SNIPPET_BOUND} THEN snippet(content, 2, '«', '»', '…', 10) ELSE NULL END AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ${scopeCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${limit}`;\n const stmt = await conn.prepare(sql);\n return (await stmt.all(query)) as unknown as LexicalHit[];\n}\n"],"names":["queryLexical","SNIPPET_BOUND","WEIGHTED_BM25","conn","terms","opts","segmenting","whereJoin","whereCond","scopeCond","limit","query","sql","stmt","segmentMatch","trim","prepare","all"],"mappings":";;;;+BAasBA;;;eAAAA;;;yBAbO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG7B,iFAAiF;AACjF,6FAA6F;AAC7F,oCAAoC;AACpC,IAAMC,gBAAgB;AACtB,IAAMC,gBAAgB;AAMf,SAAeF,aAAaG,IAAgB,EAAEC,KAAa,EAAEC,IAAyB,EAAEC,UAAmB;;YACxGC,WAAWC,WAAWC,WAAWC,OACnCC,OAKAC,KACAC;;;;oBAPEN,YAA2CF,KAA3CE,WAAWC,YAAgCH,KAAhCG,WAAWC,YAAqBJ,KAArBI,WAAWC,QAAUL,KAAVK;oBACnCC,QAAQL,aAAaQ,IAAAA,uBAAY,EAACV,SAASA;oBACjD,yFAAyF;oBACzF,4FAA4F;oBAC5F,uEAAuE;oBACvE,IAAIO,MAAMI,IAAI,OAAO,IAAI;;;;oBACnBH,MAAM,AAAC,kEAAgKL,OAA/FN,eAAc,yFAAoHO,OAAnCD,WAAU,2BAAsCE,OAAbD,WAAU,KAAyBN,OAAtBO,WAAU,cAAmCC,OAAvBR,eAAc,WAAe,OAANQ;oBAC7P;;wBAAMP,KAAKa,OAAO,CAACJ;;;oBAA1BC,OAAO;oBACL;;wBAAMA,KAAKI,GAAG,CAACN;;;oBAAvB;;wBAAQ;;;;IACV"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/lexical.ts"],"sourcesContent":["import { segmentMatch } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\n// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc\n// (benchmark/reports/2026-08-23-hub-release-battery.md); past this bound, hit stays NULL for the caller's JS excerpt fallback.\nconst SNIPPET_BOUND = 16_384;\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';\n\n// Ranked BM25 word-match query with excerpt, scoped by the caller-built SQL fragments. `segmenting`\n// mirrors the predicate that populated the `_seg` sidecars (contentTokenize(cfg) === undefined): an unspaced-script run in `terms` becomes a quoted grapheme phrase against them only when they exist.\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions, segmenting: boolean): Promise<LexicalHit[]> {\n const { whereJoin, whereCond, scopeCond, limit } = opts;\n const query = segmenting ? segmentMatch(terms) : terms;\n // A run of pure unspaced-script punctuation segments to nothing searchable: an empty MATCH\n // string is an FTS5 syntax error, not zero rows, so return zero rows directly rather than issuing it.\n if (query.trim() === '') return [];\n const sql = `SELECT content.path AS path, CASE WHEN length(content.text) <= ${SNIPPET_BOUND} THEN snippet(content, 2, '«', '»', '…', 10) ELSE NULL END AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ${scopeCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${limit}`;\n const stmt = await conn.prepare(sql);\n return (await stmt.all(query)) as unknown as LexicalHit[];\n}\n"],"names":["queryLexical","SNIPPET_BOUND","WEIGHTED_BM25","conn","terms","opts","segmenting","whereJoin","whereCond","scopeCond","limit","query","sql","stmt","segmentMatch","trim","prepare","all"],"mappings":";;;;+BAUsBA;;;eAAAA;;;yBAVO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG7B,iFAAiF;AACjF,+HAA+H;AAC/H,IAAMC,gBAAgB;AACtB,IAAMC,gBAAgB;AAIf,SAAeF,aAAaG,IAAgB,EAAEC,KAAa,EAAEC,IAAyB,EAAEC,UAAmB;;YACxGC,WAAWC,WAAWC,WAAWC,OACnCC,OAIAC,KACAC;;;;oBANEN,YAA2CF,KAA3CE,WAAWC,YAAgCH,KAAhCG,WAAWC,YAAqBJ,KAArBI,WAAWC,QAAUL,KAAVK;oBACnCC,QAAQL,aAAaQ,IAAAA,uBAAY,EAACV,SAASA;oBACjD,2FAA2F;oBAC3F,sGAAsG;oBACtG,IAAIO,MAAMI,IAAI,OAAO,IAAI;;;;oBACnBH,MAAM,AAAC,kEAAgKL,OAA/FN,eAAc,yFAAoHO,OAAnCD,WAAU,2BAAsCE,OAAbD,WAAU,KAAyBN,OAAtBO,WAAU,cAAmCC,OAAvBR,eAAc,WAAe,OAANQ;oBAC7P;;wBAAMP,KAAKa,OAAO,CAACJ;;;oBAA1BC,OAAO;oBACL;;wBAAMA,KAAKI,GAAG,CAACN;;;oBAAvB;;wBAAQ;;;;IACV"}
@@ -11,4 +11,3 @@ export interface OpenResult {
11
11
  }
12
12
  export declare function openSqlite(cfg: ResolvedConfig): Promise<OpenResult>;
13
13
  export declare function docCount(store: Store): Promise<number>;
14
- export declare function clearCache(cfg: ResolvedConfig): void;
@@ -11,4 +11,3 @@ export interface OpenResult {
11
11
  }
12
12
  export declare function openSqlite(cfg: ResolvedConfig): Promise<OpenResult>;
13
13
  export declare function docCount(store: Store): Promise<number>;
14
- export declare function clearCache(cfg: ResolvedConfig): void;
@@ -17,9 +17,6 @@ _export(exports, {
17
17
  get SCHEMA_VERSION () {
18
18
  return SCHEMA_VERSION;
19
19
  },
20
- get clearCache () {
21
- return clearCache;
22
- },
23
20
  get docCount () {
24
21
  return docCount;
25
22
  },
@@ -31,8 +28,10 @@ var _nodefs = require("node:fs");
31
28
  var _nodepath = require("node:path");
32
29
  var _nodesqlite = require("node:sqlite");
33
30
  var _indexts = require("../../config/index.js");
31
+ var _handoffts = require("../../embed/handoff.js");
34
32
  var _errorsts = require("../../errors.js");
35
33
  var _indexts1 = require("../../features/index.js");
34
+ var _cachets = require("../cache.js");
36
35
  var _sharedts = require("../shared.js");
37
36
  var _transactionts = require("../transaction.js");
38
37
  var _connectionts = require("./connection.js");
@@ -193,16 +192,11 @@ function _unsupported_iterable_to_array(o, minLen) {
193
192
  }
194
193
  var DB_FILENAME = 'cache.db';
195
194
  var SCHEMA_VERSION = '18';
196
- // Stemming is English-only, but the segmentation underneath it is what decides coverage:
197
- // unicode61 splits on spaces, so a language written without them (Chinese, Japanese, Thai)
198
- // indexes a whole run as one token and word search finds nothing. `content.tokenize` is how
199
- // such a tree picks trigram instead.
195
+ // unicode61 splits on spaces, so a language written without them indexes a whole run as one
196
+ // token and word search finds nothing; `content.tokenize` is how such a tree picks trigram.
200
197
  var DEFAULT_TOKENIZE = 'porter unicode61';
201
- // FTS5 takes its tokenizer as a string literal inside DDL, where nothing can bind, so a
202
- // configured value has to be concatenated. Probing a throwaway table is what makes that safe
203
- // and is also the whole validation: anything the linked SQLite accepts passes, anything else
204
- // fails here with SQLite's own message rather than against the real table. It means no table
205
- // of which version added which tokenizer has to be maintained.
198
+ // FTS5 takes its tokenizer as a DDL string literal where nothing can bind, so the configured
199
+ // value is concatenated; probing a throwaway table first is both the safety and the validation.
206
200
  function resolveTokenize(db, cfg) {
207
201
  var configured = (0, _indexts.contentTokenize)(cfg);
208
202
  if (configured === undefined) return DEFAULT_TOKENIZE;
@@ -224,11 +218,8 @@ function storedTokenize(db) {
224
218
  var m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);
225
219
  return m ? m[1] : null;
226
220
  }
227
- // The three `_seg` sidecars are appended after path, never inserted: bm25(content, ...) and
228
- // snippet(content, 2, ...) are documented against the first three columns and keep working
229
- // (FTS5 defaults the weights it was not given). Each carries its field's exploded unspaced
230
- // runs for text that needs it, and an empty string for text that does not. Shared by
231
- // ensureSchema and the tokenize-only rebuild in connect(), so both create identical DDL.
221
+ // The `_seg` sidecars are appended after path, never inserted: bm25() and snippet(content, 2)
222
+ // are documented against the first three columns. Each holds its field's exploded unspaced runs.
232
223
  function createContentTable(conn, tokenize) {
233
224
  return _async_to_generator(function() {
234
225
  return _ts_generator(this, function(_state) {
@@ -393,9 +384,8 @@ function connect(cfg) {
393
384
  });
394
385
  dbPath = (0, _nodepath.join)(stateDir, DB_FILENAME);
395
386
  db = new _nodesqlite.DatabaseSync(dbPath);
396
- // A throw below (tokenizer probe, schema, reconcile's COLUMN_LIMIT) must release this handle,
397
- // or on Windows the leaked WAL db is undeletable and scratch cleanup fails with EPERM/EBUSY.
398
- // closed marks the rebuild branches that close-and-recurse, so the catch never double-closes.
387
+ // A throw below must release this handle, or on Windows the leaked WAL db is undeletable and
388
+ // scratch cleanup fails with EPERM/EBUSY. `closed` keeps the catch from double-closing.
399
389
  closed = false;
400
390
  _state.label = 1;
401
391
  case 1:
@@ -439,7 +429,7 @@ function connect(cfg) {
439
429
  console.error('sense: cache format changed (new sensemaking version); rebuilding the index');
440
430
  closed = true;
441
431
  db.close();
442
- clearCache(cfg);
432
+ (0, _cachets.clearCache)(cfg);
443
433
  return [
444
434
  2,
445
435
  connect(cfg)
@@ -451,11 +441,8 @@ function connect(cfg) {
451
441
  5
452
442
  ];
453
443
  console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');
454
- // Drop and recreate as one transaction: a crash before COMMIT rolls back to the table
455
- // that was there before (old tokenizer, still queryable), never to no table at all.
456
- // IF EXISTS also makes a retry after such a crash a no-op instead of a raw SQLite error --
457
- // meta.features is only updated once rebuildContentTable (below) finishes, so a retry
458
- // re-enters this branch and repopulates from scratch either way.
444
+ // One transaction, so a crash before COMMIT rolls back to the old tokenizer's table rather
445
+ // than to no table. IF EXISTS makes a retry after such a crash a no-op, not a raw error.
459
446
  return [
460
447
  4,
461
448
  (0, _transactionts.withTransaction)(conn, function() {
@@ -513,22 +500,20 @@ function connect(cfg) {
513
500
  console.error("sense: config change (".concat(changed, ") rebuilds the index"));
514
501
  closed = true;
515
502
  db.close();
516
- clearCache(cfg);
503
+ (0, _cachets.clearCache)(cfg);
517
504
  return [
518
505
  2,
519
506
  connect(cfg)
520
507
  ];
521
508
  case 8:
522
- // Meta can lie after a crash between table creation and the signature write; the table's
523
- // own DDL cannot. A mismatch here rebuilds no matter what meta says. A tokenize-only rebuild
524
- // already recreated content with this tokenize above, so stored already matches and this
525
- // guard is skipped naturally rather than needing its own case.
509
+ // Meta can lie after a crash between table creation and the signature write; the table's own
510
+ // DDL cannot, so a mismatch here rebuilds whatever meta says.
526
511
  stored = storedTokenize(db);
527
512
  if (stored !== null && stored !== tokenize) {
528
513
  console.error('sense: cache was built with a different content tokenizer; rebuilding the index');
529
514
  closed = true;
530
515
  db.close();
531
- clearCache(cfg);
516
+ (0, _cachets.clearCache)(cfg);
532
517
  return [
533
518
  2,
534
519
  connect(cfg)
@@ -599,7 +584,7 @@ function connect(cfg) {
599
584
  }
600
585
  function openSqlite(cfg) {
601
586
  return _async_to_generator(function() {
602
- var _ref, db, conn, resolvedCfg, dbPath, parsed, warnings;
587
+ var _ref, db, conn, resolvedCfg, dbPath, parsed, warnings, store;
603
588
  return _ts_generator(this, function(_state) {
604
589
  switch(_state.label){
605
590
  case 0:
@@ -609,10 +594,13 @@ function openSqlite(cfg) {
609
594
  ];
610
595
  case 1:
611
596
  _ref = _state.sent(), db = _ref.db, conn = _ref.conn, resolvedCfg = _ref.cfg, dbPath = _ref.dbPath, parsed = _ref.parsed, warnings = _ref.warnings;
597
+ store = (0, _storets.createStore)(db, conn, resolvedCfg, resolvedCfg.baseDir);
598
+ // reconcile ran before this object existed, so its chunk text is keyed by the connection.
599
+ (0, _handoffts.rekeyChunkText)(conn, store);
612
600
  return [
613
601
  2,
614
602
  {
615
- store: (0, _storets.createStore)(db, conn, resolvedCfg, resolvedCfg.baseDir),
603
+ store: store,
616
604
  cfg: resolvedCfg,
617
605
  dbPath: dbPath,
618
606
  parsed: parsed,
@@ -648,10 +636,4 @@ function docCount(store) {
648
636
  });
649
637
  })();
650
638
  }
651
- function clearCache(cfg) {
652
- (0, _nodefs.rmSync)((0, _nodepath.join)(cfg.baseDir, _indexts.STATE_DIR), {
653
- recursive: true,
654
- force: true
655
- });
656
- }
657
639
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { contentTokenize, featureSignature, STATE_DIR } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, Store } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { changedSignatureKeys, embedIdentityAdopted, rebuildContentTable, reconcile, signatureDiff } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '18';\n\ninterface ConnectResult {\n db: DatabaseSync;\n conn: Connection;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// Stemming is English-only, but the segmentation underneath it is what decides coverage:\n// unicode61 splits on spaces, so a language written without them (Chinese, Japanese, Thai)\n// indexes a whole run as one token and word search finds nothing. `content.tokenize` is how\n// such a tree picks trigram instead.\nconst DEFAULT_TOKENIZE = 'porter unicode61';\n\n// FTS5 takes its tokenizer as a string literal inside DDL, where nothing can bind, so a\n// configured value has to be concatenated. Probing a throwaway table is what makes that safe\n// and is also the whole validation: anything the linked SQLite accepts passes, anything else\n// fails here with SQLite's own message rather than against the real table. It means no table\n// of which version added which tokenizer has to be maintained.\nfunction resolveTokenize(db: DatabaseSync, cfg: Config): string {\n const configured = contentTokenize(cfg);\n if (configured === undefined) return DEFAULT_TOKENIZE;\n const literal = configured.replace(/'/g, \"''\");\n try {\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n db.exec(`CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '${literal}')`);\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n } catch (err) {\n throw new SenseError('CONFIG_INVALID', `content.tokenize \"${configured}\" is not a tokenizer this SQLite accepts (${(err as Error).message}); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options`);\n }\n return literal;\n}\n\n// The tokenizer the content table was actually built with, from its own DDL -- the one\n// record that cannot desynchronize from the table. NULL when the table does not exist yet.\nfunction storedTokenize(db: DatabaseSync): string | null {\n const row = db.prepare(`SELECT sql FROM sqlite_master WHERE name = 'content'`).get() as { sql: string } | undefined;\n if (!row) return null;\n const m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);\n return m ? m[1] : null;\n}\n\n// The three `_seg` sidecars are appended after path, never inserted: bm25(content, ...) and\n// snippet(content, 2, ...) are documented against the first three columns and keep working\n// (FTS5 defaults the weights it was not given). Each carries its field's exploded unspaced\n// runs for text that needs it, and an empty string for text that does not. Shared by\n// ensureSchema and the tokenize-only rebuild in connect(), so both create identical DDL.\nasync function createContentTable(conn: Connection, tokenize: string): Promise<void> {\n await conn.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${tokenize}')`);\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nasync function ensureSchema(conn: Connection, cfg: Config, tokenize: string): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n // IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL\n // against the resolved tokenizer before this runs, so a stale table is already gone by now.\n await createContentTable(conn, tokenize);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n await conn.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) await feature.schema(conn);\n if ((await getMeta(conn, 'schema_version')) === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);\n if ((await getMeta(conn, 'features')) === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));\n}\n\nasync function connect(cfg: ResolvedConfig): Promise<ConnectResult> {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n // A throw below (tokenizer probe, schema, reconcile's COLUMN_LIMIT) must release this handle,\n // or on Windows the leaked WAL db is undeletable and scratch cleanup fails with EPERM/EBUSY.\n // closed marks the rebuild branches that close-and-recurse, so the catch never double-closes.\n let closed = false;\n try {\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile (~5s for 500 files at 26k notes). A query\n // that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db, contentTokenize(cfg) === undefined);\n const conn = createConnection(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd\n // tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.\n const tokenize = resolveTokenize(db, cfg);\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const wantFeatures = featureSignature(cfg, FEATURES);\n let tokenizeOnlyRebuild = false;\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Indexing derives from presets, so a config edit rebuilding the cache must say so and\n // name what changed -- silent rebuilds make derived indexing look like a hang or a bug.\n if (version !== null && version !== SCHEMA_VERSION) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n // Only the tokenizer moved: frontmatter, links, sections, and embeddings are file-derived\n // and tokenizer-independent, so they don't need re-deriving. Everything else -- a preset\n // edit, an embed model change -- still takes the full clear/reopen below.\n if (changedKeys.size === 1 && changedKeys.has('tokenize')) {\n console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');\n // Drop and recreate as one transaction: a crash before COMMIT rolls back to the table\n // that was there before (old tokenizer, still queryable), never to no table at all.\n // IF EXISTS also makes a retry after such a crash a no-op instead of a raw SQLite error --\n // meta.features is only updated once rebuildContentTable (below) finishes, so a retry\n // re-enters this branch and repopulates from scratch either way.\n await withTransaction(conn, async () => {\n await conn.exec('DROP TABLE IF EXISTS content');\n await createContentTable(conn, tokenize);\n });\n tokenizeOnlyRebuild = true;\n } else if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features ?? '', wantFeatures)) {\n // First sight of a resolved weight identity: the model itself hasn't changed, so\n // adopt it into meta with no rebuild and no re-embed, mirroring the tokenize precedent.\n console.error(\"sense: recorded the embedding model's resolved identity; vectors are unaffected\");\n await setMeta(conn, 'features', wantFeatures);\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n }\n\n // Meta can lie after a crash between table creation and the signature write; the table's\n // own DDL cannot. A mismatch here rebuilds no matter what meta says. A tokenize-only rebuild\n // already recreated content with this tokenize above, so stored already matches and this\n // guard is skipped naturally rather than needing its own case.\n const stored = storedTokenize(db);\n if (stored !== null && stored !== tokenize) {\n console.error('sense: cache was built with a different content tokenizer; rebuilding the index');\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n\n await ensureSchema(conn, cfg, tokenize);\n\n let rebuildWarnings: string[] = [];\n if (tokenizeOnlyRebuild) {\n rebuildWarnings = await rebuildContentTable(conn, cfg, cfg.baseDir);\n await setMeta(conn, 'features', wantFeatures);\n }\n\n // 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.\n // Installed before reconcile() -- that call is the one that races a watcher's transaction.\n const recordedMaxMs = Number((await getMeta(conn, 'reconcile_max_ms')) ?? '0');\n db.exec(`PRAGMA busy_timeout = ${Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000)}`);\n\n const { parsed, warnings } = await reconcile(conn, cfg, cfg.baseDir);\n\n return { db, conn, cfg, dbPath, parsed, warnings: [...rebuildWarnings, ...warnings] };\n } catch (err) {\n if (!closed) db.close();\n throw err;\n }\n}\n\n// The sqlite store's open: connects synchronously (see connect() above), then wraps the\n// resulting connection in the async Store interface.\nexport async function openSqlite(cfg: ResolvedConfig): Promise<OpenResult> {\n const { db, conn, cfg: resolvedCfg, dbPath, parsed, warnings } = await connect(cfg);\n return { store: createStore(db, conn, resolvedCfg, resolvedCfg.baseDir), cfg: resolvedCfg, dbPath, parsed, warnings };\n}\n\nexport async function docCount(store: Store): Promise<number> {\n const stmt = await store.prepare('SELECT COUNT(*) AS n FROM frontmatter');\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// Deletes the cache directory, and only that. The rebuild is not this function's job: the next\n// open() reconciles, which is what running any command already does, so a verb that bundled the\n// two described the half that was not its own. Manual reset for a doubted cache; the schema and\n// config-signature mismatches below reset themselves.\nexport function clearCache(cfg: ResolvedConfig): void {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","clearCache","docCount","openSqlite","DEFAULT_TOKENIZE","resolveTokenize","db","cfg","configured","contentTokenize","undefined","literal","replace","exec","err","SenseError","message","storedTokenize","row","prepare","get","m","sql","match","createContentTable","conn","tokenize","ensureSchema","feature","activeFeatures","schema","getMeta","setMeta","featureSignature","FEATURES","connect","stateDir","dbPath","closed","version","features","wantFeatures","tokenizeOnlyRebuild","changedKeys","changed","stored","rebuildWarnings","recordedMaxMs","parsed","warnings","join","baseDir","STATE_DIR","mkdirSync","recursive","DatabaseSync","registerFunctions","createConnection","console","error","close","changedSignatureKeys","size","has","withTransaction","embedIdentityAdopted","signatureDiff","rebuildContentTable","Number","Math","min","max","reconcile","resolvedCfg","store","createStore","stmt","n","rmSync","force"],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;;;;;;;;;;;;QAgBpFA;eAAAA;;QAGAC;eAAAA;;QAwMGC;eAAAA;;QATMC;eAAAA;;QALAC;eAAAA;;;sBA5MY;wBACb;0BACQ;uBAEgC;wBAClC;wBACc;wBACR;6BACD;4BAEC;2BACyE;8BACxE;uBACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAErB,IAAMJ,cAAc;AAGpB,IAAMC,iBAAiB;AAmB9B,yFAAyF;AACzF,2FAA2F;AAC3F,4FAA4F;AAC5F,qCAAqC;AACrC,IAAMI,mBAAmB;AAEzB,wFAAwF;AACxF,6FAA6F;AAC7F,6FAA6F;AAC7F,6FAA6F;AAC7F,+DAA+D;AAC/D,SAASC,gBAAgBC,EAAgB,EAAEC,GAAW;IACpD,IAAMC,aAAaC,IAAAA,wBAAe,EAACF;IACnC,IAAIC,eAAeE,WAAW,OAAON;IACrC,IAAMO,UAAUH,WAAWI,OAAO,CAAC,MAAM;IACzC,IAAI;QACFN,GAAGO,IAAI,CAAC;QACRP,GAAGO,IAAI,CAAC,AAAC,4EAAmF,OAARF,SAAQ;QAC5FL,GAAGO,IAAI,CAAC;IACV,EAAE,OAAOC,KAAK;QACZ,MAAM,IAAIC,oBAAU,CAAC,kBAAkB,AAAC,qBAA2E,OAAvDP,YAAW,8CAAmE,OAAvB,AAACM,IAAcE,OAAO,EAAC;IAC5I;IACA,OAAOL;AACT;AAEA,uFAAuF;AACvF,2FAA2F;AAC3F,SAASM,eAAeX,EAAgB;IACtC,IAAMY,MAAMZ,GAAGa,OAAO,CAAC,wDAAwDC,GAAG;IAClF,IAAI,CAACF,KAAK,OAAO;IACjB,IAAMG,IAAIH,IAAII,GAAG,CAACC,KAAK,CAAC;IACxB,OAAOF,IAAIA,CAAC,CAAC,EAAE,GAAG;AACpB;AAEA,4FAA4F;AAC5F,2FAA2F;AAC3F,2FAA2F;AAC3F,qFAAqF;AACrF,yFAAyF;AACzF,SAAeG,mBAAmBC,IAAgB,EAAEC,QAAgB;;;;;oBAClE;;wBAAMD,KAAKZ,IAAI,CAAC,AAAC,6IAAqJ,OAATa,UAAS;;;oBAAtK;;;;;;IACF;;AAEA,2FAA2F;AAC3F,wFAAwF;AACxF,SAAeC,aAAaF,IAAgB,EAAElB,GAAW,EAAEmB,QAAgB;;YASpE,2BAAA,mBAAA,gBAAA,WAAA,OAAME;;;;oBARX;;wBAAMH,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACA,wFAAwF;oBACxF,4FAA4F;oBAC5F;;wBAAMW,mBAAmBC,MAAMC;;;oBAA/B;oBACA,6FAA6F;oBAC7F,qFAAqF;oBACrF;;wBAAMD,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACA;;wBAAMY,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACK,kCAAA,2BAAA;;;;;;;;;oBAAA,YAAiBgB,IAAAA,wBAAc,EAACtB;;;2BAAhC,6BAAA,QAAA;;;;oBAAMqB,UAAN;oBAAsC;;wBAAMA,QAAQE,MAAM,CAACL;;;oBAArB;;;oBAAtC;;;;;;;;;;;;oBAAA;oBAAA;;;;;;;6BAAA,6BAAA;4BAAA;;;4BAAA;kCAAA;;;;;;;oBACA;;wBAAMM,IAAAA,iBAAO,EAACN,MAAM;;;yBAArB,CAAA,AAAC,kBAA2C,IAAG,GAA/C;;;;oBAAkD;;wBAAMO,IAAAA,iBAAO,EAACP,MAAM,kBAAkBzB;;;oBAAtC;;;oBACjD;;wBAAM+B,IAAAA,iBAAO,EAACN,MAAM;;;yBAArB,CAAA,AAAC,kBAAqC,IAAG,GAAzC;;;;oBAA4C;;wBAAMO,IAAAA,iBAAO,EAACP,MAAM,YAAYQ,IAAAA,yBAAgB,EAAC1B,KAAK2B,kBAAQ;;;oBAA9D;;;;;;;;IAClD;;AAEA,SAAeC,QAAQ5B,GAAmB;;YAClC6B,UAEAC,QAEA/B,IAIFgC,QAqF4B,MA9ExBb,MAMAC,UAIAa,SACAC,UACAC,cACFC,qBAWIC,aAsBEC,SAaJC,QAWFC,iBAQEC,eAGuB,OAArBC,QAAQC,UAGTnC;;;;oBAnGHsB,WAAWc,IAAAA,cAAI,EAAC3C,IAAI4C,OAAO,EAAEC,kBAAS;oBAC5CC,IAAAA,iBAAS,EAACjB,UAAU;wBAAEkB,WAAW;oBAAK;oBAChCjB,SAASa,IAAAA,cAAI,EAACd,UAAUrC;oBAExBO,KAAK,IAAIiD,wBAAY,CAAClB;oBAC5B,8FAA8F;oBAC9F,6FAA6F;oBAC7F,8FAA8F;oBAC1FC,SAAS;;;;;;;;;oBAEXhC,GAAGO,IAAI,CAAC;oBACR,yFAAyF;oBACzF,uCAAuC;oBACvCP,GAAGO,IAAI,CAAC;oBACR2C,IAAAA,iCAAiB,EAAClD,IAAIG,IAAAA,wBAAe,EAACF,SAASG;oBACzCe,OAAOgC,IAAAA,8BAAgB,EAACnD;oBAE9BA,GAAGO,IAAI,CAAC;oBAER,2FAA2F;oBAC3F,8FAA8F;oBACxFa,WAAWrB,gBAAgBC,IAAIC;oBAIrB;;wBAAMwB,IAAAA,iBAAO,EAACN,MAAM;;;oBAA9Bc,UAAU;oBACC;;wBAAMR,IAAAA,iBAAO,EAACN,MAAM;;;oBAA/Be,WAAW;oBACXC,eAAeR,IAAAA,yBAAgB,EAAC1B,KAAK2B,kBAAQ;oBAC/CQ,sBAAsB;yBACtB,CAAA,AAACH,YAAY,QAAQA,YAAYvC,kBAAoBwC,aAAa,QAAQA,aAAaC,YAAY,GAAnG;;;;oBACF,uFAAuF;oBACvF,wFAAwF;oBACxF,IAAIF,YAAY,QAAQA,YAAYvC,gBAAgB;wBAClD0D,QAAQC,KAAK,CAAC;wBACdrB,SAAS;wBACThC,GAAGsD,KAAK;wBACR3D,WAAWM;wBACX;;4BAAO4B,QAAQ5B;;oBACjB;oBACMoC,cAAckB,IAAAA,iCAAoB,EAACrB,qBAAAA,sBAAAA,WAAY,IAAIC;yBAIrDE,CAAAA,YAAYmB,IAAI,KAAK,KAAKnB,YAAYoB,GAAG,CAAC,WAAU,GAApDpB;;;;oBACFe,QAAQC,KAAK,CAAC;oBACd,sFAAsF;oBACtF,oFAAoF;oBACpF,2FAA2F;oBAC3F,sFAAsF;oBACtF,iEAAiE;oBACjE;;wBAAMK,IAAAA,8BAAe,EAACvC,MAAM;;;;;4CAC1B;;gDAAMA,KAAKZ,IAAI,CAAC;;;4CAAhB;4CACA;;gDAAMW,mBAAmBC,MAAMC;;;4CAA/B;;;;;;4BACF;;;;oBAHA;oBAIAgB,sBAAsB;;;;;;yBACbC,CAAAA,YAAYmB,IAAI,KAAK,KAAKnB,YAAYoB,GAAG,CAAC,YAAYE,IAAAA,iCAAoB,EAACzB,qBAAAA,sBAAAA,WAAY,IAAIC,aAAY,GAAvGE;;;;oBACT,iFAAiF;oBACjF,wFAAwF;oBACxFe,QAAQC,KAAK,CAAC;oBACd;;wBAAM3B,IAAAA,iBAAO,EAACP,MAAM,YAAYgB;;;oBAAhC;;;;;;oBAEMG,UAAUsB,IAAAA,0BAAa,EAAC1B,qBAAAA,sBAAAA,WAAY,IAAIC;oBAC9CiB,QAAQC,KAAK,CAAC,AAAC,yBAAgC,OAARf,SAAQ;oBAC/CN,SAAS;oBACThC,GAAGsD,KAAK;oBACR3D,WAAWM;oBACX;;wBAAO4B,QAAQ5B;;;oBAInB,yFAAyF;oBACzF,6FAA6F;oBAC7F,yFAAyF;oBACzF,+DAA+D;oBACzDsC,SAAS5B,eAAeX;oBAC9B,IAAIuC,WAAW,QAAQA,WAAWnB,UAAU;wBAC1CgC,QAAQC,KAAK,CAAC;wBACdrB,SAAS;wBACThC,GAAGsD,KAAK;wBACR3D,WAAWM;wBACX;;4BAAO4B,QAAQ5B;;oBACjB;oBAEA;;wBAAMoB,aAAaF,MAAMlB,KAAKmB;;;oBAA9B;oBAEIoB;yBACAJ,qBAAAA;;;;oBACgB;;wBAAMyB,IAAAA,gCAAmB,EAAC1C,MAAMlB,KAAKA,IAAI4C,OAAO;;;oBAAlEL,kBAAkB;oBAClB;;wBAAMd,IAAAA,iBAAO,EAACP,MAAM,YAAYgB;;;oBAAhC;;;oBAK4B;;wBAAMV,IAAAA,iBAAO,EAACN,MAAM;;;oBAA5CsB,gBAAgBqB;yBAAQ,OAAA,2BAAA,kBAAA,OAA4C;;oBAC1E9D,GAAGO,IAAI,CAAC,AAAC,yBAA8E,OAAtDwD,KAAKC,GAAG,CAACD,KAAKE,GAAG,CAAC,OAAO,IAAIxB,gBAAgB;oBAEjD;;wBAAMyB,IAAAA,sBAAS,EAAC/C,MAAMlB,KAAKA,IAAI4C,OAAO;;;oBAAtC,QAAA,eAArBH,SAAqB,MAArBA,QAAQC,WAAa,MAAbA;oBAEhB;;wBAAO;4BAAE3C,IAAAA;4BAAImB,MAAAA;4BAAMlB,KAAAA;4BAAK8B,QAAAA;4BAAQW,QAAAA;4BAAQC,UAAU,AAAC,qBAAGH,wBAAiB,qBAAGG;wBAAU;;;oBAC7EnC;oBACP,IAAI,CAACwB,QAAQhC,GAAGsD,KAAK;oBACrB,MAAM9C;;;;;;;IAEV;;AAIO,SAAeX,WAAWI,GAAmB;;YACe,MAAzDD,IAAImB,MAAWgD,aAAapC,QAAQW,QAAQC;;;;oBAAa;;wBAAMd,QAAQ5B;;;oBAAd,OAAA,eAAzDD,KAAyD,KAAzDA,IAAImB,OAAqD,KAArDA,MAAWgD,cAA0C,KAA/ClE,KAAkB8B,SAA6B,KAA7BA,QAAQW,SAAqB,KAArBA,QAAQC,WAAa,KAAbA;oBACpD;;wBAAO;4BAAEyB,OAAOC,IAAAA,oBAAW,EAACrE,IAAImB,MAAMgD,aAAaA,YAAYtB,OAAO;4BAAG5C,KAAKkE;4BAAapC,QAAAA;4BAAQW,QAAAA;4BAAQC,UAAAA;wBAAS;;;;IACtH;;AAEO,SAAe/C,SAASwE,KAAY;;YACnCE;;;;oBAAO;;wBAAMF,MAAMvD,OAAO,CAAC;;;oBAA3ByD,OAAO;oBACJ;;wBAAMA,KAAKxD,GAAG;;;oBAAvB;;wBAAS,cAAoCyD,CAAC;;;;IAChD;;AAMO,SAAS5E,WAAWM,GAAmB;IAC5CuE,IAAAA,cAAM,EAAC5B,IAAAA,cAAI,EAAC3C,IAAI4C,OAAO,EAAEC,kBAAS,GAAG;QAAEE,WAAW;QAAMyB,OAAO;IAAK;AACtE"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { contentTokenize, featureSignature, STATE_DIR } from '../../config/index.ts';\nimport { rekeyChunkText } from '../../embed/handoff.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport { clearCache } from '../cache.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, Store } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { changedSignatureKeys, embedIdentityAdopted, rebuildContentTable, reconcile, signatureDiff } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '18';\n\ninterface ConnectResult {\n db: DatabaseSync;\n conn: Connection;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// unicode61 splits on spaces, so a language written without them indexes a whole run as one\n// token and word search finds nothing; `content.tokenize` is how such a tree picks trigram.\nconst DEFAULT_TOKENIZE = 'porter unicode61';\n\n// FTS5 takes its tokenizer as a DDL string literal where nothing can bind, so the configured\n// value is concatenated; probing a throwaway table first is both the safety and the validation.\nfunction resolveTokenize(db: DatabaseSync, cfg: Config): string {\n const configured = contentTokenize(cfg);\n if (configured === undefined) return DEFAULT_TOKENIZE;\n const literal = configured.replace(/'/g, \"''\");\n try {\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n db.exec(`CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '${literal}')`);\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n } catch (err) {\n throw new SenseError('CONFIG_INVALID', `content.tokenize \"${configured}\" is not a tokenizer this SQLite accepts (${(err as Error).message}); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options`);\n }\n return literal;\n}\n\n// The tokenizer the content table was actually built with, from its own DDL -- the one\n// record that cannot desynchronize from the table. NULL when the table does not exist yet.\nfunction storedTokenize(db: DatabaseSync): string | null {\n const row = db.prepare(`SELECT sql FROM sqlite_master WHERE name = 'content'`).get() as { sql: string } | undefined;\n if (!row) return null;\n const m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);\n return m ? m[1] : null;\n}\n\n// The `_seg` sidecars are appended after path, never inserted: bm25() and snippet(content, 2)\n// are documented against the first three columns. Each holds its field's exploded unspaced runs.\nasync function createContentTable(conn: Connection, tokenize: string): Promise<void> {\n await conn.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${tokenize}')`);\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nasync function ensureSchema(conn: Connection, cfg: Config, tokenize: string): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n // IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL\n // against the resolved tokenizer before this runs, so a stale table is already gone by now.\n await createContentTable(conn, tokenize);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n await conn.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) await feature.schema(conn);\n if ((await getMeta(conn, 'schema_version')) === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);\n if ((await getMeta(conn, 'features')) === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));\n}\n\nasync function connect(cfg: ResolvedConfig): Promise<ConnectResult> {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n // A throw below must release this handle, or on Windows the leaked WAL db is undeletable and\n // scratch cleanup fails with EPERM/EBUSY. `closed` keeps the catch from double-closing.\n let closed = false;\n try {\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile (~5s for 500 files at 26k notes). A query\n // that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db, contentTokenize(cfg) === undefined);\n const conn = createConnection(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd\n // tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.\n const tokenize = resolveTokenize(db, cfg);\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const wantFeatures = featureSignature(cfg, FEATURES);\n let tokenizeOnlyRebuild = false;\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Indexing derives from presets, so a config edit rebuilding the cache must say so and\n // name what changed -- silent rebuilds make derived indexing look like a hang or a bug.\n if (version !== null && version !== SCHEMA_VERSION) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n // Only the tokenizer moved, and frontmatter, links, sections and embeddings are file-derived\n // and tokenizer-independent. Any other signature change takes the full clear/reopen below.\n if (changedKeys.size === 1 && changedKeys.has('tokenize')) {\n console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');\n // One transaction, so a crash before COMMIT rolls back to the old tokenizer's table rather\n // than to no table. IF EXISTS makes a retry after such a crash a no-op, not a raw error.\n await withTransaction(conn, async () => {\n await conn.exec('DROP TABLE IF EXISTS content');\n await createContentTable(conn, tokenize);\n });\n tokenizeOnlyRebuild = true;\n } else if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features ?? '', wantFeatures)) {\n // First sight of a resolved weight identity: the model itself hasn't changed, so\n // adopt it into meta with no rebuild and no re-embed, mirroring the tokenize precedent.\n console.error(\"sense: recorded the embedding model's resolved identity; vectors are unaffected\");\n await setMeta(conn, 'features', wantFeatures);\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n }\n\n // Meta can lie after a crash between table creation and the signature write; the table's own\n // DDL cannot, so a mismatch here rebuilds whatever meta says.\n const stored = storedTokenize(db);\n if (stored !== null && stored !== tokenize) {\n console.error('sense: cache was built with a different content tokenizer; rebuilding the index');\n closed = true;\n db.close();\n clearCache(cfg);\n return connect(cfg);\n }\n\n await ensureSchema(conn, cfg, tokenize);\n\n let rebuildWarnings: string[] = [];\n if (tokenizeOnlyRebuild) {\n rebuildWarnings = await rebuildContentTable(conn, cfg, cfg.baseDir);\n await setMeta(conn, 'features', wantFeatures);\n }\n\n // 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.\n // Installed before reconcile() -- that call is the one that races a watcher's transaction.\n const recordedMaxMs = Number((await getMeta(conn, 'reconcile_max_ms')) ?? '0');\n db.exec(`PRAGMA busy_timeout = ${Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000)}`);\n\n const { parsed, warnings } = await reconcile(conn, cfg, cfg.baseDir);\n\n return { db, conn, cfg, dbPath, parsed, warnings: [...rebuildWarnings, ...warnings] };\n } catch (err) {\n if (!closed) db.close();\n throw err;\n }\n}\n\n// The sqlite store's open: connects synchronously (see connect() above), then wraps the\n// resulting connection in the async Store interface.\nexport async function openSqlite(cfg: ResolvedConfig): Promise<OpenResult> {\n const { db, conn, cfg: resolvedCfg, dbPath, parsed, warnings } = await connect(cfg);\n const store = createStore(db, conn, resolvedCfg, resolvedCfg.baseDir);\n // reconcile ran before this object existed, so its chunk text is keyed by the connection.\n rekeyChunkText(conn, store);\n return { store: store, cfg: resolvedCfg, dbPath, parsed, warnings };\n}\n\nexport async function docCount(store: Store): Promise<number> {\n const stmt = await store.prepare('SELECT COUNT(*) AS n FROM frontmatter');\n return ((await stmt.get()) as { n: number }).n;\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","docCount","openSqlite","DEFAULT_TOKENIZE","resolveTokenize","db","cfg","configured","contentTokenize","undefined","literal","replace","exec","err","SenseError","message","storedTokenize","row","prepare","get","m","sql","match","createContentTable","conn","tokenize","ensureSchema","feature","activeFeatures","schema","getMeta","setMeta","featureSignature","FEATURES","connect","stateDir","dbPath","closed","version","features","wantFeatures","tokenizeOnlyRebuild","changedKeys","changed","stored","rebuildWarnings","recordedMaxMs","parsed","warnings","join","baseDir","STATE_DIR","mkdirSync","recursive","DatabaseSync","registerFunctions","createConnection","console","error","close","clearCache","changedSignatureKeys","size","has","withTransaction","embedIdentityAdopted","signatureDiff","rebuildContentTable","Number","Math","min","max","reconcile","resolvedCfg","store","createStore","rekeyChunkText","stmt","n"],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;;;;;;;;;;;;QAkBpFA;eAAAA;;QAGAC;eAAAA;;QAmLSC;eAAAA;;QARAC;eAAAA;;;sBA/LI;wBACL;0BACQ;uBAEgC;yBAC9B;wBACJ;wBACc;uBACd;wBACM;6BACD;4BAEC;2BACyE;8BACxE;uBACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAErB,IAAMH,cAAc;AAGpB,IAAMC,iBAAiB;AAmB9B,4FAA4F;AAC5F,4FAA4F;AAC5F,IAAMG,mBAAmB;AAEzB,6FAA6F;AAC7F,gGAAgG;AAChG,SAASC,gBAAgBC,EAAgB,EAAEC,GAAW;IACpD,IAAMC,aAAaC,IAAAA,wBAAe,EAACF;IACnC,IAAIC,eAAeE,WAAW,OAAON;IACrC,IAAMO,UAAUH,WAAWI,OAAO,CAAC,MAAM;IACzC,IAAI;QACFN,GAAGO,IAAI,CAAC;QACRP,GAAGO,IAAI,CAAC,AAAC,4EAAmF,OAARF,SAAQ;QAC5FL,GAAGO,IAAI,CAAC;IACV,EAAE,OAAOC,KAAK;QACZ,MAAM,IAAIC,oBAAU,CAAC,kBAAkB,AAAC,qBAA2E,OAAvDP,YAAW,8CAAmE,OAAvB,AAACM,IAAcE,OAAO,EAAC;IAC5I;IACA,OAAOL;AACT;AAEA,uFAAuF;AACvF,2FAA2F;AAC3F,SAASM,eAAeX,EAAgB;IACtC,IAAMY,MAAMZ,GAAGa,OAAO,CAAC,wDAAwDC,GAAG;IAClF,IAAI,CAACF,KAAK,OAAO;IACjB,IAAMG,IAAIH,IAAII,GAAG,CAACC,KAAK,CAAC;IACxB,OAAOF,IAAIA,CAAC,CAAC,EAAE,GAAG;AACpB;AAEA,8FAA8F;AAC9F,iGAAiG;AACjG,SAAeG,mBAAmBC,IAAgB,EAAEC,QAAgB;;;;;oBAClE;;wBAAMD,KAAKZ,IAAI,CAAC,AAAC,6IAAqJ,OAATa,UAAS;;;oBAAtK;;;;;;IACF;;AAEA,2FAA2F;AAC3F,wFAAwF;AACxF,SAAeC,aAAaF,IAAgB,EAAElB,GAAW,EAAEmB,QAAgB;;YASpE,2BAAA,mBAAA,gBAAA,WAAA,OAAME;;;;oBARX;;wBAAMH,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACA,wFAAwF;oBACxF,4FAA4F;oBAC5F;;wBAAMW,mBAAmBC,MAAMC;;;oBAA/B;oBACA,6FAA6F;oBAC7F,qFAAqF;oBACrF;;wBAAMD,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACA;;wBAAMY,KAAKZ,IAAI,CAAC;;;oBAAhB;oBACK,kCAAA,2BAAA;;;;;;;;;oBAAA,YAAiBgB,IAAAA,wBAAc,EAACtB;;;2BAAhC,6BAAA,QAAA;;;;oBAAMqB,UAAN;oBAAsC;;wBAAMA,QAAQE,MAAM,CAACL;;;oBAArB;;;oBAAtC;;;;;;;;;;;;oBAAA;oBAAA;;;;;;;6BAAA,6BAAA;4BAAA;;;4BAAA;kCAAA;;;;;;;oBACA;;wBAAMM,IAAAA,iBAAO,EAACN,MAAM;;;yBAArB,CAAA,AAAC,kBAA2C,IAAG,GAA/C;;;;oBAAkD;;wBAAMO,IAAAA,iBAAO,EAACP,MAAM,kBAAkBxB;;;oBAAtC;;;oBACjD;;wBAAM8B,IAAAA,iBAAO,EAACN,MAAM;;;yBAArB,CAAA,AAAC,kBAAqC,IAAG,GAAzC;;;;oBAA4C;;wBAAMO,IAAAA,iBAAO,EAACP,MAAM,YAAYQ,IAAAA,yBAAgB,EAAC1B,KAAK2B,kBAAQ;;;oBAA9D;;;;;;;;IAClD;;AAEA,SAAeC,QAAQ5B,GAAmB;;YAClC6B,UAEAC,QAEA/B,IAGFgC,QA+E4B,MAxExBb,MAMAC,UAIAa,SACAC,UACAC,cACFC,qBAWIC,aAkBEC,SAWJC,QAWFC,iBAQEC,eAGuB,OAArBC,QAAQC,UAGTnC;;;;oBA5FHsB,WAAWc,IAAAA,cAAI,EAAC3C,IAAI4C,OAAO,EAAEC,kBAAS;oBAC5CC,IAAAA,iBAAS,EAACjB,UAAU;wBAAEkB,WAAW;oBAAK;oBAChCjB,SAASa,IAAAA,cAAI,EAACd,UAAUpC;oBAExBM,KAAK,IAAIiD,wBAAY,CAAClB;oBAC5B,6FAA6F;oBAC7F,wFAAwF;oBACpFC,SAAS;;;;;;;;;oBAEXhC,GAAGO,IAAI,CAAC;oBACR,yFAAyF;oBACzF,uCAAuC;oBACvCP,GAAGO,IAAI,CAAC;oBACR2C,IAAAA,iCAAiB,EAAClD,IAAIG,IAAAA,wBAAe,EAACF,SAASG;oBACzCe,OAAOgC,IAAAA,8BAAgB,EAACnD;oBAE9BA,GAAGO,IAAI,CAAC;oBAER,2FAA2F;oBAC3F,8FAA8F;oBACxFa,WAAWrB,gBAAgBC,IAAIC;oBAIrB;;wBAAMwB,IAAAA,iBAAO,EAACN,MAAM;;;oBAA9Bc,UAAU;oBACC;;wBAAMR,IAAAA,iBAAO,EAACN,MAAM;;;oBAA/Be,WAAW;oBACXC,eAAeR,IAAAA,yBAAgB,EAAC1B,KAAK2B,kBAAQ;oBAC/CQ,sBAAsB;yBACtB,CAAA,AAACH,YAAY,QAAQA,YAAYtC,kBAAoBuC,aAAa,QAAQA,aAAaC,YAAY,GAAnG;;;;oBACF,uFAAuF;oBACvF,wFAAwF;oBACxF,IAAIF,YAAY,QAAQA,YAAYtC,gBAAgB;wBAClDyD,QAAQC,KAAK,CAAC;wBACdrB,SAAS;wBACThC,GAAGsD,KAAK;wBACRC,IAAAA,mBAAU,EAACtD;wBACX;;4BAAO4B,QAAQ5B;;oBACjB;oBACMoC,cAAcmB,IAAAA,iCAAoB,EAACtB,qBAAAA,sBAAAA,WAAY,IAAIC;yBAGrDE,CAAAA,YAAYoB,IAAI,KAAK,KAAKpB,YAAYqB,GAAG,CAAC,WAAU,GAApDrB;;;;oBACFe,QAAQC,KAAK,CAAC;oBACd,2FAA2F;oBAC3F,yFAAyF;oBACzF;;wBAAMM,IAAAA,8BAAe,EAACxC,MAAM;;;;;4CAC1B;;gDAAMA,KAAKZ,IAAI,CAAC;;;4CAAhB;4CACA;;gDAAMW,mBAAmBC,MAAMC;;;4CAA/B;;;;;;4BACF;;;;oBAHA;oBAIAgB,sBAAsB;;;;;;yBACbC,CAAAA,YAAYoB,IAAI,KAAK,KAAKpB,YAAYqB,GAAG,CAAC,YAAYE,IAAAA,iCAAoB,EAAC1B,qBAAAA,sBAAAA,WAAY,IAAIC,aAAY,GAAvGE;;;;oBACT,iFAAiF;oBACjF,wFAAwF;oBACxFe,QAAQC,KAAK,CAAC;oBACd;;wBAAM3B,IAAAA,iBAAO,EAACP,MAAM,YAAYgB;;;oBAAhC;;;;;;oBAEMG,UAAUuB,IAAAA,0BAAa,EAAC3B,qBAAAA,sBAAAA,WAAY,IAAIC;oBAC9CiB,QAAQC,KAAK,CAAC,AAAC,yBAAgC,OAARf,SAAQ;oBAC/CN,SAAS;oBACThC,GAAGsD,KAAK;oBACRC,IAAAA,mBAAU,EAACtD;oBACX;;wBAAO4B,QAAQ5B;;;oBAInB,6FAA6F;oBAC7F,8DAA8D;oBACxDsC,SAAS5B,eAAeX;oBAC9B,IAAIuC,WAAW,QAAQA,WAAWnB,UAAU;wBAC1CgC,QAAQC,KAAK,CAAC;wBACdrB,SAAS;wBACThC,GAAGsD,KAAK;wBACRC,IAAAA,mBAAU,EAACtD;wBACX;;4BAAO4B,QAAQ5B;;oBACjB;oBAEA;;wBAAMoB,aAAaF,MAAMlB,KAAKmB;;;oBAA9B;oBAEIoB;yBACAJ,qBAAAA;;;;oBACgB;;wBAAM0B,IAAAA,gCAAmB,EAAC3C,MAAMlB,KAAKA,IAAI4C,OAAO;;;oBAAlEL,kBAAkB;oBAClB;;wBAAMd,IAAAA,iBAAO,EAACP,MAAM,YAAYgB;;;oBAAhC;;;oBAK4B;;wBAAMV,IAAAA,iBAAO,EAACN,MAAM;;;oBAA5CsB,gBAAgBsB;yBAAQ,OAAA,2BAAA,kBAAA,OAA4C;;oBAC1E/D,GAAGO,IAAI,CAAC,AAAC,yBAA8E,OAAtDyD,KAAKC,GAAG,CAACD,KAAKE,GAAG,CAAC,OAAO,IAAIzB,gBAAgB;oBAEjD;;wBAAM0B,IAAAA,sBAAS,EAAChD,MAAMlB,KAAKA,IAAI4C,OAAO;;;oBAAtC,QAAA,eAArBH,SAAqB,MAArBA,QAAQC,WAAa,MAAbA;oBAEhB;;wBAAO;4BAAE3C,IAAAA;4BAAImB,MAAAA;4BAAMlB,KAAAA;4BAAK8B,QAAAA;4BAAQW,QAAAA;4BAAQC,UAAU,AAAC,qBAAGH,wBAAiB,qBAAGG;wBAAU;;;oBAC7EnC;oBACP,IAAI,CAACwB,QAAQhC,GAAGsD,KAAK;oBACrB,MAAM9C;;;;;;;IAEV;;AAIO,SAAeX,WAAWI,GAAmB;;YACe,MAAzDD,IAAImB,MAAWiD,aAAarC,QAAQW,QAAQC,UAC9C0B;;;;oBAD2D;;wBAAMxC,QAAQ5B;;;oBAAd,OAAA,eAAzDD,KAAyD,KAAzDA,IAAImB,OAAqD,KAArDA,MAAWiD,cAA0C,KAA/CnE,KAAkB8B,SAA6B,KAA7BA,QAAQW,SAAqB,KAArBA,QAAQC,WAAa,KAAbA;oBAC9C0B,QAAQC,IAAAA,oBAAW,EAACtE,IAAImB,MAAMiD,aAAaA,YAAYvB,OAAO;oBACpE,0FAA0F;oBAC1F0B,IAAAA,yBAAc,EAACpD,MAAMkD;oBACrB;;wBAAO;4BAAEA,OAAOA;4BAAOpE,KAAKmE;4BAAarC,QAAAA;4BAAQW,QAAAA;4BAAQC,UAAAA;wBAAS;;;;IACpE;;AAEO,SAAe/C,SAASyE,KAAY;;YACnCG;;;;oBAAO;;wBAAMH,MAAMxD,OAAO,CAAC;;;oBAA3B2D,OAAO;oBACJ;;wBAAMA,KAAK1D,GAAG;;;oBAAvB;;wBAAS,cAAoC2D,CAAC;;;;IAChD"}
@@ -473,9 +473,8 @@ function reconcile(conn, cfg, baseDir) {
473
473
  3,
474
474
  12
475
475
  ];
476
- // FTS5 has no upsert, so delete-before-insert into `content`, coupled to the frontmatter
477
- // rowid (indexed via its PRIMARY KEY) rather than the UNINDEXED `path` column, which a
478
- // per-row DELETE would otherwise scan the whole table to find.
476
+ // FTS5 has no upsert, so delete-before-insert, coupled to the frontmatter rowid (indexed via
477
+ // its PRIMARY KEY) rather than the UNINDEXED `path` column, which a per-row DELETE would scan to find.
479
478
  return [
480
479
  4,
481
480
  conn.runBatch('DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE "path" = ?)', reparsedExisting.map(function(p) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } from '../../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../../features/types.ts';\nimport { progress } from '../../output/progress.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { listFiles, parseFile, RESERVED_COLUMNS } from '../../scan/index.ts';\nimport { reparseFiles } from '../../scan/reparse.ts';\nimport { segmentField } from '../../text/segment.ts';\nimport { getColumns, getMeta, quoteIdent, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection } from '../types.ts';\n\n// Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last\n// computed value on every touch, not just the reconciles that recompute it.\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\n// SQLite's compile-time SQLITE_MAX_COLUMN, default 2000 (https://www.sqlite.org/limits.html).\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Shared by reconcile() and the tokenize-only rebuild in open(), so both prepare the same\n// literal instead of two copies drifting apart.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)`;\n\n// A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.\n// Assumes the frontmatter row for doc.relPath already exists (rowid subquery).\nfunction contentRow(doc: ParsedDoc, segmenting: boolean): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmenting ? segmentField(doc.search.title) : '', segmenting ? segmentField(doc.search.summary) : '', segmenting ? segmentField(doc.search.text) : ''];\n}\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short\n // reconciles stay silent (progress() has a threshold).\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick);\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n // Fence before ALTERing: SQLite's own failure past this point is a raw\n // \"too many columns on sqlite_altertab_frontmatter\" with no indication of the boundary or the levers.\n if (allColumns.length > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError(\n 'COLUMN_LIMIT',\n `frontmatter would need ${allColumns.length} columns, crossing SQLite's compile-time SQLITE_MAX_COLUMN limit (default ${MAX_FRONTMATTER_COLUMNS}; see https://www.sqlite.org/limits.html). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`\n );\n }\n // Columns the frontmatter upsert actually writes: core + parsed frontmatter keys, never a\n // feature-owned reserved column (see CORE_FRONTMATTER_COLUMNS above).\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n // ON CONFLICT UPDATE (not OR REPLACE) keeps the row's rowid stable across reparses --\n // content rows are coupled to that rowid below.\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n\n const txStart = Date.now();\n await withTransaction(conn, async () => {\n for (const col of newColumns) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n\n // content's rowid lookup depends on the frontmatter row it's coupled to, so every content\n // delete/insert below must run after that row exists (vanished paths still have their\n // frontmatter row at this point) and before it is removed (vanished frontmatter delete\n // comes last).\n if (vanished.length > 0) {\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n vanished.map((p) => [p])\n );\n }\n if (reparsedExisting.length > 0) {\n // FTS5 has no upsert, so delete-before-insert into `content`, coupled to the frontmatter\n // rowid (indexed via its PRIMARY KEY) rather than the UNINDEXED `path` column, which a\n // per-row DELETE would otherwise scan the whole table to find.\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n reparsedExisting.map((p) => [p])\n );\n }\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n\n // A non-default tokenizer means the tree has chosen its own scheme; a phrase query over\n // grapheme runs would be nonsense against trigram, so the sidecars stay empty.\n const segmenting = contentTokenize(cfg) === undefined;\n await conn.runBatch(\n INSERT_CONTENT_SQL,\n parsedDocs.map((doc) => contentRow(doc, segmenting))\n );\n }\n\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n const presetTouched = [...vanished, ...reparsedExisting];\n if (presetTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n presetTouched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n const removedPaths = [...vanished, ...reparsedExisting];\n if (removedPaths.length > 0) for (const feature of features) await feature.remove?.(conn, removedPaths, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n });\n\n // Reconcile's own write-transaction duration, for open()'s derived busy_timeout (F):\n // keep the observed max so a big watcher reconcile's lock hold is what the next open bounds its wait against.\n const durationMs = Date.now() - txStart;\n const prevRaw = await getMeta(conn, 'reconcile_max_ms');\n // -1, not 0, so a genuinely 0ms first reconcile (sub-millisecond, common on a tiny tree)\n // still gets recorded instead of losing to the \"nothing recorded yet\" default.\n const prevMax = prevRaw === null ? -1 : Number(prevRaw);\n if (durationMs > prevMax) await setMeta(conn, 'reconcile_max_ms', String(durationMs));\n\n return { parsed: parsedDocs.length, warnings };\n}\n\n// Segment keys that moved between two feature signatures (see config.featureSignature's\n// format: global features, embed provider, tokenize, then one segment per preset).\nexport function changedSignatureKeys(before: string, after: string): Set<string> {\n const keyOf = (part: string) => (part.startsWith('preset:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0]);\n const parse = (sig: string) => new Map(sig.split('|').map((part) => [keyOf(part), part]));\n const a = parse(before);\n const b = parse(after);\n const changed = new Set<string>();\n for (const [key, val] of b) if (a.get(key) !== val) changed.add(key);\n for (const key of a.keys()) if (!b.has(key)) changed.add(key);\n return changed;\n}\n\n// Whether the embed segment only gained its resolved weight identity: same provider and\n// model, no identity recorded before, one now -- adopted into meta without a rebuild.\nexport function embedIdentityAdopted(before: string, after: string): boolean {\n const embedPart = (sig: string) => sig.split('|').find((p) => p.startsWith('embed:'));\n const b = embedPart(before);\n const a = embedPart(after);\n if (b === undefined || a === undefined) return false;\n const at = a.indexOf('@');\n return b.indexOf('@') === -1 && at !== -1 && a.slice(0, at) === b;\n}\n\n// Names what moved, for the rebuild notice.\nexport function signatureDiff(before: string, after: string): string {\n const changed = changedSignatureKeys(before, after);\n const label = (key: string) => (key === 'embed' ? 'embed settings' : key === 'tokenize' ? 'content tokenizer' : key.startsWith('preset:') ? `preset \"${key.slice(7)}\"` : 'features');\n return changed.size === 0 ? 'features' : [...changed].map(label).join(', ');\n}\n\n// The tokenize-only rebuild in open(): content is dropped and repopulated from files already\n// listed in frontmatter, which itself is untouched. Frontmatter, links, sections, and\n// embeddings are file-derived and tokenizer-independent, so they survive. No feature extractors\n// run here -- doc.search (title/summary/text) is all content population needs. Returns\n// parseFile's per-file warnings (e.g. a bad date) so open() can surface them -- mtimes are\n// untouched, so reconcile() never reparses these files and would otherwise never emit them again.\nexport async function rebuildContentTable(conn: Connection, cfg: Config, baseDir: string): Promise<string[]> {\n const stmt = await conn.prepare('SELECT \"path\" FROM frontmatter');\n const known = new Set(((await stmt.all()) as Array<{ path: string }>).map((r) => r.path));\n const files = listFiles(cfg, baseDir).filter((f) => known.has(f.relPath));\n const segmenting = contentTokenize(cfg) === undefined;\n const warnings: string[] = [];\n const rows: unknown[][] = [];\n for (const file of files) {\n const { doc, warnings: fileWarnings } = parseFile(file);\n warnings.push(...fileWarnings);\n rows.push(contentRow(doc, segmenting));\n }\n await withTransaction(conn, async () => {\n if (rows.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, rows);\n });\n return warnings;\n}\n"],"names":["changedSignatureKeys","embedIdentityAdopted","rebuildContentTable","reconcile","signatureDiff","CORE_FRONTMATTER_COLUMNS","Set","MAX_FRONTMATTER_COLUMNS","INSERT_CONTENT_SQL","contentRow","doc","segmenting","relPath","search","title","summary","text","segmentField","conn","cfg","baseDir","files","currentSet","existingStmt","existingRows","existing","vanished","toReparse","features","seenColumns","report","parsedDocs","warnings","newColumns","col","allColumns","writableColumns","insertSql","added","delta","addedSet","reparsedExisting","txStart","durationMs","prevRaw","prevMax","listFiles","map","f","prepare","all","Map","r","path","filter","has","row","get","_mtime","mtimeMs","_size","size","length","parsed","activeFeatures","getColumns","progress","reparseFiles","tick","docs","finish","add","SenseError","c","RESERVED_COLUMNS","quoteIdent","join","reparsed","d","p","Date","now","withTransaction","feature","rows","presetTouched","presetRows","presetName","removedPaths","exec","runBatch","ctimeMs","parseError","data","contentTokenize","undefined","presets","push","remove","docsForFeature","extracted","name","store","afterReconcile","getMeta","Number","setMeta","String","before","after","keyOf","part","startsWith","split","slice","parse","sig","a","b","changed","key","val","keys","embedPart","find","at","indexOf","label","stmt","known","file","parseFile","fileWarnings"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } from '../../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../../features/types.ts';\nimport { progress } from '../../output/progress.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { listFiles, parseFile, RESERVED_COLUMNS } from '../../scan/index.ts';\nimport { reparseFiles } from '../../scan/reparse.ts';\nimport { segmentField } from '../../text/segment.ts';\nimport { getColumns, getMeta, quoteIdent, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection } from '../types.ts';\n\n// Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last\n// computed value on every touch, not just the reconciles that recompute it.\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\n// SQLite's compile-time SQLITE_MAX_COLUMN, default 2000 (https://www.sqlite.org/limits.html).\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Shared by reconcile() and the tokenize-only rebuild in open(), so both prepare the same\n// literal instead of two copies drifting apart.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)`;\n\n// A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.\n// Assumes the frontmatter row for doc.relPath already exists (rowid subquery).\nfunction contentRow(doc: ParsedDoc, segmenting: boolean): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmenting ? segmentField(doc.search.title) : '', segmenting ? segmentField(doc.search.summary) : '', segmenting ? segmentField(doc.search.text) : ''];\n}\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short\n // reconciles stay silent (progress() has a threshold).\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick);\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n // Fence before ALTERing: SQLite's own failure past this point is a raw\n // \"too many columns on sqlite_altertab_frontmatter\" with no indication of the boundary or the levers.\n if (allColumns.length > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError(\n 'COLUMN_LIMIT',\n `frontmatter would need ${allColumns.length} columns, crossing SQLite's compile-time SQLITE_MAX_COLUMN limit (default ${MAX_FRONTMATTER_COLUMNS}; see https://www.sqlite.org/limits.html). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`\n );\n }\n // Columns the frontmatter upsert actually writes: core + parsed frontmatter keys, never a\n // feature-owned reserved column (see CORE_FRONTMATTER_COLUMNS above).\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n // ON CONFLICT UPDATE (not OR REPLACE) keeps the row's rowid stable across reparses --\n // content rows are coupled to that rowid below.\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n\n const txStart = Date.now();\n await withTransaction(conn, async () => {\n for (const col of newColumns) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n\n // content's rowid lookup depends on its coupled frontmatter row, so the content\n // deletes/inserts run while every path still has one -- the vanished frontmatter delete comes last.\n if (vanished.length > 0) {\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n vanished.map((p) => [p])\n );\n }\n if (reparsedExisting.length > 0) {\n // FTS5 has no upsert, so delete-before-insert, coupled to the frontmatter rowid (indexed via\n // its PRIMARY KEY) rather than the UNINDEXED `path` column, which a per-row DELETE would scan to find.\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n reparsedExisting.map((p) => [p])\n );\n }\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n\n // A non-default tokenizer means the tree has chosen its own scheme; a phrase query over\n // grapheme runs would be nonsense against trigram, so the sidecars stay empty.\n const segmenting = contentTokenize(cfg) === undefined;\n await conn.runBatch(\n INSERT_CONTENT_SQL,\n parsedDocs.map((doc) => contentRow(doc, segmenting))\n );\n }\n\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n const presetTouched = [...vanished, ...reparsedExisting];\n if (presetTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n presetTouched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n const removedPaths = [...vanished, ...reparsedExisting];\n if (removedPaths.length > 0) for (const feature of features) await feature.remove?.(conn, removedPaths, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n });\n\n // Reconcile's own write-transaction duration, for open()'s derived busy_timeout (F):\n // keep the observed max so a big watcher reconcile's lock hold is what the next open bounds its wait against.\n const durationMs = Date.now() - txStart;\n const prevRaw = await getMeta(conn, 'reconcile_max_ms');\n // -1, not 0, so a genuinely 0ms first reconcile (sub-millisecond, common on a tiny tree)\n // still gets recorded instead of losing to the \"nothing recorded yet\" default.\n const prevMax = prevRaw === null ? -1 : Number(prevRaw);\n if (durationMs > prevMax) await setMeta(conn, 'reconcile_max_ms', String(durationMs));\n\n return { parsed: parsedDocs.length, warnings };\n}\n\n// Segment keys that moved between two feature signatures (see config.featureSignature's\n// format: global features, embed provider, tokenize, then one segment per preset).\nexport function changedSignatureKeys(before: string, after: string): Set<string> {\n const keyOf = (part: string) => (part.startsWith('preset:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0]);\n const parse = (sig: string) => new Map(sig.split('|').map((part) => [keyOf(part), part]));\n const a = parse(before);\n const b = parse(after);\n const changed = new Set<string>();\n for (const [key, val] of b) if (a.get(key) !== val) changed.add(key);\n for (const key of a.keys()) if (!b.has(key)) changed.add(key);\n return changed;\n}\n\n// Whether the embed segment only gained its resolved weight identity: same provider and\n// model, no identity recorded before, one now -- adopted into meta without a rebuild.\nexport function embedIdentityAdopted(before: string, after: string): boolean {\n const embedPart = (sig: string) => sig.split('|').find((p) => p.startsWith('embed:'));\n const b = embedPart(before);\n const a = embedPart(after);\n if (b === undefined || a === undefined) return false;\n const at = a.indexOf('@');\n return b.indexOf('@') === -1 && at !== -1 && a.slice(0, at) === b;\n}\n\n// Names what moved, for the rebuild notice.\nexport function signatureDiff(before: string, after: string): string {\n const changed = changedSignatureKeys(before, after);\n const label = (key: string) => (key === 'embed' ? 'embed settings' : key === 'tokenize' ? 'content tokenizer' : key.startsWith('preset:') ? `preset \"${key.slice(7)}\"` : 'features');\n return changed.size === 0 ? 'features' : [...changed].map(label).join(', ');\n}\n\n// Tokenize-only rebuild: only `content` is repopulated (the other tables are tokenizer-independent)\n// and no feature extractors run. Returns per-file warnings, since mtimes stay untouched and reconcile would never reparse them.\nexport async function rebuildContentTable(conn: Connection, cfg: Config, baseDir: string): Promise<string[]> {\n const stmt = await conn.prepare('SELECT \"path\" FROM frontmatter');\n const known = new Set(((await stmt.all()) as Array<{ path: string }>).map((r) => r.path));\n const files = listFiles(cfg, baseDir).filter((f) => known.has(f.relPath));\n const segmenting = contentTokenize(cfg) === undefined;\n const warnings: string[] = [];\n const rows: unknown[][] = [];\n for (const file of files) {\n const { doc, warnings: fileWarnings } = parseFile(file);\n warnings.push(...fileWarnings);\n rows.push(contentRow(doc, segmenting));\n }\n await withTransaction(conn, async () => {\n if (rows.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, rows);\n });\n return warnings;\n}\n"],"names":["changedSignatureKeys","embedIdentityAdopted","rebuildContentTable","reconcile","signatureDiff","CORE_FRONTMATTER_COLUMNS","Set","MAX_FRONTMATTER_COLUMNS","INSERT_CONTENT_SQL","contentRow","doc","segmenting","relPath","search","title","summary","text","segmentField","conn","cfg","baseDir","files","currentSet","existingStmt","existingRows","existing","vanished","toReparse","features","seenColumns","report","parsedDocs","warnings","newColumns","col","allColumns","writableColumns","insertSql","added","delta","addedSet","reparsedExisting","txStart","durationMs","prevRaw","prevMax","listFiles","map","f","prepare","all","Map","r","path","filter","has","row","get","_mtime","mtimeMs","_size","size","length","parsed","activeFeatures","getColumns","progress","reparseFiles","tick","docs","finish","add","SenseError","c","RESERVED_COLUMNS","quoteIdent","join","reparsed","d","p","Date","now","withTransaction","feature","rows","presetTouched","presetRows","presetName","removedPaths","exec","runBatch","ctimeMs","parseError","data","contentTokenize","undefined","presets","push","remove","docsForFeature","extracted","name","store","afterReconcile","getMeta","Number","setMeta","String","before","after","keyOf","part","startsWith","split","slice","parse","sig","a","b","changed","key","val","keys","embedPart","find","at","indexOf","label","stmt","known","file","parseFile","fileWarnings"],"mappings":"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"}
@@ -19,10 +19,8 @@ function registerFunctions(db, segmenting) {
19
19
  deterministic: true,
20
20
  varargs: true
21
21
  }, _sqlfunctionsts.basenameImpl);
22
- // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back, and
23
- // matches nothing, so the same transform is available to hand-written SQL by name. `segmenting`
24
- // is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) ===
25
- // undefined), passed in by open() rather than read back from persisted state.
22
+ // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back and matches
23
+ // nothing, so the same transform is available to hand-written SQL by name. `segmenting` is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) === undefined), passed in by open().
26
24
  db.function('segment', {
27
25
  deterministic: true,
28
26
  varargs: false
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/sql-functions.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { segmentMatch } from '../../text/segment.ts';\nimport { basenameImpl, hasImpl } from '../sql-functions.ts';\n\nexport function registerFunctions(db: DatabaseSync, segmenting: boolean): void {\n db.function('has', { deterministic: true, varargs: false }, hasImpl);\n\n db.function('basename', { deterministic: true, varargs: true }, basenameImpl);\n\n // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back, and\n // matches nothing, so the same transform is available to hand-written SQL by name. `segmenting`\n // is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) ===\n // undefined), passed in by open() rather than read back from persisted state.\n db.function('segment', { deterministic: true, varargs: false }, (terms: unknown): string => {\n const text = terms === null || terms === undefined ? '' : String(terms);\n return segmenting ? segmentMatch(text) : text;\n });\n}\n"],"names":["registerFunctions","db","segmenting","function","deterministic","varargs","hasImpl","basenameImpl","terms","text","undefined","String","segmentMatch"],"mappings":";;;;+BAIgBA;;;eAAAA;;;yBAHa;8BACS;AAE/B,SAASA,kBAAkBC,EAAgB,EAAEC,UAAmB;IACrED,GAAGE,QAAQ,CAAC,OAAO;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAGC,uBAAO;IAEnEL,GAAGE,QAAQ,CAAC,YAAY;QAAEC,eAAe;QAAMC,SAAS;IAAK,GAAGE,4BAAY;IAE5E,0FAA0F;IAC1F,gGAAgG;IAChG,yFAAyF;IACzF,8EAA8E;IAC9EN,GAAGE,QAAQ,CAAC,WAAW;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAG,SAACG;QAC/D,IAAMC,OAAOD,UAAU,QAAQA,UAAUE,YAAY,KAAKC,OAAOH;QACjE,OAAON,aAAaU,IAAAA,uBAAY,EAACH,QAAQA;IAC3C;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/sql-functions.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { segmentMatch } from '../../text/segment.ts';\nimport { basenameImpl, hasImpl } from '../sql-functions.ts';\n\nexport function registerFunctions(db: DatabaseSync, segmenting: boolean): void {\n db.function('has', { deterministic: true, varargs: false }, hasImpl);\n\n db.function('basename', { deterministic: true, varargs: true }, basenameImpl);\n\n // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back and matches\n // nothing, so the same transform is available to hand-written SQL by name. `segmenting` is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) === undefined), passed in by open().\n db.function('segment', { deterministic: true, varargs: false }, (terms: unknown): string => {\n const text = terms === null || terms === undefined ? '' : String(terms);\n return segmenting ? segmentMatch(text) : text;\n });\n}\n"],"names":["registerFunctions","db","segmenting","function","deterministic","varargs","hasImpl","basenameImpl","terms","text","undefined","String","segmentMatch"],"mappings":";;;;+BAIgBA;;;eAAAA;;;yBAHa;8BACS;AAE/B,SAASA,kBAAkBC,EAAgB,EAAEC,UAAmB;IACrED,GAAGE,QAAQ,CAAC,OAAO;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAGC,uBAAO;IAEnEL,GAAGE,QAAQ,CAAC,YAAY;QAAEC,eAAe;QAAMC,SAAS;IAAK,GAAGE,4BAAY;IAE5E,iGAAiG;IACjG,gNAAgN;IAChNN,GAAGE,QAAQ,CAAC,WAAW;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAG,SAACG;QAC/D,IAAMC,OAAOD,UAAU,QAAQA,UAAUE,YAAY,KAAKC,OAAOH;QACjE,OAAON,aAAaU,IAAAA,uBAAY,EAACH,QAAQA;IAC3C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/store.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store, VectorWriteRow } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { reconcile } from './reconcile.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['phrases', 'snippets', 'watch-concurrency', 'lexical', 'vectors']);\n\n// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one\n// Connection instance (conn) with open()'s own reconcile call so transaction depth (see\n// transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: DatabaseSync, conn: Connection, cfg: Config, baseDir: string): Store {\n return {\n name: 'sqlite',\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);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n async fieldStats(columns, scopeWhere) {\n return fieldStats(conn, columns, scopeWhere);\n },\n },\n lexical: {\n async query(terms, opts) {\n return queryLexical(conn, terms, opts, contentTokenize(cfg) === undefined);\n },\n },\n vectors: {\n async pending() {\n return pendingRows(conn);\n },\n async writeVectors(rows: VectorWriteRow[]) {\n await writeVectorBatch(conn, rows);\n },\n async candidates(qv, storeDims, fetch, allowed) {\n return scanCandidates(conn, qv, storeDims, fetch, allowed);\n },\n async similar(path, opts) {\n return scanSimilar(conn, path, opts);\n },\n async hasVector(path) {\n return hasVectorRow(conn, path);\n },\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), not a value re-derived here.\n const row = db.prepare('PRAGMA busy_timeout').get() as { timeout: number };\n return { busy_timeout: `${row.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 = db.prepare(sql);\n stmt.setReadBigInts(true); // int64 past 2^53 arrives as BigInt instead of throwing at step time\n return {\n columns: () => stmt.columns(),\n // sense sql streams through an async iterator wrapping the sync iterate().\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...(params as Parameters<typeof stmt.iterate>));\n },\n };\n },\n },\n async close() {\n 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","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","contentTokenize","undefined","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","candidates","qv","storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","timeout","raw","stmt","setReadBigInts","iterate","params","close"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/store.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store, VectorWriteRow } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { reconcile } from './reconcile.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['phrases', 'snippets', 'watch-concurrency', 'lexical', 'vectors']);\n\n// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one\n// Connection instance (conn) with open()'s own reconcile call so transaction depth is tracked against the same object everywhere.\nexport function createStore(db: DatabaseSync, conn: Connection, cfg: Config, baseDir: string): Store {\n return {\n name: 'sqlite',\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);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n async fieldStats(columns, scopeWhere) {\n return fieldStats(conn, columns, scopeWhere);\n },\n },\n lexical: {\n async query(terms, opts) {\n return queryLexical(conn, terms, opts, contentTokenize(cfg) === undefined);\n },\n },\n vectors: {\n async pending() {\n return pendingRows(conn);\n },\n async writeVectors(rows: VectorWriteRow[]) {\n await writeVectorBatch(conn, rows);\n },\n async candidates(qv, storeDims, fetch, allowed) {\n return scanCandidates(conn, qv, storeDims, fetch, allowed);\n },\n async similar(path, opts) {\n return scanSimilar(conn, path, opts);\n },\n async hasVector(path) {\n return hasVectorRow(conn, path);\n },\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), not a value re-derived here.\n const row = db.prepare('PRAGMA busy_timeout').get() as { timeout: number };\n return { busy_timeout: `${row.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 = db.prepare(sql);\n stmt.setReadBigInts(true); // int64 past 2^53 arrives as BigInt instead of throwing at step time\n return {\n columns: () => stmt.columns(),\n // sense sql streams through an async iterator wrapping the sync iterate().\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...(params as Parameters<typeof stmt.iterate>));\n },\n };\n },\n },\n async close() {\n 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","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","contentTokenize","undefined","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","candidates","qv","storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","timeout","raw","stmt","setReadBigInts","iterate","params","close"],"mappings":"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"}
@@ -1,7 +1,5 @@
1
1
  // Store-agnostic joining transaction helper: node:sqlite and DuckDB both support only
2
- // BEGIN/COMMIT/ROLLBACK, no SAVEPOINT -- nesting joins the outer transaction instead of
3
- // stacking, so both stores share one semantics through the one thing they have in common,
4
- // exec(sql).
2
+ // BEGIN/COMMIT/ROLLBACK, no SAVEPOINT -- nesting joins the outer transaction, sharing one semantics through exec(sql).
5
3
  "use strict";
6
4
  Object.defineProperty(exports, "__esModule", {
7
5
  value: true
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/transaction.ts"],"sourcesContent":["// Store-agnostic joining transaction helper: node:sqlite and DuckDB both support only\n// BEGIN/COMMIT/ROLLBACK, no SAVEPOINT -- nesting joins the outer transaction instead of\n// stacking, so both stores share one semantics through the one thing they have in common,\n// exec(sql).\ninterface ExecConnection {\n exec(sql: string): Promise<void>;\n}\n\ninterface TxState {\n depth: number;\n rollbackOnly: boolean;\n}\n\n// Keyed by connection, not a module singleton: two connections can coexist in one process\n// (e.g. watch.ts alongside a CLI query) and must not share depth.\nconst states = new WeakMap<ExecConnection, TxState>();\n\nfunction stateFor(conn: ExecConnection): TxState {\n let state = states.get(conn);\n if (!state) {\n state = { depth: 0, rollbackOnly: false };\n states.set(conn, state);\n }\n return state;\n}\n\nasync function enter(conn: ExecConnection, state: TxState): Promise<void> {\n if (state.depth === 0) {\n await conn.exec('BEGIN');\n state.rollbackOnly = false;\n }\n state.depth++;\n}\n\n// Reaching depth 0 still rollback-only means an inner scope failed and an enclosing catch\n// swallowed it; commit here would persist a partial write, so this rolls back and throws instead.\nasync function leaveOk(conn: ExecConnection, state: TxState): Promise<void> {\n state.depth--;\n if (state.depth > 0) return;\n if (state.rollbackOnly) {\n await conn.exec('ROLLBACK');\n throw new Error('transaction rolled back: a nested scope failed');\n }\n await conn.exec('COMMIT');\n}\n\nasync function leaveErr(conn: ExecConnection, state: TxState): Promise<void> {\n state.rollbackOnly = true;\n state.depth--;\n if (state.depth === 0) await conn.exec('ROLLBACK');\n}\n\n// Depth-0 opens BEGIN; a nested call joins it and issues no SQL of its own. The outermost call\n// owns COMMIT/ROLLBACK. Every store, and every internal write loop (reconcile, runBatch), goes\n// through this one helper, so join semantics never differ by call site.\nexport async function withTransaction<T>(conn: ExecConnection, fn: () => Promise<T>): Promise<T> {\n const state = stateFor(conn);\n await enter(conn, state);\n let result: T;\n try {\n result = await fn();\n } catch (err) {\n await leaveErr(conn, state);\n throw err;\n }\n await leaveOk(conn, state);\n return result;\n}\n"],"names":["withTransaction","states","WeakMap","stateFor","conn","state","get","depth","rollbackOnly","set","enter","exec","leaveOk","Error","leaveErr","fn","result","err"],"mappings":"AAAA,sFAAsF;AACtF,wFAAwF;AACxF,0FAA0F;AAC1F,aAAa;;;;;+BAoDSA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA1CtB,0FAA0F;AAC1F,kEAAkE;AAClE,IAAMC,SAAS,IAAIC;AAEnB,SAASC,SAASC,IAAoB;IACpC,IAAIC,QAAQJ,OAAOK,GAAG,CAACF;IACvB,IAAI,CAACC,OAAO;QACVA,QAAQ;YAAEE,OAAO;YAAGC,cAAc;QAAM;QACxCP,OAAOQ,GAAG,CAACL,MAAMC;IACnB;IACA,OAAOA;AACT;AAEA,SAAeK,MAAMN,IAAoB,EAAEC,KAAc;;;;;yBACnDA,CAAAA,MAAME,KAAK,KAAK,CAAA,GAAhBF;;;;oBACF;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;oBACAN,MAAMG,YAAY,GAAG;;;oBAEvBH,MAAME,KAAK;;;;;;IACb;;AAEA,0FAA0F;AAC1F,kGAAkG;AAClG,SAAeK,QAAQR,IAAoB,EAAEC,KAAc;;;;;oBACzDA,MAAME,KAAK;oBACX,IAAIF,MAAME,KAAK,GAAG,GAAG;;;yBACjBF,MAAMG,YAAY,EAAlBH;;;;oBACF;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;oBACA,MAAM,IAAIE,MAAM;;oBAElB;;wBAAMT,KAAKO,IAAI,CAAC;;;oBAAhB;;;;;;IACF;;AAEA,SAAeG,SAASV,IAAoB,EAAEC,KAAc;;;;;oBAC1DA,MAAMG,YAAY,GAAG;oBACrBH,MAAME,KAAK;yBACPF,CAAAA,MAAME,KAAK,KAAK,CAAA,GAAhBF;;;;oBAAmB;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;;;;;;;;IACzB;;AAKO,SAAeX,gBAAmBI,IAAoB,EAAEW,EAAoB;;YAC3EV,OAEFW,QAGKC;;;;oBALHZ,QAAQF,SAASC;oBACvB;;wBAAMM,MAAMN,MAAMC;;;oBAAlB;;;;;;;;;oBAGW;;wBAAMU;;;oBAAfC,SAAS;;;;;;oBACFC;oBACP;;wBAAMH,SAASV,MAAMC;;;oBAArB;oBACA,MAAMY;;oBAER;;wBAAML,QAAQR,MAAMC;;;oBAApB;oBACA;;wBAAOW;;;;IACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/transaction.ts"],"sourcesContent":["// Store-agnostic joining transaction helper: node:sqlite and DuckDB both support only\n// BEGIN/COMMIT/ROLLBACK, no SAVEPOINT -- nesting joins the outer transaction, sharing one semantics through exec(sql).\ninterface ExecConnection {\n exec(sql: string): Promise<void>;\n}\n\ninterface TxState {\n depth: number;\n rollbackOnly: boolean;\n}\n\n// Keyed by connection, not a module singleton: two connections can coexist in one process\n// (e.g. watch.ts alongside a CLI query) and must not share depth.\nconst states = new WeakMap<ExecConnection, TxState>();\n\nfunction stateFor(conn: ExecConnection): TxState {\n let state = states.get(conn);\n if (!state) {\n state = { depth: 0, rollbackOnly: false };\n states.set(conn, state);\n }\n return state;\n}\n\nasync function enter(conn: ExecConnection, state: TxState): Promise<void> {\n if (state.depth === 0) {\n await conn.exec('BEGIN');\n state.rollbackOnly = false;\n }\n state.depth++;\n}\n\n// Reaching depth 0 still rollback-only means an inner scope failed and an enclosing catch\n// swallowed it; commit here would persist a partial write, so this rolls back and throws instead.\nasync function leaveOk(conn: ExecConnection, state: TxState): Promise<void> {\n state.depth--;\n if (state.depth > 0) return;\n if (state.rollbackOnly) {\n await conn.exec('ROLLBACK');\n throw new Error('transaction rolled back: a nested scope failed');\n }\n await conn.exec('COMMIT');\n}\n\nasync function leaveErr(conn: ExecConnection, state: TxState): Promise<void> {\n state.rollbackOnly = true;\n state.depth--;\n if (state.depth === 0) await conn.exec('ROLLBACK');\n}\n\n// Depth-0 opens BEGIN; a nested call joins it and issues no SQL of its own. The outermost call\n// owns COMMIT/ROLLBACK; every store and internal write loop goes through this one helper, so join semantics never differ by call site.\nexport async function withTransaction<T>(conn: ExecConnection, fn: () => Promise<T>): Promise<T> {\n const state = stateFor(conn);\n await enter(conn, state);\n let result: T;\n try {\n result = await fn();\n } catch (err) {\n await leaveErr(conn, state);\n throw err;\n }\n await leaveOk(conn, state);\n return result;\n}\n"],"names":["withTransaction","states","WeakMap","stateFor","conn","state","get","depth","rollbackOnly","set","enter","exec","leaveOk","Error","leaveErr","fn","result","err"],"mappings":"AAAA,sFAAsF;AACtF,uHAAuH;;;;;+BAmDjGA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAzCtB,0FAA0F;AAC1F,kEAAkE;AAClE,IAAMC,SAAS,IAAIC;AAEnB,SAASC,SAASC,IAAoB;IACpC,IAAIC,QAAQJ,OAAOK,GAAG,CAACF;IACvB,IAAI,CAACC,OAAO;QACVA,QAAQ;YAAEE,OAAO;YAAGC,cAAc;QAAM;QACxCP,OAAOQ,GAAG,CAACL,MAAMC;IACnB;IACA,OAAOA;AACT;AAEA,SAAeK,MAAMN,IAAoB,EAAEC,KAAc;;;;;yBACnDA,CAAAA,MAAME,KAAK,KAAK,CAAA,GAAhBF;;;;oBACF;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;oBACAN,MAAMG,YAAY,GAAG;;;oBAEvBH,MAAME,KAAK;;;;;;IACb;;AAEA,0FAA0F;AAC1F,kGAAkG;AAClG,SAAeK,QAAQR,IAAoB,EAAEC,KAAc;;;;;oBACzDA,MAAME,KAAK;oBACX,IAAIF,MAAME,KAAK,GAAG,GAAG;;;yBACjBF,MAAMG,YAAY,EAAlBH;;;;oBACF;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;oBACA,MAAM,IAAIE,MAAM;;oBAElB;;wBAAMT,KAAKO,IAAI,CAAC;;;oBAAhB;;;;;;IACF;;AAEA,SAAeG,SAASV,IAAoB,EAAEC,KAAc;;;;;oBAC1DA,MAAMG,YAAY,GAAG;oBACrBH,MAAME,KAAK;yBACPF,CAAAA,MAAME,KAAK,KAAK,CAAA,GAAhBF;;;;oBAAmB;;wBAAMD,KAAKO,IAAI,CAAC;;;oBAAhB;;;;;;;;IACzB;;AAIO,SAAeX,gBAAmBI,IAAoB,EAAEW,EAAoB;;YAC3EV,OAEFW,QAGKC;;;;oBALHZ,QAAQF,SAASC;oBACvB;;wBAAMM,MAAMN,MAAMC;;;oBAAlB;;;;;;;;;oBAGW;;wBAAMU;;;oBAAfC,SAAS;;;;;;oBACFC;oBACP;;wBAAMH,SAASV,MAAMC;;;oBAArB;oBACA,MAAMY;;oBAER;;wBAAML,QAAQR,MAAMC;;;oBAApB;oBACA;;wBAAOW;;;;IACT"}