sensemaking 0.24.6 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -11
- package/dist/cjs/cli/build.d.cts +3 -0
- package/dist/cjs/cli/build.d.ts +3 -0
- package/dist/cjs/cli/build.js +195 -0
- package/dist/cjs/cli/build.js.map +1 -0
- package/dist/cjs/cli/index.d.cts +7 -6
- package/dist/cjs/cli/index.d.ts +7 -6
- package/dist/cjs/cli/index.js +12 -6
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js +2 -2
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js +7 -2
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +63 -21
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +7 -2
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +7 -2
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +55 -6
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +54 -15
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.d.cts +9 -3
- package/dist/cjs/cli/shared.d.ts +9 -3
- package/dist/cjs/cli/shared.js +109 -74
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js +8 -3
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js +142 -50
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/cli/types.d.cts +4 -2
- package/dist/cjs/cli/types.d.ts +4 -2
- package/dist/cjs/cli.js +10 -4
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/commands/map.js +10 -6
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/peek.js +10 -6
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.d.cts +1 -0
- package/dist/cjs/commands/related.d.ts +1 -0
- package/dist/cjs/commands/related.js +56 -21
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js +1 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.js +102 -84
- package/dist/cjs/commands/search.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.map +1 -1
- package/dist/cjs/config/load.d.cts +6 -1
- package/dist/cjs/config/load.d.ts +6 -1
- package/dist/cjs/config/load.js +38 -1
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/types.d.cts +2 -1
- package/dist/cjs/config/types.d.ts +2 -1
- package/dist/cjs/config/types.js +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +20 -0
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/embed/query.d.cts +2 -1
- package/dist/cjs/embed/query.d.ts +2 -1
- package/dist/cjs/embed/query.js +186 -109
- package/dist/cjs/embed/query.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/embed.js.map +1 -1
- package/dist/cjs/graph/traverse.js +11 -6
- package/dist/cjs/graph/traverse.js.map +1 -1
- package/dist/cjs/index.d.cts +3 -3
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/atomic-write.js +20 -6
- package/dist/cjs/lib/atomic-write.js.map +1 -1
- package/dist/cjs/lib/serial-query.d.cts +1 -0
- package/dist/cjs/lib/serial-query.d.ts +1 -0
- package/dist/cjs/lib/serial-query.js +22 -0
- package/dist/cjs/lib/serial-query.js.map +1 -0
- package/dist/cjs/scan/index.d.cts +5 -0
- package/dist/cjs/scan/index.d.ts +5 -0
- package/dist/cjs/scan/index.js +12 -4
- package/dist/cjs/scan/index.js.map +1 -1
- package/dist/cjs/scan/pool.d.cts +5 -0
- package/dist/cjs/scan/pool.d.ts +5 -0
- package/dist/cjs/scan/pool.js +167 -55
- package/dist/cjs/scan/pool.js.map +1 -1
- package/dist/cjs/store/builder.d.cts +6 -1
- package/dist/cjs/store/builder.d.ts +6 -1
- package/dist/cjs/store/builder.js +135 -5
- package/dist/cjs/store/builder.js.map +1 -1
- package/dist/cjs/store/duckdb/lexical.d.cts +2 -0
- package/dist/cjs/store/duckdb/lexical.d.ts +2 -0
- package/dist/cjs/store/duckdb/lexical.js +139 -7
- package/dist/cjs/store/duckdb/lexical.js.map +1 -1
- package/dist/cjs/store/duckdb/open.d.cts +3 -3
- package/dist/cjs/store/duckdb/open.d.ts +3 -3
- package/dist/cjs/store/duckdb/open.js +61 -42
- package/dist/cjs/store/duckdb/open.js.map +1 -1
- package/dist/cjs/store/duckdb/reconcile.js +2 -2
- package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
- package/dist/cjs/store/duckdb/vectors.d.cts +1 -0
- package/dist/cjs/store/duckdb/vectors.d.ts +1 -0
- package/dist/cjs/store/duckdb/vectors.js +55 -43
- package/dist/cjs/store/duckdb/vectors.js.map +1 -1
- package/dist/cjs/store/index.d.cts +23 -4
- package/dist/cjs/store/index.d.ts +23 -4
- package/dist/cjs/store/index.js +224 -15
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/store/open.d.cts +9 -1
- package/dist/cjs/store/open.d.ts +9 -1
- package/dist/cjs/store/open.js +479 -128
- package/dist/cjs/store/open.js.map +1 -1
- package/dist/cjs/store/reconcile.js +93 -63
- package/dist/cjs/store/reconcile.js.map +1 -1
- package/dist/cjs/store/sqlite/open.d.cts +3 -3
- package/dist/cjs/store/sqlite/open.d.ts +3 -3
- package/dist/cjs/store/sqlite/open.js +44 -36
- package/dist/cjs/store/sqlite/open.js.map +1 -1
- package/dist/cjs/store/turso/open.d.cts +3 -3
- package/dist/cjs/store/turso/open.d.ts +3 -3
- package/dist/cjs/store/turso/open.js +83 -66
- package/dist/cjs/store/turso/open.js.map +1 -1
- package/dist/cjs/store/turso/store.d.cts +3 -1
- package/dist/cjs/store/turso/store.d.ts +3 -1
- package/dist/cjs/store/turso/store.js +46 -31
- package/dist/cjs/store/turso/store.js.map +1 -1
- package/dist/cjs/store/types.d.cts +12 -3
- package/dist/cjs/store/types.d.ts +12 -3
- package/dist/cjs/store/types.js.map +1 -1
- package/dist/cjs/watch.js +155 -35
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/cli/build.d.ts +3 -0
- package/dist/esm/cli/build.js +18 -0
- package/dist/esm/cli/build.js.map +1 -0
- package/dist/esm/cli/index.d.ts +7 -6
- package/dist/esm/cli/index.js +8 -6
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js +2 -2
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/map.js +8 -2
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js +38 -14
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js +8 -2
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js +8 -2
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js +21 -3
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js +28 -7
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.d.ts +9 -3
- package/dist/esm/cli/shared.js +44 -24
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/sql.js +8 -2
- package/dist/esm/cli/sql.js.map +1 -1
- package/dist/esm/cli/status.js +166 -111
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/cli/types.d.ts +4 -2
- package/dist/esm/cli/types.js.map +1 -1
- package/dist/esm/cli.js +9 -4
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/commands/map.js +5 -1
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/peek.js +5 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.d.ts +1 -0
- package/dist/esm/commands/related.js +19 -10
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.js +1 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.js +11 -10
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/config/index.d.ts +1 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.d.ts +6 -1
- package/dist/esm/config/load.js +32 -3
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/types.d.ts +2 -1
- package/dist/esm/config/types.js +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +20 -0
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/embed/query.d.ts +2 -1
- package/dist/esm/embed/query.js +40 -34
- package/dist/esm/embed/query.js.map +1 -1
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/graph/traverse.js +5 -1
- package/dist/esm/graph/traverse.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/atomic-write.js +14 -6
- package/dist/esm/lib/atomic-write.js.map +1 -1
- package/dist/esm/lib/serial-query.d.ts +1 -0
- package/dist/esm/lib/serial-query.js +13 -0
- package/dist/esm/lib/serial-query.js.map +1 -0
- package/dist/esm/scan/index.d.ts +5 -0
- package/dist/esm/scan/index.js +7 -3
- package/dist/esm/scan/index.js.map +1 -1
- package/dist/esm/scan/pool.d.ts +5 -0
- package/dist/esm/scan/pool.js +41 -16
- package/dist/esm/scan/pool.js.map +1 -1
- package/dist/esm/store/builder.d.ts +6 -1
- package/dist/esm/store/builder.js +19 -0
- package/dist/esm/store/builder.js.map +1 -1
- package/dist/esm/store/duckdb/lexical.d.ts +2 -0
- package/dist/esm/store/duckdb/lexical.js +32 -4
- package/dist/esm/store/duckdb/lexical.js.map +1 -1
- package/dist/esm/store/duckdb/open.d.ts +3 -3
- package/dist/esm/store/duckdb/open.js +16 -7
- package/dist/esm/store/duckdb/open.js.map +1 -1
- package/dist/esm/store/duckdb/reconcile.js +2 -2
- package/dist/esm/store/duckdb/reconcile.js.map +1 -1
- package/dist/esm/store/duckdb/vectors.d.ts +1 -0
- package/dist/esm/store/duckdb/vectors.js +18 -14
- package/dist/esm/store/duckdb/vectors.js.map +1 -1
- package/dist/esm/store/index.d.ts +23 -4
- package/dist/esm/store/index.js +80 -15
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/store/open.d.ts +9 -1
- package/dist/esm/store/open.js +175 -84
- package/dist/esm/store/open.js.map +1 -1
- package/dist/esm/store/reconcile.js +12 -0
- package/dist/esm/store/reconcile.js.map +1 -1
- package/dist/esm/store/sqlite/open.d.ts +3 -3
- package/dist/esm/store/sqlite/open.js +9 -6
- package/dist/esm/store/sqlite/open.js.map +1 -1
- package/dist/esm/store/turso/open.d.ts +3 -3
- package/dist/esm/store/turso/open.js +14 -10
- package/dist/esm/store/turso/open.js.map +1 -1
- package/dist/esm/store/turso/store.d.ts +3 -1
- package/dist/esm/store/turso/store.js +5 -1
- package/dist/esm/store/turso/store.js.map +1 -1
- package/dist/esm/store/types.d.ts +12 -3
- package/dist/esm/store/types.js.map +1 -1
- package/dist/esm/watch.js +57 -19
- package/dist/esm/watch.js.map +1 -1
- package/package.json +1 -1
- package/schema.json +7 -2
- package/skills/sense/SKILL.md +3 -3
- package/skills/sense-bases/SKILL.md +1 -2
- package/skills/sense-setup/SKILL.md +1 -1
- package/skills/sense-setup/references/embeddings.md +1 -1
- package/skills/sense-setup/references/store-benchmarks.md +6 -6
package/dist/esm/store/open.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { channel } from 'node:diagnostics_channel';
|
|
2
|
-
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { featureSignature, STATE_DIR } from '../config/index.js';
|
|
4
|
+
import { anyPresetEmbeds, featureSignature, STATE_DIR } from '../config/index.js';
|
|
5
5
|
import { rekeyChunkText } from '../embed/handoff.js';
|
|
6
|
+
import { embedPending } from '../embed/query.js';
|
|
6
7
|
import { SenseError } from '../errors.js';
|
|
7
8
|
import { FEATURES } from '../features/index.js';
|
|
8
9
|
import { createBuilder } from './builder.js';
|
|
@@ -13,20 +14,40 @@ import { lockWaitBudgetMs } from './lock-wait.js';
|
|
|
13
14
|
import { forcedPresetPaths, isPresetOnlyChange } from './preset-scope.js';
|
|
14
15
|
import { getMeta, setMeta } from './shared.js';
|
|
15
16
|
import { changedSignatureKeys, embedIdentityAdopted, signatureDiff } from './signature.js';
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
import { stageRecorder } from './stages.js';
|
|
18
|
+
import { withTransaction } from './transaction.js';
|
|
19
|
+
const CORE_READY_META_KEY = 'core_ready';
|
|
18
20
|
const LOCK_POLL_MS = 50;
|
|
19
21
|
const lockWait = channel('sensemaking.store.lock-wait');
|
|
20
|
-
|
|
22
|
+
function notReady(reason) {
|
|
23
|
+
return new SenseError('INDEX_NOT_READY', `${reason}; run "sense build" or call build(config) before opening with { build: false }`);
|
|
24
|
+
}
|
|
25
|
+
function normaliseConfig(cfg) {
|
|
26
|
+
// `baseDir` was the original public open() input. Keep direct API callers working while
|
|
27
|
+
// making the two ownership paths explicit for every internal operation.
|
|
28
|
+
if (!cfg.rootDir) {
|
|
29
|
+
if (!cfg.baseDir) throw new SenseError('CONFIG_INVALID', 'open requires rootDir (or legacy baseDir)');
|
|
30
|
+
cfg.rootDir = cfg.baseDir;
|
|
31
|
+
}
|
|
32
|
+
if (!cfg.configDir) cfg.configDir = cfg.rootDir;
|
|
33
|
+
return cfg;
|
|
34
|
+
}
|
|
35
|
+
function cachePath(cfg, dialect) {
|
|
36
|
+
var _cfg_configDir;
|
|
37
|
+
return join((_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : cfg.baseDir, STATE_DIR, dialect.filename);
|
|
38
|
+
}
|
|
39
|
+
async function connectUnlocked(dbPath, cfg, configDir, dialect, options) {
|
|
21
40
|
const budgetMs = lockWaitBudgetMs(configDir);
|
|
22
41
|
const deadline = Date.now() + budgetMs;
|
|
23
42
|
for(;;){
|
|
24
43
|
try {
|
|
25
|
-
return await dialect.connect(dbPath, cfg);
|
|
44
|
+
return await dialect.connect(dbPath, cfg, options);
|
|
26
45
|
} catch (err) {
|
|
27
46
|
var _dialect_isLocked;
|
|
47
|
+
if (options.existingOnly && !existsSync(dbPath)) {
|
|
48
|
+
throw notReady(`the derived index does not exist at ${dbPath}`);
|
|
49
|
+
}
|
|
28
50
|
if (!((_dialect_isLocked = dialect.isLocked) === null || _dialect_isLocked === void 0 ? void 0 : _dialect_isLocked.call(dialect, err))) throw err;
|
|
29
|
-
// Observe a real rejected native connection, never merely the start of an async open.
|
|
30
51
|
if (lockWait.hasSubscribers) lockWait.publish({
|
|
31
52
|
store: cfg.store,
|
|
32
53
|
dbPath
|
|
@@ -38,7 +59,71 @@ async function connectUnlocked(dbPath, cfg, configDir, dialect) {
|
|
|
38
59
|
}
|
|
39
60
|
}
|
|
40
61
|
}
|
|
41
|
-
async function
|
|
62
|
+
async function assertCoreSchema(conn) {
|
|
63
|
+
for (const table of [
|
|
64
|
+
'frontmatter',
|
|
65
|
+
'content',
|
|
66
|
+
'preset_files',
|
|
67
|
+
'indexed_sources'
|
|
68
|
+
]){
|
|
69
|
+
await (await conn.prepare(`SELECT 1 FROM ${table} LIMIT 0`)).all();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function connectExisting(cfg, dialect, requirements) {
|
|
73
|
+
var _cfg_configDir;
|
|
74
|
+
const configDir = (_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : cfg.baseDir;
|
|
75
|
+
const dbPath = cachePath(cfg, dialect);
|
|
76
|
+
if (!existsSync(dbPath)) throw notReady(`the derived index does not exist at ${dbPath}`);
|
|
77
|
+
const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect, {
|
|
78
|
+
existingOnly: true,
|
|
79
|
+
observational: true
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
let version;
|
|
83
|
+
let features;
|
|
84
|
+
let ready;
|
|
85
|
+
try {
|
|
86
|
+
version = await getMeta(conn, 'schema_version');
|
|
87
|
+
features = await getMeta(conn, 'features');
|
|
88
|
+
ready = await getMeta(conn, CORE_READY_META_KEY);
|
|
89
|
+
await assertCoreSchema(conn);
|
|
90
|
+
} catch {
|
|
91
|
+
if (!existsSync(dbPath)) throw notReady(`the derived index was deleted while opening ${dbPath}`);
|
|
92
|
+
throw notReady(`the derived index at ${dbPath} has no compatible schema`);
|
|
93
|
+
}
|
|
94
|
+
if (version !== dialect.schemaVersion) {
|
|
95
|
+
throw notReady(`the derived index at ${dbPath} uses schema ${version !== null && version !== void 0 ? version : 'unknown'}, but this sense version requires ${dialect.schemaVersion}`);
|
|
96
|
+
}
|
|
97
|
+
const wantFeatures = featureSignature(cfg, FEATURES);
|
|
98
|
+
if (features !== wantFeatures) {
|
|
99
|
+
throw notReady(`the derived index at ${dbPath} was built for different configuration features`);
|
|
100
|
+
}
|
|
101
|
+
if (ready !== '1') {
|
|
102
|
+
throw notReady(`the derived index at ${dbPath} has an incomplete core build`);
|
|
103
|
+
}
|
|
104
|
+
if (requirements.has('lexical')) {
|
|
105
|
+
var _dialect_assertLexicalReady;
|
|
106
|
+
await ((_dialect_assertLexicalReady = dialect.assertLexicalReady) === null || _dialect_assertLexicalReady === void 0 ? void 0 : _dialect_assertLexicalReady.call(dialect, conn));
|
|
107
|
+
}
|
|
108
|
+
if (!existsSync(dbPath)) throw notReady(`the derived index was deleted while opening ${dbPath}`);
|
|
109
|
+
return {
|
|
110
|
+
handle,
|
|
111
|
+
conn,
|
|
112
|
+
cfg,
|
|
113
|
+
dbPath,
|
|
114
|
+
parsed: 0,
|
|
115
|
+
warnings: [],
|
|
116
|
+
stages: stageRecorder().take(0, 0),
|
|
117
|
+
observational: true
|
|
118
|
+
};
|
|
119
|
+
} catch (err) {
|
|
120
|
+
await dialect.close(handle, {
|
|
121
|
+
observational: true
|
|
122
|
+
});
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function connectForBuild(cfg, dialect, requirements) {
|
|
42
127
|
var _cfg_configDir, _cfg_rootDir;
|
|
43
128
|
const configDir = (_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : cfg.baseDir;
|
|
44
129
|
const stateDir = join(configDir, STATE_DIR);
|
|
@@ -46,107 +131,84 @@ async function connectWithDialect(cfg, dialect) {
|
|
|
46
131
|
recursive: true
|
|
47
132
|
});
|
|
48
133
|
const dbPath = join(stateDir, dialect.filename);
|
|
49
|
-
const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect
|
|
50
|
-
|
|
51
|
-
|
|
134
|
+
const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect, {
|
|
135
|
+
existingOnly: false,
|
|
136
|
+
observational: false
|
|
137
|
+
});
|
|
52
138
|
let closed = false;
|
|
53
|
-
// Created here, not inside reconcile(): invalidate()/invalidateFeatures() below share its pool
|
|
54
|
-
// with the build() call that follows them.
|
|
55
139
|
const builder = createBuilder(conn, cfg, {
|
|
56
140
|
rootDir: (_cfg_rootDir = cfg.rootDir) !== null && _cfg_rootDir !== void 0 ? _cfg_rootDir : cfg.baseDir,
|
|
57
141
|
configDir
|
|
58
142
|
}, dialect.reconcileDialect);
|
|
59
143
|
try {
|
|
60
144
|
await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');
|
|
61
|
-
// Schema-version or feature-set mismatch: reconcile only reparses changed files, so an
|
|
62
|
-
// old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).
|
|
63
145
|
const version = await getMeta(conn, 'schema_version');
|
|
64
146
|
const features = await getMeta(conn, 'features');
|
|
147
|
+
const ready = await getMeta(conn, CORE_READY_META_KEY);
|
|
65
148
|
const wantFeatures = featureSignature(cfg, FEATURES);
|
|
66
|
-
//
|
|
67
|
-
//
|
|
149
|
+
// `ready=0` may belong to another SQLite builder, so recovery stays in place and lets the
|
|
150
|
+
// engine serialize it. It never unlinks the cache.
|
|
151
|
+
await setMeta(conn, CORE_READY_META_KEY, '0');
|
|
152
|
+
const recoverIncomplete = ready === '0' && (version === null || version === dialect.schemaVersion);
|
|
68
153
|
let forcedPaths;
|
|
69
|
-
// Set only by the embed-narrow branch below; applied via builder.invalidate() after ensureSchema.
|
|
70
154
|
let embedInvalidate;
|
|
71
|
-
// Set only by the feature-toggle branch below; applied via builder.invalidateFeatures() after ensureSchema.
|
|
72
155
|
let featureToggles;
|
|
73
|
-
if (version !== null && version !== dialect.schemaVersion
|
|
156
|
+
if (version !== null && version !== dialect.schemaVersion) {
|
|
157
|
+
console.error('sense: cache format changed (new sensemaking version); rebuilding the index');
|
|
158
|
+
closed = true;
|
|
159
|
+
await dialect.close(handle);
|
|
160
|
+
clearCache(cfg);
|
|
161
|
+
return connectForBuild(cfg, dialect, requirements);
|
|
162
|
+
}
|
|
163
|
+
if (recoverIncomplete) {
|
|
164
|
+
console.error('sense: recovering an incomplete index generation in place');
|
|
165
|
+
} else if (features !== null && features !== wantFeatures) {
|
|
74
166
|
var _cfg_rootDir1;
|
|
75
|
-
// Indexing derives from presets, so a config edit rebuilding the cache must say so and
|
|
76
|
-
// name what changed -- silent rebuilds make derived indexing look like a hang or a bug.
|
|
77
|
-
if (version !== null && version !== dialect.schemaVersion) {
|
|
78
|
-
console.error('sense: cache format changed (new sensemaking version); rebuilding the index');
|
|
79
|
-
// Close before clearCache deletes the files out from under the still-open handle.
|
|
80
|
-
closed = true;
|
|
81
|
-
await dialect.close(handle);
|
|
82
|
-
clearCache(cfg);
|
|
83
|
-
return connectWithDialect(cfg, dialect);
|
|
84
|
-
}
|
|
85
167
|
const changedKeys = changedSignatureKeys(features !== null && features !== void 0 ? features : '', wantFeatures);
|
|
86
|
-
// Null (rather than the empty set) when changedKeys isn't preset-only, or a changed
|
|
87
|
-
// preset's stored segment can't be parsed back into old include/exclude/vectors -- either
|
|
88
|
-
// way the branch below is skipped and the full rebuild stays the fallback.
|
|
89
168
|
const presetForced = isPresetOnlyChange(changedKeys) ? forcedPresetPaths(cfg, (_cfg_rootDir1 = cfg.rootDir) !== null && _cfg_rootDir1 !== void 0 ? _cfg_rootDir1 : cfg.baseDir, features !== null && features !== void 0 ? features : '', changedKeys) : null;
|
|
90
|
-
// Null unless exactly one segment changed and it decomposes into a recognised embed-only
|
|
91
|
-
// case (embed-scope.ts): anything else, including embed toggled on/off, stays null.
|
|
92
169
|
const embedKind = changedKeys.size === 1 && changedKeys.has('embed') ? classifyEmbedChange(features !== null && features !== void 0 ? features : '', wantFeatures) : null;
|
|
93
|
-
// Null (rather than the empty set) when changedKeys isn't feature-toggle-only, or a changed
|
|
94
|
-
// feature's stored segment can't be parsed back into old/new on-off -- either way the branch
|
|
95
|
-
// below is skipped and the full rebuild stays the fallback.
|
|
96
170
|
const toggles = isFeatureOnlyChange(changedKeys) ? classifyFeatureToggles(features !== null && features !== void 0 ? features : '', wantFeatures, changedKeys) : null;
|
|
97
171
|
if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features !== null && features !== void 0 ? features : '', wantFeatures)) {
|
|
98
|
-
// First sight of a resolved weight identity: the model itself hasn't changed, so
|
|
99
|
-
// adopt it into meta with no rebuild and no re-embed.
|
|
100
172
|
console.error("sense: recorded the embedding model's resolved identity; vectors are unaffected");
|
|
101
|
-
await setMeta(conn, 'features', wantFeatures);
|
|
102
173
|
} else if (embedKind !== null) {
|
|
103
|
-
// Chunk boundaries are file-derived, not model-derived (embed.ts's chunksOf), so neither
|
|
104
|
-
// case needs any file reparsed for any feature but embed itself.
|
|
105
174
|
embedInvalidate = embedKind;
|
|
106
175
|
console.error(embedKind === 'model' ? 'sense: config change (embed settings) invalidates only the stale vectors' : 'sense: config change (embed settings) rebuilds only the embeddings it affects');
|
|
107
|
-
await setMeta(conn, 'features', wantFeatures);
|
|
108
176
|
} else if (presetForced !== null) {
|
|
109
|
-
// Membership only: which files a preset covers moved, not a file's own content, a
|
|
110
|
-
// global feature, or the embed model. reconcile() already knows how to
|
|
111
|
-
// add, update, and remove a file correctly (every cross-feature cascade included); it
|
|
112
|
-
// just needs telling which unchanged files to treat as touched (forcedPaths, above).
|
|
113
177
|
forcedPaths = presetForced;
|
|
114
178
|
const changed = signatureDiff(features !== null && features !== void 0 ? features : '', wantFeatures);
|
|
115
179
|
console.error(`sense: config change (${changed}) reparses only the files it affects`);
|
|
116
|
-
await setMeta(conn, 'features', wantFeatures);
|
|
117
180
|
} else if (toggles !== null) {
|
|
118
|
-
// A feature turned off or on: reconcile.ts's activeFeatures skips a disabled feature's
|
|
119
|
-
// hooks entirely, so its rows rot while off -- invalidateFeatures drops them and, for a
|
|
120
|
-
// feature turning back on, fully re-derives across every indexed file rather than
|
|
121
|
-
// trusting rows written before or during that window.
|
|
122
181
|
featureToggles = toggles;
|
|
123
182
|
const changed = signatureDiff(features !== null && features !== void 0 ? features : '', wantFeatures);
|
|
124
183
|
console.error(`sense: config change (${changed}) reparses only the feature it affects`);
|
|
125
|
-
await setMeta(conn, 'features', wantFeatures);
|
|
126
184
|
} else {
|
|
127
185
|
const changed = signatureDiff(features !== null && features !== void 0 ? features : '', wantFeatures);
|
|
128
186
|
console.error(`sense: config change (${changed}) rebuilds the index`);
|
|
129
187
|
closed = true;
|
|
130
188
|
await dialect.close(handle);
|
|
131
189
|
clearCache(cfg);
|
|
132
|
-
return
|
|
190
|
+
return connectForBuild(cfg, dialect, requirements);
|
|
133
191
|
}
|
|
134
192
|
}
|
|
135
193
|
await dialect.ensureSchema(handle, conn, cfg);
|
|
136
|
-
// 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.
|
|
137
|
-
// Installed before build() -- that call is the one that races a watcher's transaction.
|
|
138
194
|
if (dialect.setDerivedBusyTimeout) {
|
|
139
195
|
var _ref;
|
|
140
196
|
const recordedMaxMs = Number((_ref = await getMeta(conn, 'reconcile_max_ms')) !== null && _ref !== void 0 ? _ref : '0');
|
|
141
197
|
await dialect.setDerivedBusyTimeout(handle, conn, Math.min(Math.max(30000, 3 * recordedMaxMs), 600000));
|
|
142
198
|
}
|
|
143
|
-
// Runs before build(): forcedPaths, embedInvalidate and featureToggles never target the same
|
|
144
|
-
// call (each comes from its own mutually exclusive branch above: a preset-only, embed-only, or
|
|
145
|
-
// feature-toggle-only changed-key set).
|
|
146
199
|
const embedParsed = embedInvalidate ? (await builder.invalidate(embedInvalidate)).parsed : 0;
|
|
147
200
|
const featureParsed = featureToggles ? (await builder.invalidateFeatures(featureToggles)).parsed : 0;
|
|
148
|
-
const { parsed, warnings, stages } = await builder.build(forcedPaths);
|
|
149
|
-
|
|
201
|
+
const { parsed, warnings, stages } = recoverIncomplete ? await builder.recover() : await builder.build(forcedPaths);
|
|
202
|
+
await withTransaction(conn, async ()=>{
|
|
203
|
+
await setMeta(conn, 'features', wantFeatures);
|
|
204
|
+
await setMeta(conn, CORE_READY_META_KEY, '1');
|
|
205
|
+
});
|
|
206
|
+
// Lexical preparation is a separate capability stage. A failure leaves core usable and the
|
|
207
|
+
// dialect's own lexical marker stale, so unrelated map/peek/path/sql commands stay available.
|
|
208
|
+
if (requirements.has('lexical')) {
|
|
209
|
+
var _dialect_prepareLexical;
|
|
210
|
+
await ((_dialect_prepareLexical = dialect.prepareLexical) === null || _dialect_prepareLexical === void 0 ? void 0 : _dialect_prepareLexical.call(dialect, conn));
|
|
211
|
+
}
|
|
150
212
|
await builder.close();
|
|
151
213
|
return {
|
|
152
214
|
handle,
|
|
@@ -155,7 +217,8 @@ async function connectWithDialect(cfg, dialect) {
|
|
|
155
217
|
dbPath,
|
|
156
218
|
parsed: parsed + embedParsed + featureParsed,
|
|
157
219
|
warnings,
|
|
158
|
-
stages
|
|
220
|
+
stages,
|
|
221
|
+
observational: false
|
|
159
222
|
};
|
|
160
223
|
} catch (err) {
|
|
161
224
|
await builder.close();
|
|
@@ -163,28 +226,56 @@ async function connectWithDialect(cfg, dialect) {
|
|
|
163
226
|
throw err;
|
|
164
227
|
}
|
|
165
228
|
}
|
|
166
|
-
// A
|
|
167
|
-
//
|
|
168
|
-
export async function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
229
|
+
// A static provider can resolve its identity while embedding. Publish it only after vectors
|
|
230
|
+
// succeed and only when the durable signature still matches the prepared generation.
|
|
231
|
+
export async function prepareDocumentEmbeddings(store, cfg, allowed) {
|
|
232
|
+
const preparedSignature = await getMeta(store, 'features');
|
|
233
|
+
await embedPending(store, cfg, allowed);
|
|
234
|
+
const actualSignature = featureSignature(cfg, FEATURES);
|
|
235
|
+
if (preparedSignature === actualSignature) return;
|
|
236
|
+
const changedKeys = changedSignatureKeys(preparedSignature !== null && preparedSignature !== void 0 ? preparedSignature : '', actualSignature);
|
|
237
|
+
if (preparedSignature === null || changedKeys.size !== 1 || !changedKeys.has('embed') || !embedIdentityAdopted(preparedSignature, actualSignature)) {
|
|
238
|
+
throw notReady('the index configuration changed while vectors were being prepared');
|
|
239
|
+
}
|
|
240
|
+
const update = await store.prepare('UPDATE meta SET value = ? WHERE key = ? AND value = ?');
|
|
241
|
+
const result = await update.run(actualSignature, 'features', preparedSignature);
|
|
242
|
+
if (Number(result.changes) !== 1) throw notReady('another build changed the index configuration while vectors were being prepared');
|
|
243
|
+
}
|
|
244
|
+
export async function openWithDialect(input, dialect, options = {}) {
|
|
245
|
+
var _options_requirements;
|
|
246
|
+
const cfg = normaliseConfig(input);
|
|
247
|
+
const buildEnabled = options.build !== false;
|
|
248
|
+
const defaultRequirements = new Set(buildEnabled ? [
|
|
249
|
+
'core',
|
|
250
|
+
'lexical'
|
|
251
|
+
] : [
|
|
252
|
+
'core'
|
|
253
|
+
]);
|
|
254
|
+
if (buildEnabled && anyPresetEmbeds(cfg)) defaultRequirements.add('vectors');
|
|
255
|
+
const requirements = (_options_requirements = options.requirements) !== null && _options_requirements !== void 0 ? _options_requirements : defaultRequirements;
|
|
256
|
+
const connected = buildEnabled ? await connectForBuild(cfg, dialect, requirements) : await connectExisting(cfg, dialect, requirements);
|
|
257
|
+
let store;
|
|
258
|
+
try {
|
|
259
|
+
store = dialect.createStore(connected.handle, connected.conn, connected.cfg, {
|
|
260
|
+
observational: connected.observational
|
|
261
|
+
});
|
|
262
|
+
rekeyChunkText(connected.conn, store);
|
|
263
|
+
if (requirements.has('vectors') && buildEnabled) await prepareDocumentEmbeddings(store, connected.cfg);
|
|
264
|
+
return {
|
|
265
|
+
store,
|
|
266
|
+
cfg: connected.cfg,
|
|
267
|
+
dbPath: connected.dbPath,
|
|
268
|
+
parsed: connected.parsed,
|
|
269
|
+
warnings: connected.warnings,
|
|
270
|
+
stages: connected.stages
|
|
271
|
+
};
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (store) await store.close();
|
|
274
|
+
else await dialect.close(connected.handle, {
|
|
275
|
+
observational: connected.observational
|
|
276
|
+
});
|
|
277
|
+
throw err;
|
|
174
278
|
}
|
|
175
|
-
if (!cfg.configDir) cfg.configDir = cfg.rootDir;
|
|
176
|
-
const { handle, conn, cfg: resolvedCfg, dbPath, parsed, warnings, stages } = await connectWithDialect(cfg, dialect);
|
|
177
|
-
const store = dialect.createStore(handle, conn, resolvedCfg);
|
|
178
|
-
// reconcile ran before this object existed, so its chunk text is keyed by the connection.
|
|
179
|
-
rekeyChunkText(conn, store);
|
|
180
|
-
return {
|
|
181
|
-
store,
|
|
182
|
-
cfg: resolvedCfg,
|
|
183
|
-
dbPath,
|
|
184
|
-
parsed,
|
|
185
|
-
warnings,
|
|
186
|
-
stages
|
|
187
|
-
};
|
|
188
279
|
}
|
|
189
280
|
export async function docCount(store) {
|
|
190
281
|
const stmt = await store.prepare('SELECT COUNT(*) AS n FROM frontmatter');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/open.ts"],"sourcesContent":["import { channel } from 'node:diagnostics_channel';\nimport { mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { featureSignature, STATE_DIR } from '../config/index.ts';\nimport { rekeyChunkText } from '../embed/handoff.ts';\nimport { SenseError } from '../errors.ts';\nimport { FEATURES } from '../features/index.ts';\nimport { createBuilder } from './builder.ts';\nimport { clearCache } from './cache.ts';\nimport type { EmbedChangeKind } from './embed-scope.ts';\nimport { classifyEmbedChange } from './embed-scope.ts';\nimport type { FeatureToggle } from './feature-scope.ts';\nimport { classifyFeatureToggles, isFeatureOnlyChange } from './feature-scope.ts';\nimport { lockWaitBudgetMs } from './lock-wait.ts';\nimport { forcedPresetPaths, isPresetOnlyChange } from './preset-scope.ts';\nimport { getMeta, setMeta } from './shared.ts';\nimport { changedSignatureKeys, embedIdentityAdopted, signatureDiff } from './signature.ts';\nimport type { Stages } from './stages.ts';\nimport type { Connection, OpenDialect, Store } from './types.ts';\n\n// One open algorithm shared by every store, parameterised by a per-engine OpenDialect (types.ts).\n// Ordering is universal, not a dialect concern: connect, meta table, schema-version and\n// feature-signature comparison (rebuild, adopt, or partial rebuild), any extra rebuild trigger,\n// ensureSchema, the derived busy_timeout PRAGMA, reconcile, then the store itself.\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n // Per-stage wall time for this open's build pass (stages.ts), not for any narrow embed or\n // feature invalidation that ran beside it -- on a cold build there is none, so it is the whole cost.\n stages: Stages;\n}\n\ninterface ConnectResult<Handle> {\n handle: Handle;\n conn: Connection;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n stages: Stages;\n}\n\n// duckdb/turso hold the cache file for their connection's whole life, so a second command waits.\n// Bounded by this tree's recorded reconcile time (lock-wait.ts), not a fixed guess.\nconst LOCK_POLL_MS = 50;\nconst lockWait = channel('sensemaking.store.lock-wait');\n\nasync function connectUnlocked<Handle>(dbPath: string, cfg: ResolvedConfig, configDir: string, dialect: OpenDialect<Handle>): Promise<{ handle: Handle; conn: Connection }> {\n const budgetMs = lockWaitBudgetMs(configDir);\n const deadline = Date.now() + budgetMs;\n for (;;) {\n try {\n return await dialect.connect(dbPath, cfg);\n } catch (err) {\n if (!dialect.isLocked?.(err as Error)) throw err;\n // Observe a real rejected native connection, never merely the start of an async open.\n if (lockWait.hasSubscribers) lockWait.publish({ store: cfg.store, dbPath });\n if (Date.now() >= deadline) {\n throw new SenseError('STORE_BUSY', `another sense process is using this tree's ${cfg.store} cache (${dbPath}) and did not release it within ${Math.round(budgetMs / 1000)}s; wait for that command to finish, or set \"store\" to \"sqlite\" in sense.config.json, which serves concurrent commands`);\n }\n await new Promise((resolve) => setTimeout(resolve, LOCK_POLL_MS));\n }\n }\n}\n\nasync function connectWithDialect<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): Promise<ConnectResult<Handle>> {\n const configDir = cfg.configDir ?? cfg.baseDir;\n const stateDir = join(configDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, dialect.filename);\n\n const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect);\n // A throw below must release this handle, or the leaked WAL makes the cache file undeletable on\n // Windows. `closed` keeps the catch from double-closing a handle a rebuild branch already closed.\n let closed = false;\n // Created here, not inside reconcile(): invalidate()/invalidateFeatures() below share its pool\n // with the build() call that follows them.\n const builder = createBuilder(conn, cfg, { rootDir: cfg.rootDir ?? cfg.baseDir, configDir }, dialect.reconcileDialect);\n try {\n await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const wantFeatures = featureSignature(cfg, FEATURES);\n // Set only by the preset-membership branch below; reconcile treats these paths as touched\n // despite an unchanged stamp, for this one build() call.\n let forcedPaths: Set<string> | undefined;\n // Set only by the embed-narrow branch below; applied via builder.invalidate() after ensureSchema.\n let embedInvalidate: EmbedChangeKind | undefined;\n // Set only by the feature-toggle branch below; applied via builder.invalidateFeatures() after ensureSchema.\n let featureToggles: FeatureToggle[] | undefined;\n if ((version !== null && version !== dialect.schemaVersion) || (features !== null && features !== wantFeatures)) {\n // Indexing derives from presets, so a config edit rebuilding the cache must say so and\n // name what changed -- silent rebuilds make derived indexing look like a hang or a bug.\n if (version !== null && version !== dialect.schemaVersion) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n // Close before clearCache deletes the files out from under the still-open handle.\n closed = true;\n await dialect.close(handle);\n clearCache(cfg);\n return connectWithDialect(cfg, dialect);\n }\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n // Null (rather than the empty set) when changedKeys isn't preset-only, or a changed\n // preset's stored segment can't be parsed back into old include/exclude/vectors -- either\n // way the branch below is skipped and the full rebuild stays the fallback.\n const presetForced = isPresetOnlyChange(changedKeys) ? forcedPresetPaths(cfg, cfg.rootDir ?? cfg.baseDir, features ?? '', changedKeys) : null;\n // Null unless exactly one segment changed and it decomposes into a recognised embed-only\n // case (embed-scope.ts): anything else, including embed toggled on/off, stays null.\n const embedKind = changedKeys.size === 1 && changedKeys.has('embed') ? classifyEmbedChange(features ?? '', wantFeatures) : null;\n // Null (rather than the empty set) when changedKeys isn't feature-toggle-only, or a changed\n // feature's stored segment can't be parsed back into old/new on-off -- either way the branch\n // below is skipped and the full rebuild stays the fallback.\n const toggles = isFeatureOnlyChange(changedKeys) ? classifyFeatureToggles(features ?? '', wantFeatures, changedKeys) : null;\n if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features ?? '', wantFeatures)) {\n // First sight of a resolved weight identity: the model itself hasn't changed, so\n // adopt it into meta with no rebuild and no re-embed.\n console.error(\"sense: recorded the embedding model's resolved identity; vectors are unaffected\");\n await setMeta(conn, 'features', wantFeatures);\n } else if (embedKind !== null) {\n // Chunk boundaries are file-derived, not model-derived (embed.ts's chunksOf), so neither\n // case needs any file reparsed for any feature but embed itself.\n embedInvalidate = embedKind;\n console.error(embedKind === 'model' ? 'sense: config change (embed settings) invalidates only the stale vectors' : 'sense: config change (embed settings) rebuilds only the embeddings it affects');\n await setMeta(conn, 'features', wantFeatures);\n } else if (presetForced !== null) {\n // Membership only: which files a preset covers moved, not a file's own content, a\n // global feature, or the embed model. reconcile() already knows how to\n // add, update, and remove a file correctly (every cross-feature cascade included); it\n // just needs telling which unchanged files to treat as touched (forcedPaths, above).\n forcedPaths = presetForced;\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) reparses only the files it affects`);\n await setMeta(conn, 'features', wantFeatures);\n } else if (toggles !== null) {\n // A feature turned off or on: reconcile.ts's activeFeatures skips a disabled feature's\n // hooks entirely, so its rows rot while off -- invalidateFeatures drops them and, for a\n // feature turning back on, fully re-derives across every indexed file rather than\n // trusting rows written before or during that window.\n featureToggles = toggles;\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) reparses only the feature it affects`);\n await setMeta(conn, 'features', wantFeatures);\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n closed = true;\n await dialect.close(handle);\n clearCache(cfg);\n return connectWithDialect(cfg, dialect);\n }\n }\n\n await dialect.ensureSchema(handle, conn, cfg);\n\n // 3x the largest reconcile this cache has recorded, floored at 30s and capped at 10min.\n // Installed before build() -- that call is the one that races a watcher's transaction.\n if (dialect.setDerivedBusyTimeout) {\n const recordedMaxMs = Number((await getMeta(conn, 'reconcile_max_ms')) ?? '0');\n await dialect.setDerivedBusyTimeout(handle, conn, Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000));\n }\n\n // Runs before build(): forcedPaths, embedInvalidate and featureToggles never target the same\n // call (each comes from its own mutually exclusive branch above: a preset-only, embed-only, or\n // feature-toggle-only changed-key set).\n const embedParsed = embedInvalidate ? (await builder.invalidate(embedInvalidate)).parsed : 0;\n const featureParsed = featureToggles ? (await builder.invalidateFeatures(featureToggles)).parsed : 0;\n\n const { parsed, warnings, stages } = await builder.build(forcedPaths);\n // Every open reconciles once and is done with it -- a watcher opens fresh per event instead of holding this connection (src/watch.ts).\n await builder.close();\n\n return { handle, conn, cfg, dbPath, parsed: parsed + embedParsed + featureParsed, warnings, stages };\n } catch (err) {\n await builder.close();\n if (!closed) await dialect.close(handle);\n throw err;\n }\n}\n\n// A store's open(): connects (see connectWithDialect above), then wraps the resulting connection\n// in the Store interface. The builder's pool closes right after the initial build.\nexport async function openWithDialect<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): Promise<OpenResult> {\n // `baseDir` was the original public open() input. Keep direct API callers working while\n // making the two ownership paths explicit for every internal operation.\n if (!cfg.rootDir) {\n if (!cfg.baseDir) throw new SenseError('CONFIG_INVALID', 'open requires rootDir (or legacy baseDir)');\n cfg.rootDir = cfg.baseDir;\n }\n if (!cfg.configDir) cfg.configDir = cfg.rootDir;\n const { handle, conn, cfg: resolvedCfg, dbPath, parsed, warnings, stages } = await connectWithDialect(cfg, dialect);\n const store = dialect.createStore(handle, conn, resolvedCfg);\n // reconcile ran before this object existed, so its chunk text is keyed by the connection.\n rekeyChunkText(conn, store);\n return { store, cfg: resolvedCfg, dbPath, parsed, warnings, stages };\n}\n\nexport async function docCount(store: Store): Promise<number> {\n const stmt = await store.prepare('SELECT COUNT(*) AS n FROM frontmatter');\n return ((await stmt.get()) as { n: number }).n;\n}\n"],"names":["channel","mkdirSync","join","featureSignature","STATE_DIR","rekeyChunkText","SenseError","FEATURES","createBuilder","clearCache","classifyEmbedChange","classifyFeatureToggles","isFeatureOnlyChange","lockWaitBudgetMs","forcedPresetPaths","isPresetOnlyChange","getMeta","setMeta","changedSignatureKeys","embedIdentityAdopted","signatureDiff","LOCK_POLL_MS","lockWait","connectUnlocked","dbPath","cfg","configDir","dialect","budgetMs","deadline","Date","now","connect","err","isLocked","hasSubscribers","publish","store","Math","round","Promise","resolve","setTimeout","connectWithDialect","baseDir","stateDir","recursive","filename","handle","conn","closed","builder","rootDir","reconcileDialect","exec","version","features","wantFeatures","forcedPaths","embedInvalidate","featureToggles","schemaVersion","console","error","close","changedKeys","presetForced","embedKind","size","has","toggles","changed","ensureSchema","setDerivedBusyTimeout","recordedMaxMs","Number","min","max","embedParsed","invalidate","parsed","featureParsed","invalidateFeatures","warnings","stages","build","openWithDialect","resolvedCfg","createStore","docCount","stmt","prepare","get","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/open.ts"],"sourcesContent":["import { channel } from 'node:diagnostics_channel';\nimport { existsSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { anyPresetEmbeds, featureSignature, STATE_DIR } from '../config/index.ts';\nimport { rekeyChunkText } from '../embed/handoff.ts';\nimport { embedPending } from '../embed/query.ts';\nimport { SenseError } from '../errors.ts';\nimport { FEATURES } from '../features/index.ts';\nimport { createBuilder } from './builder.ts';\nimport { clearCache } from './cache.ts';\nimport type { EmbedChangeKind } from './embed-scope.ts';\nimport { classifyEmbedChange } from './embed-scope.ts';\nimport type { FeatureToggle } from './feature-scope.ts';\nimport { classifyFeatureToggles, isFeatureOnlyChange } from './feature-scope.ts';\nimport { lockWaitBudgetMs } from './lock-wait.ts';\nimport { forcedPresetPaths, isPresetOnlyChange } from './preset-scope.ts';\nimport { getMeta, setMeta } from './shared.ts';\nimport { changedSignatureKeys, embedIdentityAdopted, signatureDiff } from './signature.ts';\nimport type { Stages } from './stages.ts';\nimport { stageRecorder } from './stages.ts';\nimport { withTransaction } from './transaction.ts';\nimport type { Connection, OpenDialect, Store } from './types.ts';\n\nexport type BuildRequirement = 'core' | 'lexical' | 'vectors';\n\nexport interface OpenOptions {\n build?: boolean;\n}\n\nexport interface InternalOpenOptions extends OpenOptions {\n requirements?: ReadonlySet<BuildRequirement>;\n}\n\nexport interface OpenResult {\n store: Store;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n stages: Stages;\n}\n\ninterface ConnectResult<Handle> {\n handle: Handle;\n conn: Connection;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n stages: Stages;\n observational: boolean;\n}\n\nconst CORE_READY_META_KEY = 'core_ready';\nconst LOCK_POLL_MS = 50;\nconst lockWait = channel('sensemaking.store.lock-wait');\n\nfunction notReady(reason: string): SenseError {\n return new SenseError('INDEX_NOT_READY', `${reason}; run \"sense build\" or call build(config) before opening with { build: false }`);\n}\n\nfunction normaliseConfig(cfg: ResolvedConfig): ResolvedConfig {\n // `baseDir` was the original public open() input. Keep direct API callers working while\n // making the two ownership paths explicit for every internal operation.\n if (!cfg.rootDir) {\n if (!cfg.baseDir) throw new SenseError('CONFIG_INVALID', 'open requires rootDir (or legacy baseDir)');\n cfg.rootDir = cfg.baseDir;\n }\n if (!cfg.configDir) cfg.configDir = cfg.rootDir;\n return cfg;\n}\n\nfunction cachePath<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>): string {\n return join(cfg.configDir ?? cfg.baseDir, STATE_DIR, dialect.filename);\n}\n\nasync function connectUnlocked<Handle>(dbPath: string, cfg: ResolvedConfig, configDir: string, dialect: OpenDialect<Handle>, options: { existingOnly: boolean; observational: boolean }): Promise<{ handle: Handle; conn: Connection }> {\n const budgetMs = lockWaitBudgetMs(configDir);\n const deadline = Date.now() + budgetMs;\n for (;;) {\n try {\n return await dialect.connect(dbPath, cfg, options);\n } catch (err) {\n if (options.existingOnly && !existsSync(dbPath)) {\n throw notReady(`the derived index does not exist at ${dbPath}`);\n }\n if (!dialect.isLocked?.(err as Error)) throw err;\n if (lockWait.hasSubscribers) lockWait.publish({ store: cfg.store, dbPath });\n if (Date.now() >= deadline) {\n throw new SenseError('STORE_BUSY', `another sense process is using this tree's ${cfg.store} cache (${dbPath}) and did not release it within ${Math.round(budgetMs / 1000)}s; wait for that command to finish, or set \"store\" to \"sqlite\" in sense.config.json, which serves concurrent commands`);\n }\n await new Promise((resolve) => setTimeout(resolve, LOCK_POLL_MS));\n }\n }\n}\n\nasync function assertCoreSchema(conn: Connection): Promise<void> {\n for (const table of ['frontmatter', 'content', 'preset_files', 'indexed_sources']) {\n await (await conn.prepare(`SELECT 1 FROM ${table} LIMIT 0`)).all();\n }\n}\n\nasync function connectExisting<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>, requirements: ReadonlySet<BuildRequirement>): Promise<ConnectResult<Handle>> {\n const configDir = cfg.configDir ?? cfg.baseDir;\n const dbPath = cachePath(cfg, dialect);\n if (!existsSync(dbPath)) throw notReady(`the derived index does not exist at ${dbPath}`);\n\n const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect, { existingOnly: true, observational: true });\n try {\n let version: string | null;\n let features: string | null;\n let ready: string | null;\n try {\n version = await getMeta(conn, 'schema_version');\n features = await getMeta(conn, 'features');\n ready = await getMeta(conn, CORE_READY_META_KEY);\n await assertCoreSchema(conn);\n } catch {\n if (!existsSync(dbPath)) throw notReady(`the derived index was deleted while opening ${dbPath}`);\n throw notReady(`the derived index at ${dbPath} has no compatible schema`);\n }\n\n if (version !== dialect.schemaVersion) {\n throw notReady(`the derived index at ${dbPath} uses schema ${version ?? 'unknown'}, but this sense version requires ${dialect.schemaVersion}`);\n }\n const wantFeatures = featureSignature(cfg, FEATURES);\n if (features !== wantFeatures) {\n throw notReady(`the derived index at ${dbPath} was built for different configuration features`);\n }\n if (ready !== '1') {\n throw notReady(`the derived index at ${dbPath} has an incomplete core build`);\n }\n if (requirements.has('lexical')) {\n await dialect.assertLexicalReady?.(conn);\n }\n if (!existsSync(dbPath)) throw notReady(`the derived index was deleted while opening ${dbPath}`);\n\n return {\n handle,\n conn,\n cfg,\n dbPath,\n parsed: 0,\n warnings: [],\n stages: stageRecorder().take(0, 0),\n observational: true,\n };\n } catch (err) {\n await dialect.close(handle, { observational: true });\n throw err;\n }\n}\n\nasync function connectForBuild<Handle>(cfg: ResolvedConfig, dialect: OpenDialect<Handle>, requirements: ReadonlySet<BuildRequirement>): Promise<ConnectResult<Handle>> {\n const configDir = cfg.configDir ?? cfg.baseDir;\n const stateDir = join(configDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, dialect.filename);\n\n const { handle, conn } = await connectUnlocked(dbPath, cfg, configDir, dialect, { existingOnly: false, observational: false });\n let closed = false;\n const builder = createBuilder(conn, cfg, { rootDir: cfg.rootDir ?? cfg.baseDir, configDir }, dialect.reconcileDialect);\n try {\n await conn.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n const version = await getMeta(conn, 'schema_version');\n const features = await getMeta(conn, 'features');\n const ready = await getMeta(conn, CORE_READY_META_KEY);\n const wantFeatures = featureSignature(cfg, FEATURES);\n // `ready=0` may belong to another SQLite builder, so recovery stays in place and lets the\n // engine serialize it. It never unlinks the cache.\n await setMeta(conn, CORE_READY_META_KEY, '0');\n const recoverIncomplete = ready === '0' && (version === null || version === dialect.schemaVersion);\n let forcedPaths: Set<string> | undefined;\n let embedInvalidate: EmbedChangeKind | undefined;\n let featureToggles: FeatureToggle[] | undefined;\n if (version !== null && version !== dialect.schemaVersion) {\n console.error('sense: cache format changed (new sensemaking version); rebuilding the index');\n closed = true;\n await dialect.close(handle);\n clearCache(cfg);\n return connectForBuild(cfg, dialect, requirements);\n }\n if (recoverIncomplete) {\n console.error('sense: recovering an incomplete index generation in place');\n } else if (features !== null && features !== wantFeatures) {\n const changedKeys = changedSignatureKeys(features ?? '', wantFeatures);\n const presetForced = isPresetOnlyChange(changedKeys) ? forcedPresetPaths(cfg, cfg.rootDir ?? cfg.baseDir, features ?? '', changedKeys) : null;\n const embedKind = changedKeys.size === 1 && changedKeys.has('embed') ? classifyEmbedChange(features ?? '', wantFeatures) : null;\n const toggles = isFeatureOnlyChange(changedKeys) ? classifyFeatureToggles(features ?? '', wantFeatures, changedKeys) : null;\n if (changedKeys.size === 1 && changedKeys.has('embed') && embedIdentityAdopted(features ?? '', wantFeatures)) {\n console.error(\"sense: recorded the embedding model's resolved identity; vectors are unaffected\");\n } else if (embedKind !== null) {\n embedInvalidate = embedKind;\n console.error(embedKind === 'model' ? 'sense: config change (embed settings) invalidates only the stale vectors' : 'sense: config change (embed settings) rebuilds only the embeddings it affects');\n } else if (presetForced !== null) {\n forcedPaths = presetForced;\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) reparses only the files it affects`);\n } else if (toggles !== null) {\n featureToggles = toggles;\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) reparses only the feature it affects`);\n } else {\n const changed = signatureDiff(features ?? '', wantFeatures);\n console.error(`sense: config change (${changed}) rebuilds the index`);\n closed = true;\n await dialect.close(handle);\n clearCache(cfg);\n return connectForBuild(cfg, dialect, requirements);\n }\n }\n\n await dialect.ensureSchema(handle, conn, cfg);\n\n if (dialect.setDerivedBusyTimeout) {\n const recordedMaxMs = Number((await getMeta(conn, 'reconcile_max_ms')) ?? '0');\n await dialect.setDerivedBusyTimeout(handle, conn, Math.min(Math.max(30000, 3 * recordedMaxMs), 600_000));\n }\n\n const embedParsed = embedInvalidate ? (await builder.invalidate(embedInvalidate)).parsed : 0;\n const featureParsed = featureToggles ? (await builder.invalidateFeatures(featureToggles)).parsed : 0;\n const { parsed, warnings, stages } = recoverIncomplete ? await builder.recover() : await builder.build(forcedPaths);\n await withTransaction(conn, async () => {\n await setMeta(conn, 'features', wantFeatures);\n await setMeta(conn, CORE_READY_META_KEY, '1');\n });\n\n // Lexical preparation is a separate capability stage. A failure leaves core usable and the\n // dialect's own lexical marker stale, so unrelated map/peek/path/sql commands stay available.\n if (requirements.has('lexical')) {\n await dialect.prepareLexical?.(conn);\n }\n\n await builder.close();\n return { handle, conn, cfg, dbPath, parsed: parsed + embedParsed + featureParsed, warnings, stages, observational: false };\n } catch (err) {\n await builder.close();\n if (!closed) await dialect.close(handle);\n throw err;\n }\n}\n\n// A static provider can resolve its identity while embedding. Publish it only after vectors\n// succeed and only when the durable signature still matches the prepared generation.\nexport async function prepareDocumentEmbeddings(store: Store, cfg: ResolvedConfig, allowed?: ReadonlySet<string>): Promise<void> {\n const preparedSignature = await getMeta(store, 'features');\n await embedPending(store, cfg, allowed);\n const actualSignature = featureSignature(cfg, FEATURES);\n if (preparedSignature === actualSignature) return;\n\n const changedKeys = changedSignatureKeys(preparedSignature ?? '', actualSignature);\n if (preparedSignature === null || changedKeys.size !== 1 || !changedKeys.has('embed') || !embedIdentityAdopted(preparedSignature, actualSignature)) {\n throw notReady('the index configuration changed while vectors were being prepared');\n }\n\n const update = await store.prepare('UPDATE meta SET value = ? WHERE key = ? AND value = ?');\n const result = await update.run(actualSignature, 'features', preparedSignature);\n if (Number(result.changes) !== 1) throw notReady('another build changed the index configuration while vectors were being prepared');\n}\n\nexport async function openWithDialect<Handle>(input: ResolvedConfig, dialect: OpenDialect<Handle>, options: InternalOpenOptions = {}): Promise<OpenResult> {\n const cfg = normaliseConfig(input);\n const buildEnabled = options.build !== false;\n const defaultRequirements = new Set<BuildRequirement>(buildEnabled ? ['core', 'lexical'] : ['core']);\n if (buildEnabled && anyPresetEmbeds(cfg)) defaultRequirements.add('vectors');\n const requirements = options.requirements ?? defaultRequirements;\n const connected = buildEnabled ? await connectForBuild(cfg, dialect, requirements) : await connectExisting(cfg, dialect, requirements);\n let store: Store | undefined;\n try {\n store = dialect.createStore(connected.handle, connected.conn, connected.cfg, { observational: connected.observational });\n rekeyChunkText(connected.conn, store);\n if (requirements.has('vectors') && buildEnabled) await prepareDocumentEmbeddings(store, connected.cfg);\n return {\n store,\n cfg: connected.cfg,\n dbPath: connected.dbPath,\n parsed: connected.parsed,\n warnings: connected.warnings,\n stages: connected.stages,\n };\n } catch (err) {\n if (store) await store.close();\n else await dialect.close(connected.handle, { observational: connected.observational });\n throw err;\n }\n}\n\nexport async function docCount(store: Store): Promise<number> {\n const stmt = await store.prepare('SELECT COUNT(*) AS n FROM frontmatter');\n return ((await stmt.get()) as { n: number }).n;\n}\n"],"names":["channel","existsSync","mkdirSync","join","anyPresetEmbeds","featureSignature","STATE_DIR","rekeyChunkText","embedPending","SenseError","FEATURES","createBuilder","clearCache","classifyEmbedChange","classifyFeatureToggles","isFeatureOnlyChange","lockWaitBudgetMs","forcedPresetPaths","isPresetOnlyChange","getMeta","setMeta","changedSignatureKeys","embedIdentityAdopted","signatureDiff","stageRecorder","withTransaction","CORE_READY_META_KEY","LOCK_POLL_MS","lockWait","notReady","reason","normaliseConfig","cfg","rootDir","baseDir","configDir","cachePath","dialect","filename","connectUnlocked","dbPath","options","budgetMs","deadline","Date","now","connect","err","existingOnly","isLocked","hasSubscribers","publish","store","Math","round","Promise","resolve","setTimeout","assertCoreSchema","conn","table","prepare","all","connectExisting","requirements","handle","observational","version","features","ready","schemaVersion","wantFeatures","has","assertLexicalReady","parsed","warnings","stages","take","close","connectForBuild","stateDir","recursive","closed","builder","reconcileDialect","exec","recoverIncomplete","forcedPaths","embedInvalidate","featureToggles","console","error","changedKeys","presetForced","embedKind","size","toggles","changed","ensureSchema","setDerivedBusyTimeout","recordedMaxMs","Number","min","max","embedParsed","invalidate","featureParsed","invalidateFeatures","recover","build","prepareLexical","prepareDocumentEmbeddings","allowed","preparedSignature","actualSignature","update","result","run","changes","openWithDialect","input","buildEnabled","defaultRequirements","Set","add","connected","createStore","docCount","stmt","get","n"],"mappings":"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"}
|
|
@@ -134,6 +134,18 @@ export async function reconcile(conn, cfg, paths, dialect, pool, forcedPaths) {
|
|
|
134
134
|
// After the upsert, so every doc already has the frontmatter row sqlite's content rowid
|
|
135
135
|
// couples to. ON CONFLICT DO UPDATE preserves that rowid, so a reparse keeps its identity.
|
|
136
136
|
await stages.time('text-index', ()=>dialect.reconcileContent(conn, contentTouched, parsedDocs, delta, cfg));
|
|
137
|
+
// Exact source text belongs to the same indexed generation as content and section ranges.
|
|
138
|
+
// The upsert also covers an added path another writer committed while this build waited.
|
|
139
|
+
if (contentTouched.length > 0) await conn.runBatch('DELETE FROM indexed_sources WHERE "path" = ?', contentTouched.map((p)=>[
|
|
140
|
+
p
|
|
141
|
+
]));
|
|
142
|
+
await appendRows(conn, 'indexed_sources', [
|
|
143
|
+
'path',
|
|
144
|
+
'text'
|
|
145
|
+
], 'INSERT INTO indexed_sources ("path", text) VALUES (?, ?) ON CONFLICT("path") DO UPDATE SET text = excluded.text', parsedDocs.map((doc)=>[
|
|
146
|
+
doc.relPath,
|
|
147
|
+
doc.source
|
|
148
|
+
]));
|
|
137
149
|
// A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;
|
|
138
150
|
// new docs have nothing to clear, which keeps cold builds linear.
|
|
139
151
|
await stages.time('presets', async ()=>{
|
|
@@ -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 { recordLockWaitMs } from './lock-wait.ts';\nimport { appendRows, getColumns, quoteIdent } from './shared.ts';\nimport { featureStage, type Stages, stageRecorder } from './stages.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.\nexport const CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\nexport interface ReconcilePaths {\n rootDir: string;\n configDir: string;\n}\n\nexport async function reconcile(conn: Connection, cfg: Config, paths: ReconcilePaths, dialect: ReconcileDialect, pool?: ParsePool, forcedPaths?: ReadonlySet<string>): Promise<{ parsed: number; warnings: string[]; stages: Stages }> {\n const { rootDir, configDir } = paths;\n const start = process.hrtime.bigint();\n const features = activeFeatures(cfg);\n const stages = stageRecorder(features.map((f) => f.name));\n const elapsed = () => Number(process.hrtime.bigint() - start) / 1e6;\n const files = await stages.time('list', () => listFiles(cfg, rootDir));\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = await stages.time('existing', async () => {\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n return (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n });\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: [], stages: stages.take(elapsed(), 0) };\n\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 // Pool wall time, dispatch to drain, so this stage shares a clock with every other one.\n const { docs: parsedDocs, warnings, newColumns, workerParseMs } = await stages.time('parse', () => 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 stages.time('alter', () => 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 contentTouched = await stages.time('added-recheck', async () => {\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 return staleAdded.length > 0 ? [...touched, ...staleAdded] : touched;\n });\n\n if (parsedDocs.length > 0) {\n const toRow = (doc: (typeof parsedDocs)[number]) =>\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 // A path in `added` has no existing frontmatter row, so it can never conflict; the rest\n // genuinely can, and keep the upsert.\n await stages.time('fm-upsert', async () => {\n const newDocs = parsedDocs.filter((d) => addedSet.has(d.relPath));\n const updateDocs = parsedDocs.filter((d) => !addedSet.has(d.relPath));\n await appendRows(conn, 'frontmatter', writableColumns, insertSql, newDocs.map(toRow));\n if (updateDocs.length > 0) await conn.runBatch(insertSql, updateDocs.map(toRow));\n });\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 stages.time('text-index', () => 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 await stages.time('presets', async () => {\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 await appendRows(conn, 'preset_files', ['path', 'preset'], 'INSERT INTO preset_files (\"path\", preset) VALUES (?, ?) ON CONFLICT(\"path\", preset) DO NOTHING', presetRows);\n });\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 stages.time('vanished', () =>\n conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n )\n );\n\n // Timed per feature per hook, so link resolution and PageRank are named stages without\n // links.ts or rank.ts knowing anything about this, and a new feature is visible for free.\n if (touched.length > 0) for (const feature of features) await stages.time(featureStage(feature.name, 'remove'), () => 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 stages.time(featureStage(feature.name, 'store'), () => feature.store?.(conn, docsForFeature, delta));\n }\n for (const feature of features) await stages.time(featureStage(feature.name, 'after'), () => feature.afterReconcile?.(conn, delta));\n },\n dialect.beginMode()\n );\n\n const durationMs = Date.now() - txStart;\n // Every store, not just the ones with a PRAGMA to derive: connectUnlocked's lock-wait budget needs it too.\n recordLockWaitMs(configDir, durationMs);\n if (dialect.recordDuration) await stages.time('meta', () => dialect.recordDuration?.(conn, durationMs));\n\n return { parsed: parsedDocs.length, warnings, stages: stages.take(elapsed(), durationMs, workerParseMs) };\n}\n"],"names":["activeFeatures","progress","listFiles","RESERVED_COLUMNS","reparseFiles","recordLockWaitMs","appendRows","getColumns","quoteIdent","featureStage","stageRecorder","withTransaction","CORE_FRONTMATTER_COLUMNS","Set","reconcile","conn","cfg","paths","dialect","pool","forcedPaths","rootDir","configDir","start","process","hrtime","bigint","features","stages","map","f","name","elapsed","Number","files","time","currentSet","relPath","existingRows","existingStmt","prepare","all","existing","Map","r","path","vanished","filter","has","toReparse","row","get","_mtime","mtimeMs","_size","size","length","parsed","warnings","take","seenColumns","report","docs","parsedDocs","newColumns","workerParseMs","tick","finish","col","add","allColumns","checkColumnLimit","writableColumns","c","insertSql","join","added","delta","reparsed","d","addedSet","reparsedExisting","p","touched","txStart","Date","now","present","missingColumns","addColumns","contentTouched","currentPathsStmt","currentPaths","staleAdded","toRow","doc","ctimeMs","parseError","data","newDocs","updateDocs","runBatch","reconcileContent","presetRows","presetName","presets","push","feature","remove","docsForFeature","extracted","store","afterReconcile","beginMode","durationMs","recordDuration"],"mappings":"AACA,SAASA,cAAc,QAAQ,uBAAuB;AAEtD,SAASC,QAAQ,QAAQ,wBAAwB;AACjD,SAASC,SAAS,EAAEC,gBAAgB,QAAQ,mBAAmB;AAE/D,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,gBAAgB,QAAQ,iBAAiB;AAClD,SAASC,UAAU,EAAEC,UAAU,EAAEC,UAAU,QAAQ,cAAc;AACjE,SAASC,YAAY,EAAeC,aAAa,QAAQ,cAAc;AACvE,SAASC,eAAe,QAAQ,mBAAmB;AAGnD,gGAAgG;AAChG,gGAAgG;AAChG,qGAAqG;AACrG,6FAA6F;AAC7F,gFAAgF;AAEhF,6FAA6F;AAC7F,4EAA4E;AAC5E,OAAO,MAAMC,2BAA2B,IAAIC,IAAI;IAAC;IAAQ;IAAU;IAAU;IAAS;CAAe,EAAE;AAOvG,OAAO,eAAeC,UAAUC,IAAgB,EAAEC,GAAW,EAAEC,KAAqB,EAAEC,OAAyB,EAAEC,IAAgB,EAAEC,WAAiC;IAClK,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAE,GAAGL;IAC/B,MAAMM,QAAQC,QAAQC,MAAM,CAACC,MAAM;IACnC,MAAMC,WAAW3B,eAAegB;IAChC,MAAMY,SAASlB,cAAciB,SAASE,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IACvD,MAAMC,UAAU,IAAMC,OAAOT,QAAQC,MAAM,CAACC,MAAM,KAAKH,SAAS;IAChE,MAAMW,QAAQ,MAAMN,OAAOO,IAAI,CAAC,QAAQ,IAAMjC,UAAUc,KAAKK;IAC7D,MAAMe,aAAa,IAAIvB,IAAIqB,MAAML,GAAG,CAAC,CAACC,IAAMA,EAAEO,OAAO;IAErD,MAAMC,eAAe,MAAMV,OAAOO,IAAI,CAAC,YAAY;QACjD,MAAMI,eAAe,MAAMxB,KAAKyB,OAAO,CAAC;QACxC,OAAQ,MAAMD,aAAaE,GAAG;IAChC;IACA,MAAMC,WAAW,IAAIC,IAAIL,aAAaT,GAAG,CAAC,CAACe,IAAM;YAACA,EAAEC,IAAI;YAAED;SAAE;IAC5D,+FAA+F;IAC/F,+FAA+F;IAC/F,gFAAgF;IAChF,MAAME,WAAWR,aAAaS,MAAM,CAAC,CAACH,IAAM,CAACR,WAAWY,GAAG,CAACJ,EAAEC,IAAI,GAAGhB,GAAG,CAAC,CAACe,IAAMA,EAAEC,IAAI;IAEtF,6FAA6F;IAC7F,0FAA0F;IAC1F,MAAMI,YAAYf,MAAMa,MAAM,CAAC,CAACjB;;QAC9B,MAAMoB,MAAMR,SAASS,GAAG,CAACrB,EAAEO,OAAO;QAClC,OAAO,CAACa,OAAOA,IAAIE,MAAM,KAAKtB,EAAEuB,OAAO,IAAIH,IAAII,KAAK,KAAKxB,EAAEyB,IAAI,aAAKnC,wBAAAA,kCAAAA,YAAa4B,GAAG,CAAClB,EAAEO,OAAO,wCAAK;IACrG;IAEA,IAAIS,SAASU,MAAM,KAAK,KAAKP,UAAUO,MAAM,KAAK,GAAG,OAAO;QAAEC,QAAQ;QAAGC,UAAU,EAAE;QAAE9B,QAAQA,OAAO+B,IAAI,CAAC3B,WAAW;IAAG;IAEzH,MAAM4B,cAAc,MAAMrD,WAAWQ;IAErC,oFAAoF;IACpF,uDAAuD;IACvD,MAAM8C,SAAS5D,SAAS,mBAAmBgD,UAAUO,MAAM;IAC3D,wFAAwF;IACxF,MAAM,EAAEM,MAAMC,UAAU,EAAEL,QAAQ,EAAEM,UAAU,EAAEC,aAAa,EAAE,GAAG,MAAMrC,OAAOO,IAAI,CAAC,SAAS,IAAM/B,aAAa6C,WAAWtB,UAAUX,KAAK4C,aAAaC,OAAOK,IAAI,EAAE;YAAE/C;QAAK;IAC3K0C,OAAOM,MAAM;IACb,KAAK,MAAMC,OAAOJ,WAAYJ,YAAYS,GAAG,CAACD;IAE9C,MAAME,aAAa;WAAIV;KAAY;IACnC,yFAAyF;IACzF,iGAAiG;IACjG1C,QAAQqD,gBAAgB,CAACD,WAAWd,MAAM;IAC1C,0FAA0F;IAC1F,sEAAsE;IACtE,MAAMgB,kBAAkBF,WAAWvB,MAAM,CAAC,CAAC0B,IAAM7D,yBAAyBoC,GAAG,CAACyB,MAAM,CAACtE,iBAAiB6C,GAAG,CAACyB;IAC1G,yFAAyF;IACzF,mDAAmD;IACnD,MAAMC,YAAY,CAAC,yBAAyB,EAAEF,gBAAgB3C,GAAG,CAACrB,YAAYmE,IAAI,CAAC,MAAM,UAAU,EAAEH,gBAAgB3C,GAAG,CAAC,IAAM,KAAK8C,IAAI,CAAC,MAAM,oCAAoC,EAAEH,gBAClLzB,MAAM,CAAC,CAAC0B,IAAMA,MAAM,QACpB5C,GAAG,CAAC,CAAC4C,IAAM,GAAGjE,WAAWiE,GAAG,YAAY,EAAEjE,WAAWiE,IAAI,EACzDE,IAAI,CAAC,OAAO;IAEf,MAAMC,QAAQ3B,UAAUF,MAAM,CAAC,CAACjB,IAAM,CAACY,SAASM,GAAG,CAAClB,EAAEO,OAAO,GAAGR,GAAG,CAAC,CAACC,IAAMA,EAAEO,OAAO;IACpF,MAAMwC,QAAwB;QAAE3C;QAAO4C,UAAUf,WAAWlC,GAAG,CAAC,CAACkD,IAAMA,EAAE1C,OAAO;QAAGuC;QAAO9B;IAAS;IACnG,MAAMkC,WAAW,IAAInE,IAAI+D;IACzB,MAAMK,mBAAmBlB,WAAWlC,GAAG,CAAC,CAACkD,IAAMA,EAAE1C,OAAO,EAAEU,MAAM,CAAC,CAACmC,IAAM,CAACF,SAAShC,GAAG,CAACkC;IACtF,+FAA+F;IAC/F,0EAA0E;IAC1E,MAAMC,UAAU;WAAIrC;WAAamC;KAAiB;IAElD,MAAMG,UAAUC,KAAKC,GAAG;IACxB,MAAM3E,gBACJI,MACA;QACE,iGAAiG;QACjG,sFAAsF;QACtF,MAAMwE,UAAU,MAAMhF,WAAWQ;QACjC,MAAMyE,iBAAiBxB,WAAWjB,MAAM,CAAC,CAACqB,MAAQ,CAACmB,QAAQvC,GAAG,CAACoB;QAC/D,IAAIoB,eAAehC,MAAM,GAAG,GAAG,MAAM5B,OAAOO,IAAI,CAAC,SAAS,IAAMjB,QAAQuE,UAAU,CAAC1E,MAAMyE;QAEzF,yFAAyF;QACzF,sFAAsF;QACtF,sFAAsF;QACtF,sFAAsF;QACtF,4FAA4F;QAC5F,IAAIE,iBAAiBP;QACrB,IAAIP,MAAMpB,MAAM,GAAG,GACjBkC,iBAAiB,MAAM9D,OAAOO,IAAI,CAAC,iBAAiB;YAClD,MAAMwD,mBAAmB,MAAM5E,KAAKyB,OAAO,CAAC;YAC5C,MAAMoD,eAAe,IAAI/E,IAAI,AAAE,CAAA,MAAM8E,iBAAiBlD,GAAG,EAAC,EAA+BZ,GAAG,CAAC,CAACe,IAAMA,EAAEC,IAAI;YAC1G,MAAMgD,aAAajB,MAAM7B,MAAM,CAAC,CAACmC,IAAMU,aAAa5C,GAAG,CAACkC;YACxD,OAAOW,WAAWrC,MAAM,GAAG,IAAI;mBAAI2B;mBAAYU;aAAW,GAAGV;QAC/D;QAEF,IAAIpB,WAAWP,MAAM,GAAG,GAAG;YACzB,MAAMsC,QAAQ,CAACC,MACbvB,gBAAgB3C,GAAG,CAAC,CAACuC;wBAOZ2B;oBANP,IAAI3B,QAAQ,QAAQ,OAAO2B,IAAI1D,OAAO;oBACtC,IAAI+B,QAAQ,UAAU,OAAO2B,IAAI1C,OAAO;oBACxC,IAAIe,QAAQ,UAAU,OAAO2B,IAAIC,OAAO;oBACxC,IAAI5B,QAAQ,SAAS,OAAO2B,IAAIxC,IAAI;oBACpC,mFAAmF;oBACnF,IAAIa,QAAQ,gBAAgB,OAAO2B,IAAIE,UAAU;oBACjD,QAAOF,gBAAAA,IAAIG,IAAI,CAAC9B,IAAI,cAAb2B,2BAAAA,gBAAiB;gBAC1B;YACF,wFAAwF;YACxF,sCAAsC;YACtC,MAAMnE,OAAOO,IAAI,CAAC,aAAa;gBAC7B,MAAMgE,UAAUpC,WAAWhB,MAAM,CAAC,CAACgC,IAAMC,SAAShC,GAAG,CAAC+B,EAAE1C,OAAO;gBAC/D,MAAM+D,aAAarC,WAAWhB,MAAM,CAAC,CAACgC,IAAM,CAACC,SAAShC,GAAG,CAAC+B,EAAE1C,OAAO;gBACnE,MAAM/B,WAAWS,MAAM,eAAeyD,iBAAiBE,WAAWyB,QAAQtE,GAAG,CAACiE;gBAC9E,IAAIM,WAAW5C,MAAM,GAAG,GAAG,MAAMzC,KAAKsF,QAAQ,CAAC3B,WAAW0B,WAAWvE,GAAG,CAACiE;YAC3E;QACF;QAEA,wFAAwF;QACxF,2FAA2F;QAC3F,MAAMlE,OAAOO,IAAI,CAAC,cAAc,IAAMjB,QAAQoF,gBAAgB,CAACvF,MAAM2E,gBAAgB3B,YAAYc,OAAO7D;QAExG,0FAA0F;QAC1F,kEAAkE;QAClE,MAAMY,OAAOO,IAAI,CAAC,WAAW;YAC3B,IAAIgD,QAAQ3B,MAAM,GAAG,GACnB,MAAMzC,KAAKsF,QAAQ,CACjB,6CACAlB,QAAQtD,GAAG,CAAC,CAACqD,IAAM;oBAACA;iBAAE;YAE1B,MAAMqB,aAA0B,EAAE;YAClC,KAAK,MAAMR,OAAOhC,WAAY,KAAK,MAAMyC,cAAcT,IAAIU,OAAO,CAAEF,WAAWG,IAAI,CAAC;gBAACX,IAAI1D,OAAO;gBAAEmE;aAAW;YAC7G,uFAAuF;YACvF,wFAAwF;YACxF,uGAAuG;YACvG,MAAMlG,WAAWS,MAAM,gBAAgB;gBAAC;gBAAQ;aAAS,EAAE,kGAAkGwF;QAC/J;QAEA,+FAA+F;QAC/F,4FAA4F;QAC5F,IAAIzD,SAASU,MAAM,GAAG,GACpB,MAAM5B,OAAOO,IAAI,CAAC,YAAY,IAC5BpB,KAAKsF,QAAQ,CACX,4CACAvD,SAASjB,GAAG,CAAC,CAACqD,IAAM;oBAACA;iBAAE;QAI7B,uFAAuF;QACvF,0FAA0F;QAC1F,IAAIC,QAAQ3B,MAAM,GAAG,GAAG,KAAK,MAAMmD,WAAWhF,SAAU,MAAMC,OAAOO,IAAI,CAAC1B,aAAakG,QAAQ5E,IAAI,EAAE,WAAW;gBAAM4E;oBAAAA,kBAAAA,QAAQC,MAAM,cAAdD,sCAAAA,qBAAAA,SAAiB5F,MAAMoE,SAASN;;QACtJ,KAAK,MAAM8B,WAAWhF,SAAU;YAC9B,MAAMkF,iBAAiC9C,WAAWlC,GAAG,CAAC,CAACkE,MAAS,CAAA;oBAAElD,MAAMkD,IAAI1D,OAAO;oBAAEyE,WAAWf,IAAIe,SAAS,CAACH,QAAQ5E,IAAI,CAAC;gBAAC,CAAA;YAC5H,MAAMH,OAAOO,IAAI,CAAC1B,aAAakG,QAAQ5E,IAAI,EAAE,UAAU;oBAAM4E;wBAAAA,iBAAAA,QAAQI,KAAK,cAAbJ,qCAAAA,oBAAAA,SAAgB5F,MAAM8F,gBAAgBhC;;QACrG;QACA,KAAK,MAAM8B,WAAWhF,SAAU,MAAMC,OAAOO,IAAI,CAAC1B,aAAakG,QAAQ5E,IAAI,EAAE,UAAU;gBAAM4E;oBAAAA,0BAAAA,QAAQK,cAAc,cAAtBL,8CAAAA,6BAAAA,SAAyB5F,MAAM8D;;IAC9H,GACA3D,QAAQ+F,SAAS;IAGnB,MAAMC,aAAa7B,KAAKC,GAAG,KAAKF;IAChC,2GAA2G;IAC3G/E,iBAAiBiB,WAAW4F;IAC5B,IAAIhG,QAAQiG,cAAc,EAAE,MAAMvF,OAAOO,IAAI,CAAC,QAAQ;YAAMjB;gBAAAA,0BAAAA,QAAQiG,cAAc,cAAtBjG,8CAAAA,6BAAAA,SAAyBH,MAAMmG;;IAE3F,OAAO;QAAEzD,QAAQM,WAAWP,MAAM;QAAEE;QAAU9B,QAAQA,OAAO+B,IAAI,CAAC3B,WAAWkF,YAAYjD;IAAe;AAC1G"}
|
|
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 { recordLockWaitMs } from './lock-wait.ts';\nimport { appendRows, getColumns, quoteIdent } from './shared.ts';\nimport { featureStage, type Stages, stageRecorder } from './stages.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.\nexport const CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\nexport interface ReconcilePaths {\n rootDir: string;\n configDir: string;\n}\n\nexport async function reconcile(conn: Connection, cfg: Config, paths: ReconcilePaths, dialect: ReconcileDialect, pool?: ParsePool, forcedPaths?: ReadonlySet<string>): Promise<{ parsed: number; warnings: string[]; stages: Stages }> {\n const { rootDir, configDir } = paths;\n const start = process.hrtime.bigint();\n const features = activeFeatures(cfg);\n const stages = stageRecorder(features.map((f) => f.name));\n const elapsed = () => Number(process.hrtime.bigint() - start) / 1e6;\n const files = await stages.time('list', () => listFiles(cfg, rootDir));\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = await stages.time('existing', async () => {\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n return (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n });\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: [], stages: stages.take(elapsed(), 0) };\n\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 // Pool wall time, dispatch to drain, so this stage shares a clock with every other one.\n const { docs: parsedDocs, warnings, newColumns, workerParseMs } = await stages.time('parse', () => 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 stages.time('alter', () => 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 contentTouched = await stages.time('added-recheck', async () => {\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 return staleAdded.length > 0 ? [...touched, ...staleAdded] : touched;\n });\n\n if (parsedDocs.length > 0) {\n const toRow = (doc: (typeof parsedDocs)[number]) =>\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 // A path in `added` has no existing frontmatter row, so it can never conflict; the rest\n // genuinely can, and keep the upsert.\n await stages.time('fm-upsert', async () => {\n const newDocs = parsedDocs.filter((d) => addedSet.has(d.relPath));\n const updateDocs = parsedDocs.filter((d) => !addedSet.has(d.relPath));\n await appendRows(conn, 'frontmatter', writableColumns, insertSql, newDocs.map(toRow));\n if (updateDocs.length > 0) await conn.runBatch(insertSql, updateDocs.map(toRow));\n });\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 stages.time('text-index', () => dialect.reconcileContent(conn, contentTouched, parsedDocs, delta, cfg));\n\n // Exact source text belongs to the same indexed generation as content and section ranges.\n // The upsert also covers an added path another writer committed while this build waited.\n if (contentTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM indexed_sources WHERE \"path\" = ?',\n contentTouched.map((p) => [p])\n );\n await appendRows(\n conn,\n 'indexed_sources',\n ['path', 'text'],\n 'INSERT INTO indexed_sources (\"path\", text) VALUES (?, ?) ON CONFLICT(\"path\") DO UPDATE SET text = excluded.text',\n parsedDocs.map((doc) => [doc.relPath, doc.source])\n );\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 await stages.time('presets', async () => {\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 await appendRows(conn, 'preset_files', ['path', 'preset'], 'INSERT INTO preset_files (\"path\", preset) VALUES (?, ?) ON CONFLICT(\"path\", preset) DO NOTHING', presetRows);\n });\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 stages.time('vanished', () =>\n conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n )\n );\n\n // Timed per feature per hook, so link resolution and PageRank are named stages without\n // links.ts or rank.ts knowing anything about this, and a new feature is visible for free.\n if (touched.length > 0) for (const feature of features) await stages.time(featureStage(feature.name, 'remove'), () => 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 stages.time(featureStage(feature.name, 'store'), () => feature.store?.(conn, docsForFeature, delta));\n }\n for (const feature of features) await stages.time(featureStage(feature.name, 'after'), () => feature.afterReconcile?.(conn, delta));\n },\n dialect.beginMode()\n );\n\n const durationMs = Date.now() - txStart;\n // Every store, not just the ones with a PRAGMA to derive: connectUnlocked's lock-wait budget needs it too.\n recordLockWaitMs(configDir, durationMs);\n if (dialect.recordDuration) await stages.time('meta', () => dialect.recordDuration?.(conn, durationMs));\n\n return { parsed: parsedDocs.length, warnings, stages: stages.take(elapsed(), durationMs, workerParseMs) };\n}\n"],"names":["activeFeatures","progress","listFiles","RESERVED_COLUMNS","reparseFiles","recordLockWaitMs","appendRows","getColumns","quoteIdent","featureStage","stageRecorder","withTransaction","CORE_FRONTMATTER_COLUMNS","Set","reconcile","conn","cfg","paths","dialect","pool","forcedPaths","rootDir","configDir","start","process","hrtime","bigint","features","stages","map","f","name","elapsed","Number","files","time","currentSet","relPath","existingRows","existingStmt","prepare","all","existing","Map","r","path","vanished","filter","has","toReparse","row","get","_mtime","mtimeMs","_size","size","length","parsed","warnings","take","seenColumns","report","docs","parsedDocs","newColumns","workerParseMs","tick","finish","col","add","allColumns","checkColumnLimit","writableColumns","c","insertSql","join","added","delta","reparsed","d","addedSet","reparsedExisting","p","touched","txStart","Date","now","present","missingColumns","addColumns","contentTouched","currentPathsStmt","currentPaths","staleAdded","toRow","doc","ctimeMs","parseError","data","newDocs","updateDocs","runBatch","reconcileContent","source","presetRows","presetName","presets","push","feature","remove","docsForFeature","extracted","store","afterReconcile","beginMode","durationMs","recordDuration"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import type { ResolvedConfig } from '../../config/index.js';
|
|
3
|
-
import type { OpenResult } from '../open.js';
|
|
3
|
+
import type { InternalOpenOptions, OpenResult } from '../open.js';
|
|
4
4
|
import type { OpenDialect } from '../types.js';
|
|
5
5
|
export declare const DB_FILENAME = "cache.db";
|
|
6
|
-
export declare const SCHEMA_VERSION = "
|
|
6
|
+
export declare const SCHEMA_VERSION = "21";
|
|
7
7
|
export type { OpenResult };
|
|
8
8
|
interface SqliteHandle {
|
|
9
9
|
db: DatabaseSync;
|
|
10
10
|
}
|
|
11
11
|
export declare const sqliteOpenDialect: OpenDialect<SqliteHandle>;
|
|
12
|
-
export declare function openSqlite(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
12
|
+
export declare function openSqlite(cfg: ResolvedConfig, options?: InternalOpenOptions): Promise<OpenResult>;
|