sensemaking 0.24.7 → 0.25.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 (242) hide show
  1. package/README.md +20 -11
  2. package/dist/cjs/cli/build.d.cts +3 -0
  3. package/dist/cjs/cli/build.d.ts +3 -0
  4. package/dist/cjs/cli/build.js +195 -0
  5. package/dist/cjs/cli/build.js.map +1 -0
  6. package/dist/cjs/cli/index.d.cts +7 -6
  7. package/dist/cjs/cli/index.d.ts +7 -6
  8. package/dist/cjs/cli/index.js +12 -6
  9. package/dist/cjs/cli/index.js.map +1 -1
  10. package/dist/cjs/cli/init.js +2 -2
  11. package/dist/cjs/cli/init.js.map +1 -1
  12. package/dist/cjs/cli/map.js +7 -2
  13. package/dist/cjs/cli/map.js.map +1 -1
  14. package/dist/cjs/cli/named.js +63 -21
  15. package/dist/cjs/cli/named.js.map +1 -1
  16. package/dist/cjs/cli/path.js +7 -2
  17. package/dist/cjs/cli/path.js.map +1 -1
  18. package/dist/cjs/cli/peek.js +7 -2
  19. package/dist/cjs/cli/peek.js.map +1 -1
  20. package/dist/cjs/cli/related.js +55 -6
  21. package/dist/cjs/cli/related.js.map +1 -1
  22. package/dist/cjs/cli/search.js +54 -15
  23. package/dist/cjs/cli/search.js.map +1 -1
  24. package/dist/cjs/cli/shared.d.cts +9 -3
  25. package/dist/cjs/cli/shared.d.ts +9 -3
  26. package/dist/cjs/cli/shared.js +109 -74
  27. package/dist/cjs/cli/shared.js.map +1 -1
  28. package/dist/cjs/cli/sql.js +8 -3
  29. package/dist/cjs/cli/sql.js.map +1 -1
  30. package/dist/cjs/cli/status.js +142 -50
  31. package/dist/cjs/cli/status.js.map +1 -1
  32. package/dist/cjs/cli/types.d.cts +4 -2
  33. package/dist/cjs/cli/types.d.ts +4 -2
  34. package/dist/cjs/cli.js +10 -4
  35. package/dist/cjs/cli.js.map +1 -1
  36. package/dist/cjs/commands/map.js +10 -6
  37. package/dist/cjs/commands/map.js.map +1 -1
  38. package/dist/cjs/commands/peek.js +10 -6
  39. package/dist/cjs/commands/peek.js.map +1 -1
  40. package/dist/cjs/commands/related.d.cts +1 -0
  41. package/dist/cjs/commands/related.d.ts +1 -0
  42. package/dist/cjs/commands/related.js +56 -21
  43. package/dist/cjs/commands/related.js.map +1 -1
  44. package/dist/cjs/commands/scope.js +1 -1
  45. package/dist/cjs/commands/scope.js.map +1 -1
  46. package/dist/cjs/commands/search.js +102 -84
  47. package/dist/cjs/commands/search.js.map +1 -1
  48. package/dist/cjs/config/index.d.cts +1 -1
  49. package/dist/cjs/config/index.d.ts +1 -1
  50. package/dist/cjs/config/index.js.map +1 -1
  51. package/dist/cjs/config/load.d.cts +6 -1
  52. package/dist/cjs/config/load.d.ts +6 -1
  53. package/dist/cjs/config/load.js +38 -1
  54. package/dist/cjs/config/load.js.map +1 -1
  55. package/dist/cjs/config/types.d.cts +2 -1
  56. package/dist/cjs/config/types.d.ts +2 -1
  57. package/dist/cjs/config/types.js +1 -1
  58. package/dist/cjs/config/types.js.map +1 -1
  59. package/dist/cjs/config/validate.js +20 -0
  60. package/dist/cjs/config/validate.js.map +1 -1
  61. package/dist/cjs/embed/query.d.cts +2 -1
  62. package/dist/cjs/embed/query.d.ts +2 -1
  63. package/dist/cjs/embed/query.js +186 -109
  64. package/dist/cjs/embed/query.js.map +1 -1
  65. package/dist/cjs/errors.d.cts +1 -1
  66. package/dist/cjs/errors.d.ts +1 -1
  67. package/dist/cjs/errors.js.map +1 -1
  68. package/dist/cjs/features/embed.js.map +1 -1
  69. package/dist/cjs/graph/traverse.js +11 -6
  70. package/dist/cjs/graph/traverse.js.map +1 -1
  71. package/dist/cjs/index.d.cts +3 -3
  72. package/dist/cjs/index.d.ts +3 -3
  73. package/dist/cjs/index.js +3 -0
  74. package/dist/cjs/index.js.map +1 -1
  75. package/dist/cjs/lib/atomic-write.js +20 -6
  76. package/dist/cjs/lib/atomic-write.js.map +1 -1
  77. package/dist/cjs/lib/serial-query.d.cts +1 -0
  78. package/dist/cjs/lib/serial-query.d.ts +1 -0
  79. package/dist/cjs/lib/serial-query.js +22 -0
  80. package/dist/cjs/lib/serial-query.js.map +1 -0
  81. package/dist/cjs/scan/index.d.cts +5 -0
  82. package/dist/cjs/scan/index.d.ts +5 -0
  83. package/dist/cjs/scan/index.js +12 -4
  84. package/dist/cjs/scan/index.js.map +1 -1
  85. package/dist/cjs/scan/pool.d.cts +5 -0
  86. package/dist/cjs/scan/pool.d.ts +5 -0
  87. package/dist/cjs/scan/pool.js +167 -55
  88. package/dist/cjs/scan/pool.js.map +1 -1
  89. package/dist/cjs/store/builder.d.cts +6 -1
  90. package/dist/cjs/store/builder.d.ts +6 -1
  91. package/dist/cjs/store/builder.js +135 -5
  92. package/dist/cjs/store/builder.js.map +1 -1
  93. package/dist/cjs/store/duckdb/lexical.d.cts +2 -0
  94. package/dist/cjs/store/duckdb/lexical.d.ts +2 -0
  95. package/dist/cjs/store/duckdb/lexical.js +139 -7
  96. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  97. package/dist/cjs/store/duckdb/open.d.cts +3 -3
  98. package/dist/cjs/store/duckdb/open.d.ts +3 -3
  99. package/dist/cjs/store/duckdb/open.js +53 -41
  100. package/dist/cjs/store/duckdb/open.js.map +1 -1
  101. package/dist/cjs/store/duckdb/reconcile.js +2 -2
  102. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  103. package/dist/cjs/store/index.d.cts +23 -4
  104. package/dist/cjs/store/index.d.ts +23 -4
  105. package/dist/cjs/store/index.js +224 -15
  106. package/dist/cjs/store/index.js.map +1 -1
  107. package/dist/cjs/store/open.d.cts +9 -1
  108. package/dist/cjs/store/open.d.ts +9 -1
  109. package/dist/cjs/store/open.js +479 -128
  110. package/dist/cjs/store/open.js.map +1 -1
  111. package/dist/cjs/store/reconcile.js +93 -63
  112. package/dist/cjs/store/reconcile.js.map +1 -1
  113. package/dist/cjs/store/sqlite/open.d.cts +3 -3
  114. package/dist/cjs/store/sqlite/open.d.ts +3 -3
  115. package/dist/cjs/store/sqlite/open.js +44 -36
  116. package/dist/cjs/store/sqlite/open.js.map +1 -1
  117. package/dist/cjs/store/turso/open.d.cts +3 -3
  118. package/dist/cjs/store/turso/open.d.ts +3 -3
  119. package/dist/cjs/store/turso/open.js +83 -66
  120. package/dist/cjs/store/turso/open.js.map +1 -1
  121. package/dist/cjs/store/turso/store.d.cts +3 -1
  122. package/dist/cjs/store/turso/store.d.ts +3 -1
  123. package/dist/cjs/store/turso/store.js +46 -31
  124. package/dist/cjs/store/turso/store.js.map +1 -1
  125. package/dist/cjs/store/types.d.cts +12 -3
  126. package/dist/cjs/store/types.d.ts +12 -3
  127. package/dist/cjs/store/types.js.map +1 -1
  128. package/dist/cjs/watch.js +155 -35
  129. package/dist/cjs/watch.js.map +1 -1
  130. package/dist/esm/cli/build.d.ts +3 -0
  131. package/dist/esm/cli/build.js +18 -0
  132. package/dist/esm/cli/build.js.map +1 -0
  133. package/dist/esm/cli/index.d.ts +7 -6
  134. package/dist/esm/cli/index.js +8 -6
  135. package/dist/esm/cli/index.js.map +1 -1
  136. package/dist/esm/cli/init.js +2 -2
  137. package/dist/esm/cli/init.js.map +1 -1
  138. package/dist/esm/cli/map.js +8 -2
  139. package/dist/esm/cli/map.js.map +1 -1
  140. package/dist/esm/cli/named.js +38 -14
  141. package/dist/esm/cli/named.js.map +1 -1
  142. package/dist/esm/cli/path.js +8 -2
  143. package/dist/esm/cli/path.js.map +1 -1
  144. package/dist/esm/cli/peek.js +8 -2
  145. package/dist/esm/cli/peek.js.map +1 -1
  146. package/dist/esm/cli/related.js +21 -3
  147. package/dist/esm/cli/related.js.map +1 -1
  148. package/dist/esm/cli/search.js +28 -7
  149. package/dist/esm/cli/search.js.map +1 -1
  150. package/dist/esm/cli/shared.d.ts +9 -3
  151. package/dist/esm/cli/shared.js +44 -24
  152. package/dist/esm/cli/shared.js.map +1 -1
  153. package/dist/esm/cli/sql.js +8 -2
  154. package/dist/esm/cli/sql.js.map +1 -1
  155. package/dist/esm/cli/status.js +166 -111
  156. package/dist/esm/cli/status.js.map +1 -1
  157. package/dist/esm/cli/types.d.ts +4 -2
  158. package/dist/esm/cli/types.js.map +1 -1
  159. package/dist/esm/cli.js +9 -4
  160. package/dist/esm/cli.js.map +1 -1
  161. package/dist/esm/commands/map.js +5 -1
  162. package/dist/esm/commands/map.js.map +1 -1
  163. package/dist/esm/commands/peek.js +5 -1
  164. package/dist/esm/commands/peek.js.map +1 -1
  165. package/dist/esm/commands/related.d.ts +1 -0
  166. package/dist/esm/commands/related.js +19 -10
  167. package/dist/esm/commands/related.js.map +1 -1
  168. package/dist/esm/commands/scope.js +1 -1
  169. package/dist/esm/commands/scope.js.map +1 -1
  170. package/dist/esm/commands/search.js +11 -10
  171. package/dist/esm/commands/search.js.map +1 -1
  172. package/dist/esm/config/index.d.ts +1 -1
  173. package/dist/esm/config/index.js.map +1 -1
  174. package/dist/esm/config/load.d.ts +6 -1
  175. package/dist/esm/config/load.js +32 -3
  176. package/dist/esm/config/load.js.map +1 -1
  177. package/dist/esm/config/types.d.ts +2 -1
  178. package/dist/esm/config/types.js +1 -1
  179. package/dist/esm/config/types.js.map +1 -1
  180. package/dist/esm/config/validate.js +20 -0
  181. package/dist/esm/config/validate.js.map +1 -1
  182. package/dist/esm/embed/query.d.ts +2 -1
  183. package/dist/esm/embed/query.js +40 -34
  184. package/dist/esm/embed/query.js.map +1 -1
  185. package/dist/esm/errors.d.ts +1 -1
  186. package/dist/esm/errors.js.map +1 -1
  187. package/dist/esm/features/embed.js.map +1 -1
  188. package/dist/esm/graph/traverse.js +5 -1
  189. package/dist/esm/graph/traverse.js.map +1 -1
  190. package/dist/esm/index.d.ts +3 -3
  191. package/dist/esm/index.js +1 -1
  192. package/dist/esm/index.js.map +1 -1
  193. package/dist/esm/lib/atomic-write.js +14 -6
  194. package/dist/esm/lib/atomic-write.js.map +1 -1
  195. package/dist/esm/lib/serial-query.d.ts +1 -0
  196. package/dist/esm/lib/serial-query.js +13 -0
  197. package/dist/esm/lib/serial-query.js.map +1 -0
  198. package/dist/esm/scan/index.d.ts +5 -0
  199. package/dist/esm/scan/index.js +7 -3
  200. package/dist/esm/scan/index.js.map +1 -1
  201. package/dist/esm/scan/pool.d.ts +5 -0
  202. package/dist/esm/scan/pool.js +41 -16
  203. package/dist/esm/scan/pool.js.map +1 -1
  204. package/dist/esm/store/builder.d.ts +6 -1
  205. package/dist/esm/store/builder.js +19 -0
  206. package/dist/esm/store/builder.js.map +1 -1
  207. package/dist/esm/store/duckdb/lexical.d.ts +2 -0
  208. package/dist/esm/store/duckdb/lexical.js +32 -4
  209. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  210. package/dist/esm/store/duckdb/open.d.ts +3 -3
  211. package/dist/esm/store/duckdb/open.js +13 -6
  212. package/dist/esm/store/duckdb/open.js.map +1 -1
  213. package/dist/esm/store/duckdb/reconcile.js +2 -2
  214. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  215. package/dist/esm/store/index.d.ts +23 -4
  216. package/dist/esm/store/index.js +80 -15
  217. package/dist/esm/store/index.js.map +1 -1
  218. package/dist/esm/store/open.d.ts +9 -1
  219. package/dist/esm/store/open.js +175 -84
  220. package/dist/esm/store/open.js.map +1 -1
  221. package/dist/esm/store/reconcile.js +12 -0
  222. package/dist/esm/store/reconcile.js.map +1 -1
  223. package/dist/esm/store/sqlite/open.d.ts +3 -3
  224. package/dist/esm/store/sqlite/open.js +9 -6
  225. package/dist/esm/store/sqlite/open.js.map +1 -1
  226. package/dist/esm/store/turso/open.d.ts +3 -3
  227. package/dist/esm/store/turso/open.js +14 -10
  228. package/dist/esm/store/turso/open.js.map +1 -1
  229. package/dist/esm/store/turso/store.d.ts +3 -1
  230. package/dist/esm/store/turso/store.js +5 -1
  231. package/dist/esm/store/turso/store.js.map +1 -1
  232. package/dist/esm/store/types.d.ts +12 -3
  233. package/dist/esm/store/types.js.map +1 -1
  234. package/dist/esm/watch.js +57 -19
  235. package/dist/esm/watch.js.map +1 -1
  236. package/package.json +1 -1
  237. package/schema.json +7 -2
  238. package/skills/sense/SKILL.md +3 -3
  239. package/skills/sense-bases/SKILL.md +1 -2
  240. package/skills/sense-setup/SKILL.md +1 -1
  241. package/skills/sense-setup/references/embeddings.md +1 -1
  242. package/skills/sense-setup/references/store-benchmarks.md +6 -6
@@ -1 +1 @@
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';\nimport { createVectorWriteStage } from './vectors.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 = '4';\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 await createVectorWriteStage(handle.duckdb, STORE_DIMS);\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","createVectorWriteStage","DB_FILENAME","SCHEMA_VERSION","ensureSchema","handle","conn","cfg","exec","feature","name","duckdb","schema","close","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
+ {"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 { InternalOpenOptions, 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 { assertFtsReady, prepareFts } from './lexical.ts';\nimport { DUCKDB_PACKAGE, duckdbApi } from './native.ts';\nimport { duckdbDialect } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\nimport { createVectorWriteStage } from './vectors.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 = '5';\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): build prepares the persistent FTS index over it,\n// while contains() verification reads the table directly.\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 indexed_sources (\"path\" TEXT PRIMARY KEY, text TEXT NOT NULL)`);\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 await createVectorWriteStage(handle.duckdb, STORE_DIMS);\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, options: { existingOnly: boolean; observational: boolean }): 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, options.observational ? { access_mode: 'READ_ONLY' } : { 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 prepareLexical: prepareFts,\n assertLexicalReady: assertFtsReady,\n createStore: (handle, conn) => createStore(handle.instance, handle.duckdb, conn),\n};\n\nexport async function openDuckdb(cfg: ResolvedConfig, options?: InternalOpenOptions): Promise<OpenResult> {\n return openWithDialect(cfg, duckdbOpenDialect, options);\n}\n"],"names":["featureSignature","STORE_DIMS","SenseError","activeFeatures","FEATURES","openWithDialect","getMeta","setMeta","createConnection","assertFtsReady","prepareFts","DUCKDB_PACKAGE","duckdbApi","duckdbDialect","registerFunctions","createStore","createVectorWriteStage","DB_FILENAME","SCHEMA_VERSION","ensureSchema","handle","conn","cfg","exec","feature","name","duckdb","schema","close","disconnectSync","instance","closeSync","connect","dbPath","_cfg","options","DuckDBInstance","err","message","create","observational","access_mode","storage_compatibility_version","duckdbOpenDialect","filename","schemaVersion","reconcileDialect","isLocked","test","prepareLexical","assertLexicalReady","openDuckdb"],"mappings":"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"}
@@ -32,8 +32,8 @@ async function reconcileContent(conn, touched, docs, _delta) {
32
32
  // The delete above cleared every touched path, and duckdb holds the cache file for its whole
33
33
  // connection, so no second writer can have landed one of these rows: nothing can conflict.
34
34
  await appendRows(conn, 'content', CONTENT_COLUMNS, INSERT_CONTENT_SQL, docs.map(contentRow));
35
- // content changed: the fts index is rebuilt lazily, on the next lexical query that needs it,
36
- // not here. Awaited so the persisted mark (lexical.ts) commits with this same transaction.
35
+ // Build prepares the FTS index after reconcile. This persisted mark commits with the content
36
+ // change, so an interrupted build cannot expose stale lexical results.
37
37
  await markContentStale(conn);
38
38
  }
39
39
  // DuckDB rejects more than one ALTER command per statement ("Parser Error: Only one ALTER
@@ -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 { appendRows, 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 CONTENT_COLUMNS = ['path', 'title', 'summary', 'text'];\nconst INSERT_CONTENT_SQL = `INSERT INTO content (${CONTENT_COLUMNS.map(quoteIdent).join(', ')}) 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 // The delete above cleared every touched path, and duckdb holds the cache file for its whole\n // connection, so no second writer can have landed one of these rows: nothing can conflict.\n await appendRows(conn, 'content', CONTENT_COLUMNS, 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. Awaited so the persisted mark (lexical.ts) commits with this same transaction.\n await 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","appendRows","quoteIdent","markContentStale","CONTENT_COLUMNS","INSERT_CONTENT_SQL","map","join","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcileContent","conn","touched","docs","_delta","length","runBatch","p","addColumns","names","exec","name","duckdbDialect","beginMode","checkColumnLimit","count"],"mappings":"AAAA,SAASA,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,UAAU,EAAEC,UAAU,QAAQ,eAAe;AAEtD,SAASC,gBAAgB,QAAQ,eAAe;AAEhD,qFAAqF;AACrF,+FAA+F;AAC/F,4FAA4F;AAC5F,kFAAkF;AAElF,0FAA0F;AAC1F,uEAAuE;AACvE,MAAMC,kBAAkB;IAAC;IAAQ;IAAS;IAAW;CAAO;AAC5D,MAAMC,qBAAqB,CAAC,qBAAqB,EAAED,gBAAgBE,GAAG,CAACJ,YAAYK,IAAI,CAAC,MAAM,qBAAqB,CAAC;AAEpH,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,QAAQZ,GAAG,CAAC,CAACiB,IAAM;YAACA;SAAE;IAE1B,6FAA6F;IAC7F,2FAA2F;IAC3F,MAAMtB,WAAWgB,MAAM,WAAWb,iBAAiBC,oBAAoBc,KAAKb,GAAG,CAACE;IAChF,6FAA6F;IAC7F,2FAA2F;IAC3F,MAAML,iBAAiBc;AACzB;AAEA,0FAA0F;AAC1F,+FAA+F;AAC/F,oGAAoG;AACpG,+FAA+F;AAC/F,qCAAqC;AACrC,eAAeO,WAAWP,IAAgB,EAAEQ,KAAe;IACzD,IAAIA,MAAMJ,MAAM,KAAK,GAAG;IACxB,MAAMJ,KAAKS,IAAI,CAACD,MAAMnB,GAAG,CAAC,CAACqB,OAAS,CAAC,mCAAmC,EAAEzB,WAAWyB,MAAM,QAAQ,CAAC,EAAEpB,IAAI,CAAC;AAC7G;AAEA,OAAO,MAAMqB,gBAAkC;IAC7CC,WAAW,IAAM;IACjBC,kBAAiBC,KAAK;QACpB,IAAIA,QAAQhB,yBAAyB;YACnC,MAAM,IAAIf,WAAW,gBAAgB,CAAC,uBAAuB,EAAE+B,MAAM,8CAA8C,EAAEhB,wBAAwB,gIAAgI,CAAC;QAChR;IACF;IACAS;IACAR;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 { appendRows, 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 CONTENT_COLUMNS = ['path', 'title', 'summary', 'text'];\nconst INSERT_CONTENT_SQL = `INSERT INTO content (${CONTENT_COLUMNS.map(quoteIdent).join(', ')}) 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 // The delete above cleared every touched path, and duckdb holds the cache file for its whole\n // connection, so no second writer can have landed one of these rows: nothing can conflict.\n await appendRows(conn, 'content', CONTENT_COLUMNS, INSERT_CONTENT_SQL, docs.map(contentRow));\n // Build prepares the FTS index after reconcile. This persisted mark commits with the content\n // change, so an interrupted build cannot expose stale lexical results.\n await 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","appendRows","quoteIdent","markContentStale","CONTENT_COLUMNS","INSERT_CONTENT_SQL","map","join","contentRow","doc","relPath","search","title","summary","text","MAX_FRONTMATTER_COLUMNS","reconcileContent","conn","touched","docs","_delta","length","runBatch","p","addColumns","names","exec","name","duckdbDialect","beginMode","checkColumnLimit","count"],"mappings":"AAAA,SAASA,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,UAAU,EAAEC,UAAU,QAAQ,eAAe;AAEtD,SAASC,gBAAgB,QAAQ,eAAe;AAEhD,qFAAqF;AACrF,+FAA+F;AAC/F,4FAA4F;AAC5F,kFAAkF;AAElF,0FAA0F;AAC1F,uEAAuE;AACvE,MAAMC,kBAAkB;IAAC;IAAQ;IAAS;IAAW;CAAO;AAC5D,MAAMC,qBAAqB,CAAC,qBAAqB,EAAED,gBAAgBE,GAAG,CAACJ,YAAYK,IAAI,CAAC,MAAM,qBAAqB,CAAC;AAEpH,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,QAAQZ,GAAG,CAAC,CAACiB,IAAM;YAACA;SAAE;IAE1B,6FAA6F;IAC7F,2FAA2F;IAC3F,MAAMtB,WAAWgB,MAAM,WAAWb,iBAAiBC,oBAAoBc,KAAKb,GAAG,CAACE;IAChF,6FAA6F;IAC7F,uEAAuE;IACvE,MAAML,iBAAiBc;AACzB;AAEA,0FAA0F;AAC1F,+FAA+F;AAC/F,oGAAoG;AACpG,+FAA+F;AAC/F,qCAAqC;AACrC,eAAeO,WAAWP,IAAgB,EAAEQ,KAAe;IACzD,IAAIA,MAAMJ,MAAM,KAAK,GAAG;IACxB,MAAMJ,KAAKS,IAAI,CAACD,MAAMnB,GAAG,CAAC,CAACqB,OAAS,CAAC,mCAAmC,EAAEzB,WAAWyB,MAAM,QAAQ,CAAC,EAAEpB,IAAI,CAAC;AAC7G;AAEA,OAAO,MAAMqB,gBAAkC;IAC7CC,WAAW,IAAM;IACjBC,kBAAiBC,KAAK;QACpB,IAAIA,QAAQhB,yBAAyB;YACnC,MAAM,IAAIf,WAAW,gBAAgB,CAAC,uBAAuB,EAAE+B,MAAM,8CAA8C,EAAEhB,wBAAwB,gIAAgI,CAAC;QAChR;IACF;IACAS;IACAR;AACF,EAAE"}
@@ -1,11 +1,30 @@
1
1
  import type { ResolvedConfig } from '../config/index.js';
2
- import type { OpenResult } from './open.js';
3
- export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
4
- export { clearCache } from './cache.js';
5
- export type { OpenResult } from './open.js';
2
+ import { clearCache } from './cache.js';
3
+ import type { InternalOpenOptions, OpenOptions, OpenResult } from './open.js';
4
+ import type { Stages } from './stages.js';
5
+ export declare function openStoreFor(cfg: ResolvedConfig, options: InternalOpenOptions): Promise<OpenResult>;
6
+ export declare function openStore(cfg: ResolvedConfig, options?: OpenOptions): Promise<OpenResult>;
7
+ export declare function lexicalReadiness(cfg: ResolvedConfig): Promise<{
8
+ ready: boolean;
9
+ error: string | null;
10
+ }>;
11
+ export interface BuildOptions {
12
+ force?: boolean;
13
+ }
14
+ export interface BuildResult {
15
+ cfg: ResolvedConfig;
16
+ dbPath: string;
17
+ parsed: number;
18
+ warnings: string[];
19
+ stages: Stages;
20
+ }
21
+ export declare function buildIndex(cfg: ResolvedConfig, options?: BuildOptions): Promise<BuildResult>;
22
+ export declare function storeFilename(cfg: ResolvedConfig): string;
23
+ export type { OpenOptions, OpenResult } from './open.js';
6
24
  export { docCount } from './open.js';
7
25
  export { getMeta, setMeta } from './shared.js';
8
26
  export { DB_FILENAME, SCHEMA_VERSION } from './sqlite/open.js';
9
27
  export type { Stages } from './stages.js';
10
28
  export type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.js';
11
29
  export { CAPABILITY_NAMES } from './types.js';
30
+ export { clearCache };
@@ -1,26 +1,28 @@
1
- // Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything
2
- // beyond frontmatter + content lives in src/features/.
1
+ // Rows -> a backing store: registry + the public open/build boundary. Parsing lives in scan.ts;
2
+ // everything 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 { clearCache } from './cache.js';
6
+ import { DB_FILENAME as DUCKDB_FILENAME, openDuckdb } from './duckdb/open.js';
6
7
  import { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.js';
7
- import { openSqlite } from './sqlite/open.js';
8
+ import { openSqlite, DB_FILENAME as SQLITE_FILENAME } 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, DB_FILENAME as TURSO_FILENAME } from './turso/open.js';
10
11
  import { CAPABILITIES as TURSO_CAPABILITIES } from './turso/store.js';
11
- // Capabilities are checked against the registry entry before opening (no wasted connect/reconcile
12
- // work for a config that cannot be satisfied), then the store itself opens.
13
12
  const REGISTRY = {
14
13
  sqlite: {
15
14
  capabilities: SQLITE_CAPABILITIES,
15
+ filename: SQLITE_FILENAME,
16
16
  open: openSqlite
17
17
  },
18
18
  duckdb: {
19
19
  capabilities: DUCKDB_CAPABILITIES,
20
+ filename: DUCKDB_FILENAME,
20
21
  open: openDuckdb
21
22
  },
22
23
  turso: {
23
24
  capabilities: TURSO_CAPABILITIES,
25
+ filename: TURSO_FILENAME,
24
26
  open: openTurso
25
27
  }
26
28
  };
@@ -29,18 +31,81 @@ function entryFor(name) {
29
31
  if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store "${name}"; available: ${Object.keys(REGISTRY).join(', ')}`);
30
32
  return entry;
31
33
  }
32
- export async function openStore(cfg) {
33
- const name = storeName(cfg);
34
- const entry = entryFor(name);
35
- // The one unambiguous, config-level capability need: an embed block some preset actually uses
36
- // 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.
37
- if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {
34
+ function requirementsFor(cfg) {
35
+ const requirements = new Set([
36
+ 'core',
37
+ 'lexical'
38
+ ]);
39
+ if (anyPresetEmbeds(cfg)) requirements.add('vectors');
40
+ return requirements;
41
+ }
42
+ function validateRequirements(cfg, entry, requirements) {
43
+ if (requirements.has('vectors') && anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {
44
+ const name = storeName(cfg);
38
45
  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)`);
39
46
  }
40
- return entry.open(cfg);
41
47
  }
42
- export { clearCache } from './cache.js';
48
+ export async function openStoreFor(cfg, options) {
49
+ var _options_requirements;
50
+ const entry = entryFor(storeName(cfg));
51
+ validateRequirements(cfg, entry, (_options_requirements = options.requirements) !== null && _options_requirements !== void 0 ? _options_requirements : new Set([
52
+ 'core'
53
+ ]));
54
+ return entry.open(cfg, options);
55
+ }
56
+ export async function openStore(cfg, options = {}) {
57
+ const buildEnabled = options.build !== false;
58
+ return openStoreFor(cfg, {
59
+ build: buildEnabled,
60
+ requirements: buildEnabled ? requirementsFor(cfg) : new Set([
61
+ 'core'
62
+ ])
63
+ });
64
+ }
65
+ export async function lexicalReadiness(cfg) {
66
+ let opened;
67
+ try {
68
+ opened = await openStoreFor(cfg, {
69
+ build: false,
70
+ requirements: new Set([
71
+ 'core',
72
+ 'lexical'
73
+ ])
74
+ });
75
+ } catch (err) {
76
+ if (err instanceof SenseError && err.code === 'INDEX_NOT_READY') return {
77
+ ready: false,
78
+ error: err.message
79
+ };
80
+ throw err;
81
+ }
82
+ try {
83
+ return {
84
+ ready: true,
85
+ error: null
86
+ };
87
+ } finally{
88
+ await opened.store.close();
89
+ }
90
+ }
91
+ export async function buildIndex(cfg, options = {}) {
92
+ if (options.force) clearCache(cfg);
93
+ const opened = await openStoreFor(cfg, {
94
+ build: true,
95
+ requirements: requirementsFor(cfg)
96
+ });
97
+ try {
98
+ const { store: _store, ...result } = opened;
99
+ return result;
100
+ } finally{
101
+ await opened.store.close();
102
+ }
103
+ }
104
+ export function storeFilename(cfg) {
105
+ return entryFor(storeName(cfg)).filename;
106
+ }
43
107
  export { docCount } from './open.js';
44
108
  export { getMeta, setMeta } from './shared.js';
45
109
  export { DB_FILENAME, SCHEMA_VERSION } from './sqlite/open.js';
46
110
  export { CAPABILITY_NAMES } from './types.js';
111
+ export { clearCache };
@@ -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 './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\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 { Stages } from './stages.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","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;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,SAASE,UAAU,QAAQ,aAAa;AAExC,SAASC,QAAQ,QAAQ,YAAY;AACrC,SAASC,OAAO,EAAEC,OAAO,QAAQ,cAAc;AAC/C,SAASC,WAAW,EAAEC,cAAc,QAAQ,mBAAmB;AAG/D,SAASC,gBAAgB,QAAQ,aAAa"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + the public open/build boundary. Parsing lives in scan.ts;\n// everything 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 { clearCache } from './cache.ts';\nimport { DB_FILENAME as DUCKDB_FILENAME, openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { BuildRequirement, InternalOpenOptions, OpenOptions, OpenResult } from './open.ts';\nimport { openSqlite, DB_FILENAME as SQLITE_FILENAME } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport type { Stages } from './stages.ts';\nimport { openTurso, DB_FILENAME as TURSO_FILENAME } 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 filename: string;\n open: (cfg: ResolvedConfig, options?: InternalOpenOptions) => Promise<OpenResult>;\n}\n\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, filename: SQLITE_FILENAME, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, filename: DUCKDB_FILENAME, open: openDuckdb },\n turso: { capabilities: TURSO_CAPABILITIES, filename: TURSO_FILENAME, 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\nfunction requirementsFor(cfg: ResolvedConfig): Set<BuildRequirement> {\n const requirements = new Set<BuildRequirement>(['core', 'lexical']);\n if (anyPresetEmbeds(cfg)) requirements.add('vectors');\n return requirements;\n}\n\nfunction validateRequirements(cfg: ResolvedConfig, entry: StoreEntry, requirements: ReadonlySet<BuildRequirement>): void {\n if (requirements.has('vectors') && anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n const name = storeName(cfg);\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\nexport async function openStoreFor(cfg: ResolvedConfig, options: InternalOpenOptions): Promise<OpenResult> {\n const entry = entryFor(storeName(cfg));\n validateRequirements(cfg, entry, options.requirements ?? new Set<BuildRequirement>(['core']));\n return entry.open(cfg, options);\n}\n\nexport async function openStore(cfg: ResolvedConfig, options: OpenOptions = {}): Promise<OpenResult> {\n const buildEnabled = options.build !== false;\n return openStoreFor(cfg, {\n build: buildEnabled,\n requirements: buildEnabled ? requirementsFor(cfg) : new Set<BuildRequirement>(['core']),\n });\n}\n\nexport async function lexicalReadiness(cfg: ResolvedConfig): Promise<{ ready: boolean; error: string | null }> {\n let opened: OpenResult;\n try {\n opened = await openStoreFor(cfg, { build: false, requirements: new Set<BuildRequirement>(['core', 'lexical']) });\n } catch (err) {\n if (err instanceof SenseError && err.code === 'INDEX_NOT_READY') return { ready: false, error: err.message };\n throw err;\n }\n try {\n return { ready: true, error: null };\n } finally {\n await opened.store.close();\n }\n}\n\nexport interface BuildOptions {\n force?: boolean;\n}\n\nexport interface BuildResult {\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n stages: Stages;\n}\n\nexport async function buildIndex(cfg: ResolvedConfig, options: BuildOptions = {}): Promise<BuildResult> {\n if (options.force) clearCache(cfg);\n const opened = await openStoreFor(cfg, { build: true, requirements: requirementsFor(cfg) });\n try {\n const { store: _store, ...result } = opened;\n return result;\n } finally {\n await opened.store.close();\n }\n}\n\nexport function storeFilename(cfg: ResolvedConfig): string {\n return entryFor(storeName(cfg)).filename;\n}\n\nexport type { OpenOptions, 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 { Stages } from './stages.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';\nexport { clearCache };\n"],"names":["anyPresetEmbeds","storeName","SenseError","clearCache","DB_FILENAME","DUCKDB_FILENAME","openDuckdb","CAPABILITIES","DUCKDB_CAPABILITIES","openSqlite","SQLITE_FILENAME","SQLITE_CAPABILITIES","openTurso","TURSO_FILENAME","TURSO_CAPABILITIES","REGISTRY","sqlite","capabilities","filename","open","duckdb","turso","entryFor","name","entry","Object","keys","join","requirementsFor","cfg","requirements","Set","add","validateRequirements","has","openStoreFor","options","openStore","buildEnabled","build","lexicalReadiness","opened","err","code","ready","error","message","store","close","buildIndex","force","_store","result","storeFilename","docCount","getMeta","setMeta","SCHEMA_VERSION","CAPABILITY_NAMES"],"mappings":"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"}
@@ -1,6 +1,13 @@
1
1
  import type { ResolvedConfig } from '../config/index.js';
2
2
  import type { Stages } from './stages.js';
3
3
  import type { OpenDialect, Store } from './types.js';
4
+ export type BuildRequirement = 'core' | 'lexical' | 'vectors';
5
+ export interface OpenOptions {
6
+ build?: boolean;
7
+ }
8
+ export interface InternalOpenOptions extends OpenOptions {
9
+ requirements?: ReadonlySet<BuildRequirement>;
10
+ }
4
11
  export interface OpenResult {
5
12
  store: Store;
6
13
  cfg: ResolvedConfig;
@@ -9,5 +16,6 @@ export interface OpenResult {
9
16
  warnings: string[];
10
17
  stages: Stages;
11
18
  }
12
- export declare function openWithDialect<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): Promise<OpenResult>;
19
+ export declare function prepareDocumentEmbeddings(store: Store, cfg: ResolvedConfig, allowed?: ReadonlySet<string>): Promise<void>;
20
+ export declare function openWithDialect<Handle>(input: ResolvedConfig, dialect: OpenDialect<Handle>, options?: InternalOpenOptions): Promise<OpenResult>;
13
21
  export declare function docCount(store: Store): Promise<number>;