@wrongstack/tools 1.0.10 → 1.0.12
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/builtin.js +17 -17
- package/dist/{chunk-6I6RXQFG.js → chunk-2BCRQAIW.js} +9 -9
- package/dist/{chunk-XRNN44QY.js → chunk-2IRHFHZY.js} +235 -71
- package/dist/{chunk-5RBUYFRV.js → chunk-4IMNVQRB.js} +8 -2
- package/dist/{chunk-NEYUPZL6.js → chunk-6D3UIQMF.js} +651 -303
- package/dist/chunk-BJ5LXLKE.js +351 -0
- package/dist/{chunk-C6ZOXNXV.js → chunk-CSS42GRP.js} +2 -2
- package/dist/{chunk-SKRMUHTU.js → chunk-HSLDY55L.js} +41 -8
- package/dist/{chunk-ENE22I5W.js → chunk-K37AKCTN.js} +884 -362
- package/dist/{chunk-V25Q6TQ7.js → chunk-K3VX6V5P.js} +3 -3
- package/dist/chunk-OIFY4XNP.js +855 -0
- package/dist/{chunk-OUERRFEM.js → chunk-OMQDKZOT.js} +17 -8
- package/dist/{chunk-7GOE2ZTZ.js → chunk-OMWC2OGT.js} +2 -2
- package/dist/{chunk-IE4KDAOB.js → chunk-OYQE2UNT.js} +3 -3
- package/dist/chunk-R3HR3QI3.js +97 -0
- package/dist/{chunk-XPVYZ2S6.js → chunk-RYLMSGR5.js} +2 -2
- package/dist/{chunk-HYHE4VNA.js → chunk-UMOP3VGK.js} +79 -45
- package/dist/{chunk-FVSMTEO3.js → chunk-VMNWQI4V.js} +2 -2
- package/dist/{chunk-2UVAIS5O.js → chunk-YPSVPCIE.js} +2 -2
- package/dist/{chunk-PWZUHZP7.js → chunk-Z77XMU3U.js} +2 -2
- package/dist/codebase-index/ast-invariant-engine.d.ts +19 -2
- package/dist/codebase-index/atlas-projection.d.ts +19 -0
- package/dist/codebase-index/background-indexer.d.ts +14 -0
- package/dist/codebase-index/circuit-breaker.d.ts +7 -0
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +6 -0
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -0
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +26 -0
- package/dist/codebase-index/dead-code-scan.d.ts +4 -0
- package/dist/codebase-index/go-parser.d.ts +23 -3
- package/dist/codebase-index/graph-rank-pass.d.ts +10 -0
- package/dist/codebase-index/graph-rank.d.ts +8 -0
- package/dist/codebase-index/index.d.ts +1 -1
- package/dist/codebase-index/index.js +9 -9
- package/dist/codebase-index/lsp-kind.d.ts +3 -1
- package/dist/codebase-index/module-resolver.d.ts +21 -1
- package/dist/codebase-index/parser-batch.d.ts +6 -8
- package/dist/codebase-index/parser-output.d.ts +5 -1
- package/dist/codebase-index/parser-worker-script.js +4 -4
- package/dist/codebase-index/project-server-framing.d.ts +1 -17
- package/dist/codebase-index/project-server-watcher.d.ts +6 -0
- package/dist/codebase-index/project-server.js +72 -26
- package/dist/codebase-index/py-parser.d.ts +4 -4
- package/dist/codebase-index/rs-parser.d.ts +7 -0
- package/dist/codebase-index/toolchain-scripts.d.ts +64 -0
- package/dist/codebase-index/tree-sitter/queries.d.ts +14 -0
- package/dist/codebase-index/tree-sitter/util.d.ts +6 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +7 -0
- package/dist/codebase-index/worker.js +23 -16
- package/dist/codebase-index/writer-refs.d.ts +17 -2
- package/dist/codebase-index/writer-search-helpers.d.ts +9 -0
- package/dist/codebase-index/writer.d.ts +20 -1
- package/dist/edit.js +8 -8
- package/dist/{generic-parser-N2PLGBC2.js → generic-parser-IVWLFKMR.js} +2 -2
- package/dist/go-parser-LWV3L5V7.js +197 -0
- package/dist/index.js +19 -19
- package/dist/json-parser-PERV2JHX.js +204 -0
- package/dist/pack.js +18 -18
- package/dist/parser-dispatch-ZSWCQB6M.js +14 -0
- package/dist/patch.js +8 -8
- package/dist/{py-parser-4PFZYAJ7.js → py-parser-B6D6RGH7.js} +4 -4
- package/dist/read.js +8 -8
- package/dist/replace.js +8 -8
- package/dist/rs-parser-L7SP4GWJ.js +182 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +1 -1
- package/dist/tool-tier.js +19 -19
- package/dist/{tree-sitter-parser-GILYQLS4.js → tree-sitter-parser-KU6JF3ZC.js} +6 -4
- package/dist/{ts-parser-7W26YB26.js → ts-parser-PML6QIH7.js} +55 -23
- package/dist/typecheck.js +1 -1
- package/dist/write.js +8 -8
- package/dist/{yaml-parser-DFBC5FWH.js → yaml-parser-7BJN544B.js} +41 -6
- package/package.json +5 -5
- package/dist/chunk-4CE3RKEP.js +0 -390
- package/dist/chunk-KHUCKLOR.js +0 -899
- package/dist/chunk-ZKC5UNGD.js +0 -167
- package/dist/go-parser-Z4H6SR3R.js +0 -474
- package/dist/json-parser-IJD77VL4.js +0 -256
- package/dist/parser-dispatch-WG4DNBHH.js +0 -14
- package/dist/rs-parser-GIWPUA3T.js +0 -81
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseFilesContent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BJ5LXLKE.js";
|
|
4
4
|
import {
|
|
5
5
|
recordFilesystemRead
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OIFY4XNP.js";
|
|
7
7
|
import {
|
|
8
8
|
INDEXABLE_EXTENSIONS,
|
|
9
9
|
LANG_FAMILY_ENTRIES,
|
|
@@ -75,6 +75,15 @@ var IndexCircuitBreaker = class {
|
|
|
75
75
|
this.openedAt = this.now();
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Release a probe that ended without a verdict (the caller aborted it).
|
|
80
|
+
* Neither success nor failure is recorded, but a half-open circuit must be
|
|
81
|
+
* able to admit the next probe — otherwise it refuses every request until a
|
|
82
|
+
* manual reset.
|
|
83
|
+
*/
|
|
84
|
+
abandonProbe() {
|
|
85
|
+
this.probeInFlight = false;
|
|
86
|
+
}
|
|
78
87
|
/** Force-close the circuit (manual recovery: `/codebase-reindex`). */
|
|
79
88
|
reset() {
|
|
80
89
|
this.state = "closed";
|
|
@@ -139,7 +148,8 @@ function buildWiringGraph(refs, options = {}) {
|
|
|
139
148
|
const targetFile = fileOf.get(ref.toId);
|
|
140
149
|
if (sourceFile !== void 0 && targetFile !== void 0 && sourceFile !== targetFile) {
|
|
141
150
|
const imports = importsOf.get(sourceFile);
|
|
142
|
-
if (
|
|
151
|
+
if (options.implicitlyVisible?.(sourceFile, targetFile, imports)) {
|
|
152
|
+
} else if (imports === void 0) {
|
|
143
153
|
w *= UNVERIFIED_VISIBILITY_WEIGHT;
|
|
144
154
|
} else if (!imports.has(targetFile)) {
|
|
145
155
|
w *= CONTRADICTED_VISIBILITY_WEIGHT;
|
|
@@ -309,6 +319,71 @@ function aggregateFileRank(rows, fileOf) {
|
|
|
309
319
|
return out;
|
|
310
320
|
}
|
|
311
321
|
|
|
322
|
+
// src/codebase-index/graph-rank-pass.ts
|
|
323
|
+
import * as path from "node:path";
|
|
324
|
+
var DIRECTORY_SCOPED_FAMILIES = /* @__PURE__ */ new Set(["go", "jvm", "dotnet", "swift"]);
|
|
325
|
+
var DIRECTORY_IMPORT_FAMILIES = /* @__PURE__ */ new Set(["go", "jvm"]);
|
|
326
|
+
function familyOf(file) {
|
|
327
|
+
const lang = detectLang(file);
|
|
328
|
+
return lang ? languageFamily(lang) : void 0;
|
|
329
|
+
}
|
|
330
|
+
function createImplicitVisibility() {
|
|
331
|
+
const importedDirs = /* @__PURE__ */ new WeakMap();
|
|
332
|
+
return (sourceFile, targetFile, imports) => {
|
|
333
|
+
const family = familyOf(targetFile);
|
|
334
|
+
if (family === void 0) return false;
|
|
335
|
+
const targetDir = path.dirname(targetFile);
|
|
336
|
+
if (DIRECTORY_SCOPED_FAMILIES.has(family) && path.dirname(sourceFile) === targetDir && familyOf(sourceFile) === family) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
if (imports === void 0 || !DIRECTORY_IMPORT_FAMILIES.has(family)) return false;
|
|
340
|
+
let dirs = importedDirs.get(imports);
|
|
341
|
+
if (dirs === void 0) {
|
|
342
|
+
dirs = new Set([...imports].map((file) => path.dirname(file)));
|
|
343
|
+
importedDirs.set(imports, dirs);
|
|
344
|
+
}
|
|
345
|
+
return dirs.has(targetDir);
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
var RANK_VERSION = "1";
|
|
349
|
+
var RANK_VERSION_KEY = "rank_version";
|
|
350
|
+
var RANK_REFRESH_FILE_THRESHOLD = 25;
|
|
351
|
+
function shouldRefreshRanks(store, opts) {
|
|
352
|
+
if (opts.force) return true;
|
|
353
|
+
if (!opts.files) return true;
|
|
354
|
+
if (store.getMetadata(RANK_VERSION_KEY) !== RANK_VERSION) return true;
|
|
355
|
+
if (store.getRankCounts().symbols === 0) return true;
|
|
356
|
+
return opts.files.length >= RANK_REFRESH_FILE_THRESHOLD;
|
|
357
|
+
}
|
|
358
|
+
function runGraphRankPass(store, errors) {
|
|
359
|
+
const startMs = Date.now();
|
|
360
|
+
try {
|
|
361
|
+
const refs = store.getAllResolvedRefs();
|
|
362
|
+
const fileOf = /* @__PURE__ */ new Map();
|
|
363
|
+
for (const symbol of store.getAllSymbols()) fileOf.set(symbol.id, symbol.file);
|
|
364
|
+
const graph = buildWiringGraph(refs, {
|
|
365
|
+
candidates: store.getSymbolNameCandidates(),
|
|
366
|
+
fileOf,
|
|
367
|
+
importsOf: store.getImportVisibility(),
|
|
368
|
+
implicitlyVisible: createImplicitVisibility()
|
|
369
|
+
});
|
|
370
|
+
const scores = pageRank(graph);
|
|
371
|
+
const symbolRows = toSymbolRankRows(graph, scores);
|
|
372
|
+
const fileRows = aggregateFileRank(symbolRows, fileOf);
|
|
373
|
+
store.replaceRanks(symbolRows, fileRows);
|
|
374
|
+
store.setMetadata(RANK_VERSION_KEY, RANK_VERSION);
|
|
375
|
+
return {
|
|
376
|
+
computed: true,
|
|
377
|
+
symbols: symbolRows.length,
|
|
378
|
+
files: fileRows.length,
|
|
379
|
+
durationMs: Date.now() - startMs
|
|
380
|
+
};
|
|
381
|
+
} catch (error) {
|
|
382
|
+
errors.push(`graph rank: ${error instanceof Error ? error.message : String(error)}`);
|
|
383
|
+
return { computed: false, symbols: 0, files: 0, durationMs: Date.now() - startMs };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
312
387
|
// src/codebase-index/graph-adjacency-cache.ts
|
|
313
388
|
var cached = null;
|
|
314
389
|
function cacheKey(projectRoot, indexDir) {
|
|
@@ -334,7 +409,8 @@ function getWiringSnapshot(store, projectRoot, indexDir) {
|
|
|
334
409
|
const graph = buildWiringGraph(store.getAllResolvedRefs(), {
|
|
335
410
|
candidates: store.getSymbolNameCandidates(),
|
|
336
411
|
fileOf,
|
|
337
|
-
importsOf: store.getImportVisibility()
|
|
412
|
+
importsOf: store.getImportVisibility(),
|
|
413
|
+
implicitlyVisible: createImplicitVisibility()
|
|
338
414
|
});
|
|
339
415
|
cached = { key, stamp, graph, fileOf };
|
|
340
416
|
return { graph, fileOf };
|
|
@@ -560,6 +636,17 @@ function retrieveContext(store, projectRoot, indexDir, options, relativeOf) {
|
|
|
560
636
|
score: scoreBySymbolId.get(row.id) ?? 0,
|
|
561
637
|
snippet: ""
|
|
562
638
|
}));
|
|
639
|
+
let floor = Number.POSITIVE_INFINITY;
|
|
640
|
+
for (const score of scoreBySymbolId.values()) if (score < floor) floor = score;
|
|
641
|
+
if (!Number.isFinite(floor) || floor <= 0) floor = 1;
|
|
642
|
+
const unwired = search.results.filter((hit) => !scoreBySymbolId.has(hit.id));
|
|
643
|
+
const maxLexical = Math.max(0, ...unwired.map((hit) => hit.score));
|
|
644
|
+
for (const hit of unwired) {
|
|
645
|
+
const lexical = maxLexical > 0 && hit.score > 0 ? hit.score / maxLexical : 1;
|
|
646
|
+
const score = floor * 0.5 * lexical;
|
|
647
|
+
scoreBySymbolId.set(hit.id, score);
|
|
648
|
+
walked.push({ ...hit, score });
|
|
649
|
+
}
|
|
563
650
|
const grouped = groupIntoEntries(
|
|
564
651
|
walked,
|
|
565
652
|
scoreBySymbolId,
|
|
@@ -619,45 +706,6 @@ function groupIntoEntries(symbols, scoreBySymbolId, fileOf, limit, symbolsPerFil
|
|
|
619
706
|
return { entries, totalCandidates };
|
|
620
707
|
}
|
|
621
708
|
|
|
622
|
-
// src/codebase-index/graph-rank-pass.ts
|
|
623
|
-
var RANK_VERSION = "1";
|
|
624
|
-
var RANK_VERSION_KEY = "rank_version";
|
|
625
|
-
var RANK_REFRESH_FILE_THRESHOLD = 25;
|
|
626
|
-
function shouldRefreshRanks(store, opts) {
|
|
627
|
-
if (opts.force) return true;
|
|
628
|
-
if (!opts.files) return true;
|
|
629
|
-
if (store.getMetadata(RANK_VERSION_KEY) !== RANK_VERSION) return true;
|
|
630
|
-
if (store.getRankCounts().symbols === 0) return true;
|
|
631
|
-
return opts.files.length >= RANK_REFRESH_FILE_THRESHOLD;
|
|
632
|
-
}
|
|
633
|
-
function runGraphRankPass(store, errors) {
|
|
634
|
-
const startMs = Date.now();
|
|
635
|
-
try {
|
|
636
|
-
const refs = store.getAllResolvedRefs();
|
|
637
|
-
const fileOf = /* @__PURE__ */ new Map();
|
|
638
|
-
for (const symbol of store.getAllSymbols()) fileOf.set(symbol.id, symbol.file);
|
|
639
|
-
const graph = buildWiringGraph(refs, {
|
|
640
|
-
candidates: store.getSymbolNameCandidates(),
|
|
641
|
-
fileOf,
|
|
642
|
-
importsOf: store.getImportVisibility()
|
|
643
|
-
});
|
|
644
|
-
const scores = pageRank(graph);
|
|
645
|
-
const symbolRows = toSymbolRankRows(graph, scores);
|
|
646
|
-
const fileRows = aggregateFileRank(symbolRows, fileOf);
|
|
647
|
-
store.replaceRanks(symbolRows, fileRows);
|
|
648
|
-
store.setMetadata(RANK_VERSION_KEY, RANK_VERSION);
|
|
649
|
-
return {
|
|
650
|
-
computed: true,
|
|
651
|
-
symbols: symbolRows.length,
|
|
652
|
-
files: fileRows.length,
|
|
653
|
-
durationMs: Date.now() - startMs
|
|
654
|
-
};
|
|
655
|
-
} catch (error) {
|
|
656
|
-
errors.push(`graph rank: ${error instanceof Error ? error.message : String(error)}`);
|
|
657
|
-
return { computed: false, symbols: 0, files: 0, durationMs: Date.now() - startMs };
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
709
|
// src/codebase-index/bm25.ts
|
|
662
710
|
var K1 = 1.5;
|
|
663
711
|
var B = 0.75;
|
|
@@ -1037,36 +1085,27 @@ function decorateGraphNodes(stmt, nodes, packageOf) {
|
|
|
1037
1085
|
}
|
|
1038
1086
|
|
|
1039
1087
|
// src/codebase-index/lsp-kind.ts
|
|
1088
|
+
var LSP_TO_INTERNAL_KINDS = {
|
|
1089
|
+
[2 /* Module */]: ["mod"],
|
|
1090
|
+
[3 /* Namespace */]: ["namespace"],
|
|
1091
|
+
[5 /* Class */]: ["class"],
|
|
1092
|
+
[6 /* Method */]: ["method"],
|
|
1093
|
+
[7 /* Property */]: ["property"],
|
|
1094
|
+
[8 /* Field */]: ["property"],
|
|
1095
|
+
[10 /* Enum */]: ["enum"],
|
|
1096
|
+
[11 /* Interface */]: ["interface", "trait"],
|
|
1097
|
+
[12 /* Function */]: ["function"],
|
|
1098
|
+
[13 /* Variable */]: ["var", "let"],
|
|
1099
|
+
[14 /* Constant */]: ["const", "static"],
|
|
1100
|
+
[19 /* Object */]: ["object"],
|
|
1101
|
+
[23 /* Struct */]: ["struct"],
|
|
1102
|
+
[26 /* TypeParameter */]: ["type"]
|
|
1103
|
+
};
|
|
1104
|
+
function lspKindToInternalKinds(k) {
|
|
1105
|
+
return LSP_TO_INTERNAL_KINDS[k] ?? [];
|
|
1106
|
+
}
|
|
1040
1107
|
function lspKindToInternalKind(k) {
|
|
1041
|
-
|
|
1042
|
-
case 5 /* Class */:
|
|
1043
|
-
return "class";
|
|
1044
|
-
case 6 /* Method */:
|
|
1045
|
-
return "method";
|
|
1046
|
-
case 7 /* Property */:
|
|
1047
|
-
case 8 /* Field */:
|
|
1048
|
-
return "property";
|
|
1049
|
-
case 9 /* Constructor */:
|
|
1050
|
-
return "class";
|
|
1051
|
-
case 10 /* Enum */:
|
|
1052
|
-
return "enum";
|
|
1053
|
-
case 11 /* Interface */:
|
|
1054
|
-
return "interface";
|
|
1055
|
-
case 12 /* Function */:
|
|
1056
|
-
return "function";
|
|
1057
|
-
case 13 /* Variable */:
|
|
1058
|
-
return "var";
|
|
1059
|
-
case 14 /* Constant */:
|
|
1060
|
-
return "const";
|
|
1061
|
-
case 22 /* EnumMember */:
|
|
1062
|
-
return "enum";
|
|
1063
|
-
case 26 /* TypeParameter */:
|
|
1064
|
-
return "type";
|
|
1065
|
-
case 3 /* Namespace */:
|
|
1066
|
-
return "namespace";
|
|
1067
|
-
default:
|
|
1068
|
-
return null;
|
|
1069
|
-
}
|
|
1108
|
+
return LSP_TO_INTERNAL_KINDS[k]?.[0] ?? null;
|
|
1070
1109
|
}
|
|
1071
1110
|
function internalKindToLspKind(k) {
|
|
1072
1111
|
switch (k) {
|
|
@@ -1092,7 +1131,17 @@ function internalKindToLspKind(k) {
|
|
|
1092
1131
|
return 3 /* Namespace */;
|
|
1093
1132
|
case "type":
|
|
1094
1133
|
return 26 /* TypeParameter */;
|
|
1095
|
-
|
|
1134
|
+
case "struct":
|
|
1135
|
+
return 23 /* Struct */;
|
|
1136
|
+
case "trait":
|
|
1137
|
+
return 11 /* Interface */;
|
|
1138
|
+
case "mod":
|
|
1139
|
+
return 2 /* Module */;
|
|
1140
|
+
case "object":
|
|
1141
|
+
return 19 /* Object */;
|
|
1142
|
+
case "static":
|
|
1143
|
+
return 14 /* Constant */;
|
|
1144
|
+
// parameter, impl, literal, schema have no LSP equivalent
|
|
1096
1145
|
default:
|
|
1097
1146
|
return null;
|
|
1098
1147
|
}
|
|
@@ -1217,8 +1266,16 @@ function countFileVectorsWithStatement(stmt) {
|
|
|
1217
1266
|
stmt("SELECT COUNT(*) AS n FROM file_vectors").get()?.n ?? 0
|
|
1218
1267
|
);
|
|
1219
1268
|
}
|
|
1269
|
+
function l2Norm(vector) {
|
|
1270
|
+
let sum = 0;
|
|
1271
|
+
for (let i = 0; i < vector.length; i++) sum += vector[i] * vector[i];
|
|
1272
|
+
return Math.sqrt(sum);
|
|
1273
|
+
}
|
|
1220
1274
|
function searchFileVectorsWithStatement(stmt, query, limit, minScore) {
|
|
1221
|
-
if (limit
|
|
1275
|
+
if (!(limit > 0) || query.length === 0) return [];
|
|
1276
|
+
const queryNorm = l2Norm(query);
|
|
1277
|
+
if (!(queryNorm > 0)) return [];
|
|
1278
|
+
const floor = Number.isFinite(minScore) ? minScore : Number.NEGATIVE_INFINITY;
|
|
1222
1279
|
const rows = stmt("SELECT file, vector FROM file_vectors").all();
|
|
1223
1280
|
const hits = [];
|
|
1224
1281
|
for (const row of rows) {
|
|
@@ -1229,9 +1286,12 @@ function searchFileVectorsWithStatement(stmt, query, limit, minScore) {
|
|
|
1229
1286
|
continue;
|
|
1230
1287
|
}
|
|
1231
1288
|
if (vector.length !== query.length) continue;
|
|
1289
|
+
const vectorNorm = l2Norm(vector);
|
|
1290
|
+
if (!(vectorNorm > 0)) continue;
|
|
1232
1291
|
let dot = 0;
|
|
1233
1292
|
for (let i = 0; i < query.length; i++) dot += query[i] * vector[i];
|
|
1234
|
-
|
|
1293
|
+
const score = dot / (queryNorm * vectorNorm);
|
|
1294
|
+
if (score >= floor) hits.push({ file: row.file, score });
|
|
1235
1295
|
}
|
|
1236
1296
|
hits.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
|
|
1237
1297
|
return hits.slice(0, limit);
|
|
@@ -1239,7 +1299,7 @@ function searchFileVectorsWithStatement(stmt, query, limit, minScore) {
|
|
|
1239
1299
|
|
|
1240
1300
|
// src/codebase-index/writer.ts
|
|
1241
1301
|
import * as fs3 from "node:fs";
|
|
1242
|
-
import * as
|
|
1302
|
+
import * as path4 from "node:path";
|
|
1243
1303
|
|
|
1244
1304
|
// src/codebase-index/sqlite-runtime.ts
|
|
1245
1305
|
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
@@ -1311,7 +1371,7 @@ function runSqliteWithRetry(fn) {
|
|
|
1311
1371
|
|
|
1312
1372
|
// src/codebase-index/writer-admin.ts
|
|
1313
1373
|
import * as fs from "node:fs";
|
|
1314
|
-
import * as
|
|
1374
|
+
import * as path2 from "node:path";
|
|
1315
1375
|
var DB_FILE = "index.db";
|
|
1316
1376
|
function getAllIndexableWithStatement(stmt) {
|
|
1317
1377
|
const rows = stmt("SELECT id, name, signature, doc_comment FROM symbols").all();
|
|
@@ -1386,7 +1446,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
1386
1446
|
}
|
|
1387
1447
|
function getIndexDbSizeBytes(indexDir) {
|
|
1388
1448
|
try {
|
|
1389
|
-
return fs.statSync(
|
|
1449
|
+
return fs.statSync(path2.join(indexDir, DB_FILE)).size;
|
|
1390
1450
|
} catch {
|
|
1391
1451
|
return 0;
|
|
1392
1452
|
}
|
|
@@ -1492,7 +1552,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
1492
1552
|
|
|
1493
1553
|
// src/codebase-index/module-roots.ts
|
|
1494
1554
|
import * as fs2 from "node:fs/promises";
|
|
1495
|
-
import * as
|
|
1555
|
+
import * as path3 from "node:path";
|
|
1496
1556
|
function toPortablePath(file) {
|
|
1497
1557
|
return file.includes("\\") ? file.replace(/\\/g, "/") : file;
|
|
1498
1558
|
}
|
|
@@ -1557,7 +1617,7 @@ function ancestorsOf(dir, stopAt) {
|
|
|
1557
1617
|
for (; ; ) {
|
|
1558
1618
|
out.push(current);
|
|
1559
1619
|
if (current === stopAt || current.length <= stopAt.length) break;
|
|
1560
|
-
const parent =
|
|
1620
|
+
const parent = path3.posix.dirname(current);
|
|
1561
1621
|
if (parent === current) break;
|
|
1562
1622
|
current = parent;
|
|
1563
1623
|
}
|
|
@@ -1568,8 +1628,12 @@ var MARKER_PROBES = [
|
|
|
1568
1628
|
kind: "npm",
|
|
1569
1629
|
file: "package.json",
|
|
1570
1630
|
build: (dir, source) => {
|
|
1571
|
-
const
|
|
1572
|
-
return {
|
|
1631
|
+
const declared = parsePackageJsonName(source);
|
|
1632
|
+
return {
|
|
1633
|
+
name: declared ?? path3.posix.basename(dir),
|
|
1634
|
+
importPath: declared,
|
|
1635
|
+
sourceRoots: [dir]
|
|
1636
|
+
};
|
|
1573
1637
|
}
|
|
1574
1638
|
},
|
|
1575
1639
|
{
|
|
@@ -1582,7 +1646,7 @@ var MARKER_PROBES = [
|
|
|
1582
1646
|
name: `crate:${name}`,
|
|
1583
1647
|
// Rust paths use underscores where crate names often use dashes.
|
|
1584
1648
|
importPath: name.replace(/-/g, "_"),
|
|
1585
|
-
sourceRoots: [
|
|
1649
|
+
sourceRoots: [path3.posix.join(dir, "src")]
|
|
1586
1650
|
};
|
|
1587
1651
|
}
|
|
1588
1652
|
},
|
|
@@ -1599,13 +1663,13 @@ var MARKER_PROBES = [
|
|
|
1599
1663
|
kind: "python",
|
|
1600
1664
|
file: "pyproject.toml",
|
|
1601
1665
|
build: (dir, source) => {
|
|
1602
|
-
const name = parseTomlTableName(source, ["project", "tool.poetry"]) ??
|
|
1666
|
+
const name = parseTomlTableName(source, ["project", "tool.poetry"]) ?? path3.posix.basename(dir);
|
|
1603
1667
|
return {
|
|
1604
1668
|
name: `py:${name}`,
|
|
1605
1669
|
importPath: void 0,
|
|
1606
1670
|
// `src/` layout is the packaging-guide default; the root itself covers
|
|
1607
1671
|
// the flat layout. Both are probed, missing ones simply never match.
|
|
1608
|
-
sourceRoots: [
|
|
1672
|
+
sourceRoots: [path3.posix.join(dir, "src"), dir]
|
|
1609
1673
|
};
|
|
1610
1674
|
}
|
|
1611
1675
|
},
|
|
@@ -1613,24 +1677,24 @@ var MARKER_PROBES = [
|
|
|
1613
1677
|
kind: "python",
|
|
1614
1678
|
file: "setup.py",
|
|
1615
1679
|
build: (dir) => ({
|
|
1616
|
-
name: `py:${
|
|
1680
|
+
name: `py:${path3.posix.basename(dir)}`,
|
|
1617
1681
|
importPath: void 0,
|
|
1618
|
-
sourceRoots: [
|
|
1682
|
+
sourceRoots: [path3.posix.join(dir, "src"), dir]
|
|
1619
1683
|
})
|
|
1620
1684
|
},
|
|
1621
1685
|
{
|
|
1622
1686
|
kind: "maven",
|
|
1623
1687
|
file: "pom.xml",
|
|
1624
1688
|
build: (dir, source) => {
|
|
1625
|
-
const artifactId = parsePomArtifactId(source) ??
|
|
1689
|
+
const artifactId = parsePomArtifactId(source) ?? path3.posix.basename(dir);
|
|
1626
1690
|
return {
|
|
1627
1691
|
name: `mvn:${artifactId}`,
|
|
1628
1692
|
importPath: void 0,
|
|
1629
1693
|
sourceRoots: [
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1694
|
+
path3.posix.join(dir, "src/main/java"),
|
|
1695
|
+
path3.posix.join(dir, "src/main/kotlin"),
|
|
1696
|
+
path3.posix.join(dir, "src/main/scala"),
|
|
1697
|
+
path3.posix.join(dir, "src/test/java")
|
|
1634
1698
|
]
|
|
1635
1699
|
};
|
|
1636
1700
|
}
|
|
@@ -1648,12 +1712,12 @@ var MARKER_PROBES = [
|
|
|
1648
1712
|
];
|
|
1649
1713
|
function buildGradleRoot(dir) {
|
|
1650
1714
|
return {
|
|
1651
|
-
name: `gradle:${
|
|
1715
|
+
name: `gradle:${path3.posix.basename(dir)}`,
|
|
1652
1716
|
importPath: void 0,
|
|
1653
1717
|
sourceRoots: [
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1718
|
+
path3.posix.join(dir, "src/main/java"),
|
|
1719
|
+
path3.posix.join(dir, "src/main/kotlin"),
|
|
1720
|
+
path3.posix.join(dir, "src/main/scala")
|
|
1657
1721
|
]
|
|
1658
1722
|
};
|
|
1659
1723
|
}
|
|
@@ -1675,6 +1739,17 @@ async function probeDotnetRoot(dir) {
|
|
|
1675
1739
|
sourceRoots: [dir]
|
|
1676
1740
|
};
|
|
1677
1741
|
}
|
|
1742
|
+
var MARKER_PROBE_CONCURRENCY = 16;
|
|
1743
|
+
async function forEachLimited(items, limit, fn) {
|
|
1744
|
+
let next = 0;
|
|
1745
|
+
const worker = async () => {
|
|
1746
|
+
while (next < items.length) {
|
|
1747
|
+
const item = items[next++];
|
|
1748
|
+
await fn(item);
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
1752
|
+
}
|
|
1678
1753
|
async function detectModuleRoots(projectRoot, files) {
|
|
1679
1754
|
const root = toPortablePath(projectRoot).replace(/\/+$/, "");
|
|
1680
1755
|
const langsByDir = /* @__PURE__ */ new Map();
|
|
@@ -1682,7 +1757,7 @@ async function detectModuleRoots(projectRoot, files) {
|
|
|
1682
1757
|
const portable = toPortablePath(file);
|
|
1683
1758
|
const lang = detectLang(portable);
|
|
1684
1759
|
if (!lang) continue;
|
|
1685
|
-
const dir =
|
|
1760
|
+
const dir = path3.posix.dirname(portable);
|
|
1686
1761
|
let langs = langsByDir.get(dir);
|
|
1687
1762
|
if (!langs) {
|
|
1688
1763
|
langs = /* @__PURE__ */ new Set();
|
|
@@ -1702,21 +1777,19 @@ async function detectModuleRoots(projectRoot, files) {
|
|
|
1702
1777
|
}
|
|
1703
1778
|
}
|
|
1704
1779
|
const roots = [];
|
|
1705
|
-
await
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
})
|
|
1719
|
-
);
|
|
1780
|
+
await forEachLimited([...candidates], MARKER_PROBE_CONCURRENCY, async ([dir, langs]) => {
|
|
1781
|
+
for (const probe of MARKER_PROBES) {
|
|
1782
|
+
if (!LANGS_BY_KIND[probe.kind].some((lang) => langs.has(lang))) continue;
|
|
1783
|
+
const source = await readTextIfPresent(path3.posix.join(dir, probe.file));
|
|
1784
|
+
if (source === void 0) continue;
|
|
1785
|
+
const built = probe.build(dir, source);
|
|
1786
|
+
if (built) roots.push({ dir, kind: probe.kind, ...built });
|
|
1787
|
+
}
|
|
1788
|
+
if (LANGS_BY_KIND.dotnet.some((lang) => langs.has(lang))) {
|
|
1789
|
+
const dotnet = await probeDotnetRoot(dir);
|
|
1790
|
+
if (dotnet) roots.push(dotnet);
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1720
1793
|
roots.sort((a, b) => b.dir.length - a.dir.length || a.dir.localeCompare(b.dir));
|
|
1721
1794
|
return { projectRoot: root, roots };
|
|
1722
1795
|
}
|
|
@@ -1744,13 +1817,13 @@ function derivePackageFromLayout(filePath) {
|
|
|
1744
1817
|
}
|
|
1745
1818
|
function pythonPackageLabel(structure, file, initDirs) {
|
|
1746
1819
|
const portable = toPortablePath(file);
|
|
1747
|
-
const dir =
|
|
1820
|
+
const dir = path3.posix.dirname(portable);
|
|
1748
1821
|
if (!initDirs.has(dir)) return void 0;
|
|
1749
1822
|
const segments = [];
|
|
1750
1823
|
let current = dir;
|
|
1751
1824
|
while (initDirs.has(current) && current.length > structure.projectRoot.length) {
|
|
1752
|
-
segments.unshift(
|
|
1753
|
-
current =
|
|
1825
|
+
segments.unshift(path3.posix.basename(current));
|
|
1826
|
+
current = path3.posix.dirname(current);
|
|
1754
1827
|
}
|
|
1755
1828
|
return segments.length > 0 ? `py:${segments.join(".")}` : void 0;
|
|
1756
1829
|
}
|
|
@@ -1758,8 +1831,8 @@ function assignPackageLabels(structure, files) {
|
|
|
1758
1831
|
const initDirs = /* @__PURE__ */ new Set();
|
|
1759
1832
|
for (const file of files) {
|
|
1760
1833
|
const portable = toPortablePath(file);
|
|
1761
|
-
if (
|
|
1762
|
-
initDirs.add(
|
|
1834
|
+
if (path3.posix.basename(portable) === "__init__.py") {
|
|
1835
|
+
initDirs.add(path3.posix.dirname(portable));
|
|
1763
1836
|
}
|
|
1764
1837
|
}
|
|
1765
1838
|
const labels = /* @__PURE__ */ new Map();
|
|
@@ -1768,12 +1841,12 @@ function assignPackageLabels(structure, files) {
|
|
|
1768
1841
|
const lang = detectLang(portable);
|
|
1769
1842
|
if (lang === "go") {
|
|
1770
1843
|
const owner2 = findOwningRoot(structure, portable, ["go"]);
|
|
1771
|
-
const dir =
|
|
1844
|
+
const dir = path3.posix.dirname(portable);
|
|
1772
1845
|
if (owner2?.importPath) {
|
|
1773
|
-
const relative3 =
|
|
1846
|
+
const relative3 = path3.posix.relative(owner2.dir, dir);
|
|
1774
1847
|
labels.set(file, relative3 ? `${owner2.importPath}/${relative3}` : owner2.importPath);
|
|
1775
1848
|
} else {
|
|
1776
|
-
labels.set(file, `go:${
|
|
1849
|
+
labels.set(file, `go:${path3.posix.relative(structure.projectRoot, dir) || "."}`);
|
|
1777
1850
|
}
|
|
1778
1851
|
continue;
|
|
1779
1852
|
}
|
|
@@ -1969,6 +2042,34 @@ function chunkedIdQuery(stmt, ids, buildSql, extraArgs = []) {
|
|
|
1969
2042
|
}
|
|
1970
2043
|
return results;
|
|
1971
2044
|
}
|
|
2045
|
+
function chunkedValueQuery(stmt, values, buildSql) {
|
|
2046
|
+
const results = [];
|
|
2047
|
+
let cursor = 0;
|
|
2048
|
+
for (const take of inListChunks(values.length, MAX_SQL_VARS2)) {
|
|
2049
|
+
const chunk = padToInBucket(values.slice(cursor, cursor + take));
|
|
2050
|
+
cursor += take;
|
|
2051
|
+
results.push(...stmt(buildSql(placeholders(chunk.length))).all(...chunk));
|
|
2052
|
+
}
|
|
2053
|
+
return results;
|
|
2054
|
+
}
|
|
2055
|
+
function refCountsTouching(stmt, localIds) {
|
|
2056
|
+
const local = new Set(localIds);
|
|
2057
|
+
const outgoing = chunkedIdQuery(
|
|
2058
|
+
stmt,
|
|
2059
|
+
localIds,
|
|
2060
|
+
(ph) => `SELECT from_id, to_id, call_type, COUNT(*) AS n FROM refs
|
|
2061
|
+
WHERE from_id IN (${ph}) AND to_id IS NOT NULL
|
|
2062
|
+
GROUP BY from_id, to_id, call_type`
|
|
2063
|
+
);
|
|
2064
|
+
const incoming = chunkedIdQuery(
|
|
2065
|
+
stmt,
|
|
2066
|
+
localIds,
|
|
2067
|
+
(ph) => `SELECT from_id, to_id, call_type, COUNT(*) AS n FROM refs
|
|
2068
|
+
WHERE to_id IN (${ph})
|
|
2069
|
+
GROUP BY from_id, to_id, call_type`
|
|
2070
|
+
).filter((row) => !local.has(row.from_id));
|
|
2071
|
+
return [...outgoing, ...incoming];
|
|
2072
|
+
}
|
|
1972
2073
|
function getSymbolsByIdsWithStatement(stmt, ids) {
|
|
1973
2074
|
if (ids.length === 0) return [];
|
|
1974
2075
|
return chunkedIdQuery(
|
|
@@ -2010,6 +2111,9 @@ function mapCallSiteRow(row) {
|
|
|
2010
2111
|
line: row.ref_line
|
|
2011
2112
|
};
|
|
2012
2113
|
}
|
|
2114
|
+
function compareCallSiteRows(a, b) {
|
|
2115
|
+
return a.ref_line - b.ref_line || (a.sym_file < b.sym_file ? -1 : a.sym_file > b.sym_file ? 1 : 0) || a.sym_line - b.sym_line || a.sym_id - b.sym_id;
|
|
2116
|
+
}
|
|
2013
2117
|
function resolveIndexedFiles(stmt, file) {
|
|
2014
2118
|
const rows = stmt(
|
|
2015
2119
|
`SELECT DISTINCT file FROM symbols WHERE ${indexedFileMatchSql("file")} ORDER BY length(file), file`
|
|
@@ -2037,21 +2141,22 @@ function resolveSymbolIds(stmt, symbolName, file) {
|
|
|
2037
2141
|
}
|
|
2038
2142
|
return ids;
|
|
2039
2143
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2144
|
+
var MAX_DISAMBIGUATION_FILES = 64;
|
|
2145
|
+
function directIncomingRows(stmt, matchIds, scopedTargetIds) {
|
|
2146
|
+
const targetFiles = scopedTargetIds === null ? [] : chunkedIdQuery(
|
|
2147
|
+
stmt,
|
|
2148
|
+
scopedTargetIds,
|
|
2149
|
+
(ph) => `SELECT DISTINCT file FROM symbols WHERE id IN (${ph})`
|
|
2150
|
+
).map((row) => row.file);
|
|
2151
|
+
const refine = targetFiles.length > 0 && targetFiles.length <= MAX_DISAMBIGUATION_FILES;
|
|
2152
|
+
const fph = refine ? targetFiles.map(() => "?").join(",") : "";
|
|
2153
|
+
const evidenceFilter = refine ? `AND (r.to_file IS NULL OR r.to_file IN (${fph}))
|
|
2154
|
+
AND NOT EXISTS (
|
|
2155
|
+
SELECT 1 FROM refs ri JOIN symbols si ON si.id = ri.from_id
|
|
2156
|
+
WHERE si.file = s.file AND ri.call_type = 'import' AND ri.to_name = r.to_name
|
|
2157
|
+
AND ri.to_file IS NOT NULL AND ri.to_file NOT IN (${fph})
|
|
2158
|
+
)` : "";
|
|
2159
|
+
return chunkedIdQuery(
|
|
2055
2160
|
stmt,
|
|
2056
2161
|
matchIds,
|
|
2057
2162
|
(ph) => `SELECT
|
|
@@ -2067,9 +2172,26 @@ function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2067
2172
|
FROM refs r
|
|
2068
2173
|
JOIN symbols s ON s.id = r.from_id
|
|
2069
2174
|
WHERE r.to_id IN (${ph})
|
|
2175
|
+
${evidenceFilter}
|
|
2070
2176
|
ORDER BY r.line, r.id`,
|
|
2071
|
-
[]
|
|
2177
|
+
refine ? [...targetFiles, ...targetFiles] : []
|
|
2072
2178
|
);
|
|
2179
|
+
}
|
|
2180
|
+
function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
2181
|
+
const targetIds = resolveSymbolIds(stmt, symbolName, file);
|
|
2182
|
+
if (targetIds.length === 0)
|
|
2183
|
+
return { calls: [], symbolFound: false, ambiguous: false, totalMatches: 0 };
|
|
2184
|
+
let matchIds = targetIds;
|
|
2185
|
+
let ambiguous = false;
|
|
2186
|
+
if (file !== void 0) {
|
|
2187
|
+
const allNamedIds = resolveSymbolIds(stmt, symbolName, void 0);
|
|
2188
|
+
if (allNamedIds.length > targetIds.length) {
|
|
2189
|
+
matchIds = allNamedIds;
|
|
2190
|
+
ambiguous = true;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
const useFallback = !file;
|
|
2194
|
+
const rows = directIncomingRows(stmt, matchIds, ambiguous ? targetIds : null);
|
|
2073
2195
|
if (useFallback) {
|
|
2074
2196
|
const fallbackRows = stmt(
|
|
2075
2197
|
`SELECT
|
|
@@ -2089,7 +2211,7 @@ function findIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2089
2211
|
).all(symbolName);
|
|
2090
2212
|
rows.push(...fallbackRows);
|
|
2091
2213
|
}
|
|
2092
|
-
rows.sort(
|
|
2214
|
+
rows.sort(compareCallSiteRows);
|
|
2093
2215
|
const allCalls = rows.map(mapCallSiteRow);
|
|
2094
2216
|
return {
|
|
2095
2217
|
calls: allCalls.slice(0, limit),
|
|
@@ -2127,7 +2249,7 @@ function findOutgoingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2127
2249
|
ORDER BY r.line, r.id`,
|
|
2128
2250
|
[]
|
|
2129
2251
|
);
|
|
2130
|
-
rows.sort(
|
|
2252
|
+
rows.sort(compareCallSiteRows);
|
|
2131
2253
|
const calls = rows.map(mapCallSiteRow).slice(0, limit);
|
|
2132
2254
|
return { calls, symbolFound: true, unresolvedCount, totalMatches: rows.length };
|
|
2133
2255
|
}
|
|
@@ -2149,6 +2271,32 @@ function runCteWithSeeds(stmt, seedIds, buildSql) {
|
|
|
2149
2271
|
stmt("DROP TABLE IF EXISTS _cte_seeds").run();
|
|
2150
2272
|
}
|
|
2151
2273
|
}
|
|
2274
|
+
function annotateDirectEdges(stmt, rows, seedIds, seedColumn, otherColumn) {
|
|
2275
|
+
if (rows.length === 0) return;
|
|
2276
|
+
const edges = chunkedIdQuery(
|
|
2277
|
+
stmt,
|
|
2278
|
+
[...seedIds],
|
|
2279
|
+
(ph) => `SELECT ${otherColumn} AS sym_id, call_type, line
|
|
2280
|
+
FROM refs
|
|
2281
|
+
WHERE ${seedColumn} IN (${ph}) AND ${otherColumn} IS NOT NULL`,
|
|
2282
|
+
[]
|
|
2283
|
+
);
|
|
2284
|
+
const best = /* @__PURE__ */ new Map();
|
|
2285
|
+
for (const edge of edges) {
|
|
2286
|
+
const current = best.get(edge.sym_id);
|
|
2287
|
+
const edgeIsImport = edge.call_type === "import";
|
|
2288
|
+
const currentIsImport = current?.call_type === "import";
|
|
2289
|
+
if (!current || currentIsImport && !edgeIsImport || currentIsImport === edgeIsImport && edge.line < current.line) {
|
|
2290
|
+
best.set(edge.sym_id, { call_type: edge.call_type, line: edge.line });
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
for (const row of rows) {
|
|
2294
|
+
const edge = best.get(row.sym_id);
|
|
2295
|
+
if (!edge) continue;
|
|
2296
|
+
row.call_type = edge.call_type;
|
|
2297
|
+
row.ref_line = edge.line;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2152
2300
|
function findTransitiveIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
2153
2301
|
const targetIds = resolveSymbolIds(stmt, symbolName, file);
|
|
2154
2302
|
if (targetIds.length === 0)
|
|
@@ -2162,10 +2310,11 @@ function findTransitiveIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2162
2310
|
ambiguous = true;
|
|
2163
2311
|
}
|
|
2164
2312
|
}
|
|
2313
|
+
const anchorCallerIds = ambiguous ? [...new Set(directIncomingRows(stmt, matchIds, targetIds).map((row) => row.sym_id))] : null;
|
|
2165
2314
|
const cteSql = (seedSource) => `WITH RECURSIVE incoming_tree(from_id) AS (
|
|
2166
|
-
SELECT r.from_id
|
|
2315
|
+
${anchorCallerIds === null ? `SELECT r.from_id
|
|
2167
2316
|
FROM refs r
|
|
2168
|
-
WHERE r.to_id IN (${seedSource})
|
|
2317
|
+
WHERE r.to_id IN (${seedSource})` : `SELECT s0.id FROM symbols s0 WHERE s0.id IN (${seedSource})`}
|
|
2169
2318
|
|
|
2170
2319
|
UNION
|
|
2171
2320
|
|
|
@@ -2187,7 +2336,8 @@ function findTransitiveIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2187
2336
|
JOIN symbols s ON s.id = it.from_id
|
|
2188
2337
|
GROUP BY s.id
|
|
2189
2338
|
ORDER BY s.file, s.line`;
|
|
2190
|
-
const rows = runCteWithSeeds(stmt, matchIds, cteSql);
|
|
2339
|
+
const rows = anchorCallerIds !== null && anchorCallerIds.length === 0 ? [] : runCteWithSeeds(stmt, anchorCallerIds ?? matchIds, cteSql);
|
|
2340
|
+
annotateDirectEdges(stmt, rows, matchIds, "to_id", "from_id");
|
|
2191
2341
|
if (!file) {
|
|
2192
2342
|
const fallbackRows = stmt(
|
|
2193
2343
|
`SELECT
|
|
@@ -2207,7 +2357,7 @@ function findTransitiveIncomingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2207
2357
|
).all(symbolName);
|
|
2208
2358
|
rows.push(...fallbackRows);
|
|
2209
2359
|
}
|
|
2210
|
-
rows.sort(
|
|
2360
|
+
rows.sort(compareCallSiteRows);
|
|
2211
2361
|
const allCalls = rows.map(mapCallSiteRow);
|
|
2212
2362
|
return {
|
|
2213
2363
|
calls: allCalls.slice(0, limit),
|
|
@@ -2252,6 +2402,7 @@ function findTransitiveOutgoingCallsByName(stmt, symbolName, file, limit) {
|
|
|
2252
2402
|
GROUP BY s.id
|
|
2253
2403
|
ORDER BY s.file, s.line`;
|
|
2254
2404
|
const rows = runCteWithSeeds(stmt, sourceIds, cteSql);
|
|
2405
|
+
annotateDirectEdges(stmt, rows, sourceIds, "from_id", "to_id");
|
|
2255
2406
|
const calls = rows.map(mapCallSiteRow).slice(0, limit);
|
|
2256
2407
|
return { calls, symbolFound: true, unresolvedCount, totalMatches: rows.length };
|
|
2257
2408
|
}
|
|
@@ -2361,33 +2512,29 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
2361
2512
|
const pkgFilePaths = allFiles.filter((f) => matchesIndexedPackageFilter(f.file, packageOf(f.file), packageFilter)).map((f) => f.file);
|
|
2362
2513
|
const localFiles = new Set(pkgFilePaths);
|
|
2363
2514
|
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2515
|
+
const pkgSyms = chunkedValueQuery(
|
|
2516
|
+
stmt,
|
|
2517
|
+
[...localFiles],
|
|
2518
|
+
(ph) => `SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${ph})`
|
|
2519
|
+
).sort((a, b) => a.id - b.id);
|
|
2368
2520
|
const { fileNodes, symToFile, fileStats, ensureFileNode } = buildFileGraphNodeState(
|
|
2369
2521
|
pkgSyms,
|
|
2370
2522
|
localFiles,
|
|
2371
2523
|
packageOf
|
|
2372
2524
|
);
|
|
2373
|
-
const
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
2377
|
-
OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))
|
|
2378
|
-
AND r.to_id IS NOT NULL
|
|
2379
|
-
GROUP BY r.from_id, r.to_id, r.call_type`
|
|
2380
|
-
).all(...pkgFilePaths, ...pkgFilePaths);
|
|
2381
|
-
const knownSymIds = new Set(pkgSyms.map((s) => s.id));
|
|
2525
|
+
const localIds = pkgSyms.map((s) => s.id);
|
|
2526
|
+
const refRows = refCountsTouching(stmt, localIds);
|
|
2527
|
+
const knownSymIds = new Set(localIds);
|
|
2382
2528
|
const crossRefIds = /* @__PURE__ */ new Set();
|
|
2383
2529
|
for (const r of refRows) {
|
|
2384
2530
|
if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);
|
|
2385
2531
|
if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);
|
|
2386
2532
|
}
|
|
2387
2533
|
if (crossRefIds.size > 0) {
|
|
2388
|
-
const
|
|
2389
|
-
|
|
2390
|
-
...crossRefIds
|
|
2534
|
+
const extras = chunkedIdQuery(
|
|
2535
|
+
stmt,
|
|
2536
|
+
[...crossRefIds],
|
|
2537
|
+
(ph) => `SELECT id, file FROM symbols WHERE id IN (${ph})`
|
|
2391
2538
|
);
|
|
2392
2539
|
for (const x of extras) {
|
|
2393
2540
|
symToFile.set(x.id, x.file);
|
|
@@ -2408,15 +2555,17 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
2408
2555
|
const n = Number(r.n) || 0;
|
|
2409
2556
|
addWeightedEdge(edgeMap, fromFile, toFile, r.call_type, n);
|
|
2410
2557
|
}
|
|
2411
|
-
const importRows =
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2558
|
+
const importRows = chunkedIdQuery(
|
|
2559
|
+
stmt,
|
|
2560
|
+
localIds,
|
|
2561
|
+
(ph) => `SELECT r.from_id, COALESCE(r.to_file, st.file) AS to_file, COUNT(*) AS n
|
|
2562
|
+
FROM refs r
|
|
2563
|
+
LEFT JOIN symbols st ON st.id = r.to_id
|
|
2564
|
+
WHERE r.call_type = 'import'
|
|
2565
|
+
AND COALESCE(r.to_file, st.file) IS NOT NULL
|
|
2566
|
+
AND r.from_id IN (${ph})
|
|
2567
|
+
GROUP BY r.from_id, COALESCE(r.to_file, st.file)`
|
|
2568
|
+
);
|
|
2420
2569
|
for (const r of importRows) {
|
|
2421
2570
|
const fromFile = symToFile.get(r.from_id);
|
|
2422
2571
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
@@ -2438,29 +2587,18 @@ function getFileGraphWithStatement(stmt, packageFilter) {
|
|
|
2438
2587
|
function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
2439
2588
|
const indexedFiles = resolveIndexedFiles(stmt, fileFilter);
|
|
2440
2589
|
if (indexedFiles.length === 0) return { nodes: [], edges: [] };
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2590
|
+
const syms = chunkedValueQuery(
|
|
2591
|
+
stmt,
|
|
2592
|
+
indexedFiles,
|
|
2593
|
+
(ph) => `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file IN (${ph})`
|
|
2594
|
+
).sort((a, b) => a.line - b.line || a.id - b.id);
|
|
2445
2595
|
if (syms.length === 0) return { nodes: [], edges: [] };
|
|
2446
2596
|
const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
|
|
2447
2597
|
const relatedIds = new Set(syms.map((symbol) => symbol.id));
|
|
2448
|
-
const refRows =
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
FROM refs r
|
|
2453
|
-
JOIN symbols s ON s.id = r.from_id
|
|
2454
|
-
WHERE s.file IN (${filePlaceholders})
|
|
2455
|
-
UNION ALL
|
|
2456
|
-
SELECT r.from_id, r.to_id, r.call_type
|
|
2457
|
-
FROM refs r
|
|
2458
|
-
JOIN symbols s ON s.id = r.to_id
|
|
2459
|
-
WHERE s.file IN (${filePlaceholders})
|
|
2460
|
-
)
|
|
2461
|
-
WHERE to_id IS NOT NULL
|
|
2462
|
-
GROUP BY from_id, to_id, call_type`
|
|
2463
|
-
).all(...indexedFiles, ...indexedFiles);
|
|
2598
|
+
const refRows = refCountsTouching(
|
|
2599
|
+
stmt,
|
|
2600
|
+
syms.map((symbol) => symbol.id)
|
|
2601
|
+
);
|
|
2464
2602
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
2465
2603
|
for (const r of refRows) {
|
|
2466
2604
|
if (r.to_id == null) continue;
|
|
@@ -2473,11 +2611,7 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
|
2473
2611
|
const loadedIds = new Set(syms.map((s) => s.id));
|
|
2474
2612
|
const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));
|
|
2475
2613
|
if (missingIds.length > 0) {
|
|
2476
|
-
const
|
|
2477
|
-
const extras = stmt(
|
|
2478
|
-
`SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders2})`
|
|
2479
|
-
).all(...missingIds);
|
|
2480
|
-
for (const s of extras) symById.set(s.id, s);
|
|
2614
|
+
for (const s of getSymbolsByIdsWithStatement(stmt, missingIds)) symById.set(s.id, s);
|
|
2481
2615
|
}
|
|
2482
2616
|
const packageOf = readPackageLabeller(stmt);
|
|
2483
2617
|
const nodes = buildSymbolGraphNodes(
|
|
@@ -2799,7 +2933,7 @@ function allocateSymbolIds(stmt, count, getMaxSymbolId) {
|
|
|
2799
2933
|
if (count <= 0) return getMaxSymbolId() + 1;
|
|
2800
2934
|
ensureNextSymbolIdSeeded(stmt);
|
|
2801
2935
|
const row = stmt("SELECT value FROM metadata WHERE key = ?").get(NEXT_SYMBOL_ID_KEY);
|
|
2802
|
-
const start = Math.max(1, Number(row?.value ?? 1) || 1);
|
|
2936
|
+
const start = Math.max(1, Number(row?.value ?? 1) || 1, getMaxSymbolId() + 1);
|
|
2803
2937
|
stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)").run(
|
|
2804
2938
|
NEXT_SYMBOL_ID_KEY,
|
|
2805
2939
|
String(start + count)
|
|
@@ -2880,6 +3014,9 @@ function compactIfNeeded(db, stmt, runWithRetry, options = {}) {
|
|
|
2880
3014
|
}
|
|
2881
3015
|
|
|
2882
3016
|
// src/codebase-index/writer-mutations.ts
|
|
3017
|
+
function rankKey(file, name, kind, scope) {
|
|
3018
|
+
return `${file}\0${name}\0${kind}\0${scope}`;
|
|
3019
|
+
}
|
|
2883
3020
|
function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvailable, allocateSymbolIds2, invalidateIncomingRefsForFiles, resolveRefsForNamesUnsafe2, entries, options = {}) {
|
|
2884
3021
|
if (entries.length === 0 && (options.deleteForFiles?.length ?? 0) === 0) {
|
|
2885
3022
|
return [];
|
|
@@ -2889,6 +3026,7 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
|
|
|
2889
3026
|
for (const symbol of entry.symbols) affectedNames.add(symbol.name);
|
|
2890
3027
|
for (const ref of entry.refs) affectedNames.add(ref.toName);
|
|
2891
3028
|
}
|
|
3029
|
+
const carriedRanks = /* @__PURE__ */ new Map();
|
|
2892
3030
|
if (options.deleteForFiles && options.deleteForFiles.length > 0) {
|
|
2893
3031
|
for (const name of invalidateIncomingRefsForFiles(options.deleteForFiles)) {
|
|
2894
3032
|
affectedNames.add(name);
|
|
@@ -2898,6 +3036,21 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
|
|
|
2898
3036
|
const bucket = padToInBucket(options.deleteForFiles.slice(cursor, cursor + take));
|
|
2899
3037
|
cursor += take;
|
|
2900
3038
|
const ph = placeholders(bucket.length);
|
|
3039
|
+
const rankRows = stmtFn(
|
|
3040
|
+
`SELECT s.file AS file, s.name AS name, s.kind AS kind, s.scope AS scope,
|
|
3041
|
+
r.rank AS rank, r.in_deg AS inDeg, r.out_deg AS outDeg
|
|
3042
|
+
FROM symbol_rank r JOIN symbols s ON s.id = r.symbol_id
|
|
3043
|
+
WHERE s.file IN (${ph})`
|
|
3044
|
+
).all(...bucket);
|
|
3045
|
+
for (const row of rankRows) {
|
|
3046
|
+
const key = rankKey(row.file, row.name, row.kind, row.scope);
|
|
3047
|
+
if (!carriedRanks.has(key)) {
|
|
3048
|
+
carriedRanks.set(key, { rank: row.rank, inDeg: row.inDeg, outDeg: row.outDeg });
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
stmtFn(
|
|
3052
|
+
`DELETE FROM symbol_rank WHERE symbol_id IN (SELECT id FROM symbols WHERE file IN (${ph}))`
|
|
3053
|
+
).run(...bucket);
|
|
2901
3054
|
if (ftsAvailable) {
|
|
2902
3055
|
stmtFn(
|
|
2903
3056
|
`DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file IN (${ph}))`
|
|
@@ -2961,6 +3114,24 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
|
|
|
2961
3114
|
bulkInsertVectorsWithStatement((sql) => stmtFn(sql), maxSqlVars, vectorRows);
|
|
2962
3115
|
}
|
|
2963
3116
|
bulkInsertRefsWithStatement((sql) => stmtFn(sql), maxSqlVars, refsToInsert);
|
|
3117
|
+
if (carriedRanks.size > 0) {
|
|
3118
|
+
const rankRows = [];
|
|
3119
|
+
for (const symbol of allInserted) {
|
|
3120
|
+
const carried = carriedRanks.get(
|
|
3121
|
+
rankKey(symbol.file, symbol.name, symbol.kind, symbol.scope)
|
|
3122
|
+
);
|
|
3123
|
+
if (carried) rankRows.push([symbol.id, carried.rank, carried.inDeg, carried.outDeg]);
|
|
3124
|
+
}
|
|
3125
|
+
let cursor = 0;
|
|
3126
|
+
for (const take of ladderChunkSizes(rankRows.length, Math.max(1, Math.floor(maxSqlVars / 4)))) {
|
|
3127
|
+
const chunk = rankRows.slice(cursor, cursor + take);
|
|
3128
|
+
cursor += take;
|
|
3129
|
+
stmtFn(
|
|
3130
|
+
`INSERT OR REPLACE INTO symbol_rank(symbol_id, rank, in_deg, out_deg)
|
|
3131
|
+
VALUES ${chunk.map(() => "(?, ?, ?, ?)").join(", ")}`
|
|
3132
|
+
).run(...chunk.flat());
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
2964
3135
|
const upsertStmt = stmtFn(
|
|
2965
3136
|
`INSERT INTO files(file, lang, mtime_ms, content_hash, symbol_count, last_indexed)
|
|
2966
3137
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
@@ -3289,6 +3460,16 @@ function resolveRefsWithStatement(stmtFn) {
|
|
|
3289
3460
|
return result.changes ?? 0;
|
|
3290
3461
|
}
|
|
3291
3462
|
}
|
|
3463
|
+
function getFilesWithDanglingImportsWithStatement(stmtFn) {
|
|
3464
|
+
return stmtFn(
|
|
3465
|
+
`SELECT DISTINCT s.file AS file
|
|
3466
|
+
FROM refs r
|
|
3467
|
+
JOIN symbols s ON s.id = r.from_id
|
|
3468
|
+
WHERE r.call_type = 'import'
|
|
3469
|
+
AND r.to_file IS NOT NULL
|
|
3470
|
+
AND NOT EXISTS (SELECT 1 FROM files f WHERE f.file = r.to_file)`
|
|
3471
|
+
).all().map((row) => row.file);
|
|
3472
|
+
}
|
|
3292
3473
|
function applyImportResolutionsWithStatement(db, stmtFn, runWithRetry, maxSqlVars, resolutions) {
|
|
3293
3474
|
if (resolutions.length === 0) return 0;
|
|
3294
3475
|
return runWithRetry(() => {
|
|
@@ -3298,7 +3479,7 @@ function applyImportResolutionsWithStatement(db, stmtFn, runWithRetry, maxSqlVar
|
|
|
3298
3479
|
from_file TEXT NOT NULL,
|
|
3299
3480
|
lang TEXT NOT NULL,
|
|
3300
3481
|
module TEXT NOT NULL,
|
|
3301
|
-
to_file TEXT
|
|
3482
|
+
to_file TEXT
|
|
3302
3483
|
)`
|
|
3303
3484
|
);
|
|
3304
3485
|
const chunkSize = Math.max(1, Math.floor(maxSqlVars / 4));
|
|
@@ -3404,21 +3585,23 @@ var SEARCH_CANDIDATE_SCAN_CAP = 5e3;
|
|
|
3404
3585
|
function normalizeSearchLimit(limit) {
|
|
3405
3586
|
return typeof limit === "number" && Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : void 0;
|
|
3406
3587
|
}
|
|
3588
|
+
function resolveKindFilter(filter) {
|
|
3589
|
+
const lspKinds = filter?.lspKind != null ? lspKindToInternalKinds(filter.lspKind) : void 0;
|
|
3590
|
+
if (lspKinds !== void 0 && lspKinds.length === 0) return null;
|
|
3591
|
+
if (filter?.kind) {
|
|
3592
|
+
if (lspKinds !== void 0 && !lspKinds.includes(filter.kind)) return null;
|
|
3593
|
+
return [filter.kind];
|
|
3594
|
+
}
|
|
3595
|
+
return lspKinds;
|
|
3596
|
+
}
|
|
3407
3597
|
function buildWriterSearchWhere(query, filter) {
|
|
3408
3598
|
const conditions = [];
|
|
3409
3599
|
const values = [];
|
|
3410
|
-
|
|
3411
|
-
if (
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
} else {
|
|
3416
|
-
return null;
|
|
3417
|
-
}
|
|
3418
|
-
}
|
|
3419
|
-
if (effectiveKind) {
|
|
3420
|
-
conditions.push("kind = ?");
|
|
3421
|
-
values.push(effectiveKind);
|
|
3600
|
+
const kinds = resolveKindFilter(filter);
|
|
3601
|
+
if (kinds === null) return null;
|
|
3602
|
+
if (kinds !== void 0) {
|
|
3603
|
+
conditions.push(`kind IN (${kinds.map(() => "?").join(", ")})`);
|
|
3604
|
+
values.push(...kinds);
|
|
3422
3605
|
}
|
|
3423
3606
|
if (filter?.lang) {
|
|
3424
3607
|
conditions.push("lang = ?");
|
|
@@ -3493,12 +3676,8 @@ function searchRankedWithStatement(stmtFn, searchFn, ftsAvailable, vectorsAvaila
|
|
|
3493
3676
|
safeLimit
|
|
3494
3677
|
);
|
|
3495
3678
|
}
|
|
3496
|
-
|
|
3497
|
-
if (
|
|
3498
|
-
const mapped = lspKindToInternalKind(filter.lspKind);
|
|
3499
|
-
if (mapped === null) return { results: [], total: 0 };
|
|
3500
|
-
effectiveKind = mapped;
|
|
3501
|
-
}
|
|
3679
|
+
const kinds = resolveKindFilter(filter);
|
|
3680
|
+
if (kinds === null) return { results: [], total: 0 };
|
|
3502
3681
|
const longTokens = tokens.filter((t) => t.length >= 3);
|
|
3503
3682
|
const shortTokens = tokens.filter((t) => t.length < 3);
|
|
3504
3683
|
if (longTokens.length === 0) {
|
|
@@ -3521,9 +3700,9 @@ function searchRankedWithStatement(stmtFn, searchFn, ftsAvailable, vectorsAvaila
|
|
|
3521
3700
|
);
|
|
3522
3701
|
values.push(like, like, like);
|
|
3523
3702
|
}
|
|
3524
|
-
if (
|
|
3525
|
-
conditions.push(
|
|
3526
|
-
values.push(
|
|
3703
|
+
if (kinds !== void 0) {
|
|
3704
|
+
conditions.push(`s.kind IN (${kinds.map(() => "?").join(", ")})`);
|
|
3705
|
+
values.push(...kinds);
|
|
3527
3706
|
}
|
|
3528
3707
|
if (filter?.lang) {
|
|
3529
3708
|
conditions.push("s.lang = ?");
|
|
@@ -3560,10 +3739,17 @@ function searchRankedWithStatement(stmtFn, searchFn, ftsAvailable, vectorsAvaila
|
|
|
3560
3739
|
const vecRows = stmtFn(
|
|
3561
3740
|
`SELECT sv.symbol_id, sv.vector FROM symbol_vectors sv WHERE sv.symbol_id IN (${placeholders2})`
|
|
3562
3741
|
).all(...candidateIds);
|
|
3563
|
-
const vecScores =
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3742
|
+
const vecScores = [];
|
|
3743
|
+
for (const r of vecRows) {
|
|
3744
|
+
try {
|
|
3745
|
+
vecScores.push({
|
|
3746
|
+
id: r.symbol_id,
|
|
3747
|
+
sim: cosineSimilarity(queryVec, decodeVector(r.vector))
|
|
3748
|
+
});
|
|
3749
|
+
} catch {
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
vecScores.sort((a, b) => b.sim - a.sim);
|
|
3567
3753
|
const bm25Rank = /* @__PURE__ */ new Map();
|
|
3568
3754
|
bm25Rows.forEach((r, i) => {
|
|
3569
3755
|
bm25Rank.set(r.id, i);
|
|
@@ -3667,7 +3853,7 @@ var StorePool = class {
|
|
|
3667
3853
|
release(store) {
|
|
3668
3854
|
const k = this.keyByStore.get(store);
|
|
3669
3855
|
const entry = k === void 0 ? void 0 : this.stores.get(k);
|
|
3670
|
-
if (entry && entry.refs > 0) entry.refs--;
|
|
3856
|
+
if (entry && entry.store === store && entry.refs > 0) entry.refs--;
|
|
3671
3857
|
this.trim();
|
|
3672
3858
|
}
|
|
3673
3859
|
trim() {
|
|
@@ -3729,6 +3915,8 @@ var IndexStore = class _IndexStore {
|
|
|
3729
3915
|
stmtCache = /* @__PURE__ */ new Map();
|
|
3730
3916
|
bm25Cache = null;
|
|
3731
3917
|
bm25Dirty = true;
|
|
3918
|
+
/** `PRAGMA data_version` the BM25 cache was built at. */
|
|
3919
|
+
bm25DataVersion = -1;
|
|
3732
3920
|
stmt(sql) {
|
|
3733
3921
|
const cached2 = this.stmtCache.get(sql);
|
|
3734
3922
|
if (cached2 !== void 0) {
|
|
@@ -3748,7 +3936,7 @@ var IndexStore = class _IndexStore {
|
|
|
3748
3936
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
3749
3937
|
fs3.mkdirSync(this.indexDir, { recursive: true });
|
|
3750
3938
|
const Database = loadDatabaseSync();
|
|
3751
|
-
this.db = new Database(
|
|
3939
|
+
this.db = new Database(path4.join(this.indexDir, DB_FILE2));
|
|
3752
3940
|
applyIndexStorePragmas(this.db);
|
|
3753
3941
|
this.initSchema();
|
|
3754
3942
|
}
|
|
@@ -3768,6 +3956,7 @@ var IndexStore = class _IndexStore {
|
|
|
3768
3956
|
this.db.exec("ROLLBACK");
|
|
3769
3957
|
} catch {
|
|
3770
3958
|
}
|
|
3959
|
+
this.invalidateBm25();
|
|
3771
3960
|
throw error;
|
|
3772
3961
|
} finally {
|
|
3773
3962
|
this.atomicIndexUpdateActive = false;
|
|
@@ -3786,12 +3975,22 @@ var IndexStore = class _IndexStore {
|
|
|
3786
3975
|
if (savepoint) this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
3787
3976
|
else this.db.exec("COMMIT");
|
|
3788
3977
|
}
|
|
3978
|
+
/**
|
|
3979
|
+
* Never throws: every caller is already propagating the failure that caused
|
|
3980
|
+
* the rollback. When SQLite has rolled the transaction back itself (disk
|
|
3981
|
+
* full, I/O error) the ROLLBACK fails with "no transaction is active", and
|
|
3982
|
+
* that message used to replace the real error.
|
|
3983
|
+
*/
|
|
3789
3984
|
rollbackWriteTransaction(savepoint) {
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3985
|
+
this.invalidateBm25();
|
|
3986
|
+
try {
|
|
3987
|
+
if (savepoint) {
|
|
3988
|
+
this.db.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
3989
|
+
this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
3990
|
+
} else {
|
|
3991
|
+
this.db.exec("ROLLBACK");
|
|
3992
|
+
}
|
|
3993
|
+
} catch {
|
|
3795
3994
|
}
|
|
3796
3995
|
}
|
|
3797
3996
|
initSchema() {
|
|
@@ -3876,6 +4075,9 @@ var IndexStore = class _IndexStore {
|
|
|
3876
4075
|
"DELETE FROM symbol_vectors WHERE symbol_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
3877
4076
|
).run(file);
|
|
3878
4077
|
}
|
|
4078
|
+
this.stmt(
|
|
4079
|
+
"DELETE FROM symbol_rank WHERE symbol_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
4080
|
+
).run(file);
|
|
3879
4081
|
const deletedChanges = Number(
|
|
3880
4082
|
this.stmt("DELETE FROM symbols WHERE file = ?").run(file).changes
|
|
3881
4083
|
);
|
|
@@ -3907,6 +4109,10 @@ var IndexStore = class _IndexStore {
|
|
|
3907
4109
|
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
3908
4110
|
file
|
|
3909
4111
|
);
|
|
4112
|
+
this.stmt(
|
|
4113
|
+
"DELETE FROM symbol_rank WHERE symbol_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
4114
|
+
).run(file);
|
|
4115
|
+
this.stmt("DELETE FROM file_rank WHERE file = ?").run(file);
|
|
3910
4116
|
const deletedChanges = Number(
|
|
3911
4117
|
this.stmt("DELETE FROM symbols WHERE file = ?").run(file).changes
|
|
3912
4118
|
);
|
|
@@ -3945,6 +4151,9 @@ var IndexStore = class _IndexStore {
|
|
|
3945
4151
|
onlyFiles
|
|
3946
4152
|
);
|
|
3947
4153
|
}
|
|
4154
|
+
getFilesWithDanglingImports() {
|
|
4155
|
+
return getFilesWithDanglingImportsWithStatement((sql) => this.stmt(sql));
|
|
4156
|
+
}
|
|
3948
4157
|
applyImportResolutions(resolutions) {
|
|
3949
4158
|
return applyImportResolutionsWithStatement(
|
|
3950
4159
|
this.db,
|
|
@@ -3976,13 +4185,33 @@ var IndexStore = class _IndexStore {
|
|
|
3976
4185
|
this.bm25Dirty = true;
|
|
3977
4186
|
this.bm25Cache = null;
|
|
3978
4187
|
}
|
|
4188
|
+
/**
|
|
4189
|
+
* The corpus is rebuilt when this connection wrote (bm25Dirty) OR another
|
|
4190
|
+
* connection committed since it was built. Pooled stores stay open for the
|
|
4191
|
+
* life of the host while the project daemon, an inline indexer or another
|
|
4192
|
+
* process writes the same database; the dirty flag alone never saw those
|
|
4193
|
+
* writes, so short-query results silently dropped every symbol added since.
|
|
4194
|
+
*/
|
|
3979
4195
|
getOrBuildBm25() {
|
|
3980
|
-
|
|
4196
|
+
const version = this.dataVersion();
|
|
4197
|
+
if (this.bm25Cache && !this.bm25Dirty && version === this.bm25DataVersion) {
|
|
4198
|
+
return this.bm25Cache;
|
|
4199
|
+
}
|
|
3981
4200
|
const docs = this.getAllIndexable();
|
|
3982
4201
|
this.bm25Cache = buildBm25Index(docs);
|
|
3983
4202
|
this.bm25Dirty = false;
|
|
4203
|
+
this.bm25DataVersion = version;
|
|
3984
4204
|
return this.bm25Cache;
|
|
3985
4205
|
}
|
|
4206
|
+
/** Changes whenever ANOTHER connection commits to this database. */
|
|
4207
|
+
dataVersion() {
|
|
4208
|
+
try {
|
|
4209
|
+
const row = this.stmt("PRAGMA data_version").get();
|
|
4210
|
+
return Number(row?.data_version ?? -1);
|
|
4211
|
+
} catch {
|
|
4212
|
+
return -1;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
3986
4215
|
getAllIndexable() {
|
|
3987
4216
|
return getAllIndexableWithStatement((sql) => this.stmt(sql));
|
|
3988
4217
|
}
|
|
@@ -4128,6 +4357,9 @@ var IndexStore = class _IndexStore {
|
|
|
4128
4357
|
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
4129
4358
|
meta.file
|
|
4130
4359
|
);
|
|
4360
|
+
this.stmt(
|
|
4361
|
+
"DELETE FROM symbol_rank WHERE symbol_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
4362
|
+
).run(meta.file);
|
|
4131
4363
|
const deletedChanges = Number(
|
|
4132
4364
|
this.stmt("DELETE FROM symbols WHERE file = ?").run(meta.file).changes
|
|
4133
4365
|
);
|
|
@@ -4252,7 +4484,9 @@ var IndexStore = class _IndexStore {
|
|
|
4252
4484
|
return getSymbolGraphWithStatement((sql) => this.stmt(sql), fileFilter);
|
|
4253
4485
|
}
|
|
4254
4486
|
getAllSymbols() {
|
|
4255
|
-
return this.stmt(
|
|
4487
|
+
return this.stmt(
|
|
4488
|
+
"SELECT id, name, file, kind, line, scope FROM symbols ORDER BY id"
|
|
4489
|
+
).all().map((r) => ({ ...r, kind: r.kind }));
|
|
4256
4490
|
}
|
|
4257
4491
|
/** Declarations in one file, in source order. */
|
|
4258
4492
|
getFileSymbols(file, limit) {
|
|
@@ -4422,7 +4656,7 @@ import { execFile } from "node:child_process";
|
|
|
4422
4656
|
import { createHash } from "node:crypto";
|
|
4423
4657
|
import * as fs5 from "node:fs/promises";
|
|
4424
4658
|
import { availableParallelism } from "node:os";
|
|
4425
|
-
import * as
|
|
4659
|
+
import * as path6 from "node:path";
|
|
4426
4660
|
import {
|
|
4427
4661
|
DEFAULT_WALK_IGNORE_DIRS,
|
|
4428
4662
|
indexParallelBatchSize,
|
|
@@ -4507,7 +4741,7 @@ function xxhash64String(content) {
|
|
|
4507
4741
|
}
|
|
4508
4742
|
|
|
4509
4743
|
// src/codebase-index/module-resolver.ts
|
|
4510
|
-
import * as
|
|
4744
|
+
import * as path5 from "node:path";
|
|
4511
4745
|
var EXTENSIONS = {
|
|
4512
4746
|
js: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte"],
|
|
4513
4747
|
py: [".py", ".pyi"],
|
|
@@ -4526,6 +4760,13 @@ var DIRECTORY_ENTRIES = {
|
|
|
4526
4760
|
function normalizeNamespace(value) {
|
|
4527
4761
|
return value.replace(/::|[\\/]/g, ".").replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
4528
4762
|
}
|
|
4763
|
+
function rustModuleDir(file) {
|
|
4764
|
+
const base = path5.posix.basename(file);
|
|
4765
|
+
if (base === "mod.rs" || base === "lib.rs" || base === "main.rs") {
|
|
4766
|
+
return path5.posix.dirname(file);
|
|
4767
|
+
}
|
|
4768
|
+
return file.replace(/\.rs$/i, "");
|
|
4769
|
+
}
|
|
4529
4770
|
var ModuleResolver = class {
|
|
4530
4771
|
structure;
|
|
4531
4772
|
/** Lowercased portable path → the path as indexed (case is preserved). */
|
|
@@ -4540,14 +4781,23 @@ var ModuleResolver = class {
|
|
|
4540
4781
|
this.byDir = /* @__PURE__ */ new Map();
|
|
4541
4782
|
this.byNamespace = /* @__PURE__ */ new Map();
|
|
4542
4783
|
const dirsByKey = /* @__PURE__ */ new Map();
|
|
4784
|
+
const collidedPaths = /* @__PURE__ */ new Set();
|
|
4785
|
+
const collidedDirs = /* @__PURE__ */ new Set();
|
|
4543
4786
|
for (const file of files) {
|
|
4544
4787
|
const portable = toPortablePath(file);
|
|
4545
4788
|
const pathKey = portable.toLowerCase();
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4789
|
+
if (!collidedPaths.has(pathKey)) {
|
|
4790
|
+
const priorPath = this.byPath.get(pathKey);
|
|
4791
|
+
if (priorPath !== void 0 && priorPath !== file) {
|
|
4792
|
+
this.byPath.delete(pathKey);
|
|
4793
|
+
collidedPaths.add(pathKey);
|
|
4794
|
+
} else {
|
|
4795
|
+
this.byPath.set(pathKey, file);
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
const dir = path5.posix.dirname(portable);
|
|
4550
4799
|
const dirKey = dir.toLowerCase();
|
|
4800
|
+
if (collidedDirs.has(dirKey)) continue;
|
|
4551
4801
|
const knownDir = dirsByKey.get(dirKey);
|
|
4552
4802
|
if (knownDir === void 0) {
|
|
4553
4803
|
dirsByKey.set(dirKey, dir);
|
|
@@ -4557,6 +4807,7 @@ var ModuleResolver = class {
|
|
|
4557
4807
|
} else {
|
|
4558
4808
|
dirsByKey.delete(dirKey);
|
|
4559
4809
|
this.byDir.delete(dirKey);
|
|
4810
|
+
collidedDirs.add(dirKey);
|
|
4560
4811
|
}
|
|
4561
4812
|
}
|
|
4562
4813
|
for (const { name, file } of namespaces) {
|
|
@@ -4621,7 +4872,7 @@ var ModuleResolver = class {
|
|
|
4621
4872
|
}
|
|
4622
4873
|
// ─── Lookup primitives ──────────────────────────────────────────────────────
|
|
4623
4874
|
lookup(candidate) {
|
|
4624
|
-
return this.byPath.get(
|
|
4875
|
+
return this.byPath.get(path5.posix.normalize(candidate).toLowerCase());
|
|
4625
4876
|
}
|
|
4626
4877
|
/**
|
|
4627
4878
|
* Try `base` verbatim, then `base` + each extension, then each directory
|
|
@@ -4631,7 +4882,7 @@ var ModuleResolver = class {
|
|
|
4631
4882
|
const direct = this.lookup(base);
|
|
4632
4883
|
if (direct) return direct;
|
|
4633
4884
|
const extensions = EXTENSIONS[family] ?? [];
|
|
4634
|
-
const suffix =
|
|
4885
|
+
const suffix = path5.posix.extname(base);
|
|
4635
4886
|
const stem = suffix && extensions.includes(suffix) ? base.slice(0, -suffix.length) : base;
|
|
4636
4887
|
for (const ext of extensions) {
|
|
4637
4888
|
const hit = this.lookup(`${stem}${ext}`);
|
|
@@ -4639,7 +4890,7 @@ var ModuleResolver = class {
|
|
|
4639
4890
|
}
|
|
4640
4891
|
for (const entry of DIRECTORY_ENTRIES[family] ?? []) {
|
|
4641
4892
|
for (const ext of extensions) {
|
|
4642
|
-
const hit = this.lookup(
|
|
4893
|
+
const hit = this.lookup(path5.posix.join(base, `${entry}${ext}`));
|
|
4643
4894
|
if (hit) return hit;
|
|
4644
4895
|
}
|
|
4645
4896
|
}
|
|
@@ -4656,14 +4907,14 @@ var ModuleResolver = class {
|
|
|
4656
4907
|
* member represents it.
|
|
4657
4908
|
*/
|
|
4658
4909
|
representativeIn(dir, family) {
|
|
4659
|
-
const members = this.byDir.get(
|
|
4910
|
+
const members = this.byDir.get(path5.posix.normalize(dir).toLowerCase());
|
|
4660
4911
|
if (!members?.length) return void 0;
|
|
4661
4912
|
const extensions = EXTENSIONS[family] ?? [];
|
|
4662
|
-
const eligible = members.filter((file) => extensions.includes(
|
|
4913
|
+
const eligible = members.filter((file) => extensions.includes(path5.posix.extname(toPortablePath(file)).toLowerCase())).sort((a, b) => toPortablePath(a).localeCompare(toPortablePath(b)));
|
|
4663
4914
|
if (eligible.length === 0) return void 0;
|
|
4664
|
-
const base =
|
|
4915
|
+
const base = path5.posix.basename(path5.posix.normalize(dir)).toLowerCase();
|
|
4665
4916
|
const named = eligible.find(
|
|
4666
|
-
(file) =>
|
|
4917
|
+
(file) => path5.posix.basename(toPortablePath(file)).split(".")[0]?.toLowerCase() === base
|
|
4667
4918
|
);
|
|
4668
4919
|
return named ?? eligible[0];
|
|
4669
4920
|
}
|
|
@@ -4671,7 +4922,7 @@ var ModuleResolver = class {
|
|
|
4671
4922
|
/** Relative specifiers, then workspace package names and their subpaths. */
|
|
4672
4923
|
resolveJs(fromFile, spec) {
|
|
4673
4924
|
if (spec.startsWith(".")) {
|
|
4674
|
-
const absolute =
|
|
4925
|
+
const absolute = path5.posix.join(path5.posix.dirname(fromFile), spec);
|
|
4675
4926
|
return this.lookupWithExtensions(absolute, "js");
|
|
4676
4927
|
}
|
|
4677
4928
|
const owner = this.structure.roots.find(
|
|
@@ -4680,9 +4931,9 @@ var ModuleResolver = class {
|
|
|
4680
4931
|
if (!owner?.importPath) return void 0;
|
|
4681
4932
|
const subpath = spec.slice(owner.importPath.length).replace(/^\//, "");
|
|
4682
4933
|
if (!subpath) {
|
|
4683
|
-
return this.lookupWithExtensions(
|
|
4934
|
+
return this.lookupWithExtensions(path5.posix.join(owner.dir, "src/index"), "js") ?? this.lookupWithExtensions(path5.posix.join(owner.dir, "index"), "js");
|
|
4684
4935
|
}
|
|
4685
|
-
return this.lookupWithExtensions(
|
|
4936
|
+
return this.lookupWithExtensions(path5.posix.join(owner.dir, subpath), "js") ?? this.lookupWithExtensions(path5.posix.join(owner.dir, "src", subpath), "js");
|
|
4686
4937
|
}
|
|
4687
4938
|
/** Go import paths are absolute module paths; a package is a directory. */
|
|
4688
4939
|
resolveGo(spec) {
|
|
@@ -4691,7 +4942,7 @@ var ModuleResolver = class {
|
|
|
4691
4942
|
);
|
|
4692
4943
|
if (!owner?.importPath) return void 0;
|
|
4693
4944
|
const subpath = spec.slice(owner.importPath.length).replace(/^\//, "");
|
|
4694
|
-
return this.representativeIn(
|
|
4945
|
+
return this.representativeIn(path5.posix.join(owner.dir, subpath), "go");
|
|
4695
4946
|
}
|
|
4696
4947
|
/**
|
|
4697
4948
|
* `import a.b.c` / `from a.b import c`, plus PEP 328 relative imports whose
|
|
@@ -4700,20 +4951,20 @@ var ModuleResolver = class {
|
|
|
4700
4951
|
resolvePython(fromFile, spec) {
|
|
4701
4952
|
const leadingDots = /^\.*/.exec(spec)?.[0].length ?? 0;
|
|
4702
4953
|
if (leadingDots > 0) {
|
|
4703
|
-
let base =
|
|
4704
|
-
for (let i = 1; i < leadingDots; i++) base =
|
|
4954
|
+
let base = path5.posix.dirname(fromFile);
|
|
4955
|
+
for (let i = 1; i < leadingDots; i++) base = path5.posix.dirname(base);
|
|
4705
4956
|
const rest = spec.slice(leadingDots).split(".").filter(Boolean);
|
|
4706
|
-
return this.lookupWithExtensions(
|
|
4957
|
+
return this.lookupWithExtensions(path5.posix.join(base, ...rest), "py");
|
|
4707
4958
|
}
|
|
4708
4959
|
const segments = spec.split(".").filter(Boolean);
|
|
4709
4960
|
if (segments.length === 0) return void 0;
|
|
4710
4961
|
const sourceRoots = this.structure.roots.filter((root) => root.kind === "python").flatMap((root) => root.sourceRoots);
|
|
4711
4962
|
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
4712
|
-
const hit = this.lookupWithExtensions(
|
|
4963
|
+
const hit = this.lookupWithExtensions(path5.posix.join(base, ...segments), "py");
|
|
4713
4964
|
if (hit) return hit;
|
|
4714
4965
|
if (segments.length > 1) {
|
|
4715
4966
|
const parent = this.lookupWithExtensions(
|
|
4716
|
-
|
|
4967
|
+
path5.posix.join(base, ...segments.slice(0, -1)),
|
|
4717
4968
|
"py"
|
|
4718
4969
|
);
|
|
4719
4970
|
if (parent) return parent;
|
|
@@ -4721,39 +4972,64 @@ var ModuleResolver = class {
|
|
|
4721
4972
|
}
|
|
4722
4973
|
return void 0;
|
|
4723
4974
|
}
|
|
4724
|
-
/**
|
|
4975
|
+
/**
|
|
4976
|
+
* `use crate::a::b`, `use super::x`, `use self::y`, `use other_crate::z`,
|
|
4977
|
+
* and `mod x;` declarations.
|
|
4978
|
+
*
|
|
4979
|
+
* Paths are resolved from the declaring file's MODULE directory, which is
|
|
4980
|
+
* only its filesystem directory for `mod.rs`/`lib.rs`/`main.rs`. A plain
|
|
4981
|
+
* `src/net.rs` owns `src/net/`: its `mod http;` is `src/net/http.rs`, its
|
|
4982
|
+
* `super` is the crate root. Using the file's directory put every
|
|
4983
|
+
* `self::`/`super::`/`mod` path from such a file one level too high, so
|
|
4984
|
+
* those edges silently resolved to nothing (or to an unrelated file).
|
|
4985
|
+
*/
|
|
4725
4986
|
resolveRust(fromFile, spec) {
|
|
4726
4987
|
const segments = spec.split("::").filter(Boolean);
|
|
4727
4988
|
if (segments.length === 0) return void 0;
|
|
4728
4989
|
const head = segments[0];
|
|
4729
4990
|
if (head === "self" || head === "super") {
|
|
4730
|
-
let base =
|
|
4991
|
+
let base = rustModuleDir(fromFile);
|
|
4992
|
+
let consumed = 0;
|
|
4731
4993
|
for (const segment of segments) {
|
|
4732
|
-
if (segment === "super") base =
|
|
4994
|
+
if (segment === "super") base = path5.posix.dirname(base);
|
|
4733
4995
|
else if (segment !== "self") break;
|
|
4996
|
+
consumed++;
|
|
4734
4997
|
}
|
|
4735
|
-
|
|
4736
|
-
return this.lookupWithExtensions(path4.posix.join(base, ...rest2), "rs");
|
|
4998
|
+
return this.lookupRustPath(base, segments.slice(consumed)) ?? this.lookupRustModuleFile(base);
|
|
4737
4999
|
}
|
|
4738
|
-
const
|
|
4739
|
-
const crate = head === "crate" ? owningCrate : this.structure.roots.find(
|
|
5000
|
+
const crate = head === "crate" ? findOwningRoot(this.structure, fromFile, ["cargo"]) : this.structure.roots.find(
|
|
4740
5001
|
(root) => root.kind === "cargo" && root.importPath === head?.replace(/-/g, "_")
|
|
4741
5002
|
);
|
|
4742
5003
|
if (!crate) {
|
|
4743
|
-
return this.
|
|
4744
|
-
path4.posix.join(path4.posix.dirname(fromFile), ...segments),
|
|
4745
|
-
"rs"
|
|
4746
|
-
);
|
|
5004
|
+
return this.lookupRustPath(rustModuleDir(fromFile), segments);
|
|
4747
5005
|
}
|
|
4748
5006
|
const rest = segments.slice(1);
|
|
4749
5007
|
for (const base of crate.sourceRoots) {
|
|
4750
|
-
const
|
|
4751
|
-
|
|
4752
|
-
|
|
5008
|
+
const hit = this.lookupRustPath(base, rest) ?? this.lookupRustCrateRoot(base);
|
|
5009
|
+
if (hit) return hit;
|
|
5010
|
+
}
|
|
5011
|
+
return void 0;
|
|
5012
|
+
}
|
|
5013
|
+
/**
|
|
5014
|
+
* Longest module prefix of `segments` under `base` that is an indexed file.
|
|
5015
|
+
* The trailing segments of a `use` are usually items (`net::http::Client::new`),
|
|
5016
|
+
* so each is dropped in turn until a module file matches.
|
|
5017
|
+
*/
|
|
5018
|
+
lookupRustPath(base, segments) {
|
|
5019
|
+
for (let end = segments.length; end > 0; end--) {
|
|
5020
|
+
const hit = this.lookupWithExtensions(path5.posix.join(base, ...segments.slice(0, end)), "rs");
|
|
4753
5021
|
if (hit) return hit;
|
|
4754
5022
|
}
|
|
4755
5023
|
return void 0;
|
|
4756
5024
|
}
|
|
5025
|
+
/** The file defining the module whose children live in `dir`. */
|
|
5026
|
+
lookupRustModuleFile(dir) {
|
|
5027
|
+
return this.lookupWithExtensions(dir, "rs") ?? this.lookupRustCrateRoot(dir);
|
|
5028
|
+
}
|
|
5029
|
+
/** A crate's root file: the library root, else the binary root. */
|
|
5030
|
+
lookupRustCrateRoot(sourceRoot) {
|
|
5031
|
+
return this.lookup(path5.posix.join(sourceRoot, "lib.rs")) ?? this.lookup(path5.posix.join(sourceRoot, "main.rs"));
|
|
5032
|
+
}
|
|
4757
5033
|
/** `com.example.Thing` and `com.example.*` against JVM source roots. */
|
|
4758
5034
|
resolveJvm(spec) {
|
|
4759
5035
|
const segments = spec.split(".").filter(Boolean);
|
|
@@ -4762,24 +5038,31 @@ var ModuleResolver = class {
|
|
|
4762
5038
|
const wildcard = segments[segments.length - 1] === "*";
|
|
4763
5039
|
const parts = wildcard ? segments.slice(0, -1) : segments;
|
|
4764
5040
|
for (const base of [...sourceRoots, this.structure.projectRoot]) {
|
|
4765
|
-
const target =
|
|
4766
|
-
|
|
4767
|
-
|
|
5041
|
+
const target = path5.posix.join(base, ...parts);
|
|
5042
|
+
if (wildcard) {
|
|
5043
|
+
const hit = this.representativeIn(target, "jvm") ?? this.lookupWithExtensions(target, "jvm");
|
|
5044
|
+
if (hit) return hit;
|
|
5045
|
+
continue;
|
|
5046
|
+
}
|
|
5047
|
+
for (let end = parts.length; end >= Math.max(2, parts.length - 2); end--) {
|
|
5048
|
+
const hit = this.lookupWithExtensions(path5.posix.join(base, ...parts.slice(0, end)), "jvm");
|
|
5049
|
+
if (hit) return hit;
|
|
5050
|
+
}
|
|
4768
5051
|
}
|
|
4769
5052
|
return void 0;
|
|
4770
5053
|
}
|
|
4771
5054
|
/** `#include "foo/bar.h"` — quoted form only; `<…>` is a system header. */
|
|
4772
5055
|
resolveInclude(fromFile, spec) {
|
|
4773
5056
|
const relative3 = this.lookupWithExtensions(
|
|
4774
|
-
|
|
5057
|
+
path5.posix.join(path5.posix.dirname(fromFile), spec),
|
|
4775
5058
|
"c"
|
|
4776
5059
|
);
|
|
4777
5060
|
if (relative3) return relative3;
|
|
4778
5061
|
for (const base of [
|
|
4779
|
-
|
|
5062
|
+
path5.posix.join(this.structure.projectRoot, "include"),
|
|
4780
5063
|
this.structure.projectRoot
|
|
4781
5064
|
]) {
|
|
4782
|
-
const hit = this.lookupWithExtensions(
|
|
5065
|
+
const hit = this.lookupWithExtensions(path5.posix.join(base, spec), "c");
|
|
4783
5066
|
if (hit) return hit;
|
|
4784
5067
|
}
|
|
4785
5068
|
return void 0;
|
|
@@ -4787,15 +5070,15 @@ var ModuleResolver = class {
|
|
|
4787
5070
|
/** `require_relative 'x'` is relative; `require 'x'` is looked up under lib/. */
|
|
4788
5071
|
resolveRuby(fromFile, spec) {
|
|
4789
5072
|
const relative3 = this.lookupWithExtensions(
|
|
4790
|
-
|
|
5073
|
+
path5.posix.join(path5.posix.dirname(fromFile), spec),
|
|
4791
5074
|
"ruby"
|
|
4792
5075
|
);
|
|
4793
5076
|
if (relative3) return relative3;
|
|
4794
5077
|
for (const base of [
|
|
4795
|
-
|
|
5078
|
+
path5.posix.join(this.structure.projectRoot, "lib"),
|
|
4796
5079
|
this.structure.projectRoot
|
|
4797
5080
|
]) {
|
|
4798
|
-
const hit = this.lookupWithExtensions(
|
|
5081
|
+
const hit = this.lookupWithExtensions(path5.posix.join(base, spec), "ruby");
|
|
4799
5082
|
if (hit) return hit;
|
|
4800
5083
|
}
|
|
4801
5084
|
return void 0;
|
|
@@ -4993,7 +5276,7 @@ var ParserWorkerPool = class {
|
|
|
4993
5276
|
*/
|
|
4994
5277
|
async reparseInline(batch, orphaned, workerId) {
|
|
4995
5278
|
try {
|
|
4996
|
-
const { parseFileContent } = await import("./parser-dispatch-
|
|
5279
|
+
const { parseFileContent } = await import("./parser-dispatch-ZSWCQB6M.js");
|
|
4997
5280
|
for (const item of orphaned) {
|
|
4998
5281
|
if (batch.settled) return;
|
|
4999
5282
|
try {
|
|
@@ -5098,15 +5381,15 @@ var IndexSourceChangedError = class extends Error {
|
|
|
5098
5381
|
name = "IndexSourceChangedError";
|
|
5099
5382
|
};
|
|
5100
5383
|
function isWithinProject(projectRoot, file) {
|
|
5101
|
-
const rel =
|
|
5102
|
-
return rel !== "" && !rel.startsWith(`..${
|
|
5384
|
+
const rel = path6.relative(projectRoot, file);
|
|
5385
|
+
return rel !== "" && !rel.startsWith(`..${path6.sep}`) && rel !== ".." && !path6.isAbsolute(rel);
|
|
5103
5386
|
}
|
|
5104
5387
|
function isMissingPathError(err) {
|
|
5105
5388
|
const code = err?.code;
|
|
5106
5389
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
5107
5390
|
}
|
|
5108
5391
|
function normalizeComparablePath(value) {
|
|
5109
|
-
const resolved =
|
|
5392
|
+
const resolved = path6.resolve(value);
|
|
5110
5393
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
5111
5394
|
}
|
|
5112
5395
|
function gitOutput(projectRoot, args) {
|
|
@@ -5152,24 +5435,24 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
5152
5435
|
const record = statusRecords[i];
|
|
5153
5436
|
if (!record) continue;
|
|
5154
5437
|
const status = record.slice(0, 2);
|
|
5155
|
-
const changedPath =
|
|
5438
|
+
const changedPath = path6.resolve(projectRoot, record.slice(3));
|
|
5156
5439
|
dirty.add(changedPath);
|
|
5157
5440
|
if (status.includes("D")) deleted.add(changedPath);
|
|
5158
5441
|
if (status.includes("R") || status.includes("C")) {
|
|
5159
5442
|
const source = statusRecords[++i];
|
|
5160
|
-
if (source) dirty.add(
|
|
5443
|
+
if (source) dirty.add(path6.resolve(projectRoot, source));
|
|
5161
5444
|
}
|
|
5162
5445
|
}
|
|
5163
5446
|
const files = [];
|
|
5164
5447
|
for (const relative3 of output.toString("utf8").split("\0")) {
|
|
5165
5448
|
if (!relative3) continue;
|
|
5166
5449
|
const portable = relative3.replace(/\\/g, "/");
|
|
5167
|
-
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(
|
|
5450
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path6.posix.basename(portable)) || isAtlasProjection(portable)) {
|
|
5168
5451
|
continue;
|
|
5169
5452
|
}
|
|
5170
|
-
const full =
|
|
5453
|
+
const full = path6.resolve(projectRoot, relative3);
|
|
5171
5454
|
if (deleted.has(full)) continue;
|
|
5172
|
-
const ext =
|
|
5455
|
+
const ext = path6.extname(relative3).toLowerCase();
|
|
5173
5456
|
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
5174
5457
|
}
|
|
5175
5458
|
const snapshot = createHash("sha256").update(stagedOutput).update("\0").update(statusOutput);
|
|
@@ -5188,6 +5471,57 @@ async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
|
5188
5471
|
return null;
|
|
5189
5472
|
}
|
|
5190
5473
|
}
|
|
5474
|
+
async function expandTargetedDirectories(store, projectRoot, targets, isGitIgnored, signal) {
|
|
5475
|
+
const isIndexableName = (file) => INDEXABLE_EXTENSION_SET.has(path6.extname(file).toLowerCase()) || detectLang(file) !== null;
|
|
5476
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
5477
|
+
let indexedFiles;
|
|
5478
|
+
for (const target of targets) {
|
|
5479
|
+
if (isIndexableName(target) || !isWithinProject(projectRoot, target)) {
|
|
5480
|
+
expanded.add(target);
|
|
5481
|
+
continue;
|
|
5482
|
+
}
|
|
5483
|
+
let stats;
|
|
5484
|
+
try {
|
|
5485
|
+
stats = await fs5.stat(target);
|
|
5486
|
+
} catch (err) {
|
|
5487
|
+
if (!isMissingPathError(err)) {
|
|
5488
|
+
expanded.add(target);
|
|
5489
|
+
continue;
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
if (stats && !stats.isDirectory()) {
|
|
5493
|
+
expanded.add(target);
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
indexedFiles ??= store.getAllFileMetas().map((meta) => meta.file);
|
|
5497
|
+
const prefix = normalizeComparablePath(target) + path6.sep;
|
|
5498
|
+
for (const file of indexedFiles) {
|
|
5499
|
+
if (normalizeComparablePath(file).startsWith(prefix)) expanded.add(file);
|
|
5500
|
+
}
|
|
5501
|
+
if (!stats) continue;
|
|
5502
|
+
const walk = async (dir) => {
|
|
5503
|
+
throwIfAborted(signal);
|
|
5504
|
+
let entries;
|
|
5505
|
+
try {
|
|
5506
|
+
entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
5507
|
+
} catch {
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
for (const entry of entries) {
|
|
5511
|
+
if (DEFAULT_IGNORE.includes(entry.name)) continue;
|
|
5512
|
+
const full = path6.join(dir, entry.name);
|
|
5513
|
+
const rel = path6.relative(projectRoot, full).replace(/\\/g, "/");
|
|
5514
|
+
if (entry.isDirectory()) {
|
|
5515
|
+
if (!isGitIgnored(rel, true)) await walk(full);
|
|
5516
|
+
} else if (entry.isFile() && isIndexableName(full)) {
|
|
5517
|
+
expanded.add(full);
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
};
|
|
5521
|
+
await walk(target);
|
|
5522
|
+
}
|
|
5523
|
+
return [...expanded];
|
|
5524
|
+
}
|
|
5191
5525
|
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
5192
5526
|
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
5193
5527
|
if (gitFiles) {
|
|
@@ -5222,15 +5556,15 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
|
5222
5556
|
dirCount++;
|
|
5223
5557
|
for (const e of entries) {
|
|
5224
5558
|
if (ignoreSet.has(e.name)) continue;
|
|
5225
|
-
const full =
|
|
5226
|
-
const rel =
|
|
5559
|
+
const full = path6.join(dir, e.name);
|
|
5560
|
+
const rel = path6.relative(projectRoot, full).replace(/\\/g, "/");
|
|
5227
5561
|
if (e.isDirectory()) {
|
|
5228
5562
|
if (isGitIgnored(rel, true)) continue;
|
|
5229
5563
|
await walk(full);
|
|
5230
5564
|
} else if (e.isFile()) {
|
|
5231
5565
|
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
5232
5566
|
if (isAtlasProjection(rel)) continue;
|
|
5233
|
-
const ext =
|
|
5567
|
+
const ext = path6.extname(e.name).toLowerCase();
|
|
5234
5568
|
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
5235
5569
|
results.push(full);
|
|
5236
5570
|
}
|
|
@@ -5269,13 +5603,15 @@ async function resolveProjectRelations(store, projectRoot, opts) {
|
|
|
5269
5603
|
if (opts.signal?.aborted) return;
|
|
5270
5604
|
store.setFilePackages(assignPackageLabels(structure, indexedFiles));
|
|
5271
5605
|
const resolver = new ModuleResolver(structure, indexedFiles, store.getNamespaceDeclarations());
|
|
5272
|
-
const
|
|
5606
|
+
const scope = opts.onlyFiles ? [.../* @__PURE__ */ new Set([...opts.onlyFiles, ...store.getFilesWithDanglingImports()])] : void 0;
|
|
5607
|
+
const pending = store.getUnresolvedImports(scope);
|
|
5273
5608
|
const resolutions = [];
|
|
5274
5609
|
for (const entry of pending) {
|
|
5275
5610
|
const toFile = resolver.resolve(entry.fromFile, entry.lang, entry.module);
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5611
|
+
resolutions.push({
|
|
5612
|
+
...entry,
|
|
5613
|
+
toFile: toFile && toFile !== entry.fromFile ? toFile : null
|
|
5614
|
+
});
|
|
5279
5615
|
}
|
|
5280
5616
|
if (opts.signal?.aborted) return;
|
|
5281
5617
|
store.applyImportResolutions(resolutions);
|
|
@@ -5309,7 +5645,8 @@ async function runIndexerAtomic(store, opts) {
|
|
|
5309
5645
|
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
5310
5646
|
const relationGraphVersion = "2";
|
|
5311
5647
|
const refResolutionVersion = "2";
|
|
5312
|
-
const
|
|
5648
|
+
const graphVersionStale = store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
5649
|
+
const force = (opts.force ?? false) || graphVersionStale;
|
|
5313
5650
|
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
5314
5651
|
const startMs = Date.now();
|
|
5315
5652
|
const errors = [];
|
|
@@ -5327,10 +5664,17 @@ async function runIndexerAtomic(store, opts) {
|
|
|
5327
5664
|
let trustedUnchanged;
|
|
5328
5665
|
let discoverySnapshotKey;
|
|
5329
5666
|
if (opts.files && opts.files.length > 0) {
|
|
5330
|
-
|
|
5667
|
+
const targeted = await expandTargetedDirectories(
|
|
5668
|
+
store,
|
|
5669
|
+
projectRoot,
|
|
5670
|
+
opts.files.map((f) => path6.resolve(projectRoot, f)),
|
|
5671
|
+
isGitIgnored,
|
|
5672
|
+
signal
|
|
5673
|
+
);
|
|
5674
|
+
files = targeted.filter((f) => {
|
|
5331
5675
|
if (!isWithinProject(projectRoot, f)) return false;
|
|
5332
|
-
const rel =
|
|
5333
|
-
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(
|
|
5676
|
+
const rel = path6.relative(projectRoot, f).replace(/\\/g, "/");
|
|
5677
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path6.basename(f)) && !isAtlasProjection(rel) && !isGitIgnored(rel, false);
|
|
5334
5678
|
});
|
|
5335
5679
|
} else {
|
|
5336
5680
|
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
@@ -5348,7 +5692,8 @@ async function runIndexerAtomic(store, opts) {
|
|
|
5348
5692
|
return lang ? langSet.has(lang) : false;
|
|
5349
5693
|
});
|
|
5350
5694
|
}
|
|
5351
|
-
|
|
5695
|
+
const scopedRun = (langs?.length ?? 0) > 0 || (opts.files?.length ?? 0) > 0;
|
|
5696
|
+
if (force && (graphVersionStale || !scopedRun)) store.clearAll();
|
|
5352
5697
|
const existingMeta = /* @__PURE__ */ new Map();
|
|
5353
5698
|
if (!force) {
|
|
5354
5699
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
@@ -5513,7 +5858,10 @@ async function runIndexerAtomic(store, opts) {
|
|
|
5513
5858
|
}
|
|
5514
5859
|
const result = settled.value;
|
|
5515
5860
|
if (result.error) {
|
|
5516
|
-
if (result.missing)
|
|
5861
|
+
if (result.missing) {
|
|
5862
|
+
store.deleteFile(file);
|
|
5863
|
+
continue;
|
|
5864
|
+
}
|
|
5517
5865
|
errors.push(`${file}: ${result.error}`);
|
|
5518
5866
|
filesFailed++;
|
|
5519
5867
|
continue;
|
|
@@ -5671,7 +6019,7 @@ async function runIndexerAtomic(store, opts) {
|
|
|
5671
6019
|
}
|
|
5672
6020
|
|
|
5673
6021
|
// src/codebase-index/index-service.ts
|
|
5674
|
-
import * as
|
|
6022
|
+
import * as path7 from "node:path";
|
|
5675
6023
|
async function indexService(args, hooks = {}) {
|
|
5676
6024
|
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5677
6025
|
try {
|
|
@@ -5725,7 +6073,7 @@ function contextService(args) {
|
|
|
5725
6073
|
pathPrefix: args.pathPrefix
|
|
5726
6074
|
},
|
|
5727
6075
|
(file) => {
|
|
5728
|
-
const relative3 =
|
|
6076
|
+
const relative3 = path7.relative(args.projectRoot, file);
|
|
5729
6077
|
if (!relative3 || relative3.startsWith("..")) return posixIndexPath(file);
|
|
5730
6078
|
return posixIndexPath(relative3);
|
|
5731
6079
|
}
|
|
@@ -5817,6 +6165,11 @@ export {
|
|
|
5817
6165
|
pageRank,
|
|
5818
6166
|
toSymbolRankRows,
|
|
5819
6167
|
aggregateFileRank,
|
|
6168
|
+
RANK_VERSION,
|
|
6169
|
+
RANK_VERSION_KEY,
|
|
6170
|
+
RANK_REFRESH_FILE_THRESHOLD,
|
|
6171
|
+
shouldRefreshRanks,
|
|
6172
|
+
runGraphRankPass,
|
|
5820
6173
|
getWiringSnapshot,
|
|
5821
6174
|
clearWiringSnapshot,
|
|
5822
6175
|
posixIndexPath,
|
|
@@ -5827,11 +6180,6 @@ export {
|
|
|
5827
6180
|
DEFAULT_SYMBOLS_PER_FILE,
|
|
5828
6181
|
retrieveContext,
|
|
5829
6182
|
xxhash64String,
|
|
5830
|
-
RANK_VERSION,
|
|
5831
|
-
RANK_VERSION_KEY,
|
|
5832
|
-
RANK_REFRESH_FILE_THRESHOLD,
|
|
5833
|
-
shouldRefreshRanks,
|
|
5834
|
-
runGraphRankPass,
|
|
5835
6183
|
tokenise,
|
|
5836
6184
|
buildIndexableText,
|
|
5837
6185
|
buildBm25Index,
|
|
@@ -5856,4 +6204,4 @@ export {
|
|
|
5856
6204
|
incomingCallsService,
|
|
5857
6205
|
outgoingCallsService
|
|
5858
6206
|
};
|
|
5859
|
-
//# sourceMappingURL=chunk-
|
|
6207
|
+
//# sourceMappingURL=chunk-6D3UIQMF.js.map
|