@wrongstack/tools 0.316.2 → 0.317.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_spawn-stream.d.ts +1 -1
- package/dist/audit.d.ts +37 -2
- package/dist/audit.js +4 -4
- package/dist/bash.js +1 -1
- package/dist/batch-tool-use.js +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tools.d.ts +16 -1
- package/dist/builtin.js +43 -43
- package/dist/{chunk-SDLVH5RZ.js → chunk-3QQSBUXR.js} +6 -5
- package/dist/{chunk-K3WNSHUF.js → chunk-45JPRMC2.js} +62 -36
- package/dist/{chunk-EJWSCQ6D.js → chunk-4AEZIFOW.js} +6 -5
- package/dist/{chunk-PQKK5JH7.js → chunk-4E42D7UF.js} +2 -2
- package/dist/{chunk-JRJJX3L5.js → chunk-5WNDJHT6.js} +3 -2
- package/dist/{chunk-7EY5DCRO.js → chunk-5ZYEGP2P.js} +4 -4
- package/dist/{chunk-MZZN7VCD.js → chunk-6RR5VX6U.js} +35 -17
- package/dist/{chunk-HYX2TZ7O.js → chunk-A2PX7M7A.js} +5 -5
- package/dist/{chunk-L25CVUVR.js → chunk-AK2K4KHR.js} +30 -22
- package/dist/{chunk-VFKPWSOX.js → chunk-BANPF4LY.js} +11 -7
- package/dist/{chunk-RGISBZBD.js → chunk-CPSAF2AU.js} +3 -2
- package/dist/{chunk-BSLVR56S.js → chunk-CPVLVQYT.js} +6 -5
- package/dist/{chunk-VJNHFZ7J.js → chunk-CVALEVGA.js} +6 -5
- package/dist/{chunk-PY2IOS7C.js → chunk-CWALNI6K.js} +5 -5
- package/dist/{chunk-LIJQMYYX.js → chunk-ELFFQ4RU.js} +7 -3
- package/dist/{chunk-JNNVRHMV.js → chunk-FJEQZHZN.js} +8 -8
- package/dist/{chunk-S5CCLZL3.js → chunk-FRN7TIAA.js} +2 -2
- package/dist/{chunk-I64BS3VC.js → chunk-FYDKHXN7.js} +2 -2
- package/dist/{chunk-NKJW5GFO.js → chunk-H6AWAZ2Y.js} +3 -3
- package/dist/{chunk-IVWV2UM6.js → chunk-HTYEO67J.js} +6 -5
- package/dist/{chunk-UNSKG6DC.js → chunk-ICAXDRP6.js} +10 -15
- package/dist/{chunk-IYK7XGDZ.js → chunk-JJQB6R7Y.js} +7 -7
- package/dist/{chunk-LWKUTMD5.js → chunk-MRZOVQ5D.js} +6 -5
- package/dist/{chunk-JOHXDF3D.js → chunk-NKX4SJ5R.js} +12 -9
- package/dist/{chunk-U4D5S3VQ.js → chunk-NWZ6XGBJ.js} +2 -1
- package/dist/{chunk-UKU2ZX4C.js → chunk-P7ZQ2FAJ.js} +27 -17
- package/dist/{chunk-4I4LWFOO.js → chunk-PQTONQ4P.js} +16 -11
- package/dist/{chunk-MQX2KXVW.js → chunk-RMB76PDS.js} +3 -3
- package/dist/{chunk-QZQSPUBF.js → chunk-RUOMW2I6.js} +4 -3
- package/dist/{chunk-553IQUMO.js → chunk-S2JRYDYG.js} +7 -6
- package/dist/{chunk-OUBDL3WH.js → chunk-SOG7XIRF.js} +11 -8
- package/dist/{chunk-ZJQ4IGXK.js → chunk-TDLZIDCY.js} +9 -7
- package/dist/{chunk-DA4KNE2H.js → chunk-TFEGJDH5.js} +303 -99
- package/dist/{chunk-AHHDWNXC.js → chunk-TKA4VEX2.js} +2 -2
- package/dist/{chunk-5H4GSJAI.js → chunk-UE3UBMCP.js} +7 -6
- package/dist/{chunk-IXMNTNW5.js → chunk-UKGMDCXT.js} +4 -3
- package/dist/{chunk-4UESMQDP.js → chunk-UOXX4SQZ.js} +7 -6
- package/dist/{chunk-T2F4S7U4.js → chunk-WI3VDXHD.js} +18 -8
- package/dist/{chunk-O4MBV4AX.js → chunk-WJYXMB2R.js} +6 -5
- package/dist/{chunk-U4VAATKP.js → chunk-WXJY3BKV.js} +16 -6
- package/dist/{chunk-DR7JOQTC.js → chunk-X5K3U6GI.js} +3 -3
- package/dist/{chunk-VXECAJJ7.js → chunk-XVGB6KYU.js} +37 -36
- package/dist/{chunk-TEBQ4VPZ.js → chunk-YOHO4DVT.js} +6 -5
- package/dist/{chunk-RXISBFR5.js → chunk-ZE6KLBUO.js} +5 -4
- package/dist/{chunk-22B7GE5T.js → chunk-ZHH3BEWU.js} +13 -12
- package/dist/codebase-index/bm25.d.ts +14 -1
- package/dist/codebase-index/index.js +8 -8
- package/dist/codebase-index/json-parser.d.ts +4 -0
- package/dist/codebase-index/parser-worker-script.js +2 -2
- package/dist/codebase-index/project-server.js +48 -9
- package/dist/codebase-index/worker.js +4 -4
- package/dist/codebase-index/writer-bulk-insert.d.ts +0 -1
- package/dist/codebase-index/writer-schema.d.ts +1 -1
- package/dist/codebase-index/writer.d.ts +38 -0
- package/dist/diff.js +1 -1
- package/dist/e2e.d.ts +40 -1
- package/dist/e2e.js +1 -1
- package/dist/edit.js +7 -7
- package/dist/exec.js +1 -1
- package/dist/fetch.js +1 -1
- package/dist/format.d.ts +42 -2
- package/dist/format.js +4 -4
- package/dist/git.d.ts +70 -1
- package/dist/git.js +1 -1
- package/dist/glob.js +3 -3
- package/dist/{go-parser-JXYKJHRC.js → go-parser-IKHW67WE.js} +2 -2
- package/dist/grep.js +3 -3
- package/dist/index.js +63 -45
- package/dist/install.d.ts +51 -2
- package/dist/install.js +4 -4
- package/dist/{json-parser-IBHZ4BZC.js → json-parser-2EOVP5MR.js} +81 -15
- package/dist/kanban.d.ts +19 -1
- package/dist/kanban.js +2 -2
- package/dist/languages/index.js +2 -2
- package/dist/{legacy-bridge-7M6QLUQN.js → legacy-bridge-SFNXIRAP.js} +4 -4
- package/dist/lint.js +4 -4
- package/dist/logs.js +1 -1
- package/dist/outdated.d.ts +25 -1
- package/dist/outdated.js +1 -1
- package/dist/pack.js +44 -44
- package/dist/{parser-dispatch-QH4XPGG6.js → parser-dispatch-X62UWXJS.js} +3 -3
- package/dist/patch.js +7 -7
- package/dist/plan.js +4 -4
- package/dist/{py-parser-IS4HKYMF.js → py-parser-TDXSQZBC.js} +2 -2
- package/dist/read.js +7 -7
- package/dist/replace.js +7 -7
- package/dist/{rs-parser-XHUUO2KZ.js → rs-parser-GIWPUA3T.js} +2 -2
- package/dist/scaffold.js +1 -1
- package/dist/search.js +1 -1
- package/dist/session-kanban.js +1 -1
- package/dist/task.js +4 -4
- package/dist/test.js +4 -4
- package/dist/todo.js +3 -3
- package/dist/tool-diff.js +1 -1
- package/dist/tool-tier.js +45 -45
- package/dist/tool-use.js +1 -1
- package/dist/{ts-parser-RCTYKSVI.js → ts-parser-7W26YB26.js} +2 -2
- package/dist/typecheck.js +4 -4
- package/dist/write.js +7 -7
- package/dist/{yaml-parser-4SAXV3PL.js → yaml-parser-RGBXEZE4.js} +2 -2
- package/package.json +5 -5
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseFilesContent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JJQB6R7Y.js";
|
|
4
4
|
import {
|
|
5
5
|
INDEXABLE_EXTENSIONS,
|
|
6
6
|
LANG_FAMILY_ENTRIES,
|
|
7
7
|
detectLang,
|
|
8
8
|
languageFamily
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-YOHO4DVT.js";
|
|
10
10
|
import {
|
|
11
11
|
loadGitignoreMatcher
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ELFFQ4RU.js";
|
|
13
13
|
|
|
14
14
|
// src/codebase-index/circuit-breaker.ts
|
|
15
15
|
var CircuitOpenError = class extends Error {
|
|
@@ -99,13 +99,59 @@ var K1 = 1.5;
|
|
|
99
99
|
var B = 0.75;
|
|
100
100
|
var TOKENISE_RE = new RegExp("[^\\p{L}\\p{N}$']", "gu");
|
|
101
101
|
function tokenise(text) {
|
|
102
|
-
|
|
102
|
+
if (!text) return [];
|
|
103
|
+
const cleaned = text.replace(TOKENISE_RE, " ").toLowerCase().trim();
|
|
104
|
+
if (!cleaned) return [];
|
|
105
|
+
return cleaned.split(/\s+/);
|
|
103
106
|
}
|
|
107
|
+
var SPLIT_CANDIDATE_RE = /[A-Z_\d-]/;
|
|
104
108
|
function splitName(name) {
|
|
109
|
+
if (!name || !SPLIT_CANDIDATE_RE.test(name)) return name;
|
|
105
110
|
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([\p{L}])(\d)/gu, "$1 $2").replace(/(\d)([\p{L}])/gu, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
106
111
|
}
|
|
112
|
+
var FTS_SIGNATURE_MAX_CHARS = 300;
|
|
113
|
+
function ftsSignature(signature) {
|
|
114
|
+
if (!signature) return "";
|
|
115
|
+
const bodyStart = declarationBodyStart(signature);
|
|
116
|
+
const header = bodyStart === -1 ? signature : signature.slice(0, bodyStart);
|
|
117
|
+
return header.slice(0, FTS_SIGNATURE_MAX_CHARS);
|
|
118
|
+
}
|
|
119
|
+
function declarationBodyStart(s) {
|
|
120
|
+
let parenDepth = 0;
|
|
121
|
+
let paramsClosed = false;
|
|
122
|
+
let i = 0;
|
|
123
|
+
while (i < s.length) {
|
|
124
|
+
const ch = s[i];
|
|
125
|
+
if (!paramsClosed) {
|
|
126
|
+
if (ch === "(") parenDepth++;
|
|
127
|
+
else if (ch === ")") {
|
|
128
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
129
|
+
if (parenDepth === 0) paramsClosed = true;
|
|
130
|
+
}
|
|
131
|
+
i++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (ch === "{") {
|
|
135
|
+
let j = i - 1;
|
|
136
|
+
while (j >= 0 && /\s/.test(s[j] ?? "")) j--;
|
|
137
|
+
if (s[j] === ":") {
|
|
138
|
+
let braceDepth = 1;
|
|
139
|
+
i++;
|
|
140
|
+
while (i < s.length && braceDepth > 0) {
|
|
141
|
+
if (s[i] === "{") braceDepth++;
|
|
142
|
+
else if (s[i] === "}") braceDepth--;
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
return i;
|
|
148
|
+
}
|
|
149
|
+
i++;
|
|
150
|
+
}
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
107
153
|
function buildIndexableText(name, signature, docComment) {
|
|
108
|
-
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
154
|
+
return [splitName(name), name, ftsSignature(signature), docComment].filter(Boolean).join(" ");
|
|
109
155
|
}
|
|
110
156
|
function buildBm25Index(docs) {
|
|
111
157
|
const documents = docs.map((d) => {
|
|
@@ -189,7 +235,7 @@ var SCHEMA_VERSION = 5;
|
|
|
189
235
|
// src/codebase-index/writer-helpers.ts
|
|
190
236
|
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
191
237
|
function escapeLike(value) {
|
|
192
|
-
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
238
|
+
return /[\\%_]/.test(value) ? value.replace(/[\\%_]/g, (char) => `\\${char}`) : value;
|
|
193
239
|
}
|
|
194
240
|
function ladderChunkSizes(total, max) {
|
|
195
241
|
if (total <= 0) return [];
|
|
@@ -214,8 +260,22 @@ function padToInBucket(values) {
|
|
|
214
260
|
while (padded.length < target) padded.push(padded[0]);
|
|
215
261
|
return padded;
|
|
216
262
|
}
|
|
263
|
+
var PLACEHOLDER_BUCKETS = {
|
|
264
|
+
1: "?",
|
|
265
|
+
2: "?,?",
|
|
266
|
+
4: "?,?,?,?",
|
|
267
|
+
8: "?,?,?,?,?,?,?,?",
|
|
268
|
+
16: "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?",
|
|
269
|
+
32: "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?",
|
|
270
|
+
64: "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?",
|
|
271
|
+
128: Array.from({ length: 128 }, () => "?").join(","),
|
|
272
|
+
256: Array.from({ length: 256 }, () => "?").join(","),
|
|
273
|
+
512: Array.from({ length: 512 }, () => "?").join(","),
|
|
274
|
+
1024: Array.from({ length: 1024 }, () => "?").join(",")
|
|
275
|
+
};
|
|
217
276
|
function placeholders(count) {
|
|
218
|
-
|
|
277
|
+
if (count <= 0) return "";
|
|
278
|
+
return PLACEHOLDER_BUCKETS[count] ?? Array.from({ length: count }, () => "?").join(",");
|
|
219
279
|
}
|
|
220
280
|
function inListChunks(total, max) {
|
|
221
281
|
if (total <= 0) return [];
|
|
@@ -224,7 +284,8 @@ function inListChunks(total, max) {
|
|
|
224
284
|
return ladderChunkSizes(total, powMax);
|
|
225
285
|
}
|
|
226
286
|
function posixIndexPath(file) {
|
|
227
|
-
|
|
287
|
+
const p = file.includes("\\") ? file.replace(/\\/g, "/") : file;
|
|
288
|
+
return p.startsWith("./") ? p.slice(2) : p;
|
|
228
289
|
}
|
|
229
290
|
function indexedFileMatchSql(column = "file") {
|
|
230
291
|
return `(${column} = ? OR replace(${column}, '\\', '/') = ? OR replace(${column}, '\\', '/') LIKE ? ESCAPE '\\')`;
|
|
@@ -373,11 +434,13 @@ function isLockError(err) {
|
|
|
373
434
|
if (typeof code === "number" && (code === 5 || code === 6)) return true;
|
|
374
435
|
return /SQLITE_(BUSY|LOCKED)/.test(err.message);
|
|
375
436
|
}
|
|
437
|
+
var SLEEP_BUFFER;
|
|
376
438
|
function sleepSync(ms) {
|
|
377
439
|
try {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
440
|
+
if (!SLEEP_BUFFER) {
|
|
441
|
+
SLEEP_BUFFER = new Int32Array(new SharedArrayBuffer(4));
|
|
442
|
+
}
|
|
443
|
+
Atomics.wait(SLEEP_BUFFER, 0, 0, ms);
|
|
381
444
|
} catch {
|
|
382
445
|
}
|
|
383
446
|
}
|
|
@@ -410,19 +473,12 @@ var NGRAM_SIZE = 3;
|
|
|
410
473
|
function embedText(text) {
|
|
411
474
|
const vec = new Float32Array(VECTOR_DIMENSIONS);
|
|
412
475
|
const normalized = text.toLowerCase().trim();
|
|
413
|
-
if (normalized.length
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
} else {
|
|
421
|
-
for (let i = 0; i <= normalized.length - NGRAM_SIZE; i++) {
|
|
422
|
-
const ngram = normalized.slice(i, i + NGRAM_SIZE);
|
|
423
|
-
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
424
|
-
vec[bucket] += 1;
|
|
425
|
-
}
|
|
476
|
+
if (normalized.length === 0) return vec;
|
|
477
|
+
const target = normalized.length < NGRAM_SIZE ? ` ${normalized} `.slice(0, Math.max(NGRAM_SIZE, normalized.length + 2)) : normalized;
|
|
478
|
+
for (let i = 0; i <= target.length - NGRAM_SIZE; i++) {
|
|
479
|
+
const ngram = target.slice(i, i + NGRAM_SIZE);
|
|
480
|
+
const bucket = hashNgram(ngram) % VECTOR_DIMENSIONS;
|
|
481
|
+
vec[bucket] += 1;
|
|
426
482
|
}
|
|
427
483
|
let norm = 0;
|
|
428
484
|
for (let i = 0; i < VECTOR_DIMENSIONS; i++) {
|
|
@@ -456,11 +512,12 @@ function encodeVector(vec) {
|
|
|
456
512
|
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
457
513
|
}
|
|
458
514
|
function decodeVector(buf) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
copy[i] = view.getFloat32(i * 4, true);
|
|
515
|
+
if (buf.byteLength === 0) return new Float32Array(0);
|
|
516
|
+
if (buf.byteLength % 4 !== 0) {
|
|
517
|
+
throw new Error(`decodeVector: invalid vector byteLength ${buf.byteLength} (must be a multiple of 4)`);
|
|
463
518
|
}
|
|
519
|
+
const copy = new Float32Array(buf.byteLength / 4);
|
|
520
|
+
new Uint8Array(copy.buffer).set(buf);
|
|
464
521
|
return copy;
|
|
465
522
|
}
|
|
466
523
|
function reciprocalRankFusion(bm25Ranks, vectorRanks, k = RRF_K) {
|
|
@@ -483,9 +540,11 @@ import * as fs from "node:fs";
|
|
|
483
540
|
import * as path from "node:path";
|
|
484
541
|
var DB_FILE = "index.db";
|
|
485
542
|
function getAllIndexableWithStatement(stmt) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
543
|
+
const rows = stmt("SELECT id, name, signature, doc_comment FROM symbols").all();
|
|
544
|
+
return rows.map((row) => ({
|
|
545
|
+
id: row.id,
|
|
546
|
+
text: buildIndexableText(row.name, row.signature, row.doc_comment)
|
|
547
|
+
}));
|
|
489
548
|
}
|
|
490
549
|
function getMaxSymbolIdWithStatement(stmt) {
|
|
491
550
|
const rows = stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
@@ -560,6 +619,16 @@ function getIndexDbSizeBytes(indexDir) {
|
|
|
560
619
|
}
|
|
561
620
|
|
|
562
621
|
// src/codebase-index/writer-bulk-insert.ts
|
|
622
|
+
var ROW_PLACEHOLDERS_CACHE = /* @__PURE__ */ new Map();
|
|
623
|
+
function getRowPlaceholders(rowTemplate, count) {
|
|
624
|
+
const key = `${rowTemplate}\0${count}`;
|
|
625
|
+
let result = ROW_PLACEHOLDERS_CACHE.get(key);
|
|
626
|
+
if (result === void 0) {
|
|
627
|
+
result = Array.from({ length: count }, () => rowTemplate).join(", ");
|
|
628
|
+
ROW_PLACEHOLDERS_CACHE.set(key, result);
|
|
629
|
+
}
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
563
632
|
function bulkInsertSymbolsWithStatement(stmt, maxSqlVars, rows) {
|
|
564
633
|
if (rows.length === 0) return;
|
|
565
634
|
const ladder = ladderChunkSizes(rows.length, Math.max(1, Math.floor(maxSqlVars / 12)));
|
|
@@ -567,9 +636,12 @@ function bulkInsertSymbolsWithStatement(stmt, maxSqlVars, rows) {
|
|
|
567
636
|
for (const take of ladder) {
|
|
568
637
|
const chunk = rows.slice(cursor, cursor + take);
|
|
569
638
|
cursor += take;
|
|
570
|
-
const placeholders2 =
|
|
639
|
+
const placeholders2 = getRowPlaceholders(
|
|
640
|
+
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
641
|
+
chunk.length
|
|
642
|
+
);
|
|
571
643
|
const insert = stmt(
|
|
572
|
-
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope
|
|
644
|
+
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope)
|
|
573
645
|
VALUES ${placeholders2}`
|
|
574
646
|
);
|
|
575
647
|
const binds = [];
|
|
@@ -584,8 +656,7 @@ function bulkInsertSymbolsWithStatement(stmt, maxSqlVars, rows) {
|
|
|
584
656
|
r.col,
|
|
585
657
|
r.signature,
|
|
586
658
|
r.docComment,
|
|
587
|
-
r.scope
|
|
588
|
-
r.text
|
|
659
|
+
r.scope
|
|
589
660
|
);
|
|
590
661
|
}
|
|
591
662
|
insert.run(...binds);
|
|
@@ -598,7 +669,7 @@ function bulkInsertFtsWithStatement(stmt, maxSqlVars, ftsAvailable, rows) {
|
|
|
598
669
|
for (const take of ladder) {
|
|
599
670
|
const chunk = rows.slice(cursor, cursor + take);
|
|
600
671
|
cursor += take;
|
|
601
|
-
const placeholders2 =
|
|
672
|
+
const placeholders2 = getRowPlaceholders("(?, ?)", chunk.length);
|
|
602
673
|
const insert = stmt(`INSERT INTO symbols_fts(rowid, text) VALUES ${placeholders2}`);
|
|
603
674
|
const binds = [];
|
|
604
675
|
for (const r of chunk) binds.push(r.id, r.text);
|
|
@@ -612,7 +683,7 @@ function bulkInsertVectorsWithStatement(stmt, maxSqlVars, rows) {
|
|
|
612
683
|
for (const take of ladder) {
|
|
613
684
|
const chunk = rows.slice(cursor, cursor + take);
|
|
614
685
|
cursor += take;
|
|
615
|
-
const placeholders2 =
|
|
686
|
+
const placeholders2 = getRowPlaceholders("(?, ?)", chunk.length);
|
|
616
687
|
const insert = stmt(`INSERT INTO symbol_vectors(symbol_id, vector) VALUES ${placeholders2}`);
|
|
617
688
|
const binds = [];
|
|
618
689
|
for (const r of chunk) binds.push(r.id, r.vector);
|
|
@@ -626,7 +697,10 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
626
697
|
for (const take of ladder) {
|
|
627
698
|
const chunk = refs.slice(cursor, cursor + take);
|
|
628
699
|
cursor += take;
|
|
629
|
-
const placeholders2 =
|
|
700
|
+
const placeholders2 = getRowPlaceholders(
|
|
701
|
+
"(?, ?, ?, ?, ?, ?, ?, ?)",
|
|
702
|
+
chunk.length
|
|
703
|
+
);
|
|
630
704
|
const insert = stmt(
|
|
631
705
|
`INSERT INTO refs(from_id, to_name, to_id, call_type, line, lang, module, to_file)
|
|
632
706
|
VALUES ${placeholders2}`
|
|
@@ -652,7 +726,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
652
726
|
import * as fs2 from "node:fs/promises";
|
|
653
727
|
import * as path2 from "node:path";
|
|
654
728
|
function toPortablePath(file) {
|
|
655
|
-
return file.replace(/\\/g, "/");
|
|
729
|
+
return file.includes("\\") ? file.replace(/\\/g, "/") : file;
|
|
656
730
|
}
|
|
657
731
|
async function readTextIfPresent(file) {
|
|
658
732
|
try {
|
|
@@ -956,9 +1030,16 @@ function createPackageLabeller(stored) {
|
|
|
956
1030
|
function buildPackageGraphNodes(fileCounts, files, packageOf) {
|
|
957
1031
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
958
1032
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
1033
|
+
const getOrSetPkg = (file) => {
|
|
1034
|
+
let pkg = fileToPkg.get(file);
|
|
1035
|
+
if (pkg === void 0) {
|
|
1036
|
+
pkg = packageOf(file);
|
|
1037
|
+
fileToPkg.set(file, pkg);
|
|
1038
|
+
}
|
|
1039
|
+
return pkg;
|
|
1040
|
+
};
|
|
959
1041
|
for (const { file, n } of fileCounts) {
|
|
960
|
-
const pkg =
|
|
961
|
-
fileToPkg.set(file, pkg);
|
|
1042
|
+
const pkg = getOrSetPkg(file);
|
|
962
1043
|
const node = pkgNodes.get(pkg);
|
|
963
1044
|
if (node) {
|
|
964
1045
|
node.symbolCount = (node.symbolCount ?? 0) + n;
|
|
@@ -974,8 +1055,7 @@ function buildPackageGraphNodes(fileCounts, files, packageOf) {
|
|
|
974
1055
|
}
|
|
975
1056
|
}
|
|
976
1057
|
for (const { file } of files) {
|
|
977
|
-
const pkg =
|
|
978
|
-
fileToPkg.set(file, pkg);
|
|
1058
|
+
const pkg = getOrSetPkg(file);
|
|
979
1059
|
const node = pkgNodes.get(pkg);
|
|
980
1060
|
if (node) {
|
|
981
1061
|
node.fileCount = (node.fileCount ?? 0) + 1;
|
|
@@ -1007,9 +1087,11 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
|
|
|
1007
1087
|
const ensureFileNode = (file) => {
|
|
1008
1088
|
if (fileNodes.has(file)) return;
|
|
1009
1089
|
const stats = fileStats.get(file);
|
|
1090
|
+
const lastSlash = Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\"));
|
|
1091
|
+
const label = lastSlash === -1 ? file : file.slice(lastSlash + 1);
|
|
1010
1092
|
fileNodes.set(file, {
|
|
1011
1093
|
id: `file:${file}`,
|
|
1012
|
-
label
|
|
1094
|
+
label,
|
|
1013
1095
|
kind: "file",
|
|
1014
1096
|
package: packageOf(file),
|
|
1015
1097
|
file,
|
|
@@ -1024,17 +1106,25 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
|
|
|
1024
1106
|
return { fileNodes, symToFile, fileStats, ensureFileNode };
|
|
1025
1107
|
}
|
|
1026
1108
|
function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
)
|
|
1031
|
-
|
|
1032
|
-
const isLocal = (file) => local.has(file.replace(/\\/g, "/"));
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
const
|
|
1109
|
+
const rawSet = typeof localFiles === "string" ? [localFiles] : localFiles;
|
|
1110
|
+
const local = /* @__PURE__ */ new Set();
|
|
1111
|
+
for (const f of rawSet) {
|
|
1112
|
+
local.add(f.includes("\\") ? f.replace(/\\/g, "/") : f);
|
|
1113
|
+
}
|
|
1114
|
+
const isLocal = (file) => local.has(file.includes("\\") ? file.replace(/\\/g, "/") : file);
|
|
1115
|
+
const matched = [];
|
|
1116
|
+
for (const id of relatedIds) {
|
|
1117
|
+
const s = symById.get(id);
|
|
1118
|
+
if (s) {
|
|
1119
|
+
matched.push({ ...s, isExt: !isLocal(s.file) });
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
matched.sort((a, b) => {
|
|
1123
|
+
const aExternal = a.isExt ? 1 : 0;
|
|
1124
|
+
const bExternal = b.isExt ? 1 : 0;
|
|
1036
1125
|
return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;
|
|
1037
|
-
})
|
|
1126
|
+
});
|
|
1127
|
+
return matched.map((s) => ({
|
|
1038
1128
|
id: `sym:${s.id}`,
|
|
1039
1129
|
label: s.name,
|
|
1040
1130
|
kind: "symbol",
|
|
@@ -1046,7 +1136,7 @@ function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
|
|
|
1046
1136
|
line: s.line,
|
|
1047
1137
|
signature: s.signature,
|
|
1048
1138
|
scope: s.scope,
|
|
1049
|
-
external:
|
|
1139
|
+
external: s.isExt
|
|
1050
1140
|
}));
|
|
1051
1141
|
}
|
|
1052
1142
|
function addWeightedEdge(edgeMap, source, target, callType, weight) {
|
|
@@ -1092,7 +1182,7 @@ function mapWriterRefRow(row) {
|
|
|
1092
1182
|
line: row.line,
|
|
1093
1183
|
// `lang`/`module`/`to_file` are absent from the narrower column lists some
|
|
1094
1184
|
// queries select; `undefined` keeps those rows valid Refs.
|
|
1095
|
-
lang: row.lang
|
|
1185
|
+
lang: row.lang ?? void 0,
|
|
1096
1186
|
module: row.module ?? void 0,
|
|
1097
1187
|
toFile: row.to_file ?? void 0
|
|
1098
1188
|
};
|
|
@@ -1102,9 +1192,10 @@ function mapWriterRefRow(row) {
|
|
|
1102
1192
|
var MAX_SQL_VARS = 900;
|
|
1103
1193
|
function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
1104
1194
|
const results = [];
|
|
1195
|
+
let cursor = 0;
|
|
1105
1196
|
for (const take of inListChunks(ids.length, MAX_SQL_VARS)) {
|
|
1106
|
-
const chunk = padToInBucket(ids.slice(
|
|
1107
|
-
|
|
1197
|
+
const chunk = padToInBucket(ids.slice(cursor, cursor + take));
|
|
1198
|
+
cursor += take;
|
|
1108
1199
|
const sql = buildSql(placeholders(chunk.length));
|
|
1109
1200
|
results.push(...stmt(sql).all(...chunk, ...extraArgs));
|
|
1110
1201
|
}
|
|
@@ -1112,9 +1203,10 @@ function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
|
1112
1203
|
}
|
|
1113
1204
|
function chunkedIdScalar(stmt, ids, buildSql, extraArgs = []) {
|
|
1114
1205
|
let total = 0;
|
|
1206
|
+
let cursor = 0;
|
|
1115
1207
|
for (const take of inListChunks(ids.length, MAX_SQL_VARS)) {
|
|
1116
|
-
const chunk = padToInBucket(ids.slice(
|
|
1117
|
-
|
|
1208
|
+
const chunk = padToInBucket(ids.slice(cursor, cursor + take));
|
|
1209
|
+
cursor += take;
|
|
1118
1210
|
const sql = buildSql(placeholders(chunk.length));
|
|
1119
1211
|
const rows = stmt(sql).all(...chunk, ...extraArgs);
|
|
1120
1212
|
total += rows[0]?.n ?? 0;
|
|
@@ -1666,8 +1758,7 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
|
|
|
1666
1758
|
col: s.col,
|
|
1667
1759
|
signature: s.signature,
|
|
1668
1760
|
docComment: s.docComment,
|
|
1669
|
-
scope: s.scope
|
|
1670
|
-
text: s.text
|
|
1761
|
+
scope: s.scope
|
|
1671
1762
|
});
|
|
1672
1763
|
if (ftsAvailable) {
|
|
1673
1764
|
ftsRows.push({
|
|
@@ -1679,7 +1770,7 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
|
|
|
1679
1770
|
vectorRows.push({
|
|
1680
1771
|
id,
|
|
1681
1772
|
vector: encodeVector(
|
|
1682
|
-
embedText(
|
|
1773
|
+
embedText(buildIndexableText(s.name, s.signature, s.docComment))
|
|
1683
1774
|
)
|
|
1684
1775
|
});
|
|
1685
1776
|
}
|
|
@@ -1773,8 +1864,7 @@ var CORE_TABLES_SQL = `
|
|
|
1773
1864
|
col INTEGER NOT NULL,
|
|
1774
1865
|
signature TEXT NOT NULL DEFAULT '',
|
|
1775
1866
|
doc_comment TEXT NOT NULL DEFAULT '',
|
|
1776
|
-
scope TEXT NOT NULL DEFAULT ''
|
|
1777
|
-
text TEXT NOT NULL DEFAULT ''
|
|
1867
|
+
scope TEXT NOT NULL DEFAULT ''
|
|
1778
1868
|
);
|
|
1779
1869
|
`;
|
|
1780
1870
|
var FILE_INDEX_SQL = [
|
|
@@ -1849,7 +1939,11 @@ function getNamespaceDeclarationsWithStatement(stmtFn) {
|
|
|
1849
1939
|
}
|
|
1850
1940
|
function getFilePackagesWithStatement(stmtFn) {
|
|
1851
1941
|
const rows = stmtFn("SELECT file, package FROM files WHERE package != ''").all();
|
|
1852
|
-
|
|
1942
|
+
const map = /* @__PURE__ */ new Map();
|
|
1943
|
+
for (const row of rows) {
|
|
1944
|
+
map.set(row.file, row.package);
|
|
1945
|
+
}
|
|
1946
|
+
return map;
|
|
1853
1947
|
}
|
|
1854
1948
|
function getUnresolvedImportsWithStatement(stmtFn, maxSqlVars, onlyFiles) {
|
|
1855
1949
|
const base = `SELECT DISTINCT s.file AS fromFile, r.lang AS lang, r.module AS module
|
|
@@ -2064,8 +2158,15 @@ function buildWriterSearchWhere(query, filter) {
|
|
|
2064
2158
|
}
|
|
2065
2159
|
if (query.trim()) {
|
|
2066
2160
|
const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
2067
|
-
conditions.push(
|
|
2068
|
-
|
|
2161
|
+
conditions.push(
|
|
2162
|
+
`(${tokens.map(
|
|
2163
|
+
() => "(name LIKE ? ESCAPE '\\' OR signature LIKE ? ESCAPE '\\' OR doc_comment LIKE ? ESCAPE '\\')"
|
|
2164
|
+
).join(" OR ")})`
|
|
2165
|
+
);
|
|
2166
|
+
for (const token of tokens) {
|
|
2167
|
+
const like = `%${escapeLike(token)}%`;
|
|
2168
|
+
values.push(like, like, like);
|
|
2169
|
+
}
|
|
2069
2170
|
}
|
|
2070
2171
|
return { where: conditions.length ? `WHERE ${conditions.join(" AND ")}` : "", values };
|
|
2071
2172
|
}
|
|
@@ -2142,8 +2243,11 @@ function searchRankedWithStatement(stmtFn, searchFn, ftsAvailable, vectorsAvaila
|
|
|
2142
2243
|
const conditions = ["symbols_fts MATCH ?"];
|
|
2143
2244
|
const values = [match];
|
|
2144
2245
|
for (const shortTok of shortTokens) {
|
|
2145
|
-
|
|
2146
|
-
|
|
2246
|
+
const like = `%${escapeLike(shortTok)}%`;
|
|
2247
|
+
conditions.push(
|
|
2248
|
+
"(s.name LIKE ? ESCAPE '\\' OR s.signature LIKE ? ESCAPE '\\' OR s.doc_comment LIKE ? ESCAPE '\\')"
|
|
2249
|
+
);
|
|
2250
|
+
values.push(like, like, like);
|
|
2147
2251
|
}
|
|
2148
2252
|
if (effectiveKind) {
|
|
2149
2253
|
conditions.push("s.kind = ?");
|
|
@@ -2442,11 +2546,11 @@ var IndexStore = class _IndexStore {
|
|
|
2442
2546
|
}
|
|
2443
2547
|
];
|
|
2444
2548
|
for (const { table, columns } of expected) {
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
)
|
|
2449
|
-
|
|
2549
|
+
const rows = this.db.prepare(`PRAGMA table_info(${table})`).all();
|
|
2550
|
+
const present = /* @__PURE__ */ new Set();
|
|
2551
|
+
for (const row of rows) {
|
|
2552
|
+
if (typeof row.name === "string") present.add(row.name);
|
|
2553
|
+
}
|
|
2450
2554
|
if (present.size === 0) continue;
|
|
2451
2555
|
for (const [name, type] of columns) {
|
|
2452
2556
|
if (present.has(name)) continue;
|
|
@@ -2454,6 +2558,31 @@ var IndexStore = class _IndexStore {
|
|
|
2454
2558
|
}
|
|
2455
2559
|
}
|
|
2456
2560
|
}
|
|
2561
|
+
/**
|
|
2562
|
+
* P4: `symbols.text` duplicated name + signature + doc_comment (~19 MB on
|
|
2563
|
+
* the live index) and is derived on demand now. In-place DROP COLUMN — no
|
|
2564
|
+
* SCHEMA_VERSION bump, so no wipe-and-reparse. Best-effort: a busy or
|
|
2565
|
+
* SQLite-without-DROP-COLUMN open leaves the column in place, where new
|
|
2566
|
+
* inserts simply default it to '' and the next open retries.
|
|
2567
|
+
*/
|
|
2568
|
+
dropDeprecatedSymbolsColumns() {
|
|
2569
|
+
if (this.getMetadata(_IndexStore.SYMBOLS_TEXT_DROPPED_KEY) !== void 0) return;
|
|
2570
|
+
const present = new Set(
|
|
2571
|
+
this.db.prepare("PRAGMA table_info(symbols)").all().flatMap(
|
|
2572
|
+
(row) => typeof row.name === "string" ? [row.name] : []
|
|
2573
|
+
)
|
|
2574
|
+
);
|
|
2575
|
+
if (present.size === 0) return;
|
|
2576
|
+
if (!present.has("text")) {
|
|
2577
|
+
this.setMetadata(_IndexStore.SYMBOLS_TEXT_DROPPED_KEY, "1");
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
try {
|
|
2581
|
+
this.db.exec("ALTER TABLE symbols DROP COLUMN text");
|
|
2582
|
+
this.setMetadata(_IndexStore.SYMBOLS_TEXT_DROPPED_KEY, "1");
|
|
2583
|
+
} catch {
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2457
2586
|
initSchema() {
|
|
2458
2587
|
this.db.exec(METADATA_TABLE_SQL);
|
|
2459
2588
|
const storedRows = this.stmt("SELECT value FROM metadata WHERE key = ?").all("version");
|
|
@@ -2479,6 +2608,7 @@ var IndexStore = class _IndexStore {
|
|
|
2479
2608
|
this.db.exec(CORE_TABLES_SQL);
|
|
2480
2609
|
this.db.exec(REFS_TABLE_SQL);
|
|
2481
2610
|
this.repairMissingColumns();
|
|
2611
|
+
this.dropDeprecatedSymbolsColumns();
|
|
2482
2612
|
for (const sql of FILE_INDEX_SQL) this.db.exec(sql);
|
|
2483
2613
|
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
2484
2614
|
for (const sql of REFS_INDEX_SQL) this.db.exec(sql);
|
|
@@ -2538,6 +2668,10 @@ var IndexStore = class _IndexStore {
|
|
|
2538
2668
|
}
|
|
2539
2669
|
static NEXT_SYMBOL_ID_KEY = "next_symbol_id";
|
|
2540
2670
|
static MAX_SQL_VARS = 900;
|
|
2671
|
+
/** P2: persisted FTS-mutation churn counter — see {@link optimizeFtsIfNeeded}. */
|
|
2672
|
+
static FTS_CHURN_KEY = "fts_churn_since_maintain";
|
|
2673
|
+
/** P4: one-shot marker that the deprecated symbols.text column is gone. */
|
|
2674
|
+
static SYMBOLS_TEXT_DROPPED_KEY = "symbols_text_dropped";
|
|
2541
2675
|
ensureNextSymbolIdSeeded() {
|
|
2542
2676
|
const existing = this.stmt("SELECT value FROM metadata WHERE key = ?").get(
|
|
2543
2677
|
_IndexStore.NEXT_SYMBOL_ID_KEY
|
|
@@ -2608,8 +2742,7 @@ var IndexStore = class _IndexStore {
|
|
|
2608
2742
|
col: s.col,
|
|
2609
2743
|
signature: s.signature,
|
|
2610
2744
|
docComment: s.docComment,
|
|
2611
|
-
scope: s.scope
|
|
2612
|
-
text: s.text
|
|
2745
|
+
scope: s.scope
|
|
2613
2746
|
});
|
|
2614
2747
|
if (this.ftsAvailable) {
|
|
2615
2748
|
ftsRows.push({ id, text: buildIndexableText(s.name, s.signature, s.docComment) });
|
|
@@ -2618,7 +2751,7 @@ var IndexStore = class _IndexStore {
|
|
|
2618
2751
|
vectorRows.push({
|
|
2619
2752
|
id,
|
|
2620
2753
|
vector: encodeVector(
|
|
2621
|
-
embedText(
|
|
2754
|
+
embedText(buildIndexableText(s.name, s.signature, s.docComment))
|
|
2622
2755
|
)
|
|
2623
2756
|
});
|
|
2624
2757
|
}
|
|
@@ -2638,6 +2771,7 @@ var IndexStore = class _IndexStore {
|
|
|
2638
2771
|
vectorRows
|
|
2639
2772
|
);
|
|
2640
2773
|
}
|
|
2774
|
+
this.recordFtsChurn(symbols.length);
|
|
2641
2775
|
this.commitWriteTransaction(ownsTransaction);
|
|
2642
2776
|
return result;
|
|
2643
2777
|
} catch (err) {
|
|
@@ -2662,7 +2796,10 @@ var IndexStore = class _IndexStore {
|
|
|
2662
2796
|
"DELETE FROM symbol_vectors WHERE symbol_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
2663
2797
|
).run(file);
|
|
2664
2798
|
}
|
|
2665
|
-
|
|
2799
|
+
const deletedChanges = Number(
|
|
2800
|
+
this.stmt("DELETE FROM symbols WHERE file = ?").run(file).changes
|
|
2801
|
+
);
|
|
2802
|
+
this.recordFtsChurn(deletedChanges);
|
|
2666
2803
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
2667
2804
|
this.commitWriteTransaction(ownsTransaction);
|
|
2668
2805
|
} catch (error) {
|
|
@@ -2690,7 +2827,10 @@ var IndexStore = class _IndexStore {
|
|
|
2690
2827
|
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
2691
2828
|
file
|
|
2692
2829
|
);
|
|
2693
|
-
|
|
2830
|
+
const deletedChanges = Number(
|
|
2831
|
+
this.stmt("DELETE FROM symbols WHERE file = ?").run(file).changes
|
|
2832
|
+
);
|
|
2833
|
+
this.recordFtsChurn(deletedChanges);
|
|
2694
2834
|
this.stmt("DELETE FROM files WHERE file = ?").run(file);
|
|
2695
2835
|
this.resolveRefsForNamesUnsafe(affectedNames);
|
|
2696
2836
|
this.commitWriteTransaction(ownsTransaction);
|
|
@@ -2859,6 +2999,19 @@ var IndexStore = class _IndexStore {
|
|
|
2859
2999
|
return this.runWithRetry(() => {
|
|
2860
3000
|
const ownsTransaction = this.beginWriteTransaction();
|
|
2861
3001
|
try {
|
|
3002
|
+
const owned = options.deleteForFiles?.length ?? 0;
|
|
3003
|
+
let churnRows = entries.reduce((sum, e) => sum + e.symbols.length, 0);
|
|
3004
|
+
if (owned > 0) {
|
|
3005
|
+
let cursor = 0;
|
|
3006
|
+
for (const take of inListChunks(owned, Math.floor(_IndexStore.MAX_SQL_VARS / 4))) {
|
|
3007
|
+
const bucket = options.deleteForFiles.slice(cursor, cursor + take);
|
|
3008
|
+
cursor += take;
|
|
3009
|
+
const row = this.stmt(
|
|
3010
|
+
`SELECT COUNT(*) AS n FROM symbols WHERE file IN (${placeholders(bucket.length)})`
|
|
3011
|
+
).get(...bucket);
|
|
3012
|
+
churnRows += Number(row?.n ?? 0);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
2862
3015
|
const result = commitBatchWithStatement(
|
|
2863
3016
|
(sql) => this.stmt(sql),
|
|
2864
3017
|
_IndexStore.MAX_SQL_VARS,
|
|
@@ -2870,6 +3023,7 @@ var IndexStore = class _IndexStore {
|
|
|
2870
3023
|
entries,
|
|
2871
3024
|
options
|
|
2872
3025
|
);
|
|
3026
|
+
this.recordFtsChurn(churnRows);
|
|
2873
3027
|
this.commitWriteTransaction(ownsTransaction);
|
|
2874
3028
|
return result;
|
|
2875
3029
|
} catch (err) {
|
|
@@ -2910,7 +3064,10 @@ var IndexStore = class _IndexStore {
|
|
|
2910
3064
|
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
2911
3065
|
meta.file
|
|
2912
3066
|
);
|
|
2913
|
-
|
|
3067
|
+
const deletedChanges = Number(
|
|
3068
|
+
this.stmt("DELETE FROM symbols WHERE file = ?").run(meta.file).changes
|
|
3069
|
+
);
|
|
3070
|
+
this.recordFtsChurn(deletedChanges);
|
|
2914
3071
|
this.stmt(
|
|
2915
3072
|
`INSERT INTO files(file, lang, mtime_ms, content_hash, symbol_count, last_indexed)
|
|
2916
3073
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
@@ -2942,6 +3099,54 @@ var IndexStore = class _IndexStore {
|
|
|
2942
3099
|
} catch {
|
|
2943
3100
|
}
|
|
2944
3101
|
}
|
|
3102
|
+
/**
|
|
3103
|
+
* P2: churn-gated FTS5 segment maintenance.
|
|
3104
|
+
*
|
|
3105
|
+
* FTS5 postings only compact when FTS5 itself merges them: every delete
|
|
3106
|
+
* leaves tombstone postings in the segments, and neither VACUUM nor
|
|
3107
|
+
* `PRAGMA optimize` reclaims them. Measured on the live index (2026-08-30):
|
|
3108
|
+
* one delete+reinsert cycle grew symbols_fts_data 174.9→212.8 MB, while the
|
|
3109
|
+
* FTS5 'optimize'/'rebuild' merge resets it (rebuild: 212.9→28.2 MB in
|
|
3110
|
+
* 1.26 s for 131k rows). 'optimize' is the recurring command — an
|
|
3111
|
+
* incremental merge that discards deleted docs — and only runs once churn
|
|
3112
|
+
* crosses the gate, so a clean index never pays for it.
|
|
3113
|
+
*
|
|
3114
|
+
* Follows the {@link checkpointWal} contract: best-effort, returns whether
|
|
3115
|
+
* it ran, safe to call from the daemon's single-threaded idle path.
|
|
3116
|
+
*/
|
|
3117
|
+
optimizeFtsIfNeeded(options = {}) {
|
|
3118
|
+
if (!this.ftsAvailable) return false;
|
|
3119
|
+
try {
|
|
3120
|
+
const minChurnRatio = options.minChurnRatio ?? 0.1;
|
|
3121
|
+
const minChurnRows = options.minChurnRows ?? 5e3;
|
|
3122
|
+
const churn = Number(this.getMetadata(_IndexStore.FTS_CHURN_KEY) ?? "0") || 0;
|
|
3123
|
+
const liveRows = Number(
|
|
3124
|
+
this.stmt("SELECT COUNT(*) AS n FROM symbols").get()?.n ?? 0
|
|
3125
|
+
);
|
|
3126
|
+
if (churn < Math.max(minChurnRows, liveRows * minChurnRatio)) return false;
|
|
3127
|
+
this.runWithRetry(() => {
|
|
3128
|
+
this.stmt(`INSERT INTO symbols_fts(symbols_fts) VALUES('optimize')`).run();
|
|
3129
|
+
});
|
|
3130
|
+
this.setMetadata(_IndexStore.FTS_CHURN_KEY, "0");
|
|
3131
|
+
return true;
|
|
3132
|
+
} catch {
|
|
3133
|
+
return false;
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
/**
|
|
3137
|
+
* Best-effort churn bookkeeping for {@link optimizeFtsIfNeeded}. Counts the
|
|
3138
|
+
* FTS rows a mutation inserts or deletes so the maintenance gate can fire
|
|
3139
|
+
* after real churn, not on a timer. Persisted in metadata so the counter
|
|
3140
|
+
* survives store open/close cycles in the daemon pool.
|
|
3141
|
+
*/
|
|
3142
|
+
recordFtsChurn(rows) {
|
|
3143
|
+
if (rows <= 0 || !this.ftsAvailable) return;
|
|
3144
|
+
try {
|
|
3145
|
+
const current = Number(this.getMetadata(_IndexStore.FTS_CHURN_KEY) ?? "0") || 0;
|
|
3146
|
+
this.setMetadata(_IndexStore.FTS_CHURN_KEY, String(current + rows));
|
|
3147
|
+
} catch {
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
2945
3150
|
/**
|
|
2946
3151
|
* P4.14: best-effort WAL checkpoint for idle-time maintenance.
|
|
2947
3152
|
*
|
|
@@ -3069,15 +3274,11 @@ function rotl64(x, n) {
|
|
|
3069
3274
|
const v = x & MASK64;
|
|
3070
3275
|
return (v << BigInt(n) | v >> BigInt(64 - n)) & MASK64;
|
|
3071
3276
|
}
|
|
3072
|
-
function readU64LE(
|
|
3073
|
-
|
|
3074
|
-
for (let i = 7; i >= 0; i--) {
|
|
3075
|
-
v = v << 8n | BigInt(buf[off + i] ?? 0);
|
|
3076
|
-
}
|
|
3077
|
-
return v & MASK64;
|
|
3277
|
+
function readU64LE(view, off) {
|
|
3278
|
+
return view.getBigUint64(off, true);
|
|
3078
3279
|
}
|
|
3079
|
-
function readU32LE(
|
|
3080
|
-
return
|
|
3280
|
+
function readU32LE(view, off) {
|
|
3281
|
+
return BigInt(view.getUint32(off, true));
|
|
3081
3282
|
}
|
|
3082
3283
|
function xxh64Round(acc, lane) {
|
|
3083
3284
|
return mul64(rotl64(acc + mul64(lane, PRIME64_2) & MASK64, 31), PRIME64_1);
|
|
@@ -3087,6 +3288,7 @@ function xxh64MergeRound(acc, val) {
|
|
|
3087
3288
|
}
|
|
3088
3289
|
function xxhash64Hex(buf, explicitLen) {
|
|
3089
3290
|
const length = explicitLen ?? buf.length;
|
|
3291
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
3090
3292
|
let h;
|
|
3091
3293
|
let off = 0;
|
|
3092
3294
|
if (length >= 32) {
|
|
@@ -3096,10 +3298,10 @@ function xxhash64Hex(buf, explicitLen) {
|
|
|
3096
3298
|
let v4 = 0n - PRIME64_1 & MASK64;
|
|
3097
3299
|
const end32 = length - 32;
|
|
3098
3300
|
while (off <= end32) {
|
|
3099
|
-
v1 = xxh64Round(v1, readU64LE(
|
|
3100
|
-
v2 = xxh64Round(v2, readU64LE(
|
|
3101
|
-
v3 = xxh64Round(v3, readU64LE(
|
|
3102
|
-
v4 = xxh64Round(v4, readU64LE(
|
|
3301
|
+
v1 = xxh64Round(v1, readU64LE(view, off));
|
|
3302
|
+
v2 = xxh64Round(v2, readU64LE(view, off + 8));
|
|
3303
|
+
v3 = xxh64Round(v3, readU64LE(view, off + 16));
|
|
3304
|
+
v4 = xxh64Round(v4, readU64LE(view, off + 24));
|
|
3103
3305
|
off += 32;
|
|
3104
3306
|
}
|
|
3105
3307
|
h = rotl64(v1, 1) + rotl64(v2, 7) + rotl64(v3, 12) + rotl64(v4, 18) & MASK64;
|
|
@@ -3112,12 +3314,12 @@ function xxhash64Hex(buf, explicitLen) {
|
|
|
3112
3314
|
}
|
|
3113
3315
|
h = h + BigInt(length) & MASK64;
|
|
3114
3316
|
while (off + 8 <= length) {
|
|
3115
|
-
const k1 = xxh64Round(0n, readU64LE(
|
|
3317
|
+
const k1 = xxh64Round(0n, readU64LE(view, off));
|
|
3116
3318
|
h = mul64(rotl64(h ^ k1, 27), PRIME64_1) + PRIME64_4 & MASK64;
|
|
3117
3319
|
off += 8;
|
|
3118
3320
|
}
|
|
3119
3321
|
if (off + 4 <= length) {
|
|
3120
|
-
h = mul64(rotl64(h ^ mul64(readU32LE(
|
|
3322
|
+
h = mul64(rotl64(h ^ mul64(readU32LE(view, off), PRIME64_1), 23), PRIME64_2) + PRIME64_3 & MASK64;
|
|
3121
3323
|
off += 4;
|
|
3122
3324
|
}
|
|
3123
3325
|
while (off < length) {
|
|
@@ -3191,8 +3393,10 @@ var ModuleResolver = class {
|
|
|
3191
3393
|
for (const { name, file } of namespaces) {
|
|
3192
3394
|
const lang = detectLang(file);
|
|
3193
3395
|
if (!lang) continue;
|
|
3194
|
-
const
|
|
3195
|
-
if (
|
|
3396
|
+
const norm = normalizeNamespace(name);
|
|
3397
|
+
if (!norm) continue;
|
|
3398
|
+
const key = `${languageFamily(lang)}:${norm}`;
|
|
3399
|
+
if (!this.byNamespace.has(key)) {
|
|
3196
3400
|
this.byNamespace.set(key, file);
|
|
3197
3401
|
}
|
|
3198
3402
|
}
|
|
@@ -3620,7 +3824,7 @@ var ParserWorkerPool = class {
|
|
|
3620
3824
|
*/
|
|
3621
3825
|
async reparseInline(batch, orphaned, workerId) {
|
|
3622
3826
|
try {
|
|
3623
|
-
const { parseFileContent } = await import("./parser-dispatch-
|
|
3827
|
+
const { parseFileContent } = await import("./parser-dispatch-X62UWXJS.js");
|
|
3624
3828
|
for (const item of orphaned) {
|
|
3625
3829
|
if (batch.settled) return;
|
|
3626
3830
|
try {
|
|
@@ -4409,4 +4613,4 @@ export {
|
|
|
4409
4613
|
incomingCallsService,
|
|
4410
4614
|
outgoingCallsService
|
|
4411
4615
|
};
|
|
4412
|
-
//# sourceMappingURL=chunk-
|
|
4616
|
+
//# sourceMappingURL=chunk-TFEGJDH5.js.map
|