sensemaking 0.25.1 → 0.27.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.
- package/README.md +48 -12
- package/dist/cjs/cli/index.d.cts +3 -3
- package/dist/cjs/cli/index.d.ts +3 -3
- package/dist/cjs/cli/index.js +3 -3
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/path.js +5 -29
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +43 -2
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +4 -3
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +38 -6
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/commands/index.d.cts +6 -3
- package/dist/cjs/commands/index.d.ts +6 -3
- package/dist/cjs/commands/index.js +4 -0
- package/dist/cjs/commands/index.js.map +1 -1
- package/dist/cjs/commands/map.d.cts +14 -5
- package/dist/cjs/commands/map.d.ts +14 -5
- package/dist/cjs/commands/map.js +42 -35
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/path.d.cts +14 -0
- package/dist/cjs/commands/path.d.ts +14 -0
- package/dist/cjs/commands/path.js +209 -0
- package/dist/cjs/commands/path.js.map +1 -0
- package/dist/cjs/commands/peek.d.cts +25 -2
- package/dist/cjs/commands/peek.d.ts +25 -2
- package/dist/cjs/commands/peek.js +131 -50
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.d.cts +16 -3
- package/dist/cjs/commands/related.d.ts +16 -3
- package/dist/cjs/commands/related.js +131 -98
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.d.cts +1 -0
- package/dist/cjs/commands/scope.d.ts +1 -0
- package/dist/cjs/commands/scope.js +149 -0
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.d.cts +30 -3
- package/dist/cjs/commands/search.d.ts +30 -3
- package/dist/cjs/commands/search.js +240 -73
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.d.cts +22 -7
- package/dist/cjs/commands/signals.d.ts +22 -7
- package/dist/cjs/commands/signals.js +77 -15
- package/dist/cjs/commands/signals.js.map +1 -1
- package/dist/cjs/commands/snapshot.d.cts +3 -0
- package/dist/cjs/commands/snapshot.d.ts +3 -0
- package/dist/cjs/commands/snapshot.js +180 -0
- package/dist/cjs/commands/snapshot.js.map +1 -0
- package/dist/cjs/config/load.js +6 -0
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/embed/cohere.js +41 -16
- package/dist/cjs/embed/cohere.js.map +1 -1
- package/dist/cjs/embed/endpoint.d.cts +2 -0
- package/dist/cjs/embed/endpoint.d.ts +2 -0
- package/dist/cjs/embed/endpoint.js +30 -0
- package/dist/cjs/embed/endpoint.js.map +1 -0
- package/dist/cjs/embed/handoff.d.cts +7 -2
- package/dist/cjs/embed/handoff.d.ts +7 -2
- package/dist/cjs/embed/handoff.js +10 -0
- package/dist/cjs/embed/handoff.js.map +1 -1
- package/dist/cjs/embed/http.d.cts +18 -1
- package/dist/cjs/embed/http.d.ts +18 -1
- package/dist/cjs/embed/http.js +239 -40
- package/dist/cjs/embed/http.js.map +1 -1
- package/dist/cjs/embed/openai.js +39 -17
- package/dist/cjs/embed/openai.js.map +1 -1
- package/dist/cjs/embed/query.d.cts +9 -3
- package/dist/cjs/embed/query.d.ts +9 -3
- package/dist/cjs/embed/query.js +257 -90
- package/dist/cjs/embed/query.js.map +1 -1
- package/dist/cjs/embed/registry.d.cts +2 -2
- package/dist/cjs/embed/registry.d.ts +2 -2
- package/dist/cjs/embed/registry.js +36 -4
- package/dist/cjs/embed/registry.js.map +1 -1
- package/dist/cjs/embed/static.js +6 -2
- package/dist/cjs/embed/static.js.map +1 -1
- package/dist/cjs/embed/store.js +42 -77
- package/dist/cjs/embed/store.js.map +1 -1
- package/dist/cjs/embed/types.d.cts +5 -2
- package/dist/cjs/embed/types.d.ts +5 -2
- package/dist/cjs/embed/types.js.map +1 -1
- package/dist/cjs/features/embed.js +290 -43
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/graph/traverse.d.cts +2 -2
- package/dist/cjs/graph/traverse.d.ts +2 -2
- package/dist/cjs/graph/traverse.js +15 -5
- package/dist/cjs/graph/traverse.js.map +1 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output/output.d.cts +4 -36
- package/dist/cjs/output/output.d.ts +4 -36
- package/dist/cjs/output/output.js +1 -2
- package/dist/cjs/output/output.js.map +1 -1
- package/dist/cjs/scan/pool.d.cts +2 -1
- package/dist/cjs/scan/pool.d.ts +2 -1
- package/dist/cjs/scan/pool.js +116 -27
- package/dist/cjs/scan/pool.js.map +1 -1
- package/dist/cjs/scan/reparse.d.cts +1 -0
- package/dist/cjs/scan/reparse.d.ts +1 -0
- package/dist/cjs/scan/reparse.js +75 -21
- package/dist/cjs/scan/reparse.js.map +1 -1
- package/dist/cjs/store/builder.d.cts +11 -21
- package/dist/cjs/store/builder.d.ts +11 -21
- package/dist/cjs/store/builder.js +1077 -438
- package/dist/cjs/store/builder.js.map +1 -1
- package/dist/cjs/store/duckdb/connection.js +16 -2
- package/dist/cjs/store/duckdb/connection.js.map +1 -1
- package/dist/cjs/store/duckdb/open.d.cts +1 -1
- package/dist/cjs/store/duckdb/open.d.ts +1 -1
- package/dist/cjs/store/duckdb/open.js +73 -65
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js +533 -15
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/embed-scope.js +19 -9
- package/dist/cjs/store/embed-scope.js.map +1 -1
- package/dist/cjs/store/index.d.cts +6 -0
- package/dist/cjs/store/index.d.ts +6 -0
- package/dist/cjs/store/index.js +8 -4
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/open.d.cts +6 -1
- package/dist/cjs/store/open.d.ts +6 -1
- package/dist/cjs/store/open.js +230 -307
- package/dist/cjs/store/open.js.map +1 -1
- package/dist/cjs/store/readiness.d.cts +5 -0
- package/dist/cjs/store/readiness.d.ts +5 -0
- package/dist/cjs/store/readiness.js +39 -0
- package/dist/cjs/store/readiness.js.map +1 -0
- package/dist/cjs/store/reconcile.d.cts +26 -1
- package/dist/cjs/store/reconcile.d.ts +26 -1
- package/dist/cjs/store/reconcile.js +670 -576
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/signature.d.cts +1 -0
- package/dist/cjs/store/signature.d.ts +1 -0
- package/dist/cjs/store/signature.js +9 -0
- package/dist/cjs/store/signature.js.map +1 -1
- package/dist/cjs/store/sqlite/open.d.cts +1 -1
- package/dist/cjs/store/sqlite/open.d.ts +1 -1
- package/dist/cjs/store/sqlite/open.js +84 -90
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
- package/dist/cjs/store/sqlite/store.js +63 -60
- package/dist/cjs/store/sqlite/store.js.map +1 -1
- package/dist/cjs/store/sqlite/vectors.d.cts +2 -2
- package/dist/cjs/store/sqlite/vectors.d.ts +2 -2
- package/dist/cjs/store/sqlite/vectors.js +8 -4
- package/dist/cjs/store/sqlite/vectors.js.map +1 -1
- package/dist/cjs/store/stages.js.map +1 -1
- package/dist/cjs/store/turso/connection.d.cts +1 -0
- package/dist/cjs/store/turso/connection.d.ts +1 -0
- package/dist/cjs/store/turso/connection.js +319 -94
- package/dist/cjs/store/turso/connection.js.map +1 -1
- package/dist/cjs/store/turso/open.d.cts +1 -1
- package/dist/cjs/store/turso/open.d.ts +1 -1
- package/dist/cjs/store/turso/open.js +70 -74
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/store.js +25 -23
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/store/turso/vectors.d.cts +2 -2
- package/dist/cjs/store/turso/vectors.d.ts +2 -2
- package/dist/cjs/store/turso/vectors.js +26 -16
- package/dist/cjs/store/turso/vectors.js.map +1 -1
- package/dist/cjs/store/types.d.cts +4 -1
- package/dist/cjs/store/types.d.ts +4 -1
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/cjs/store/vectors.d.cts +14 -1
- package/dist/cjs/store/vectors.d.ts +14 -1
- package/dist/cjs/store/vectors.js +61 -0
- package/dist/cjs/store/vectors.js.map +1 -1
- package/dist/cjs/watch.d.cts +6 -0
- package/dist/cjs/watch.d.ts +6 -0
- package/dist/cjs/watch.js +42 -10
- package/dist/cjs/watch.js.map +1 -1
- package/dist/cjs/workers/parse.d.cts +4 -3
- package/dist/cjs/workers/parse.d.ts +4 -3
- package/dist/cjs/workers/parse.js +19 -17
- package/dist/cjs/workers/parse.js.map +1 -1
- package/dist/esm/cli/index.d.ts +3 -3
- package/dist/esm/cli/index.js +3 -3
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/path.js +4 -10
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js +20 -2
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js +5 -3
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js +12 -2
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/commands/index.d.ts +6 -3
- package/dist/esm/commands/index.js +1 -0
- package/dist/esm/commands/index.js.map +1 -1
- package/dist/esm/commands/map.d.ts +14 -5
- package/dist/esm/commands/map.js +2 -0
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/path.d.ts +14 -0
- package/dist/esm/commands/path.js +33 -0
- package/dist/esm/commands/path.js.map +1 -0
- package/dist/esm/commands/peek.d.ts +25 -2
- package/dist/esm/commands/peek.js +59 -19
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.d.ts +16 -3
- package/dist/esm/commands/related.js +45 -28
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.d.ts +1 -0
- package/dist/esm/commands/scope.js +58 -0
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.d.ts +30 -3
- package/dist/esm/commands/search.js +95 -20
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.d.ts +22 -7
- package/dist/esm/commands/signals.js +74 -17
- package/dist/esm/commands/signals.js.map +1 -1
- package/dist/esm/commands/snapshot.d.ts +3 -0
- package/dist/esm/commands/snapshot.js +25 -0
- package/dist/esm/commands/snapshot.js.map +1 -0
- package/dist/esm/config/load.js +6 -0
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/embed/cohere.js +35 -10
- package/dist/esm/embed/cohere.js.map +1 -1
- package/dist/esm/embed/endpoint.d.ts +2 -0
- package/dist/esm/embed/endpoint.js +11 -0
- package/dist/esm/embed/endpoint.js.map +1 -0
- package/dist/esm/embed/handoff.d.ts +7 -2
- package/dist/esm/embed/handoff.js +7 -0
- package/dist/esm/embed/handoff.js.map +1 -1
- package/dist/esm/embed/http.d.ts +18 -1
- package/dist/esm/embed/http.js +137 -17
- package/dist/esm/embed/http.js.map +1 -1
- package/dist/esm/embed/openai.js +34 -12
- package/dist/esm/embed/openai.js.map +1 -1
- package/dist/esm/embed/query.d.ts +9 -3
- package/dist/esm/embed/query.js +98 -26
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/embed/registry.d.ts +2 -2
- package/dist/esm/embed/registry.js +34 -5
- package/dist/esm/embed/registry.js.map +1 -1
- package/dist/esm/embed/static.js +10 -2
- package/dist/esm/embed/static.js.map +1 -1
- package/dist/esm/embed/store.js +30 -21
- package/dist/esm/embed/store.js.map +1 -1
- package/dist/esm/embed/types.d.ts +5 -2
- package/dist/esm/embed/types.js.map +1 -1
- package/dist/esm/features/embed.js +82 -21
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/graph/traverse.d.ts +2 -2
- package/dist/esm/graph/traverse.js +4 -2
- package/dist/esm/graph/traverse.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output/output.d.ts +4 -36
- package/dist/esm/output/output.js +1 -2
- package/dist/esm/output/output.js.map +1 -1
- package/dist/esm/scan/pool.d.ts +2 -1
- package/dist/esm/scan/pool.js +80 -23
- package/dist/esm/scan/pool.js.map +1 -1
- package/dist/esm/scan/reparse.d.ts +1 -0
- package/dist/esm/scan/reparse.js +40 -12
- package/dist/esm/scan/reparse.js.map +1 -1
- package/dist/esm/store/builder.d.ts +11 -21
- package/dist/esm/store/builder.js +337 -133
- package/dist/esm/store/builder.js.map +1 -1
- package/dist/esm/store/duckdb/connection.js +9 -1
- package/dist/esm/store/duckdb/connection.js.map +1 -1
- package/dist/esm/store/duckdb/open.d.ts +1 -1
- package/dist/esm/store/duckdb/open.js +13 -9
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js +145 -10
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/embed-scope.js +16 -6
- package/dist/esm/store/embed-scope.js.map +1 -1
- package/dist/esm/store/index.d.ts +6 -0
- package/dist/esm/store/index.js +10 -4
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/open.d.ts +6 -1
- package/dist/esm/store/open.js +77 -87
- package/dist/esm/store/open.js.map +1 -1
- package/dist/esm/store/readiness.d.ts +5 -0
- package/dist/esm/store/readiness.js +11 -0
- package/dist/esm/store/readiness.js.map +1 -0
- package/dist/esm/store/reconcile.d.ts +26 -1
- package/dist/esm/store/reconcile.js +142 -120
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/signature.d.ts +1 -0
- package/dist/esm/store/signature.js +8 -0
- package/dist/esm/store/signature.js.map +1 -1
- package/dist/esm/store/sqlite/open.d.ts +1 -1
- package/dist/esm/store/sqlite/open.js +12 -12
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.js +2 -2
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/store.js +21 -18
- package/dist/esm/store/sqlite/store.js.map +1 -1
- package/dist/esm/store/sqlite/vectors.d.ts +2 -2
- package/dist/esm/store/sqlite/vectors.js +7 -5
- package/dist/esm/store/sqlite/vectors.js.map +1 -1
- package/dist/esm/store/stages.js.map +1 -1
- package/dist/esm/store/turso/connection.d.ts +1 -0
- package/dist/esm/store/turso/connection.js +97 -28
- package/dist/esm/store/turso/connection.js.map +1 -1
- package/dist/esm/store/turso/open.d.ts +1 -1
- package/dist/esm/store/turso/open.js +9 -8
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/store.js +16 -14
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/store/turso/vectors.d.ts +2 -2
- package/dist/esm/store/turso/vectors.js +27 -17
- package/dist/esm/store/turso/vectors.js.map +1 -1
- package/dist/esm/store/types.d.ts +4 -1
- package/dist/esm/store/types.js.map +1 -1
- package/dist/esm/store/vectors.d.ts +14 -1
- package/dist/esm/store/vectors.js +55 -0
- package/dist/esm/store/vectors.js.map +1 -1
- package/dist/esm/watch.d.ts +6 -0
- package/dist/esm/watch.js +40 -14
- package/dist/esm/watch.js.map +1 -1
- package/dist/esm/workers/parse.d.ts +4 -3
- package/dist/esm/workers/parse.js +19 -17
- package/dist/esm/workers/parse.js.map +1 -1
- package/package.json +1 -1
- package/skills/sense/EXAMPLES.md +3 -3
- package/skills/sense/SKILL.md +11 -3
- package/skills/sense/references/search.md +17 -1
- package/skills/sense/references/sql.md +1 -1
- package/skills/sense/references/stores/duckdb.md +1 -1
- package/skills/sense/references/terminology.md +32 -0
- package/skills/sense-setup/SKILL.md +2 -2
- package/skills/sense-setup/references/embeddings.md +1 -1
- package/skills/sense-setup/references/store-benchmarks.md +5 -5
- package/skills/sense-setup/references/stores/duckdb.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport type { ReconcileDelta } from '../../features/types.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { segmentField } from '../../text/segment.ts';\nimport { quoteIdent, recordReconcileDuration } from '../shared.ts';\nimport { BEGIN_WRITE } from '../transaction.ts';\nimport type { Connection, ReconcileDialect } from '../types.ts';\n\n// This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in\n// store/reconcile.ts. content is FTS5-virtual with \"_seg\" sidecars for lexical.ts.\n\n// SQLite's compile-time SQLITE_MAX_COLUMN, default 2000 (https://www.sqlite.org/limits.html).\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Shared by reconcileContent() and the tokenize-only rebuild in open.ts, so both prepare the\n// same literal instead of two copies drifting apart.\nexport const INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)`;\n\n// A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.\n// Assumes the frontmatter row for doc.relPath already exists (rowid subquery).\nexport function contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmentField(doc.search.title), segmentField(doc.search.summary), segmentField(doc.search.text)];\n}\n\nasync function reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], _delta: ReconcileDelta, _cfg: Config): Promise<void> {\n // FTS5 has no upsert, so delete-before-insert, coupled to the frontmatter rowid (indexed via\n // its PRIMARY KEY) rather than the UNINDEXED `path` column, which a per-row DELETE would scan to find.\n //
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport type { ReconcileDelta } from '../../features/types.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { segmentField } from '../../text/segment.ts';\nimport { quoteIdent, recordReconcileDuration } from '../shared.ts';\nimport { BEGIN_WRITE } from '../transaction.ts';\nimport type { Connection, ReconcileDialect } from '../types.ts';\n\n// This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in\n// store/reconcile.ts. content is FTS5-virtual with \"_seg\" sidecars for lexical.ts.\n\n// SQLite's compile-time SQLITE_MAX_COLUMN, default 2000 (https://www.sqlite.org/limits.html).\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Shared by reconcileContent() and the tokenize-only rebuild in open.ts, so both prepare the\n// same literal instead of two copies drifting apart.\nexport const INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)`;\n\n// A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.\n// Assumes the frontmatter row for doc.relPath already exists (rowid subquery).\nexport function contentRow(doc: ParsedDoc): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmentField(doc.search.title), segmentField(doc.search.summary), segmentField(doc.search.text)];\n}\n\nasync function reconcileContent(conn: Connection, touched: string[], docs: ParsedDoc[], _delta: ReconcileDelta, _cfg: Config): Promise<void> {\n // FTS5 has no upsert, so delete-before-insert, coupled to the frontmatter rowid (indexed via\n // its PRIMARY KEY) rather than the UNINDEXED `path` column, which a per-row DELETE would scan to find.\n // builder.ts's generation fence rejects a plan whose added/touched classification went stale\n // behind another publication, so this receives one classification from the locked generation.\n if (touched.length > 0)\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n touched.map((p) => [p])\n );\n\n if (docs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, docs.map(contentRow));\n}\n\n// SQLite's ADD COLUMN is metadata-only, so a loop costs nothing extra over one statement.\nasync function addColumns(conn: Connection, names: string[]): Promise<void> {\n for (const name of names) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(name)}`);\n}\n\nexport const sqliteDialect: ReconcileDialect = {\n beginMode: () => BEGIN_WRITE,\n checkColumnLimit(count) {\n // Fence before ALTERing: SQLite's own failure past this point is a raw\n // \"too many columns on sqlite_altertab_frontmatter\" with no indication of the boundary or the levers.\n if (count > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError(\n 'COLUMN_LIMIT',\n `frontmatter would need ${count} columns, crossing SQLite's compile-time SQLITE_MAX_COLUMN limit (default ${MAX_FRONTMATTER_COLUMNS}; see https://www.sqlite.org/limits.html). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`\n );\n }\n },\n addColumns,\n reconcileContent,\n recordDuration: recordReconcileDuration,\n};\n"],"names":["SenseError","segmentField","quoteIdent","recordReconcileDuration","BEGIN_WRITE","MAX_FRONTMATTER_COLUMNS","INSERT_CONTENT_SQL","contentRow","doc","relPath","search","title","summary","text","reconcileContent","conn","touched","docs","_delta","_cfg","length","runBatch","map","p","addColumns","names","name","exec","sqliteDialect","beginMode","checkColumnLimit","count","recordDuration"],"mappings":"AACA,SAASA,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,YAAY,QAAQ,wBAAwB;AACrD,SAASC,UAAU,EAAEC,uBAAuB,QAAQ,eAAe;AACnE,SAASC,WAAW,QAAQ,oBAAoB;AAGhD,qFAAqF;AACrF,mFAAmF;AAEnF,8FAA8F;AAC9F,MAAMC,0BAA0B;AAEhC,6FAA6F;AAC7F,qDAAqD;AACrD,OAAO,MAAMC,qBAAqB,CAAC,0KAA0K,CAAC,CAAC;AAE/M,uFAAuF;AACvF,+EAA+E;AAC/E,OAAO,SAASC,WAAWC,GAAc;IACvC,OAAO;QAACA,IAAIC,OAAO;QAAED,IAAIE,MAAM,CAACC,KAAK;QAAEH,IAAIE,MAAM,CAACE,OAAO;QAAEJ,IAAIE,MAAM,CAACG,IAAI;QAAEL,IAAIC,OAAO;QAAER,aAAaO,IAAIE,MAAM,CAACC,KAAK;QAAGV,aAAaO,IAAIE,MAAM,CAACE,OAAO;QAAGX,aAAaO,IAAIE,MAAM,CAACG,IAAI;KAAE;AAC3L;AAEA,eAAeC,iBAAiBC,IAAgB,EAAEC,OAAiB,EAAEC,IAAiB,EAAEC,MAAsB,EAAEC,IAAY;IAC1H,6FAA6F;IAC7F,uGAAuG;IACvG,6FAA6F;IAC7F,8FAA8F;IAC9F,IAAIH,QAAQI,MAAM,GAAG,GACnB,MAAML,KAAKM,QAAQ,CACjB,sFACAL,QAAQM,GAAG,CAAC,CAACC,IAAM;YAACA;SAAE;IAG1B,IAAIN,KAAKG,MAAM,GAAG,GAAG,MAAML,KAAKM,QAAQ,CAACf,oBAAoBW,KAAKK,GAAG,CAACf;AACxE;AAEA,0FAA0F;AAC1F,eAAeiB,WAAWT,IAAgB,EAAEU,KAAe;IACzD,KAAK,MAAMC,QAAQD,MAAO,MAAMV,KAAKY,IAAI,CAAC,CAAC,mCAAmC,EAAEzB,WAAWwB,OAAO;AACpG;AAEA,OAAO,MAAME,gBAAkC;IAC7CC,WAAW,IAAMzB;IACjB0B,kBAAiBC,KAAK;QACpB,uEAAuE;QACvE,sGAAsG;QACtG,IAAIA,QAAQ1B,yBAAyB;YACnC,MAAM,IAAIL,WACR,gBACA,CAAC,uBAAuB,EAAE+B,MAAM,0EAA0E,EAAE1B,wBAAwB,wKAAwK,CAAC;QAEjT;IACF;IACAmB;IACAV;IACAkB,gBAAgB7B;AAClB,EAAE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getColumns } from '../shared.js';
|
|
2
2
|
import { withTransaction } from '../transaction.js';
|
|
3
|
-
import { hasVectorRow, pendingRows } from '../vectors.js';
|
|
3
|
+
import { createNativeVectorScopeOwner, hasVectorRow, pendingRows } from '../vectors.js';
|
|
4
4
|
import { fieldStats } from './fieldStats.js';
|
|
5
5
|
import { queryLexical } from './lexical.js';
|
|
6
6
|
import { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.js';
|
|
@@ -11,6 +11,25 @@ export const CAPABILITIES = new Set([
|
|
|
11
11
|
// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one
|
|
12
12
|
// Connection instance (conn) with the builder's own reconcile call so transaction depth is tracked against the same object everywhere.
|
|
13
13
|
export function createStore(db, conn) {
|
|
14
|
+
const vectorScope = createNativeVectorScopeOwner();
|
|
15
|
+
const vectors = {
|
|
16
|
+
async pending () {
|
|
17
|
+
return pendingRows(conn);
|
|
18
|
+
},
|
|
19
|
+
async writeVectors (rows) {
|
|
20
|
+
await writeVectorBatch(conn, rows);
|
|
21
|
+
},
|
|
22
|
+
async candidates (qv, storeDims, fetch, allowed) {
|
|
23
|
+
return scanCandidates(conn, qv, storeDims, fetch, allowed, vectorScope.activeFor(allowed));
|
|
24
|
+
},
|
|
25
|
+
async similar (path, opts) {
|
|
26
|
+
return scanSimilar(conn, path, opts, vectorScope.activeFor(opts.allowed));
|
|
27
|
+
},
|
|
28
|
+
async hasVector (path) {
|
|
29
|
+
return hasVectorRow(conn, path);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
vectorScope.bind(vectors);
|
|
14
33
|
return {
|
|
15
34
|
name: 'sqlite',
|
|
16
35
|
capabilities: CAPABILITIES,
|
|
@@ -41,23 +60,7 @@ export function createStore(db, conn) {
|
|
|
41
60
|
return queryLexical(conn, terms, opts);
|
|
42
61
|
}
|
|
43
62
|
},
|
|
44
|
-
vectors
|
|
45
|
-
async pending () {
|
|
46
|
-
return pendingRows(conn);
|
|
47
|
-
},
|
|
48
|
-
async writeVectors (rows) {
|
|
49
|
-
await writeVectorBatch(conn, rows);
|
|
50
|
-
},
|
|
51
|
-
async candidates (qv, storeDims, fetch, allowed) {
|
|
52
|
-
return scanCandidates(conn, qv, storeDims, fetch, allowed);
|
|
53
|
-
},
|
|
54
|
-
async similar (path, opts) {
|
|
55
|
-
return scanSimilar(conn, path, opts);
|
|
56
|
-
},
|
|
57
|
-
async hasVector (path) {
|
|
58
|
-
return hasVectorRow(conn, path);
|
|
59
|
-
}
|
|
60
|
-
},
|
|
63
|
+
vectors,
|
|
61
64
|
async engineStatus () {
|
|
62
65
|
// Read back rather than recomputed: this is what open() actually set (3x the largest
|
|
63
66
|
// recorded reconcile, floored at 30s, capped at 10min), not a value re-derived here.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/store.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store, VectorWriteRow } from '../types.ts';\nimport { hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['vectors', 'segment']);\n\n// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one\n// Connection instance (conn) with the builder's own reconcile call so transaction depth is tracked against the same object everywhere.\nexport function createStore(db: DatabaseSync, conn: Connection): Store {\n return {\n name: 'sqlite',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n async fieldStats(columns, scopeWhere) {\n return fieldStats(conn, columns, scopeWhere);\n },\n },\n lexical: {\n async query(terms, opts) {\n return queryLexical(conn, terms, opts);\n },\n },\n vectors
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/store.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store, VectorStore, VectorWriteRow } from '../types.ts';\nimport { createNativeVectorScopeOwner, hasVectorRow, pendingRows } from '../vectors.ts';\nimport { fieldStats } from './fieldStats.ts';\nimport { queryLexical } from './lexical.ts';\nimport { scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['vectors', 'segment']);\n\n// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one\n// Connection instance (conn) with the builder's own reconcile call so transaction depth is tracked against the same object everywhere.\nexport function createStore(db: DatabaseSync, conn: Connection): Store {\n const vectorScope = createNativeVectorScopeOwner();\n const vectors: VectorStore = {\n async pending() {\n return pendingRows(conn);\n },\n async writeVectors(rows: VectorWriteRow[]) {\n await writeVectorBatch(conn, rows);\n },\n async candidates(qv, storeDims, fetch, allowed) {\n return scanCandidates(conn, qv, storeDims, fetch, allowed, vectorScope.activeFor(allowed));\n },\n async similar(path, opts) {\n return scanSimilar(conn, path, opts, vectorScope.activeFor(opts.allowed));\n },\n async hasVector(path) {\n return hasVectorRow(conn, path);\n },\n };\n vectorScope.bind(vectors);\n\n return {\n name: 'sqlite',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n async fieldStats(columns, scopeWhere) {\n return fieldStats(conn, columns, scopeWhere);\n },\n },\n lexical: {\n async query(terms, opts) {\n return queryLexical(conn, terms, opts);\n },\n },\n vectors,\n async engineStatus() {\n // Read back rather than recomputed: this is what open() actually set (3x the largest\n // recorded reconcile, floored at 30s, capped at 10min), not a value re-derived here.\n const row = db.prepare('PRAGMA busy_timeout').get() as { timeout: number };\n return { busy_timeout: `${row.timeout}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)` };\n },\n raw: {\n async prepare(sql: string) {\n const stmt = db.prepare(sql);\n stmt.setReadBigInts(true); // int64 past 2^53 arrives as BigInt instead of throwing at step time\n return {\n columns: () => stmt.columns(),\n // sense sql streams through an async iterator wrapping the sync iterate().\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...(params as Parameters<typeof stmt.iterate>));\n },\n };\n },\n },\n async close() {\n db.close();\n },\n };\n}\n"],"names":["getColumns","withTransaction","createNativeVectorScopeOwner","hasVectorRow","pendingRows","fieldStats","queryLexical","scanCandidates","scanSimilar","writeVectorBatch","CAPABILITIES","Set","createStore","db","conn","vectorScope","vectors","pending","writeVectors","rows","candidates","qv","storeDims","fetch","allowed","activeFor","similar","path","opts","hasVector","bind","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","docs","columns","scopeWhere","lexical","query","terms","engineStatus","row","get","busy_timeout","timeout","raw","stmt","setReadBigInts","iterate","params","close"],"mappings":"AACA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,SAASC,4BAA4B,EAAEC,YAAY,EAAEC,WAAW,QAAQ,gBAAgB;AACxF,SAASC,UAAU,QAAQ,kBAAkB;AAC7C,SAASC,YAAY,QAAQ,eAAe;AAC5C,SAASC,cAAc,EAAEC,WAAW,EAAEC,gBAAgB,QAAQ,eAAe;AAE7E,OAAO,MAAMC,eAAwC,IAAIC,IAAI;IAAC;IAAW;CAAU,EAAE;AAErF,0FAA0F;AAC1F,uIAAuI;AACvI,OAAO,SAASC,YAAYC,EAAgB,EAAEC,IAAgB;IAC5D,MAAMC,cAAcb;IACpB,MAAMc,UAAuB;QAC3B,MAAMC;YACJ,OAAOb,YAAYU;QACrB;QACA,MAAMI,cAAaC,IAAsB;YACvC,MAAMV,iBAAiBK,MAAMK;QAC/B;QACA,MAAMC,YAAWC,EAAE,EAAEC,SAAS,EAAEC,KAAK,EAAEC,OAAO;YAC5C,OAAOjB,eAAeO,MAAMO,IAAIC,WAAWC,OAAOC,SAAST,YAAYU,SAAS,CAACD;QACnF;QACA,MAAME,SAAQC,IAAI,EAAEC,IAAI;YACtB,OAAOpB,YAAYM,MAAMa,MAAMC,MAAMb,YAAYU,SAAS,CAACG,KAAKJ,OAAO;QACzE;QACA,MAAMK,WAAUF,IAAI;YAClB,OAAOxB,aAAaW,MAAMa;QAC5B;IACF;IACAZ,YAAYe,IAAI,CAACd;IAEjB,OAAO;QACLe,MAAM;QACNC,cAActB;QACd,MAAMuB,MAAKC,GAAW;YACpB,MAAMpB,KAAKmB,IAAI,CAACC;QAClB;QACA,MAAMC,SAAQD,GAAW;YACvB,OAAOpB,KAAKqB,OAAO,CAACD;QACtB;QACA,MAAME,UAASF,GAAW,EAAEG,SAAsB;YAChD,MAAMvB,KAAKsB,QAAQ,CAACF,KAAKG;QAC3B;QACA,MAAMC,aAAeC,EAAoB;YACvC,OAAOtC,gBAAgBa,MAAMyB;QAC/B;QACAC,MAAM;YACJ,MAAMC;gBACJ,OAAO;uBAAK,MAAMzC,WAAWc;iBAAO;YACtC;YACA,MAAMT,YAAWoC,OAAO,EAAEC,UAAU;gBAClC,OAAOrC,WAAWS,MAAM2B,SAASC;YACnC;QACF;QACAC,SAAS;YACP,MAAMC,OAAMC,KAAK,EAAEjB,IAAI;gBACrB,OAAOtB,aAAaQ,MAAM+B,OAAOjB;YACnC;QACF;QACAZ;QACA,MAAM8B;YACJ,qFAAqF;YACrF,qFAAqF;YACrF,MAAMC,MAAMlC,GAAGsB,OAAO,CAAC,uBAAuBa,GAAG;YACjD,OAAO;gBAAEC,cAAc,GAAGF,IAAIG,OAAO,CAAC,kFAAkF,CAAC;YAAC;QAC5H;QACAC,KAAK;YACH,MAAMhB,SAAQD,GAAW;gBACvB,MAAMkB,OAAOvC,GAAGsB,OAAO,CAACD;gBACxBkB,KAAKC,cAAc,CAAC,OAAO,qEAAqE;gBAChG,OAAO;oBACLZ,SAAS,IAAMW,KAAKX,OAAO;oBAC3B,2EAA2E;oBAC3Ea,SAAS,gBAAiB,GAAGC,MAAiB;wBAC5C,OAAOH,KAAKE,OAAO,IAAKC;oBAC1B;gBACF;YACF;QACF;QACA,MAAMC;YACJ3C,GAAG2C,KAAK;QACV;IACF;AACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Connection, VectorCandidate, VectorSimilar, VectorWriteRow } from '../types.js';
|
|
2
2
|
export declare function writeVectorBatch(conn: Connection, rows: VectorWriteRow[]): Promise<void>;
|
|
3
|
-
export declare function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string
|
|
3
|
+
export declare function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>, nativeScope?: boolean): Promise<VectorCandidate[]>;
|
|
4
4
|
export declare function scanSimilar(conn: Connection, path: string, opts: {
|
|
5
5
|
exclude: Set<string>;
|
|
6
6
|
allowed?: Set<string>;
|
|
7
7
|
k: number;
|
|
8
|
-
}): Promise<VectorSimilar[]>;
|
|
8
|
+
}, nativeScope?: boolean): Promise<VectorSimilar[]>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { asCosine, compareVectorScores, sampleEvenly } from '../vectors.js';
|
|
1
|
+
import { asCosine, compareVectorScores, NATIVE_VECTOR_SCOPE_TABLE, sampleEvenly } from '../vectors.js';
|
|
2
2
|
// One runBatch call per provider batch: the caller (embed/query.ts) already batches by the
|
|
3
3
|
// provider's batchCap, so this is one crossing per batch, never per row.
|
|
4
4
|
export async function writeVectorBatch(conn, rows) {
|
|
@@ -12,8 +12,9 @@ export async function writeVectorBatch(conn, rows) {
|
|
|
12
12
|
}
|
|
13
13
|
// Best chunk per file by cosine, its line range riding along: the JS loop over stored int8
|
|
14
14
|
// BLOBs this store's scan strategy is (a native store scans differently, same contract).
|
|
15
|
-
export async function scanCandidates(conn, qv, storeDims, fetch, allowed) {
|
|
16
|
-
const
|
|
15
|
+
export async function scanCandidates(conn, qv, storeDims, fetch, allowed, nativeScope = false) {
|
|
16
|
+
const scope = nativeScope ? ` AND "path" IN (SELECT "path" FROM ${NATIVE_VECTOR_SCOPE_TABLE})` : '';
|
|
17
|
+
const stmt = await conn.prepare(`SELECT "path", chunk, start_line, end_line, vector FROM embeddings WHERE vector IS NOT NULL${scope} ORDER BY "path", chunk`);
|
|
17
18
|
const rows = await stmt.all();
|
|
18
19
|
let queryNorm = 0;
|
|
19
20
|
for(let d = 0; d < Math.min(storeDims, qv.length); d++)queryNorm += qv[d] * qv[d];
|
|
@@ -55,7 +56,7 @@ export async function scanCandidates(conn, qv, storeDims, fetch, allowed) {
|
|
|
55
56
|
}
|
|
56
57
|
// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear
|
|
57
58
|
// scan of stored vectors.
|
|
58
|
-
export async function scanSimilar(conn, path, opts) {
|
|
59
|
+
export async function scanSimilar(conn, path, opts, nativeScope = false) {
|
|
59
60
|
const targetStmt = await conn.prepare('SELECT vector FROM embeddings WHERE "path" = ? AND vector IS NOT NULL ORDER BY chunk');
|
|
60
61
|
const targetRows = await targetStmt.all(path);
|
|
61
62
|
if (targetRows.length === 0) return [];
|
|
@@ -69,7 +70,8 @@ export async function scanSimilar(conn, path, opts) {
|
|
|
69
70
|
norm: Math.sqrt(norm)
|
|
70
71
|
};
|
|
71
72
|
});
|
|
72
|
-
const
|
|
73
|
+
const scope = nativeScope ? ` AND "path" IN (SELECT "path" FROM ${NATIVE_VECTOR_SCOPE_TABLE})` : '';
|
|
74
|
+
const stmt = await conn.prepare(`SELECT "path", vector FROM embeddings WHERE vector IS NOT NULL${scope}`);
|
|
73
75
|
const rows = await stmt.all();
|
|
74
76
|
const best = new Map();
|
|
75
77
|
for (const row of rows){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/vectors.ts"],"sourcesContent":["import type { Connection, VectorCandidate, VectorSimilar, VectorWriteRow } from '../types.ts';\nimport { asCosine, compareVectorScores, sampleEvenly } from '../vectors.ts';\n\n// One runBatch call per provider batch: the caller (embed/query.ts) already batches by the\n// provider's batchCap, so this is one crossing per batch, never per row.\nexport async function writeVectorBatch(conn: Connection, rows: VectorWriteRow[]): Promise<void> {\n if (rows.length === 0) return;\n await conn.runBatch(\n 'UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?',\n rows.map((row) => [row.scale, row.vector, row.path, row.chunk])\n );\n}\n\n// Best chunk per file by cosine, its line range riding along: the JS loop over stored int8\n// BLOBs this store's scan strategy is (a native store scans differently, same contract).\nexport async function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>): Promise<VectorCandidate[]> {\n const stmt = await conn.prepare('SELECT \"path\", chunk, start_line, end_line, vector FROM embeddings WHERE vector IS NOT NULL ORDER BY \"path\", chunk');\n const rows = (await stmt.all()) as Array<{\n path: string;\n chunk: number;\n start_line: number;\n end_line: number;\n vector: Uint8Array;\n }>;\n\n let queryNorm = 0;\n for (let d = 0; d < Math.min(storeDims, qv.length); d++) queryNorm += qv[d] * qv[d];\n queryNorm = Math.sqrt(queryNorm);\n\n const best = new Map<string, { score: number; chunk: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n // Rounding to int8 costs the stored row its unit norm, so dividing by that norm is what makes\n // this a cosine rather than a scaled dot product. `scale` cancels out of the ratio.\n let dot = 0;\n let norm = 0;\n const length = Math.min(q.length, qv.length);\n for (let d = 0; d < length; d++) {\n dot += q[d] * qv[d];\n norm += q[d] * q[d];\n }\n for (let d = length; d < q.length; d++) norm += q[d] * q[d];\n const score = norm === 0 || queryNorm === 0 ? 0 : dot / (Math.sqrt(norm) * queryNorm);\n const existing = best.get(row.path);\n if (!existing || score > existing.score || (score === existing.score && row.chunk < existing.chunk)) {\n best.set(row.path, { score, chunk: row.chunk, lines: `L${row.start_line}-${row.end_line}` });\n }\n }\n return [...best.entries()]\n .map(([path, value]) => ({ path, ...value }))\n .sort(compareVectorScores)\n .slice(0, fetch)\n .map(({ path, lines, score }) => ({ path, lines, similarity: asCosine(score) }));\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors.\nexport async function scanSimilar(conn: Connection, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Promise<VectorSimilar[]> {\n const targetStmt = await conn.prepare('SELECT vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk');\n const targetRows = (await targetStmt.all(path)) as Array<{ vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n // Each side's norm divides its own dot product, so both are taken once here rather than per pair.\n const target = sampleEvenly(targetRows).map((row) => {\n const v = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n let norm = 0;\n for (let d = 0; d < v.length; d++) norm += v[d] * v[d];\n return { v, norm: Math.sqrt(norm) };\n });\n\n const stmt = await conn.prepare('SELECT \"path\", vector FROM embeddings WHERE vector IS NOT NULL');\n const rows = (await stmt.all()) as Array<{ path: string; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n let otherNorm = 0;\n for (let d = 0; d < other.length; d++) otherNorm += other[d] * other[d];\n otherNorm = Math.sqrt(otherNorm);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = t.norm === 0 || otherNorm === 0 ? 0 : dot / (t.norm * otherNorm);\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .map(([p, score]) => ({ path: p, score }))\n .sort(compareVectorScores)\n .slice(0, opts.k)\n .map(({ path: p, score }) => ({ path: p, similarity: asCosine(score) }));\n}\n"],"names":["asCosine","compareVectorScores","sampleEvenly","writeVectorBatch","conn","rows","length","runBatch","map","row","scale","vector","path","chunk","scanCandidates","qv","storeDims","fetch","allowed","stmt","prepare","all","queryNorm","d","Math","min","sqrt","best","Map","has","q","Int8Array","buffer","byteOffset","byteLength","dot","norm","score","existing","get","set","lines","start_line","end_line","entries","value","sort","slice","similarity","scanSimilar","opts","targetStmt","targetRows","target","v","exclude","other","otherNorm","t","len","undefined","p","k"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/vectors.ts"],"sourcesContent":["import type { Connection, VectorCandidate, VectorSimilar, VectorWriteRow } from '../types.ts';\nimport { asCosine, compareVectorScores, NATIVE_VECTOR_SCOPE_TABLE, sampleEvenly } from '../vectors.ts';\n\n// One runBatch call per provider batch: the caller (embed/query.ts) already batches by the\n// provider's batchCap, so this is one crossing per batch, never per row.\nexport async function writeVectorBatch(conn: Connection, rows: VectorWriteRow[]): Promise<void> {\n if (rows.length === 0) return;\n await conn.runBatch(\n 'UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?',\n rows.map((row) => [row.scale, row.vector, row.path, row.chunk])\n );\n}\n\n// Best chunk per file by cosine, its line range riding along: the JS loop over stored int8\n// BLOBs this store's scan strategy is (a native store scans differently, same contract).\nexport async function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>, nativeScope = false): Promise<VectorCandidate[]> {\n const scope = nativeScope ? ` AND \"path\" IN (SELECT \"path\" FROM ${NATIVE_VECTOR_SCOPE_TABLE})` : '';\n const stmt = await conn.prepare(`SELECT \"path\", chunk, start_line, end_line, vector FROM embeddings WHERE vector IS NOT NULL${scope} ORDER BY \"path\", chunk`);\n const rows = (await stmt.all()) as Array<{\n path: string;\n chunk: number;\n start_line: number;\n end_line: number;\n vector: Uint8Array;\n }>;\n\n let queryNorm = 0;\n for (let d = 0; d < Math.min(storeDims, qv.length); d++) queryNorm += qv[d] * qv[d];\n queryNorm = Math.sqrt(queryNorm);\n\n const best = new Map<string, { score: number; chunk: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n // Rounding to int8 costs the stored row its unit norm, so dividing by that norm is what makes\n // this a cosine rather than a scaled dot product. `scale` cancels out of the ratio.\n let dot = 0;\n let norm = 0;\n const length = Math.min(q.length, qv.length);\n for (let d = 0; d < length; d++) {\n dot += q[d] * qv[d];\n norm += q[d] * q[d];\n }\n for (let d = length; d < q.length; d++) norm += q[d] * q[d];\n const score = norm === 0 || queryNorm === 0 ? 0 : dot / (Math.sqrt(norm) * queryNorm);\n const existing = best.get(row.path);\n if (!existing || score > existing.score || (score === existing.score && row.chunk < existing.chunk)) {\n best.set(row.path, { score, chunk: row.chunk, lines: `L${row.start_line}-${row.end_line}` });\n }\n }\n return [...best.entries()]\n .map(([path, value]) => ({ path, ...value }))\n .sort(compareVectorScores)\n .slice(0, fetch)\n .map(({ path, lines, score }) => ({ path, lines, similarity: asCosine(score) }));\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors.\nexport async function scanSimilar(conn: Connection, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }, nativeScope = false): Promise<VectorSimilar[]> {\n const targetStmt = await conn.prepare('SELECT vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk');\n const targetRows = (await targetStmt.all(path)) as Array<{ vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n // Each side's norm divides its own dot product, so both are taken once here rather than per pair.\n const target = sampleEvenly(targetRows).map((row) => {\n const v = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n let norm = 0;\n for (let d = 0; d < v.length; d++) norm += v[d] * v[d];\n return { v, norm: Math.sqrt(norm) };\n });\n\n const scope = nativeScope ? ` AND \"path\" IN (SELECT \"path\" FROM ${NATIVE_VECTOR_SCOPE_TABLE})` : '';\n const stmt = await conn.prepare(`SELECT \"path\", vector FROM embeddings WHERE vector IS NOT NULL${scope}`);\n const rows = (await stmt.all()) as Array<{ path: string; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n let otherNorm = 0;\n for (let d = 0; d < other.length; d++) otherNorm += other[d] * other[d];\n otherNorm = Math.sqrt(otherNorm);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = t.norm === 0 || otherNorm === 0 ? 0 : dot / (t.norm * otherNorm);\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .map(([p, score]) => ({ path: p, score }))\n .sort(compareVectorScores)\n .slice(0, opts.k)\n .map(({ path: p, score }) => ({ path: p, similarity: asCosine(score) }));\n}\n"],"names":["asCosine","compareVectorScores","NATIVE_VECTOR_SCOPE_TABLE","sampleEvenly","writeVectorBatch","conn","rows","length","runBatch","map","row","scale","vector","path","chunk","scanCandidates","qv","storeDims","fetch","allowed","nativeScope","scope","stmt","prepare","all","queryNorm","d","Math","min","sqrt","best","Map","has","q","Int8Array","buffer","byteOffset","byteLength","dot","norm","score","existing","get","set","lines","start_line","end_line","entries","value","sort","slice","similarity","scanSimilar","opts","targetStmt","targetRows","target","v","exclude","other","otherNorm","t","len","undefined","p","k"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/stages.ts"],"sourcesContent":["// Per-stage wall time for one reconcile, all on this thread's clock. Stages name work a build has\n// to do, never the function or file doing it, so the vocabulary outlives a pipeline refactor.\nexport const STAGES_VERSION = 's1';\n\n// Every stage outside the feature hooks, in execution order. Feature hooks add\n// `feature:<name>:<hook>` keys, so a new feature becomes visible without touching this list.\nexport const FIXED_STAGES = ['list', 'existing', 'parse', 'alter', 'added-recheck', 'fm-upsert', 'text-index', 'presets', 'vanished', 'meta'] as const;\n\nexport const FEATURE_HOOKS = ['remove', 'store', 'after'] as const;\n\nexport type FeatureHook = (typeof FEATURE_HOOKS)[number];\n\nexport interface Stages {\n version: string;\n totalMs: number;\n // The write transaction's own duration. It contains most of the spans below, so it is reported\n // beside them rather than among them, and never counts toward the sum.\n txMs: number;\n // Summed worker-side parseFile time inside `parse`:
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/stages.ts"],"sourcesContent":["// Per-stage wall time for one reconcile, all on this thread's clock. Stages name work a build has\n// to do, never the function or file doing it, so the vocabulary outlives a pipeline refactor.\nexport const STAGES_VERSION = 's1';\n\n// Every stage outside the feature hooks, in execution order. Feature hooks add\n// `feature:<name>:<hook>` keys, so a new feature becomes visible without touching this list.\nexport const FIXED_STAGES = ['list', 'existing', 'parse', 'alter', 'added-recheck', 'fm-upsert', 'text-index', 'presets', 'vanished', 'meta'] as const;\n\nexport const FEATURE_HOOKS = ['remove', 'store', 'after'] as const;\n\nexport type FeatureHook = (typeof FEATURE_HOOKS)[number];\n\nexport interface Stages {\n version: string;\n totalMs: number;\n // The write transaction's own duration. It contains most of the spans below, so it is reported\n // beside them rather than among them, and never counts toward the sum.\n txMs: number;\n // Summed worker-side parseFile elapsed time inside `parse`: concurrent workers can make it\n // exceed the wall span, so it sits beside the spans like txMs and never counts toward the sum.\n parseWorkerMs: number;\n // Disjoint spans, so `totalMs` less their sum is time no stage claims.\n spans: Record<string, number>;\n}\n\nexport interface StageRecorder {\n time<T>(stage: string, fn: () => T | Promise<T>): Promise<T>;\n take(totalMs: number, txMs: number, parseWorkerMs?: number): Stages;\n}\n\nexport function featureStage(name: string, hook: FeatureHook): string {\n return `feature:${name}:${hook}`;\n}\n\nconst round = (ms: number): number => Math.round(ms * 10) / 10;\n\n// `featureNames` seeds this build's feature-hook keys, so the key set is a function of config\n// alone: a cold build (nothing to remove) and an incremental one report the same stages.\nexport function stageRecorder(featureNames: readonly string[] = []): StageRecorder {\n const spans = new Map<string, number>();\n for (const stage of FIXED_STAGES) spans.set(stage, 0);\n for (const name of featureNames) for (const hook of FEATURE_HOOKS) spans.set(featureStage(name, hook), 0);\n return {\n // A throw records nothing: an aborted reconcile must not report a partial stage as a complete one.\n async time(stage, fn) {\n const at = process.hrtime.bigint();\n const value = await fn();\n spans.set(stage, (spans.get(stage) ?? 0) + Number(process.hrtime.bigint() - at) / 1e6);\n return value;\n },\n // Every declared stage reports, 0 included, so a store that skips some work and a store that\n // does it cheaply read alike across a timeline. An absent `stages` is a build with no timers.\n take(totalMs, txMs, parseWorkerMs = 0) {\n const out: Record<string, number> = {};\n for (const [stage, ms] of spans) out[stage] = round(ms);\n return { version: STAGES_VERSION, totalMs: round(totalMs), txMs: round(txMs), parseWorkerMs: round(parseWorkerMs), spans: out };\n },\n };\n}\n\n// Time no stage claims: transaction commit, lock wait, and any cost added since without a timer.\n// A residual that grows across releases is the signal that this vocabulary stopped covering the build.\nexport function unaccountedMs(stages: Stages): number {\n let sum = 0;\n for (const ms of Object.values(stages.spans)) sum += ms;\n return round(stages.totalMs - sum);\n}\n"],"names":["STAGES_VERSION","FIXED_STAGES","FEATURE_HOOKS","featureStage","name","hook","round","ms","Math","stageRecorder","featureNames","spans","Map","stage","set","time","fn","at","process","hrtime","bigint","value","get","Number","take","totalMs","txMs","parseWorkerMs","out","version","unaccountedMs","stages","sum","Object","values"],"mappings":"AAAA,kGAAkG;AAClG,8FAA8F;AAC9F,OAAO,MAAMA,iBAAiB,KAAK;AAEnC,+EAA+E;AAC/E,6FAA6F;AAC7F,OAAO,MAAMC,eAAe;IAAC;IAAQ;IAAY;IAAS;IAAS;IAAiB;IAAa;IAAc;IAAW;IAAY;CAAO,CAAU;AAEvJ,OAAO,MAAMC,gBAAgB;IAAC;IAAU;IAAS;CAAQ,CAAU;AAsBnE,OAAO,SAASC,aAAaC,IAAY,EAAEC,IAAiB;IAC1D,OAAO,CAAC,QAAQ,EAAED,KAAK,CAAC,EAAEC,MAAM;AAClC;AAEA,MAAMC,QAAQ,CAACC,KAAuBC,KAAKF,KAAK,CAACC,KAAK,MAAM;AAE5D,8FAA8F;AAC9F,yFAAyF;AACzF,OAAO,SAASE,cAAcC,eAAkC,EAAE;IAChE,MAAMC,QAAQ,IAAIC;IAClB,KAAK,MAAMC,SAASZ,aAAcU,MAAMG,GAAG,CAACD,OAAO;IACnD,KAAK,MAAMT,QAAQM,aAAc,KAAK,MAAML,QAAQH,cAAeS,MAAMG,GAAG,CAACX,aAAaC,MAAMC,OAAO;IACvG,OAAO;QACL,mGAAmG;QACnG,MAAMU,MAAKF,KAAK,EAAEG,EAAE;gBAGAL;YAFlB,MAAMM,KAAKC,QAAQC,MAAM,CAACC,MAAM;YAChC,MAAMC,QAAQ,MAAML;YACpBL,MAAMG,GAAG,CAACD,OAAO,EAACF,aAAAA,MAAMW,GAAG,CAACT,oBAAVF,wBAAAA,aAAoB,KAAKY,OAAOL,QAAQC,MAAM,CAACC,MAAM,KAAKH,MAAM;YAClF,OAAOI;QACT;QACA,6FAA6F;QAC7F,8FAA8F;QAC9FG,MAAKC,OAAO,EAAEC,IAAI,EAAEC,gBAAgB,CAAC;YACnC,MAAMC,MAA8B,CAAC;YACrC,KAAK,MAAM,CAACf,OAAON,GAAG,IAAII,MAAOiB,GAAG,CAACf,MAAM,GAAGP,MAAMC;YACpD,OAAO;gBAAEsB,SAAS7B;gBAAgByB,SAASnB,MAAMmB;gBAAUC,MAAMpB,MAAMoB;gBAAOC,eAAerB,MAAMqB;gBAAgBhB,OAAOiB;YAAI;QAChI;IACF;AACF;AAEA,iGAAiG;AACjG,uGAAuG;AACvG,OAAO,SAASE,cAAcC,MAAc;IAC1C,IAAIC,MAAM;IACV,KAAK,MAAMzB,MAAM0B,OAAOC,MAAM,CAACH,OAAOpB,KAAK,EAAGqB,OAAOzB;IACrD,OAAOD,MAAMyB,OAAON,OAAO,GAAGO;AAChC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Database } from '@tursodatabase/database';
|
|
2
2
|
import type { Connection } from '../types.js';
|
|
3
|
+
export declare const INSERT_BIND_BUDGET = 8192;
|
|
3
4
|
export declare function checkpointWal(db: Database): Promise<void>;
|
|
4
5
|
export declare function cacheFilePath(conn: Connection): Promise<string | null>;
|
|
5
6
|
export declare function fileSize(path: string): Promise<number | null>;
|
|
@@ -1,32 +1,90 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { setImmediate } from 'node:timers/promises';
|
|
2
3
|
import { rewriteInsert } from '../batch.js';
|
|
3
4
|
import { setMeta } from '../shared.js';
|
|
4
5
|
import { BEGIN_WRITE, withTransaction } from '../transaction.js';
|
|
5
6
|
import { CONNECT_OPTS, tursoApi } from './native.js';
|
|
6
7
|
import { rewriteFunctions } from './sql-functions.js';
|
|
7
|
-
//
|
|
8
|
-
|
|
8
|
+
// Bound native multi-row compilation work; this is not Turso's variable-count limit.
|
|
9
|
+
export const INSERT_BIND_BUDGET = 8192;
|
|
10
|
+
async function closeStatement(stmt, failed, failure) {
|
|
11
|
+
try {
|
|
12
|
+
await stmt.close();
|
|
13
|
+
} catch (cleanup) {
|
|
14
|
+
if (failed) throw new AggregateError([
|
|
15
|
+
failure,
|
|
16
|
+
cleanup
|
|
17
|
+
], 'Turso statement execution and cleanup failed');
|
|
18
|
+
throw cleanup;
|
|
19
|
+
} finally{
|
|
20
|
+
// Native result cleanup can wait on the event loop even after the statement is finalized.
|
|
21
|
+
await setImmediate();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Native statements are finalized after execution. Reusing the portable statement prepares
|
|
25
|
+
// it again; column metadata remains available after execution without retaining native state.
|
|
9
26
|
let TursoStatementWrapper = class TursoStatementWrapper {
|
|
27
|
+
async acquire() {
|
|
28
|
+
const retained = this.stmt;
|
|
29
|
+
this.stmt = null;
|
|
30
|
+
const stmt = retained !== null && retained !== void 0 ? retained : await this.db.prepare(this.sql);
|
|
31
|
+
try {
|
|
32
|
+
stmt.safeIntegers(this.readBigInts);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
await closeStatement(stmt, true, error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return stmt;
|
|
38
|
+
}
|
|
39
|
+
async execute(operation) {
|
|
40
|
+
const stmt = await this.acquire();
|
|
41
|
+
let failed = false;
|
|
42
|
+
let failure;
|
|
43
|
+
try {
|
|
44
|
+
return await operation(stmt);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
failed = true;
|
|
47
|
+
failure = error;
|
|
48
|
+
throw error;
|
|
49
|
+
} finally{
|
|
50
|
+
await closeStatement(stmt, failed, failure);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
10
53
|
async run(...params) {
|
|
11
|
-
return this.stmt.run(...params);
|
|
54
|
+
return this.execute((stmt)=>stmt.run(...params));
|
|
12
55
|
}
|
|
13
56
|
async get(...params) {
|
|
14
|
-
return this.stmt.get(...params);
|
|
57
|
+
return this.execute((stmt)=>stmt.get(...params));
|
|
15
58
|
}
|
|
16
59
|
async all(...params) {
|
|
17
|
-
return this.stmt.all(...params);
|
|
60
|
+
return this.execute((stmt)=>stmt.all(...params));
|
|
18
61
|
}
|
|
19
62
|
async *iterate(...params) {
|
|
20
|
-
|
|
63
|
+
const stmt = await this.acquire();
|
|
64
|
+
let failed = false;
|
|
65
|
+
let failure;
|
|
66
|
+
try {
|
|
67
|
+
yield* stmt.iterate(...params);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
failed = true;
|
|
70
|
+
failure = error;
|
|
71
|
+
throw error;
|
|
72
|
+
} finally{
|
|
73
|
+
await closeStatement(stmt, failed, failure);
|
|
74
|
+
}
|
|
21
75
|
}
|
|
22
76
|
columns() {
|
|
23
|
-
return this.
|
|
77
|
+
return this.metadata;
|
|
24
78
|
}
|
|
25
79
|
setReadBigInts(enabled) {
|
|
26
|
-
this.
|
|
80
|
+
this.readBigInts = enabled;
|
|
27
81
|
}
|
|
28
|
-
constructor(stmt){
|
|
82
|
+
constructor(db, sql, stmt){
|
|
83
|
+
this.readBigInts = false;
|
|
84
|
+
this.db = db;
|
|
85
|
+
this.sql = sql;
|
|
29
86
|
this.stmt = stmt;
|
|
87
|
+
this.metadata = stmt.columns();
|
|
30
88
|
}
|
|
31
89
|
};
|
|
32
90
|
// This client leaves the WAL behind for the next opener, where node:sqlite checkpoints on the last
|
|
@@ -83,32 +141,43 @@ export function createConnection(db) {
|
|
|
83
141
|
await db.exec(sql);
|
|
84
142
|
},
|
|
85
143
|
async prepare (sql) {
|
|
86
|
-
|
|
144
|
+
const rewritten = rewriteFunctions(sql);
|
|
145
|
+
const stmt = await db.prepare(rewritten);
|
|
146
|
+
try {
|
|
147
|
+
return new TursoStatementWrapper(db, rewritten, stmt);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
await closeStatement(stmt, true, error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
87
152
|
},
|
|
88
|
-
//
|
|
89
|
-
//
|
|
153
|
+
// Fold INSERTs into bounded multi-row statements in one transaction. UPDATE/DELETE keep
|
|
154
|
+
// the per-row loop; each native statement owns its finalization and cleanup turn.
|
|
90
155
|
async runBatch (sql, paramRows) {
|
|
91
156
|
if (paramRows.length === 0) return;
|
|
92
157
|
await withTransaction(conn, async ()=>{
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
158
|
+
const shape = rewriteInsert(sql, 1);
|
|
159
|
+
const batchRows = shape ? Math.max(1, Math.floor(INSERT_BIND_BUDGET / shape.width)) : paramRows.length;
|
|
160
|
+
for(let offset = 0; offset < paramRows.length; offset += batchRows){
|
|
161
|
+
var _ref;
|
|
162
|
+
const rows = shape ? paramRows.slice(offset, offset + batchRows) : paramRows;
|
|
163
|
+
const rewritten = shape ? rewriteInsert(sql, rows.length) : null;
|
|
164
|
+
const stmt = await db.prepare((_ref = rewritten === null || rewritten === void 0 ? void 0 : rewritten.sql) !== null && _ref !== void 0 ? _ref : sql);
|
|
165
|
+
let failed = false;
|
|
166
|
+
let failure;
|
|
96
167
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
168
|
+
if (rewritten) {
|
|
169
|
+
// Pass one array: spreading a wide tuple can exceed the JS argument limit.
|
|
170
|
+
await stmt.run(rows.flat());
|
|
171
|
+
} else {
|
|
172
|
+
for (const row of rows)await stmt.run(row);
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
failed = true;
|
|
176
|
+
failure = error;
|
|
177
|
+
throw error;
|
|
100
178
|
} finally{
|
|
101
|
-
await stmt
|
|
179
|
+
await closeStatement(stmt, failed, failure);
|
|
102
180
|
}
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
// Finalized here because nothing else will: open() hands back a connection the caller
|
|
106
|
-
// can hold across many batches, and nothing else owns this statement's lifetime.
|
|
107
|
-
const stmt = await db.prepare(sql);
|
|
108
|
-
try {
|
|
109
|
-
for (const row of paramRows)await stmt.run(row);
|
|
110
|
-
} finally{
|
|
111
|
-
await stmt.close();
|
|
112
181
|
}
|
|
113
182
|
}, BEGIN_WRITE);
|
|
114
183
|
}
|
|
@@ -1 +1 @@
|
|
|
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';\nimport { rewriteFunctions } from './sql-functions.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(rewriteFunctions(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 nothing else owns this statement's lifetime.\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","rewriteFunctions","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
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/connection.ts"],"sourcesContent":["import { stat } from 'node:fs/promises';\nimport { setImmediate } from 'node:timers/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';\nimport { rewriteFunctions } from './sql-functions.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// Bound native multi-row compilation work; this is not Turso's variable-count limit.\nexport const INSERT_BIND_BUDGET = 8192;\n\nasync function closeStatement(stmt: TursoStatement, failed: boolean, failure: unknown): Promise<void> {\n try {\n await stmt.close();\n } catch (cleanup) {\n if (failed) throw new AggregateError([failure, cleanup], 'Turso statement execution and cleanup failed');\n throw cleanup;\n } finally {\n // Native result cleanup can wait on the event loop even after the statement is finalized.\n await setImmediate();\n }\n}\n\n// Native statements are finalized after execution. Reusing the portable statement prepares\n// it again; column metadata remains available after execution without retaining native state.\nclass TursoStatementWrapper implements Statement {\n private stmt: TursoStatement | null;\n private readonly metadata: Array<{ name: string }>;\n private readBigInts = false;\n private readonly db: Database;\n private readonly sql: string;\n\n constructor(db: Database, sql: string, stmt: TursoStatement) {\n this.db = db;\n this.sql = sql;\n this.stmt = stmt;\n this.metadata = stmt.columns();\n }\n\n private async acquire(): Promise<TursoStatement> {\n const retained = this.stmt;\n this.stmt = null;\n const stmt = retained ?? (await this.db.prepare(this.sql));\n try {\n stmt.safeIntegers(this.readBigInts);\n } catch (error) {\n await closeStatement(stmt, true, error);\n throw error;\n }\n return stmt;\n }\n\n private async execute<T>(operation: (stmt: TursoStatement) => Promise<T>): Promise<T> {\n const stmt = await this.acquire();\n let failed = false;\n let failure: unknown;\n try {\n return await operation(stmt);\n } catch (error) {\n failed = true;\n failure = error;\n throw error;\n } finally {\n await closeStatement(stmt, failed, failure);\n }\n }\n\n async run(...params: unknown[]): Promise<RunResult> {\n return this.execute((stmt) => stmt.run(...params));\n }\n\n async get(...params: unknown[]): Promise<unknown> {\n return this.execute((stmt) => stmt.get(...params));\n }\n\n async all(...params: unknown[]): Promise<unknown[]> {\n return this.execute((stmt) => stmt.all(...params));\n }\n\n async *iterate(...params: unknown[]): AsyncIterable<unknown> {\n const stmt = await this.acquire();\n let failed = false;\n let failure: unknown;\n try {\n yield* stmt.iterate(...params);\n } catch (error) {\n failed = true;\n failure = error;\n throw error;\n } finally {\n await closeStatement(stmt, failed, failure);\n }\n }\n\n columns(): Array<{ name: string }> {\n return this.metadata;\n }\n\n setReadBigInts(enabled: boolean): void {\n this.readBigInts = 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 const rewritten = rewriteFunctions(sql);\n const stmt = await db.prepare(rewritten);\n try {\n return new TursoStatementWrapper(db, rewritten, stmt);\n } catch (error) {\n await closeStatement(stmt, true, error);\n throw error;\n }\n },\n // Fold INSERTs into bounded multi-row statements in one transaction. UPDATE/DELETE keep\n // the per-row loop; each native statement owns its finalization and cleanup turn.\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n if (paramRows.length === 0) return;\n await withTransaction(\n conn,\n async () => {\n const shape = rewriteInsert(sql, 1);\n const batchRows = shape ? Math.max(1, Math.floor(INSERT_BIND_BUDGET / shape.width)) : paramRows.length;\n for (let offset = 0; offset < paramRows.length; offset += batchRows) {\n const rows = shape ? paramRows.slice(offset, offset + batchRows) : paramRows;\n const rewritten = shape ? rewriteInsert(sql, rows.length) : null;\n const stmt = await db.prepare(rewritten?.sql ?? sql);\n let failed = false;\n let failure: unknown;\n try {\n if (rewritten) {\n // Pass one array: spreading a wide tuple can exceed the JS argument limit.\n await stmt.run(rows.flat());\n } else {\n for (const row of rows) await stmt.run(row);\n }\n } catch (error) {\n failed = true;\n failure = error;\n throw error;\n } finally {\n await closeStatement(stmt, failed, failure);\n }\n }\n },\n BEGIN_WRITE\n );\n },\n };\n return conn;\n}\n"],"names":["stat","setImmediate","rewriteInsert","setMeta","BEGIN_WRITE","withTransaction","CONNECT_OPTS","tursoApi","rewriteFunctions","INSERT_BIND_BUDGET","closeStatement","stmt","failed","failure","close","cleanup","AggregateError","TursoStatementWrapper","acquire","retained","db","prepare","sql","safeIntegers","readBigInts","error","execute","operation","run","params","get","all","iterate","columns","metadata","setReadBigInts","enabled","checkpointWal","exec","err","console","message","cacheFilePath","conn","rows","find","r","name","file","fileSize","path","size","reclaimSpace","turso","connect","experimental","createConnection","String","rewritten","runBatch","paramRows","length","shape","batchRows","Math","max","floor","width","offset","slice","flat","row"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import type { ResolvedConfig } from '../../config/index.js';
|
|
|
3
3
|
import type { InternalOpenOptions, OpenResult } from '../open.js';
|
|
4
4
|
import type { OpenDialect } from '../types.js';
|
|
5
5
|
export declare const DB_FILENAME = "cache.turso.db";
|
|
6
|
-
export declare const SCHEMA_VERSION = "
|
|
6
|
+
export declare const SCHEMA_VERSION = "10";
|
|
7
7
|
export type { OpenResult };
|
|
8
8
|
export declare const tursoOpenDialect: OpenDialect<Database>;
|
|
9
9
|
export declare function openTurso(cfg: ResolvedConfig, options?: InternalOpenOptions): Promise<OpenResult>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { featureSignature } from '../../config/index.js';
|
|
2
1
|
import { STORE_DIMS } from '../../embed/types.js';
|
|
3
2
|
import { SenseError } from '../../errors.js';
|
|
4
|
-
import { activeFeatures
|
|
3
|
+
import { activeFeatures } from '../../features/index.js';
|
|
5
4
|
import { openWithDialect } from '../open.js';
|
|
6
5
|
import { getMeta, setMeta } from '../shared.js';
|
|
7
6
|
import { checkpointWal, createConnection } from './connection.js';
|
|
@@ -11,27 +10,28 @@ import { createStore } from './store.js';
|
|
|
11
10
|
export const DB_FILENAME = 'cache.turso.db';
|
|
12
11
|
// Independent of sqlite's and duckdb's SCHEMA_VERSION: each store's cache shape evolves
|
|
13
12
|
// separately. Covers the FTS indexes, the "_ngram" sidecar columns, and embeddings.vector's width.
|
|
14
|
-
export const SCHEMA_VERSION = '
|
|
13
|
+
export const SCHEMA_VERSION = '10';
|
|
15
14
|
// This store's Handle (types.ts's OpenDialect<Handle>) is the connected Database itself: no
|
|
16
15
|
// extra native state to thread, unlike duckdb's separate instance/connection pair.
|
|
17
|
-
async function
|
|
16
|
+
async function ensureCoreSchema(_handle, conn) {
|
|
18
17
|
await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter ("path" TEXT PRIMARY KEY, "_mtime" REAL, "_ctime" REAL, "_size" INTEGER, "_parse_error" TEXT)`);
|
|
19
18
|
await conn.exec(`CREATE TABLE IF NOT EXISTS content ("path" TEXT PRIMARY KEY, title TEXT, summary TEXT, text TEXT, title_stem TEXT, summary_stem TEXT, text_stem TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);
|
|
20
19
|
await conn.exec(`CREATE TABLE IF NOT EXISTS indexed_sources ("path" TEXT PRIMARY KEY, text TEXT NOT NULL)`);
|
|
21
20
|
for (const ddl of CONTENT_FTS_DDL)await conn.exec(ddl);
|
|
22
21
|
await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files ("path" TEXT, preset TEXT, PRIMARY KEY ("path", preset))`);
|
|
23
22
|
await conn.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');
|
|
23
|
+
if (await getMeta(conn, 'schema_version') === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);
|
|
24
|
+
}
|
|
25
|
+
async function ensureFeatureSchema(_handle, conn, cfg) {
|
|
24
26
|
for (const feature of activeFeatures(cfg)){
|
|
25
27
|
// Native F32_BLOB(STORE_DIMS) instead of the embed feature's engine-neutral BLOB DDL. `scale`
|
|
26
28
|
// is kept unused, so the shared reconcile-time INSERT/DELETE names a column both stores have.
|
|
27
29
|
if (feature.name === 'embed') {
|
|
28
|
-
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))`);
|
|
30
|
+
await conn.exec(`CREATE TABLE IF NOT EXISTS embeddings ("path" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, content_identity TEXT NOT NULL, scale REAL, vector F32_BLOB(${STORE_DIMS}), PRIMARY KEY ("path", chunk))`);
|
|
29
31
|
continue;
|
|
30
32
|
}
|
|
31
33
|
await feature.schema(conn);
|
|
32
34
|
}
|
|
33
|
-
if (await getMeta(conn, 'schema_version') === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);
|
|
34
|
-
if (await getMeta(conn, 'features') === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));
|
|
35
35
|
}
|
|
36
36
|
async function close(handle, options = {}) {
|
|
37
37
|
if (!options.observational) await checkpointWal(handle);
|
|
@@ -82,7 +82,8 @@ export const tursoOpenDialect = {
|
|
|
82
82
|
// process", Windows "another process has locked a portion of the file (os error 33)". Distinct from
|
|
83
83
|
// the write-time "database is locked" its connect-time `timeout` covers; that one never reaches here.
|
|
84
84
|
isLocked: (err)=>/File is locked by another process|locked a portion of the file/.test(err.message),
|
|
85
|
-
|
|
85
|
+
ensureCoreSchema,
|
|
86
|
+
ensureFeatureSchema,
|
|
86
87
|
setDerivedBusyTimeout,
|
|
87
88
|
createStore: (handle, conn, _cfg, options)=>createStore(handle, conn, options)
|
|
88
89
|
};
|
|
@@ -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 {
|
|
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 { STORE_DIMS } from '../../embed/types.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } 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 { 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 = '10';\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 ensureCoreSchema(_handle: Database, conn: Connection): 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_stem TEXT, summary_stem TEXT, text_stem TEXT, title_ngram TEXT, summary_ngram TEXT, text_ngram TEXT)`);\n await conn.exec(`CREATE TABLE IF NOT EXISTS indexed_sources (\"path\" TEXT PRIMARY KEY, text TEXT NOT NULL)`);\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 if ((await getMeta(conn, 'schema_version')) === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);\n}\n\nasync function ensureFeatureSchema(_handle: Database, conn: Connection, cfg: Config): Promise<void> {\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, content_identity TEXT NOT NULL, scale REAL, vector F32_BLOB(${STORE_DIMS}), PRIMARY KEY (\"path\", chunk))`);\n continue;\n }\n await feature.schema(conn);\n }\n}\n\nasync function close(handle: Database, options: { observational?: boolean } = {}): Promise<void> {\n if (!options.observational) 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, options: { existingOnly: boolean; observational: boolean }): 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 // Turso's readonly mode rejects native FTS SELECTs and TEMP query tables. Existing-only opens\n // stay query-capable; shared no-build orchestration and observational close prevent upkeep writes.\n db = await turso.connect(dbPath, { ...CONNECT_OPTS, experimental: [...CONNECT_OPTS.experimental], ...(options.existingOnly ? { fileMustExist: true } : {}) });\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 ensureCoreSchema,\n ensureFeatureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn, _cfg, options) => createStore(handle, conn, options),\n};\n\nexport async function openTurso(cfg: ResolvedConfig, options?: InternalOpenOptions): Promise<OpenResult> {\n return openWithDialect(cfg, tursoOpenDialect, options);\n}\n"],"names":["STORE_DIMS","SenseError","activeFeatures","openWithDialect","getMeta","setMeta","checkpointWal","createConnection","CONNECT_OPTS","TURSO_PACKAGE","tursoApi","CONTENT_FTS_DDL","tursoDialect","createStore","DB_FILENAME","SCHEMA_VERSION","ensureCoreSchema","_handle","conn","exec","ddl","ensureFeatureSchema","cfg","feature","name","schema","close","handle","options","observational","setDerivedBusyTimeout","ms","connect","dbPath","_cfg","turso","err","message","db","experimental","existingOnly","fileMustExist","tursoOpenDialect","filename","schemaVersion","reconcileDialect","isLocked","test","openTurso"],"mappings":"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"}
|