sensemaking 0.27.0 → 0.27.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.
- package/dist/cjs/chunk/extract.js.map +1 -1
- package/dist/cjs/chunk/group.js.map +1 -1
- package/dist/cjs/chunk/index.js.map +1 -1
- package/dist/cjs/chunk/parse.js.map +1 -1
- package/dist/cjs/chunk/parser.js.map +1 -1
- package/dist/cjs/chunk/tokens.js.map +1 -1
- package/dist/cjs/chunk/version.js.map +1 -1
- package/dist/cjs/cli/build.js.map +1 -1
- package/dist/cjs/cli/download.js.map +1 -1
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/cli/watch.js.map +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/index.js.map +1 -1
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/path.js.map +1 -1
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.js.map +1 -1
- package/dist/cjs/commands/snapshot.js.map +1 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/resolve.js.map +1 -1
- package/dist/cjs/config/signals.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/embed/cohere.js.map +1 -1
- package/dist/cjs/embed/distribution.js.map +1 -1
- package/dist/cjs/embed/endpoint.js.map +1 -1
- package/dist/cjs/embed/handoff.js.map +1 -1
- package/dist/cjs/embed/http.js.map +1 -1
- package/dist/cjs/embed/identity.js.map +1 -1
- package/dist/cjs/embed/langfit.js.map +1 -1
- package/dist/cjs/embed/languages.js.map +1 -1
- package/dist/cjs/embed/openai.js.map +1 -1
- package/dist/cjs/embed/query.js.map +1 -1
- package/dist/cjs/embed/registry.js.map +1 -1
- package/dist/cjs/embed/static.js.map +1 -1
- package/dist/cjs/embed/store.js.map +1 -1
- package/dist/cjs/embed/types.js.map +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/fences.js.map +1 -1
- package/dist/cjs/features/index.js.map +1 -1
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js.map +1 -1
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/graph/graph.js.map +1 -1
- package/dist/cjs/graph/traverse.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/atomic-write.js.map +1 -1
- package/dist/cjs/lib/guarded-tick.js.map +1 -1
- package/dist/cjs/lib/serial-query.js.map +1 -1
- package/dist/cjs/lib/watch-notification.js.map +1 -1
- package/dist/cjs/lib/worker-file.js.map +1 -1
- package/dist/cjs/output/column-hint.js.map +1 -1
- package/dist/cjs/output/output.js.map +1 -1
- package/dist/cjs/output/progress.js.map +1 -1
- package/dist/cjs/output/search-error.js.map +1 -1
- package/dist/cjs/scan/frontmatter.js.map +1 -1
- package/dist/cjs/scan/index.js.map +1 -1
- package/dist/cjs/scan/list.js.map +1 -1
- package/dist/cjs/scan/pool.js.map +1 -1
- package/dist/cjs/scan/reparse.js.map +1 -1
- package/dist/cjs/scan/worker-error.js.map +1 -1
- package/dist/cjs/store/batch.js.map +1 -1
- package/dist/cjs/store/builder.js.map +1 -1
- package/dist/cjs/store/cache.js.map +1 -1
- package/dist/cjs/store/duckdb/batch.js.map +1 -1
- package/dist/cjs/store/duckdb/connection.js.map +1 -1
- package/dist/cjs/store/duckdb/fieldStats.js.map +1 -1
- package/dist/cjs/store/duckdb/lexical.d.cts +1 -6
- package/dist/cjs/store/duckdb/lexical.d.ts +1 -6
- package/dist/cjs/store/duckdb/lexical.js +28 -39
- package/dist/cjs/store/duckdb/lexical.js.map +1 -1
- package/dist/cjs/store/duckdb/native.d.cts +1 -2
- package/dist/cjs/store/duckdb/native.d.ts +1 -2
- package/dist/cjs/store/duckdb/native.js +1 -6
- package/dist/cjs/store/duckdb/native.js.map +1 -1
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/ordered-bm25.d.cts +3 -16
- package/dist/cjs/store/duckdb/ordered-bm25.d.ts +3 -16
- package/dist/cjs/store/duckdb/ordered-bm25.js +27 -79
- package/dist/cjs/store/duckdb/ordered-bm25.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/duckdb/sql-functions.js.map +1 -1
- package/dist/cjs/store/duckdb/store.js.map +1 -1
- package/dist/cjs/store/duckdb/vectors.js.map +1 -1
- package/dist/cjs/store/embed-scope.js.map +1 -1
- package/dist/cjs/store/feature-scope.js.map +1 -1
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/lock-wait.js.map +1 -1
- package/dist/cjs/store/native.js.map +1 -1
- package/dist/cjs/store/open.js.map +1 -1
- package/dist/cjs/store/preset-scope.js.map +1 -1
- package/dist/cjs/store/readiness.js.map +1 -1
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/shared.js.map +1 -1
- package/dist/cjs/store/signature.js.map +1 -1
- package/dist/cjs/store/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/connection.js.map +1 -1
- package/dist/cjs/store/sqlite/fieldStats.js.map +1 -1
- package/dist/cjs/store/sqlite/lexical.js.map +1 -1
- 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/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/store.js.map +1 -1
- package/dist/cjs/store/sqlite/vectors.js.map +1 -1
- package/dist/cjs/store/stages.js.map +1 -1
- package/dist/cjs/store/transaction.js.map +1 -1
- package/dist/cjs/store/turso/connection.js.map +1 -1
- package/dist/cjs/store/turso/fieldStats.js.map +1 -1
- package/dist/cjs/store/turso/lexical-text.js.map +1 -1
- package/dist/cjs/store/turso/lexical.js.map +1 -1
- package/dist/cjs/store/turso/native.js.map +1 -1
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/reconcile.js.map +1 -1
- package/dist/cjs/store/turso/sql-functions.js.map +1 -1
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/store/turso/vectors.js.map +1 -1
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/cjs/store/vectors.js.map +1 -1
- package/dist/cjs/text/segment.js.map +1 -1
- package/dist/cjs/text/strip.js.map +1 -1
- package/dist/cjs/watch-claim.js.map +1 -1
- package/dist/cjs/watch.js.map +1 -1
- package/dist/cjs/workers/parse.js.map +1 -1
- package/dist/cjs/workers/watch-heartbeat.js.map +1 -1
- package/dist/esm/chunk/extract.js.map +1 -1
- package/dist/esm/chunk/group.js.map +1 -1
- package/dist/esm/chunk/index.js.map +1 -1
- package/dist/esm/chunk/parse.js.map +1 -1
- package/dist/esm/chunk/parser.js.map +1 -1
- package/dist/esm/chunk/tokens.js.map +1 -1
- package/dist/esm/chunk/types.js.map +1 -1
- package/dist/esm/chunk/version.js.map +1 -1
- package/dist/esm/cli/build.js.map +1 -1
- package/dist/esm/cli/download.js.map +1 -1
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/sql.js.map +1 -1
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/cli/types.js.map +1 -1
- package/dist/esm/cli/watch.js.map +1 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/index.js.map +1 -1
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/path.js.map +1 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.js.map +1 -1
- package/dist/esm/commands/snapshot.js.map +1 -1
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/resolve.js.map +1 -1
- package/dist/esm/config/signals.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/embed/cohere.js.map +1 -1
- package/dist/esm/embed/distribution.js.map +1 -1
- package/dist/esm/embed/endpoint.js.map +1 -1
- package/dist/esm/embed/handoff.js.map +1 -1
- package/dist/esm/embed/http.js.map +1 -1
- package/dist/esm/embed/identity.js.map +1 -1
- package/dist/esm/embed/langfit.js.map +1 -1
- package/dist/esm/embed/languages.js.map +1 -1
- package/dist/esm/embed/openai.js.map +1 -1
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/embed/registry.js.map +1 -1
- package/dist/esm/embed/static.js.map +1 -1
- package/dist/esm/embed/store.js.map +1 -1
- package/dist/esm/embed/types.js.map +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/features/fences.js.map +1 -1
- package/dist/esm/features/index.js.map +1 -1
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/rank.js.map +1 -1
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/graph/graph.js.map +1 -1
- package/dist/esm/graph/traverse.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/atomic-write.js.map +1 -1
- package/dist/esm/lib/guarded-tick.js.map +1 -1
- package/dist/esm/lib/serial-query.js.map +1 -1
- package/dist/esm/lib/watch-notification.js.map +1 -1
- package/dist/esm/lib/worker-file.js.map +1 -1
- package/dist/esm/output/column-hint.js.map +1 -1
- package/dist/esm/output/output.js.map +1 -1
- package/dist/esm/output/progress.js.map +1 -1
- package/dist/esm/output/search-error.js.map +1 -1
- package/dist/esm/scan/frontmatter.js.map +1 -1
- package/dist/esm/scan/index.js.map +1 -1
- package/dist/esm/scan/list.js.map +1 -1
- package/dist/esm/scan/pool.js.map +1 -1
- package/dist/esm/scan/reparse.js.map +1 -1
- package/dist/esm/scan/worker-error.js.map +1 -1
- package/dist/esm/store/batch.js.map +1 -1
- package/dist/esm/store/builder.js.map +1 -1
- package/dist/esm/store/cache.js.map +1 -1
- package/dist/esm/store/duckdb/batch.js.map +1 -1
- package/dist/esm/store/duckdb/connection.js.map +1 -1
- package/dist/esm/store/duckdb/fieldStats.js.map +1 -1
- package/dist/esm/store/duckdb/lexical.d.ts +1 -6
- package/dist/esm/store/duckdb/lexical.js +13 -17
- package/dist/esm/store/duckdb/lexical.js.map +1 -1
- package/dist/esm/store/duckdb/native.d.ts +1 -2
- package/dist/esm/store/duckdb/native.js +1 -3
- package/dist/esm/store/duckdb/native.js.map +1 -1
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/ordered-bm25.d.ts +3 -16
- package/dist/esm/store/duckdb/ordered-bm25.js +18 -54
- package/dist/esm/store/duckdb/ordered-bm25.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/duckdb/sql-functions.js.map +1 -1
- package/dist/esm/store/duckdb/store.js.map +1 -1
- package/dist/esm/store/duckdb/vectors.js.map +1 -1
- package/dist/esm/store/embed-scope.js.map +1 -1
- package/dist/esm/store/feature-scope.js.map +1 -1
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/lock-wait.js.map +1 -1
- package/dist/esm/store/native.js.map +1 -1
- package/dist/esm/store/open.js.map +1 -1
- package/dist/esm/store/preset-scope.js.map +1 -1
- package/dist/esm/store/readiness.js.map +1 -1
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/shared.js.map +1 -1
- package/dist/esm/store/signature.js.map +1 -1
- package/dist/esm/store/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/connection.js.map +1 -1
- package/dist/esm/store/sqlite/fieldStats.js.map +1 -1
- package/dist/esm/store/sqlite/lexical.js.map +1 -1
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/store.js.map +1 -1
- package/dist/esm/store/sqlite/vectors.js.map +1 -1
- package/dist/esm/store/stages.js.map +1 -1
- package/dist/esm/store/transaction.js.map +1 -1
- package/dist/esm/store/turso/connection.js.map +1 -1
- package/dist/esm/store/turso/fieldStats.js.map +1 -1
- package/dist/esm/store/turso/lexical-text.js.map +1 -1
- package/dist/esm/store/turso/lexical.js.map +1 -1
- package/dist/esm/store/turso/native.js.map +1 -1
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/reconcile.js.map +1 -1
- package/dist/esm/store/turso/sql-functions.js.map +1 -1
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/store/turso/vectors.js.map +1 -1
- package/dist/esm/store/types.js.map +1 -1
- package/dist/esm/store/vectors.js.map +1 -1
- package/dist/esm/text/segment.js.map +1 -1
- package/dist/esm/text/strip.js.map +1 -1
- package/dist/esm/watch-claim.js.map +1 -1
- package/dist/esm/watch.js.map +1 -1
- package/dist/esm/workers/parse.js.map +1 -1
- package/dist/esm/workers/watch-heartbeat.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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 +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, 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
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/transaction.ts"],"sourcesContent":["// Store-agnostic joining transaction helper: node:sqlite and DuckDB both support only\n// BEGIN/COMMIT/ROLLBACK, no SAVEPOINT -- nesting joins the outer transaction, sharing one semantics through exec(sql).\ninterface ExecConnection {\n exec(sql: string): Promise<void>;\n}\n\ninterface TxState {\n depth: number;\n rollbackOnly: boolean;\n}\n\n// Keyed by connection, not a module singleton: two connections can coexist in one process\n// (e.g. watch.ts alongside a CLI query) and must not share depth.\nconst states = new WeakMap<ExecConnection, TxState>();\n\nfunction stateFor(conn: ExecConnection): TxState {\n let state = states.get(conn);\n if (!state) {\n state = { depth: 0, rollbackOnly: false };\n states.set(conn, state);\n }\n return state;\n}\n\nasync function enter(conn: ExecConnection, state: TxState, begin: string): Promise<void> {\n if (state.depth === 0) {\n await conn.exec(begin);\n state.rollbackOnly = false;\n }\n state.depth++;\n}\n\n// Reaching depth 0 still rollback-only means an inner scope failed and an enclosing catch\n// swallowed it; commit here would persist a partial write, so this rolls back and throws instead.\nasync function leaveOk(conn: ExecConnection, state: TxState): Promise<void> {\n state.depth--;\n if (state.depth > 0) return;\n if (state.rollbackOnly) {\n await conn.exec('ROLLBACK');\n throw new Error('transaction rolled back: a nested scope failed');\n }\n await conn.exec('COMMIT');\n}\n\nasync function leaveErr(conn: ExecConnection, state: TxState): Promise<void> {\n state.rollbackOnly = true;\n state.depth--;\n if (state.depth === 0) await conn.exec('ROLLBACK');\n}\n\n// A writer takes its lock at BEGIN rather than on first write: SQLite will not retry a deferred\n// transaction that read first and then upgrades, so busy_timeout never applies and the second\n// concurrent command fails outright instead of queueing. Reads keep plain BEGIN, or every snapshot\n// read (map, peek) would serialize against writers. DuckDB has no IMMEDIATE and passes nothing.\nexport const BEGIN_WRITE = 'BEGIN IMMEDIATE';\n\n// Depth-0 opens the transaction; a nested call joins it and issues no SQL of its own. The outermost\n// call owns COMMIT/ROLLBACK, so join semantics never differ by call site.\nexport async function withTransaction<T>(conn: ExecConnection, fn: () => Promise<T>, begin = 'BEGIN'): Promise<T> {\n const state = stateFor(conn);\n await enter(conn, state, begin);\n let result: T;\n try {\n result = await fn();\n } catch (err) {\n await leaveErr(conn, state);\n throw err;\n }\n await leaveOk(conn, state);\n return result;\n}\n"],"names":["states","WeakMap","stateFor","conn","state","get","depth","rollbackOnly","set","enter","begin","exec","leaveOk","Error","leaveErr","BEGIN_WRITE","withTransaction","fn","result","err"],"mappings":"AAAA,sFAAsF;AACtF,uHAAuH;AAUvH,0FAA0F;AAC1F,kEAAkE;AAClE,MAAMA,SAAS,IAAIC;AAEnB,SAASC,SAASC,IAAoB;IACpC,IAAIC,QAAQJ,OAAOK,GAAG,CAACF;IACvB,IAAI,CAACC,OAAO;QACVA,QAAQ;YAAEE,OAAO;YAAGC,cAAc;QAAM;QACxCP,OAAOQ,GAAG,CAACL,MAAMC;IACnB;IACA,OAAOA;AACT;AAEA,eAAeK,MAAMN,IAAoB,EAAEC,KAAc,EAAEM,KAAa;IACtE,IAAIN,MAAME,KAAK,KAAK,GAAG;QACrB,MAAMH,KAAKQ,IAAI,CAACD;QAChBN,MAAMG,YAAY,GAAG;IACvB;IACAH,MAAME,KAAK;AACb;AAEA,0FAA0F;AAC1F,kGAAkG;AAClG,eAAeM,QAAQT,IAAoB,EAAEC,KAAc;IACzDA,MAAME,KAAK;IACX,IAAIF,MAAME,KAAK,GAAG,GAAG;IACrB,IAAIF,MAAMG,YAAY,EAAE;QACtB,MAAMJ,KAAKQ,IAAI,CAAC;QAChB,MAAM,IAAIE,MAAM;IAClB;IACA,MAAMV,KAAKQ,IAAI,CAAC;AAClB;AAEA,eAAeG,SAASX,IAAoB,EAAEC,KAAc;IAC1DA,MAAMG,YAAY,GAAG;IACrBH,MAAME,KAAK;IACX,IAAIF,MAAME,KAAK,KAAK,GAAG,MAAMH,KAAKQ,IAAI,CAAC;AACzC;AAEA,gGAAgG;AAChG,8FAA8F;AAC9F,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,MAAMI,cAAc,kBAAkB;AAE7C,oGAAoG;AACpG,0EAA0E;AAC1E,OAAO,eAAeC,gBAAmBb,IAAoB,EAAEc,EAAoB,EAAEP,QAAQ,OAAO;IAClG,MAAMN,QAAQF,SAASC;IACvB,MAAMM,MAAMN,MAAMC,OAAOM;IACzB,IAAIQ;IACJ,IAAI;QACFA,SAAS,MAAMD;IACjB,EAAE,OAAOE,KAAK;QACZ,MAAML,SAASX,MAAMC;QACrB,MAAMe;IACR;IACA,MAAMP,QAAQT,MAAMC;IACpB,OAAOc;AACT"}
|
|
@@ -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 { 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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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/turso/fieldStats.ts"],"sourcesContent":["import { quoteIdent } from '../shared.ts';\nimport type { Connection, FieldStat } from '../types.ts';\n\n// Fork of sqlite/fieldStats.ts (house precedent: each store owns its engine-specific SQL):\n// Turso is SQLite dialect (spike-verified: typeof(), GROUP_CONCAT, FILTER behave identically to node:sqlite), so the SQL text is identical -- only the Connection differs.\nexport async function fieldStats(conn: Connection, columns: string[], scopeWhere: string): Promise<FieldStat[]> {\n const exprs = columns.map((name, i) => {\n const quoted = quoteIdent(name);\n return `COUNT(${quoted}) AS n${i}, GROUP_CONCAT(DISTINCT typeof(${quoted})) FILTER (WHERE ${quoted} IS NOT NULL) AS t${i}`;\n });\n const stmt = await conn.prepare(`SELECT ${exprs.join(', ')} FROM frontmatter ${scopeWhere}`);\n const row = (await stmt.get()) as Record<string, number | string | null>;\n return columns.map((name, i) => ({\n field: name,\n coverage: Number(row[`n${i}`] ?? 0),\n type: [...new Set(((row[`t${i}`] as string) ?? '').split(',').filter(Boolean))].sort().join(','),\n }));\n}\n"],"names":["quoteIdent","fieldStats","conn","columns","scopeWhere","exprs","map","name","i","quoted","stmt","prepare","join","row","get","field","coverage","Number","type","Set","split","filter","Boolean","sort"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAG1C,2FAA2F;AAC3F,2KAA2K;AAC3K,OAAO,eAAeC,WAAWC,IAAgB,EAAEC,OAAiB,EAAEC,UAAkB;IACtF,MAAMC,QAAQF,QAAQG,GAAG,CAAC,CAACC,MAAMC;QAC/B,MAAMC,SAAST,WAAWO;QAC1B,OAAO,CAAC,MAAM,EAAEE,OAAO,MAAM,EAAED,EAAE,+BAA+B,EAAEC,OAAO,iBAAiB,EAAEA,OAAO,kBAAkB,EAAED,GAAG;IAC5H;IACA,MAAME,OAAO,MAAMR,KAAKS,OAAO,CAAC,CAAC,OAAO,EAAEN,MAAMO,IAAI,CAAC,MAAM,kBAAkB,EAAER,YAAY;IAC3F,MAAMS,MAAO,MAAMH,KAAKI,GAAG;IAC3B,OAAOX,QAAQG,GAAG,CAAC,CAACC,MAAMC;YAEPK,OACGA;eAHW;YAC/BE,OAAOR;YACPS,UAAUC,QAAOJ,QAAAA,GAAG,CAAC,CAAC,CAAC,EAAEL,GAAG,CAAC,cAAZK,mBAAAA,QAAgB;YACjCK,MAAM;mBAAI,IAAIC,IAAI,EAAEN,SAAAA,GAAG,CAAC,CAAC,CAAC,EAAEL,GAAG,CAAC,cAAZK,oBAAAA,SAA2B,IAAIO,KAAK,CAAC,KAAKC,MAAM,CAACC;aAAU,CAACC,IAAI,GAAGX,IAAI,CAAC;QAC9F;;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/turso/lexical-text.ts"],"sourcesContent":["import { stemmer } from 'stemmer';\nimport { foldForSearch, searchTokens } from '../../text/segment.ts';\n\nexport function stemFolded(text: string): string {\n let out = '';\n let end = 0;\n for (const token of searchTokens(text)) {\n out += text.slice(end, token.start).replace(/[\\u0027\\u2019]/gu, ' ');\n out += token.unspaced ? token.text : stemmer(foldForSearch(token.text));\n end = token.end;\n }\n return out + text.slice(end).replace(/[\\u0027\\u2019]/gu, ' ');\n}\n"],"names":["stemmer","foldForSearch","searchTokens","stemFolded","text","out","end","token","slice","start","replace","unspaced"],"mappings":"AAAA,SAASA,OAAO,QAAQ,UAAU;AAClC,SAASC,aAAa,EAAEC,YAAY,QAAQ,wBAAwB;AAEpE,OAAO,SAASC,WAAWC,IAAY;IACrC,IAAIC,MAAM;IACV,IAAIC,MAAM;IACV,KAAK,MAAMC,SAASL,aAAaE,MAAO;QACtCC,OAAOD,KAAKI,KAAK,CAACF,KAAKC,MAAME,KAAK,EAAEC,OAAO,CAAC,oBAAoB;QAChEL,OAAOE,MAAMI,QAAQ,GAAGJ,MAAMH,IAAI,GAAGJ,QAAQC,cAAcM,MAAMH,IAAI;QACrEE,MAAMC,MAAMD,GAAG;IACjB;IACA,OAAOD,MAAMD,KAAKI,KAAK,CAACF,KAAKI,OAAO,CAAC,oBAAoB;AAC3D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/lexical.ts"],"sourcesContent":["// T1: Tantivy FTS via fts_match/fts_score on the default-tokenizer index for bare words and\n// quoted phrases, plus a second ngram-tokenized index over \"_ngram\" sidecars for unspaced-script runs.\nimport { SenseError } from '../../errors.ts';\nimport { matchesSearchPhrase, type SearchToken, searchTokens } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\nimport { stemFolded } from './lexical-text.ts';\n\nconst FIELDS = ['title_stem', 'summary_stem', 'text_stem'] as const;\nconst NGRAM_FIELDS = ['title_ngram', 'summary_ngram', 'text_ngram'] as const;\nconst AUTHORED_FIELDS = ['title', 'summary', 'text'] as const;\ntype AuthoredField = (typeof AUTHORED_FIELDS)[number];\n\n// FTS5 operator syntax a bare query would otherwise treat as literal text (PRINCIPLES:\n// no-silent-modes). `^` is rejected either way: boost to Tantivy, initial-token match to FTS5.\nconst FTS5_OPERATORS: Array<{ label: string; re: RegExp }> = [\n { label: 'prefix query', re: /[\\p{L}\\p{N}_]+\\*(?=\\s|$)/u },\n { label: 'boolean operator', re: /(?:^|\\s)(?:AND|OR|NOT)(?=\\s|$)/ },\n { label: 'NEAR operator', re: /(?:^|\\s)NEAR\\b/ },\n { label: 'boost/initial-token operator', re: /\\S*\\^\\S*/ },\n { label: 'column filter', re: /(?:^|\\s)[\\p{L}_]\\w*\\s*:/u },\n { label: 'grouping operator', re: /[()]/ },\n];\n\nfunction unsupportedOperator(terms: string): { label: string; token: string } | null {\n const withoutPhrases = terms.replace(/\"[^\"]*\"/g, 'phrase');\n for (const { label, re } of FTS5_OPERATORS) {\n const m = withoutPhrases.match(re);\n if (m) return { label, token: m[0].trim() };\n }\n return null;\n}\n\n// A run whose script marks no word boundaries indexes as one opaque token under every tokenizer\n// but ngram, so such runs -- bare or quoted -- go to the ngram sidecar index instead.\nfunction splitTerms(terms: string): { words: string[]; phrases: SearchToken[][]; unspaced: string[]; emptyPhrase: boolean } {\n const words: string[] = [];\n const phrases: SearchToken[][] = [];\n const unspaced: string[] = [];\n let emptyPhrase = false;\n const withoutPhrases = terms.replace(/\"([^\"]*)\"/g, (_m, inner: string) => {\n const phrase = inner.trim();\n const tokens = searchTokens(phrase);\n if (tokens.length === 0) {\n emptyPhrase = true;\n return ' ';\n }\n unspaced.push(...tokens.filter((token) => token.unspaced).map((token) => token.text));\n phrases.push(tokens);\n return ' ';\n });\n for (const tok of withoutPhrases.split(/\\s+/)) {\n if (tok.length === 0) continue;\n for (const token of searchTokens(tok)) {\n if (token.unspaced) unspaced.push(token.text);\n else words.push(token.text);\n }\n }\n return { words, phrases, unspaced, emptyPhrase };\n}\n\n// fts_match/fts_score's query argument must be a SQL literal: bound as `?`, the row set stays\n// correct but fts_score silently returns 0 for every row (PRINCIPLES: no-silent-modes).\nfunction escapeFtsLiteral(query: string): string {\n return query.replace(/'/g, \"''\");\n}\n\n// Terms are quoted (\"O'Brien's\" is a parse error bare) and AND-joined: Tantivy's default for\n// bare terms is disjunctive, so space-joining would loosen \"apple banana\" into \"apple OR banana\".\nfunction buildQueries(words: string[], phrases: SearchToken[][], unspaced: string[]): { defaultQuery: string; ngramQuery: string } {\n const phraseParts = phrases.flatMap((phrase) => {\n const latin = phrase.filter((token) => !token.unspaced).map((token) => stemFolded(token.text));\n if (latin.length === 0) return [];\n return phrase.some((token) => token.unspaced) ? latin : [latin.join(' ')];\n });\n const defaultParts = [...words.map(stemFolded), ...phraseParts].map((t) => `\"${t}\"`);\n const unspacedParts = unspaced.map((t) => `\"${t}\"`);\n return { defaultQuery: defaultParts.join(' AND '), ngramQuery: unspacedParts.join(' AND ') };\n}\n\n// fts_score() is real only as a SELECT's bare projected expression, and only for the index that\n// SELECT's fts_match() chose; every other form silently returns 0. Hence one SELECT per index.\nfunction ftsBranch(cols: readonly string[], query: string, opts: LexicalQueryOptions): string {\n const colList = cols.map((c) => `content.${c}`).join(', ');\n const lit = escapeFtsLiteral(query);\n return `SELECT content.\"path\" AS path, fts_score(${colList}, '${lit}') AS score\n FROM content ${opts.whereJoin} WHERE fts_match(${colList}, '${lit}') ${opts.whereCond} ${opts.scopeCond}`;\n}\n\n// Ranked query, scoped by the caller-built SQL fragments (same shape sqlite's/duckdb's\n// queryLexical take). `snippets` is always NULL: fts_highlight returns the full column text, not a bounded snippet, so every row goes through the caller's JS snippet fallback (commands/search.ts).\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]> {\n const unsupported = unsupportedOperator(terms);\n if (unsupported !== null) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"turso\" does not implement FTS5's ${unsupported.label} (\"${unsupported.token}\") in this build; rephrase \"${terms.trim()}\" without it, or set \"store\" to \"sqlite\" in this tree's config to search it as written`);\n }\n const { words, phrases, unspaced, emptyPhrase } = splitTerms(terms);\n if (emptyPhrase) return [];\n const { defaultQuery, ngramQuery } = buildQueries(words, phrases, unspaced);\n if (defaultQuery === '' && ngramQuery === '') return [];\n\n const branches: string[] = [];\n if (defaultQuery !== '') branches.push(ftsBranch(FIELDS, defaultQuery, opts));\n if (ngramQuery !== '') branches.push(ftsBranch(NGRAM_FIELDS, ngramQuery, opts));\n\n // HAVING COUNT(*) = branches.length is the AND across indexes; with one branch it is a no-op,\n // `path` being content's primary key. fts_score is higher-is-better BM25, so DESC. Phrase\n // candidates carry authored fields through the join below so the shared verifier can reject\n // cross-field, reordered, and non-adjacent matches before applying the caller's limit.\n const ranked = `SELECT path, SUM(score) AS score FROM (\n ${branches.join(' UNION ALL ')}\n ) parts GROUP BY path HAVING COUNT(*) = ${branches.length}`;\n const phraseSelect = phrases.length > 0 ? ', content.title, content.summary, content.text' : '';\n const phraseJoin = phrases.length > 0 ? ' JOIN content ON content.path = sq.path' : '';\n const sql = `SELECT sq.path, sq.score${phraseSelect} FROM (\n ${ranked}\n ) sq${phraseJoin} ORDER BY sq.score DESC, sq.path${phrases.length > 0 ? '' : ' LIMIT ?'}`;\n const stmt = await conn.prepare(sql);\n const rows = (await stmt.all(...(phrases.length > 0 ? [] : [opts.limit]))) as Array<LexicalHit & Partial<Record<AuthoredField, string>>>;\n // A concurrent FTS read during an open write transaction can yield a {path: null} row. Raised,\n // not filtered: filtering returns a silently short result (PRINCIPLES: no-silent-modes).\n if (rows.some((r) => r.path === null)) {\n throw new SenseError('LEXICAL_NULL_PATH', 'store \"turso\" returned a search hit with no path (a known engine anomaly under concurrent FTS reads); retry the query');\n }\n const matches = phrases.length === 0 ? rows : rows.filter((row) => phrases.every((phrase) => AUTHORED_FIELDS.some((field) => matchesSearchPhrase(row[field] ?? '', phrase))));\n return matches.slice(0, opts.limit).map(({ path }) => ({ path }));\n}\n"],"names":["SenseError","matchesSearchPhrase","searchTokens","stemFolded","FIELDS","NGRAM_FIELDS","AUTHORED_FIELDS","FTS5_OPERATORS","label","re","unsupportedOperator","terms","withoutPhrases","replace","m","match","token","trim","splitTerms","words","phrases","unspaced","emptyPhrase","_m","inner","phrase","tokens","length","push","filter","map","text","tok","split","escapeFtsLiteral","query","buildQueries","phraseParts","flatMap","latin","some","join","defaultParts","t","unspacedParts","defaultQuery","ngramQuery","ftsBranch","cols","opts","colList","c","lit","whereJoin","whereCond","scopeCond","queryLexical","conn","unsupported","branches","ranked","phraseSelect","phraseJoin","sql","stmt","prepare","rows","all","limit","r","path","matches","row","every","field","slice"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/turso/lexical.ts"],"sourcesContent":["// T1: Tantivy FTS via fts_match/fts_score on the default-tokenizer index for bare words and\n// quoted phrases, plus a second ngram-tokenized index over \"_ngram\" sidecars for unspaced-script runs.\nimport { SenseError } from '../../errors.ts';\nimport { matchesSearchPhrase, type SearchToken, searchTokens } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\nimport { stemFolded } from './lexical-text.ts';\n\nconst FIELDS = ['title_stem', 'summary_stem', 'text_stem'] as const;\nconst NGRAM_FIELDS = ['title_ngram', 'summary_ngram', 'text_ngram'] as const;\nconst AUTHORED_FIELDS = ['title', 'summary', 'text'] as const;\ntype AuthoredField = (typeof AUTHORED_FIELDS)[number];\n\n// FTS5 operator syntax a bare query would otherwise treat as literal text (PRINCIPLES:\n// no-silent-modes). `^` is rejected either way: boost to Tantivy, initial-token match to FTS5.\nconst FTS5_OPERATORS: Array<{ label: string; re: RegExp }> = [\n { label: 'prefix query', re: /[\\p{L}\\p{N}_]+\\*(?=\\s|$)/u },\n { label: 'boolean operator', re: /(?:^|\\s)(?:AND|OR|NOT)(?=\\s|$)/ },\n { label: 'NEAR operator', re: /(?:^|\\s)NEAR\\b/ },\n { label: 'boost/initial-token operator', re: /\\S*\\^\\S*/ },\n { label: 'column filter', re: /(?:^|\\s)[\\p{L}_]\\w*\\s*:/u },\n { label: 'grouping operator', re: /[()]/ },\n];\n\nfunction unsupportedOperator(terms: string): { label: string; token: string } | null {\n const withoutPhrases = terms.replace(/\"[^\"]*\"/g, 'phrase');\n for (const { label, re } of FTS5_OPERATORS) {\n const m = withoutPhrases.match(re);\n if (m) return { label, token: m[0].trim() };\n }\n return null;\n}\n\n// A run whose script marks no word boundaries indexes as one opaque token under every tokenizer\n// but ngram, so such runs -- bare or quoted -- go to the ngram sidecar index instead.\nfunction splitTerms(terms: string): { words: string[]; phrases: SearchToken[][]; unspaced: string[]; emptyPhrase: boolean } {\n const words: string[] = [];\n const phrases: SearchToken[][] = [];\n const unspaced: string[] = [];\n let emptyPhrase = false;\n const withoutPhrases = terms.replace(/\"([^\"]*)\"/g, (_m, inner: string) => {\n const phrase = inner.trim();\n const tokens = searchTokens(phrase);\n if (tokens.length === 0) {\n emptyPhrase = true;\n return ' ';\n }\n unspaced.push(...tokens.filter((token) => token.unspaced).map((token) => token.text));\n phrases.push(tokens);\n return ' ';\n });\n for (const tok of withoutPhrases.split(/\\s+/)) {\n if (tok.length === 0) continue;\n for (const token of searchTokens(tok)) {\n if (token.unspaced) unspaced.push(token.text);\n else words.push(token.text);\n }\n }\n return { words, phrases, unspaced, emptyPhrase };\n}\n\n// fts_match/fts_score's query argument must be a SQL literal: bound as `?`, the row set stays\n// correct but fts_score silently returns 0 for every row (PRINCIPLES: no-silent-modes).\nfunction escapeFtsLiteral(query: string): string {\n return query.replace(/'/g, \"''\");\n}\n\n// Terms are quoted (\"O'Brien's\" is a parse error bare) and AND-joined: Tantivy's default for\n// bare terms is disjunctive, so space-joining would loosen \"apple banana\" into \"apple OR banana\".\nfunction buildQueries(words: string[], phrases: SearchToken[][], unspaced: string[]): { defaultQuery: string; ngramQuery: string } {\n const phraseParts = phrases.flatMap((phrase) => {\n const latin = phrase.filter((token) => !token.unspaced).map((token) => stemFolded(token.text));\n if (latin.length === 0) return [];\n return phrase.some((token) => token.unspaced) ? latin : [latin.join(' ')];\n });\n const defaultParts = [...words.map(stemFolded), ...phraseParts].map((t) => `\"${t}\"`);\n const unspacedParts = unspaced.map((t) => `\"${t}\"`);\n return { defaultQuery: defaultParts.join(' AND '), ngramQuery: unspacedParts.join(' AND ') };\n}\n\n// fts_score() is real only as a SELECT's bare projected expression, and only for the index that\n// SELECT's fts_match() chose; every other form silently returns 0. Hence one SELECT per index.\nfunction ftsBranch(cols: readonly string[], query: string, opts: LexicalQueryOptions): string {\n const colList = cols.map((c) => `content.${c}`).join(', ');\n const lit = escapeFtsLiteral(query);\n return `SELECT content.\"path\" AS path, fts_score(${colList}, '${lit}') AS score\n FROM content ${opts.whereJoin} WHERE fts_match(${colList}, '${lit}') ${opts.whereCond} ${opts.scopeCond}`;\n}\n\n// Ranked query, scoped by the caller-built SQL fragments (same shape sqlite's/duckdb's\n// queryLexical take). `snippets` is always NULL: fts_highlight returns the full column text, not a bounded snippet, so every row goes through the caller's JS snippet fallback (commands/search.ts).\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]> {\n const unsupported = unsupportedOperator(terms);\n if (unsupported !== null) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"turso\" does not implement FTS5's ${unsupported.label} (\"${unsupported.token}\") in this build; rephrase \"${terms.trim()}\" without it, or set \"store\" to \"sqlite\" in this tree's config to search it as written`);\n }\n const { words, phrases, unspaced, emptyPhrase } = splitTerms(terms);\n if (emptyPhrase) return [];\n const { defaultQuery, ngramQuery } = buildQueries(words, phrases, unspaced);\n if (defaultQuery === '' && ngramQuery === '') return [];\n\n const branches: string[] = [];\n if (defaultQuery !== '') branches.push(ftsBranch(FIELDS, defaultQuery, opts));\n if (ngramQuery !== '') branches.push(ftsBranch(NGRAM_FIELDS, ngramQuery, opts));\n\n // HAVING COUNT(*) = branches.length is the AND across indexes; with one branch it is a no-op,\n // `path` being content's primary key. fts_score is higher-is-better BM25, so DESC. Phrase\n // candidates carry authored fields through the join below so the shared verifier can reject\n // cross-field, reordered, and non-adjacent matches before applying the caller's limit.\n const ranked = `SELECT path, SUM(score) AS score FROM (\n ${branches.join(' UNION ALL ')}\n ) parts GROUP BY path HAVING COUNT(*) = ${branches.length}`;\n const phraseSelect = phrases.length > 0 ? ', content.title, content.summary, content.text' : '';\n const phraseJoin = phrases.length > 0 ? ' JOIN content ON content.path = sq.path' : '';\n const sql = `SELECT sq.path, sq.score${phraseSelect} FROM (\n ${ranked}\n ) sq${phraseJoin} ORDER BY sq.score DESC, sq.path${phrases.length > 0 ? '' : ' LIMIT ?'}`;\n const stmt = await conn.prepare(sql);\n const rows = (await stmt.all(...(phrases.length > 0 ? [] : [opts.limit]))) as Array<LexicalHit & Partial<Record<AuthoredField, string>>>;\n // A concurrent FTS read during an open write transaction can yield a {path: null} row. Raised,\n // not filtered: filtering returns a silently short result (PRINCIPLES: no-silent-modes).\n if (rows.some((r) => r.path === null)) {\n throw new SenseError('LEXICAL_NULL_PATH', 'store \"turso\" returned a search hit with no path (a known engine anomaly under concurrent FTS reads); retry the query');\n }\n const matches = phrases.length === 0 ? rows : rows.filter((row) => phrases.every((phrase) => AUTHORED_FIELDS.some((field) => matchesSearchPhrase(row[field] ?? '', phrase))));\n return matches.slice(0, opts.limit).map(({ path }) => ({ path }));\n}\n"],"names":["SenseError","matchesSearchPhrase","searchTokens","stemFolded","FIELDS","NGRAM_FIELDS","AUTHORED_FIELDS","FTS5_OPERATORS","label","re","unsupportedOperator","terms","withoutPhrases","replace","m","match","token","trim","splitTerms","words","phrases","unspaced","emptyPhrase","_m","inner","phrase","tokens","length","push","filter","map","text","tok","split","escapeFtsLiteral","query","buildQueries","phraseParts","flatMap","latin","some","join","defaultParts","t","unspacedParts","defaultQuery","ngramQuery","ftsBranch","cols","opts","colList","c","lit","whereJoin","whereCond","scopeCond","queryLexical","conn","unsupported","branches","ranked","phraseSelect","phraseJoin","sql","stmt","prepare","rows","all","limit","r","path","matches","row","every","field","slice"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/turso/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 { hasUnspacedRun } from '../../text/segment.ts';\nimport { quoteIdent, recordReconcileDuration } from '../shared.ts';\nimport { BEGIN_WRITE } from '../transaction.ts';\nimport type { Connection, ReconcileDialect } from '../types.ts';\nimport { stemFolded } from './lexical-text.ts';\n\n// This store's dialect (types.ts's ReconcileDialect) for the shared orchestration in\n// store/reconcile.ts. content is a plain table with \"_ngram\" sidecars for lexical.ts.\n\n// The ngram index is scoped to disjoint \"_ngram\" sidecar columns: a second index over the same\n// columns makes a bare substring match a whole word, defeating the prefix-query rejection.\n// The two FTS indexes, named here (not open.ts) because reconcile drops and rebuilds them around\n// a bulk load: Tantivy maintains them per inserted row, which is quadratic in what is already indexed.\nexport const CONTENT_FTS_DDL = [\n `CREATE INDEX IF NOT EXISTS content_fts ON content USING fts (title_stem, summary_stem, text_stem) WITH (weights = 'title_stem=10.0,summary_stem=5.0,text_stem=1.0')`,\n `CREATE INDEX IF NOT EXISTS content_fts_ngram ON content USING fts (title_ngram, summary_ngram, text_ngram) WITH (tokenizer='ngram', weights='title_ngram=10.0,summary_ngram=5.0,text_ngram=1.0')`,\n] as const;\nexport const CONTENT_FTS_NAMES = ['content_fts', 'content_fts_ngram'] as const;\n\n// '' when the field has no unspaced-script run (the common case), so the ngram index carries\n// nothing for it -- same \"pay for nothing when absent\" shape as sqlite's segmentField sidecars.\nfunction ngramSidecar(text: string): string {\n return hasUnspacedRun(text) ? text : '';\n}\n\n// Not a compile-time cap on ALTER TABLE ADD COLUMN (spike-measured: turso accepts 10,000 with\n// no error); the real fence is a SELECT projecting more than this many result columns, which fails to prepare.\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Retained policy boundary from 2026-08-30 corpus-scale measurements; the optimum after later\n// update-path changes remains pending matched corpus-scale crossover evidence.\nexport const FTS_REBUILD_THRESHOLD = 250;\n\nexport type TursoFtsStrategy = 'incremental' | 'rebuild';\n\n// No rowid coupling (unlike sqlite's FTS5 content): `path` is content's own primary key.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (\"path\", title, summary, text, title_stem, summary_stem, text_stem, title_ngram, summary_ngram, text_ngram) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n\nfunction contentRow(doc: ParsedDoc): unknown[] {\n const { title, summary, text } = doc.search;\n return [doc.relPath, title, summary, text, stemFolded(title), stemFolded(summary), stemFolded(text), ngramSidecar(title), ngramSidecar(summary), ngramSidecar(text)];\n}\n\nexport function tursoFtsStrategy(delta: ReconcileDelta): TursoFtsStrategy {\n const churn = delta.reparsed.length + delta.vanished.length;\n return delta.files.length === 0 || churn > FTS_REBUILD_THRESHOLD ? 'rebuild' : 'incremental';\n}\n\n// Kept transaction-free because the shared reconcile owns the one write transaction. The explicit\n// strategy is internal composition for real-engine diagnostics; production selects it from delta.\nexport async function reconcileTursoContentWithStrategy(conn: Connection, touched: string[], docs: ParsedDoc[], strategy: TursoFtsStrategy): Promise<void> {\n // Tantivy indexes per inserted row at a cost that grows with the batch, so a large insert is\n // superlinear and a rebuild wins past the threshold.\n const bulk = strategy === 'rebuild';\n if (bulk) for (const name of CONTENT_FTS_NAMES) await conn.exec(`DROP INDEX IF EXISTS ${name}`);\n\n // content is a plain table keyed by its own path (no rowid subquery, unlike sqlite's FTS5\n // content), so vanished and reparsed docs delete in one pass.\n if (touched.length > 0) {\n const placeholders = touched.map(() => '?').join(', ');\n await conn.runBatch(`DELETE FROM content WHERE \"path\" IN (${placeholders})`, [touched]);\n }\n if (docs.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, docs.map(contentRow));\n if (bulk) for (const ddl of CONTENT_FTS_DDL) await conn.exec(ddl);\n}\n\nasync function reconcileTursoContent(conn: Connection, touched: string[], docs: ParsedDoc[], delta: ReconcileDelta, _cfg: Config): Promise<void> {\n await reconcileTursoContentWithStrategy(conn, touched, docs, tursoFtsStrategy(delta));\n}\n\n// turso'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 tursoDialect: ReconcileDialect = {\n beginMode: () => BEGIN_WRITE,\n checkColumnLimit(count) {\n if (count > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError(\n 'COLUMN_LIMIT',\n `frontmatter would need ${count} columns, crossing turso's SELECT result-set column limit (${MAX_FRONTMATTER_COLUMNS}; ALTER TABLE ADD COLUMN itself accepts far more, but a query projecting past this many columns fails to prepare). 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: reconcileTursoContent,\n recordDuration: recordReconcileDuration,\n};\n"],"names":["SenseError","hasUnspacedRun","quoteIdent","recordReconcileDuration","BEGIN_WRITE","stemFolded","CONTENT_FTS_DDL","CONTENT_FTS_NAMES","ngramSidecar","text","MAX_FRONTMATTER_COLUMNS","FTS_REBUILD_THRESHOLD","INSERT_CONTENT_SQL","contentRow","doc","title","summary","search","relPath","tursoFtsStrategy","delta","churn","reparsed","length","vanished","files","reconcileTursoContentWithStrategy","conn","touched","docs","strategy","bulk","name","exec","placeholders","map","join","runBatch","ddl","reconcileTursoContent","_cfg","addColumns","names","tursoDialect","beginMode","checkColumnLimit","count","reconcileContent","recordDuration"],"mappings":"AACA,SAASA,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,cAAc,QAAQ,wBAAwB;AACvD,SAASC,UAAU,EAAEC,uBAAuB,QAAQ,eAAe;AACnE,SAASC,WAAW,QAAQ,oBAAoB;AAEhD,SAASC,UAAU,QAAQ,oBAAoB;AAE/C,qFAAqF;AACrF,sFAAsF;AAEtF,+FAA+F;AAC/F,2FAA2F;AAC3F,iGAAiG;AACjG,uGAAuG;AACvG,OAAO,MAAMC,kBAAkB;IAC7B,CAAC,mKAAmK,CAAC;IACrK,CAAC,gMAAgM,CAAC;CACnM,CAAU;AACX,OAAO,MAAMC,oBAAoB;IAAC;IAAe;CAAoB,CAAU;AAE/E,6FAA6F;AAC7F,gGAAgG;AAChG,SAASC,aAAaC,IAAY;IAChC,OAAOR,eAAeQ,QAAQA,OAAO;AACvC;AAEA,8FAA8F;AAC9F,+GAA+G;AAC/G,MAAMC,0BAA0B;AAEhC,8FAA8F;AAC9F,+EAA+E;AAC/E,OAAO,MAAMC,wBAAwB,IAAI;AAIzC,yFAAyF;AACzF,MAAMC,qBAAqB,CAAC,qKAAqK,CAAC;AAElM,SAASC,WAAWC,GAAc;IAChC,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAEP,IAAI,EAAE,GAAGK,IAAIG,MAAM;IAC3C,OAAO;QAACH,IAAII,OAAO;QAAEH;QAAOC;QAASP;QAAMJ,WAAWU;QAAQV,WAAWW;QAAUX,WAAWI;QAAOD,aAAaO;QAAQP,aAAaQ;QAAUR,aAAaC;KAAM;AACtK;AAEA,OAAO,SAASU,iBAAiBC,KAAqB;IACpD,MAAMC,QAAQD,MAAME,QAAQ,CAACC,MAAM,GAAGH,MAAMI,QAAQ,CAACD,MAAM;IAC3D,OAAOH,MAAMK,KAAK,CAACF,MAAM,KAAK,KAAKF,QAAQV,wBAAwB,YAAY;AACjF;AAEA,kGAAkG;AAClG,kGAAkG;AAClG,OAAO,eAAee,kCAAkCC,IAAgB,EAAEC,OAAiB,EAAEC,IAAiB,EAAEC,QAA0B;IACxI,6FAA6F;IAC7F,qDAAqD;IACrD,MAAMC,OAAOD,aAAa;IAC1B,IAAIC,MAAM,KAAK,MAAMC,QAAQzB,kBAAmB,MAAMoB,KAAKM,IAAI,CAAC,CAAC,qBAAqB,EAAED,MAAM;IAE9F,0FAA0F;IAC1F,8DAA8D;IAC9D,IAAIJ,QAAQL,MAAM,GAAG,GAAG;QACtB,MAAMW,eAAeN,QAAQO,GAAG,CAAC,IAAM,KAAKC,IAAI,CAAC;QACjD,MAAMT,KAAKU,QAAQ,CAAC,CAAC,qCAAqC,EAAEH,aAAa,CAAC,CAAC,EAAE;YAACN;SAAQ;IACxF;IACA,IAAIC,KAAKN,MAAM,GAAG,GAAG,MAAMI,KAAKU,QAAQ,CAACzB,oBAAoBiB,KAAKM,GAAG,CAACtB;IACtE,IAAIkB,MAAM,KAAK,MAAMO,OAAOhC,gBAAiB,MAAMqB,KAAKM,IAAI,CAACK;AAC/D;AAEA,eAAeC,sBAAsBZ,IAAgB,EAAEC,OAAiB,EAAEC,IAAiB,EAAET,KAAqB,EAAEoB,IAAY;IAC9H,MAAMd,kCAAkCC,MAAMC,SAASC,MAAMV,iBAAiBC;AAChF;AAEA,yFAAyF;AACzF,eAAeqB,WAAWd,IAAgB,EAAEe,KAAe;IACzD,KAAK,MAAMV,QAAQU,MAAO,MAAMf,KAAKM,IAAI,CAAC,CAAC,mCAAmC,EAAE/B,WAAW8B,OAAO;AACpG;AAEA,OAAO,MAAMW,eAAiC;IAC5CC,WAAW,IAAMxC;IACjByC,kBAAiBC,KAAK;QACpB,IAAIA,QAAQpC,yBAAyB;YACnC,MAAM,IAAIV,WACR,gBACA,CAAC,uBAAuB,EAAE8C,MAAM,2DAA2D,EAAEpC,wBAAwB,gPAAgP,CAAC;QAE1W;IACF;IACA+B;IACAM,kBAAkBR;IAClBS,gBAAgB7C;AAClB,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/turso/sql-functions.ts"],"sourcesContent":["import { SenseError } from '../../errors.ts';\n\n// Turso can't register UDFs: has()/basename() are rewritten into portable SQL below; segment()\n// (Intl.Segmenter grapheme clustering, types.ts's 'segment' capability) has none, so it's rejected like an unsupported FTS5 operator.\n\n// Non-global: recursion (readArgs' contents rewritten via rewriteFunctions) reenters this module's\n// regexes before the outer scan resumes, and a global regex's lastIndex does not nest across that.\nconst CALL_ANYWHERE = /\\b(has|basename|segment)\\s*\\(/i;\nconst CALL_AT_START = /^(has|basename|segment)\\s*\\(/i;\n\nfunction isWordChar(ch: string | undefined): boolean {\n return ch !== undefined && /[A-Za-z0-9_]/.test(ch);\n}\n\n// The index just past the quoted literal or SQL comment starting at i, or null if i starts\n// neither. Shared by the scan and readArgs so both leave quoted/comment text opaque to a call match.\nfunction skipOpaque(sql: string, i: number): number | null {\n const ch = sql[i];\n if (ch === \"'\" || ch === '\"') {\n let j = i + 1;\n while (j < sql.length) {\n if (sql[j] === ch) {\n if (sql[j + 1] === ch) {\n j += 2;\n continue;\n }\n return j + 1;\n }\n j++;\n }\n return j; // unterminated literal: consume to end rather than loop forever\n }\n if (ch === '-' && sql[i + 1] === '-') {\n const nl = sql.indexOf('\\n', i);\n return nl === -1 ? sql.length : nl + 1;\n }\n if (ch === '/' && sql[i + 1] === '*') {\n const close = sql.indexOf('*/', i + 2);\n return close === -1 ? sql.length : close + 2;\n }\n return null;\n}\n\n// `?N`/named placeholders bind by number/name, so repeating one is safe; an anonymous `?` takes\n// the next slot per occurrence, so it's numbered here first -- sqlHas/sqlBasename duplicate their arguments.\nfunction numberPlaceholders(sql: string): string {\n let out = '';\n let i = 0;\n let n = 0;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n out += sql.slice(i, skip);\n i = skip;\n continue;\n }\n if (sql[i] === '?' && !/[0-9]/.test(sql[i + 1] ?? '')) {\n n++;\n out += `?${n}`;\n i++;\n continue;\n }\n out += sql[i];\n i++;\n }\n return out;\n}\n\n// The un-rewritten argument texts of the call whose '(' sits at parenStart, and the index just\n// past its matching ')'. Paren depth is tracked so a nested call's commas/parens aren't mistaken for ours.\nfunction readArgs(sql: string, parenStart: number): { args: string[]; end: number } {\n const args: string[] = [];\n let depth = 1;\n let cur = '';\n let i = parenStart + 1;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n cur += sql.slice(i, skip);\n i = skip;\n continue;\n }\n const ch = sql[i];\n if (ch === '(') {\n depth++;\n cur += ch;\n i++;\n continue;\n }\n if (ch === ')') {\n depth--;\n i++;\n if (depth === 0) break;\n cur += ch;\n continue;\n }\n if (ch === ',' && depth === 1) {\n args.push(cur.trim());\n cur = '';\n i++;\n continue;\n }\n cur += ch;\n i++;\n }\n if (depth !== 0) throw new Error(`turso: unterminated argument list starting at \"${sql.slice(parenStart, parenStart + 30)}...\"`);\n if (cur.trim() !== '') args.push(cur.trim());\n return { args, end: i };\n}\n\n// field.startsWith('[') && JSON.parse(field) is an array, matched by membership; every other\n// field, stringified, matched by substring (sql-functions.ts's hasImpl, mirrored branch for branch).\nfunction sqlHas(fieldExpr: string, valueExpr: string): string {\n const isArray = `substr(${fieldExpr},1,1) = '[' AND json_valid(${fieldExpr})`;\n // json_each's argument is re-guarded by its own CASE (falling back to '[]'): turso evaluates a\n // CASE branch's table function even when untaken, and a non-array argument there raises rather than yielding 0 rows (spike-verified).\n return `(CASE\n WHEN ${fieldExpr} IS NULL THEN 0\n WHEN ${isArray} THEN\n (SELECT COUNT(*) FROM json_each(CASE WHEN ${isArray} THEN ${fieldExpr} ELSE '[]' END) je WHERE CAST(je.value AS TEXT) = CAST(${valueExpr} AS TEXT)) > 0\n ELSE instr(CAST(${fieldExpr} AS TEXT), CAST(${valueExpr} AS TEXT)) > 0\n END)`;\n}\n\n// posix.basename(String(path)), suffix stripped Unix-style (sql-functions.ts's basenameImpl). No\n// last-'/' primitive exists, so reverse() finds the first '/' from the end, then reverse() restores order.\nfunction sqlBasename(pathExpr: string, suffixExpr: string): string {\n const trimmed = `rtrim(CAST(${pathExpr} AS TEXT), '/')`;\n const name = `CASE\n WHEN ${trimmed} = '' THEN ''\n WHEN instr(${trimmed}, '/') = 0 THEN ${trimmed}\n ELSE reverse(substr(reverse(${trimmed}), 1, instr(reverse(${trimmed}), '/') - 1))\n END`;\n return `(CASE WHEN ${pathExpr} IS NULL THEN NULL ELSE\n (CASE WHEN ${suffixExpr} IS NOT NULL AND CAST(${suffixExpr} AS TEXT) != '' AND CAST(${suffixExpr} AS TEXT) != (${name})\n AND substr((${name}), -length(CAST(${suffixExpr} AS TEXT))) = CAST(${suffixExpr} AS TEXT)\n THEN substr((${name}), 1, length((${name})) - length(CAST(${suffixExpr} AS TEXT)))\n ELSE (${name}) END)\n END)`;\n}\n\n// Rewrites every has()/basename() call into the portable SQL above, recursing into each call's own\n// arguments first so a nested `instr(basename(f.path), 'x')` resolves inside out; segment() instead raises STORE_CAPABILITY_MISSING, named, like an unsupported FTS5 operator.\nexport function rewriteFunctions(sql: string): string {\n if (!CALL_ANYWHERE.test(sql)) return sql;\n // Numbered once, over the whole statement, before any argument is duplicated below -- rewrite()\n // recurses over already-numbered substrings, never renumbering them a second time.\n return rewrite(numberPlaceholders(sql));\n}\n\nfunction rewrite(sql: string): string {\n let out = '';\n let i = 0;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n out += sql.slice(i, skip);\n i = skip;\n continue;\n }\n const m = !isWordChar(sql[i - 1]) ? CALL_AT_START.exec(sql.slice(i)) : null;\n if (m) {\n const name = m[1].toLowerCase();\n const parenStart = i + m[0].length - 1;\n if (name === 'segment') {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"turso\" does not implement segment() as a SQL function in this build (its client cannot register UDFs); rephrase the query without it, or set \"store\" to \"sqlite\" in this tree's config to run it as written`);\n }\n const { args, end } = readArgs(sql, parenStart);\n const rewrittenArgs = args.map(rewrite);\n if (name === 'has') {\n if (rewrittenArgs.length !== 2) throw new Error(`turso: has() takes exactly 2 arguments (field, value), got ${rewrittenArgs.length}`);\n out += sqlHas(rewrittenArgs[0], rewrittenArgs[1]);\n } else {\n if (rewrittenArgs.length < 1 || rewrittenArgs.length > 2) throw new Error(`turso: basename() takes 1 or 2 arguments (path, suffix?), got ${rewrittenArgs.length}`);\n out += sqlBasename(rewrittenArgs[0], rewrittenArgs[1] ?? 'NULL');\n }\n i = end;\n continue;\n }\n out += sql[i];\n i++;\n }\n return out;\n}\n"],"names":["SenseError","CALL_ANYWHERE","CALL_AT_START","isWordChar","ch","undefined","test","skipOpaque","sql","i","j","length","nl","indexOf","close","numberPlaceholders","out","n","skip","slice","readArgs","parenStart","args","depth","cur","push","trim","Error","end","sqlHas","fieldExpr","valueExpr","isArray","sqlBasename","pathExpr","suffixExpr","trimmed","name","rewriteFunctions","rewrite","m","exec","toLowerCase","rewrittenArgs","map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/ai/sensemaking/src/store/turso/sql-functions.ts"],"sourcesContent":["import { SenseError } from '../../errors.ts';\n\n// Turso can't register UDFs: has()/basename() are rewritten into portable SQL below; segment()\n// (Intl.Segmenter grapheme clustering, types.ts's 'segment' capability) has none, so it's rejected like an unsupported FTS5 operator.\n\n// Non-global: recursion (readArgs' contents rewritten via rewriteFunctions) reenters this module's\n// regexes before the outer scan resumes, and a global regex's lastIndex does not nest across that.\nconst CALL_ANYWHERE = /\\b(has|basename|segment)\\s*\\(/i;\nconst CALL_AT_START = /^(has|basename|segment)\\s*\\(/i;\n\nfunction isWordChar(ch: string | undefined): boolean {\n return ch !== undefined && /[A-Za-z0-9_]/.test(ch);\n}\n\n// The index just past the quoted literal or SQL comment starting at i, or null if i starts\n// neither. Shared by the scan and readArgs so both leave quoted/comment text opaque to a call match.\nfunction skipOpaque(sql: string, i: number): number | null {\n const ch = sql[i];\n if (ch === \"'\" || ch === '\"') {\n let j = i + 1;\n while (j < sql.length) {\n if (sql[j] === ch) {\n if (sql[j + 1] === ch) {\n j += 2;\n continue;\n }\n return j + 1;\n }\n j++;\n }\n return j; // unterminated literal: consume to end rather than loop forever\n }\n if (ch === '-' && sql[i + 1] === '-') {\n const nl = sql.indexOf('\\n', i);\n return nl === -1 ? sql.length : nl + 1;\n }\n if (ch === '/' && sql[i + 1] === '*') {\n const close = sql.indexOf('*/', i + 2);\n return close === -1 ? sql.length : close + 2;\n }\n return null;\n}\n\n// `?N`/named placeholders bind by number/name, so repeating one is safe; an anonymous `?` takes\n// the next slot per occurrence, so it's numbered here first -- sqlHas/sqlBasename duplicate their arguments.\nfunction numberPlaceholders(sql: string): string {\n let out = '';\n let i = 0;\n let n = 0;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n out += sql.slice(i, skip);\n i = skip;\n continue;\n }\n if (sql[i] === '?' && !/[0-9]/.test(sql[i + 1] ?? '')) {\n n++;\n out += `?${n}`;\n i++;\n continue;\n }\n out += sql[i];\n i++;\n }\n return out;\n}\n\n// The un-rewritten argument texts of the call whose '(' sits at parenStart, and the index just\n// past its matching ')'. Paren depth is tracked so a nested call's commas/parens aren't mistaken for ours.\nfunction readArgs(sql: string, parenStart: number): { args: string[]; end: number } {\n const args: string[] = [];\n let depth = 1;\n let cur = '';\n let i = parenStart + 1;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n cur += sql.slice(i, skip);\n i = skip;\n continue;\n }\n const ch = sql[i];\n if (ch === '(') {\n depth++;\n cur += ch;\n i++;\n continue;\n }\n if (ch === ')') {\n depth--;\n i++;\n if (depth === 0) break;\n cur += ch;\n continue;\n }\n if (ch === ',' && depth === 1) {\n args.push(cur.trim());\n cur = '';\n i++;\n continue;\n }\n cur += ch;\n i++;\n }\n if (depth !== 0) throw new Error(`turso: unterminated argument list starting at \"${sql.slice(parenStart, parenStart + 30)}...\"`);\n if (cur.trim() !== '') args.push(cur.trim());\n return { args, end: i };\n}\n\n// field.startsWith('[') && JSON.parse(field) is an array, matched by membership; every other\n// field, stringified, matched by substring (sql-functions.ts's hasImpl, mirrored branch for branch).\nfunction sqlHas(fieldExpr: string, valueExpr: string): string {\n const isArray = `substr(${fieldExpr},1,1) = '[' AND json_valid(${fieldExpr})`;\n // json_each's argument is re-guarded by its own CASE (falling back to '[]'): turso evaluates a\n // CASE branch's table function even when untaken, and a non-array argument there raises rather than yielding 0 rows (spike-verified).\n return `(CASE\n WHEN ${fieldExpr} IS NULL THEN 0\n WHEN ${isArray} THEN\n (SELECT COUNT(*) FROM json_each(CASE WHEN ${isArray} THEN ${fieldExpr} ELSE '[]' END) je WHERE CAST(je.value AS TEXT) = CAST(${valueExpr} AS TEXT)) > 0\n ELSE instr(CAST(${fieldExpr} AS TEXT), CAST(${valueExpr} AS TEXT)) > 0\n END)`;\n}\n\n// posix.basename(String(path)), suffix stripped Unix-style (sql-functions.ts's basenameImpl). No\n// last-'/' primitive exists, so reverse() finds the first '/' from the end, then reverse() restores order.\nfunction sqlBasename(pathExpr: string, suffixExpr: string): string {\n const trimmed = `rtrim(CAST(${pathExpr} AS TEXT), '/')`;\n const name = `CASE\n WHEN ${trimmed} = '' THEN ''\n WHEN instr(${trimmed}, '/') = 0 THEN ${trimmed}\n ELSE reverse(substr(reverse(${trimmed}), 1, instr(reverse(${trimmed}), '/') - 1))\n END`;\n return `(CASE WHEN ${pathExpr} IS NULL THEN NULL ELSE\n (CASE WHEN ${suffixExpr} IS NOT NULL AND CAST(${suffixExpr} AS TEXT) != '' AND CAST(${suffixExpr} AS TEXT) != (${name})\n AND substr((${name}), -length(CAST(${suffixExpr} AS TEXT))) = CAST(${suffixExpr} AS TEXT)\n THEN substr((${name}), 1, length((${name})) - length(CAST(${suffixExpr} AS TEXT)))\n ELSE (${name}) END)\n END)`;\n}\n\n// Rewrites every has()/basename() call into the portable SQL above, recursing into each call's own\n// arguments first so a nested `instr(basename(f.path), 'x')` resolves inside out; segment() instead raises STORE_CAPABILITY_MISSING, named, like an unsupported FTS5 operator.\nexport function rewriteFunctions(sql: string): string {\n if (!CALL_ANYWHERE.test(sql)) return sql;\n // Numbered once, over the whole statement, before any argument is duplicated below -- rewrite()\n // recurses over already-numbered substrings, never renumbering them a second time.\n return rewrite(numberPlaceholders(sql));\n}\n\nfunction rewrite(sql: string): string {\n let out = '';\n let i = 0;\n while (i < sql.length) {\n const skip = skipOpaque(sql, i);\n if (skip !== null) {\n out += sql.slice(i, skip);\n i = skip;\n continue;\n }\n const m = !isWordChar(sql[i - 1]) ? CALL_AT_START.exec(sql.slice(i)) : null;\n if (m) {\n const name = m[1].toLowerCase();\n const parenStart = i + m[0].length - 1;\n if (name === 'segment') {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"turso\" does not implement segment() as a SQL function in this build (its client cannot register UDFs); rephrase the query without it, or set \"store\" to \"sqlite\" in this tree's config to run it as written`);\n }\n const { args, end } = readArgs(sql, parenStart);\n const rewrittenArgs = args.map(rewrite);\n if (name === 'has') {\n if (rewrittenArgs.length !== 2) throw new Error(`turso: has() takes exactly 2 arguments (field, value), got ${rewrittenArgs.length}`);\n out += sqlHas(rewrittenArgs[0], rewrittenArgs[1]);\n } else {\n if (rewrittenArgs.length < 1 || rewrittenArgs.length > 2) throw new Error(`turso: basename() takes 1 or 2 arguments (path, suffix?), got ${rewrittenArgs.length}`);\n out += sqlBasename(rewrittenArgs[0], rewrittenArgs[1] ?? 'NULL');\n }\n i = end;\n continue;\n }\n out += sql[i];\n i++;\n }\n return out;\n}\n"],"names":["SenseError","CALL_ANYWHERE","CALL_AT_START","isWordChar","ch","undefined","test","skipOpaque","sql","i","j","length","nl","indexOf","close","numberPlaceholders","out","n","skip","slice","readArgs","parenStart","args","depth","cur","push","trim","Error","end","sqlHas","fieldExpr","valueExpr","isArray","sqlBasename","pathExpr","suffixExpr","trimmed","name","rewriteFunctions","rewrite","m","exec","toLowerCase","rewrittenArgs","map"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/open-source/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, VectorStore } from '../types.ts';\nimport { createNativeVectorScopeOwner, 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 'segment': has()/basename() resolve via SQL rewrite below (sql-functions.ts), but segment() has no SQL form and the client cannot register it as a UDF.\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['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, options: { observational?: boolean } = {}): Store {\n const vectorScope = createNativeVectorScopeOwner();\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 const vectors: VectorStore = {\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, vectorScope.activeFor(allowed)),\n similar: (path, opts) => scanSimilar(conn, STORE_DIMS, path, opts, vectorScope.activeFor(opts.allowed)),\n hasVector: (path) => hasVectorRow(conn, path),\n };\n vectorScope.bind(vectors);\n\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 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). Turso's PRAGMA busy_timeout names its column \"busy_timeout\" (spike-verified), not \"timeout\" like real SQLite.\n const row = (await (await conn.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 conn.prepare(sql);\n stmt.setReadBigInts(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 if (options.observational) {\n await db.close();\n return;\n }\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":["STORE_DIMS","getColumns","getMeta","setMeta","withTransaction","createNativeVectorScopeOwner","hasVectorRow","pendingRows","cacheFilePath","checkpointWal","fileSize","reclaimSpace","fieldStats","queryLexical","scanCandidates","scanSimilar","writeVectorBatch","CAPABILITIES","Set","BLOAT_FACTOR","createStore","db","conn","options","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","raw","stmt","setReadBigInts","iterate","params","close","observational","reclaimPath","base","Number","size","String","err","console","error","message"],"mappings":"AACA,SAASA,UAAU,QAAQ,uBAAuB;AAClD,SAASC,UAAU,EAAEC,OAAO,EAAEC,OAAO,QAAQ,eAAe;AAC5D,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,SAASC,4BAA4B,EAAEC,YAAY,EAAEC,WAAW,QAAQ,gBAAgB;AACxF,SAASC,aAAa,EAAEC,aAAa,EAAEC,QAAQ,EAAEC,YAAY,QAAQ,kBAAkB;AACvF,SAASC,UAAU,QAAQ,kBAAkB;AAC7C,SAASC,YAAY,QAAQ,eAAe;AAC5C,SAASC,cAAc,EAAEC,WAAW,EAAEC,gBAAgB,QAAQ,eAAe;AAE7E,6JAA6J;AAC7J,OAAO,MAAMC,eAAwC,IAAIC,IAAI;IAAC;CAAU,EAAE;AAE1E,0FAA0F;AAC1F,wFAAwF;AACxF,MAAMC,eAAe;AAErB,mGAAmG;AACnG,sEAAsE;AACtE,OAAO,SAASC,YAAYC,EAAY,EAAEC,IAAgB,EAAEC,UAAuC,CAAC,CAAC;IACnG,MAAMC,cAAcnB;IACpB,sFAAsF;IACtF,gIAAgI;IAChI,MAAMoB,UAAuB;QAC3BC,SAAS,IAAMnB,YAAYe;QAC3BK,cAAc,CAACC,OAASZ,iBAAiBM,MAAMtB,YAAY4B;QAC3DC,YAAY,CAACC,IAAIC,YAAYC,OAAOC,UAAYnB,eAAeQ,MAAMQ,IAAI9B,YAAYgC,OAAOC,SAAST,YAAYU,SAAS,CAACD;QAC3HE,SAAS,CAACC,MAAMC,OAAStB,YAAYO,MAAMtB,YAAYoC,MAAMC,MAAMb,YAAYU,SAAS,CAACG,KAAKJ,OAAO;QACrGK,WAAW,CAACF,OAAS9B,aAAagB,MAAMc;IAC1C;IACAZ,YAAYe,IAAI,CAACd;IAEjB,OAAO;QACLe,MAAM;QACNC,cAAcxB;QACd,MAAMyB,MAAKC,GAAW;YACpB,MAAMrB,KAAKoB,IAAI,CAACC;QAClB;QACA,MAAMC,SAAQD,GAAW;YACvB,OAAOrB,KAAKsB,OAAO,CAACD;QACtB;QACA,MAAME,UAASF,GAAW,EAAEG,SAAsB;YAChD,MAAMxB,KAAKuB,QAAQ,CAACF,KAAKG;QAC3B;QACA,MAAMC,aAAeC,EAAoB;YACvC,OAAO5C,gBAAgBkB,MAAM0B;QAC/B;QACAC,MAAM;YACJ,MAAMC;gBACJ,OAAO;uBAAK,MAAMjD,WAAWqB;iBAAO;YACtC;YACAV,YAAY,CAACsC,SAASC,aAAevC,WAAWU,MAAM4B,SAASC;QACjE;QACAC,SAAS;YACPC,OAAO,CAACC,OAAOjB,OAASxB,aAAaS,MAAMgC,OAAOjB;QACpD;QACAZ;QACA,MAAM8B;YACJ,qFAAqF;YACrF,sKAAsK;YACtK,MAAMC,MAAO,MAAM,AAAC,CAAA,MAAMlC,KAAKsB,OAAO,CAAC,sBAAqB,EAAGa,GAAG;YAClE,OAAO;gBAAEC,cAAc,GAAGF,IAAIE,YAAY,CAAC,kFAAkF,CAAC;YAAC;QACjI;QACAC,KAAK;YACH,MAAMf,SAAQD,GAAW;gBACvB,MAAMiB,OAAO,MAAMtC,KAAKsB,OAAO,CAACD;gBAChCiB,KAAKC,cAAc,CAAC,OAAO,gEAAgE;gBAC3F,OAAO;oBACLX,SAAS,IAAMU,KAAKV,OAAO;oBAC3B,8DAA8D;oBAC9DY,SAAS,gBAAiB,GAAGC,MAAiB;wBAC5C,OAAOH,KAAKE,OAAO,IAAIC;oBACzB;gBACF;YACF;QACF;QACA,MAAMC;YACJ,IAAIzC,QAAQ0C,aAAa,EAAE;gBACzB,MAAM5C,GAAG2C,KAAK;gBACd;YACF;YACA,4FAA4F;YAC5F,MAAMvD,cAAcY;YACpB,IAAI6C,cAA6B;YACjC,IAAI;oBAEmB;gBADrB,MAAM9B,OAAO,MAAM5B,cAAcc;gBACjC,MAAM6C,OAAOC,QAAQ,OAAA,MAAMlE,QAAQoB,MAAM,6BAApB,kBAAA,OAAwC;gBAC7D,MAAM+C,OAAOjC,SAAS,OAAO,OAAO,MAAM1B,SAAS0B;gBACnD,IAAIiC,SAAS,MAAM;oBACjB,yFAAyF;oBACzF,2FAA2F;oBAC3F,IAAIF,SAAS,GAAG;wBACd,MAAMhE,QAAQmB,MAAM,gBAAgBgD,OAAOD;wBAC3C,+EAA+E;wBAC/E,MAAM5D,cAAcY;oBACtB,OAAO,IAAIgD,OAAOlD,eAAegD,MAAM;wBACrCD,cAAc9B;oBAChB;gBACF;YACF,EAAE,OAAOmC,KAAK;gBACZC,QAAQC,KAAK,CAAC,CAAC,yFAAyF,EAAE,AAACF,IAAcG,OAAO,EAAE;YACpI;YACA,MAAMrD,GAAG2C,KAAK;YACd,qFAAqF;YACrF,+FAA+F;YAC/F,IAAIE,gBAAgB,MAAM,MAAMvD,aAAauD;QAC/C;IACF;AACF"}
|