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
@@ -82,19 +82,8 @@ function _unsupported_iterable_to_array(o, minLen) {
82
82
  if (n === "Map" || n === "Set") return Array.from(n);
83
83
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
84
84
  }
85
- // Rewrites a single-row parameterized statement -- the shape every write loop in this codebase
86
- // prepares once and calls with N different param sets -- into one statement DuckDB executes over
87
- // all N rows in a single round trip. A columnar engine's cost is row-at-a-time writes themselves,
88
- // not just the async crossing to reach them, so this recognizes the three statement shapes the
89
- // store issues (INSERT ... VALUES (...), UPDATE ... SET ... WHERE ..., DELETE ... WHERE ...) and
90
- // rewrites each into a multi-row form, verified against a live DuckDB connection at
91
- // implementation time (PRINCIPLES: documented-means-tested). Unrecognized SQL returns null;
92
- // connection.ts's runBatch falls back to a prepare-once, bind-and-run loop -- still one call
93
- // from outside the store.
94
- //
95
- // This only ever runs against sense's own SQL literals (reconcile, the feature hooks), never
96
- // arbitrary user SQL (`sense sql` is a separate, unrewritten passthrough), so the shapes below
97
- // are exactly what those call sites emit, not a general parser.
85
+ // Rewrites the store's single-row parameterized INSERT/UPDATE/DELETE into one multi-row statement --
86
+ // DuckDB's columnar engine pays for row-at-a-time writes, not just the round trip (PRINCIPLES: documented-means-tested).
98
87
  var INSERT_RE = RegExp("^(INSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+\\S+\\s*\\([^)]*\\)\\s*VALUES\\s*)\\(([^()]*)\\)(.*)$", "is");
99
88
  var UPDATE_RE = RegExp("^UPDATE\\s+(\\S+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+)$", "is");
100
89
  var DELETE_RE = RegExp("^DELETE\\s+FROM\\s+(\\S+)\\s+WHERE\\s+(.+)$", "is");
@@ -102,9 +91,8 @@ function placeholderCount(tuple) {
102
91
  var _tuple_match;
103
92
  return ((_tuple_match = tuple.match(/\?/g)) !== null && _tuple_match !== void 0 ? _tuple_match : []).length;
104
93
  }
105
- // "a = ?, b = ?" or "a = ? AND b = ?" -> ["a", "b"]. Only ever called on clauses already
106
- // shaped that way by the regexes above; a clause that doesn't fit yields a column list whose
107
- // length won't match the caller's row width, which rewriteUpdate/rewriteDelete then reject.
94
+ // "a = ?, b = ?" or "a = ? AND b = ?" -> ["a", "b"]. A clause that doesn't fit yields a shorter
95
+ // list, which rewriteUpdate/rewriteDelete reject as a width mismatch.
108
96
  function equalityColumns(clause, sep) {
109
97
  var parts = clause.split(sep).map(function(p) {
110
98
  return p.trim();
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/batch.ts"],"sourcesContent":["// Rewrites a single-row parameterized statement -- the shape every write loop in this codebase\n// prepares once and calls with N different param sets -- into one statement DuckDB executes over\n// all N rows in a single round trip. A columnar engine's cost is row-at-a-time writes themselves,\n// not just the async crossing to reach them, so this recognizes the three statement shapes the\n// store issues (INSERT ... VALUES (...), UPDATE ... SET ... WHERE ..., DELETE ... WHERE ...) and\n// rewrites each into a multi-row form, verified against a live DuckDB connection at\n// implementation time (PRINCIPLES: documented-means-tested). Unrecognized SQL returns null;\n// connection.ts's runBatch falls back to a prepare-once, bind-and-run loop -- still one call\n// from outside the store.\n//\n// This only ever runs against sense's own SQL literals (reconcile, the feature hooks), never\n// arbitrary user SQL (`sense sql` is a separate, unrewritten passthrough), so the shapes below\n// are exactly what those call sites emit, not a general parser.\n\nconst INSERT_RE = /^(INSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+\\S+\\s*\\([^)]*\\)\\s*VALUES\\s*)\\(([^()]*)\\)(.*)$/is;\nconst UPDATE_RE = /^UPDATE\\s+(\\S+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+)$/is;\nconst DELETE_RE = /^DELETE\\s+FROM\\s+(\\S+)\\s+WHERE\\s+(.+)$/is;\n\nfunction placeholderCount(tuple: string): number {\n return (tuple.match(/\\?/g) ?? []).length;\n}\n\n// \"a = ?, b = ?\" or \"a = ? AND b = ?\" -> [\"a\", \"b\"]. Only ever called on clauses already\n// shaped that way by the regexes above; a clause that doesn't fit yields a column list whose\n// length won't match the caller's row width, which rewriteUpdate/rewriteDelete then reject.\nfunction equalityColumns(clause: string, sep: RegExp): string[] | null {\n const parts = clause.split(sep).map((p) => p.trim());\n const cols: string[] = [];\n for (const part of parts) {\n const m = /^(.+?)=\\s*\\?$/.exec(part);\n if (!m) return null;\n cols.push(m[1].trim());\n }\n return cols;\n}\n\nfunction tuples(rowCount: number, width: number): string {\n const row = `(${Array.from({ length: width }, () => '?').join(', ')})`;\n return Array.from({ length: rowCount }, () => row).join(', ');\n}\n\nexport function rewriteInsert(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = INSERT_RE.exec(sql.trim());\n if (!m) return null;\n const [, head, tuple, tail] = m;\n const width = placeholderCount(tuple);\n if (width === 0) return null;\n const rows = Array.from({ length: rowCount }, () => `(${tuple})`).join(', ');\n return { sql: `${head}${rows}${tail}`, width };\n}\n\nexport function rewriteUpdate(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = UPDATE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, setClause, whereClause] = m;\n const setCols = equalityColumns(setClause, /,/);\n const whereCols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!setCols || !whereCols) return null;\n const dataCols = [...setCols, ...whereCols];\n const setAssign = setCols.map((c) => `${c} = data.${c}`).join(', ');\n const joinCond = whereCols.map((c) => `${table}.${c} = data.${c}`).join(' AND ');\n return { sql: `UPDATE ${table} SET ${setAssign} FROM (VALUES ${tuples(rowCount, dataCols.length)}) AS data(${dataCols.join(', ')}) WHERE ${joinCond}`, width: dataCols.length };\n}\n\nexport function rewriteDelete(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = DELETE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, whereClause] = m;\n const cols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!cols) return null;\n if (cols.length === 1) {\n return { sql: `DELETE FROM ${table} WHERE ${cols[0]} IN (${Array.from({ length: rowCount }, () => '?').join(', ')})`, width: 1 };\n }\n return { sql: `DELETE FROM ${table} WHERE (${cols.join(', ')}) IN (VALUES ${tuples(rowCount, cols.length)})`, width: cols.length };\n}\n\n// Tries each shape in turn; null means the caller should fall back to a per-row loop.\nexport function rewriteBatch(sql: string, rowCount: number): { sql: string; width: number } | null {\n return rewriteInsert(sql, rowCount) ?? rewriteUpdate(sql, rowCount) ?? rewriteDelete(sql, rowCount);\n}\n"],"names":["rewriteBatch","rewriteDelete","rewriteInsert","rewriteUpdate","INSERT_RE","UPDATE_RE","DELETE_RE","placeholderCount","tuple","match","length","equalityColumns","clause","sep","parts","split","map","p","trim","cols","part","m","exec","push","tuples","rowCount","width","row","Array","from","join","sql","head","tail","rows","table","setClause","whereClause","setCols","whereCols","dataCols","setAssign","c","joinCond"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/batch.ts"],"sourcesContent":["// Rewrites the store's single-row parameterized INSERT/UPDATE/DELETE into one multi-row statement --\n// DuckDB's columnar engine pays for row-at-a-time writes, not just the round trip (PRINCIPLES: documented-means-tested).\n\nconst INSERT_RE = /^(INSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+\\S+\\s*\\([^)]*\\)\\s*VALUES\\s*)\\(([^()]*)\\)(.*)$/is;\nconst UPDATE_RE = /^UPDATE\\s+(\\S+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+)$/is;\nconst DELETE_RE = /^DELETE\\s+FROM\\s+(\\S+)\\s+WHERE\\s+(.+)$/is;\n\nfunction placeholderCount(tuple: string): number {\n return (tuple.match(/\\?/g) ?? []).length;\n}\n\n// \"a = ?, b = ?\" or \"a = ? AND b = ?\" -> [\"a\", \"b\"]. A clause that doesn't fit yields a shorter\n// list, which rewriteUpdate/rewriteDelete reject as a width mismatch.\nfunction equalityColumns(clause: string, sep: RegExp): string[] | null {\n const parts = clause.split(sep).map((p) => p.trim());\n const cols: string[] = [];\n for (const part of parts) {\n const m = /^(.+?)=\\s*\\?$/.exec(part);\n if (!m) return null;\n cols.push(m[1].trim());\n }\n return cols;\n}\n\nfunction tuples(rowCount: number, width: number): string {\n const row = `(${Array.from({ length: width }, () => '?').join(', ')})`;\n return Array.from({ length: rowCount }, () => row).join(', ');\n}\n\nexport function rewriteInsert(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = INSERT_RE.exec(sql.trim());\n if (!m) return null;\n const [, head, tuple, tail] = m;\n const width = placeholderCount(tuple);\n if (width === 0) return null;\n const rows = Array.from({ length: rowCount }, () => `(${tuple})`).join(', ');\n return { sql: `${head}${rows}${tail}`, width };\n}\n\nexport function rewriteUpdate(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = UPDATE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, setClause, whereClause] = m;\n const setCols = equalityColumns(setClause, /,/);\n const whereCols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!setCols || !whereCols) return null;\n const dataCols = [...setCols, ...whereCols];\n const setAssign = setCols.map((c) => `${c} = data.${c}`).join(', ');\n const joinCond = whereCols.map((c) => `${table}.${c} = data.${c}`).join(' AND ');\n return { sql: `UPDATE ${table} SET ${setAssign} FROM (VALUES ${tuples(rowCount, dataCols.length)}) AS data(${dataCols.join(', ')}) WHERE ${joinCond}`, width: dataCols.length };\n}\n\nexport function rewriteDelete(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = DELETE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, whereClause] = m;\n const cols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!cols) return null;\n if (cols.length === 1) {\n return { sql: `DELETE FROM ${table} WHERE ${cols[0]} IN (${Array.from({ length: rowCount }, () => '?').join(', ')})`, width: 1 };\n }\n return { sql: `DELETE FROM ${table} WHERE (${cols.join(', ')}) IN (VALUES ${tuples(rowCount, cols.length)})`, width: cols.length };\n}\n\n// Tries each shape in turn; null means the caller should fall back to a per-row loop.\nexport function rewriteBatch(sql: string, rowCount: number): { sql: string; width: number } | null {\n return rewriteInsert(sql, rowCount) ?? rewriteUpdate(sql, rowCount) ?? rewriteDelete(sql, rowCount);\n}\n"],"names":["rewriteBatch","rewriteDelete","rewriteInsert","rewriteUpdate","INSERT_RE","UPDATE_RE","DELETE_RE","placeholderCount","tuple","match","length","equalityColumns","clause","sep","parts","split","map","p","trim","cols","part","m","exec","push","tuples","rowCount","width","row","Array","from","join","sql","head","tail","rows","table","setClause","whereClause","setCols","whereCols","dataCols","setAssign","c","joinCond"],"mappings":"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"}
@@ -402,9 +402,8 @@ function _wrap_async_generator(fn) {
402
402
  return new _async_generator(fn.apply(this, arguments));
403
403
  };
404
404
  }
405
- // getRowObjectsJS returns INT64 columns as BigInt regardless of magnitude, while sqlite's
406
- // small ints are numbers and consumers assume number. Beyond the safe range stays BigInt (the
407
- // setReadBigInts contract); stringifyJson keeps JSON output safe.
405
+ // getRowObjectsJS returns INT64 columns as BigInt regardless of magnitude, while sqlite's small
406
+ // ints are numbers and consumers assume number; in-range values convert here, out-of-range stays BigInt.
408
407
  var MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
409
408
  function storeValueToJs(value) {
410
409
  return (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'bigint' && value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value;
@@ -433,21 +432,13 @@ function storeRowToJs(row) {
433
432
  }
434
433
  return out;
435
434
  }
436
- // @duckdb/node-api tracks every prepared statement itself and destroys any survivor when the
437
- // connection closes, but sense's stores stay open for a whole `sense watch` session -- nothing
438
- // destroys one before then. The portable Statement interface has no dispose (sqlite's node:sqlite
439
- // statements need none, and callers like graph/traverse.ts's ring loop reuse one Statement across
440
- // several run/get/all calls, so a per-call destroy would be wrong), so cleanup happens here,
441
- // locally, once nothing references the wrapper any more. destroySync() is safe to call more than
442
- // once -- runBatch below and vectors.ts already call it explicitly while the same connection-close
443
- // sweep can also reach a statement that outlives its wrapper, with no reported issue.
435
+ // @duckdb/node-api only destroys a prepared statement when its connection closes, but a connection can stay open for a whole `sense watch` session.
436
+ // Statement has no dispose (callers reuse one instance across several calls), so this FinalizationRegistry reclaims it once nothing references the wrapper.
444
437
  var preparedFinalizer = new FinalizationRegistry(function(prepared) {
445
438
  prepared.destroySync();
446
439
  });
447
- // Wraps one already-prepared DuckDB statement: prepare() is the only async step (Store.prepare
448
- // already crosses the async boundary once), so columns() reads back synchronously from it and
449
- // run/get/all rebind and re-execute the same native statement, matching how a caller (e.g.
450
- // graph/traverse.ts's ring loop) reuses one Statement across several calls.
440
+ // Wraps one already-prepared DuckDB statement: prepare() is the only async step, so run/get/all rebind and
441
+ // re-execute the same native statement, matching how callers like graph/traverse.ts's ring loop reuse one Statement across several calls.
451
442
  var DuckdbStatement = /*#__PURE__*/ function() {
452
443
  "use strict";
453
444
  function DuckdbStatement(prepared) {
@@ -618,11 +609,8 @@ function createConnection(duckdb) {
618
609
  });
619
610
  })();
620
611
  },
621
- runBatch: // One crossing regardless of row count: rewriteBatch turns the caller's single-row SQL into
622
- // one multi-row statement when it recognizes the shape (see batch.ts); anything else falls
623
- // back to a bind-and-run loop over one prepared statement, still a single call to the caller.
624
- // Joins the caller's transaction when there is one (reconcile); otherwise opens its own, so
625
- // a standalone batch stays atomic.
612
+ runBatch: // One crossing regardless of row count: rewriteBatch folds recognized shapes into a multi-row statement (batch.ts),
613
+ // else falls back to a bind-and-run loop, one call either way. Joins the caller's transaction when there is one, else opens its own.
626
614
  function runBatch(sql, paramRows) {
627
615
  return _async_to_generator(function() {
628
616
  return _ts_generator(this, function(_state) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/connection.ts"],"sourcesContent":["import type { DuckDBConnection, DuckDBPreparedStatement, DuckDBValue } from '@duckdb/node-api';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\nimport { rewriteBatch } from './batch.ts';\n\n// getRowObjectsJS returns INT64 columns as BigInt regardless of magnitude, while sqlite's\n// small ints are numbers and consumers assume number. Beyond the safe range stays BigInt (the\n// setReadBigInts contract); stringifyJson keeps JSON output safe.\nconst MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\nexport function storeValueToJs(value: unknown): unknown {\n return typeof value === 'bigint' && value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value;\n}\nexport function storeRowToJs(row: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) out[key] = storeValueToJs(value);\n return out;\n}\n\n// @duckdb/node-api tracks every prepared statement itself and destroys any survivor when the\n// connection closes, but sense's stores stay open for a whole `sense watch` session -- nothing\n// destroys one before then. The portable Statement interface has no dispose (sqlite's node:sqlite\n// statements need none, and callers like graph/traverse.ts's ring loop reuse one Statement across\n// several run/get/all calls, so a per-call destroy would be wrong), so cleanup happens here,\n// locally, once nothing references the wrapper any more. destroySync() is safe to call more than\n// once -- runBatch below and vectors.ts already call it explicitly while the same connection-close\n// sweep can also reach a statement that outlives its wrapper, with no reported issue.\nconst preparedFinalizer = new FinalizationRegistry<DuckDBPreparedStatement>((prepared) => {\n prepared.destroySync();\n});\n\n// Wraps one already-prepared DuckDB statement: prepare() is the only async step (Store.prepare\n// already crosses the async boundary once), so columns() reads back synchronously from it and\n// run/get/all rebind and re-execute the same native statement, matching how a caller (e.g.\n// graph/traverse.ts's ring loop) reuses one Statement across several calls.\nclass DuckdbStatement implements Statement {\n private prepared: DuckDBPreparedStatement;\n\n constructor(prepared: DuckDBPreparedStatement) {\n this.prepared = prepared;\n preparedFinalizer.register(this, prepared);\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n if (params.length > 0) this.prepared.bind(params as DuckDBValue[]);\n const result = await this.prepared.run();\n return { changes: result.rowsChanged, lastInsertRowid: 0 };\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n const rows = await this.all(...params);\n return rows[0];\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n if (params.length > 0) this.prepared.bind(params as DuckDBValue[]);\n const reader = await this.prepared.runAndReadAll();\n return (reader.getRowObjectsJS() as Array<Record<string, unknown>>).map(storeRowToJs);\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n // Never called on the portable Connection/Statement surface today (only Store.raw streams);\n // materializing keeps this a correct, if eager, AsyncIterable.\n yield* await this.all(...params);\n }\n\n columns(): Array<{ name: string }> {\n const out: Array<{ name: string }> = [];\n for (let i = 0; i < this.prepared.columnCount; i++) out.push({ name: this.prepared.columnName(i) });\n return out;\n }\n\n setReadBigInts(_enabled: boolean): void {\n // No-op: in-range ints are already numbers here (storeRowToJs converts what getRowObjectsJS\n // hands back as BigInt), so the node:sqlite throw-at-step-time problem does not exist.\n }\n}\n\nexport function createConnection(duckdb: DuckDBConnection): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n await duckdb.run(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new DuckdbStatement(await duckdb.prepare(sql));\n },\n // One crossing regardless of row count: rewriteBatch turns the caller's single-row SQL into\n // one multi-row statement when it recognizes the shape (see batch.ts); anything else falls\n // back to a bind-and-run loop over one prepared statement, still a single call to the caller.\n // Joins the caller's transaction when there is one (reconcile); otherwise opens its own, so\n // a standalone batch stays atomic.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(conn, async () => {\n const rewritten = rewriteBatch(sql, paramRows.length);\n if (rewritten) {\n const stmt = await duckdb.prepare(rewritten.sql);\n try {\n stmt.bind(paramRows.flat() as DuckDBValue[]);\n await stmt.run();\n } finally {\n stmt.destroySync();\n }\n return;\n }\n const stmt = await duckdb.prepare(sql);\n try {\n for (const row of paramRows) {\n stmt.bind(row as DuckDBValue[]);\n await stmt.run();\n }\n } finally {\n stmt.destroySync();\n }\n });\n },\n };\n return conn;\n}\n"],"names":["createConnection","storeRowToJs","storeValueToJs","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","value","row","out","Object","entries","key","preparedFinalizer","FinalizationRegistry","prepared","destroySync","DuckdbStatement","register","run","params","result","length","bind","changes","rowsChanged","lastInsertRowid","get","rows","all","reader","runAndReadAll","getRowObjectsJS","map","iterate","columns","i","columnCount","push","name","columnName","setReadBigInts","_enabled","duckdb","conn","exec","sql","prepare","runBatch","paramRows","withTransaction","rewritten","stmt","rewriteBatch","flat"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/connection.ts"],"sourcesContent":["import type { DuckDBConnection, DuckDBPreparedStatement, DuckDBValue } from '@duckdb/node-api';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\nimport { rewriteBatch } from './batch.ts';\n\n// getRowObjectsJS returns INT64 columns as BigInt regardless of magnitude, while sqlite's small\n// ints are numbers and consumers assume number; in-range values convert here, out-of-range stays BigInt.\nconst MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\nexport function storeValueToJs(value: unknown): unknown {\n return typeof value === 'bigint' && value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value;\n}\nexport function storeRowToJs(row: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) out[key] = storeValueToJs(value);\n return out;\n}\n\n// @duckdb/node-api only destroys a prepared statement when its connection closes, but a connection can stay open for a whole `sense watch` session.\n// Statement has no dispose (callers reuse one instance across several calls), so this FinalizationRegistry reclaims it once nothing references the wrapper.\nconst preparedFinalizer = new FinalizationRegistry<DuckDBPreparedStatement>((prepared) => {\n prepared.destroySync();\n});\n\n// Wraps one already-prepared DuckDB statement: prepare() is the only async step, so run/get/all rebind and\n// re-execute the same native statement, matching how callers like graph/traverse.ts's ring loop reuse one Statement across several calls.\nclass DuckdbStatement implements Statement {\n private prepared: DuckDBPreparedStatement;\n\n constructor(prepared: DuckDBPreparedStatement) {\n this.prepared = prepared;\n preparedFinalizer.register(this, prepared);\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n if (params.length > 0) this.prepared.bind(params as DuckDBValue[]);\n const result = await this.prepared.run();\n return { changes: result.rowsChanged, lastInsertRowid: 0 };\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n const rows = await this.all(...params);\n return rows[0];\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n if (params.length > 0) this.prepared.bind(params as DuckDBValue[]);\n const reader = await this.prepared.runAndReadAll();\n return (reader.getRowObjectsJS() as Array<Record<string, unknown>>).map(storeRowToJs);\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n // Never called on the portable Connection/Statement surface today (only Store.raw streams);\n // materializing keeps this a correct, if eager, AsyncIterable.\n yield* await this.all(...params);\n }\n\n columns(): Array<{ name: string }> {\n const out: Array<{ name: string }> = [];\n for (let i = 0; i < this.prepared.columnCount; i++) out.push({ name: this.prepared.columnName(i) });\n return out;\n }\n\n setReadBigInts(_enabled: boolean): void {\n // No-op: in-range ints are already numbers here (storeRowToJs converts what getRowObjectsJS\n // hands back as BigInt), so the node:sqlite throw-at-step-time problem does not exist.\n }\n}\n\nexport function createConnection(duckdb: DuckDBConnection): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n await duckdb.run(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new DuckdbStatement(await duckdb.prepare(sql));\n },\n // One crossing regardless of row count: rewriteBatch folds recognized shapes into a multi-row statement (batch.ts),\n // else falls back to a bind-and-run loop, one call either way. Joins the caller's transaction when there is one, else opens its own.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(conn, async () => {\n const rewritten = rewriteBatch(sql, paramRows.length);\n if (rewritten) {\n const stmt = await duckdb.prepare(rewritten.sql);\n try {\n stmt.bind(paramRows.flat() as DuckDBValue[]);\n await stmt.run();\n } finally {\n stmt.destroySync();\n }\n return;\n }\n const stmt = await duckdb.prepare(sql);\n try {\n for (const row of paramRows) {\n stmt.bind(row as DuckDBValue[]);\n await stmt.run();\n }\n } finally {\n stmt.destroySync();\n }\n });\n },\n };\n return conn;\n}\n"],"names":["createConnection","storeRowToJs","storeValueToJs","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","value","row","out","Object","entries","key","preparedFinalizer","FinalizationRegistry","prepared","destroySync","DuckdbStatement","register","run","params","result","length","bind","changes","rowsChanged","lastInsertRowid","get","rows","all","reader","runAndReadAll","getRowObjectsJS","map","iterate","columns","i","columnCount","push","name","columnName","setReadBigInts","_enabled","duckdb","conn","exec","sql","prepare","runBatch","paramRows","withTransaction","rewritten","stmt","rewriteBatch","flat"],"mappings":"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"}
@@ -161,14 +161,8 @@ function _unsupported_iterable_to_array(o, minLen) {
161
161
  if (n === "Map" || n === "Set") return Array.from(n);
162
162
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
163
163
  }
164
- // DuckDB's frontmatter columns are VARIANT (reconcile.ts's comment on why), so typeof() reports
165
- // VARIANT for every field regardless of the boxed value; variant_typeof() reports the boxed
166
- // native type instead (spike-verified against mapValue()'s actual bind shapes: a bound JS bigint
167
- // always boxes as INT128, a non-integer JS number always boxes as DOUBLE, a string as VARCHAR --
168
- // literal SQL values box narrower, e.g. INTEGER/DECIMAL(p, s), which this still covers since a
169
- // query issued through `sense sql` can produce either). Mapped onto the shared integer/real/text
170
- // vocabulary sqlite/turso's typeof() already emits natively; an unrecognized name throws rather
171
- // than being silently called text (no-silent-modes).
164
+ // DuckDB's frontmatter columns are VARIANT (reconcile.ts), so typeof() always reports VARIANT; variant_typeof() reports the boxed native
165
+ // type instead -- INT128, DOUBLE, VARCHAR, or a literal's narrower type like DECIMAL(p, s) -- mapped onto sqlite/turso's integer/real/text vocabulary; an unrecognized name throws (no-silent-modes).
172
166
  function classifyVariantType(name, field) {
173
167
  if (/^U?INT\d+$/.test(name)) return 'integer';
174
168
  if (name === 'FLOAT' || name === 'DOUBLE' || name.startsWith('DECIMAL(')) return 'real';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/fieldStats.ts"],"sourcesContent":["import { quoteIdent } from '../shared.ts';\nimport type { Connection, FieldStat } from '../types.ts';\n\n// DuckDB's frontmatter columns are VARIANT (reconcile.ts's comment on why), so typeof() reports\n// VARIANT for every field regardless of the boxed value; variant_typeof() reports the boxed\n// native type instead (spike-verified against mapValue()'s actual bind shapes: a bound JS bigint\n// always boxes as INT128, a non-integer JS number always boxes as DOUBLE, a string as VARCHAR --\n// literal SQL values box narrower, e.g. INTEGER/DECIMAL(p, s), which this still covers since a\n// query issued through `sense sql` can produce either). Mapped onto the shared integer/real/text\n// vocabulary sqlite/turso's typeof() already emits natively; an unrecognized name throws rather\n// than being silently called text (no-silent-modes).\nfunction classifyVariantType(name: string, field: string): 'integer' | 'real' | 'text' {\n if (/^U?INT\\d+$/.test(name)) return 'integer';\n if (name === 'FLOAT' || name === 'DOUBLE' || name.startsWith('DECIMAL(')) return 'real';\n if (name === 'VARCHAR') return 'text';\n throw new Error(`duckdb: variant_typeof() returned unrecognized type \"${name}\" for column \"${field}\"`);\n}\n\n// One aggregate query, one result row: COUNT + string_agg(DISTINCT variant_typeof()) per\n// column, so the query stays O(columns) rather than O(rows x columns). '|' is the string_agg\n// separator, not ',': DECIMAL's own display name (\"DECIMAL(4, 2)\") contains a comma, which a\n// ',' separator could not be split back apart from.\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}, string_agg(DISTINCT variant_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 const raw = ((row[`t${i}`] as string) ?? '').split('|').filter(Boolean);\n const types = [...new Set(raw.map((t) => classifyVariantType(t, name)))].sort();\n return { field: name, coverage: Number(row[`n${i}`] ?? 0), type: types.join(',') };\n });\n}\n"],"names":["fieldStats","classifyVariantType","name","field","test","startsWith","Error","conn","columns","scopeWhere","exprs","stmt","row","map","i","quoted","quoteIdent","prepare","join","get","raw","split","filter","Boolean","types","Set","t","sort","coverage","Number","type"],"mappings":";;;;+BAsBsBA;;;eAAAA;;;wBAtBK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG3B,gGAAgG;AAChG,4FAA4F;AAC5F,iGAAiG;AACjG,iGAAiG;AACjG,+FAA+F;AAC/F,iGAAiG;AACjG,gGAAgG;AAChG,qDAAqD;AACrD,SAASC,oBAAoBC,IAAY,EAAEC,KAAa;IACtD,IAAI,aAAaC,IAAI,CAACF,OAAO,OAAO;IACpC,IAAIA,SAAS,WAAWA,SAAS,YAAYA,KAAKG,UAAU,CAAC,aAAa,OAAO;IACjF,IAAIH,SAAS,WAAW,OAAO;IAC/B,MAAM,IAAII,MAAM,AAAC,wDAA4EH,OAArBD,MAAK,kBAAsB,OAANC,OAAM;AACrG;AAMO,SAAeH,WAAWO,IAAgB,EAAEC,OAAiB,EAAEC,UAAkB;;YAChFC,OAIAC,MACAC;;;;oBALAF,QAAQF,QAAQK,GAAG,CAAC,SAACX,MAAMY;wBAC/B,IAAMC,SAASC,IAAAA,oBAAU,EAACd;wBAC1B,OAAO,AAAC,SAAuBY,OAAfC,QAAO,UAAiDA,OAAzCD,GAAE,yCAAsEC,OAA/BA,QAAO,0BAAmDD,OAA3BC,QAAO,sBAAsB,OAAFD;oBACpI;oBACa;;wBAAMP,KAAKU,OAAO,CAAC,AAAC,UAA8CR,OAArCC,MAAMQ,IAAI,CAAC,OAAM,sBAA+B,OAAXT;;;oBAAzEE,OAAO;oBACA;;wBAAMA,KAAKQ,GAAG;;;oBAArBP,MAAO;oBACb;;wBAAOJ,QAAQK,GAAG,CAAC,SAACX,MAAMY;gCACVF,OAEyBA;4BAFvC,IAAMQ,MAAM,EAAER,QAAAA,GAAG,CAAC,AAAC,IAAK,OAAFE,GAAI,cAAZF,mBAAAA,QAA2B,IAAIS,KAAK,CAAC,KAAKC,MAAM,CAACC;4BAC/D,IAAMC,QAAQ,AAAC,qBAAG,IAAIC,IAAIL,IAAIP,GAAG,CAAC,SAACa;uCAAMzB,oBAAoByB,GAAGxB;iCAASyB,IAAI;4BAC7E,OAAO;gCAAExB,OAAOD;gCAAM0B,UAAUC,QAAOjB,SAAAA,GAAG,CAAC,AAAC,IAAK,OAAFE,GAAI,cAAZF,oBAAAA,SAAgB;gCAAIkB,MAAMN,MAAMN,IAAI,CAAC;4BAAK;wBACnF;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/fieldStats.ts"],"sourcesContent":["import { quoteIdent } from '../shared.ts';\nimport type { Connection, FieldStat } from '../types.ts';\n\n// DuckDB's frontmatter columns are VARIANT (reconcile.ts), so typeof() always reports VARIANT; variant_typeof() reports the boxed native\n// type instead -- INT128, DOUBLE, VARCHAR, or a literal's narrower type like DECIMAL(p, s) -- mapped onto sqlite/turso's integer/real/text vocabulary; an unrecognized name throws (no-silent-modes).\nfunction classifyVariantType(name: string, field: string): 'integer' | 'real' | 'text' {\n if (/^U?INT\\d+$/.test(name)) return 'integer';\n if (name === 'FLOAT' || name === 'DOUBLE' || name.startsWith('DECIMAL(')) return 'real';\n if (name === 'VARCHAR') return 'text';\n throw new Error(`duckdb: variant_typeof() returned unrecognized type \"${name}\" for column \"${field}\"`);\n}\n\n// One aggregate query, one result row: COUNT + string_agg(DISTINCT variant_typeof()) per column keeps this O(columns), not O(rows x columns).\n// '|' is the separator, not ',': DECIMAL's display name (\"DECIMAL(4, 2)\") contains a comma.\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}, string_agg(DISTINCT variant_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 const raw = ((row[`t${i}`] as string) ?? '').split('|').filter(Boolean);\n const types = [...new Set(raw.map((t) => classifyVariantType(t, name)))].sort();\n return { field: name, coverage: Number(row[`n${i}`] ?? 0), type: types.join(',') };\n });\n}\n"],"names":["fieldStats","classifyVariantType","name","field","test","startsWith","Error","conn","columns","scopeWhere","exprs","stmt","row","map","i","quoted","quoteIdent","prepare","join","get","raw","split","filter","Boolean","types","Set","t","sort","coverage","Number","type"],"mappings":";;;;+BAcsBA;;;eAAAA;;;wBAdK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG3B,yIAAyI;AACzI,sMAAsM;AACtM,SAASC,oBAAoBC,IAAY,EAAEC,KAAa;IACtD,IAAI,aAAaC,IAAI,CAACF,OAAO,OAAO;IACpC,IAAIA,SAAS,WAAWA,SAAS,YAAYA,KAAKG,UAAU,CAAC,aAAa,OAAO;IACjF,IAAIH,SAAS,WAAW,OAAO;IAC/B,MAAM,IAAII,MAAM,AAAC,wDAA4EH,OAArBD,MAAK,kBAAsB,OAANC,OAAM;AACrG;AAIO,SAAeH,WAAWO,IAAgB,EAAEC,OAAiB,EAAEC,UAAkB;;YAChFC,OAIAC,MACAC;;;;oBALAF,QAAQF,QAAQK,GAAG,CAAC,SAACX,MAAMY;wBAC/B,IAAMC,SAASC,IAAAA,oBAAU,EAACd;wBAC1B,OAAO,AAAC,SAAuBY,OAAfC,QAAO,UAAiDA,OAAzCD,GAAE,yCAAsEC,OAA/BA,QAAO,0BAAmDD,OAA3BC,QAAO,sBAAsB,OAAFD;oBACpI;oBACa;;wBAAMP,KAAKU,OAAO,CAAC,AAAC,UAA8CR,OAArCC,MAAMQ,IAAI,CAAC,OAAM,sBAA+B,OAAXT;;;oBAAzEE,OAAO;oBACA;;wBAAMA,KAAKQ,GAAG;;;oBAArBP,MAAO;oBACb;;wBAAOJ,QAAQK,GAAG,CAAC,SAACX,MAAMY;gCACVF,OAEyBA;4BAFvC,IAAMQ,MAAM,EAAER,QAAAA,GAAG,CAAC,AAAC,IAAK,OAAFE,GAAI,cAAZF,mBAAAA,QAA2B,IAAIS,KAAK,CAAC,KAAKC,MAAM,CAACC;4BAC/D,IAAMC,QAAQ,AAAC,qBAAG,IAAIC,IAAIL,IAAIP,GAAG,CAAC,SAACa;uCAAMzB,oBAAoByB,GAAGxB;iCAASyB,IAAI;4BAC7E,OAAO;gCAAExB,OAAOD;gCAAM0B,UAAUC,QAAOjB,SAAAA,GAAG,CAAC,AAAC,IAAK,OAAFE,GAAI,cAAZF,oBAAAA,SAAgB;gCAAIkB,MAAMN,MAAMN,IAAI,CAAC;4BAAK;wBACnF;;;;IACF"}
@@ -178,19 +178,15 @@ var FIELDS = [
178
178
  'summary',
179
179
  'text'
180
180
  ];
181
- // Mirrors sqlite's bm25(content, 10.0, 5.0, 1.0, ...) column-weight intent; DuckDB's
182
- // match_bm25 has no field_weights argument (verified 1.5.5), so each field is scored
183
- // separately and combined here.
181
+ // Mirrors sqlite's bm25(content, 10.0, 5.0, 1.0, ...) column-weight intent; DuckDB's match_bm25 has no
182
+ // field_weights argument (verified 1.5.5), so each field is scored separately and combined here.
184
183
  var FIELD_WEIGHT = {
185
184
  title: 10,
186
185
  summary: 5,
187
186
  text: 1
188
187
  };
189
- var UNSPACED_RUN = new RegExp("[".concat(_segmentts.UNSPACED_SCRIPTS, "]"), 'u');
190
- // FTS5 operator syntax (sqlite.org/fts5.html sec. 3) that words/substrings below would
191
- // otherwise silently treat as literal terms instead of honoring (PRINCIPLES: no-silent-modes).
192
- // Checked against terms with quoted spans blanked -- those go through contains() and are
193
- // supported.
188
+ // FTS5 operator syntax (sqlite.org/fts5.html sec. 3) that words/substrings below would otherwise silently
189
+ // treat as literal terms (PRINCIPLES: no-silent-modes). Checked with quoted spans blanked; those go through contains() and are supported.
194
190
  var FTS5_OPERATORS = [
195
191
  {
196
192
  label: 'prefix query',
@@ -241,9 +237,8 @@ function unsupportedOperator(terms) {
241
237
  }
242
238
  return null;
243
239
  }
244
- // A run whose script marks no word boundaries makes match_bm25's whitespace tokenizer index
245
- // the whole run as one token (same gap FTS5 has without the `_seg` sidecar), so such runs --
246
- // and any author-quoted phrase, any script -- go through contains() instead of the fts index.
240
+ // A run whose script marks no word boundaries makes match_bm25's whitespace tokenizer index the whole run as one token
241
+ // (same gap FTS5 has without the `_seg` sidecar), so such runs -- and any author-quoted phrase, any script -- go through contains() instead.
247
242
  function splitTerms(terms) {
248
243
  var words = [];
249
244
  var substrings = [];
@@ -251,7 +246,7 @@ function splitTerms(terms) {
251
246
  var phrase = inner.trim();
252
247
  if (phrase.length === 0) return ' ';
253
248
  substrings.push(phrase);
254
- if (!UNSPACED_RUN.test(phrase)) {
249
+ if (!(0, _segmentts.hasUnspacedRun)(phrase)) {
255
250
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
256
251
  try {
257
252
  for(var _iterator = phrase.split(/\s+/)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -280,7 +275,7 @@ function splitTerms(terms) {
280
275
  for(var _iterator = withoutPhrases.split(/\s+/)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
281
276
  var tok = _step.value;
282
277
  if (tok.length === 0) continue;
283
- if (UNSPACED_RUN.test(tok)) substrings.push(tok);
278
+ if ((0, _segmentts.hasUnspacedRun)(tok)) substrings.push(tok);
284
279
  else words.push(tok);
285
280
  }
286
281
  } catch (err) {
@@ -302,9 +297,8 @@ function splitTerms(terms) {
302
297
  substrings: substrings
303
298
  };
304
299
  }
305
- // No incremental update (verified 1.5.5: PRAGMA create_fts_index is rebuild-only), so this
306
- // pays the full rebuild -- but only once per store instance's first lexical query, and only
307
- // when content actually changed since (see FtsIndexState / duckdb/store.ts's markStale()).
300
+ // No incremental update (verified 1.5.5: PRAGMA create_fts_index is rebuild-only), so this pays the full rebuild -- but only
301
+ // once per store instance's first lexical query, and only when content actually changed since (FtsIndexState / store.ts's markStale()).
308
302
  function ensureFtsFresh(conn, state) {
309
303
  return _async_to_generator(function() {
310
304
  return _ts_generator(this, function(_state) {
@@ -326,10 +320,8 @@ function ensureFtsFresh(conn, state) {
326
320
  return _ts_generator(this, function(_state) {
327
321
  switch(_state.label){
328
322
  case 0:
329
- // stopwords='none': sqlite's porter/unicode61 tokenizer never removes stopwords either
330
- // (verified 1.5.5), and the fts extension's default 571-word English list would otherwise
331
- // silently drop common query words (e.g. "and") from the index but not from match_bm25's
332
- // conjunctive gate, making a bare multi-word query that contains one match nothing.
323
+ // stopwords='none': sqlite's porter/unicode61 tokenizer never removes stopwords either (verified 1.5.5); the fts extension's
324
+ // default 571-word list would otherwise drop common words (e.g. "and") from the index but not match_bm25's conjunctive gate, breaking multi-word matches.
333
325
  return [
334
326
  4,
335
327
  conn.exec("PRAGMA create_fts_index('content', 'path', 'title', 'summary', 'text', stopwords='none', overwrite=1)")
@@ -354,13 +346,13 @@ function ensureFtsFresh(conn, state) {
354
346
  });
355
347
  })();
356
348
  }
357
- // Per-word-term and per-substring-term SQL fragments, field-weighted the same way for both
358
- // (title 10 / summary 5 / text 1), plus the params in the exact left-to-right order they are
359
- // emitted -- positional `?` binding requires that order to match the assembled SQL text.
349
+ // Field-weighted fragments (title 10 / summary 5 / text 1), with params emitted in the exact left-to-right order the assembled SQL needs for positional `?` binding.
350
+ // score/gate params stay in separate arrays until joined at the end, since the SQL puts every score part before every gate part; sharing one array would shift binds once a query has both words and substrings (PRINCIPLES: no-silent-modes).
360
351
  function buildScoreAndGate(words, substrings) {
361
352
  var scoreParts = [];
362
353
  var gateParts = [];
363
- var params = [];
354
+ var scoreParams = [];
355
+ var gateParams = [];
364
356
  if (words.length > 0) {
365
357
  var wordQuery = words.join(' ');
366
358
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -368,7 +360,7 @@ function buildScoreAndGate(words, substrings) {
368
360
  for(var _iterator = FIELDS[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
369
361
  var field = _step.value;
370
362
  scoreParts.push("".concat(FIELD_WEIGHT[field], '.0 * COALESCE(fts_main_content.match_bm25(content."path", ?, fields := \'').concat(field, "', conjunctive := false), 0)"));
371
- params.push(wordQuery);
363
+ scoreParams.push(wordQuery);
372
364
  }
373
365
  } catch (err) {
374
366
  _didIteratorError = true;
@@ -385,7 +377,7 @@ function buildScoreAndGate(words, substrings) {
385
377
  }
386
378
  }
387
379
  gateParts.push('fts_main_content.match_bm25(content."path", ?, conjunctive := true) IS NOT NULL');
388
- params.push(wordQuery);
380
+ gateParams.push(wordQuery);
389
381
  }
390
382
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
391
383
  try {
@@ -397,7 +389,7 @@ function buildScoreAndGate(words, substrings) {
397
389
  for(var _iterator2 = FIELDS[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
398
390
  var field1 = _step2.value;
399
391
  scoreParts.push("".concat(FIELD_WEIGHT[field1], ".0 * CAST(contains(lower(content.").concat(field1, "), ?) AS INTEGER)"));
400
- params.push(needle);
392
+ scoreParams.push(needle);
401
393
  }
402
394
  } catch (err) {
403
395
  _didIteratorError2 = true;
@@ -414,7 +406,7 @@ function buildScoreAndGate(words, substrings) {
414
406
  }
415
407
  }
416
408
  gateParts.push("contains(lower(content.title) || ' ' || lower(content.summary) || ' ' || lower(content.text), ?)");
417
- params.push(needle);
409
+ gateParams.push(needle);
418
410
  }
419
411
  } catch (err) {
420
412
  _didIteratorError1 = true;
@@ -433,7 +425,7 @@ function buildScoreAndGate(words, substrings) {
433
425
  return {
434
426
  scoreSql: scoreParts.join(' + '),
435
427
  gateSql: gateParts.join(' AND '),
436
- params: params
428
+ params: _to_consumable_array(scoreParams).concat(_to_consumable_array(gateParams))
437
429
  };
438
430
  }
439
431
  function queryLexical(conn, terms, opts, state) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/lexical.ts"],"sourcesContent":["// D1: fts BM25 for ranking, contains() scans for exact substring / phrase verification /\n// unspaced scripts, JS excerpts for every doc (no snippet()).\nimport { SenseError } from '../../errors.ts';\nimport { UNSPACED_SCRIPTS } from '../../text/segment.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\nconst FIELDS = ['title', 'summary', 'text'] as const;\ntype Field = (typeof FIELDS)[number];\n// Mirrors sqlite's bm25(content, 10.0, 5.0, 1.0, ...) column-weight intent; DuckDB's\n// match_bm25 has no field_weights argument (verified 1.5.5), so each field is scored\n// separately and combined here.\nconst FIELD_WEIGHT: Record<Field, number> = { title: 10, summary: 5, text: 1 };\n\nconst UNSPACED_RUN = new RegExp(`[${UNSPACED_SCRIPTS}]`, 'u');\n\n// FTS5 operator syntax (sqlite.org/fts5.html sec. 3) that words/substrings below would\n// otherwise silently treat as literal terms instead of honoring (PRINCIPLES: no-silent-modes).\n// Checked against terms with quoted spans blanked -- those go through contains() and are\n// supported.\nconst FTS5_OPERATORS: Array<{ label: string; re: RegExp }> = [\n { label: 'prefix query', re: /[\\p{L}\\p{N}_]+\\*(?=\\s|$)/u },\n { label: 'boolean operator', re: /(?:^|\\s)(?:AND|OR|NOT)(?=\\s|$)/ },\n { label: 'NEAR operator', re: /(?:^|\\s)NEAR\\b/ },\n { label: 'initial-token operator', re: /(?:^|\\s)\\^\\S+/ },\n { label: 'column filter', re: /(?:^|\\s)[\\p{L}_]\\w*\\s*:/u },\n];\n\nfunction unsupportedOperator(terms: string): { label: string; token: string } | null {\n const withoutPhrases = terms.replace(/\"[^\"]*\"/g, ' ');\n for (const { label, re } of FTS5_OPERATORS) {\n const m = withoutPhrases.match(re);\n if (m) return { label, token: m[0].trim() };\n }\n return null;\n}\n\ninterface FtsIndexState {\n stale: boolean;\n}\n\n// A run whose script marks no word boundaries makes match_bm25's whitespace tokenizer index\n// the whole run as one token (same gap FTS5 has without the `_seg` sidecar), so such runs --\n// and any author-quoted phrase, any script -- go through contains() instead of the fts index.\nfunction splitTerms(terms: string): { words: string[]; substrings: string[] } {\n const words: string[] = [];\n const substrings: string[] = [];\n const withoutPhrases = terms.replace(/\"([^\"]*)\"/g, (_m, inner: string) => {\n const phrase = inner.trim();\n if (phrase.length === 0) return ' ';\n substrings.push(phrase);\n if (!UNSPACED_RUN.test(phrase)) for (const w of phrase.split(/\\s+/)) if (w.length > 0) words.push(w);\n return ' ';\n });\n for (const tok of withoutPhrases.split(/\\s+/)) {\n if (tok.length === 0) continue;\n if (UNSPACED_RUN.test(tok)) substrings.push(tok);\n else words.push(tok);\n }\n return { words, substrings };\n}\n\n// No incremental update (verified 1.5.5: PRAGMA create_fts_index is rebuild-only), so this\n// pays the full rebuild -- but only once per store instance's first lexical query, and only\n// when content actually changed since (see FtsIndexState / duckdb/store.ts's markStale()).\nasync function ensureFtsFresh(conn: Connection, state: FtsIndexState): Promise<void> {\n if (!state.stale) return;\n await conn.exec('INSTALL fts; LOAD fts;');\n await withTransaction(conn, async () => {\n // stopwords='none': sqlite's porter/unicode61 tokenizer never removes stopwords either\n // (verified 1.5.5), and the fts extension's default 571-word English list would otherwise\n // silently drop common query words (e.g. \"and\") from the index but not from match_bm25's\n // conjunctive gate, making a bare multi-word query that contains one match nothing.\n await conn.exec(`PRAGMA create_fts_index('content', 'path', 'title', 'summary', 'text', stopwords='none', overwrite=1)`);\n });\n state.stale = false;\n}\n\n// Per-word-term and per-substring-term SQL fragments, field-weighted the same way for both\n// (title 10 / summary 5 / text 1), plus the params in the exact left-to-right order they are\n// emitted -- positional `?` binding requires that order to match the assembled SQL text.\nfunction buildScoreAndGate(words: string[], substrings: string[]): { scoreSql: string; gateSql: string; params: unknown[] } {\n const scoreParts: string[] = [];\n const gateParts: string[] = [];\n const params: unknown[] = [];\n\n if (words.length > 0) {\n const wordQuery = words.join(' ');\n for (const field of FIELDS) {\n scoreParts.push(`${FIELD_WEIGHT[field]}.0 * COALESCE(fts_main_content.match_bm25(content.\"path\", ?, fields := '${field}', conjunctive := false), 0)`);\n params.push(wordQuery);\n }\n gateParts.push(`fts_main_content.match_bm25(content.\"path\", ?, conjunctive := true) IS NOT NULL`);\n params.push(wordQuery);\n }\n\n for (const raw of substrings) {\n const needle = raw.toLowerCase();\n for (const field of FIELDS) {\n scoreParts.push(`${FIELD_WEIGHT[field]}.0 * CAST(contains(lower(content.${field}), ?) AS INTEGER)`);\n params.push(needle);\n }\n gateParts.push(`contains(lower(content.title) || ' ' || lower(content.summary) || ' ' || lower(content.text), ?)`);\n params.push(needle);\n }\n\n return { scoreSql: scoreParts.join(' + '), gateSql: gateParts.join(' AND '), params };\n}\n\n// Ranked word-match query, scoped by the caller-built SQL fragments (same fragments sqlite's\n// queryLexical takes). `hit` is always NULL: no snippet() equivalent exists, so every row goes\n// through the caller's JS excerpt fallback (commands/search.ts) rather than a second excerpt path.\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions, state: FtsIndexState): Promise<LexicalHit[]> {\n const unsupported = unsupportedOperator(terms);\n if (unsupported !== null) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"duckdb\" does not implement FTS5's ${unsupported.label} (\"${unsupported.token}\") in this build; rephrase \"${terms.trim()}\" without it, or set \"store\" to \"sqlite\" in this tree's config to search it as written`);\n }\n const { whereJoin, whereCond, scopeCond, limit } = opts;\n const { words, substrings } = splitTerms(terms);\n if (words.length === 0 && substrings.length === 0) return [];\n\n if (words.length > 0) await ensureFtsFresh(conn, state);\n\n const { scoreSql, gateSql, params } = buildScoreAndGate(words, substrings);\n const sql = `SELECT path, NULL AS hit FROM (\n SELECT content.\"path\" AS path, (${scoreSql}) AS score\n FROM content\n ${whereJoin}\n WHERE ${gateSql}\n ${whereCond} ${scopeCond}\n ) sq ORDER BY score DESC, path LIMIT ?`;\n const stmt = await conn.prepare(sql);\n return (await stmt.all(...params, limit)) as unknown as LexicalHit[];\n}\n\n// One instance per store: `stale` starts true (a fresh connection cannot know whether an\n// on-disk fts schema still matches `content`), and store.ts's reconcile wrapper flips it back\n// to true whenever content changes, so the next query rebuilds before ranking against it.\nexport function createLexicalIndex(conn: Connection): { query: (terms: string, opts: LexicalQueryOptions) => Promise<LexicalHit[]>; markStale: () => void } {\n const state: FtsIndexState = { stale: true };\n return {\n query: (terms, opts) => queryLexical(conn, terms, opts, state),\n markStale: () => {\n state.stale = true;\n },\n };\n}\n"],"names":["createLexicalIndex","queryLexical","FIELDS","FIELD_WEIGHT","title","summary","text","UNSPACED_RUN","RegExp","UNSPACED_SCRIPTS","FTS5_OPERATORS","label","re","unsupportedOperator","terms","withoutPhrases","replace","m","match","token","trim","splitTerms","words","substrings","_m","inner","phrase","length","push","test","split","w","tok","ensureFtsFresh","conn","state","stale","exec","withTransaction","buildScoreAndGate","scoreParts","gateParts","params","wordQuery","join","field","raw","needle","toLowerCase","scoreSql","gateSql","opts","stmt","unsupported","whereJoin","whereCond","scopeCond","limit","sql","SenseError","prepare","all","query","markStale"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/lexical.ts"],"sourcesContent":["// D1: fts BM25 for ranking, contains() scans for exact substring / phrase verification /\n// unspaced scripts, JS excerpts for every doc (no snippet()).\nimport { SenseError } from '../../errors.ts';\nimport { hasUnspacedRun } from '../../text/segment.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\nconst FIELDS = ['title', 'summary', 'text'] as const;\ntype Field = (typeof FIELDS)[number];\n// Mirrors sqlite's bm25(content, 10.0, 5.0, 1.0, ...) column-weight intent; DuckDB's match_bm25 has no\n// field_weights argument (verified 1.5.5), so each field is scored separately and combined here.\nconst FIELD_WEIGHT: Record<Field, number> = { title: 10, summary: 5, text: 1 };\n\n// FTS5 operator syntax (sqlite.org/fts5.html sec. 3) that words/substrings below would otherwise silently\n// treat as literal terms (PRINCIPLES: no-silent-modes). Checked with quoted spans blanked; those go through contains() and are supported.\nconst FTS5_OPERATORS: Array<{ label: string; re: RegExp }> = [\n { label: 'prefix query', re: /[\\p{L}\\p{N}_]+\\*(?=\\s|$)/u },\n { label: 'boolean operator', re: /(?:^|\\s)(?:AND|OR|NOT)(?=\\s|$)/ },\n { label: 'NEAR operator', re: /(?:^|\\s)NEAR\\b/ },\n { label: 'initial-token operator', re: /(?:^|\\s)\\^\\S+/ },\n { label: 'column filter', re: /(?:^|\\s)[\\p{L}_]\\w*\\s*:/u },\n];\n\nfunction unsupportedOperator(terms: string): { label: string; token: string } | null {\n const withoutPhrases = terms.replace(/\"[^\"]*\"/g, ' ');\n for (const { label, re } of FTS5_OPERATORS) {\n const m = withoutPhrases.match(re);\n if (m) return { label, token: m[0].trim() };\n }\n return null;\n}\n\ninterface FtsIndexState {\n stale: boolean;\n}\n\n// A run whose script marks no word boundaries makes match_bm25's whitespace tokenizer index the whole run as one token\n// (same gap FTS5 has without the `_seg` sidecar), so such runs -- and any author-quoted phrase, any script -- go through contains() instead.\nfunction splitTerms(terms: string): { words: string[]; substrings: string[] } {\n const words: string[] = [];\n const substrings: string[] = [];\n const withoutPhrases = terms.replace(/\"([^\"]*)\"/g, (_m, inner: string) => {\n const phrase = inner.trim();\n if (phrase.length === 0) return ' ';\n substrings.push(phrase);\n if (!hasUnspacedRun(phrase)) for (const w of phrase.split(/\\s+/)) if (w.length > 0) words.push(w);\n return ' ';\n });\n for (const tok of withoutPhrases.split(/\\s+/)) {\n if (tok.length === 0) continue;\n if (hasUnspacedRun(tok)) substrings.push(tok);\n else words.push(tok);\n }\n return { words, substrings };\n}\n\n// No incremental update (verified 1.5.5: PRAGMA create_fts_index is rebuild-only), so this pays the full rebuild -- but only\n// once per store instance's first lexical query, and only when content actually changed since (FtsIndexState / store.ts's markStale()).\nasync function ensureFtsFresh(conn: Connection, state: FtsIndexState): Promise<void> {\n if (!state.stale) return;\n await conn.exec('INSTALL fts; LOAD fts;');\n await withTransaction(conn, async () => {\n // stopwords='none': sqlite's porter/unicode61 tokenizer never removes stopwords either (verified 1.5.5); the fts extension's\n // default 571-word list would otherwise drop common words (e.g. \"and\") from the index but not match_bm25's conjunctive gate, breaking multi-word matches.\n await conn.exec(`PRAGMA create_fts_index('content', 'path', 'title', 'summary', 'text', stopwords='none', overwrite=1)`);\n });\n state.stale = false;\n}\n\n// Field-weighted fragments (title 10 / summary 5 / text 1), with params emitted in the exact left-to-right order the assembled SQL needs for positional `?` binding.\n// score/gate params stay in separate arrays until joined at the end, since the SQL puts every score part before every gate part; sharing one array would shift binds once a query has both words and substrings (PRINCIPLES: no-silent-modes).\nfunction buildScoreAndGate(words: string[], substrings: string[]): { scoreSql: string; gateSql: string; params: unknown[] } {\n const scoreParts: string[] = [];\n const gateParts: string[] = [];\n const scoreParams: unknown[] = [];\n const gateParams: unknown[] = [];\n\n if (words.length > 0) {\n const wordQuery = words.join(' ');\n for (const field of FIELDS) {\n scoreParts.push(`${FIELD_WEIGHT[field]}.0 * COALESCE(fts_main_content.match_bm25(content.\"path\", ?, fields := '${field}', conjunctive := false), 0)`);\n scoreParams.push(wordQuery);\n }\n gateParts.push(`fts_main_content.match_bm25(content.\"path\", ?, conjunctive := true) IS NOT NULL`);\n gateParams.push(wordQuery);\n }\n\n for (const raw of substrings) {\n const needle = raw.toLowerCase();\n for (const field of FIELDS) {\n scoreParts.push(`${FIELD_WEIGHT[field]}.0 * CAST(contains(lower(content.${field}), ?) AS INTEGER)`);\n scoreParams.push(needle);\n }\n gateParts.push(`contains(lower(content.title) || ' ' || lower(content.summary) || ' ' || lower(content.text), ?)`);\n gateParams.push(needle);\n }\n\n return { scoreSql: scoreParts.join(' + '), gateSql: gateParts.join(' AND '), params: [...scoreParams, ...gateParams] };\n}\n\n// Ranked word-match query, scoped by the caller-built SQL fragments (same fragments sqlite's queryLexical takes).\n// `hit` is always NULL: no snippet() equivalent exists, so every row goes through the caller's JS excerpt fallback (commands/search.ts).\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions, state: FtsIndexState): Promise<LexicalHit[]> {\n const unsupported = unsupportedOperator(terms);\n if (unsupported !== null) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"duckdb\" does not implement FTS5's ${unsupported.label} (\"${unsupported.token}\") in this build; rephrase \"${terms.trim()}\" without it, or set \"store\" to \"sqlite\" in this tree's config to search it as written`);\n }\n const { whereJoin, whereCond, scopeCond, limit } = opts;\n const { words, substrings } = splitTerms(terms);\n if (words.length === 0 && substrings.length === 0) return [];\n\n if (words.length > 0) await ensureFtsFresh(conn, state);\n\n const { scoreSql, gateSql, params } = buildScoreAndGate(words, substrings);\n const sql = `SELECT path, NULL AS hit FROM (\n SELECT content.\"path\" AS path, (${scoreSql}) AS score\n FROM content\n ${whereJoin}\n WHERE ${gateSql}\n ${whereCond} ${scopeCond}\n ) sq ORDER BY score DESC, path LIMIT ?`;\n const stmt = await conn.prepare(sql);\n return (await stmt.all(...params, limit)) as unknown as LexicalHit[];\n}\n\n// One instance per store: `stale` starts true (a fresh connection can't know whether an on-disk fts schema matches `content`),\n// and store.ts's reconcile wrapper flips it back to true whenever content changes, so the next query rebuilds first.\nexport function createLexicalIndex(conn: Connection): { query: (terms: string, opts: LexicalQueryOptions) => Promise<LexicalHit[]>; markStale: () => void } {\n const state: FtsIndexState = { stale: true };\n return {\n query: (terms, opts) => queryLexical(conn, terms, opts, state),\n markStale: () => {\n state.stale = true;\n },\n };\n}\n"],"names":["createLexicalIndex","queryLexical","FIELDS","FIELD_WEIGHT","title","summary","text","FTS5_OPERATORS","label","re","unsupportedOperator","terms","withoutPhrases","replace","m","match","token","trim","splitTerms","words","substrings","_m","inner","phrase","length","push","hasUnspacedRun","split","w","tok","ensureFtsFresh","conn","state","stale","exec","withTransaction","buildScoreAndGate","scoreParts","gateParts","scoreParams","gateParams","wordQuery","join","field","raw","needle","toLowerCase","scoreSql","gateSql","params","opts","stmt","unsupported","whereJoin","whereCond","scopeCond","limit","sql","SenseError","prepare","all","query","markStale"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/native.ts"],"sourcesContent":["import { loadOrInstall, type NativeDescriptor, packageNodeModules } from '../native.ts';\n\nexport const DUCKDB_PACKAGE = '@duckdb/node-api';\n\nconst DUCKDB: NativeDescriptor = { store: 'duckdb', pkg: DUCKDB_PACKAGE, sizeHint: '~110MB' };\n\nlet duckdbApiPromise: Promise<typeof import('@duckdb/node-api')> | undefined;\n\n// The one accessor every consumer of @duckdb/node-api (open.ts, sql-functions.ts, vectors.ts)\n// must go through, rather than each doing its own `import('@duckdb/node-api')`: once a fresh\n// install has happened in this process, Node keeps returning that pre-install \"not found\" miss\n// to any NEW bare-specifier lookup of the same path, so a second independent import elsewhere\n// in the process fails even though the package is now genuinely on disk (the same stale-cache\n// problem loadOrInstall works around for its own retry). Caching the resolved module here means\n// only the first caller ever resolves the bare specifier at all.\nexport function duckdbApi(): Promise<typeof import('@duckdb/node-api')> {\n if (!duckdbApiPromise) duckdbApiPromise = loadOrInstall<typeof import('@duckdb/node-api')>(DUCKDB, packageNodeModules());\n return duckdbApiPromise;\n}\n"],"names":["DUCKDB_PACKAGE","duckdbApi","DUCKDB","store","pkg","sizeHint","duckdbApiPromise","loadOrInstall","packageNodeModules"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAaGC;eAAAA;;;wBAfyD;AAElE,IAAMD,iBAAiB;AAE9B,IAAME,SAA2B;IAAEC,OAAO;IAAUC,KAAKJ;IAAgBK,UAAU;AAAS;AAE5F,IAAIC;AASG,SAASL;IACd,IAAI,CAACK,kBAAkBA,mBAAmBC,IAAAA,uBAAa,EAAoCL,QAAQM,IAAAA,4BAAkB;IACrH,OAAOF;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/native.ts"],"sourcesContent":["import { loadOrInstall, type NativeDescriptor, packageNodeModules } from '../native.ts';\n\nexport const DUCKDB_PACKAGE = '@duckdb/node-api';\n\nconst DUCKDB: NativeDescriptor = { store: 'duckdb', pkg: DUCKDB_PACKAGE, sizeHint: '~110MB' };\n\nlet duckdbApiPromise: Promise<typeof import('@duckdb/node-api')> | undefined;\n\n// The one accessor every consumer of @duckdb/node-api must go through: after a fresh install in this process, Node keeps returning\n// the pre-install \"not found\" miss to any new bare-specifier lookup of the same path, so caching the resolved module here means only the first caller ever resolves it.\nexport function duckdbApi(): Promise<typeof import('@duckdb/node-api')> {\n if (!duckdbApiPromise) duckdbApiPromise = loadOrInstall<typeof import('@duckdb/node-api')>(DUCKDB, packageNodeModules());\n return duckdbApiPromise;\n}\n"],"names":["DUCKDB_PACKAGE","duckdbApi","DUCKDB","store","pkg","sizeHint","duckdbApiPromise","loadOrInstall","packageNodeModules"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAQGC;eAAAA;;;wBAVyD;AAElE,IAAMD,iBAAiB;AAE9B,IAAME,SAA2B;IAAEC,OAAO;IAAUC,KAAKJ;IAAgBK,UAAU;AAAS;AAE5F,IAAIC;AAIG,SAASL;IACd,IAAI,CAACK,kBAAkBA,mBAAmBC,IAAAA,uBAAa,EAAoCL,QAAQM,IAAAA,4BAAkB;IACrH,OAAOF;AACT"}
@@ -22,11 +22,12 @@ _export(exports, {
22
22
  var _nodefs = require("node:fs");
23
23
  var _nodepath = require("node:path");
24
24
  var _indexts = require("../../config/index.js");
25
+ var _handoffts = require("../../embed/handoff.js");
25
26
  var _typests = require("../../embed/types.js");
26
27
  var _errorsts = require("../../errors.js");
27
28
  var _indexts1 = require("../../features/index.js");
29
+ var _cachets = require("../cache.js");
28
30
  var _sharedts = require("../shared.js");
29
- var _opents = require("../sqlite/open.js");
30
31
  var _connectionts = require("./connection.js");
31
32
  var _nativets = require("./native.js");
32
33
  var _reconcilets = require("./reconcile.js");
@@ -165,11 +166,8 @@ function _ts_generator(thisArg, body) {
165
166
  }
166
167
  var DB_FILENAME = 'cache.duckdb';
167
168
  var SCHEMA_VERSION = '2';
168
- // `content` is a plain table (not FTS-virtual): D1 builds DuckDB's fts index over it lazily,
169
- // only when a lexical query actually runs (see duckdb/lexical.ts), and reads it directly for
170
- // contains() substring/phrase/unspaced-script verification either way. No tokenizer resolution
171
- // -- this store always uses the fts extension's default (porter) stemmer, unlike sqlite's
172
- // configurable `content.tokenize` (not read here; D1 does not extend that knob to DuckDB).
169
+ // `content` is a plain table (not FTS-virtual): the fts index is built over it lazily, only when a lexical query runs (lexical.ts), and read directly for contains() verification either way.
170
+ // No tokenizer resolution: this store always uses the fts extension's default (porter) stemmer; sqlite's configurable `content.tokenize` is not read here.
173
171
  function ensureSchema(conn, cfg) {
174
172
  return _async_to_generator(function() {
175
173
  var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, feature, err;
@@ -360,10 +358,8 @@ function connect(cfg) {
360
358
  })
361
359
  ];
362
360
  case 5:
363
- // On-disk files default to an older storage format for cross-version compatibility, which
364
- // rejects VARIANT columns ("VARIANT columns are not supported in storage versions prior to
365
- // v1.5.0"); this store's dynamic frontmatter columns need VARIANT (see reconcile.ts), so
366
- // the format floor is pinned explicitly. :memory: databases are unaffected either way.
361
+ // On-disk files default to an older storage format for cross-version compatibility, which rejects VARIANT columns ("VARIANT
362
+ // columns are not supported in storage versions prior to v1.5.0"); this store's dynamic frontmatter columns need VARIANT (reconcile.ts), so the floor is pinned explicitly.
367
363
  instance = _state.sent();
368
364
  return [
369
365
  4,
@@ -427,7 +423,7 @@ function connect(cfg) {
427
423
  closed = true;
428
424
  duckdb.disconnectSync();
429
425
  instance.closeSync();
430
- (0, _opents.clearCache)(cfg);
426
+ (0, _cachets.clearCache)(cfg);
431
427
  return [
432
428
  2,
433
429
  connect(cfg)
@@ -474,7 +470,7 @@ function connect(cfg) {
474
470
  }
475
471
  function openDuckdb(cfg) {
476
472
  return _async_to_generator(function() {
477
- var _ref, instance, duckdb, conn, resolvedCfg, dbPath, parsed, warnings;
473
+ var _ref, instance, duckdb, conn, resolvedCfg, dbPath, parsed, warnings, store;
478
474
  return _ts_generator(this, function(_state) {
479
475
  switch(_state.label){
480
476
  case 0:
@@ -484,10 +480,13 @@ function openDuckdb(cfg) {
484
480
  ];
485
481
  case 1:
486
482
  _ref = _state.sent(), instance = _ref.instance, duckdb = _ref.duckdb, conn = _ref.conn, resolvedCfg = _ref.cfg, dbPath = _ref.dbPath, parsed = _ref.parsed, warnings = _ref.warnings;
483
+ store = (0, _storets.createStore)(instance, duckdb, conn, resolvedCfg, resolvedCfg.baseDir);
484
+ // reconcile ran before this object existed, so its chunk text is keyed by the connection.
485
+ (0, _handoffts.rekeyChunkText)(conn, store);
487
486
  return [
488
487
  2,
489
488
  {
490
- store: (0, _storets.createStore)(instance, duckdb, conn, resolvedCfg, resolvedCfg.baseDir),
489
+ store: store,
491
490
  cfg: resolvedCfg,
492
491
  dbPath: dbPath,
493
492
  parsed: parsed,
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/open.ts"],"sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { featureSignature, STATE_DIR } from '../../config/index.ts';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport { clearCache } from '../sqlite/open.ts';\nimport type { Connection, Store } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { DUCKDB_PACKAGE, duckdbApi } from './native.ts';\nimport { reconcile } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.duckdb';\n// Independent of sqlite's SCHEMA_VERSION -- the two stores' cache shapes evolve separately\n// (VARIANT frontmatter columns instead of untyped ones), and the store name already joins the\n// feature signature (see featureSignature), so a config's `store` key switching cleanly\n// rebuilds rather than reusing the other engine's stale cache shape.\nexport const SCHEMA_VERSION = '2';\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// `content` is a plain table (not FTS-virtual): D1 builds DuckDB's fts index over it lazily,\n// only when a lexical query actually runs (see duckdb/lexical.ts), and reads it directly for\n// contains() substring/phrase/unspaced-script verification either way. No tokenizer resolution\n// -- this store always uses the fts extension's default (porter) stemmer, unlike sqlite's\n// configurable `content.tokenize` (not read here; D1 does not extend that knob to DuckDB).\nasync function ensureSchema(conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" DOUBLE, \"_ctime\" DOUBLE, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT)`);\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)) {\n // Native FLOAT[STORE_DIMS] instead of the embed feature's engine-neutral BLOB+scale DDL\n // (see duckdb/vectors.ts). `scale` is kept, unused, so the feature's shared reconcile-time\n // INSERT/DELETE (src/features/embed.ts) still names a column that exists on both stores.\n if (feature.name === 'embed') {\n await conn.exec(`CREATE TABLE IF NOT EXISTS embeddings (\"path\" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector FLOAT[${STORE_DIMS}], PRIMARY KEY (\"path\", chunk))`);\n continue;\n }\n await feature.schema(conn);\n }\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<{ instance: DuckDBInstance; duckdb: DuckDBConnection; conn: Connection; cfg: ResolvedConfig; dbPath: string; parsed: number; warnings: string[] }> {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n // Dynamic, not a top-level import: sqlite trees must never even attempt to resolve this\n // optional peer dependency, so nothing in this store's module graph imports it as a value\n // until a duckdb tree is actually opened (types-only imports elsewhere are erased and cost\n // nothing either way). Installed on first use if missing, and shared with sql-functions.ts\n // and vectors.ts (see native.ts's duckdbApi).\n let DuckDBInstance: typeof import('@duckdb/node-api').DuckDBInstance;\n try {\n ({ DuckDBInstance } = await duckdbApi());\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"duckdb\" needs the ${DUCKDB_PACKAGE} package (${(err as Error).message})`);\n }\n\n let duckdb: DuckDBConnection;\n let instance: DuckDBInstance | undefined;\n try {\n // On-disk files default to an older storage format for cross-version compatibility, which\n // rejects VARIANT columns (\"VARIANT columns are not supported in storage versions prior to\n // v1.5.0\"); this store's dynamic frontmatter columns need VARIANT (see reconcile.ts), so\n // the format floor is pinned explicitly. :memory: databases are unaffected either way.\n instance = await DuckDBInstance.create(dbPath, { storage_compatibility_version: 'v1.5.0' });\n duckdb = await instance.connect();\n } catch (err) {\n // create() may have succeeded before connect() failed: close it, or its WAL stays open.\n instance?.closeSync();\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"duckdb\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n // A throw below (schema, reconcile's COLUMN_LIMIT) would leak the open instance, whose WAL\n // then locks the .duckdb file undeletable on Windows. closed marks the rebuild branch.\n let closed = false;\n try {\n await registerFunctions(duckdb);\n const conn = createConnection(duckdb);\n\n await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const wantFeatures = featureSignature(cfg, FEATURES);\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Reconcile only reparses changed files, so a stale cache can't be patched incrementally --\n // rebuild instead, same as sqlite's open() (see its comment for why this is announced).\n const reason = version !== null && version !== SCHEMA_VERSION ? 'cache format changed (new sensemaking version)' : 'config change (features, tokenizer, or presets)';\n console.error(`sense: ${reason}; rebuilding the index`);\n // Close before clearCache deletes the files underneath the still-open instance.\n closed = true;\n duckdb.disconnectSync();\n instance.closeSync();\n clearCache(cfg);\n return connect(cfg);\n }\n\n await ensureSchema(conn, cfg);\n\n const { parsed, warnings } = await reconcile(conn, cfg, cfg.baseDir);\n return { instance, duckdb, conn, cfg, dbPath, parsed, warnings };\n } catch (err) {\n if (!closed) {\n duckdb.disconnectSync();\n instance?.closeSync();\n }\n throw err;\n }\n}\n\nexport async function openDuckdb(cfg: ResolvedConfig): Promise<OpenResult> {\n const { instance, duckdb, conn, cfg: resolvedCfg, dbPath, parsed, warnings } = await connect(cfg);\n return { store: createStore(instance, duckdb, conn, resolvedCfg, resolvedCfg.baseDir), cfg: resolvedCfg, dbPath, parsed, warnings };\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","openDuckdb","ensureSchema","conn","cfg","feature","exec","activeFeatures","name","STORE_DIMS","schema","getMeta","setMeta","featureSignature","FEATURES","connect","stateDir","dbPath","DuckDBInstance","err","duckdb","instance","closed","version","features","wantFeatures","reason","parsed","warnings","join","baseDir","STATE_DIR","mkdirSync","recursive","duckdbApi","SenseError","DUCKDB_PACKAGE","message","create","storage_compatibility_version","closeSync","registerFunctions","createConnection","console","error","disconnectSync","clearCache","reconcile","resolvedCfg","store","createStore"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/open.ts"],"sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { featureSignature, STATE_DIR } from '../../config/index.ts';\nimport { rekeyChunkText } from '../../embed/handoff.ts';\nimport { STORE_DIMS } from '../../embed/types.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 type { Connection, Store } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { DUCKDB_PACKAGE, duckdbApi } from './native.ts';\nimport { reconcile } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.duckdb';\n// Independent of sqlite's SCHEMA_VERSION -- the two stores' cache shapes evolve separately (VARIANT frontmatter columns vs untyped).\n// The store name already joins the feature signature, so switching a config's `store` key rebuilds rather than reusing the other engine's cache.\nexport const SCHEMA_VERSION = '2';\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\n// `content` is a plain table (not FTS-virtual): the fts index is built over it lazily, only when a lexical query runs (lexical.ts), and read directly for contains() verification either way.\n// No tokenizer resolution: this store always uses the fts extension's default (porter) stemmer; sqlite's configurable `content.tokenize` is not read here.\nasync function ensureSchema(conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" DOUBLE, \"_ctime\" DOUBLE, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT)`);\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)) {\n // Native FLOAT[STORE_DIMS] instead of the embed feature's engine-neutral BLOB+scale DDL (vectors.ts). `scale` is kept, unused,\n // so the feature's shared reconcile-time INSERT/DELETE (features/embed.ts) names a column that exists on both stores.\n if (feature.name === 'embed') {\n await conn.exec(`CREATE TABLE IF NOT EXISTS embeddings (\"path\" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector FLOAT[${STORE_DIMS}], PRIMARY KEY (\"path\", chunk))`);\n continue;\n }\n await feature.schema(conn);\n }\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<{ instance: DuckDBInstance; duckdb: DuckDBConnection; conn: Connection; cfg: ResolvedConfig; dbPath: string; parsed: number; warnings: string[] }> {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n // Dynamic, not a top-level import: sqlite trees must never attempt to resolve this optional peer dependency, so nothing imports\n // it as a value until a duckdb tree opens (types-only imports are erased). Installed on first use if missing, shared with sql-functions.ts and vectors.ts via native.ts's duckdbApi.\n let DuckDBInstance: typeof import('@duckdb/node-api').DuckDBInstance;\n try {\n ({ DuckDBInstance } = await duckdbApi());\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"duckdb\" needs the ${DUCKDB_PACKAGE} package (${(err as Error).message})`);\n }\n\n let duckdb: DuckDBConnection;\n let instance: DuckDBInstance | undefined;\n try {\n // On-disk files default to an older storage format for cross-version compatibility, which rejects VARIANT columns (\"VARIANT\n // columns are not supported in storage versions prior to v1.5.0\"); this store's dynamic frontmatter columns need VARIANT (reconcile.ts), so the floor is pinned explicitly.\n instance = await DuckDBInstance.create(dbPath, { storage_compatibility_version: 'v1.5.0' });\n duckdb = await instance.connect();\n } catch (err) {\n // create() may have succeeded before connect() failed: close it, or its WAL stays open.\n instance?.closeSync();\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"duckdb\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n // A throw below (schema, reconcile's COLUMN_LIMIT) would leak the open instance, whose WAL\n // then locks the .duckdb file undeletable on Windows. closed marks the rebuild branch.\n let closed = false;\n try {\n await registerFunctions(duckdb);\n const conn = createConnection(duckdb);\n\n await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const wantFeatures = featureSignature(cfg, FEATURES);\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n // Reconcile only reparses changed files, so a stale cache can't be patched incrementally --\n // rebuild instead, same as sqlite's open() (see its comment for why this is announced).\n const reason = version !== null && version !== SCHEMA_VERSION ? 'cache format changed (new sensemaking version)' : 'config change (features, tokenizer, or presets)';\n console.error(`sense: ${reason}; rebuilding the index`);\n // Close before clearCache deletes the files underneath the still-open instance.\n closed = true;\n duckdb.disconnectSync();\n instance.closeSync();\n clearCache(cfg);\n return connect(cfg);\n }\n\n await ensureSchema(conn, cfg);\n\n const { parsed, warnings } = await reconcile(conn, cfg, cfg.baseDir);\n return { instance, duckdb, conn, cfg, dbPath, parsed, warnings };\n } catch (err) {\n if (!closed) {\n duckdb.disconnectSync();\n instance?.closeSync();\n }\n throw err;\n }\n}\n\nexport async function openDuckdb(cfg: ResolvedConfig): Promise<OpenResult> {\n const { instance, duckdb, conn, cfg: resolvedCfg, dbPath, parsed, warnings } = await connect(cfg);\n const store = createStore(instance, duckdb, 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"],"names":["DB_FILENAME","SCHEMA_VERSION","openDuckdb","ensureSchema","conn","cfg","feature","exec","activeFeatures","name","STORE_DIMS","schema","getMeta","setMeta","featureSignature","FEATURES","connect","stateDir","dbPath","DuckDBInstance","err","duckdb","instance","closed","version","features","wantFeatures","reason","parsed","warnings","join","baseDir","STATE_DIR","mkdirSync","recursive","duckdbApi","SenseError","DUCKDB_PACKAGE","message","create","storage_compatibility_version","closeSync","registerFunctions","createConnection","console","error","disconnectSync","clearCache","reconcile","resolvedCfg","store","createStore","rekeyChunkText"],"mappings":"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"}