sensemaking 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/cjs/config/access.d.cts +0 -1
- package/dist/cjs/config/access.d.ts +0 -1
- package/dist/cjs/config/access.js +10 -17
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +1 -1
- package/dist/cjs/config/index.d.ts +1 -1
- package/dist/cjs/config/index.js +0 -3
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/types.d.cts +0 -3
- package/dist/cjs/config/types.d.ts +0 -3
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +6 -24
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/links.js +4 -5
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/sections.js +4 -1
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/store/builder.d.cts +9 -1
- package/dist/cjs/store/builder.d.ts +9 -1
- package/dist/cjs/store/builder.js +593 -2
- package/dist/cjs/store/builder.js.map +1 -1
- package/dist/cjs/store/duckdb/open.js +7 -1
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js +30 -5
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/embed-scope.d.cts +2 -0
- package/dist/cjs/store/embed-scope.d.ts +2 -0
- package/dist/cjs/store/embed-scope.js +85 -0
- package/dist/cjs/store/embed-scope.js.map +1 -0
- package/dist/cjs/store/feature-scope.d.cts +8 -0
- package/dist/cjs/store/feature-scope.d.ts +8 -0
- package/dist/cjs/store/feature-scope.js +107 -0
- package/dist/cjs/store/feature-scope.js.map +1 -0
- package/dist/cjs/store/open.js +193 -101
- package/dist/cjs/store/open.js.map +1 -1
- package/dist/cjs/store/preset-scope.d.cts +3 -0
- package/dist/cjs/store/preset-scope.d.ts +3 -0
- package/dist/cjs/store/preset-scope.js +293 -0
- package/dist/cjs/store/preset-scope.js.map +1 -0
- package/dist/cjs/store/reconcile.d.cts +1 -1
- package/dist/cjs/store/reconcile.d.ts +1 -1
- package/dist/cjs/store/reconcile.js +172 -178
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/signature.js +7 -5
- package/dist/cjs/store/signature.js.map +1 -1
- package/dist/cjs/store/sqlite/lexical.d.cts +1 -1
- package/dist/cjs/store/sqlite/lexical.d.ts +1 -1
- package/dist/cjs/store/sqlite/lexical.js +2 -2
- package/dist/cjs/store/sqlite/lexical.js.map +1 -1
- package/dist/cjs/store/sqlite/open.d.cts +0 -1
- package/dist/cjs/store/sqlite/open.d.ts +0 -1
- package/dist/cjs/store/sqlite/open.js +14 -231
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/sqlite/reconcile.d.cts +1 -1
- package/dist/cjs/store/sqlite/reconcile.d.ts +1 -1
- package/dist/cjs/store/sqlite/reconcile.js +78 -16
- package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
- package/dist/cjs/store/sqlite/sql-functions.d.cts +1 -1
- package/dist/cjs/store/sqlite/sql-functions.d.ts +1 -1
- package/dist/cjs/store/sqlite/sql-functions.js +3 -3
- package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
- package/dist/cjs/store/sqlite/store.d.cts +1 -2
- package/dist/cjs/store/sqlite/store.d.ts +1 -2
- package/dist/cjs/store/sqlite/store.js +2 -3
- package/dist/cjs/store/sqlite/store.js.map +1 -1
- package/dist/cjs/store/turso/open.js +6 -0
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/reconcile.js +72 -3
- package/dist/cjs/store/turso/reconcile.js.map +1 -1
- package/dist/cjs/store/types.d.cts +2 -4
- package/dist/cjs/store/types.d.ts +2 -4
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/esm/config/access.d.ts +0 -1
- package/dist/esm/config/access.js +12 -17
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +1 -1
- package/dist/esm/config/index.js +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/types.d.ts +0 -3
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +6 -22
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/links.js +3 -4
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/sections.js +4 -1
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/store/builder.d.ts +9 -1
- package/dist/esm/store/builder.js +152 -3
- package/dist/esm/store/builder.js.map +1 -1
- package/dist/esm/store/duckdb/open.js +5 -1
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js +11 -3
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/embed-scope.d.ts +2 -0
- package/dist/esm/store/embed-scope.js +28 -0
- package/dist/esm/store/embed-scope.js.map +1 -0
- package/dist/esm/store/feature-scope.d.ts +8 -0
- package/dist/esm/store/feature-scope.js +49 -0
- package/dist/esm/store/feature-scope.js.map +1 -0
- package/dist/esm/store/open.js +78 -28
- package/dist/esm/store/open.js.map +1 -1
- package/dist/esm/store/preset-scope.d.ts +3 -0
- package/dist/esm/store/preset-scope.js +77 -0
- package/dist/esm/store/preset-scope.js.map +1 -0
- package/dist/esm/store/reconcile.d.ts +1 -1
- package/dist/esm/store/reconcile.js +30 -5
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/signature.js +7 -5
- package/dist/esm/store/signature.js.map +1 -1
- package/dist/esm/store/sqlite/lexical.d.ts +1 -1
- package/dist/esm/store/sqlite/lexical.js +4 -4
- package/dist/esm/store/sqlite/lexical.js.map +1 -1
- package/dist/esm/store/sqlite/open.d.ts +0 -1
- package/dist/esm/store/sqlite/open.js +14 -88
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/sqlite/reconcile.d.ts +1 -1
- package/dist/esm/store/sqlite/reconcile.js +14 -14
- package/dist/esm/store/sqlite/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/sql-functions.d.ts +1 -1
- package/dist/esm/store/sqlite/sql-functions.js +3 -3
- package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
- package/dist/esm/store/sqlite/store.d.ts +1 -2
- package/dist/esm/store/sqlite/store.js +2 -3
- package/dist/esm/store/sqlite/store.js.map +1 -1
- package/dist/esm/store/turso/open.js +4 -0
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/reconcile.js +6 -2
- package/dist/esm/store/turso/reconcile.js.map +1 -1
- package/dist/esm/store/types.d.ts +2 -4
- package/dist/esm/store/types.js.map +1 -1
- package/package.json +3 -2
- package/schema.json +0 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/reconcile.ts"],"sourcesContent":["import type { Config } from '../config/index.ts';\nimport { activeFeatures } from '../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../features/types.ts';\nimport { progress } from '../output/progress.ts';\nimport { listFiles, RESERVED_COLUMNS } from '../scan/index.ts';\nimport type { ParsePool } from '../scan/pool.ts';\nimport { reparseFiles } from '../scan/reparse.ts';\nimport { getColumns, quoteIdent } from './shared.ts';\nimport { withTransaction } from './transaction.ts';\nimport type { Connection, ReconcileDialect } from './types.ts';\n\n// One reconcile algorithm shared by every store, parameterised by a per-engine ReconcileDialect\n// (types.ts). Ordering is universal, not a dialect concern: ALTER, then the frontmatter upsert,\n// then reconcileContent, then preset_files, then the vanished-frontmatter delete, then feature hooks\n// -- a vanished path's content delete (inside reconcileContent) must precede its frontmatter\n// delete, since sqlite's delete SQL resolves the row via its frontmatter rowid.\n\n// Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last\n// computed value on every touch, not just the reconciles that recompute it.\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string, dialect: ReconcileDialect, pool?: ParsePool): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short\n // reconciles stay silent (progress() has a threshold).\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick, { pool });\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n // Fence before ALTERing: a store's own failure past this point is a raw, engine-specific\n // error with no indication of the boundary or the levers -- dialect.checkColumnLimit names both.\n dialect.checkColumnLimit(allColumns.length);\n // Columns the frontmatter upsert actually writes: core + parsed frontmatter keys, never a\n // feature-owned reserved column (see CORE_FRONTMATTER_COLUMNS above).\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n // ON CONFLICT DO UPDATE (not OR REPLACE) keeps the row's rowid stable across reparses --\n // sqlite's content rows are coupled to that rowid.\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n // Paths whose content (and, per feature, other rows) need clearing: gone entirely, or about to\n // be reinserted fresh. Disjoint from `added`, which has nothing to clear.\n const touched = [...vanished, ...reparsedExisting];\n\n const txStart = Date.now();\n await withTransaction(\n conn,\n async () => {\n // Re-read inside the write transaction: newColumns came from a read taken before it opened, so a\n // concurrent reconcile may have added some of them since. ALTER has no IF NOT EXISTS.\n const present = await getColumns(conn);\n for (const col of newColumns) if (!present.has(col)) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}${dialect.columnTypeSuffix()}`);\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n }\n\n // After the upsert, so every doc already has the frontmatter row sqlite's content rowid\n // couples to. ON CONFLICT DO UPDATE preserves that rowid, so a reparse keeps its identity.\n await dialect.reconcileContent(conn, touched, parsedDocs, delta, cfg);\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n if (touched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n touched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n // Before the feature hooks, never after: rank's afterReconcile reads frontmatter as PageRank's\n // node set, so a lingering vanished row would dilute rank mass across every surviving note.\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n if (touched.length > 0) for (const feature of features) await feature.remove?.(conn, touched, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n },\n dialect.beginMode()\n );\n\n const durationMs = Date.now() - txStart;\n if (dialect.recordDuration) await dialect.recordDuration(conn, durationMs);\n\n return { parsed: parsedDocs.length, warnings };\n}\n"],"names":["reconcile","CORE_FRONTMATTER_COLUMNS","Set","conn","cfg","baseDir","dialect","pool","files","currentSet","existingStmt","existingRows","existing","vanished","toReparse","features","seenColumns","report","parsedDocs","warnings","newColumns","col","allColumns","writableColumns","insertSql","added","delta","addedSet","reparsedExisting","touched","txStart","durationMs","listFiles","map","f","relPath","prepare","all","Map","r","path","filter","has","row","get","_mtime","mtimeMs","_size","size","length","parsed","activeFeatures","getColumns","progress","reparseFiles","tick","docs","finish","add","checkColumnLimit","c","RESERVED_COLUMNS","quoteIdent","join","reparsed","d","p","Date","now","withTransaction","feature","present","rows","presetRows","doc","presetName","exec","columnTypeSuffix","ctimeMs","parseError","data","runBatch","reconcileContent","presets","push","remove","docsForFeature","extracted","name","store","afterReconcile","beginMode","recordDuration"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/reconcile.ts"],"sourcesContent":["import type { Config } from '../config/index.ts';\nimport { activeFeatures } from '../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../features/types.ts';\nimport { progress } from '../output/progress.ts';\nimport { listFiles, RESERVED_COLUMNS } from '../scan/index.ts';\nimport type { ParsePool } from '../scan/pool.ts';\nimport { reparseFiles } from '../scan/reparse.ts';\nimport { getColumns, quoteIdent } from './shared.ts';\nimport { withTransaction } from './transaction.ts';\nimport type { Connection, ReconcileDialect } from './types.ts';\n\n// One reconcile algorithm shared by every store, parameterised by a per-engine ReconcileDialect\n// (types.ts). Ordering is universal, not a dialect concern: ALTER, then the frontmatter upsert,\n// then reconcileContent, then preset_files, then the vanished-frontmatter delete, then feature hooks\n// -- a vanished path's content delete (inside reconcileContent) must precede its frontmatter\n// delete, since sqlite's delete SQL resolves the row via its frontmatter rowid.\n\n// Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last\n// computed value on every touch, not just the reconciles that recompute it.\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string, dialect: ReconcileDialect, pool?: ParsePool, forcedPaths?: ReadonlySet<string>): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n // A path whose coverage moved between presets (forcedPaths) but is no longer covered at all is\n // already caught below by !currentSet.has, since it can only be forced by having existed under\n // an old preset's match, which means it was reconciled into `existing` already.\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n // forcedPaths treats an unchanged file as touched because its preset coverage moved, not its\n // stamp -- reconcile still owns add/update/remove and every cross-feature cascade for it.\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size || (forcedPaths?.has(f.relPath) ?? false);\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short\n // reconciles stay silent (progress() has a threshold).\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = await reparseFiles(toReparse, features, cfg, seenColumns, report.tick, { pool });\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n // Fence before ALTERing: a store's own failure past this point is a raw, engine-specific\n // error with no indication of the boundary or the levers -- dialect.checkColumnLimit names both.\n dialect.checkColumnLimit(allColumns.length);\n // Columns the frontmatter upsert actually writes: core + parsed frontmatter keys, never a\n // feature-owned reserved column (see CORE_FRONTMATTER_COLUMNS above).\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n // ON CONFLICT DO UPDATE (not OR REPLACE) keeps the row's rowid stable across reparses --\n // sqlite's content rows are coupled to that rowid.\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n // Paths whose content (and, per feature, other rows) need clearing: gone entirely, or about to\n // be reinserted fresh. Disjoint from `added`, which has nothing to clear.\n const touched = [...vanished, ...reparsedExisting];\n\n const txStart = Date.now();\n await withTransaction(\n conn,\n async () => {\n // Re-read inside the write transaction: newColumns came from a read taken before it opened, so a\n // concurrent reconcile may have added some of them since. ALTER has no IF NOT EXISTS.\n const present = await getColumns(conn);\n const missingColumns = newColumns.filter((col) => !present.has(col));\n if (missingColumns.length > 0) await dialect.addColumns(conn, missingColumns);\n\n // Revalidated once the lock is held, before this process's own frontmatter upsert below:\n // `added` came from a path read taken before this transaction's lock, so a path still\n // called \"added\" here may already have a content row from a concurrent reconcile that\n // committed while this one waited. One SELECT, not a DELETE per added file -- with no\n // contention it returns the same set `existing` already ruled out, so nothing extra clears.\n let contentTouched = touched;\n if (added.length > 0) {\n const currentPathsStmt = await conn.prepare('SELECT \"path\" FROM frontmatter');\n const currentPaths = new Set(((await currentPathsStmt.all()) as Array<{ path: string }>).map((r) => r.path));\n const staleAdded = added.filter((p) => currentPaths.has(p));\n if (staleAdded.length > 0) contentTouched = [...touched, ...staleAdded];\n }\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n }\n\n // After the upsert, so every doc already has the frontmatter row sqlite's content rowid\n // couples to. ON CONFLICT DO UPDATE preserves that rowid, so a reparse keeps its identity.\n await dialect.reconcileContent(conn, contentTouched, parsedDocs, delta, cfg);\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n if (touched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n touched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n // DO NOTHING, not a bare INSERT: the added/touched split above comes from a read taken\n // before this transaction's lock, so a path this process calls \"added\" can already have\n // its (path, preset) row committed by a concurrent reconcile -- the row would be identical either way.\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?) ON CONFLICT(\"path\", preset) DO NOTHING', presetRows);\n\n // Before the feature hooks, never after: rank's afterReconcile reads frontmatter as PageRank's\n // node set, so a lingering vanished row would dilute rank mass across every surviving note.\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n if (touched.length > 0) for (const feature of features) await feature.remove?.(conn, touched, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n },\n dialect.beginMode()\n );\n\n const durationMs = Date.now() - txStart;\n if (dialect.recordDuration) await dialect.recordDuration(conn, durationMs);\n\n return { parsed: parsedDocs.length, warnings };\n}\n"],"names":["reconcile","CORE_FRONTMATTER_COLUMNS","Set","conn","cfg","baseDir","dialect","pool","forcedPaths","files","currentSet","existingStmt","existingRows","existing","vanished","toReparse","features","seenColumns","report","parsedDocs","warnings","newColumns","col","allColumns","writableColumns","insertSql","added","delta","addedSet","reparsedExisting","touched","txStart","durationMs","listFiles","map","f","relPath","prepare","all","Map","r","path","filter","has","row","get","_mtime","mtimeMs","_size","size","length","parsed","activeFeatures","getColumns","progress","reparseFiles","tick","docs","finish","add","checkColumnLimit","c","RESERVED_COLUMNS","quoteIdent","join","reparsed","d","p","Date","now","withTransaction","feature","present","missingColumns","contentTouched","currentPathsStmt","currentPaths","staleAdded","rows","presetRows","doc","presetName","addColumns","ctimeMs","parseError","data","runBatch","reconcileContent","presets","push","remove","docsForFeature","extracted","name","store","afterReconcile","beginMode","recordDuration"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Feature-signature comparison for open()'s rebuild-vs-adopt decision (config.featureSignature's
|
|
2
|
-
// format:
|
|
3
|
-
// comparison, no SQL, shared by every store's open dialect.
|
|
4
|
-
// Segment keys that moved between two feature signatures.
|
|
2
|
+
// format: one `feature:<name>:on|off` segment per feature, embed provider, then one segment per
|
|
3
|
+
// preset). Pure string comparison, no SQL, shared by every store's open dialect.
|
|
4
|
+
// Segment keys that moved between two feature signatures. `preset:` and `feature:` segments key
|
|
5
|
+
// on their first two colon-separated parts (e.g. `preset:default`, `feature:tags`) so a change to
|
|
6
|
+
// one preset or one feature is independently detectable; every other segment keys on its prefix alone.
|
|
5
7
|
"use strict";
|
|
6
8
|
Object.defineProperty(exports, "__esModule", {
|
|
7
9
|
value: true
|
|
@@ -85,7 +87,7 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
85
87
|
}
|
|
86
88
|
function changedSignatureKeys(before, after) {
|
|
87
89
|
var keyOf = function keyOf(part) {
|
|
88
|
-
return part.startsWith('preset:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0];
|
|
90
|
+
return part.startsWith('preset:') || part.startsWith('feature:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0];
|
|
89
91
|
};
|
|
90
92
|
var parse = function parse(sig) {
|
|
91
93
|
return new Map(sig.split('|').map(function(part) {
|
|
@@ -155,7 +157,7 @@ function embedIdentityAdopted(before, after) {
|
|
|
155
157
|
function signatureDiff(before, after) {
|
|
156
158
|
var changed = changedSignatureKeys(before, after);
|
|
157
159
|
var label = function label(key) {
|
|
158
|
-
return key === 'embed' ? 'embed settings' : key
|
|
160
|
+
return key === 'embed' ? 'embed settings' : key.startsWith('preset:') ? 'preset "'.concat(key.slice(7), '"') : key.startsWith('feature:') ? 'feature "'.concat(key.slice(8), '"') : 'features';
|
|
159
161
|
};
|
|
160
162
|
return changed.size === 0 ? 'features' : _to_consumable_array(changed).map(label).join(', ');
|
|
161
163
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/signature.ts"],"sourcesContent":["// Feature-signature comparison for open()'s rebuild-vs-adopt decision (config.featureSignature's\n// format:
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/signature.ts"],"sourcesContent":["// Feature-signature comparison for open()'s rebuild-vs-adopt decision (config.featureSignature's\n// format: one `feature:<name>:on|off` segment per feature, embed provider, then one segment per\n// preset). Pure string comparison, no SQL, shared by every store's open dialect.\n\n// Segment keys that moved between two feature signatures. `preset:` and `feature:` segments key\n// on their first two colon-separated parts (e.g. `preset:default`, `feature:tags`) so a change to\n// one preset or one feature is independently detectable; every other segment keys on its prefix alone.\nexport function changedSignatureKeys(before: string, after: string): Set<string> {\n const keyOf = (part: string) => (part.startsWith('preset:') || part.startsWith('feature:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0]);\n const parse = (sig: string) => new Map(sig.split('|').map((part) => [keyOf(part), part]));\n const a = parse(before);\n const b = parse(after);\n const changed = new Set<string>();\n for (const [key, val] of b) if (a.get(key) !== val) changed.add(key);\n for (const key of a.keys()) if (!b.has(key)) changed.add(key);\n return changed;\n}\n\n// Whether the embed segment only gained its resolved weight identity: same provider and\n// model, no identity recorded before, one now -- adopted into meta without a rebuild.\nexport function embedIdentityAdopted(before: string, after: string): boolean {\n const embedPart = (sig: string) => sig.split('|').find((p) => p.startsWith('embed:'));\n const b = embedPart(before);\n const a = embedPart(after);\n if (b === undefined || a === undefined) return false;\n const at = a.indexOf('@');\n return b.indexOf('@') === -1 && at !== -1 && a.slice(0, at) === b;\n}\n\n// Names what moved, for the rebuild notice.\nexport function signatureDiff(before: string, after: string): string {\n const changed = changedSignatureKeys(before, after);\n const label = (key: string) => (key === 'embed' ? 'embed settings' : key.startsWith('preset:') ? `preset \"${key.slice(7)}\"` : key.startsWith('feature:') ? `feature \"${key.slice(8)}\"` : 'features');\n return changed.size === 0 ? 'features' : [...changed].map(label).join(', ');\n}\n"],"names":["changedSignatureKeys","embedIdentityAdopted","signatureDiff","before","after","keyOf","part","startsWith","split","slice","join","parse","sig","Map","map","a","b","changed","Set","key","val","get","add","keys","has","embedPart","find","p","undefined","at","indexOf","label","size"],"mappings":"AAAA,iGAAiG;AACjG,gGAAgG;AAChG,iFAAiF;AAEjF,gGAAgG;AAChG,kGAAkG;AAClG,uGAAuG;;;;;;;;;;;;QACvFA;eAAAA;;QAaAC;eAAAA;;QAUAC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAvBT,SAASF,qBAAqBG,MAAc,EAAEC,KAAa;IAChE,IAAMC,QAAQ,eAACC;eAAkBA,KAAKC,UAAU,CAAC,cAAcD,KAAKC,UAAU,CAAC,cAAcD,KAAKE,KAAK,CAAC,KAAKC,KAAK,CAAC,GAAG,GAAGC,IAAI,CAAC,OAAOJ,KAAKE,KAAK,CAAC,IAAI,CAAC,EAAE;;IACvJ,IAAMG,QAAQ,eAACC;eAAgB,IAAIC,IAAID,IAAIJ,KAAK,CAAC,KAAKM,GAAG,CAAC,SAACR;mBAAS;gBAACD,MAAMC;gBAAOA;aAAK;;;IACvF,IAAMS,IAAIJ,MAAMR;IAChB,IAAMa,IAAIL,MAAMP;IAChB,IAAMa,UAAU,IAAIC;QACf,kCAAA,2BAAA;;QAAL,QAAK,YAAoBF,sBAApB,SAAA,6BAAA,QAAA,yBAAA;YAAA,mCAAA,iBAAOG,sBAAKC;YAAW,IAAIL,EAAEM,GAAG,CAACF,SAASC,KAAKH,QAAQK,GAAG,CAACH;;;QAA3D;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;QACA,mCAAA,4BAAA;;QAAL,QAAK,aAAaJ,EAAEQ,IAAI,uBAAnB,UAAA,8BAAA,SAAA,0BAAA;YAAA,IAAMJ,OAAN;YAAuB,IAAI,CAACH,EAAEQ,GAAG,CAACL,OAAMF,QAAQK,GAAG,CAACH;;;QAApD;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IACL,OAAOF;AACT;AAIO,SAAShB,qBAAqBE,MAAc,EAAEC,KAAa;IAChE,IAAMqB,YAAY,mBAACb;eAAgBA,IAAIJ,KAAK,CAAC,KAAKkB,IAAI,CAAC,SAACC;mBAAMA,EAAEpB,UAAU,CAAC;;;IAC3E,IAAMS,IAAIS,UAAUtB;IACpB,IAAMY,IAAIU,UAAUrB;IACpB,IAAIY,MAAMY,aAAab,MAAMa,WAAW,OAAO;IAC/C,IAAMC,KAAKd,EAAEe,OAAO,CAAC;IACrB,OAAOd,EAAEc,OAAO,CAAC,SAAS,CAAC,KAAKD,OAAO,CAAC,KAAKd,EAAEN,KAAK,CAAC,GAAGoB,QAAQb;AAClE;AAGO,SAASd,cAAcC,MAAc,EAAEC,KAAa;IACzD,IAAMa,UAAUjB,qBAAqBG,QAAQC;IAC7C,IAAM2B,QAAQ,eAACZ;eAAiBA,QAAQ,UAAU,mBAAmBA,IAAIZ,UAAU,CAAC,aAAa,AAAC,WAAuB,OAAbY,IAAIV,KAAK,CAAC,IAAG,OAAKU,IAAIZ,UAAU,CAAC,cAAc,AAAC,YAAwB,OAAbY,IAAIV,KAAK,CAAC,IAAG,OAAK;;IACzL,OAAOQ,QAAQe,IAAI,KAAK,IAAI,aAAa,AAAC,qBAAGf,SAASH,GAAG,CAACiB,OAAOrB,IAAI,CAAC;AACxE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Connection, LexicalHit, LexicalQueryOptions } from '../types.js';
|
|
2
|
-
export declare function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions
|
|
2
|
+
export declare function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Connection, LexicalHit, LexicalQueryOptions } from '../types.js';
|
|
2
|
-
export declare function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions
|
|
2
|
+
export declare function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]>;
|
|
@@ -138,14 +138,14 @@ function _ts_generator(thisArg, body) {
|
|
|
138
138
|
// (benchmark/reports/2026-08-23-hub-release-battery.md); past this bound, hit stays NULL for the caller's JS excerpt fallback.
|
|
139
139
|
var SNIPPET_BOUND = 16384;
|
|
140
140
|
var WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';
|
|
141
|
-
function queryLexical(conn, terms, opts
|
|
141
|
+
function queryLexical(conn, terms, opts) {
|
|
142
142
|
return _async_to_generator(function() {
|
|
143
143
|
var whereJoin, whereCond, scopeCond, limit, query, sql, stmt;
|
|
144
144
|
return _ts_generator(this, function(_state) {
|
|
145
145
|
switch(_state.label){
|
|
146
146
|
case 0:
|
|
147
147
|
whereJoin = opts.whereJoin, whereCond = opts.whereCond, scopeCond = opts.scopeCond, limit = opts.limit;
|
|
148
|
-
query =
|
|
148
|
+
query = (0, _segmentts.segmentMatch)(terms);
|
|
149
149
|
// A run of pure unspaced-script punctuation segments to nothing searchable: an empty MATCH
|
|
150
150
|
// string is an FTS5 syntax error, not zero rows, so return zero rows directly rather than issuing it.
|
|
151
151
|
if (query.trim() === '') return [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/lexical.ts"],"sourcesContent":["import { segmentMatch } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\n// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc\n// (benchmark/reports/2026-08-23-hub-release-battery.md); past this bound, hit stays NULL for the caller's JS excerpt fallback.\nconst SNIPPET_BOUND = 16_384;\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';\n\n// Ranked BM25 word-match query with excerpt, scoped by the caller-built SQL fragments.
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/lexical.ts"],"sourcesContent":["import { segmentMatch } from '../../text/segment.ts';\nimport type { Connection, LexicalHit, LexicalQueryOptions } from '../types.ts';\n\n// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc\n// (benchmark/reports/2026-08-23-hub-release-battery.md); past this bound, hit stays NULL for the caller's JS excerpt fallback.\nconst SNIPPET_BOUND = 16_384;\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';\n\n// Ranked BM25 word-match query with excerpt, scoped by the caller-built SQL fragments. An\n// unspaced-script run in `terms` becomes a quoted grapheme phrase against the `_seg` sidecars.\nexport async function queryLexical(conn: Connection, terms: string, opts: LexicalQueryOptions): Promise<LexicalHit[]> {\n const { whereJoin, whereCond, scopeCond, limit } = opts;\n const query = segmentMatch(terms);\n // A run of pure unspaced-script punctuation segments to nothing searchable: an empty MATCH\n // string is an FTS5 syntax error, not zero rows, so return zero rows directly rather than issuing it.\n if (query.trim() === '') return [];\n const sql = `SELECT content.path AS path, CASE WHEN length(content.text) <= ${SNIPPET_BOUND} THEN snippet(content, 2, '«', '»', '…', 10) ELSE NULL END AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ${scopeCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${limit}`;\n const stmt = await conn.prepare(sql);\n return (await stmt.all(query)) as unknown as LexicalHit[];\n}\n"],"names":["queryLexical","SNIPPET_BOUND","WEIGHTED_BM25","conn","terms","opts","whereJoin","whereCond","scopeCond","limit","query","sql","stmt","segmentMatch","trim","prepare","all"],"mappings":";;;;+BAUsBA;;;eAAAA;;;yBAVO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG7B,iFAAiF;AACjF,+HAA+H;AAC/H,IAAMC,gBAAgB;AACtB,IAAMC,gBAAgB;AAIf,SAAeF,aAAaG,IAAgB,EAAEC,KAAa,EAAEC,IAAyB;;YACnFC,WAAWC,WAAWC,WAAWC,OACnCC,OAIAC,KACAC;;;;oBANEN,YAA2CD,KAA3CC,WAAWC,YAAgCF,KAAhCE,WAAWC,YAAqBH,KAArBG,WAAWC,QAAUJ,KAAVI;oBACnCC,QAAQG,IAAAA,uBAAY,EAACT;oBAC3B,2FAA2F;oBAC3F,sGAAsG;oBACtG,IAAIM,MAAMI,IAAI,OAAO,IAAI;;;;oBACnBH,MAAM,AAAC,kEAAgKL,OAA/FL,eAAc,yFAAoHM,OAAnCD,WAAU,2BAAsCE,OAAbD,WAAU,KAAyBL,OAAtBM,WAAU,cAAmCC,OAAvBP,eAAc,WAAe,OAANO;oBAC7P;;wBAAMN,KAAKY,OAAO,CAACJ;;;oBAA1BC,OAAO;oBACL;;wBAAMA,KAAKI,GAAG,CAACN;;;oBAAvB;;wBAAQ;;;;IACV"}
|
|
@@ -7,7 +7,6 @@ export declare const SCHEMA_VERSION = "19";
|
|
|
7
7
|
export type { OpenResult };
|
|
8
8
|
interface SqliteHandle {
|
|
9
9
|
db: DatabaseSync;
|
|
10
|
-
tokenize: string;
|
|
11
10
|
}
|
|
12
11
|
export declare const sqliteOpenDialect: OpenDialect<SqliteHandle>;
|
|
13
12
|
export declare function openSqlite(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
@@ -7,7 +7,6 @@ export declare const SCHEMA_VERSION = "19";
|
|
|
7
7
|
export type { OpenResult };
|
|
8
8
|
interface SqliteHandle {
|
|
9
9
|
db: DatabaseSync;
|
|
10
|
-
tokenize: string;
|
|
11
10
|
}
|
|
12
11
|
export declare const sqliteOpenDialect: OpenDialect<SqliteHandle>;
|
|
13
12
|
export declare function openSqlite(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
@@ -26,9 +26,7 @@ _export(exports, {
|
|
|
26
26
|
});
|
|
27
27
|
var _nodesqlite = require("node:sqlite");
|
|
28
28
|
var _indexts = require("../../config/index.js");
|
|
29
|
-
var _errorsts = require("../../errors.js");
|
|
30
29
|
var _indexts1 = require("../../features/index.js");
|
|
31
|
-
var _indexts2 = require("../../scan/index.js");
|
|
32
30
|
var _opents = require("../open.js");
|
|
33
31
|
var _sharedts = require("../shared.js");
|
|
34
32
|
var _transactionts = require("../transaction.js");
|
|
@@ -36,14 +34,6 @@ var _connectionts = require("./connection.js");
|
|
|
36
34
|
var _reconcilets = require("./reconcile.js");
|
|
37
35
|
var _sqlfunctionsts = require("./sql-functions.js");
|
|
38
36
|
var _storets = require("./store.js");
|
|
39
|
-
function _array_like_to_array(arr, len) {
|
|
40
|
-
if (len == null || len > arr.length) len = arr.length;
|
|
41
|
-
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
42
|
-
return arr2;
|
|
43
|
-
}
|
|
44
|
-
function _array_without_holes(arr) {
|
|
45
|
-
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
46
|
-
}
|
|
47
37
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
48
38
|
try {
|
|
49
39
|
var info = gen[key](arg);
|
|
@@ -70,17 +60,6 @@ function _async_to_generator(fn) {
|
|
|
70
60
|
});
|
|
71
61
|
};
|
|
72
62
|
}
|
|
73
|
-
function _iterable_to_array(iter) {
|
|
74
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
|
|
75
|
-
return Array.from(iter);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function _non_iterable_spread() {
|
|
79
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
80
|
-
}
|
|
81
|
-
function _to_consumable_array(arr) {
|
|
82
|
-
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
83
|
-
}
|
|
84
63
|
function _ts_generator(thisArg, body) {
|
|
85
64
|
var f, y, t, _ = {
|
|
86
65
|
label: 0,
|
|
@@ -180,52 +159,21 @@ function _ts_generator(thisArg, body) {
|
|
|
180
159
|
};
|
|
181
160
|
}
|
|
182
161
|
}
|
|
183
|
-
function _unsupported_iterable_to_array(o, minLen) {
|
|
184
|
-
if (!o) return;
|
|
185
|
-
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
186
|
-
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
187
|
-
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
188
|
-
if (n === "Map" || n === "Set") return Array.from(n);
|
|
189
|
-
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
190
|
-
}
|
|
191
162
|
var DB_FILENAME = 'cache.db';
|
|
192
163
|
var SCHEMA_VERSION = '19';
|
|
193
164
|
// unicode61 splits on spaces, so a language written without them indexes a whole run as one
|
|
194
|
-
// token and word search finds nothing; `
|
|
195
|
-
var
|
|
196
|
-
// FTS5 takes its tokenizer as a DDL string literal where nothing can bind, so the configured
|
|
197
|
-
// value is concatenated; probing a throwaway table first is both the safety and the validation.
|
|
198
|
-
function resolveTokenize(db, cfg) {
|
|
199
|
-
var configured = (0, _indexts.contentTokenize)(cfg);
|
|
200
|
-
if (configured === undefined) return DEFAULT_TOKENIZE;
|
|
201
|
-
var literal = configured.replace(/'/g, "''");
|
|
202
|
-
try {
|
|
203
|
-
db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');
|
|
204
|
-
db.exec("CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '".concat(literal, "')"));
|
|
205
|
-
db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');
|
|
206
|
-
} catch (err) {
|
|
207
|
-
throw new _errorsts.SenseError('CONFIG_INVALID', 'content.tokenize "'.concat(configured, '" is not a tokenizer this SQLite accepts (').concat(err.message, "); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options"));
|
|
208
|
-
}
|
|
209
|
-
return literal;
|
|
210
|
-
}
|
|
211
|
-
// The tokenizer the content table was actually built with, from its own DDL -- the one
|
|
212
|
-
// record that cannot desynchronize from the table. NULL when the table does not exist yet.
|
|
213
|
-
function storedTokenize(db) {
|
|
214
|
-
var row = db.prepare("SELECT sql FROM sqlite_master WHERE name = 'content'").get();
|
|
215
|
-
if (!row) return null;
|
|
216
|
-
var m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);
|
|
217
|
-
return m ? m[1] : null;
|
|
218
|
-
}
|
|
165
|
+
// token and word search finds nothing; the `_seg` sidecars (always populated) cover that case.
|
|
166
|
+
var TOKENIZE = 'porter unicode61';
|
|
219
167
|
// The `_seg` sidecars are appended after path, never inserted: bm25() and snippet(content, 2)
|
|
220
168
|
// are documented against the first three columns. Each holds its field's exploded unspaced runs.
|
|
221
|
-
function createContentTable(conn
|
|
169
|
+
function createContentTable(conn) {
|
|
222
170
|
return _async_to_generator(function() {
|
|
223
171
|
return _ts_generator(this, function(_state) {
|
|
224
172
|
switch(_state.label){
|
|
225
173
|
case 0:
|
|
226
174
|
return [
|
|
227
175
|
4,
|
|
228
|
-
conn.exec("CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '".concat(
|
|
176
|
+
conn.exec("CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '".concat(TOKENIZE, "')"))
|
|
229
177
|
];
|
|
230
178
|
case 1:
|
|
231
179
|
_state.sent();
|
|
@@ -238,7 +186,7 @@ function createContentTable(conn, tokenize) {
|
|
|
238
186
|
}
|
|
239
187
|
// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`
|
|
240
188
|
// can't dump file text into context. Features add their own tables after the core ones.
|
|
241
|
-
function ensureSchemaTables(conn, cfg
|
|
189
|
+
function ensureSchemaTables(conn, cfg) {
|
|
242
190
|
return _async_to_generator(function() {
|
|
243
191
|
var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, feature, err;
|
|
244
192
|
return _ts_generator(this, function(_state) {
|
|
@@ -250,11 +198,9 @@ function ensureSchemaTables(conn, cfg, tokenize) {
|
|
|
250
198
|
];
|
|
251
199
|
case 1:
|
|
252
200
|
_state.sent();
|
|
253
|
-
// IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL
|
|
254
|
-
// against the resolved tokenizer before this runs, so a stale table is already gone by now.
|
|
255
201
|
return [
|
|
256
202
|
4,
|
|
257
|
-
createContentTable(conn
|
|
203
|
+
createContentTable(conn)
|
|
258
204
|
];
|
|
259
205
|
case 2:
|
|
260
206
|
_state.sent();
|
|
@@ -384,9 +330,9 @@ function setJournalWal(db) {
|
|
|
384
330
|
}
|
|
385
331
|
}
|
|
386
332
|
}
|
|
387
|
-
function connect(dbPath,
|
|
333
|
+
function connect(dbPath, _cfg) {
|
|
388
334
|
return _async_to_generator(function() {
|
|
389
|
-
var db, conn
|
|
335
|
+
var db, conn;
|
|
390
336
|
return _ts_generator(this, function(_state) {
|
|
391
337
|
db = new _nodesqlite.DatabaseSync(dbPath);
|
|
392
338
|
try {
|
|
@@ -396,17 +342,13 @@ function connect(dbPath, cfg) {
|
|
|
396
342
|
// busy_timeout does not cover the WAL conversion itself: SQLite does not invoke the busy
|
|
397
343
|
// handler for it, so a concurrent cold open needs its own bounded wait.
|
|
398
344
|
setJournalWal(db);
|
|
399
|
-
(0, _sqlfunctionsts.registerFunctions)(db
|
|
345
|
+
(0, _sqlfunctionsts.registerFunctions)(db);
|
|
400
346
|
conn = (0, _connectionts.createConnection)(db);
|
|
401
|
-
// Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd
|
|
402
|
-
// tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.
|
|
403
|
-
tokenize = resolveTokenize(db, cfg);
|
|
404
347
|
return [
|
|
405
348
|
2,
|
|
406
349
|
{
|
|
407
350
|
handle: {
|
|
408
|
-
db: db
|
|
409
|
-
tokenize: tokenize
|
|
351
|
+
db: db
|
|
410
352
|
},
|
|
411
353
|
conn: conn
|
|
412
354
|
}
|
|
@@ -433,163 +375,7 @@ function close(handle) {
|
|
|
433
375
|
});
|
|
434
376
|
})();
|
|
435
377
|
}
|
|
436
|
-
|
|
437
|
-
// and tokenizer-independent. Any other signature change takes the full clear/reopen in open.ts.
|
|
438
|
-
function partialRebuild(handle, conn, _cfg, changedKeys) {
|
|
439
|
-
return _async_to_generator(function() {
|
|
440
|
-
return _ts_generator(this, function(_state) {
|
|
441
|
-
switch(_state.label){
|
|
442
|
-
case 0:
|
|
443
|
-
if (!(changedKeys.size === 1 && changedKeys.has('tokenize'))) return [
|
|
444
|
-
2,
|
|
445
|
-
false
|
|
446
|
-
];
|
|
447
|
-
console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');
|
|
448
|
-
// One transaction, so a crash before COMMIT rolls back to the old tokenizer's table rather
|
|
449
|
-
// than to no table. IF EXISTS makes a retry after such a crash a no-op, not a raw error.
|
|
450
|
-
return [
|
|
451
|
-
4,
|
|
452
|
-
(0, _transactionts.withTransaction)(conn, function() {
|
|
453
|
-
return _async_to_generator(function() {
|
|
454
|
-
return _ts_generator(this, function(_state) {
|
|
455
|
-
switch(_state.label){
|
|
456
|
-
case 0:
|
|
457
|
-
return [
|
|
458
|
-
4,
|
|
459
|
-
conn.exec('DROP TABLE IF EXISTS content')
|
|
460
|
-
];
|
|
461
|
-
case 1:
|
|
462
|
-
_state.sent();
|
|
463
|
-
return [
|
|
464
|
-
4,
|
|
465
|
-
createContentTable(conn, handle.tokenize)
|
|
466
|
-
];
|
|
467
|
-
case 2:
|
|
468
|
-
_state.sent();
|
|
469
|
-
return [
|
|
470
|
-
2
|
|
471
|
-
];
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
})();
|
|
475
|
-
}, _transactionts.BEGIN_WRITE)
|
|
476
|
-
];
|
|
477
|
-
case 1:
|
|
478
|
-
_state.sent();
|
|
479
|
-
return [
|
|
480
|
-
2,
|
|
481
|
-
true
|
|
482
|
-
];
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
})();
|
|
486
|
-
}
|
|
487
|
-
// Tokenize-only rebuild: only `content` is repopulated (the other tables are tokenizer-independent)
|
|
488
|
-
// and no feature extractors run. Warnings come from the reparse, since mtimes stay untouched and reconcile would never reparse them.
|
|
489
|
-
function postSchemaRebuild(_handle, conn, cfg, baseDir, wantFeatures) {
|
|
490
|
-
return _async_to_generator(function() {
|
|
491
|
-
var stmt, known, _, files, segmenting, warnings, rows, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, file, _warnings, _parseFile, doc, fileWarnings;
|
|
492
|
-
return _ts_generator(this, function(_state) {
|
|
493
|
-
switch(_state.label){
|
|
494
|
-
case 0:
|
|
495
|
-
return [
|
|
496
|
-
4,
|
|
497
|
-
conn.prepare('SELECT "path" FROM frontmatter')
|
|
498
|
-
];
|
|
499
|
-
case 1:
|
|
500
|
-
stmt = _state.sent();
|
|
501
|
-
_ = Set.bind;
|
|
502
|
-
return [
|
|
503
|
-
4,
|
|
504
|
-
stmt.all()
|
|
505
|
-
];
|
|
506
|
-
case 2:
|
|
507
|
-
known = new (_.apply(Set, [
|
|
508
|
-
void 0,
|
|
509
|
-
_state.sent().map(function(r) {
|
|
510
|
-
return r.path;
|
|
511
|
-
})
|
|
512
|
-
]));
|
|
513
|
-
files = (0, _indexts2.listFiles)(cfg, baseDir).filter(function(f) {
|
|
514
|
-
return known.has(f.relPath);
|
|
515
|
-
});
|
|
516
|
-
segmenting = (0, _indexts.contentTokenize)(cfg) === undefined;
|
|
517
|
-
warnings = [];
|
|
518
|
-
rows = [];
|
|
519
|
-
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
520
|
-
try {
|
|
521
|
-
for(_iterator = files[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
522
|
-
file = _step.value;
|
|
523
|
-
;
|
|
524
|
-
_parseFile = (0, _indexts2.parseFile)(file), doc = _parseFile.doc, fileWarnings = _parseFile.warnings;
|
|
525
|
-
(_warnings = warnings).push.apply(_warnings, _to_consumable_array(fileWarnings));
|
|
526
|
-
rows.push((0, _reconcilets.contentRow)(doc, segmenting));
|
|
527
|
-
}
|
|
528
|
-
} catch (err) {
|
|
529
|
-
_didIteratorError = true;
|
|
530
|
-
_iteratorError = err;
|
|
531
|
-
} finally{
|
|
532
|
-
try {
|
|
533
|
-
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
534
|
-
_iterator.return();
|
|
535
|
-
}
|
|
536
|
-
} finally{
|
|
537
|
-
if (_didIteratorError) {
|
|
538
|
-
throw _iteratorError;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return [
|
|
543
|
-
4,
|
|
544
|
-
(0, _transactionts.withTransaction)(conn, function() {
|
|
545
|
-
return _async_to_generator(function() {
|
|
546
|
-
return _ts_generator(this, function(_state) {
|
|
547
|
-
switch(_state.label){
|
|
548
|
-
case 0:
|
|
549
|
-
if (!(rows.length > 0)) return [
|
|
550
|
-
3,
|
|
551
|
-
2
|
|
552
|
-
];
|
|
553
|
-
return [
|
|
554
|
-
4,
|
|
555
|
-
conn.runBatch(_reconcilets.INSERT_CONTENT_SQL, rows)
|
|
556
|
-
];
|
|
557
|
-
case 1:
|
|
558
|
-
_state.sent();
|
|
559
|
-
_state.label = 2;
|
|
560
|
-
case 2:
|
|
561
|
-
return [
|
|
562
|
-
2
|
|
563
|
-
];
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
})();
|
|
567
|
-
}, _transactionts.BEGIN_WRITE)
|
|
568
|
-
];
|
|
569
|
-
case 3:
|
|
570
|
-
_state.sent();
|
|
571
|
-
return [
|
|
572
|
-
4,
|
|
573
|
-
(0, _sharedts.setMeta)(conn, 'features', wantFeatures)
|
|
574
|
-
];
|
|
575
|
-
case 4:
|
|
576
|
-
_state.sent();
|
|
577
|
-
return [
|
|
578
|
-
2,
|
|
579
|
-
warnings
|
|
580
|
-
];
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
})();
|
|
584
|
-
}
|
|
585
|
-
// Meta can lie after a crash between table creation and the signature write; the table's own
|
|
586
|
-
// DDL cannot, so a mismatch here rebuilds whatever meta says.
|
|
587
|
-
function extraRebuildReason(handle) {
|
|
588
|
-
var stored = storedTokenize(handle.db);
|
|
589
|
-
if (stored !== null && stored !== handle.tokenize) return 'cache was built with a different content tokenizer';
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
function ensureSchema(handle, conn, cfg) {
|
|
378
|
+
function ensureSchema(_handle, conn, cfg) {
|
|
593
379
|
return _async_to_generator(function() {
|
|
594
380
|
return _ts_generator(this, function(_state) {
|
|
595
381
|
switch(_state.label){
|
|
@@ -599,7 +385,7 @@ function ensureSchema(handle, conn, cfg) {
|
|
|
599
385
|
return [
|
|
600
386
|
4,
|
|
601
387
|
(0, _transactionts.withTransaction)(conn, function() {
|
|
602
|
-
return ensureSchemaTables(conn, cfg
|
|
388
|
+
return ensureSchemaTables(conn, cfg);
|
|
603
389
|
}, _transactionts.BEGIN_WRITE)
|
|
604
390
|
];
|
|
605
391
|
case 1:
|
|
@@ -627,13 +413,10 @@ var sqliteOpenDialect = {
|
|
|
627
413
|
reconcileDialect: _reconcilets.sqliteDialect,
|
|
628
414
|
connect: connect,
|
|
629
415
|
close: close,
|
|
630
|
-
partialRebuild: partialRebuild,
|
|
631
|
-
postSchemaRebuild: postSchemaRebuild,
|
|
632
|
-
extraRebuildReason: extraRebuildReason,
|
|
633
416
|
ensureSchema: ensureSchema,
|
|
634
417
|
setDerivedBusyTimeout: setDerivedBusyTimeout,
|
|
635
|
-
createStore: function createStore(handle, conn
|
|
636
|
-
return (0, _storets.createStore)(handle.db, conn
|
|
418
|
+
createStore: function createStore(handle, conn) {
|
|
419
|
+
return (0, _storets.createStore)(handle.db, conn);
|
|
637
420
|
}
|
|
638
421
|
};
|
|
639
422
|
function openSqlite(cfg) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { contentTokenize, featureSignature } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport { listFiles, parseFile } from '../../scan/index.ts';\nimport type { OpenResult } from '../open.ts';\nimport { openWithDialect } from '../open.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport { BEGIN_WRITE, withTransaction } from '../transaction.ts';\nimport type { Connection, OpenDialect } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { contentRow, INSERT_CONTENT_SQL, sqliteDialect } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '19';\n\nexport type { OpenResult };\n\n// unicode61 splits on spaces, so a language written without them indexes a whole run as one\n// token and word search finds nothing; `content.tokenize` is how such a tree picks trigram.\nconst DEFAULT_TOKENIZE = 'porter unicode61';\n\n// The connection plus this store's own extra state (types.ts's OpenDialect<Handle>): the\n// resolved tokenizer, threaded through connect, the tokenize-only rebuild, and ensureSchema.\ninterface SqliteHandle {\n db: DatabaseSync;\n tokenize: string;\n}\n\n// FTS5 takes its tokenizer as a DDL string literal where nothing can bind, so the configured\n// value is concatenated; probing a throwaway table first is both the safety and the validation.\nfunction resolveTokenize(db: DatabaseSync, cfg: Config): string {\n const configured = contentTokenize(cfg);\n if (configured === undefined) return DEFAULT_TOKENIZE;\n const literal = configured.replace(/'/g, \"''\");\n try {\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n db.exec(`CREATE VIRTUAL TABLE temp.sense_tokenize_probe USING fts5(x, tokenize = '${literal}')`);\n db.exec('DROP TABLE IF EXISTS temp.sense_tokenize_probe');\n } catch (err) {\n throw new SenseError('CONFIG_INVALID', `content.tokenize \"${configured}\" is not a tokenizer this SQLite accepts (${(err as Error).message}); the built-in choices are unicode61, ascii, porter, and trigram, each with their own options`);\n }\n return literal;\n}\n\n// The tokenizer the content table was actually built with, from its own DDL -- the one\n// record that cannot desynchronize from the table. NULL when the table does not exist yet.\nfunction storedTokenize(db: DatabaseSync): string | null {\n const row = db.prepare(`SELECT sql FROM sqlite_master WHERE name = 'content'`).get() as { sql: string } | undefined;\n if (!row) return null;\n const m = row.sql.match(/tokenize = '((?:[^']|'')*)'/);\n return m ? m[1] : null;\n}\n\n// The `_seg` sidecars are appended after path, never inserted: bm25() and snippet(content, 2)\n// are documented against the first three columns. Each holds its field's exploded unspaced runs.\nasync function createContentTable(conn: Connection, tokenize: string): Promise<void> {\n await conn.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${tokenize}')`);\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nasync function ensureSchemaTables(conn: Connection, cfg: Config, tokenize: string): 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 // IF NOT EXISTS is safe against a tokenizer change: open() compares the table's own DDL\n // against the resolved tokenizer before this runs, so a stale table is already gone by now.\n await createContentTable(conn, tokenize);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n await conn.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) await feature.schema(conn);\n if ((await getMeta(conn, 'schema_version')) === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);\n if ((await getMeta(conn, 'features')) === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));\n}\n\n// Two processes opening the same fresh tree both try to convert it, and the loser gets SQLITE_BUSY\n// with no busy handler behind it. Bounded because a lock held past this is a real problem, not a race.\nfunction setJournalWal(db: DatabaseSync): void {\n const deadline = Date.now() + 5000;\n for (;;) {\n try {\n db.exec('PRAGMA journal_mode = WAL');\n return;\n } catch (err) {\n if (Date.now() >= deadline || !/database is locked|busy/i.test((err as Error).message)) throw err;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);\n }\n }\n}\n\nasync function connect(dbPath: string, cfg: ResolvedConfig): Promise<{ handle: SqliteHandle; conn: Connection }> {\n const db = new DatabaseSync(dbPath);\n try {\n // Before journal_mode, not after: converting a fresh database to WAL takes a brief exclusive\n // lock, and with no timeout set yet a second process opening the same tree fails in 1ms.\n db.exec('PRAGMA busy_timeout = 30000');\n // busy_timeout does not cover the WAL conversion itself: SQLite does not invoke the busy\n // handler for it, so a concurrent cold open needs its own bounded wait.\n setJournalWal(db);\n registerFunctions(db, contentTokenize(cfg) === undefined);\n const conn = createConnection(db);\n // Before the rebuild branch below, never after: that branch deletes the cache, so a typo'd\n // tokenizer validated later would cost a full re-index (and re-embed) to reach its own error.\n const tokenize = resolveTokenize(db, cfg);\n return { handle: { db, tokenize }, conn };\n } catch (err) {\n // A throw here must release this handle, or on Windows the leaked WAL db is undeletable and\n // scratch cleanup fails with EPERM/EBUSY.\n db.close();\n throw err;\n }\n}\n\nasync function close(handle: SqliteHandle): Promise<void> {\n handle.db.close();\n}\n\n// Only the tokenizer moved, and frontmatter, links, sections and embeddings are file-derived\n// and tokenizer-independent. Any other signature change takes the full clear/reopen in open.ts.\nasync function partialRebuild(handle: SqliteHandle, conn: Connection, _cfg: Config, changedKeys: Set<string>): Promise<boolean> {\n if (!(changedKeys.size === 1 && changedKeys.has('tokenize'))) return false;\n console.error('sense: config change (content tokenizer) rebuilds the text index; vectors, links, and sections are kept');\n // One transaction, so a crash before COMMIT rolls back to the old tokenizer's table rather\n // than to no table. IF EXISTS makes a retry after such a crash a no-op, not a raw error.\n await withTransaction(\n conn,\n async () => {\n await conn.exec('DROP TABLE IF EXISTS content');\n await createContentTable(conn, handle.tokenize);\n },\n BEGIN_WRITE\n );\n return true;\n}\n\n// Tokenize-only rebuild: only `content` is repopulated (the other tables are tokenizer-independent)\n// and no feature extractors run. Warnings come from the reparse, since mtimes stay untouched and reconcile would never reparse them.\nasync function postSchemaRebuild(_handle: SqliteHandle, conn: Connection, cfg: Config, baseDir: string, wantFeatures: string): Promise<string[]> {\n const stmt = await conn.prepare('SELECT \"path\" FROM frontmatter');\n const known = new Set(((await stmt.all()) as Array<{ path: string }>).map((r) => r.path));\n const files = listFiles(cfg, baseDir).filter((f) => known.has(f.relPath));\n const segmenting = contentTokenize(cfg) === undefined;\n const warnings: string[] = [];\n const rows: unknown[][] = [];\n for (const file of files) {\n const { doc, warnings: fileWarnings } = parseFile(file);\n warnings.push(...fileWarnings);\n rows.push(contentRow(doc, segmenting));\n }\n await withTransaction(\n conn,\n async () => {\n if (rows.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, rows);\n },\n BEGIN_WRITE\n );\n await setMeta(conn, 'features', wantFeatures);\n return warnings;\n}\n\n// Meta can lie after a crash between table creation and the signature write; the table's own\n// DDL cannot, so a mismatch here rebuilds whatever meta says.\nfunction extraRebuildReason(handle: SqliteHandle): string | null {\n const stored = storedTokenize(handle.db);\n if (stored !== null && stored !== handle.tokenize) return 'cache was built with a different content tokenizer';\n return null;\n}\n\nasync function ensureSchema(handle: SqliteHandle, conn: Connection, cfg: Config): Promise<void> {\n // One writer at a time: the feature hooks check a column then add it, so two cold opens racing\n // here both see it missing and the second ALTER fails with a duplicate column.\n await withTransaction(conn, () => ensureSchemaTables(conn, cfg, handle.tokenize), BEGIN_WRITE);\n}\n\nasync function setDerivedBusyTimeout(handle: SqliteHandle, _conn: Connection, ms: number): Promise<void> {\n handle.db.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const sqliteOpenDialect: OpenDialect<SqliteHandle> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: sqliteDialect,\n connect,\n close,\n partialRebuild,\n postSchemaRebuild,\n extraRebuildReason,\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn, cfg) => createStore(handle.db, conn, cfg),\n};\n\nexport async function openSqlite(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, sqliteOpenDialect);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","openSqlite","sqliteOpenDialect","DEFAULT_TOKENIZE","resolveTokenize","db","cfg","configured","contentTokenize","undefined","literal","replace","exec","err","SenseError","message","storedTokenize","row","prepare","get","m","sql","match","createContentTable","conn","tokenize","ensureSchemaTables","feature","activeFeatures","schema","getMeta","setMeta","featureSignature","FEATURES","setJournalWal","deadline","Date","now","test","Atomics","wait","Int32Array","SharedArrayBuffer","connect","dbPath","DatabaseSync","registerFunctions","createConnection","handle","close","partialRebuild","_cfg","changedKeys","size","has","console","error","withTransaction","BEGIN_WRITE","postSchemaRebuild","_handle","baseDir","wantFeatures","stmt","known","files","segmenting","warnings","rows","file","parseFile","doc","fileWarnings","Set","all","map","r","path","listFiles","filter","f","relPath","push","contentRow","length","runBatch","INSERT_CONTENT_SQL","extraRebuildReason","stored","ensureSchema","setDerivedBusyTimeout","_conn","ms","filename","schemaVersion","reconcileDialect","sqliteDialect","createStore","openWithDialect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/open.ts"],"sourcesContent":["// The Node floor (>=22.20) is explained here and nowhere else: 22.20 is the first release with\n// both FTS5 and row-returning INSERT ... RETURNING. Raise it only for a load-bearing capability.\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../../config/index.ts';\nimport { featureSignature } from '../../config/index.ts';\nimport { activeFeatures, FEATURES } from '../../features/index.ts';\nimport type { OpenResult } from '../open.ts';\nimport { openWithDialect } from '../open.ts';\nimport { getMeta, setMeta } from '../shared.ts';\nimport { BEGIN_WRITE, withTransaction } from '../transaction.ts';\nimport type { Connection, OpenDialect } from '../types.ts';\nimport { createConnection } from './connection.ts';\nimport { sqliteDialect } from './reconcile.ts';\nimport { registerFunctions } from './sql-functions.ts';\nimport { createStore } from './store.ts';\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`. Bumping it rebuilds\n// existing trees on first query.\nexport const SCHEMA_VERSION = '19';\n\nexport type { OpenResult };\n\n// unicode61 splits on spaces, so a language written without them indexes a whole run as one\n// token and word search finds nothing; the `_seg` sidecars (always populated) cover that case.\nconst TOKENIZE = 'porter unicode61';\n\ninterface SqliteHandle {\n db: DatabaseSync;\n}\n\n// The `_seg` sidecars are appended after path, never inserted: bm25() and snippet(content, 2)\n// are documented against the first three columns. Each holds its field's exploded unspaced runs.\nasync function createContentTable(conn: Connection): Promise<void> {\n await conn.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, title_seg, summary_seg, text_seg, tokenize = '${TOKENIZE}')`);\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nasync function ensureSchemaTables(conn: Connection, cfg: Config): Promise<void> {\n await conn.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_ctime\" REAL, \"_size\" INTEGER, \"_parse_error\" TEXT)`);\n await createContentTable(conn);\n // Coverage, not ownership: a path can appear under several presets. path leads the PK so the\n // per-doc delete is an index hit -- keyed the other way, cold builds went quadratic.\n await conn.exec(`CREATE TABLE IF NOT EXISTS preset_files (\"path\" TEXT, preset TEXT, PRIMARY KEY (\"path\", preset))`);\n await conn.exec('CREATE INDEX IF NOT EXISTS preset_files_preset ON preset_files(preset)');\n for (const feature of activeFeatures(cfg)) await feature.schema(conn);\n if ((await getMeta(conn, 'schema_version')) === null) await setMeta(conn, 'schema_version', SCHEMA_VERSION);\n if ((await getMeta(conn, 'features')) === null) await setMeta(conn, 'features', featureSignature(cfg, FEATURES));\n}\n\n// Two processes opening the same fresh tree both try to convert it, and the loser gets SQLITE_BUSY\n// with no busy handler behind it. Bounded because a lock held past this is a real problem, not a race.\nfunction setJournalWal(db: DatabaseSync): void {\n const deadline = Date.now() + 5000;\n for (;;) {\n try {\n db.exec('PRAGMA journal_mode = WAL');\n return;\n } catch (err) {\n if (Date.now() >= deadline || !/database is locked|busy/i.test((err as Error).message)) throw err;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);\n }\n }\n}\n\nasync function connect(dbPath: string, _cfg: ResolvedConfig): Promise<{ handle: SqliteHandle; conn: Connection }> {\n const db = new DatabaseSync(dbPath);\n try {\n // Before journal_mode, not after: converting a fresh database to WAL takes a brief exclusive\n // lock, and with no timeout set yet a second process opening the same tree fails in 1ms.\n db.exec('PRAGMA busy_timeout = 30000');\n // busy_timeout does not cover the WAL conversion itself: SQLite does not invoke the busy\n // handler for it, so a concurrent cold open needs its own bounded wait.\n setJournalWal(db);\n registerFunctions(db);\n const conn = createConnection(db);\n return { handle: { db }, conn };\n } catch (err) {\n // A throw here must release this handle, or on Windows the leaked WAL db is undeletable and\n // scratch cleanup fails with EPERM/EBUSY.\n db.close();\n throw err;\n }\n}\n\nasync function close(handle: SqliteHandle): Promise<void> {\n handle.db.close();\n}\n\nasync function ensureSchema(_handle: SqliteHandle, conn: Connection, cfg: Config): Promise<void> {\n // One writer at a time: the feature hooks check a column then add it, so two cold opens racing\n // here both see it missing and the second ALTER fails with a duplicate column.\n await withTransaction(conn, () => ensureSchemaTables(conn, cfg), BEGIN_WRITE);\n}\n\nasync function setDerivedBusyTimeout(handle: SqliteHandle, _conn: Connection, ms: number): Promise<void> {\n handle.db.exec(`PRAGMA busy_timeout = ${ms}`);\n}\n\n// This store's dialect (types.ts's OpenDialect) for the shared orchestration in store/open.ts.\nexport const sqliteOpenDialect: OpenDialect<SqliteHandle> = {\n filename: DB_FILENAME,\n schemaVersion: SCHEMA_VERSION,\n reconcileDialect: sqliteDialect,\n connect,\n close,\n ensureSchema,\n setDerivedBusyTimeout,\n createStore: (handle, conn) => createStore(handle.db, conn),\n};\n\nexport async function openSqlite(cfg: ResolvedConfig): Promise<OpenResult> {\n return openWithDialect(cfg, sqliteOpenDialect);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","openSqlite","sqliteOpenDialect","TOKENIZE","createContentTable","conn","exec","ensureSchemaTables","cfg","feature","activeFeatures","schema","getMeta","setMeta","featureSignature","FEATURES","setJournalWal","db","deadline","Date","now","err","test","message","Atomics","wait","Int32Array","SharedArrayBuffer","connect","dbPath","_cfg","DatabaseSync","registerFunctions","createConnection","handle","close","ensureSchema","_handle","withTransaction","BEGIN_WRITE","setDerivedBusyTimeout","_conn","ms","filename","schemaVersion","reconcileDialect","sqliteDialect","createStore","openWithDialect"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ParsedDoc } from '../../scan/index.js';
|
|
2
2
|
import type { ReconcileDialect } from '../types.js';
|
|
3
3
|
export declare const INSERT_CONTENT_SQL = "INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)";
|
|
4
|
-
export declare function contentRow(doc: ParsedDoc
|
|
4
|
+
export declare function contentRow(doc: ParsedDoc): unknown[];
|
|
5
5
|
export declare const sqliteDialect: ReconcileDialect;
|