sensemaking 0.19.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/README.md +0 -1
  2. package/dist/cjs/chunk/extract.d.cts +1 -0
  3. package/dist/cjs/chunk/extract.d.ts +1 -0
  4. package/dist/cjs/chunk/extract.js +23 -5
  5. package/dist/cjs/chunk/extract.js.map +1 -1
  6. package/dist/cjs/chunk/index.d.cts +1 -1
  7. package/dist/cjs/chunk/index.d.ts +1 -1
  8. package/dist/cjs/chunk/index.js +3 -0
  9. package/dist/cjs/chunk/index.js.map +1 -1
  10. package/dist/cjs/config/access.d.cts +0 -1
  11. package/dist/cjs/config/access.d.ts +0 -1
  12. package/dist/cjs/config/access.js +10 -17
  13. package/dist/cjs/config/access.js.map +1 -1
  14. package/dist/cjs/config/index.d.cts +1 -1
  15. package/dist/cjs/config/index.d.ts +1 -1
  16. package/dist/cjs/config/index.js +0 -3
  17. package/dist/cjs/config/index.js.map +1 -1
  18. package/dist/cjs/config/types.d.cts +0 -3
  19. package/dist/cjs/config/types.d.ts +0 -3
  20. package/dist/cjs/config/types.js.map +1 -1
  21. package/dist/cjs/config/validate.js +6 -24
  22. package/dist/cjs/config/validate.js.map +1 -1
  23. package/dist/cjs/errors.d.cts +1 -1
  24. package/dist/cjs/errors.d.ts +1 -1
  25. package/dist/cjs/errors.js.map +1 -1
  26. package/dist/cjs/features/links.js +4 -5
  27. package/dist/cjs/features/links.js.map +1 -1
  28. package/dist/cjs/features/sections.js +4 -1
  29. package/dist/cjs/features/sections.js.map +1 -1
  30. package/dist/cjs/scan/pool.d.cts +15 -0
  31. package/dist/cjs/scan/pool.d.ts +15 -0
  32. package/dist/cjs/scan/pool.js +263 -0
  33. package/dist/cjs/scan/pool.js.map +1 -0
  34. package/dist/cjs/scan/reparse.d.cts +2 -0
  35. package/dist/cjs/scan/reparse.d.ts +2 -0
  36. package/dist/cjs/scan/reparse.js +15 -76
  37. package/dist/cjs/scan/reparse.js.map +1 -1
  38. package/dist/cjs/store/builder.d.cts +19 -0
  39. package/dist/cjs/store/builder.d.ts +19 -0
  40. package/dist/cjs/store/builder.js +618 -0
  41. package/dist/cjs/store/builder.js.map +1 -0
  42. package/dist/cjs/store/duckdb/lexical.d.cts +1 -0
  43. package/dist/cjs/store/duckdb/lexical.d.ts +1 -0
  44. package/dist/cjs/store/duckdb/lexical.js +21 -5
  45. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  46. package/dist/cjs/store/duckdb/open.d.cts +9 -8
  47. package/dist/cjs/store/duckdb/open.d.ts +9 -8
  48. package/dist/cjs/store/duckdb/open.js +77 -123
  49. package/dist/cjs/store/duckdb/open.js.map +1 -1
  50. package/dist/cjs/store/duckdb/reconcile.js +34 -5
  51. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  52. package/dist/cjs/store/duckdb/store.d.cts +1 -2
  53. package/dist/cjs/store/duckdb/store.d.ts +1 -2
  54. package/dist/cjs/store/duckdb/store.js +1 -26
  55. package/dist/cjs/store/duckdb/store.js.map +1 -1
  56. package/dist/cjs/store/embed-scope.d.cts +2 -0
  57. package/dist/cjs/store/embed-scope.d.ts +2 -0
  58. package/dist/cjs/store/embed-scope.js +85 -0
  59. package/dist/cjs/store/embed-scope.js.map +1 -0
  60. package/dist/cjs/store/feature-scope.d.cts +8 -0
  61. package/dist/cjs/store/feature-scope.d.ts +8 -0
  62. package/dist/cjs/store/feature-scope.js +107 -0
  63. package/dist/cjs/store/feature-scope.js.map +1 -0
  64. package/dist/cjs/store/index.d.cts +8 -3
  65. package/dist/cjs/store/index.d.ts +8 -3
  66. package/dist/cjs/store/index.js +36 -7
  67. package/dist/cjs/store/index.js.map +1 -1
  68. package/dist/cjs/store/open.d.cts +15 -0
  69. package/dist/cjs/store/open.d.ts +15 -0
  70. package/dist/cjs/store/open.js +608 -0
  71. package/dist/cjs/store/open.js.map +1 -0
  72. package/dist/cjs/store/preset-scope.d.cts +3 -0
  73. package/dist/cjs/store/preset-scope.d.ts +3 -0
  74. package/dist/cjs/store/preset-scope.js +293 -0
  75. package/dist/cjs/store/preset-scope.js.map +1 -0
  76. package/dist/cjs/store/reconcile.d.cts +2 -1
  77. package/dist/cjs/store/reconcile.d.ts +2 -1
  78. package/dist/cjs/store/reconcile.js +175 -179
  79. package/dist/cjs/store/reconcile.js.map +1 -1
  80. package/dist/cjs/store/{sqlite/signature.d.cts → signature.d.cts} +0 -3
  81. package/dist/cjs/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
  82. package/dist/cjs/store/signature.js +164 -0
  83. package/dist/cjs/store/signature.js.map +1 -0
  84. package/dist/cjs/store/sqlite/lexical.d.cts +1 -1
  85. package/dist/cjs/store/sqlite/lexical.d.ts +1 -1
  86. package/dist/cjs/store/sqlite/lexical.js +2 -2
  87. package/dist/cjs/store/sqlite/lexical.js.map +1 -1
  88. package/dist/cjs/store/sqlite/open.d.cts +8 -9
  89. package/dist/cjs/store/sqlite/open.d.ts +8 -9
  90. package/dist/cjs/store/sqlite/open.js +82 -311
  91. package/dist/cjs/store/sqlite/open.js.map +1 -1
  92. package/dist/cjs/store/sqlite/reconcile.d.cts +1 -1
  93. package/dist/cjs/store/sqlite/reconcile.d.ts +1 -1
  94. package/dist/cjs/store/sqlite/reconcile.js +78 -16
  95. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  96. package/dist/cjs/store/sqlite/sql-functions.d.cts +1 -1
  97. package/dist/cjs/store/sqlite/sql-functions.d.ts +1 -1
  98. package/dist/cjs/store/sqlite/sql-functions.js +3 -3
  99. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
  100. package/dist/cjs/store/sqlite/store.d.cts +1 -2
  101. package/dist/cjs/store/sqlite/store.d.ts +1 -2
  102. package/dist/cjs/store/sqlite/store.js +2 -15
  103. package/dist/cjs/store/sqlite/store.js.map +1 -1
  104. package/dist/cjs/store/turso/open.d.cts +6 -11
  105. package/dist/cjs/store/turso/open.d.ts +6 -11
  106. package/dist/cjs/store/turso/open.js +85 -171
  107. package/dist/cjs/store/turso/open.js.map +1 -1
  108. package/dist/cjs/store/turso/reconcile.d.cts +2 -0
  109. package/dist/cjs/store/turso/reconcile.d.ts +2 -0
  110. package/dist/cjs/store/turso/reconcile.js +88 -8
  111. package/dist/cjs/store/turso/reconcile.js.map +1 -1
  112. package/dist/cjs/store/turso/store.d.cts +1 -2
  113. package/dist/cjs/store/turso/store.d.ts +1 -2
  114. package/dist/cjs/store/turso/store.js +1 -13
  115. package/dist/cjs/store/turso/store.js.map +1 -1
  116. package/dist/cjs/store/types.d.cts +16 -6
  117. package/dist/cjs/store/types.d.ts +16 -6
  118. package/dist/cjs/store/types.js.map +1 -1
  119. package/dist/cjs/text/strip.js +3 -6
  120. package/dist/cjs/text/strip.js.map +1 -1
  121. package/dist/cjs/watch.js +28 -16
  122. package/dist/cjs/watch.js.map +1 -1
  123. package/dist/esm/chunk/extract.d.ts +1 -0
  124. package/dist/esm/chunk/extract.js +10 -2
  125. package/dist/esm/chunk/extract.js.map +1 -1
  126. package/dist/esm/chunk/index.d.ts +1 -1
  127. package/dist/esm/chunk/index.js +1 -1
  128. package/dist/esm/chunk/index.js.map +1 -1
  129. package/dist/esm/config/access.d.ts +0 -1
  130. package/dist/esm/config/access.js +12 -17
  131. package/dist/esm/config/access.js.map +1 -1
  132. package/dist/esm/config/index.d.ts +1 -1
  133. package/dist/esm/config/index.js +1 -1
  134. package/dist/esm/config/index.js.map +1 -1
  135. package/dist/esm/config/types.d.ts +0 -3
  136. package/dist/esm/config/types.js.map +1 -1
  137. package/dist/esm/config/validate.js +6 -22
  138. package/dist/esm/config/validate.js.map +1 -1
  139. package/dist/esm/errors.d.ts +1 -1
  140. package/dist/esm/errors.js.map +1 -1
  141. package/dist/esm/features/links.js +3 -4
  142. package/dist/esm/features/links.js.map +1 -1
  143. package/dist/esm/features/sections.js +4 -1
  144. package/dist/esm/features/sections.js.map +1 -1
  145. package/dist/esm/scan/pool.d.ts +15 -0
  146. package/dist/esm/scan/pool.js +77 -0
  147. package/dist/esm/scan/pool.js.map +1 -0
  148. package/dist/esm/scan/reparse.d.ts +2 -0
  149. package/dist/esm/scan/reparse.js +11 -58
  150. package/dist/esm/scan/reparse.js.map +1 -1
  151. package/dist/esm/store/builder.d.ts +19 -0
  152. package/dist/esm/store/builder.js +163 -0
  153. package/dist/esm/store/builder.js.map +1 -0
  154. package/dist/esm/store/duckdb/lexical.d.ts +1 -0
  155. package/dist/esm/store/duckdb/lexical.js +22 -9
  156. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  157. package/dist/esm/store/duckdb/open.d.ts +9 -8
  158. package/dist/esm/store/duckdb/open.js +36 -58
  159. package/dist/esm/store/duckdb/open.js.map +1 -1
  160. package/dist/esm/store/duckdb/reconcile.js +15 -3
  161. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  162. package/dist/esm/store/duckdb/store.d.ts +1 -2
  163. package/dist/esm/store/duckdb/store.js +2 -11
  164. package/dist/esm/store/duckdb/store.js.map +1 -1
  165. package/dist/esm/store/embed-scope.d.ts +2 -0
  166. package/dist/esm/store/embed-scope.js +28 -0
  167. package/dist/esm/store/embed-scope.js.map +1 -0
  168. package/dist/esm/store/feature-scope.d.ts +8 -0
  169. package/dist/esm/store/feature-scope.js +49 -0
  170. package/dist/esm/store/feature-scope.js.map +1 -0
  171. package/dist/esm/store/index.d.ts +8 -3
  172. package/dist/esm/store/index.js +22 -7
  173. package/dist/esm/store/index.js.map +1 -1
  174. package/dist/esm/store/open.d.ts +15 -0
  175. package/dist/esm/store/open.js +187 -0
  176. package/dist/esm/store/open.js.map +1 -0
  177. package/dist/esm/store/preset-scope.d.ts +3 -0
  178. package/dist/esm/store/preset-scope.js +77 -0
  179. package/dist/esm/store/preset-scope.js.map +1 -0
  180. package/dist/esm/store/reconcile.d.ts +2 -1
  181. package/dist/esm/store/reconcile.js +33 -6
  182. package/dist/esm/store/reconcile.js.map +1 -1
  183. package/dist/esm/store/{sqlite/signature.d.ts → signature.d.ts} +0 -3
  184. package/dist/esm/store/signature.js +37 -0
  185. package/dist/esm/store/signature.js.map +1 -0
  186. package/dist/esm/store/sqlite/lexical.d.ts +1 -1
  187. package/dist/esm/store/sqlite/lexical.js +4 -4
  188. package/dist/esm/store/sqlite/lexical.js.map +1 -1
  189. package/dist/esm/store/sqlite/open.d.ts +8 -9
  190. package/dist/esm/store/sqlite/open.js +41 -147
  191. package/dist/esm/store/sqlite/open.js.map +1 -1
  192. package/dist/esm/store/sqlite/reconcile.d.ts +1 -1
  193. package/dist/esm/store/sqlite/reconcile.js +14 -14
  194. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  195. package/dist/esm/store/sqlite/sql-functions.d.ts +1 -1
  196. package/dist/esm/store/sqlite/sql-functions.js +3 -3
  197. package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
  198. package/dist/esm/store/sqlite/store.d.ts +1 -2
  199. package/dist/esm/store/sqlite/store.js +3 -9
  200. package/dist/esm/store/sqlite/store.js.map +1 -1
  201. package/dist/esm/store/turso/open.d.ts +6 -11
  202. package/dist/esm/store/turso/open.js +34 -78
  203. package/dist/esm/store/turso/open.js.map +1 -1
  204. package/dist/esm/store/turso/reconcile.d.ts +2 -0
  205. package/dist/esm/store/turso/reconcile.js +18 -3
  206. package/dist/esm/store/turso/reconcile.js.map +1 -1
  207. package/dist/esm/store/turso/store.d.ts +1 -2
  208. package/dist/esm/store/turso/store.js +2 -7
  209. package/dist/esm/store/turso/store.js.map +1 -1
  210. package/dist/esm/store/types.d.ts +16 -6
  211. package/dist/esm/store/types.js.map +1 -1
  212. package/dist/esm/text/strip.js +3 -4
  213. package/dist/esm/text/strip.js.map +1 -1
  214. package/dist/esm/watch.js +5 -3
  215. package/dist/esm/watch.js.map +1 -1
  216. package/package.json +4 -3
  217. package/schema.json +0 -11
  218. package/dist/cjs/store/sqlite/signature.js +0 -380
  219. package/dist/cjs/store/sqlite/signature.js.map +0 -1
  220. package/dist/esm/store/sqlite/signature.js +0 -58
  221. package/dist/esm/store/sqlite/signature.js.map +0 -1
@@ -1,12 +1,8 @@
1
- import { mkdirSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { featureSignature, STATE_DIR } from '../../config/index.js';
4
- import { rekeyChunkText } from '../../embed/handoff.js';
1
+ import { featureSignature } from '../../config/index.js';
5
2
  import { STORE_DIMS } from '../../embed/types.js';
6
3
  import { SenseError } from '../../errors.js';
7
4
  import { activeFeatures, FEATURES } from '../../features/index.js';
8
- import { clearCache } from '../cache.js';
9
- import { reconcile } from '../reconcile.js';
5
+ import { openWithDialect } from '../open.js';
10
6
  import { getMeta, setMeta } from '../shared.js';
11
7
  import { createConnection } from './connection.js';
12
8
  import { DUCKDB_PACKAGE, duckdbApi } from './native.js';
@@ -16,10 +12,10 @@ import { createStore } from './store.js';
16
12
  export const DB_FILENAME = 'cache.duckdb';
17
13
  // Independent of sqlite's SCHEMA_VERSION -- the two stores' cache shapes evolve separately (VARIANT frontmatter columns vs untyped).
18
14
  // The store name already joins the feature signature, so switching a config's `store` key rebuilds rather than reusing the other engine's cache.
19
- export const SCHEMA_VERSION = '2';
15
+ export const SCHEMA_VERSION = '3';
20
16
  // `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.
21
- // No tokenizer resolution: this store always uses the fts extension's default (porter) stemmer; sqlite's configurable `content.tokenize` is not read here.
22
- async function ensureSchema(conn, cfg) {
17
+ // No tokenizer resolution: this store always uses the fts extension's default (porter) stemmer.
18
+ async function ensureSchema(_handle, conn, cfg) {
23
19
  await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter ("path" TEXT PRIMARY KEY, "_mtime" DOUBLE, "_ctime" DOUBLE, "_size" INTEGER, "_parse_error" TEXT)`);
24
20
  await conn.exec(`CREATE TABLE IF NOT EXISTS content ("path" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT)`);
25
21
  await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files ("path" TEXT, preset TEXT, PRIMARY KEY ("path", preset))`);
@@ -36,12 +32,12 @@ async function ensureSchema(conn, cfg) {
36
32
  if (await getMeta(conn, 'schema_version') === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);
37
33
  if (await getMeta(conn, 'features') === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));
38
34
  }
39
- async function connect(cfg) {
40
- const stateDir = join(cfg.baseDir, STATE_DIR);
41
- mkdirSync(stateDir, {
42
- recursive: true
43
- });
44
- const dbPath = join(stateDir, DB_FILENAME);
35
+ // Order matters: the connection must be gone before the instance closes the WAL.
36
+ async function close(handle) {
37
+ handle.duckdb.disconnectSync();
38
+ handle.instance.closeSync();
39
+ }
40
+ async function connect(dbPath, _cfg) {
45
41
  // Dynamic, not a top-level import: sqlite trees must never attempt to resolve this optional peer dependency, so nothing imports
46
42
  // 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.
47
43
  let DuckDBInstance;
@@ -65,57 +61,39 @@ async function connect(cfg) {
65
61
  instance === null || instance === void 0 ? void 0 : instance.closeSync();
66
62
  throw new SenseError('STORE_DEPENDENCY_MISSING', `store "duckdb" failed to open ${dbPath}: ${err.message}`);
67
63
  }
68
- // A throw below (schema, reconcile's COLUMN_LIMIT) would leak the open instance, whose WAL
69
- // then locks the .duckdb file undeletable on Windows. closed marks the rebuild branch.
70
- let closed = false;
64
+ // A throw below would leak the open instance, whose WAL then locks the .duckdb file undeletable on Windows.
71
65
  try {
72
66
  await registerFunctions(duckdb);
73
67
  const conn = createConnection(duckdb);
74
- await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');
75
- const version = await getMeta(conn, 'schema_version');
76
- const features = await getMeta(conn, 'features');
77
- const wantFeatures = featureSignature(cfg, FEATURES);
78
- if (version !== null && version !== SCHEMA_VERSION || features !== null && features !== wantFeatures) {
79
- // Reconcile only reparses changed files, so a stale cache can't be patched incrementally --
80
- // rebuild instead, same as sqlite's open() (see its comment for why this is announced).
81
- const reason = version !== null && version !== SCHEMA_VERSION ? 'cache format changed (new sensemaking version)' : 'config change (features, tokenizer, or presets)';
82
- console.error(`sense: ${reason}; rebuilding the index`);
83
- // Close before clearCache deletes the files underneath the still-open instance.
84
- closed = true;
85
- duckdb.disconnectSync();
86
- instance.closeSync();
87
- clearCache(cfg);
88
- return connect(cfg);
89
- }
90
- await ensureSchema(conn, cfg);
91
- const { parsed, warnings } = await reconcile(conn, cfg, cfg.baseDir, duckdbDialect);
92
68
  return {
93
- instance,
94
- duckdb,
95
- conn,
96
- cfg,
97
- dbPath,
98
- parsed,
99
- warnings
69
+ handle: {
70
+ instance,
71
+ duckdb
72
+ },
73
+ conn
100
74
  };
101
75
  } catch (err) {
102
- if (!closed) {
103
- duckdb.disconnectSync();
104
- instance === null || instance === void 0 ? void 0 : instance.closeSync();
105
- }
76
+ await close({
77
+ instance,
78
+ duckdb
79
+ });
106
80
  throw err;
107
81
  }
108
82
  }
83
+ // This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.
84
+ export const duckdbOpenDialect = {
85
+ filename: DB_FILENAME,
86
+ schemaVersion: SCHEMA_VERSION,
87
+ reconcileDialect: duckdbDialect,
88
+ connect,
89
+ close,
90
+ // duckdb's file lock spans the connection's life, and each platform words the refusal its own
91
+ // way: posix "Could not set lock on file ... Conflicting lock is held in <exe> (PID n)", Windows
92
+ // "Cannot open file ... being used by another process". Both are the same condition.
93
+ isLocked: (err)=>/Could not set lock on file|being used by another process/.test(err.message),
94
+ ensureSchema,
95
+ createStore: (handle, conn)=>createStore(handle.instance, handle.duckdb, conn)
96
+ };
109
97
  export async function openDuckdb(cfg) {
110
- const { instance, duckdb, conn, cfg: resolvedCfg, dbPath, parsed, warnings } = await connect(cfg);
111
- const store = createStore(instance, duckdb, conn, resolvedCfg, resolvedCfg.baseDir);
112
- // reconcile ran before this object existed, so its chunk text is keyed by the connection.
113
- rekeyChunkText(conn, store);
114
- return {
115
- store: store,
116
- cfg: resolvedCfg,
117
- dbPath,
118
- parsed,
119
- warnings
120
- };
98
+ return openWithDialect(cfg, duckdbOpenDialect);
121
99
  }
@@ -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 { 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 { reconcile } from '../reconcile.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 { duckdbDialect } 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, duckdbDialect);\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":["mkdirSync","join","featureSignature","STATE_DIR","rekeyChunkText","STORE_DIMS","SenseError","activeFeatures","FEATURES","clearCache","reconcile","getMeta","setMeta","createConnection","DUCKDB_PACKAGE","duckdbApi","duckdbDialect","registerFunctions","createStore","DB_FILENAME","SCHEMA_VERSION","ensureSchema","conn","cfg","exec","feature","name","schema","connect","stateDir","baseDir","recursive","dbPath","DuckDBInstance","err","message","duckdb","instance","create","storage_compatibility_version","closeSync","closed","version","features","wantFeatures","reason","console","error","disconnectSync","parsed","warnings","openDuckdb","resolvedCfg","store"],"mappings":"AAAA,SAASA,SAAS,QAAQ,UAAU;AACpC,SAASC,IAAI,QAAQ,YAAY;AAGjC,SAASC,gBAAgB,EAAEC,SAAS,QAAQ,wBAAwB;AACpE,SAASC,cAAc,QAAQ,yBAAyB;AACxD,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,UAAU,QAAQ,kBAAkB;AAC7C,SAASC,cAAc,EAAEC,QAAQ,QAAQ,0BAA0B;AACnE,SAASC,UAAU,QAAQ,cAAc;AACzC,SAASC,SAAS,QAAQ,kBAAkB;AAC5C,SAASC,OAAO,EAAEC,OAAO,QAAQ,eAAe;AAEhD,SAASC,gBAAgB,QAAQ,kBAAkB;AACnD,SAASC,cAAc,EAAEC,SAAS,QAAQ,cAAc;AACxD,SAASC,aAAa,QAAQ,iBAAiB;AAC/C,SAASC,iBAAiB,QAAQ,qBAAqB;AACvD,SAASC,WAAW,QAAQ,aAAa;AAEzC,OAAO,MAAMC,cAAc,eAAe;AAC1C,qIAAqI;AACrI,iJAAiJ;AACjJ,OAAO,MAAMC,iBAAiB,IAAI;AAUlC,8LAA8L;AAC9L,2JAA2J;AAC3J,eAAeC,aAAaC,IAAgB,EAAEC,GAAW;IACvD,MAAMD,KAAKE,IAAI,CAAC,CAAC,wIAAwI,CAAC;IAC1J,MAAMF,KAAKE,IAAI,CAAC,CAAC,iGAAiG,CAAC;IACnH,MAAMF,KAAKE,IAAI,CAAC,CAAC,gGAAgG,CAAC;IAClH,MAAMF,KAAKE,IAAI,CAAC;IAChB,KAAK,MAAMC,WAAWlB,eAAegB,KAAM;QACzC,+HAA+H;QAC/H,sHAAsH;QACtH,IAAIE,QAAQC,IAAI,KAAK,SAAS;YAC5B,MAAMJ,KAAKE,IAAI,CAAC,CAAC,kIAAkI,EAAEnB,WAAW,+BAA+B,CAAC;YAChM;QACF;QACA,MAAMoB,QAAQE,MAAM,CAACL;IACvB;IACA,IAAI,AAAC,MAAMX,QAAQW,MAAM,sBAAuB,MAAM,MAAMV,QAAQU,MAAM,kBAAkBF;IAC5F,IAAI,AAAC,MAAMT,QAAQW,MAAM,gBAAiB,MAAM,MAAMV,QAAQU,MAAM,YAAYpB,iBAAiBqB,KAAKf;AACxG;AAEA,eAAeoB,QAAQL,GAAmB;IACxC,MAAMM,WAAW5B,KAAKsB,IAAIO,OAAO,EAAE3B;IACnCH,UAAU6B,UAAU;QAAEE,WAAW;IAAK;IACtC,MAAMC,SAAS/B,KAAK4B,UAAUV;IAE9B,gIAAgI;IAChI,qLAAqL;IACrL,IAAIc;IACJ,IAAI;QACD,CAAA,EAAEA,cAAc,EAAE,GAAG,MAAMlB,WAAU;IACxC,EAAE,OAAOmB,KAAK;QACZ,IAAIA,eAAe5B,YAAY,MAAM4B;QACrC,MAAM,IAAI5B,WAAW,4BAA4B,CAAC,yBAAyB,EAAEQ,eAAe,UAAU,EAAE,AAACoB,IAAcC,OAAO,CAAC,CAAC,CAAC;IACnI;IAEA,IAAIC;IACJ,IAAIC;IACJ,IAAI;QACF,4HAA4H;QAC5H,4KAA4K;QAC5KA,WAAW,MAAMJ,eAAeK,MAAM,CAACN,QAAQ;YAAEO,+BAA+B;QAAS;QACzFH,SAAS,MAAMC,SAAST,OAAO;IACjC,EAAE,OAAOM,KAAK;QACZ,wFAAwF;QACxFG,qBAAAA,+BAAAA,SAAUG,SAAS;QACnB,MAAM,IAAIlC,WAAW,4BAA4B,CAAC,8BAA8B,EAAE0B,OAAO,EAAE,EAAE,AAACE,IAAcC,OAAO,EAAE;IACvH;IACA,2FAA2F;IAC3F,uFAAuF;IACvF,IAAIM,SAAS;IACb,IAAI;QACF,MAAMxB,kBAAkBmB;QACxB,MAAMd,OAAOT,iBAAiBuB;QAE9B,MAAMd,KAAKE,IAAI,CAAC;QAEhB,MAAMkB,UAAU,MAAM/B,QAAQW,MAAM;QACpC,MAAMqB,WAAW,MAAMhC,QAAQW,MAAM;QACrC,MAAMsB,eAAe1C,iBAAiBqB,KAAKf;QAC3C,IAAI,AAACkC,YAAY,QAAQA,YAAYtB,kBAAoBuB,aAAa,QAAQA,aAAaC,cAAe;YACxG,4FAA4F;YAC5F,wFAAwF;YACxF,MAAMC,SAASH,YAAY,QAAQA,YAAYtB,iBAAiB,mDAAmD;YACnH0B,QAAQC,KAAK,CAAC,CAAC,OAAO,EAAEF,OAAO,sBAAsB,CAAC;YACtD,gFAAgF;YAChFJ,SAAS;YACTL,OAAOY,cAAc;YACrBX,SAASG,SAAS;YAClB/B,WAAWc;YACX,OAAOK,QAAQL;QACjB;QAEA,MAAMF,aAAaC,MAAMC;QAEzB,MAAM,EAAE0B,MAAM,EAAEC,QAAQ,EAAE,GAAG,MAAMxC,UAAUY,MAAMC,KAAKA,IAAIO,OAAO,EAAEd;QACrE,OAAO;YAAEqB;YAAUD;YAAQd;YAAMC;YAAKS;YAAQiB;YAAQC;QAAS;IACjE,EAAE,OAAOhB,KAAK;QACZ,IAAI,CAACO,QAAQ;YACXL,OAAOY,cAAc;YACrBX,qBAAAA,+BAAAA,SAAUG,SAAS;QACrB;QACA,MAAMN;IACR;AACF;AAEA,OAAO,eAAeiB,WAAW5B,GAAmB;IAClD,MAAM,EAAEc,QAAQ,EAAED,MAAM,EAAEd,IAAI,EAAEC,KAAK6B,WAAW,EAAEpB,MAAM,EAAEiB,MAAM,EAAEC,QAAQ,EAAE,GAAG,MAAMtB,QAAQL;IAC7F,MAAM8B,QAAQnC,YAAYmB,UAAUD,QAAQd,MAAM8B,aAAaA,YAAYtB,OAAO;IAClF,0FAA0F;IAC1F1B,eAAekB,MAAM+B;IACrB,OAAO;QAAEA,OAAOA;QAAO9B,KAAK6B;QAAapB;QAAQiB;QAAQC;IAAS;AACpE"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/open.ts"],"sourcesContent":["import type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { featureSignature } from '../../config/index.ts';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport type { OpenResult } from '../open.ts';\nimport { openWithDialect } from '../open.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport type { Connection, OpenDialect } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { DUCKDB_PACKAGE, duckdbApi } from './native.ts';\nimport { duckdbDialect } 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 = '3';\n\nexport type { OpenResult };\n\n// The connection plus this store's own native handles (types.ts's OpenDialect<Handle>): the\n// instance owns the WAL and must outlive the connection borrowed from it.\ninterface DuckdbHandle {\n instance: DuckDBInstance;\n duckdb: DuckDBConnection;\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.\nasync function ensureSchema(_handle: DuckdbHandle, 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\n// Order matters: the connection must be gone before the instance closes the WAL.\nasync function close(handle: DuckdbHandle): Promise<void> {\n handle.duckdb.disconnectSync();\n handle.instance.closeSync();\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: DuckdbHandle; conn: Connection }> {\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 would leak the open instance, whose WAL then locks the .duckdb file undeletable on Windows.\n try {\n await registerFunctions(duckdb);\n const conn = createConnection(duckdb);\n return { handle: { instance, duckdb }, conn };\n } catch (err) {\n await close({ instance, duckdb });\n throw err;\n }\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const duckdbOpenDialect: OpenDialect<DuckdbHandle> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: duckdbDialect,\n connect,\n close,\n // duckdb's file lock spans the connection's life, and each platform words the refusal its own\n // way: posix \"Could not set lock on file ... Conflicting lock is held in <exe> (PID n)\", Windows\n // \"Cannot open file ... being used by another process\". Both are the same condition.\n isLocked: (err) => /Could not set lock on file|being used by another process/.test(err.message),\n ensureSchema,\n createStore: (handle, conn) => createStore(handle.instance, handle.duckdb, conn),\n};\n\nexport async function openDuckdb(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, duckdbOpenDialect);\n}\n"],"names":["featureSignature","STORE_DIMS","SenseError","activeFeatures","FEATURES","openWithDialect","getMeta","setMeta","createConnection","DUCKDB_PACKAGE","duckdbApi","duckdbDialect","registerFunctions","createStore","DB_FILENAME","SCHEMA_VERSION","ensureSchema","_handle","conn","cfg","exec","feature","name","schema","close","handle","duckdb","disconnectSync","instance","closeSync","connect","dbPath","_cfg","DuckDBInstance","err","message","create","storage_compatibility_version","duckdbOpenDialect","filename","schemaVersion","reconcileDialect","isLocked","test","openDuckdb"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  import { SenseError } from '../../errors.js';
2
+ import { quoteIdent } from '../shared.js';
3
+ import { markContentStale } from './lexical.js';
2
4
  // This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in
3
5
  // store/reconcile.ts. `content` is a plain table, not FTS-virtual, so rows are maintained here
4
6
  // unconditionally; DuckDB's ALTER TABLE needs a declared type, so every dynamic frontmatter
@@ -22,6 +24,18 @@ async function reconcileContent(conn, touched, docs, _delta) {
22
24
  p
23
25
  ]));
24
26
  if (docs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, docs.map(contentRow));
27
+ // content changed: the fts index is rebuilt lazily, on the next lexical query that needs it,
28
+ // not here (lexical.ts's FtsIndexState).
29
+ markContentStale(conn);
30
+ }
31
+ // DuckDB rejects more than one ALTER command per statement ("Parser Error: Only one ALTER
32
+ // command per statement is supported", measured), so every name's clause joins into one string
33
+ // and runs as a single exec() -- one column-add "leg" through the driver instead of `names.length`.
34
+ // VARIANT is the only type that can hold the mixed bigint/number/string/null shapes mapValue()
35
+ // produces for one key across files.
36
+ async function addColumns(conn, names) {
37
+ if (names.length === 0) return;
38
+ await conn.exec(names.map((name)=>`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(name)} VARIANT`).join('; '));
25
39
  }
26
40
  export const duckdbDialect = {
27
41
  beginMode: ()=>'BEGIN',
@@ -30,8 +44,6 @@ export const duckdbDialect = {
30
44
  throw new SenseError('COLUMN_LIMIT', `frontmatter would need ${count} columns, crossing this store's sanity limit (${MAX_FRONTMATTER_COLUMNS}). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`);
31
45
  }
32
46
  },
33
- // DuckDB's ALTER TABLE ADD COLUMN requires a type; VARIANT is the only one that can hold
34
- // the mixed bigint/number/string/null shapes mapValue() produces for one key across files.
35
- columnTypeSuffix: ()=>' VARIANT',
47
+ addColumns,
36
48
  reconcileContent
37
49
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/reconcile.ts"],"sourcesContent":["import { SenseError } from '../../errors.ts';\nimport type { ReconcileDelta } from '../../features/types.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport type { Connection, ReconcileDialect } from '../types.ts';\n\n// This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in\n// store/reconcile.ts. `content` is a plain table, not FTS-virtual, so rows are maintained here\n// unconditionally; DuckDB's ALTER TABLE needs a declared type, so every dynamic frontmatter\n// column is VARIANT (holds mapValue()'s mixed JS types for one key across files).\n\n// No rowid coupling needed (unlike sqlite's content, which links to frontmatter's rowid):\n// `path` is content's own primary key, so this is a plain per-doc row.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (\"path\", title, summary, text) VALUES (?, ?, ?, ?)`;\n\nfunction contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text];\n}\n\n// No compile-time column cap in DuckDB (unlike SQLite's SQLITE_MAX_COLUMN); kept as a sanity fence anyway\n// so a runaway frontmatter generator fails with a clear message instead of an unbounded ALTER TABLE loop.\nconst MAX_FRONTMATTER_COLUMNS = 10_000;\n\nasync function reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], _delta: ReconcileDelta): Promise<void> {\n if (touched.length > 0)\n await conn.runBatch(\n 'DELETE FROM content WHERE \"path\" = ?',\n touched.map((p) => [p])\n );\n if (docs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, docs.map(contentRow));\n}\n\nexport const duckdbDialect: ReconcileDialect = {\n beginMode: () => 'BEGIN',\n checkColumnLimit(count) {\n if (count > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError('COLUMN_LIMIT', `frontmatter would need ${count} columns, crossing this store's sanity limit (${MAX_FRONTMATTER_COLUMNS}). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`);\n }\n },\n // DuckDB's ALTER TABLE ADD COLUMN requires a type; VARIANT is the only one that can hold\n // the mixed bigint/number/string/null shapes mapValue() produces for one key across files.\n columnTypeSuffix: () => ' VARIANT',\n reconcileContent,\n};\n"],"names":["SenseError","INSERT_CONTENT_SQL","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcileContent","conn","touched","docs","_delta","length","runBatch","map","p","duckdbDialect","beginMode","checkColumnLimit","count","columnTypeSuffix"],"mappings":"AAAA,SAASA,UAAU,QAAQ,kBAAkB;AAK7C,qFAAqF;AACrF,+FAA+F;AAC/F,4FAA4F;AAC5F,kFAAkF;AAElF,0FAA0F;AAC1F,uEAAuE;AACvE,MAAMC,qBAAqB,CAAC,sEAAsE,CAAC;AAEnG,SAASC,WAAWC,GAAc;IAChC,OAAO;QAACA,IAAIC,OAAO;QAAED,IAAIE,MAAM,CAACC,KAAK;QAAEH,IAAIE,MAAM,CAACE,OAAO;QAAEJ,IAAIE,MAAM,CAACG,IAAI;KAAC;AAC7E;AAEA,0GAA0G;AAC1G,0GAA0G;AAC1G,MAAMC,0BAA0B;AAEhC,eAAeC,iBAAiBC,IAAgB,EAAEC,OAAiB,EAAEC,IAAiB,EAAEC,MAAsB;IAC5G,IAAIF,QAAQG,MAAM,GAAG,GACnB,MAAMJ,KAAKK,QAAQ,CACjB,wCACAJ,QAAQK,GAAG,CAAC,CAACC,IAAM;YAACA;SAAE;IAE1B,IAAIL,KAAKE,MAAM,GAAG,GAAG,MAAMJ,KAAKK,QAAQ,CAACf,oBAAoBY,KAAKI,GAAG,CAACf;AACxE;AAEA,OAAO,MAAMiB,gBAAkC;IAC7CC,WAAW,IAAM;IACjBC,kBAAiBC,KAAK;QACpB,IAAIA,QAAQb,yBAAyB;YACnC,MAAM,IAAIT,WAAW,gBAAgB,CAAC,uBAAuB,EAAEsB,MAAM,8CAA8C,EAAEb,wBAAwB,gIAAgI,CAAC;QAChR;IACF;IACA,yFAAyF;IACzF,2FAA2F;IAC3Fc,kBAAkB,IAAM;IACxBb;AACF,EAAE"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/reconcile.ts"],"sourcesContent":["import { SenseError } from '../../errors.ts';\nimport type { ReconcileDelta } from '../../features/types.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { quoteIdent } from '../shared.ts';\nimport type { Connection, ReconcileDialect } from '../types.ts';\nimport { markContentStale } from './lexical.ts';\n\n// This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in\n// store/reconcile.ts. `content` is a plain table, not FTS-virtual, so rows are maintained here\n// unconditionally; DuckDB's ALTER TABLE needs a declared type, so every dynamic frontmatter\n// column is VARIANT (holds mapValue()'s mixed JS types for one key across files).\n\n// No rowid coupling needed (unlike sqlite's content, which links to frontmatter's rowid):\n// `path` is content's own primary key, so this is a plain per-doc row.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (\"path\", title, summary, text) VALUES (?, ?, ?, ?)`;\n\nfunction contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text];\n}\n\n// No compile-time column cap in DuckDB (unlike SQLite's SQLITE_MAX_COLUMN); kept as a sanity fence anyway\n// so a runaway frontmatter generator fails with a clear message instead of an unbounded ALTER TABLE loop.\nconst MAX_FRONTMATTER_COLUMNS = 10_000;\n\nasync function reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], _delta: ReconcileDelta): Promise<void> {\n if (touched.length > 0)\n await conn.runBatch(\n 'DELETE FROM content WHERE \"path\" = ?',\n touched.map((p) => [p])\n );\n if (docs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, docs.map(contentRow));\n // content changed: the fts index is rebuilt lazily, on the next lexical query that needs it,\n // not here (lexical.ts's FtsIndexState).\n markContentStale(conn);\n}\n\n// DuckDB rejects more than one ALTER command per statement (\"Parser Error: Only one ALTER\n// command per statement is supported\", measured), so every name's clause joins into one string\n// and runs as a single exec() -- one column-add \"leg\" through the driver instead of `names.length`.\n// VARIANT is the only type that can hold the mixed bigint/number/string/null shapes mapValue()\n// produces for one key across files.\nasync function addColumns(conn: Connection, names: string[]): Promise<void> {\n if (names.length === 0) return;\n await conn.exec(names.map((name) => `ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(name)} VARIANT`).join('; '));\n}\n\nexport const duckdbDialect: ReconcileDialect = {\n beginMode: () => 'BEGIN',\n checkColumnLimit(count) {\n if (count > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError('COLUMN_LIMIT', `frontmatter would need ${count} columns, crossing this store's sanity limit (${MAX_FRONTMATTER_COLUMNS}). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`);\n }\n },\n addColumns,\n reconcileContent,\n};\n"],"names":["SenseError","quoteIdent","markContentStale","INSERT_CONTENT_SQL","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcileContent","conn","touched","docs","_delta","length","runBatch","map","p","addColumns","names","exec","name","join","duckdbDialect","beginMode","checkColumnLimit","count"],"mappings":"AAAA,SAASA,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,UAAU,QAAQ,eAAe;AAE1C,SAASC,gBAAgB,QAAQ,eAAe;AAEhD,qFAAqF;AACrF,+FAA+F;AAC/F,4FAA4F;AAC5F,kFAAkF;AAElF,0FAA0F;AAC1F,uEAAuE;AACvE,MAAMC,qBAAqB,CAAC,sEAAsE,CAAC;AAEnG,SAASC,WAAWC,GAAc;IAChC,OAAO;QAACA,IAAIC,OAAO;QAAED,IAAIE,MAAM,CAACC,KAAK;QAAEH,IAAIE,MAAM,CAACE,OAAO;QAAEJ,IAAIE,MAAM,CAACG,IAAI;KAAC;AAC7E;AAEA,0GAA0G;AAC1G,0GAA0G;AAC1G,MAAMC,0BAA0B;AAEhC,eAAeC,iBAAiBC,IAAgB,EAAEC,OAAiB,EAAEC,IAAiB,EAAEC,MAAsB;IAC5G,IAAIF,QAAQG,MAAM,GAAG,GACnB,MAAMJ,KAAKK,QAAQ,CACjB,wCACAJ,QAAQK,GAAG,CAAC,CAACC,IAAM;YAACA;SAAE;IAE1B,IAAIL,KAAKE,MAAM,GAAG,GAAG,MAAMJ,KAAKK,QAAQ,CAACf,oBAAoBY,KAAKI,GAAG,CAACf;IACtE,6FAA6F;IAC7F,yCAAyC;IACzCF,iBAAiBW;AACnB;AAEA,0FAA0F;AAC1F,+FAA+F;AAC/F,oGAAoG;AACpG,+FAA+F;AAC/F,qCAAqC;AACrC,eAAeQ,WAAWR,IAAgB,EAAES,KAAe;IACzD,IAAIA,MAAML,MAAM,KAAK,GAAG;IACxB,MAAMJ,KAAKU,IAAI,CAACD,MAAMH,GAAG,CAAC,CAACK,OAAS,CAAC,mCAAmC,EAAEvB,WAAWuB,MAAM,QAAQ,CAAC,EAAEC,IAAI,CAAC;AAC7G;AAEA,OAAO,MAAMC,gBAAkC;IAC7CC,WAAW,IAAM;IACjBC,kBAAiBC,KAAK;QACpB,IAAIA,QAAQlB,yBAAyB;YACnC,MAAM,IAAIX,WAAW,gBAAgB,CAAC,uBAAuB,EAAE6B,MAAM,8CAA8C,EAAElB,wBAAwB,gIAAgI,CAAC;QAChR;IACF;IACAU;IACAT;AACF,EAAE"}
@@ -1,5 +1,4 @@
1
1
  import type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';
2
- import type { Config } from '../../config/index.js';
3
2
  import type { Capability, Connection, Store } from '../types.js';
4
3
  export declare const CAPABILITIES: ReadonlySet<Capability>;
5
- export declare function createStore(instance: DuckDBInstance, duckdb: DuckDBConnection, conn: Connection, cfg: Config, baseDir: string): Store;
4
+ export declare function createStore(instance: DuckDBInstance, duckdb: DuckDBConnection, conn: Connection): Store;
@@ -1,12 +1,10 @@
1
1
  import { STORE_DIMS } from '../../embed/types.js';
2
- import { reconcile } from '../reconcile.js';
3
2
  import { getColumns } from '../shared.js';
4
3
  import { withTransaction } from '../transaction.js';
5
4
  import { hasVectorRow, pendingRows } from '../vectors.js';
6
5
  import { storeRowToJs } from './connection.js';
7
6
  import { fieldStats } from './fieldStats.js';
8
7
  import { createLexicalIndex } from './lexical.js';
9
- import { duckdbDialect } from './reconcile.js';
10
8
  import { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.js';
11
9
  // Portable surface, links/sections/tags/rank, raw sql passthrough, lexical (fts BM25 + contains(), lexical.ts), and vectors (native
12
10
  // FLOAT[N] + array_cosine_similarity, vectors.ts) are implemented. 'snippets' is declined (JS excerpt fallback handles it); 'phrases' means quoted-phrase only -- FTS5 operator syntax is rejected, not answered differently.
@@ -16,9 +14,9 @@ export const CAPABILITIES = new Set([
16
14
  'vectors',
17
15
  'sql-functions'
18
16
  ]);
19
- // Shares one Connection instance (conn) with open()'s own reconcile call so transaction depth (transaction.ts) is tracked against the same object everywhere.
17
+ // Shares one Connection instance (conn) with the builder's own reconcile call so transaction depth (transaction.ts) is tracked against the same object everywhere.
20
18
  // The instance is the native handle that owns the WAL: close() must close it, not just disconnect, or DuckDB never checkpoints and the next open reads a mismatched WAL.
21
- export function createStore(instance, duckdb, conn, cfg, baseDir) {
19
+ export function createStore(instance, duckdb, conn) {
22
20
  const lex = createLexicalIndex(conn);
23
21
  return {
24
22
  name: 'duckdb',
@@ -35,13 +33,6 @@ export function createStore(instance, duckdb, conn, cfg, baseDir) {
35
33
  async transaction (fn) {
36
34
  return withTransaction(conn, fn);
37
35
  },
38
- async reconcile () {
39
- const result = await reconcile(conn, cfg, baseDir, duckdbDialect);
40
- // content may have changed; the fts index is rebuilt lazily, on the next lexical query
41
- // that needs it, not here (see lexical.ts's FtsIndexState).
42
- lex.markStale();
43
- return result;
44
- },
45
36
  docs: {
46
37
  async columns () {
47
38
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/store.ts"],"sourcesContent":["import type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';\nimport type { Config } from '../../config/index.ts';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { reconcile } from '../reconcile.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { storeRowToJs } from './connection.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { createLexicalIndex } from './lexical.ts';\nimport { duckdbDialect } from './reconcile.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// Portable surface, links/sections/tags/rank, raw sql passthrough, lexical (fts BM25 + contains(), lexical.ts), and vectors (native\n// FLOAT[N] + array_cosine_similarity, vectors.ts) are implemented. 'snippets' is declined (JS excerpt fallback handles it); 'phrases' means quoted-phrase only -- FTS5 operator syntax is rejected, not answered differently.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors', 'sql-functions']);\n\n// Shares one Connection instance (conn) with open()'s own reconcile call so transaction depth (transaction.ts) is tracked against the same object everywhere.\n// The instance is the native handle that owns the WAL: close() must close it, not just disconnect, or DuckDB never checkpoints and the next open reads a mismatched WAL.\nexport function createStore(instance: DuckDBInstance, duckdb: DuckDBConnection, conn: Connection, cfg: Config, baseDir: string): Store {\n const lex = createLexicalIndex(conn);\n return {\n name: 'duckdb',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n async reconcile() {\n const result = await reconcile(conn, cfg, baseDir, duckdbDialect);\n // content may have changed; the fts index is rebuilt lazily, on the next lexical query\n // that needs it, not here (see lexical.ts's FtsIndexState).\n lex.markStale();\n return result;\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n fieldStats: (columns, scopeWhere) => fieldStats(conn, columns, scopeWhere),\n },\n lexical: {\n query: lex.query,\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the interface's per-call storeDims --\n // see vectors.ts's padded() for why a shorter vector remains correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(duckdb, conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(duckdb, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(duckdb, conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n // No comparable setting to report yet.\n async engineStatus() {\n return {};\n },\n raw: {\n async prepare(sql: string) {\n const prepared = await duckdb.prepare(sql);\n return {\n columns: () => {\n const out: Array<{ name: string }> = [];\n for (let i = 0; i < prepared.columnCount; i++) out.push({ name: prepared.columnName(i) });\n return out;\n },\n // Real incremental streaming: DuckDB reads its own result in row-group chunks, so\n // each readUntil only fetches what isn't already in hand.\n iterate: async function* (...params: unknown[]) {\n if (params.length > 0) prepared.bind(params as Parameters<typeof prepared.bind>[0]);\n const reader = await prepared.streamAndRead();\n let yielded = 0;\n do {\n await reader.readUntil(reader.currentRowCount + 2048);\n const rows = reader.getRowObjectsJS() as Array<Record<string, unknown>>;\n for (; yielded < rows.length; yielded++) yield storeRowToJs(rows[yielded]);\n } while (!reader.done);\n },\n };\n },\n },\n async close() {\n // Order matters: the connection must be gone before the instance closes the WAL.\n duckdb.disconnectSync();\n instance.closeSync();\n },\n };\n}\n"],"names":["STORE_DIMS","reconcile","getColumns","withTransaction","hasVectorRow","pendingRows","storeRowToJs","fieldStats","createLexicalIndex","duckdbDialect","scanCandidates","scanSimilar","writeVectorBatch","CAPABILITIES","Set","createStore","instance","duckdb","conn","cfg","baseDir","lex","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","result","markStale","docs","columns","scopeWhere","lexical","query","vectors","pending","writeVectors","rows","candidates","qv","_storeDims","fetch","allowed","similar","path","opts","hasVector","engineStatus","raw","prepared","out","i","columnCount","push","columnName","iterate","params","length","bind","reader","streamAndRead","yielded","readUntil","currentRowCount","getRowObjectsJS","done","close","disconnectSync","closeSync"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/store.ts"],"sourcesContent":["import type { DuckDBConnection, DuckDBInstance } from '@duckdb/node-api';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { storeRowToJs } from './connection.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { createLexicalIndex } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// Portable surface, links/sections/tags/rank, raw sql passthrough, lexical (fts BM25 + contains(), lexical.ts), and vectors (native\n// FLOAT[N] + array_cosine_similarity, vectors.ts) are implemented. 'snippets' is declined (JS excerpt fallback handles it); 'phrases' means quoted-phrase only -- FTS5 operator syntax is rejected, not answered differently.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors', 'sql-functions']);\n\n// Shares one Connection instance (conn) with the builder's own reconcile call so transaction depth (transaction.ts) is tracked against the same object everywhere.\n// The instance is the native handle that owns the WAL: close() must close it, not just disconnect, or DuckDB never checkpoints and the next open reads a mismatched WAL.\nexport function createStore(instance: DuckDBInstance, duckdb: DuckDBConnection, conn: Connection): Store {\n const lex = createLexicalIndex(conn);\n return {\n name: 'duckdb',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n fieldStats: (columns, scopeWhere) => fieldStats(conn, columns, scopeWhere),\n },\n lexical: {\n query: lex.query,\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the interface's per-call storeDims --\n // see vectors.ts's padded() for why a shorter vector remains correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(duckdb, conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(duckdb, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(duckdb, conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n // No comparable setting to report yet.\n async engineStatus() {\n return {};\n },\n raw: {\n async prepare(sql: string) {\n const prepared = await duckdb.prepare(sql);\n return {\n columns: () => {\n const out: Array<{ name: string }> = [];\n for (let i = 0; i < prepared.columnCount; i++) out.push({ name: prepared.columnName(i) });\n return out;\n },\n // Real incremental streaming: DuckDB reads its own result in row-group chunks, so\n // each readUntil only fetches what isn't already in hand.\n iterate: async function* (...params: unknown[]) {\n if (params.length > 0) prepared.bind(params as Parameters<typeof prepared.bind>[0]);\n const reader = await prepared.streamAndRead();\n let yielded = 0;\n do {\n await reader.readUntil(reader.currentRowCount + 2048);\n const rows = reader.getRowObjectsJS() as Array<Record<string, unknown>>;\n for (; yielded < rows.length; yielded++) yield storeRowToJs(rows[yielded]);\n } while (!reader.done);\n },\n };\n },\n },\n async close() {\n // Order matters: the connection must be gone before the instance closes the WAL.\n duckdb.disconnectSync();\n instance.closeSync();\n },\n };\n}\n"],"names":["STORE_DIMS","getColumns","withTransaction","hasVectorRow","pendingRows","storeRowToJs","fieldStats","createLexicalIndex","scanCandidates","scanSimilar","writeVectorBatch","CAPABILITIES","Set","createStore","instance","duckdb","conn","lex","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","docs","columns","scopeWhere","lexical","query","vectors","pending","writeVectors","rows","candidates","qv","_storeDims","fetch","allowed","similar","path","opts","hasVector","engineStatus","raw","prepared","out","i","columnCount","push","columnName","iterate","params","length","bind","reader","streamAndRead","yielded","readUntil","currentRowCount","getRowObjectsJS","done","close","disconnectSync","closeSync"],"mappings":"AACA,SAASA,UAAU,QAAQ,uBAAuB;AAClD,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,SAASC,YAAY,EAAEC,WAAW,QAAQ,gBAAgB;AAC1D,SAASC,YAAY,QAAQ,kBAAkB;AAC/C,SAASC,UAAU,QAAQ,kBAAkB;AAC7C,SAASC,kBAAkB,QAAQ,eAAe;AAClD,SAASC,cAAc,EAAEC,WAAW,EAAEC,gBAAgB,QAAQ,eAAe;AAE7E,oIAAoI;AACpI,8NAA8N;AAC9N,OAAO,MAAMC,eAAwC,IAAIC,IAAI;IAAC;IAAW;IAAW;IAAW;CAAgB,EAAE;AAEjH,mKAAmK;AACnK,yKAAyK;AACzK,OAAO,SAASC,YAAYC,QAAwB,EAAEC,MAAwB,EAAEC,IAAgB;IAC9F,MAAMC,MAAMV,mBAAmBS;IAC/B,OAAO;QACLE,MAAM;QACNC,cAAcR;QACd,MAAMS,MAAKC,GAAW;YACpB,MAAML,KAAKI,IAAI,CAACC;QAClB;QACA,MAAMC,SAAQD,GAAW;YACvB,OAAOL,KAAKM,OAAO,CAACD;QACtB;QACA,MAAME,UAASF,GAAW,EAAEG,SAAsB;YAChD,MAAMR,KAAKO,QAAQ,CAACF,KAAKG;QAC3B;QACA,MAAMC,aAAeC,EAAoB;YACvC,OAAOxB,gBAAgBc,MAAMU;QAC/B;QACAC,MAAM;YACJ,MAAMC;gBACJ,OAAO;uBAAK,MAAM3B,WAAWe;iBAAO;YACtC;YACAV,YAAY,CAACsB,SAASC,aAAevB,WAAWU,MAAMY,SAASC;QACjE;QACAC,SAAS;YACPC,OAAOd,IAAIc,KAAK;QAClB;QACA,wHAAwH;QACxH,6FAA6F;QAC7FC,SAAS;YACPC,SAAS,IAAM7B,YAAYY;YAC3BkB,cAAc,CAACC,OAASzB,iBAAiBK,QAAQC,MAAMhB,YAAYmC;YACnEC,YAAY,CAACC,IAAIC,YAAYC,OAAOC,UAAYhC,eAAeO,QAAQsB,IAAIrC,YAAYuC,OAAOC;YAC9FC,SAAS,CAACC,MAAMC,OAASlC,YAAYM,QAAQC,MAAMhB,YAAY0C,MAAMC;YACrEC,WAAW,CAACF,OAASvC,aAAaa,MAAM0B;QAC1C;QACA,uCAAuC;QACvC,MAAMG;YACJ,OAAO,CAAC;QACV;QACAC,KAAK;YACH,MAAMxB,SAAQD,GAAW;gBACvB,MAAM0B,WAAW,MAAMhC,OAAOO,OAAO,CAACD;gBACtC,OAAO;oBACLO,SAAS;wBACP,MAAMoB,MAA+B,EAAE;wBACvC,IAAK,IAAIC,IAAI,GAAGA,IAAIF,SAASG,WAAW,EAAED,IAAKD,IAAIG,IAAI,CAAC;4BAAEjC,MAAM6B,SAASK,UAAU,CAACH;wBAAG;wBACvF,OAAOD;oBACT;oBACA,kFAAkF;oBAClF,0DAA0D;oBAC1DK,SAAS,gBAAiB,GAAGC,MAAiB;wBAC5C,IAAIA,OAAOC,MAAM,GAAG,GAAGR,SAASS,IAAI,CAACF;wBACrC,MAAMG,SAAS,MAAMV,SAASW,aAAa;wBAC3C,IAAIC,UAAU;wBACd,GAAG;4BACD,MAAMF,OAAOG,SAAS,CAACH,OAAOI,eAAe,GAAG;4BAChD,MAAM1B,OAAOsB,OAAOK,eAAe;4BACnC,MAAOH,UAAUxB,KAAKoB,MAAM,EAAEI,UAAW,MAAMtD,aAAa8B,IAAI,CAACwB,QAAQ;wBAC3E,QAAS,CAACF,OAAOM,IAAI,CAAE;oBACzB;gBACF;YACF;QACF;QACA,MAAMC;YACJ,iFAAiF;YACjFjD,OAAOkD,cAAc;YACrBnD,SAASoD,SAAS;QACpB;IACF;AACF"}
@@ -0,0 +1,2 @@
1
+ export type EmbedChangeKind = 'model' | 'chunk';
2
+ export declare function classifyEmbedChange(before: string, after: string): EmbedChangeKind | null;
@@ -0,0 +1,28 @@
1
+ // Decomposition of the `embed:` featureSignature segment (config/access.ts's featureSignature,
2
+ // features/embed.ts's signature()), for open()'s narrow embed-invalidation routing.
3
+ // One embed segment's provider/model/chunkVersion, ignoring any trailing `@identity`. `embed:off`
4
+ // and any segment not in that exact shape return null, so the caller falls back to a full rebuild.
5
+ function parseEmbedSegment(sig) {
6
+ const part = sig.split('|').find((p)=>p.startsWith('embed:'));
7
+ if (part === undefined || part === 'embed:off') return null;
8
+ const at = part.indexOf('@');
9
+ const body = at === -1 ? part : part.slice(0, at);
10
+ const match = /^embed:(static|openai|cohere):(.+):(chunk:v\d+(?::\d+)?)$/.exec(body);
11
+ if (!match) return null;
12
+ const [, provider, model, chunkVersion] = match;
13
+ return {
14
+ provider,
15
+ model,
16
+ chunkVersion
17
+ };
18
+ }
19
+ // 'chunk' when chunkTokens or the chunk version moved (boundaries changed, embeddings rebuild);
20
+ // 'model' when only the provider or model moved (rows unchanged, values stale); null otherwise.
21
+ export function classifyEmbedChange(before, after) {
22
+ const b = parseEmbedSegment(before);
23
+ const a = parseEmbedSegment(after);
24
+ if (b === null || a === null) return null;
25
+ if (b.chunkVersion !== a.chunkVersion) return 'chunk';
26
+ if (b.provider !== a.provider || b.model !== a.model) return 'model';
27
+ return null;
28
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/embed-scope.ts"],"sourcesContent":["// Decomposition of the `embed:` featureSignature segment (config/access.ts's featureSignature,\n// features/embed.ts's signature()), for open()'s narrow embed-invalidation routing.\n\nexport type EmbedChangeKind = 'model' | 'chunk';\n\ninterface EmbedSegment {\n provider: string;\n model: string;\n chunkVersion: string;\n}\n\n// One embed segment's provider/model/chunkVersion, ignoring any trailing `@identity`. `embed:off`\n// and any segment not in that exact shape return null, so the caller falls back to a full rebuild.\nfunction parseEmbedSegment(sig: string): EmbedSegment | null {\n const part = sig.split('|').find((p) => p.startsWith('embed:'));\n if (part === undefined || part === 'embed:off') return null;\n const at = part.indexOf('@');\n const body = at === -1 ? part : part.slice(0, at);\n const match = /^embed:(static|openai|cohere):(.+):(chunk:v\\d+(?::\\d+)?)$/.exec(body);\n if (!match) return null;\n const [, provider, model, chunkVersion] = match;\n return { provider, model, chunkVersion };\n}\n\n// 'chunk' when chunkTokens or the chunk version moved (boundaries changed, embeddings rebuild);\n// 'model' when only the provider or model moved (rows unchanged, values stale); null otherwise.\nexport function classifyEmbedChange(before: string, after: string): EmbedChangeKind | null {\n const b = parseEmbedSegment(before);\n const a = parseEmbedSegment(after);\n if (b === null || a === null) return null;\n if (b.chunkVersion !== a.chunkVersion) return 'chunk';\n if (b.provider !== a.provider || b.model !== a.model) return 'model';\n return null;\n}\n"],"names":["parseEmbedSegment","sig","part","split","find","p","startsWith","undefined","at","indexOf","body","slice","match","exec","provider","model","chunkVersion","classifyEmbedChange","before","after","b","a"],"mappings":"AAAA,+FAA+F;AAC/F,oFAAoF;AAUpF,kGAAkG;AAClG,mGAAmG;AACnG,SAASA,kBAAkBC,GAAW;IACpC,MAAMC,OAAOD,IAAIE,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,IAAMA,EAAEC,UAAU,CAAC;IACrD,IAAIJ,SAASK,aAAaL,SAAS,aAAa,OAAO;IACvD,MAAMM,KAAKN,KAAKO,OAAO,CAAC;IACxB,MAAMC,OAAOF,OAAO,CAAC,IAAIN,OAAOA,KAAKS,KAAK,CAAC,GAAGH;IAC9C,MAAMI,QAAQ,4DAA4DC,IAAI,CAACH;IAC/E,IAAI,CAACE,OAAO,OAAO;IACnB,MAAM,GAAGE,UAAUC,OAAOC,aAAa,GAAGJ;IAC1C,OAAO;QAAEE;QAAUC;QAAOC;IAAa;AACzC;AAEA,gGAAgG;AAChG,gGAAgG;AAChG,OAAO,SAASC,oBAAoBC,MAAc,EAAEC,KAAa;IAC/D,MAAMC,IAAIpB,kBAAkBkB;IAC5B,MAAMG,IAAIrB,kBAAkBmB;IAC5B,IAAIC,MAAM,QAAQC,MAAM,MAAM,OAAO;IACrC,IAAID,EAAEJ,YAAY,KAAKK,EAAEL,YAAY,EAAE,OAAO;IAC9C,IAAII,EAAEN,QAAQ,KAAKO,EAAEP,QAAQ,IAAIM,EAAEL,KAAK,KAAKM,EAAEN,KAAK,EAAE,OAAO;IAC7D,OAAO;AACT"}
@@ -0,0 +1,8 @@
1
+ import type { FeatureName } from '../config/index.js';
2
+ export declare const NARROW_FEATURE_TABLE: Readonly<Record<string, string>>;
3
+ export interface FeatureToggle {
4
+ name: FeatureName;
5
+ turnedOn: boolean;
6
+ }
7
+ export declare function isFeatureOnlyChange(changedKeys: ReadonlySet<string>): boolean;
8
+ export declare function classifyFeatureToggles(before: string, after: string, changedKeys: ReadonlySet<string>): FeatureToggle[] | null;
@@ -0,0 +1,49 @@
1
+ // Decomposition of the `feature:<name>:<on|off>` featureSignature segments (config/access.ts's
2
+ // featureSignature), for open()'s narrow feature-toggle invalidation routing.
3
+ // Every narrow-supported feature but rank owns one dedicated table, dropped and (turning on)
4
+ // fully re-derived. rank has no table -- it writes PageRank into frontmatter's own `_rank`
5
+ // column -- so builder.ts handles it, and the links/rank pair, as special cases outside this map.
6
+ export const NARROW_FEATURE_TABLE = {
7
+ tags: 'tags',
8
+ sections: 'sections',
9
+ links: 'links'
10
+ };
11
+ // links feeds rank's afterReconcile pass, so a links toggle always co-changes the `feature:rank`
12
+ // segment too (featureEnabled's dependency); both are narrow-supported and builder.ts applies
13
+ // them together. A changed set naming any other feature falls through to the existing full clear.
14
+ const NARROW_FEATURE_NAMES = new Set([
15
+ ...Object.keys(NARROW_FEATURE_TABLE),
16
+ 'rank'
17
+ ]);
18
+ // Every changed key is a `feature:<name>` segment for a narrow-supported feature.
19
+ export function isFeatureOnlyChange(changedKeys) {
20
+ return changedKeys.size > 0 && [
21
+ ...changedKeys
22
+ ].every((key)=>key.startsWith('feature:') && NARROW_FEATURE_NAMES.has(key.slice('feature:'.length)));
23
+ }
24
+ // One feature segment's old on/off, read back out of a stored `feature:<name>:on|off` segment.
25
+ // Anything not in that exact shape returns null so the caller falls back to a full rebuild.
26
+ function parseOnOff(sig, name) {
27
+ const part = sig.split('|').find((p)=>p.startsWith(`feature:${name}:`));
28
+ if (part === undefined) return null;
29
+ const fields = part.split(':');
30
+ if (fields.length !== 3 || fields[2] !== 'on' && fields[2] !== 'off') return null;
31
+ return fields[2] === 'on';
32
+ }
33
+ // Old-vs-new on/off for every changed feature key, or null when a segment can't be parsed back
34
+ // out of `before` or `after`, or when it parses to the same state on both sides (should never
35
+ // happen for a genuinely changed key) -- either way the caller takes the full rebuild.
36
+ export function classifyFeatureToggles(before, after, changedKeys) {
37
+ const toggles = [];
38
+ for (const key of changedKeys){
39
+ const name = key.slice('feature:'.length);
40
+ const wasOn = parseOnOff(before, name);
41
+ const isOn = parseOnOff(after, name);
42
+ if (wasOn === null || isOn === null || wasOn === isOn) return null;
43
+ toggles.push({
44
+ name,
45
+ turnedOn: isOn
46
+ });
47
+ }
48
+ return toggles;
49
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/feature-scope.ts"],"sourcesContent":["import type { FeatureName } from '../config/index.ts';\n\n// Decomposition of the `feature:<name>:<on|off>` featureSignature segments (config/access.ts's\n// featureSignature), for open()'s narrow feature-toggle invalidation routing.\n\n// Every narrow-supported feature but rank owns one dedicated table, dropped and (turning on)\n// fully re-derived. rank has no table -- it writes PageRank into frontmatter's own `_rank`\n// column -- so builder.ts handles it, and the links/rank pair, as special cases outside this map.\nexport const NARROW_FEATURE_TABLE: Readonly<Record<string, string>> = { tags: 'tags', sections: 'sections', links: 'links' };\n\n// links feeds rank's afterReconcile pass, so a links toggle always co-changes the `feature:rank`\n// segment too (featureEnabled's dependency); both are narrow-supported and builder.ts applies\n// them together. A changed set naming any other feature falls through to the existing full clear.\nconst NARROW_FEATURE_NAMES: ReadonlySet<string> = new Set([...Object.keys(NARROW_FEATURE_TABLE), 'rank']);\n\nexport interface FeatureToggle {\n name: FeatureName;\n // true: was off, now on (must fully re-derive). false: was on, now off (drop its rows).\n turnedOn: boolean;\n}\n\n// Every changed key is a `feature:<name>` segment for a narrow-supported feature.\nexport function isFeatureOnlyChange(changedKeys: ReadonlySet<string>): boolean {\n return changedKeys.size > 0 && [...changedKeys].every((key) => key.startsWith('feature:') && NARROW_FEATURE_NAMES.has(key.slice('feature:'.length)));\n}\n\n// One feature segment's old on/off, read back out of a stored `feature:<name>:on|off` segment.\n// Anything not in that exact shape returns null so the caller falls back to a full rebuild.\nfunction parseOnOff(sig: string, name: string): boolean | null {\n const part = sig.split('|').find((p) => p.startsWith(`feature:${name}:`));\n if (part === undefined) return null;\n const fields = part.split(':');\n if (fields.length !== 3 || (fields[2] !== 'on' && fields[2] !== 'off')) return null;\n return fields[2] === 'on';\n}\n\n// Old-vs-new on/off for every changed feature key, or null when a segment can't be parsed back\n// out of `before` or `after`, or when it parses to the same state on both sides (should never\n// happen for a genuinely changed key) -- either way the caller takes the full rebuild.\nexport function classifyFeatureToggles(before: string, after: string, changedKeys: ReadonlySet<string>): FeatureToggle[] | null {\n const toggles: FeatureToggle[] = [];\n for (const key of changedKeys) {\n const name = key.slice('feature:'.length) as FeatureName;\n const wasOn = parseOnOff(before, name);\n const isOn = parseOnOff(after, name);\n if (wasOn === null || isOn === null || wasOn === isOn) return null;\n toggles.push({ name, turnedOn: isOn });\n }\n return toggles;\n}\n"],"names":["NARROW_FEATURE_TABLE","tags","sections","links","NARROW_FEATURE_NAMES","Set","Object","keys","isFeatureOnlyChange","changedKeys","size","every","key","startsWith","has","slice","length","parseOnOff","sig","name","part","split","find","p","undefined","fields","classifyFeatureToggles","before","after","toggles","wasOn","isOn","push","turnedOn"],"mappings":"AAEA,+FAA+F;AAC/F,8EAA8E;AAE9E,6FAA6F;AAC7F,2FAA2F;AAC3F,kGAAkG;AAClG,OAAO,MAAMA,uBAAyD;IAAEC,MAAM;IAAQC,UAAU;IAAYC,OAAO;AAAQ,EAAE;AAE7H,iGAAiG;AACjG,8FAA8F;AAC9F,kGAAkG;AAClG,MAAMC,uBAA4C,IAAIC,IAAI;OAAIC,OAAOC,IAAI,CAACP;IAAuB;CAAO;AAQxG,kFAAkF;AAClF,OAAO,SAASQ,oBAAoBC,WAAgC;IAClE,OAAOA,YAAYC,IAAI,GAAG,KAAK;WAAID;KAAY,CAACE,KAAK,CAAC,CAACC,MAAQA,IAAIC,UAAU,CAAC,eAAeT,qBAAqBU,GAAG,CAACF,IAAIG,KAAK,CAAC,WAAWC,MAAM;AACnJ;AAEA,+FAA+F;AAC/F,4FAA4F;AAC5F,SAASC,WAAWC,GAAW,EAAEC,IAAY;IAC3C,MAAMC,OAAOF,IAAIG,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,IAAMA,EAAEV,UAAU,CAAC,CAAC,QAAQ,EAAEM,KAAK,CAAC,CAAC;IACvE,IAAIC,SAASI,WAAW,OAAO;IAC/B,MAAMC,SAASL,KAAKC,KAAK,CAAC;IAC1B,IAAII,OAAOT,MAAM,KAAK,KAAMS,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,OAAQ,OAAO;IAC/E,OAAOA,MAAM,CAAC,EAAE,KAAK;AACvB;AAEA,+FAA+F;AAC/F,8FAA8F;AAC9F,uFAAuF;AACvF,OAAO,SAASC,uBAAuBC,MAAc,EAAEC,KAAa,EAAEnB,WAAgC;IACpG,MAAMoB,UAA2B,EAAE;IACnC,KAAK,MAAMjB,OAAOH,YAAa;QAC7B,MAAMU,OAAOP,IAAIG,KAAK,CAAC,WAAWC,MAAM;QACxC,MAAMc,QAAQb,WAAWU,QAAQR;QACjC,MAAMY,OAAOd,WAAWW,OAAOT;QAC/B,IAAIW,UAAU,QAAQC,SAAS,QAAQD,UAAUC,MAAM,OAAO;QAC9DF,QAAQG,IAAI,CAAC;YAAEb;YAAMc,UAAUF;QAAK;IACtC;IACA,OAAOF;AACT"}
@@ -1,10 +1,15 @@
1
1
  import type { ResolvedConfig } from '../config/index.js';
2
- import type { OpenResult } from './sqlite/open.js';
2
+ import type { Builder } from './builder.js';
3
+ import type { OpenResult } from './open.js';
3
4
  export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
5
+ export declare function openStoreForWatch(cfg: ResolvedConfig): Promise<OpenResult & {
6
+ builder: Builder;
7
+ }>;
4
8
  export declare function requireWatchConcurrency(cfg: ResolvedConfig): void;
5
9
  export { clearCache } from './cache.js';
10
+ export type { OpenResult } from './open.js';
11
+ export { docCount } from './open.js';
6
12
  export { getMeta, setMeta } from './shared.js';
7
- export type { OpenResult } from './sqlite/open.js';
8
- export { DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
13
+ export { DB_FILENAME, SCHEMA_VERSION } from './sqlite/open.js';
9
14
  export type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.js';
10
15
  export { CAPABILITY_NAMES } from './types.js';
@@ -2,26 +2,30 @@
2
2
  // beyond frontmatter + content lives in src/features/.
3
3
  import { anyPresetEmbeds, storeName } from '../config/index.js';
4
4
  import { SenseError } from '../errors.js';
5
- import { openDuckdb } from './duckdb/open.js';
5
+ import { duckdbOpenDialect, openDuckdb } from './duckdb/open.js';
6
6
  import { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.js';
7
- import { openSqlite } from './sqlite/open.js';
7
+ import { openWithBuilder } from './open.js';
8
+ import { openSqlite, sqliteOpenDialect } from './sqlite/open.js';
8
9
  import { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.js';
9
- import { openTurso } from './turso/open.js';
10
+ import { openTurso, tursoOpenDialect } from './turso/open.js';
10
11
  import { CAPABILITIES as TURSO_CAPABILITIES } from './turso/store.js';
11
12
  // Capabilities are checked against the registry entry before opening (no wasted connect/reconcile
12
13
  // work for a config that cannot be satisfied), then the store itself opens.
13
14
  const REGISTRY = {
14
15
  sqlite: {
15
16
  capabilities: SQLITE_CAPABILITIES,
16
- open: openSqlite
17
+ open: openSqlite,
18
+ openForWatch: (cfg)=>openWithBuilder(cfg, sqliteOpenDialect)
17
19
  },
18
20
  duckdb: {
19
21
  capabilities: DUCKDB_CAPABILITIES,
20
- open: openDuckdb
22
+ open: openDuckdb,
23
+ openForWatch: (cfg)=>openWithBuilder(cfg, duckdbOpenDialect)
21
24
  },
22
25
  turso: {
23
26
  capabilities: TURSO_CAPABILITIES,
24
- open: openTurso
27
+ open: openTurso,
28
+ openForWatch: (cfg)=>openWithBuilder(cfg, tursoOpenDialect)
25
29
  }
26
30
  };
27
31
  function entryFor(name) {
@@ -39,6 +43,16 @@ export async function openStore(cfg) {
39
43
  }
40
44
  return entry.open(cfg);
41
45
  }
46
+ // Same capability-checked open as openStore, but for a caller (only src/watch.ts) that must
47
+ // reconcile again later: keeps the builder's pool warm and hands it back instead of closing it.
48
+ export async function openStoreForWatch(cfg) {
49
+ const name = storeName(cfg);
50
+ const entry = entryFor(name);
51
+ if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {
52
+ throw new SenseError('STORE_CAPABILITY_MISSING', `store "${name}" does not implement "vectors" in this build, but this config's "embed" block is in use by at least one preset; remove or narrow it, or set "store" to a store that supports vectors (sqlite)`);
53
+ }
54
+ return entry.openForWatch(cfg);
55
+ }
42
56
  // watch keeps its connection open for the run, so a file-locking store would fail every
43
57
  // other command on the tree; check before open, as openStore does for "vectors".
44
58
  export function requireWatchConcurrency(cfg) {
@@ -48,6 +62,7 @@ export function requireWatchConcurrency(cfg) {
48
62
  }
49
63
  }
50
64
  export { clearCache } from './cache.js';
65
+ export { docCount } from './open.js';
51
66
  export { getMeta, setMeta } from './shared.js';
52
- export { DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
67
+ export { DB_FILENAME, SCHEMA_VERSION } from './sqlite/open.js';
53
68
  export { CAPABILITY_NAMES } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig, StoreName } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './sqlite/open.ts';\nimport { openSqlite } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport { openTurso } from './turso/open.ts';\nimport { CAPABILITIES as TURSO_CAPABILITIES } from './turso/store.ts';\nimport type { Capability } from './types.ts';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb },\n turso: { capabilities: TURSO_CAPABILITIES, open: openTurso },\n};\n\nfunction entryFor(name: StoreName): StoreEntry {\n const entry = REGISTRY[name];\n if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store \"${name}\"; available: ${Object.keys(REGISTRY).join(', ')}`);\n return entry;\n}\n\nexport async function openStore(cfg: ResolvedConfig): Promise<OpenResult> {\n const name = storeName(cfg);\n const entry = entryFor(name);\n\n // The one unambiguous, config-level capability need: an embed block some preset actually uses\n // for vectors. Lexical search has no such signal at this level (every preset defaults to \"words\"), so a store lacking it fails loudly at first use.\n if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"vectors\" in this build, but this config's \"embed\" block is in use by at least one preset; remove or narrow it, or set \"store\" to a store that supports vectors (sqlite)`);\n }\n\n return entry.open(cfg);\n}\n\n// watch keeps its connection open for the run, so a file-locking store would fail every\n// other command on the tree; check before open, as openStore does for \"vectors\".\nexport function requireWatchConcurrency(cfg: ResolvedConfig): void {\n const name = storeName(cfg);\n if (!entryFor(name).capabilities.has('watch-concurrency')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"watch-concurrency\" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set \"store\" to \"sqlite\" in sense.config.json`);\n }\n}\n\nexport { clearCache } from './cache.ts';\nexport { getMeta, setMeta } from './shared.ts';\nexport type { OpenResult } from './sqlite/open.ts';\nexport { DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.ts';\nexport type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.ts';\nexport { CAPABILITY_NAMES } from './types.ts';\n"],"names":["anyPresetEmbeds","storeName","SenseError","openDuckdb","CAPABILITIES","DUCKDB_CAPABILITIES","openSqlite","SQLITE_CAPABILITIES","openTurso","TURSO_CAPABILITIES","REGISTRY","sqlite","capabilities","open","duckdb","turso","entryFor","name","entry","Object","keys","join","openStore","cfg","has","requireWatchConcurrency","clearCache","getMeta","setMeta","DB_FILENAME","docCount","SCHEMA_VERSION","CAPABILITY_NAMES"],"mappings":"AAAA,2FAA2F;AAC3F,uDAAuD;AAGvD,SAASA,eAAe,EAAEC,SAAS,QAAQ,qBAAqB;AAChE,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,UAAU,QAAQ,mBAAmB;AAC9C,SAASC,gBAAgBC,mBAAmB,QAAQ,oBAAoB;AAExE,SAASC,UAAU,QAAQ,mBAAmB;AAC9C,SAASF,gBAAgBG,mBAAmB,QAAQ,oBAAoB;AACxE,SAASC,SAAS,QAAQ,kBAAkB;AAC5C,SAASJ,gBAAgBK,kBAAkB,QAAQ,mBAAmB;AAQtE,kGAAkG;AAClG,4EAA4E;AAC5E,MAAMC,WAA0C;IAC9CC,QAAQ;QAAEC,cAAcL;QAAqBM,MAAMP;IAAW;IAC9DQ,QAAQ;QAAEF,cAAcP;QAAqBQ,MAAMV;IAAW;IAC9DY,OAAO;QAAEH,cAAcH;QAAoBI,MAAML;IAAU;AAC7D;AAEA,SAASQ,SAASC,IAAe;IAC/B,MAAMC,QAAQR,QAAQ,CAACO,KAAK;IAC5B,IAAI,CAACC,OAAO,MAAM,IAAIhB,WAAW,iBAAiB,CAAC,uBAAuB,EAAEe,KAAK,cAAc,EAAEE,OAAOC,IAAI,CAACV,UAAUW,IAAI,CAAC,OAAO;IACnI,OAAOH;AACT;AAEA,OAAO,eAAeI,UAAUC,GAAmB;IACjD,MAAMN,OAAOhB,UAAUsB;IACvB,MAAML,QAAQF,SAASC;IAEvB,8FAA8F;IAC9F,oJAAoJ;IACpJ,IAAIjB,gBAAgBuB,QAAQ,CAACL,MAAMN,YAAY,CAACY,GAAG,CAAC,YAAY;QAC9D,MAAM,IAAItB,WAAW,4BAA4B,CAAC,OAAO,EAAEe,KAAK,6LAA6L,CAAC;IAChQ;IAEA,OAAOC,MAAML,IAAI,CAACU;AACpB;AAEA,wFAAwF;AACxF,iFAAiF;AACjF,OAAO,SAASE,wBAAwBF,GAAmB;IACzD,MAAMN,OAAOhB,UAAUsB;IACvB,IAAI,CAACP,SAASC,MAAML,YAAY,CAACY,GAAG,CAAC,sBAAsB;QACzD,MAAM,IAAItB,WAAW,4BAA4B,CAAC,OAAO,EAAEe,KAAK,qPAAqP,CAAC;IACxT;AACF;AAEA,SAASS,UAAU,QAAQ,aAAa;AACxC,SAASC,OAAO,EAAEC,OAAO,QAAQ,cAAc;AAE/C,SAASC,WAAW,EAAEC,QAAQ,EAAEC,cAAc,QAAQ,mBAAmB;AAEzE,SAASC,gBAAgB,QAAQ,aAAa"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig, StoreName } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Builder } from './builder.ts';\nimport { duckdbOpenDialect, openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './open.ts';\nimport { openWithBuilder } from './open.ts';\nimport { openSqlite, sqliteOpenDialect } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport { openTurso, tursoOpenDialect } from './turso/open.ts';\nimport { CAPABILITIES as TURSO_CAPABILITIES } from './turso/store.ts';\nimport type { Capability } from './types.ts';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n // Same open, but keeps the builder's parse pool alive and hands it back: only a watcher, which\n // reconciles repeatedly on the same connection, needs this.\n openForWatch: (cfg: ResolvedConfig) => Promise<OpenResult & { builder: Builder }>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite, openForWatch: (cfg) => openWithBuilder(cfg, sqliteOpenDialect) },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb, openForWatch: (cfg) => openWithBuilder(cfg, duckdbOpenDialect) },\n turso: { capabilities: TURSO_CAPABILITIES, open: openTurso, openForWatch: (cfg) => openWithBuilder(cfg, tursoOpenDialect) },\n};\n\nfunction entryFor(name: StoreName): StoreEntry {\n const entry = REGISTRY[name];\n if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store \"${name}\"; available: ${Object.keys(REGISTRY).join(', ')}`);\n return entry;\n}\n\nexport async function openStore(cfg: ResolvedConfig): Promise<OpenResult> {\n const name = storeName(cfg);\n const entry = entryFor(name);\n\n // The one unambiguous, config-level capability need: an embed block some preset actually uses\n // for vectors. Lexical search has no such signal at this level (every preset defaults to \"words\"), so a store lacking it fails loudly at first use.\n if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"vectors\" in this build, but this config's \"embed\" block is in use by at least one preset; remove or narrow it, or set \"store\" to a store that supports vectors (sqlite)`);\n }\n\n return entry.open(cfg);\n}\n\n// Same capability-checked open as openStore, but for a caller (only src/watch.ts) that must\n// reconcile again later: keeps the builder's pool warm and hands it back instead of closing it.\nexport async function openStoreForWatch(cfg: ResolvedConfig): Promise<OpenResult & { builder: Builder }> {\n const name = storeName(cfg);\n const entry = entryFor(name);\n\n if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"vectors\" in this build, but this config's \"embed\" block is in use by at least one preset; remove or narrow it, or set \"store\" to a store that supports vectors (sqlite)`);\n }\n\n return entry.openForWatch(cfg);\n}\n\n// watch keeps its connection open for the run, so a file-locking store would fail every\n// other command on the tree; check before open, as openStore does for \"vectors\".\nexport function requireWatchConcurrency(cfg: ResolvedConfig): void {\n const name = storeName(cfg);\n if (!entryFor(name).capabilities.has('watch-concurrency')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"watch-concurrency\" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set \"store\" to \"sqlite\" in sense.config.json`);\n }\n}\n\nexport { clearCache } from './cache.ts';\nexport type { OpenResult } from './open.ts';\nexport { docCount } from './open.ts';\nexport { getMeta, setMeta } from './shared.ts';\nexport { DB_FILENAME, SCHEMA_VERSION } from './sqlite/open.ts';\nexport type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.ts';\nexport { CAPABILITY_NAMES } from './types.ts';\n"],"names":["anyPresetEmbeds","storeName","SenseError","duckdbOpenDialect","openDuckdb","CAPABILITIES","DUCKDB_CAPABILITIES","openWithBuilder","openSqlite","sqliteOpenDialect","SQLITE_CAPABILITIES","openTurso","tursoOpenDialect","TURSO_CAPABILITIES","REGISTRY","sqlite","capabilities","open","openForWatch","cfg","duckdb","turso","entryFor","name","entry","Object","keys","join","openStore","has","openStoreForWatch","requireWatchConcurrency","clearCache","docCount","getMeta","setMeta","DB_FILENAME","SCHEMA_VERSION","CAPABILITY_NAMES"],"mappings":"AAAA,2FAA2F;AAC3F,uDAAuD;AAGvD,SAASA,eAAe,EAAEC,SAAS,QAAQ,qBAAqB;AAChE,SAASC,UAAU,QAAQ,eAAe;AAE1C,SAASC,iBAAiB,EAAEC,UAAU,QAAQ,mBAAmB;AACjE,SAASC,gBAAgBC,mBAAmB,QAAQ,oBAAoB;AAExE,SAASC,eAAe,QAAQ,YAAY;AAC5C,SAASC,UAAU,EAAEC,iBAAiB,QAAQ,mBAAmB;AACjE,SAASJ,gBAAgBK,mBAAmB,QAAQ,oBAAoB;AACxE,SAASC,SAAS,EAAEC,gBAAgB,QAAQ,kBAAkB;AAC9D,SAASP,gBAAgBQ,kBAAkB,QAAQ,mBAAmB;AAWtE,kGAAkG;AAClG,4EAA4E;AAC5E,MAAMC,WAA0C;IAC9CC,QAAQ;QAAEC,cAAcN;QAAqBO,MAAMT;QAAYU,cAAc,CAACC,MAAQZ,gBAAgBY,KAAKV;IAAmB;IAC9HW,QAAQ;QAAEJ,cAAcV;QAAqBW,MAAMb;QAAYc,cAAc,CAACC,MAAQZ,gBAAgBY,KAAKhB;IAAmB;IAC9HkB,OAAO;QAAEL,cAAcH;QAAoBI,MAAMN;QAAWO,cAAc,CAACC,MAAQZ,gBAAgBY,KAAKP;IAAkB;AAC5H;AAEA,SAASU,SAASC,IAAe;IAC/B,MAAMC,QAAQV,QAAQ,CAACS,KAAK;IAC5B,IAAI,CAACC,OAAO,MAAM,IAAItB,WAAW,iBAAiB,CAAC,uBAAuB,EAAEqB,KAAK,cAAc,EAAEE,OAAOC,IAAI,CAACZ,UAAUa,IAAI,CAAC,OAAO;IACnI,OAAOH;AACT;AAEA,OAAO,eAAeI,UAAUT,GAAmB;IACjD,MAAMI,OAAOtB,UAAUkB;IACvB,MAAMK,QAAQF,SAASC;IAEvB,8FAA8F;IAC9F,oJAAoJ;IACpJ,IAAIvB,gBAAgBmB,QAAQ,CAACK,MAAMR,YAAY,CAACa,GAAG,CAAC,YAAY;QAC9D,MAAM,IAAI3B,WAAW,4BAA4B,CAAC,OAAO,EAAEqB,KAAK,6LAA6L,CAAC;IAChQ;IAEA,OAAOC,MAAMP,IAAI,CAACE;AACpB;AAEA,4FAA4F;AAC5F,gGAAgG;AAChG,OAAO,eAAeW,kBAAkBX,GAAmB;IACzD,MAAMI,OAAOtB,UAAUkB;IACvB,MAAMK,QAAQF,SAASC;IAEvB,IAAIvB,gBAAgBmB,QAAQ,CAACK,MAAMR,YAAY,CAACa,GAAG,CAAC,YAAY;QAC9D,MAAM,IAAI3B,WAAW,4BAA4B,CAAC,OAAO,EAAEqB,KAAK,6LAA6L,CAAC;IAChQ;IAEA,OAAOC,MAAMN,YAAY,CAACC;AAC5B;AAEA,wFAAwF;AACxF,iFAAiF;AACjF,OAAO,SAASY,wBAAwBZ,GAAmB;IACzD,MAAMI,OAAOtB,UAAUkB;IACvB,IAAI,CAACG,SAASC,MAAMP,YAAY,CAACa,GAAG,CAAC,sBAAsB;QACzD,MAAM,IAAI3B,WAAW,4BAA4B,CAAC,OAAO,EAAEqB,KAAK,qPAAqP,CAAC;IACxT;AACF;AAEA,SAASS,UAAU,QAAQ,aAAa;AAExC,SAASC,QAAQ,QAAQ,YAAY;AACrC,SAASC,OAAO,EAAEC,OAAO,QAAQ,cAAc;AAC/C,SAASC,WAAW,EAAEC,cAAc,QAAQ,mBAAmB;AAE/D,SAASC,gBAAgB,QAAQ,aAAa"}
@@ -0,0 +1,15 @@
1
+ import type { ResolvedConfig } from '../config/index.js';
2
+ import type { Builder } from './builder.js';
3
+ import type { OpenDialect, Store } from './types.js';
4
+ export interface OpenResult {
5
+ store: Store;
6
+ cfg: ResolvedConfig;
7
+ dbPath: string;
8
+ parsed: number;
9
+ warnings: string[];
10
+ }
11
+ export declare function openWithDialect<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): Promise<OpenResult>;
12
+ export declare function openWithBuilder<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): Promise<OpenResult & {
13
+ builder: Builder;
14
+ }>;
15
+ export declare function docCount(store: Store): Promise<number>;