sensemaking 0.23.0 → 0.23.1

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 (39) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/store/batch.d.cts +4 -0
  3. package/dist/cjs/store/batch.d.ts +4 -0
  4. package/dist/cjs/store/batch.js +80 -0
  5. package/dist/cjs/store/batch.js.map +1 -0
  6. package/dist/cjs/store/duckdb/batch.d.cts +0 -4
  7. package/dist/cjs/store/duckdb/batch.d.ts +0 -4
  8. package/dist/cjs/store/duckdb/batch.js +5 -27
  9. package/dist/cjs/store/duckdb/batch.js.map +1 -1
  10. package/dist/cjs/store/turso/connection.d.cts +3 -0
  11. package/dist/cjs/store/turso/connection.d.ts +3 -0
  12. package/dist/cjs/store/turso/connection.js +300 -30
  13. package/dist/cjs/store/turso/connection.js.map +1 -1
  14. package/dist/cjs/store/turso/native.d.cts +4 -0
  15. package/dist/cjs/store/turso/native.d.ts +4 -0
  16. package/dist/cjs/store/turso/native.js +9 -0
  17. package/dist/cjs/store/turso/native.js.map +1 -1
  18. package/dist/cjs/store/turso/open.js +81 -8
  19. package/dist/cjs/store/turso/open.js.map +1 -1
  20. package/dist/cjs/store/turso/store.js +103 -1
  21. package/dist/cjs/store/turso/store.js.map +1 -1
  22. package/dist/esm/store/batch.d.ts +4 -0
  23. package/dist/esm/store/batch.js +21 -0
  24. package/dist/esm/store/batch.js.map +1 -0
  25. package/dist/esm/store/duckdb/batch.d.ts +0 -4
  26. package/dist/esm/store/duckdb/batch.js +2 -19
  27. package/dist/esm/store/duckdb/batch.js.map +1 -1
  28. package/dist/esm/store/turso/connection.d.ts +3 -0
  29. package/dist/esm/store/turso/connection.js +58 -3
  30. package/dist/esm/store/turso/connection.js.map +1 -1
  31. package/dist/esm/store/turso/native.d.ts +4 -0
  32. package/dist/esm/store/turso/native.js +8 -0
  33. package/dist/esm/store/turso/native.js.map +1 -1
  34. package/dist/esm/store/turso/open.js +5 -5
  35. package/dist/esm/store/turso/open.js.map +1 -1
  36. package/dist/esm/store/turso/store.js +29 -2
  37. package/dist/esm/store/turso/store.js.map +1 -1
  38. package/package.json +1 -1
  39. package/skills/sense-setup/SKILL.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/open.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\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 { checkpointWal, createConnection } from './connection.ts';\nimport { TURSO_PACKAGE, tursoApi } from './native.ts';\nimport { CONTENT_FTS_DDL, tursoDialect } from './reconcile.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.turso.db';\n// Independent of sqlite's and duckdb's SCHEMA_VERSION: each store's cache shape evolves\n// separately. Covers the FTS indexes, the \"_ngram\" sidecar columns, and embeddings.vector's width.\nexport const SCHEMA_VERSION = '5';\n\nexport type { OpenResult };\n\n// This store's Handle (types.ts's OpenDialect<Handle>) is the connected Database itself: no\n// extra native state to thread, unlike duckdb's separate instance/connection pair.\nasync function ensureSchema(_handle: Database, conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);\n for (const ddl of CONTENT_FTS_DDL) await conn.exec(ddl);\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 F32_BLOB(STORE_DIMS) instead of the embed feature's engine-neutral BLOB DDL. `scale`\n // is kept unused, so the shared reconcile-time INSERT/DELETE names a column both stores have.\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 F32_BLOB(${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 close(handle: Database): Promise<void> {\n await checkpointWal(handle);\n await handle.close();\n}\n\nasync function setDerivedBusyTimeout(_handle: Database, conn: Connection, ms: number): Promise<void> {\n await conn.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: Database; conn: Connection }> {\n // Dynamic, not a top-level import: a sqlite or duckdb tree must never attempt to resolve this\n // optional dependency until a turso tree is actually opened. Installed on first use if missing.\n let turso: Awaited<ReturnType<typeof tursoApi>>;\n try {\n turso = await tursoApi();\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" needs the ${TURSO_PACKAGE} package (${(err as Error).message})`);\n }\n\n let db: Database;\n try {\n // Floored at the same 30s sqlite opens with. `timeout` is connect-time only in this client;\n // the derived value is set via runtime PRAGMA below. `index_method` is required for ensureSchema()'s FTS indexes (T1).\n db = await turso.connect(dbPath, { timeout: 30_000, experimental: ['index_method'] });\n } catch (err) {\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n return { handle: db, conn: createConnection(db) };\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const tursoOpenDialect: OpenDialect<Database> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: tursoDialect,\n connect,\n close,\n // \"Locking error: Failed locking file ...\", worded per platform: posix \"File is locked by another\n // process\", Windows \"another process has locked a portion of the file (os error 33)\". Distinct from\n // the write-time \"database is locked\" its connect-time `timeout` covers; that one never reaches here.\n isLocked: (err) => /File is locked by another process|locked a portion of the file/.test(err.message),\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn) => createStore(handle, conn),\n};\n\nexport async function openTurso(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, tursoOpenDialect);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","openTurso","tursoOpenDialect","ensureSchema","_handle","conn","cfg","ddl","feature","exec","CONTENT_FTS_DDL","activeFeatures","name","STORE_DIMS","schema","getMeta","setMeta","featureSignature","FEATURES","close","handle","checkpointWal","setDerivedBusyTimeout","ms","connect","dbPath","_cfg","turso","err","db","tursoApi","SenseError","TURSO_PACKAGE","message","timeout","experimental","createConnection","filename","schemaVersion","reconcileDialect","tursoDialect","isLocked","test","createStore","openWithDialect"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/open.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\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 { checkpointWal, createConnection } from './connection.ts';\nimport { CONNECT_OPTS, TURSO_PACKAGE, tursoApi } from './native.ts';\nimport { CONTENT_FTS_DDL, tursoDialect } from './reconcile.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.turso.db';\n// Independent of sqlite's and duckdb's SCHEMA_VERSION: each store's cache shape evolves\n// separately. Covers the FTS indexes, the \"_ngram\" sidecar columns, and embeddings.vector's width.\nexport const SCHEMA_VERSION = '5';\n\nexport type { OpenResult };\n\n// This store's Handle (types.ts's OpenDialect<Handle>) is the connected Database itself: no\n// extra native state to thread, unlike duckdb's separate instance/connection pair.\nasync function ensureSchema(_handle: Database, conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);\n for (const ddl of CONTENT_FTS_DDL) await conn.exec(ddl);\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 F32_BLOB(STORE_DIMS) instead of the embed feature's engine-neutral BLOB DDL. `scale`\n // is kept unused, so the shared reconcile-time INSERT/DELETE names a column both stores have.\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 F32_BLOB(${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 close(handle: Database): Promise<void> {\n await checkpointWal(handle);\n await handle.close();\n}\n\nasync function setDerivedBusyTimeout(_handle: Database, conn: Connection, ms: number): Promise<void> {\n await conn.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: Database; conn: Connection }> {\n // Dynamic, not a top-level import: a sqlite or duckdb tree must never attempt to resolve this\n // optional dependency until a turso tree is actually opened. Installed on first use if missing.\n let turso: Awaited<ReturnType<typeof tursoApi>>;\n try {\n turso = await tursoApi();\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" needs the ${TURSO_PACKAGE} package (${(err as Error).message})`);\n }\n\n let db: Database;\n try {\n // `timeout` is connect-time only in this client; the derived value is set via runtime PRAGMA\n // below. Options in native.ts, shared with reclaimSpace's own connection.\n db = await turso.connect(dbPath, { ...CONNECT_OPTS, experimental: [...CONNECT_OPTS.experimental] });\n } catch (err) {\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n return { handle: db, conn: createConnection(db) };\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const tursoOpenDialect: OpenDialect<Database> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: tursoDialect,\n connect,\n close,\n // \"Locking error: Failed locking file ...\", worded per platform: posix \"File is locked by another\n // process\", Windows \"another process has locked a portion of the file (os error 33)\". Distinct from\n // the write-time \"database is locked\" its connect-time `timeout` covers; that one never reaches here.\n isLocked: (err) => /File is locked by another process|locked a portion of the file/.test(err.message),\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn) => createStore(handle, conn),\n};\n\nexport async function openTurso(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, tursoOpenDialect);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","openTurso","tursoOpenDialect","ensureSchema","_handle","conn","cfg","ddl","feature","exec","CONTENT_FTS_DDL","activeFeatures","name","STORE_DIMS","schema","getMeta","setMeta","featureSignature","FEATURES","close","handle","checkpointWal","setDerivedBusyTimeout","ms","connect","dbPath","_cfg","turso","err","db","tursoApi","SenseError","TURSO_PACKAGE","message","CONNECT_OPTS","experimental","createConnection","filename","schemaVersion","reconcileDialect","tursoDialect","isLocked","test","createStore","openWithDialect"],"mappings":"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"}
@@ -367,6 +367,9 @@ var CAPABILITIES = new Set([
367
367
  'phrases',
368
368
  'vectors'
369
369
  ]);
370
+ // close() reclaims once the cache file outgrows its compact size by this factor: the disk
371
+ // overhead a user should accept (PLAN 3.52), in postgres autovacuum scale-factor shape.
372
+ var BLOAT_FACTOR = 1.5;
370
373
  function createStore(db, conn) {
371
374
  return {
372
375
  name: 'turso',
@@ -555,21 +558,120 @@ function createStore(db, conn) {
555
558
  },
556
559
  close: function close() {
557
560
  return _async_to_generator(function() {
561
+ var reclaimPath, _ref, path, base, size, _tmp, err;
558
562
  return _ts_generator(this, function(_state) {
559
563
  switch(_state.label){
560
564
  case 0:
565
+ // Checkpoint before measuring: the last reconcile's pages sit in the WAL, not the file yet.
561
566
  return [
562
567
  4,
563
568
  (0, _connectionts.checkpointWal)(db)
564
569
  ];
565
570
  case 1:
566
571
  _state.sent();
572
+ reclaimPath = null;
573
+ _state.label = 2;
574
+ case 2:
575
+ _state.trys.push([
576
+ 2,
577
+ 12,
578
+ ,
579
+ 13
580
+ ]);
581
+ return [
582
+ 4,
583
+ (0, _connectionts.cacheFilePath)(conn)
584
+ ];
585
+ case 3:
586
+ path = _state.sent();
587
+ return [
588
+ 4,
589
+ (0, _sharedts.getMeta)(conn, 'compact_size')
590
+ ];
591
+ case 4:
592
+ base = Number.apply(void 0, [
593
+ (_ref = _state.sent()) !== null && _ref !== void 0 ? _ref : '0'
594
+ ]);
595
+ if (!(path === null)) return [
596
+ 3,
597
+ 5
598
+ ];
599
+ _tmp = null;
600
+ return [
601
+ 3,
602
+ 7
603
+ ];
604
+ case 5:
605
+ return [
606
+ 4,
607
+ (0, _connectionts.fileSize)(path)
608
+ ];
609
+ case 6:
610
+ _tmp = _state.sent();
611
+ _state.label = 7;
612
+ case 7:
613
+ size = _tmp;
614
+ if (!(size !== null)) return [
615
+ 3,
616
+ 11
617
+ ];
618
+ if (!(base === 0)) return [
619
+ 3,
620
+ 10
621
+ ];
622
+ return [
623
+ 4,
624
+ (0, _sharedts.setMeta)(conn, 'compact_size', String(size))
625
+ ];
626
+ case 8:
627
+ _state.sent();
628
+ // The meta write landed past the checkpoint; leave no WAL for the next opener.
629
+ return [
630
+ 4,
631
+ (0, _connectionts.checkpointWal)(db)
632
+ ];
633
+ case 9:
634
+ _state.sent();
635
+ return [
636
+ 3,
637
+ 11
638
+ ];
639
+ case 10:
640
+ if (size > BLOAT_FACTOR * base) {
641
+ reclaimPath = path;
642
+ }
643
+ _state.label = 11;
644
+ case 11:
645
+ return [
646
+ 3,
647
+ 13
648
+ ];
649
+ case 12:
650
+ err = _state.sent();
651
+ console.error("sense: turso cache bloat check failed, the cache will keep its space until one succeeds: ".concat(err.message));
652
+ return [
653
+ 3,
654
+ 13
655
+ ];
656
+ case 13:
567
657
  return [
568
658
  4,
569
659
  db.close()
570
660
  ];
571
- case 2:
661
+ case 14:
662
+ _state.sent();
663
+ if (!(reclaimPath !== null)) return [
664
+ 3,
665
+ 16
666
+ ];
667
+ return [
668
+ 4,
669
+ (0, _connectionts.reclaimSpace)(reclaimPath)
670
+ ];
671
+ case 15:
572
672
  _state.sent();
673
+ _state.label = 16;
674
+ case 16:
573
675
  return [
574
676
  2
575
677
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/store.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\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 { checkpointWal } from './connection.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// No 'snippets': fts_highlight returns the whole column, not a bounded window, so hits use\n// the caller's JS excerpt.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors']);\n\n// Shares one Connection instance (conn) with the builder's own reconcile call so transaction depth\n// (see transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: Database, conn: Connection): Store {\n return {\n name: 'turso',\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: (terms, opts) => queryLexical(conn, terms, opts),\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the\n // interface's per-call storeDims -- see vectors.ts's padded() for why a shorter vector is still correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(conn, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n async engineStatus() {\n // Read back rather than recomputed: this is what open() actually set (3x the largest\n // recorded reconcile, floored at 30s, capped at 10min). Turso's PRAGMA busy_timeout names its column \"busy_timeout\" (spike-verified), not \"timeout\" like real SQLite.\n const row = (await (await db.prepare('PRAGMA busy_timeout')).get()) as { busy_timeout: number };\n return { busy_timeout: `${row.busy_timeout}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)` };\n },\n raw: {\n async prepare(sql: string) {\n const stmt = await db.prepare(sql);\n stmt.safeIntegers(true); // int64 past 2^53 arrives as BigInt instead of losing precision\n return {\n columns: () => stmt.columns(),\n // sense sql streams through the client's own async generator.\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...params);\n },\n };\n },\n },\n async close() {\n await checkpointWal(db);\n await db.close();\n },\n };\n}\n"],"names":["CAPABILITIES","createStore","Set","db","conn","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","withTransaction","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","STORE_DIMS","candidates","qv","_storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","raw","stmt","safeIntegers","iterate","params","close","checkpointWal"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/store.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\nimport { STORE_DIMS } from '../../embed/types.ts';\nimport { getColumns, getMeta, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { cacheFilePath, checkpointWal, fileSize, reclaimSpace } from './connection.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\n// No 'snippets': fts_highlight returns the whole column, not a bounded window, so hits use\n// the caller's JS excerpt.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['lexical', 'phrases', 'vectors']);\n\n// close() reclaims once the cache file outgrows its compact size by this factor: the disk\n// overhead a user should accept (PLAN 3.52), in postgres autovacuum scale-factor shape.\nconst BLOAT_FACTOR = 1.5;\n\n// Shares one Connection instance (conn) with the builder's own reconcile call so transaction depth\n// (see transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: Database, conn: Connection): Store {\n return {\n name: 'turso',\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: (terms, opts) => queryLexical(conn, terms, opts),\n },\n // The column's fixed DDL width (STORE_DIMS) is what every scan binds against, not the\n // interface's per-call storeDims -- see vectors.ts's padded() for why a shorter vector is still correct against a wider column.\n vectors: {\n pending: () => pendingRows(conn),\n writeVectors: (rows) => writeVectorBatch(conn, STORE_DIMS, rows),\n candidates: (qv, _storeDims, fetch, allowed) => scanCandidates(conn, qv, STORE_DIMS, fetch, allowed),\n similar: (path, opts) => scanSimilar(conn, STORE_DIMS, path, opts),\n hasVector: (path) => hasVectorRow(conn, path),\n },\n async engineStatus() {\n // Read back rather than recomputed: this is what open() actually set (3x the largest\n // recorded reconcile, floored at 30s, capped at 10min). Turso's PRAGMA busy_timeout names its column \"busy_timeout\" (spike-verified), not \"timeout\" like real SQLite.\n const row = (await (await db.prepare('PRAGMA busy_timeout')).get()) as { busy_timeout: number };\n return { busy_timeout: `${row.busy_timeout}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)` };\n },\n raw: {\n async prepare(sql: string) {\n const stmt = await db.prepare(sql);\n stmt.safeIntegers(true); // int64 past 2^53 arrives as BigInt instead of losing precision\n return {\n columns: () => stmt.columns(),\n // sense sql streams through the client's own async generator.\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...params);\n },\n };\n },\n },\n async close() {\n // Checkpoint before measuring: the last reconcile's pages sit in the WAL, not the file yet.\n await checkpointWal(db);\n let reclaimPath: string | null = null;\n try {\n const path = await cacheFilePath(conn);\n const base = Number((await getMeta(conn, 'compact_size')) ?? '0');\n const size = path === null ? null : await fileSize(path);\n if (size !== null) {\n // No baseline yet (cold build or an upgraded cache): record it, never reclaim here, so a\n // cold build pays no VACUUM (PLAN 3.49 Track A) and an upgraded cache heals on a crossing.\n if (base === 0) {\n await setMeta(conn, 'compact_size', String(size));\n // The meta write landed past the checkpoint; leave no WAL for the next opener.\n await checkpointWal(db);\n } else if (size > BLOAT_FACTOR * base) {\n reclaimPath = path;\n }\n }\n } catch (err) {\n console.error(`sense: turso cache bloat check failed, the cache will keep its space until one succeeds: ${(err as Error).message}`);\n }\n await db.close();\n // After this store's connection is closed, never before: turso hands the file to one\n // connection at a time, and reclaimSpace opens its own to carry the `vacuum` flag (PLAN 3.54).\n if (reclaimPath !== null) await reclaimSpace(reclaimPath);\n },\n };\n}\n"],"names":["CAPABILITIES","createStore","Set","BLOAT_FACTOR","db","conn","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","withTransaction","docs","columns","getColumns","fieldStats","scopeWhere","lexical","query","terms","opts","queryLexical","vectors","pending","pendingRows","writeVectors","rows","writeVectorBatch","STORE_DIMS","candidates","qv","_storeDims","fetch","allowed","scanCandidates","similar","path","scanSimilar","hasVector","hasVectorRow","engineStatus","row","get","busy_timeout","raw","stmt","safeIntegers","iterate","params","close","reclaimPath","base","size","err","checkpointWal","cacheFilePath","getMeta","Number","fileSize","setMeta","String","console","error","message","reclaimSpace"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function rewriteInsert(sql: string, rowCount: number): {
2
+ sql: string;
3
+ width: number;
4
+ } | null;
@@ -0,0 +1,21 @@
1
+ // Rewrites a single-row parameterized INSERT into one multi-row statement. Engine-neutral string
2
+ // work, shared by duckdb (batch.ts's rewriteBatch) and turso (turso/connection.ts's runBatch).
3
+ const INSERT_RE = /^(INSERT\s+(?:OR\s+\w+\s+)?INTO\s+\S+\s*\([^)]*\)\s*VALUES\s*)\(([^()]*)\)(.*)$/is;
4
+ function placeholderCount(tuple) {
5
+ var _tuple_match;
6
+ return ((_tuple_match = tuple.match(/\?/g)) !== null && _tuple_match !== void 0 ? _tuple_match : []).length;
7
+ }
8
+ export function rewriteInsert(sql, rowCount) {
9
+ const m = INSERT_RE.exec(sql.trim());
10
+ if (!m) return null;
11
+ const [, head, tuple, tail] = m;
12
+ const width = placeholderCount(tuple);
13
+ if (width === 0) return null;
14
+ const rows = Array.from({
15
+ length: rowCount
16
+ }, ()=>`(${tuple})`).join(', ');
17
+ return {
18
+ sql: `${head}${rows}${tail}`,
19
+ width
20
+ };
21
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/batch.ts"],"sourcesContent":["// Rewrites a single-row parameterized INSERT into one multi-row statement. Engine-neutral string\n// work, shared by duckdb (batch.ts's rewriteBatch) and turso (turso/connection.ts's runBatch).\n\nconst INSERT_RE = /^(INSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+\\S+\\s*\\([^)]*\\)\\s*VALUES\\s*)\\(([^()]*)\\)(.*)$/is;\n\nfunction placeholderCount(tuple: string): number {\n return (tuple.match(/\\?/g) ?? []).length;\n}\n\nexport function rewriteInsert(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = INSERT_RE.exec(sql.trim());\n if (!m) return null;\n const [, head, tuple, tail] = m;\n const width = placeholderCount(tuple);\n if (width === 0) return null;\n const rows = Array.from({ length: rowCount }, () => `(${tuple})`).join(', ');\n return { sql: `${head}${rows}${tail}`, width };\n}\n"],"names":["INSERT_RE","placeholderCount","tuple","match","length","rewriteInsert","sql","rowCount","m","exec","trim","head","tail","width","rows","Array","from","join"],"mappings":"AAAA,iGAAiG;AACjG,+FAA+F;AAE/F,MAAMA,YAAY;AAElB,SAASC,iBAAiBC,KAAa;QAC7BA;IAAR,OAAO,EAACA,eAAAA,MAAMC,KAAK,CAAC,oBAAZD,0BAAAA,eAAsB,EAAE,EAAEE,MAAM;AAC1C;AAEA,OAAO,SAASC,cAAcC,GAAW,EAAEC,QAAgB;IACzD,MAAMC,IAAIR,UAAUS,IAAI,CAACH,IAAII,IAAI;IACjC,IAAI,CAACF,GAAG,OAAO;IACf,MAAM,GAAGG,MAAMT,OAAOU,KAAK,GAAGJ;IAC9B,MAAMK,QAAQZ,iBAAiBC;IAC/B,IAAIW,UAAU,GAAG,OAAO;IACxB,MAAMC,OAAOC,MAAMC,IAAI,CAAC;QAAEZ,QAAQG;IAAS,GAAG,IAAM,CAAC,CAAC,EAAEL,MAAM,CAAC,CAAC,EAAEe,IAAI,CAAC;IACvE,OAAO;QAAEX,KAAK,GAAGK,OAAOG,OAAOF,MAAM;QAAEC;IAAM;AAC/C"}
@@ -1,7 +1,3 @@
1
- export declare function rewriteInsert(sql: string, rowCount: number): {
2
- sql: string;
3
- width: number;
4
- } | null;
5
1
  export declare function rewriteUpdate(sql: string, rowCount: number): {
6
2
  sql: string;
7
3
  width: number;
@@ -1,12 +1,9 @@
1
1
  // Rewrites the store's single-row parameterized INSERT/UPDATE/DELETE into one multi-row statement --
2
2
  // DuckDB's columnar engine pays for row-at-a-time writes, not just the round trip (PRINCIPLES: documented-means-tested).
3
- const INSERT_RE = /^(INSERT\s+(?:OR\s+\w+\s+)?INTO\s+\S+\s*\([^)]*\)\s*VALUES\s*)\(([^()]*)\)(.*)$/is;
3
+ // rewriteInsert itself lives in the shared ../batch.ts: turso folds INSERTs the same way (3.36 B).
4
+ import { rewriteInsert } from '../batch.js';
4
5
  const UPDATE_RE = /^UPDATE\s+(\S+)\s+SET\s+(.+?)\s+WHERE\s+(.+)$/is;
5
6
  const DELETE_RE = /^DELETE\s+FROM\s+(\S+)\s+WHERE\s+(.+)$/is;
6
- function placeholderCount(tuple) {
7
- var _tuple_match;
8
- return ((_tuple_match = tuple.match(/\?/g)) !== null && _tuple_match !== void 0 ? _tuple_match : []).length;
9
- }
10
7
  // "a = ?, b = ?" or "a = ? AND b = ?" -> ["a", "b"]. A clause that doesn't fit yields a shorter
11
8
  // list, which rewriteUpdate/rewriteDelete reject as a width mismatch.
12
9
  function equalityColumns(clause, sep) {
@@ -27,20 +24,6 @@ function tuples(rowCount, width) {
27
24
  length: rowCount
28
25
  }, ()=>row).join(', ');
29
26
  }
30
- export function rewriteInsert(sql, rowCount) {
31
- const m = INSERT_RE.exec(sql.trim());
32
- if (!m) return null;
33
- const [, head, tuple, tail] = m;
34
- const width = placeholderCount(tuple);
35
- if (width === 0) return null;
36
- const rows = Array.from({
37
- length: rowCount
38
- }, ()=>`(${tuple})`).join(', ');
39
- return {
40
- sql: `${head}${rows}${tail}`,
41
- width
42
- };
43
- }
44
27
  export function rewriteUpdate(sql, rowCount) {
45
28
  const m = UPDATE_RE.exec(sql.trim());
46
29
  if (!m) return null;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/batch.ts"],"sourcesContent":["// Rewrites the store's single-row parameterized INSERT/UPDATE/DELETE into one multi-row statement --\n// DuckDB's columnar engine pays for row-at-a-time writes, not just the round trip (PRINCIPLES: documented-means-tested).\n\nconst INSERT_RE = /^(INSERT\\s+(?:OR\\s+\\w+\\s+)?INTO\\s+\\S+\\s*\\([^)]*\\)\\s*VALUES\\s*)\\(([^()]*)\\)(.*)$/is;\nconst UPDATE_RE = /^UPDATE\\s+(\\S+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+)$/is;\nconst DELETE_RE = /^DELETE\\s+FROM\\s+(\\S+)\\s+WHERE\\s+(.+)$/is;\n\nfunction placeholderCount(tuple: string): number {\n return (tuple.match(/\\?/g) ?? []).length;\n}\n\n// \"a = ?, b = ?\" or \"a = ? AND b = ?\" -> [\"a\", \"b\"]. A clause that doesn't fit yields a shorter\n// list, which rewriteUpdate/rewriteDelete reject as a width mismatch.\nfunction equalityColumns(clause: string, sep: RegExp): string[] | null {\n const parts = clause.split(sep).map((p) => p.trim());\n const cols: string[] = [];\n for (const part of parts) {\n const m = /^(.+?)=\\s*\\?$/.exec(part);\n if (!m) return null;\n cols.push(m[1].trim());\n }\n return cols;\n}\n\nfunction tuples(rowCount: number, width: number): string {\n const row = `(${Array.from({ length: width }, () => '?').join(', ')})`;\n return Array.from({ length: rowCount }, () => row).join(', ');\n}\n\nexport function rewriteInsert(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = INSERT_RE.exec(sql.trim());\n if (!m) return null;\n const [, head, tuple, tail] = m;\n const width = placeholderCount(tuple);\n if (width === 0) return null;\n const rows = Array.from({ length: rowCount }, () => `(${tuple})`).join(', ');\n return { sql: `${head}${rows}${tail}`, width };\n}\n\nexport function rewriteUpdate(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = UPDATE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, setClause, whereClause] = m;\n const setCols = equalityColumns(setClause, /,/);\n const whereCols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!setCols || !whereCols) return null;\n const dataCols = [...setCols, ...whereCols];\n const setAssign = setCols.map((c) => `${c} = data.${c}`).join(', ');\n const joinCond = whereCols.map((c) => `${table}.${c} = data.${c}`).join(' AND ');\n return { sql: `UPDATE ${table} SET ${setAssign} FROM (VALUES ${tuples(rowCount, dataCols.length)}) AS data(${dataCols.join(', ')}) WHERE ${joinCond}`, width: dataCols.length };\n}\n\nexport function rewriteDelete(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = DELETE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, whereClause] = m;\n const cols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!cols) return null;\n if (cols.length === 1) {\n return { sql: `DELETE FROM ${table} WHERE ${cols[0]} IN (${Array.from({ length: rowCount }, () => '?').join(', ')})`, width: 1 };\n }\n return { sql: `DELETE FROM ${table} WHERE (${cols.join(', ')}) IN (VALUES ${tuples(rowCount, cols.length)})`, width: cols.length };\n}\n\n// Tries each shape in turn; null means the caller should fall back to a per-row loop.\nexport function rewriteBatch(sql: string, rowCount: number): { sql: string; width: number } | null {\n return rewriteInsert(sql, rowCount) ?? rewriteUpdate(sql, rowCount) ?? rewriteDelete(sql, rowCount);\n}\n"],"names":["INSERT_RE","UPDATE_RE","DELETE_RE","placeholderCount","tuple","match","length","equalityColumns","clause","sep","parts","split","map","p","trim","cols","part","m","exec","push","tuples","rowCount","width","row","Array","from","join","rewriteInsert","sql","head","tail","rows","rewriteUpdate","table","setClause","whereClause","setCols","whereCols","dataCols","setAssign","c","joinCond","rewriteDelete","rewriteBatch"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/duckdb/batch.ts"],"sourcesContent":["// Rewrites the store's single-row parameterized INSERT/UPDATE/DELETE into one multi-row statement --\n// DuckDB's columnar engine pays for row-at-a-time writes, not just the round trip (PRINCIPLES: documented-means-tested).\n// rewriteInsert itself lives in the shared ../batch.ts: turso folds INSERTs the same way (3.36 B).\n\nimport { rewriteInsert } from '../batch.ts';\n\nconst UPDATE_RE = /^UPDATE\\s+(\\S+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+)$/is;\nconst DELETE_RE = /^DELETE\\s+FROM\\s+(\\S+)\\s+WHERE\\s+(.+)$/is;\n\n// \"a = ?, b = ?\" or \"a = ? AND b = ?\" -> [\"a\", \"b\"]. A clause that doesn't fit yields a shorter\n// list, which rewriteUpdate/rewriteDelete reject as a width mismatch.\nfunction equalityColumns(clause: string, sep: RegExp): string[] | null {\n const parts = clause.split(sep).map((p) => p.trim());\n const cols: string[] = [];\n for (const part of parts) {\n const m = /^(.+?)=\\s*\\?$/.exec(part);\n if (!m) return null;\n cols.push(m[1].trim());\n }\n return cols;\n}\n\nfunction tuples(rowCount: number, width: number): string {\n const row = `(${Array.from({ length: width }, () => '?').join(', ')})`;\n return Array.from({ length: rowCount }, () => row).join(', ');\n}\n\nexport function rewriteUpdate(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = UPDATE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, setClause, whereClause] = m;\n const setCols = equalityColumns(setClause, /,/);\n const whereCols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!setCols || !whereCols) return null;\n const dataCols = [...setCols, ...whereCols];\n const setAssign = setCols.map((c) => `${c} = data.${c}`).join(', ');\n const joinCond = whereCols.map((c) => `${table}.${c} = data.${c}`).join(' AND ');\n return { sql: `UPDATE ${table} SET ${setAssign} FROM (VALUES ${tuples(rowCount, dataCols.length)}) AS data(${dataCols.join(', ')}) WHERE ${joinCond}`, width: dataCols.length };\n}\n\nexport function rewriteDelete(sql: string, rowCount: number): { sql: string; width: number } | null {\n const m = DELETE_RE.exec(sql.trim());\n if (!m) return null;\n const [, table, whereClause] = m;\n const cols = equalityColumns(whereClause, /\\bAND\\b/i);\n if (!cols) return null;\n if (cols.length === 1) {\n return { sql: `DELETE FROM ${table} WHERE ${cols[0]} IN (${Array.from({ length: rowCount }, () => '?').join(', ')})`, width: 1 };\n }\n return { sql: `DELETE FROM ${table} WHERE (${cols.join(', ')}) IN (VALUES ${tuples(rowCount, cols.length)})`, width: cols.length };\n}\n\n// Tries each shape in turn; null means the caller should fall back to a per-row loop.\nexport function rewriteBatch(sql: string, rowCount: number): { sql: string; width: number } | null {\n return rewriteInsert(sql, rowCount) ?? rewriteUpdate(sql, rowCount) ?? rewriteDelete(sql, rowCount);\n}\n"],"names":["rewriteInsert","UPDATE_RE","DELETE_RE","equalityColumns","clause","sep","parts","split","map","p","trim","cols","part","m","exec","push","tuples","rowCount","width","row","Array","from","length","join","rewriteUpdate","sql","table","setClause","whereClause","setCols","whereCols","dataCols","setAssign","c","joinCond","rewriteDelete","rewriteBatch"],"mappings":"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"}
@@ -1,4 +1,7 @@
1
1
  import type { Database } from '@tursodatabase/database';
2
2
  import type { Connection } from '../types.js';
3
3
  export declare function checkpointWal(db: Database): Promise<void>;
4
+ export declare function cacheFilePath(conn: Connection): Promise<string | null>;
5
+ export declare function fileSize(path: string): Promise<number | null>;
6
+ export declare function reclaimSpace(path: string): Promise<void>;
4
7
  export declare function createConnection(db: Database): Connection;
@@ -1,4 +1,8 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { rewriteInsert } from '../batch.js';
3
+ import { setMeta } from '../shared.js';
1
4
  import { BEGIN_WRITE, withTransaction } from '../transaction.js';
5
+ import { CONNECT_OPTS, tursoApi } from './native.js';
2
6
  // @tursodatabase/database/compat offers a synchronous escape hatch, but it measured only 4-12%
3
7
  // faster than this promise client, so this wraps the async client in the same Connection/Statement shape 1:1.
4
8
  let TursoStatementWrapper = class TursoStatementWrapper {
@@ -33,6 +37,45 @@ export async function checkpointWal(db) {
33
37
  console.error(`sense: turso WAL checkpoint failed, the -wal file will keep growing until one succeeds: ${err.message}`);
34
38
  }
35
39
  }
40
+ // The main cache file's path, or null when unresolvable. The store holds a Database and
41
+ // Connection, not a path (PLAN 3.52), so the path is read from the database itself.
42
+ export async function cacheFilePath(conn) {
43
+ var _rows_find;
44
+ const rows = await (await conn.prepare('PRAGMA database_list')).all();
45
+ return ((_rows_find = rows.find((r)=>r.name === 'main')) === null || _rows_find === void 0 ? void 0 : _rows_find.file) || null;
46
+ }
47
+ export async function fileSize(path) {
48
+ try {
49
+ return (await stat(path)).size;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ // Reclaims turso#8170's FTS space amplification (PLAN 3.41), on a throwaway connection after the
55
+ // store's own has closed: `vacuum` doubles incremental reconcile cost merely by being enabled (3.54).
56
+ export async function reclaimSpace(path) {
57
+ try {
58
+ const turso = await tursoApi();
59
+ const db = await turso.connect(path, {
60
+ ...CONNECT_OPTS,
61
+ experimental: [
62
+ ...CONNECT_OPTS.experimental,
63
+ 'vacuum'
64
+ ]
65
+ });
66
+ try {
67
+ await db.exec('VACUUM');
68
+ const size = await fileSize(path);
69
+ // Recorded here, not by the caller: this connection is the only one still open on the file.
70
+ if (size !== null) await setMeta(createConnection(db), 'compact_size', String(size));
71
+ await checkpointWal(db);
72
+ } finally{
73
+ await db.close();
74
+ }
75
+ } catch (err) {
76
+ console.error(`sense: turso VACUUM failed, the cache will keep the space until one succeeds: ${err.message}`);
77
+ }
78
+ }
36
79
  export function createConnection(db) {
37
80
  const conn = {
38
81
  async exec (sql) {
@@ -41,14 +84,26 @@ export function createConnection(db) {
41
84
  async prepare (sql) {
42
85
  return new TursoStatementWrapper(await db.prepare(sql));
43
86
  },
44
- // Prepares once and awaits run() per row: db.batch() re-prepares each statement, which cost as
45
- // much as preparing per row. A literal nested BEGIN hard-errors, so withTransaction's join-not-savepoint helper makes this safe inside reconcile's own transaction too.
87
+ // Folds a plain INSERT into one multi-row VALUES statement (shared rewriteInsert, ../batch.ts):
88
+ // no bind-variable ceiling to chunk against, measured empirically. UPDATE/DELETE keep the per-row loop.
46
89
  async runBatch (sql, paramRows) {
47
90
  if (paramRows.length === 0) return;
48
91
  await withTransaction(conn, async ()=>{
49
- const stmt = await db.prepare(sql);
92
+ const rewritten = rewriteInsert(sql, paramRows.length);
93
+ if (rewritten) {
94
+ const stmt = await db.prepare(rewritten.sql);
95
+ try {
96
+ // One flat array, not spread: spreading tens of thousands of args hits a JS call-stack
97
+ // limit well before turso enforces any variable-count ceiling (measured; see PLAN.md).
98
+ await stmt.run(paramRows.flat());
99
+ } finally{
100
+ await stmt.close();
101
+ }
102
+ return;
103
+ }
50
104
  // Finalized here because nothing else will: open() hands back a connection the caller
51
105
  // can hold across many batches, and the db.batch() this replaced finalized its own.
106
+ const stmt = await db.prepare(sql);
52
107
  try {
53
108
  for (const row of paramRows)await stmt.run(...row);
54
109
  } finally{
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/connection.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\nimport { BEGIN_WRITE, withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\n\n// The client's own Statement class isn't re-exported by name from '@tursodatabase/database',\n// so its type is derived structurally from Database.prepare()'s return type instead.\ntype TursoStatement = Awaited<ReturnType<Database['prepare']>>;\n\n// @tursodatabase/database/compat offers a synchronous escape hatch, but it measured only 4-12%\n// faster than this promise client, so this wraps the async client in the same Connection/Statement shape 1:1.\nclass TursoStatementWrapper implements Statement {\n private stmt: TursoStatement;\n\n constructor(stmt: TursoStatement) {\n this.stmt = stmt;\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n return this.stmt.run(...params);\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n return this.stmt.get(...params);\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n return this.stmt.all(...params);\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n yield* this.stmt.iterate(...params);\n }\n\n columns(): Array<{ name: string }> {\n return this.stmt.columns();\n }\n\n setReadBigInts(enabled: boolean): void {\n this.stmt.safeIntegers(enabled);\n }\n}\n\n// This client leaves the WAL behind for the next opener, where node:sqlite checkpoints on the last\n// close, so a tree reconciled over and over grows one without bound. Best-effort: close must not throw.\nexport async function checkpointWal(db: Database): Promise<void> {\n try {\n await db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n } catch (err) {\n console.error(`sense: turso WAL checkpoint failed, the -wal file will keep growing until one succeeds: ${(err as Error).message}`);\n }\n}\n\nexport function createConnection(db: Database): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n await db.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new TursoStatementWrapper(await db.prepare(sql));\n },\n // Prepares once and awaits run() per row: db.batch() re-prepares each statement, which cost as\n // much as preparing per row. A literal nested BEGIN hard-errors, so withTransaction's join-not-savepoint helper makes this safe inside reconcile's own transaction too.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(\n conn,\n async () => {\n const stmt = await db.prepare(sql);\n // Finalized here because nothing else will: open() hands back a connection the caller\n // can hold across many batches, and the db.batch() this replaced finalized its own.\n try {\n for (const row of paramRows) await stmt.run(...row);\n } finally {\n await stmt.close();\n }\n },\n BEGIN_WRITE\n );\n },\n };\n return conn;\n}\n"],"names":["BEGIN_WRITE","withTransaction","TursoStatementWrapper","run","params","stmt","get","all","iterate","columns","setReadBigInts","enabled","safeIntegers","checkpointWal","db","exec","err","console","error","message","createConnection","conn","sql","prepare","runBatch","paramRows","length","row","close"],"mappings":"AACA,SAASA,WAAW,EAAEC,eAAe,QAAQ,oBAAoB;AAOjE,+FAA+F;AAC/F,8GAA8G;AAC9G,IAAA,AAAMC,wBAAN,MAAMA;IAOJ,MAAMC,IAAI,GAAGC,MAAiB,EAAsB;QAClD,OAAO,IAAI,CAACC,IAAI,CAACF,GAAG,IAAIC;IAC1B;IAEA,MAAME,IAAI,GAAGF,MAAiB,EAAoB;QAChD,OAAO,IAAI,CAACC,IAAI,CAACC,GAAG,IAAIF;IAC1B;IAEA,MAAMG,IAAI,GAAGH,MAAiB,EAAsB;QAClD,OAAO,IAAI,CAACC,IAAI,CAACE,GAAG,IAAIH;IAC1B;IAEA,OAAOI,QAAQ,GAAGJ,MAAiB,EAA0B;QAC3D,OAAO,IAAI,CAACC,IAAI,CAACG,OAAO,IAAIJ;IAC9B;IAEAK,UAAmC;QACjC,OAAO,IAAI,CAACJ,IAAI,CAACI,OAAO;IAC1B;IAEAC,eAAeC,OAAgB,EAAQ;QACrC,IAAI,CAACN,IAAI,CAACO,YAAY,CAACD;IACzB;IA1BA,YAAYN,IAAoB,CAAE;QAChC,IAAI,CAACA,IAAI,GAAGA;IACd;AAyBF;AAEA,mGAAmG;AACnG,wGAAwG;AACxG,OAAO,eAAeQ,cAAcC,EAAY;IAC9C,IAAI;QACF,MAAMA,GAAGC,IAAI,CAAC;IAChB,EAAE,OAAOC,KAAK;QACZC,QAAQC,KAAK,CAAC,CAAC,wFAAwF,EAAE,AAACF,IAAcG,OAAO,EAAE;IACnI;AACF;AAEA,OAAO,SAASC,iBAAiBN,EAAY;IAC3C,MAAMO,OAAmB;QACvB,MAAMN,MAAKO,GAAW;YACpB,MAAMR,GAAGC,IAAI,CAACO;QAChB;QACA,MAAMC,SAAQD,GAAW;YACvB,OAAO,IAAIpB,sBAAsB,MAAMY,GAAGS,OAAO,CAACD;QACpD;QACA,+FAA+F;QAC/F,wKAAwK;QACxK,MAAME,UAASF,GAAW,EAAEG,SAAsB;YAChD,IAAIA,UAAUC,MAAM,KAAK,GAAG;YAC5B,MAAMzB,gBACJoB,MACA;gBACE,MAAMhB,OAAO,MAAMS,GAAGS,OAAO,CAACD;gBAC9B,sFAAsF;gBACtF,oFAAoF;gBACpF,IAAI;oBACF,KAAK,MAAMK,OAAOF,UAAW,MAAMpB,KAAKF,GAAG,IAAIwB;gBACjD,SAAU;oBACR,MAAMtB,KAAKuB,KAAK;gBAClB;YACF,GACA5B;QAEJ;IACF;IACA,OAAOqB;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/connection.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport type { Database } from '@tursodatabase/database';\nimport { rewriteInsert } from '../batch.ts';\nimport { setMeta } from '../shared.ts';\nimport { BEGIN_WRITE, withTransaction } from '../transaction.ts';\nimport type { Connection, RunResult, Statement } from '../types.ts';\nimport { CONNECT_OPTS, tursoApi } from './native.ts';\n\n// The client's own Statement class isn't re-exported by name from '@tursodatabase/database',\n// so its type is derived structurally from Database.prepare()'s return type instead.\ntype TursoStatement = Awaited<ReturnType<Database['prepare']>>;\n\n// @tursodatabase/database/compat offers a synchronous escape hatch, but it measured only 4-12%\n// faster than this promise client, so this wraps the async client in the same Connection/Statement shape 1:1.\nclass TursoStatementWrapper implements Statement {\n private stmt: TursoStatement;\n\n constructor(stmt: TursoStatement) {\n this.stmt = stmt;\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n return this.stmt.run(...params);\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n return this.stmt.get(...params);\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n return this.stmt.all(...params);\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n yield* this.stmt.iterate(...params);\n }\n\n columns(): Array<{ name: string }> {\n return this.stmt.columns();\n }\n\n setReadBigInts(enabled: boolean): void {\n this.stmt.safeIntegers(enabled);\n }\n}\n\n// This client leaves the WAL behind for the next opener, where node:sqlite checkpoints on the last\n// close, so a tree reconciled over and over grows one without bound. Best-effort: close must not throw.\nexport async function checkpointWal(db: Database): Promise<void> {\n try {\n await db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n } catch (err) {\n console.error(`sense: turso WAL checkpoint failed, the -wal file will keep growing until one succeeds: ${(err as Error).message}`);\n }\n}\n\n// The main cache file's path, or null when unresolvable. The store holds a Database and\n// Connection, not a path (PLAN 3.52), so the path is read from the database itself.\nexport async function cacheFilePath(conn: Connection): Promise<string | null> {\n const rows = (await (await conn.prepare('PRAGMA database_list')).all()) as Array<{ name: string; file: string }>;\n return rows.find((r) => r.name === 'main')?.file || null;\n}\n\nexport async function fileSize(path: string): Promise<number | null> {\n try {\n return (await stat(path)).size;\n } catch {\n return null;\n }\n}\n\n// Reclaims turso#8170's FTS space amplification (PLAN 3.41), on a throwaway connection after the\n// store's own has closed: `vacuum` doubles incremental reconcile cost merely by being enabled (3.54).\nexport async function reclaimSpace(path: string): Promise<void> {\n try {\n const turso = await tursoApi();\n const db = await turso.connect(path, { ...CONNECT_OPTS, experimental: [...CONNECT_OPTS.experimental, 'vacuum'] });\n try {\n await db.exec('VACUUM');\n const size = await fileSize(path);\n // Recorded here, not by the caller: this connection is the only one still open on the file.\n if (size !== null) await setMeta(createConnection(db), 'compact_size', String(size));\n await checkpointWal(db);\n } finally {\n await db.close();\n }\n } catch (err) {\n console.error(`sense: turso VACUUM failed, the cache will keep the space until one succeeds: ${(err as Error).message}`);\n }\n}\n\nexport function createConnection(db: Database): Connection {\n const conn: Connection = {\n async exec(sql: string): Promise<void> {\n await db.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return new TursoStatementWrapper(await db.prepare(sql));\n },\n // Folds a plain INSERT into one multi-row VALUES statement (shared rewriteInsert, ../batch.ts):\n // no bind-variable ceiling to chunk against, measured empirically. UPDATE/DELETE keep the per-row loop.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(\n conn,\n async () => {\n const rewritten = rewriteInsert(sql, paramRows.length);\n if (rewritten) {\n const stmt = await db.prepare(rewritten.sql);\n try {\n // One flat array, not spread: spreading tens of thousands of args hits a JS call-stack\n // limit well before turso enforces any variable-count ceiling (measured; see PLAN.md).\n await stmt.run(paramRows.flat());\n } finally {\n await stmt.close();\n }\n return;\n }\n // Finalized here because nothing else will: open() hands back a connection the caller\n // can hold across many batches, and the db.batch() this replaced finalized its own.\n const stmt = await db.prepare(sql);\n try {\n for (const row of paramRows) await stmt.run(...row);\n } finally {\n await stmt.close();\n }\n },\n BEGIN_WRITE\n );\n },\n };\n return conn;\n}\n"],"names":["stat","rewriteInsert","setMeta","BEGIN_WRITE","withTransaction","CONNECT_OPTS","tursoApi","TursoStatementWrapper","run","params","stmt","get","all","iterate","columns","setReadBigInts","enabled","safeIntegers","checkpointWal","db","exec","err","console","error","message","cacheFilePath","conn","rows","prepare","find","r","name","file","fileSize","path","size","reclaimSpace","turso","connect","experimental","createConnection","String","close","sql","runBatch","paramRows","length","rewritten","flat","row"],"mappings":"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"}
@@ -1,2 +1,6 @@
1
1
  export declare const TURSO_PACKAGE = "@tursodatabase/database";
2
+ export declare const CONNECT_OPTS: {
3
+ readonly timeout: 30000;
4
+ readonly experimental: readonly ["index_method"];
5
+ };
2
6
  export declare function tursoApi(): Promise<typeof import('@tursodatabase/database')>;
@@ -5,6 +5,14 @@ const TURSO = {
5
5
  pkg: TURSO_PACKAGE,
6
6
  sizeHint: '~16MB'
7
7
  };
8
+ // Every connect() in this store goes through these: the same 30s floor sqlite opens with, and the
9
+ // FTS index method ensureSchema() needs. reclaimSpace adds 'vacuum'; nothing else may (PLAN 3.54).
10
+ export const CONNECT_OPTS = {
11
+ timeout: 30000,
12
+ experimental: [
13
+ 'index_method'
14
+ ]
15
+ };
8
16
  let tursoApiPromise;
9
17
  // One accessor every consumer must go through, not each its own bare-specifier import: Node
10
18
  // keeps returning a pre-install "not found" miss to any new lookup of the same path afterward.
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/native.ts"],"sourcesContent":["import { loadOrInstall, type NativeDescriptor, packageNodeModules } from '../native.ts';\n\nexport const TURSO_PACKAGE = '@tursodatabase/database';\n\nconst TURSO: NativeDescriptor = { store: 'turso', pkg: TURSO_PACKAGE, sizeHint: '~16MB' };\n\nlet tursoApiPromise: Promise<typeof import('@tursodatabase/database')> | undefined;\n\n// One accessor every consumer must go through, not each its own bare-specifier import: Node\n// keeps returning a pre-install \"not found\" miss to any new lookup of the same path afterward.\nexport function tursoApi(): Promise<typeof import('@tursodatabase/database')> {\n if (!tursoApiPromise) tursoApiPromise = loadOrInstall<typeof import('@tursodatabase/database')>(TURSO, packageNodeModules());\n return tursoApiPromise;\n}\n"],"names":["loadOrInstall","packageNodeModules","TURSO_PACKAGE","TURSO","store","pkg","sizeHint","tursoApiPromise","tursoApi"],"mappings":"AAAA,SAASA,aAAa,EAAyBC,kBAAkB,QAAQ,eAAe;AAExF,OAAO,MAAMC,gBAAgB,0BAA0B;AAEvD,MAAMC,QAA0B;IAAEC,OAAO;IAASC,KAAKH;IAAeI,UAAU;AAAQ;AAExF,IAAIC;AAEJ,4FAA4F;AAC5F,+FAA+F;AAC/F,OAAO,SAASC;IACd,IAAI,CAACD,iBAAiBA,kBAAkBP,cAAwDG,OAAOF;IACvG,OAAOM;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/native.ts"],"sourcesContent":["import { loadOrInstall, type NativeDescriptor, packageNodeModules } from '../native.ts';\n\nexport const TURSO_PACKAGE = '@tursodatabase/database';\n\nconst TURSO: NativeDescriptor = { store: 'turso', pkg: TURSO_PACKAGE, sizeHint: '~16MB' };\n\n// Every connect() in this store goes through these: the same 30s floor sqlite opens with, and the\n// FTS index method ensureSchema() needs. reclaimSpace adds 'vacuum'; nothing else may (PLAN 3.54).\nexport const CONNECT_OPTS = { timeout: 30_000, experimental: ['index_method'] } as const;\n\nlet tursoApiPromise: Promise<typeof import('@tursodatabase/database')> | undefined;\n\n// One accessor every consumer must go through, not each its own bare-specifier import: Node\n// keeps returning a pre-install \"not found\" miss to any new lookup of the same path afterward.\nexport function tursoApi(): Promise<typeof import('@tursodatabase/database')> {\n if (!tursoApiPromise) tursoApiPromise = loadOrInstall<typeof import('@tursodatabase/database')>(TURSO, packageNodeModules());\n return tursoApiPromise;\n}\n"],"names":["loadOrInstall","packageNodeModules","TURSO_PACKAGE","TURSO","store","pkg","sizeHint","CONNECT_OPTS","timeout","experimental","tursoApiPromise","tursoApi"],"mappings":"AAAA,SAASA,aAAa,EAAyBC,kBAAkB,QAAQ,eAAe;AAExF,OAAO,MAAMC,gBAAgB,0BAA0B;AAEvD,MAAMC,QAA0B;IAAEC,OAAO;IAASC,KAAKH;IAAeI,UAAU;AAAQ;AAExF,kGAAkG;AAClG,mGAAmG;AACnG,OAAO,MAAMC,eAAe;IAAEC,SAAS;IAAQC,cAAc;QAAC;KAAe;AAAC,EAAW;AAEzF,IAAIC;AAEJ,4FAA4F;AAC5F,+FAA+F;AAC/F,OAAO,SAASC;IACd,IAAI,CAACD,iBAAiBA,kBAAkBV,cAAwDG,OAAOF;IACvG,OAAOS;AACT"}
@@ -5,7 +5,7 @@ import { activeFeatures, FEATURES } from '../../features/index.js';
5
5
  import { openWithDialect } from '../open.js';
6
6
  import { getMeta, setMeta } from '../shared.js';
7
7
  import { checkpointWal, createConnection } from './connection.js';
8
- import { TURSO_PACKAGE, tursoApi } from './native.js';
8
+ import { CONNECT_OPTS, TURSO_PACKAGE, tursoApi } from './native.js';
9
9
  import { CONTENT_FTS_DDL, tursoDialect } from './reconcile.js';
10
10
  import { createStore } from './store.js';
11
11
  export const DB_FILENAME = 'cache.turso.db';
@@ -51,12 +51,12 @@ async function connect(dbPath, _cfg) {
51
51
  }
52
52
  let db;
53
53
  try {
54
- // Floored at the same 30s sqlite opens with. `timeout` is connect-time only in this client;
55
- // the derived value is set via runtime PRAGMA below. `index_method` is required for ensureSchema()'s FTS indexes (T1).
54
+ // `timeout` is connect-time only in this client; the derived value is set via runtime PRAGMA
55
+ // below. Options in native.ts, shared with reclaimSpace's own connection.
56
56
  db = await turso.connect(dbPath, {
57
- timeout: 30000,
57
+ ...CONNECT_OPTS,
58
58
  experimental: [
59
- 'index_method'
59
+ ...CONNECT_OPTS.experimental
60
60
  ]
61
61
  });
62
62
  } catch (err) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/open.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\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 { checkpointWal, createConnection } from './connection.ts';\nimport { TURSO_PACKAGE, tursoApi } from './native.ts';\nimport { CONTENT_FTS_DDL, tursoDialect } from './reconcile.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.turso.db';\n// Independent of sqlite's and duckdb's SCHEMA_VERSION: each store's cache shape evolves\n// separately. Covers the FTS indexes, the \"_ngram\" sidecar columns, and embeddings.vector's width.\nexport const SCHEMA_VERSION = '5';\n\nexport type { OpenResult };\n\n// This store's Handle (types.ts's OpenDialect<Handle>) is the connected Database itself: no\n// extra native state to thread, unlike duckdb's separate instance/connection pair.\nasync function ensureSchema(_handle: Database, conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);\n for (const ddl of CONTENT_FTS_DDL) await conn.exec(ddl);\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 F32_BLOB(STORE_DIMS) instead of the embed feature's engine-neutral BLOB DDL. `scale`\n // is kept unused, so the shared reconcile-time INSERT/DELETE names a column both stores have.\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 F32_BLOB(${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 close(handle: Database): Promise<void> {\n await checkpointWal(handle);\n await handle.close();\n}\n\nasync function setDerivedBusyTimeout(_handle: Database, conn: Connection, ms: number): Promise<void> {\n await conn.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: Database; conn: Connection }> {\n // Dynamic, not a top-level import: a sqlite or duckdb tree must never attempt to resolve this\n // optional dependency until a turso tree is actually opened. Installed on first use if missing.\n let turso: Awaited<ReturnType<typeof tursoApi>>;\n try {\n turso = await tursoApi();\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" needs the ${TURSO_PACKAGE} package (${(err as Error).message})`);\n }\n\n let db: Database;\n try {\n // Floored at the same 30s sqlite opens with. `timeout` is connect-time only in this client;\n // the derived value is set via runtime PRAGMA below. `index_method` is required for ensureSchema()'s FTS indexes (T1).\n db = await turso.connect(dbPath, { timeout: 30_000, experimental: ['index_method'] });\n } catch (err) {\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n return { handle: db, conn: createConnection(db) };\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const tursoOpenDialect: OpenDialect<Database> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: tursoDialect,\n connect,\n close,\n // \"Locking error: Failed locking file ...\", worded per platform: posix \"File is locked by another\n // process\", Windows \"another process has locked a portion of the file (os error 33)\". Distinct from\n // the write-time \"database is locked\" its connect-time `timeout` covers; that one never reaches here.\n isLocked: (err) => /File is locked by another process|locked a portion of the file/.test(err.message),\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn) => createStore(handle, conn),\n};\n\nexport async function openTurso(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, tursoOpenDialect);\n}\n"],"names":["featureSignature","STORE_DIMS","SenseError","activeFeatures","FEATURES","openWithDialect","getMeta","setMeta","checkpointWal","createConnection","TURSO_PACKAGE","tursoApi","CONTENT_FTS_DDL","tursoDialect","createStore","DB_FILENAME","SCHEMA_VERSION","ensureSchema","_handle","conn","cfg","exec","ddl","feature","name","schema","close","handle","setDerivedBusyTimeout","ms","connect","dbPath","_cfg","turso","err","message","db","timeout","experimental","tursoOpenDialect","filename","schemaVersion","reconcileDialect","isLocked","test","openTurso"],"mappings":"AAEA,SAASA,gBAAgB,QAAQ,wBAAwB;AACzD,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,UAAU,QAAQ,kBAAkB;AAC7C,SAASC,cAAc,EAAEC,QAAQ,QAAQ,0BAA0B;AAEnE,SAASC,eAAe,QAAQ,aAAa;AAC7C,SAASC,OAAO,EAAEC,OAAO,QAAQ,eAAe;AAEhD,SAASC,aAAa,EAAEC,gBAAgB,QAAQ,kBAAkB;AAClE,SAASC,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AACtD,SAASC,eAAe,EAAEC,YAAY,QAAQ,iBAAiB;AAC/D,SAASC,WAAW,QAAQ,aAAa;AAEzC,OAAO,MAAMC,cAAc,iBAAiB;AAC5C,wFAAwF;AACxF,mGAAmG;AACnG,OAAO,MAAMC,iBAAiB,IAAI;AAIlC,4FAA4F;AAC5F,mFAAmF;AACnF,eAAeC,aAAaC,OAAiB,EAAEC,IAAgB,EAAEC,GAAW;IAC1E,MAAMD,KAAKE,IAAI,CAAC,CAAC,oIAAoI,CAAC;IACtJ,MAAMF,KAAKE,IAAI,CAAC,CAAC,wJAAwJ,CAAC;IAC1K,KAAK,MAAMC,OAAOV,gBAAiB,MAAMO,KAAKE,IAAI,CAACC;IACnD,MAAMH,KAAKE,IAAI,CAAC,CAAC,gGAAgG,CAAC;IAClH,MAAMF,KAAKE,IAAI,CAAC;IAChB,KAAK,MAAME,WAAWpB,eAAeiB,KAAM;QACzC,8FAA8F;QAC9F,8FAA8F;QAC9F,IAAIG,QAAQC,IAAI,KAAK,SAAS;YAC5B,MAAML,KAAKE,IAAI,CAAC,CAAC,qIAAqI,EAAEpB,WAAW,+BAA+B,CAAC;YACnM;QACF;QACA,MAAMsB,QAAQE,MAAM,CAACN;IACvB;IACA,IAAI,AAAC,MAAMb,QAAQa,MAAM,sBAAuB,MAAM,MAAMZ,QAAQY,MAAM,kBAAkBH;IAC5F,IAAI,AAAC,MAAMV,QAAQa,MAAM,gBAAiB,MAAM,MAAMZ,QAAQY,MAAM,YAAYnB,iBAAiBoB,KAAKhB;AACxG;AAEA,eAAesB,MAAMC,MAAgB;IACnC,MAAMnB,cAAcmB;IACpB,MAAMA,OAAOD,KAAK;AACpB;AAEA,eAAeE,sBAAsBV,OAAiB,EAAEC,IAAgB,EAAEU,EAAU;IAClF,MAAMV,KAAKE,IAAI,CAAC,CAAC,sBAAsB,EAAEQ,IAAI;AAC/C;AAEA,eAAeC,QAAQC,MAAc,EAAEC,IAAoB;IACzD,8FAA8F;IAC9F,gGAAgG;IAChG,IAAIC;IACJ,IAAI;QACFA,QAAQ,MAAMtB;IAChB,EAAE,OAAOuB,KAAK;QACZ,IAAIA,eAAehC,YAAY,MAAMgC;QACrC,MAAM,IAAIhC,WAAW,4BAA4B,CAAC,wBAAwB,EAAEQ,cAAc,UAAU,EAAE,AAACwB,IAAcC,OAAO,CAAC,CAAC,CAAC;IACjI;IAEA,IAAIC;IACJ,IAAI;QACF,4FAA4F;QAC5F,uHAAuH;QACvHA,KAAK,MAAMH,MAAMH,OAAO,CAACC,QAAQ;YAAEM,SAAS;YAAQC,cAAc;gBAAC;aAAe;QAAC;IACrF,EAAE,OAAOJ,KAAK;QACZ,MAAM,IAAIhC,WAAW,4BAA4B,CAAC,6BAA6B,EAAE6B,OAAO,EAAE,EAAE,AAACG,IAAcC,OAAO,EAAE;IACtH;IACA,OAAO;QAAER,QAAQS;QAAIjB,MAAMV,iBAAiB2B;IAAI;AAClD;AAEA,+FAA+F;AAC/F,OAAO,MAAMG,mBAA0C;IACrDC,UAAUzB;IACV0B,eAAezB;IACf0B,kBAAkB7B;IAClBiB;IACAJ;IACA,kGAAkG;IAClG,oGAAoG;IACpG,sGAAsG;IACtGiB,UAAU,CAACT,MAAQ,iEAAiEU,IAAI,CAACV,IAAIC,OAAO;IACpGlB;IACAW;IACAd,aAAa,CAACa,QAAQR,OAASL,YAAYa,QAAQR;AACrD,EAAE;AAEF,OAAO,eAAe0B,UAAUzB,GAAmB;IACjD,OAAOf,gBAAgBe,KAAKmB;AAC9B"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/open.ts"],"sourcesContent":["import type { Database } from '@tursodatabase/database';\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 { checkpointWal, createConnection } from './connection.ts';\nimport { CONNECT_OPTS, TURSO_PACKAGE, tursoApi } from './native.ts';\nimport { CONTENT_FTS_DDL, tursoDialect } from './reconcile.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.turso.db';\n// Independent of sqlite's and duckdb's SCHEMA_VERSION: each store's cache shape evolves\n// separately. Covers the FTS indexes, the \"_ngram\" sidecar columns, and embeddings.vector's width.\nexport const SCHEMA_VERSION = '5';\n\nexport type { OpenResult };\n\n// This store's Handle (types.ts's OpenDialect<Handle>) is the connected Database itself: no\n// extra native state to thread, unlike duckdb's separate instance/connection pair.\nasync function ensureSchema(_handle: Database, conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS content (\"path\" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);\n for (const ddl of CONTENT_FTS_DDL) await conn.exec(ddl);\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 F32_BLOB(STORE_DIMS) instead of the embed feature's engine-neutral BLOB DDL. `scale`\n // is kept unused, so the shared reconcile-time INSERT/DELETE names a column both stores have.\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 F32_BLOB(${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 close(handle: Database): Promise<void> {\n await checkpointWal(handle);\n await handle.close();\n}\n\nasync function setDerivedBusyTimeout(_handle: Database, conn: Connection, ms: number): Promise<void> {\n await conn.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: Database; conn: Connection }> {\n // Dynamic, not a top-level import: a sqlite or duckdb tree must never attempt to resolve this\n // optional dependency until a turso tree is actually opened. Installed on first use if missing.\n let turso: Awaited<ReturnType<typeof tursoApi>>;\n try {\n turso = await tursoApi();\n } catch (err) {\n if (err instanceof SenseError) throw err;\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" needs the ${TURSO_PACKAGE} package (${(err as Error).message})`);\n }\n\n let db: Database;\n try {\n // `timeout` is connect-time only in this client; the derived value is set via runtime PRAGMA\n // below. Options in native.ts, shared with reclaimSpace's own connection.\n db = await turso.connect(dbPath, { ...CONNECT_OPTS, experimental: [...CONNECT_OPTS.experimental] });\n } catch (err) {\n throw new SenseError('STORE_DEPENDENCY_MISSING', `store \"turso\" failed to open ${dbPath}: ${(err as Error).message}`);\n }\n return { handle: db, conn: createConnection(db) };\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const tursoOpenDialect: OpenDialect<Database> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: tursoDialect,\n connect,\n close,\n // \"Locking error: Failed locking file ...\", worded per platform: posix \"File is locked by another\n // process\", Windows \"another process has locked a portion of the file (os error 33)\". Distinct from\n // the write-time \"database is locked\" its connect-time `timeout` covers; that one never reaches here.\n isLocked: (err) => /File is locked by another process|locked a portion of the file/.test(err.message),\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn) => createStore(handle, conn),\n};\n\nexport async function openTurso(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, tursoOpenDialect);\n}\n"],"names":["featureSignature","STORE_DIMS","SenseError","activeFeatures","FEATURES","openWithDialect","getMeta","setMeta","checkpointWal","createConnection","CONNECT_OPTS","TURSO_PACKAGE","tursoApi","CONTENT_FTS_DDL","tursoDialect","createStore","DB_FILENAME","SCHEMA_VERSION","ensureSchema","_handle","conn","cfg","exec","ddl","feature","name","schema","close","handle","setDerivedBusyTimeout","ms","connect","dbPath","_cfg","turso","err","message","db","experimental","tursoOpenDialect","filename","schemaVersion","reconcileDialect","isLocked","test","openTurso"],"mappings":"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"}