@wrongstack/tools 0.296.2 → 0.296.4
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/browser/artifacts.d.ts +2 -0
- package/dist/browser/artifacts.d.ts.map +1 -1
- package/dist/browser/index.js +41 -0
- package/dist/browser/index.js.map +2 -2
- package/dist/builtin.js +1812 -1426
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/dead-code-scan.d.ts +2 -2
- package/dist/codebase-index/dead-code-scan.d.ts.map +1 -1
- package/dist/codebase-index/index.d.ts +1 -0
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +1827 -1542
- package/dist/codebase-index/index.js.map +4 -4
- package/dist/codebase-index/project-server-client.d.ts +13 -1
- package/dist/codebase-index/project-server-client.d.ts.map +1 -1
- package/dist/codebase-index/project-server-endpoint.d.ts +29 -1
- package/dist/codebase-index/project-server-endpoint.d.ts.map +1 -1
- package/dist/codebase-index/project-server.js +226 -152
- package/dist/codebase-index/project-server.js.map +4 -4
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +214 -145
- package/dist/codebase-index/worker.js.map +3 -3
- package/dist/codebase-index/writer.d.ts +19 -1
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1851 -1462
- package/dist/index.js.map +4 -4
- package/dist/pack.js +1812 -1426
- package/dist/pack.js.map +4 -4
- package/dist/read.d.ts +31 -0
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +5465 -78
- package/dist/read.js.map +4 -4
- package/dist/tool-tier.js +1812 -1426
- package/dist/tool-tier.js.map +4 -4
- package/package.json +3 -3
|
@@ -14,16 +14,16 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// src/codebase-index/languages.ts
|
|
17
|
-
import * as
|
|
17
|
+
import * as path6 from "node:path";
|
|
18
18
|
function detectLang(file) {
|
|
19
|
-
const base =
|
|
19
|
+
const base = path6.basename(file);
|
|
20
20
|
const lowerBase = base.toLowerCase();
|
|
21
21
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
22
22
|
return "ts";
|
|
23
23
|
}
|
|
24
24
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
25
25
|
if (special) return special;
|
|
26
|
-
const ext =
|
|
26
|
+
const ext = path6.extname(base).toLowerCase();
|
|
27
27
|
if (!ext) return null;
|
|
28
28
|
return EXT_TO_LANG[ext] ?? null;
|
|
29
29
|
}
|
|
@@ -255,11 +255,18 @@ async function parseSymbols(opts) {
|
|
|
255
255
|
} else if (ts.isHeritageClause(node)) {
|
|
256
256
|
for (const t of node.types) {
|
|
257
257
|
const name = getTypeName(t.expression);
|
|
258
|
-
if (name)
|
|
258
|
+
if (name)
|
|
259
|
+
refs.push({
|
|
260
|
+
fromId: 0,
|
|
261
|
+
toName: name,
|
|
262
|
+
callType: node.token === ts.SyntaxKind.ExtendsKeyword ? "inherit" : "implement",
|
|
263
|
+
line: lineNum
|
|
264
|
+
});
|
|
259
265
|
}
|
|
260
266
|
} else if (ts.isImportDeclaration(node)) {
|
|
261
|
-
|
|
262
|
-
|
|
267
|
+
emitImportSpecifierRefs(node, refs, lineNum);
|
|
268
|
+
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
|
|
269
|
+
emitExportSpecifierRefs(node, refs, lineNum);
|
|
263
270
|
}
|
|
264
271
|
const scopeIdx = scopeParts.length;
|
|
265
272
|
pushScopeName(node, scopeParts);
|
|
@@ -275,11 +282,6 @@ function getTypeName(name) {
|
|
|
275
282
|
if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;
|
|
276
283
|
return "";
|
|
277
284
|
}
|
|
278
|
-
function getModuleName(node) {
|
|
279
|
-
const moduleSpecifier = node.moduleSpecifier;
|
|
280
|
-
if (ts.isStringLiteral(moduleSpecifier)) return moduleSpecifier.text;
|
|
281
|
-
return "";
|
|
282
|
-
}
|
|
283
285
|
function deduplicateRefs(refs) {
|
|
284
286
|
const seen = /* @__PURE__ */ new Set();
|
|
285
287
|
return refs.filter((r) => {
|
|
@@ -289,6 +291,44 @@ function deduplicateRefs(refs) {
|
|
|
289
291
|
return true;
|
|
290
292
|
});
|
|
291
293
|
}
|
|
294
|
+
function getImportSpecifierName(spec) {
|
|
295
|
+
return spec.propertyName?.text ?? spec.name.text;
|
|
296
|
+
}
|
|
297
|
+
function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
298
|
+
const clause = node.importClause;
|
|
299
|
+
if (!clause) return;
|
|
300
|
+
if (clause.name) {
|
|
301
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
302
|
+
}
|
|
303
|
+
const bindings = clause.namedBindings;
|
|
304
|
+
if (!bindings) return;
|
|
305
|
+
if (ts.isNamedImports(bindings)) {
|
|
306
|
+
for (const element of bindings.elements) {
|
|
307
|
+
refs.push({
|
|
308
|
+
fromId: 0,
|
|
309
|
+
toName: getImportSpecifierName(element),
|
|
310
|
+
callType: "import",
|
|
311
|
+
line: lineNum
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
} else if (ts.isNamespaceImport(bindings)) {
|
|
315
|
+
refs.push({ fromId: 0, toName: bindings.name.text, callType: "import", line: lineNum });
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function emitExportSpecifierRefs(node, refs, lineNum) {
|
|
319
|
+
const clause = node.exportClause;
|
|
320
|
+
if (clause && ts.isNamespaceExport(clause)) {
|
|
321
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (clause && ts.isNamedExports(clause)) {
|
|
325
|
+
for (const element of clause.elements) {
|
|
326
|
+
const originalName = element.propertyName?.text ?? element.name.text;
|
|
327
|
+
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum });
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
292
332
|
var ts, tsLoad, kindMapCache;
|
|
293
333
|
var init_ts_parser = __esm({
|
|
294
334
|
"src/codebase-index/ts-parser.ts"() {
|
|
@@ -300,21 +340,21 @@ var init_ts_parser = __esm({
|
|
|
300
340
|
});
|
|
301
341
|
|
|
302
342
|
// src/_win32-resolve.ts
|
|
303
|
-
import * as
|
|
304
|
-
import * as
|
|
343
|
+
import * as fs6 from "node:fs";
|
|
344
|
+
import * as path7 from "node:path";
|
|
305
345
|
function resolveWin32Command(cmd) {
|
|
306
346
|
if (process.platform !== "win32") return cmd;
|
|
307
|
-
if (cmd.includes("/") || cmd.includes("\\") ||
|
|
347
|
+
if (cmd.includes("/") || cmd.includes("\\") || path7.extname(cmd.replace(/\//g, "\\"))) {
|
|
308
348
|
return cmd;
|
|
309
349
|
}
|
|
310
350
|
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
311
|
-
const pathDirs = (process.env["PATH"] ?? "").split(
|
|
351
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path7.delimiter);
|
|
312
352
|
for (const dir of pathDirs) {
|
|
313
|
-
const base =
|
|
353
|
+
const base = path7.join(dir, cmd);
|
|
314
354
|
for (const ext of pathext) {
|
|
315
355
|
const full = `${base}${ext}`;
|
|
316
356
|
try {
|
|
317
|
-
|
|
357
|
+
fs6.accessSync(full, fs6.constants.X_OK);
|
|
318
358
|
return full;
|
|
319
359
|
} catch {
|
|
320
360
|
}
|
|
@@ -351,10 +391,10 @@ __export(go_parser_exports, {
|
|
|
351
391
|
detectLang: () => detectLang,
|
|
352
392
|
parseSymbols: () => parseSymbols2
|
|
353
393
|
});
|
|
354
|
-
import { spawn } from "node:child_process";
|
|
355
|
-
import * as
|
|
356
|
-
import * as
|
|
357
|
-
import * as
|
|
394
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
395
|
+
import * as os2 from "node:os";
|
|
396
|
+
import * as path8 from "node:path";
|
|
397
|
+
import * as fs7 from "node:fs/promises";
|
|
358
398
|
async function parseSymbols2(opts) {
|
|
359
399
|
const { file, content, lang } = opts;
|
|
360
400
|
try {
|
|
@@ -426,16 +466,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
426
466
|
try {
|
|
427
467
|
let scriptPath = _cachedGoScriptPath;
|
|
428
468
|
if (!scriptPath) {
|
|
429
|
-
const tmpDir = await
|
|
430
|
-
scriptPath =
|
|
431
|
-
await
|
|
469
|
+
const tmpDir = await fs7.mkdtemp(path8.join(os2.tmpdir(), "ws-go-parse-"));
|
|
470
|
+
scriptPath = path8.join(tmpDir, "parse.go");
|
|
471
|
+
await fs7.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
432
472
|
_cachedGoScriptPath = scriptPath;
|
|
433
473
|
}
|
|
434
474
|
const goBinary = resolveWin32Command("go");
|
|
435
475
|
const goResult = await new Promise(
|
|
436
476
|
(resolve4, reject) => {
|
|
437
477
|
let settled = false;
|
|
438
|
-
const proc =
|
|
478
|
+
const proc = spawn2(goBinary, ["run", scriptPath], {
|
|
439
479
|
stdio: ["pipe", "pipe", "pipe"],
|
|
440
480
|
windowsHide: true
|
|
441
481
|
});
|
|
@@ -1038,10 +1078,10 @@ __export(py_parser_exports, {
|
|
|
1038
1078
|
detectLang: () => detectLang,
|
|
1039
1079
|
parseSymbols: () => parseSymbols4
|
|
1040
1080
|
});
|
|
1041
|
-
import { spawn as
|
|
1042
|
-
import * as
|
|
1043
|
-
import * as
|
|
1044
|
-
import * as
|
|
1081
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1082
|
+
import * as fs8 from "node:fs/promises";
|
|
1083
|
+
import * as os3 from "node:os";
|
|
1084
|
+
import * as path9 from "node:path";
|
|
1045
1085
|
async function parseSymbols4(opts) {
|
|
1046
1086
|
const { file, content, lang } = opts;
|
|
1047
1087
|
try {
|
|
@@ -1063,7 +1103,7 @@ async function resolvePython() {
|
|
|
1063
1103
|
function commandIsAvailable(command) {
|
|
1064
1104
|
return new Promise((resolve4) => {
|
|
1065
1105
|
let settled = false;
|
|
1066
|
-
const proc =
|
|
1106
|
+
const proc = spawn3(command, ["--version"], {
|
|
1067
1107
|
stdio: "ignore",
|
|
1068
1108
|
windowsHide: true
|
|
1069
1109
|
});
|
|
@@ -1085,7 +1125,7 @@ function commandIsAvailable(command) {
|
|
|
1085
1125
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
1086
1126
|
return new Promise((resolve4, reject) => {
|
|
1087
1127
|
let settled = false;
|
|
1088
|
-
const proc =
|
|
1128
|
+
const proc = spawn3(pyBinary, [scriptPath, filePath], {
|
|
1089
1129
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1090
1130
|
windowsHide: true
|
|
1091
1131
|
});
|
|
@@ -1119,10 +1159,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
1119
1159
|
async function syncPyParse(filePath, content, lang) {
|
|
1120
1160
|
try {
|
|
1121
1161
|
if (!_cachedScriptPath) {
|
|
1122
|
-
const tmpDir =
|
|
1123
|
-
await
|
|
1124
|
-
_cachedScriptPath =
|
|
1125
|
-
await
|
|
1162
|
+
const tmpDir = path9.join(os3.tmpdir(), "ws-py-parse");
|
|
1163
|
+
await fs8.mkdir(tmpDir, { recursive: true });
|
|
1164
|
+
_cachedScriptPath = path9.join(tmpDir, "parse.py");
|
|
1165
|
+
await fs8.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
1126
1166
|
}
|
|
1127
1167
|
cachedPyBinary ??= resolvePython();
|
|
1128
1168
|
const pyBinary = await cachedPyBinary;
|
|
@@ -1376,10 +1416,10 @@ __export(rs_parser_exports, {
|
|
|
1376
1416
|
detectLang: () => detectLang,
|
|
1377
1417
|
parseSymbols: () => parseSymbols5
|
|
1378
1418
|
});
|
|
1379
|
-
import { expectDefined } from "@wrongstack/core/utils";
|
|
1380
|
-
import { execFile, spawn as
|
|
1381
|
-
import * as
|
|
1382
|
-
import * as
|
|
1419
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
1420
|
+
import { execFile, spawn as spawn4 } from "node:child_process";
|
|
1421
|
+
import * as fs9 from "node:fs/promises";
|
|
1422
|
+
import * as path10 from "node:path";
|
|
1383
1423
|
async function parseSymbols5(opts) {
|
|
1384
1424
|
const { file, content, lang } = opts;
|
|
1385
1425
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -1401,7 +1441,7 @@ function checkNativeParser() {
|
|
|
1401
1441
|
nativeParserAvailability ??= (async () => {
|
|
1402
1442
|
try {
|
|
1403
1443
|
await probe("rustc", ["--version"]);
|
|
1404
|
-
const toolsDir =
|
|
1444
|
+
const toolsDir = path10.join(process.cwd(), "tools");
|
|
1405
1445
|
await probe(
|
|
1406
1446
|
"cargo",
|
|
1407
1447
|
[
|
|
@@ -1410,7 +1450,7 @@ function checkNativeParser() {
|
|
|
1410
1450
|
"--format-version",
|
|
1411
1451
|
"1",
|
|
1412
1452
|
"--manifest-path",
|
|
1413
|
-
|
|
1453
|
+
path10.join(toolsDir, "Cargo.toml")
|
|
1414
1454
|
]
|
|
1415
1455
|
);
|
|
1416
1456
|
return true;
|
|
@@ -1422,17 +1462,17 @@ function checkNativeParser() {
|
|
|
1422
1462
|
}
|
|
1423
1463
|
async function tryNativeParse(file, content) {
|
|
1424
1464
|
try {
|
|
1425
|
-
const toolsDir =
|
|
1426
|
-
const crateDir =
|
|
1427
|
-
const tmpFile =
|
|
1428
|
-
await
|
|
1465
|
+
const toolsDir = path10.join(process.cwd(), "tools");
|
|
1466
|
+
const crateDir = path10.join(toolsDir, "syn-parser");
|
|
1467
|
+
const tmpFile = path10.join(crateDir, "src", "input.rs");
|
|
1468
|
+
await fs9.writeFile(tmpFile, content, "utf8");
|
|
1429
1469
|
const cargoBinary = resolveWin32Command("cargo");
|
|
1430
1470
|
const result = await new Promise(
|
|
1431
1471
|
(resolve4, reject) => {
|
|
1432
1472
|
let settled = false;
|
|
1433
|
-
const proc =
|
|
1473
|
+
const proc = spawn4(
|
|
1434
1474
|
cargoBinary,
|
|
1435
|
-
["run", "--manifest-path",
|
|
1475
|
+
["run", "--manifest-path", path10.join(toolsDir, "Cargo.toml")],
|
|
1436
1476
|
{
|
|
1437
1477
|
cwd: process.cwd(),
|
|
1438
1478
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1491,7 +1531,7 @@ function regexParse(opts) {
|
|
|
1491
1531
|
let hi = lineOffsets.length - 1;
|
|
1492
1532
|
while (lo < hi) {
|
|
1493
1533
|
const mid = lo + hi + 1 >>> 1;
|
|
1494
|
-
if (
|
|
1534
|
+
if (expectDefined2(lineOffsets[mid]) <= offset) lo = mid;
|
|
1495
1535
|
else hi = mid - 1;
|
|
1496
1536
|
}
|
|
1497
1537
|
return lo + 1;
|
|
@@ -1503,7 +1543,7 @@ function regexParse(opts) {
|
|
|
1503
1543
|
for (const pattern of RS_PATTERNS) {
|
|
1504
1544
|
pattern.regex.lastIndex = 0;
|
|
1505
1545
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
1506
|
-
const name =
|
|
1546
|
+
const name = expectDefined2(match[1]);
|
|
1507
1547
|
const offset = match.index ?? 0;
|
|
1508
1548
|
const line = lineFromOffset(offset);
|
|
1509
1549
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1560,8 +1600,8 @@ __export(json_parser_exports, {
|
|
|
1560
1600
|
detectLang: () => detectLang,
|
|
1561
1601
|
parseSymbols: () => parseSymbols6
|
|
1562
1602
|
});
|
|
1563
|
-
import { expectDefined as
|
|
1564
|
-
import * as
|
|
1603
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
1604
|
+
import * as path11 from "node:path";
|
|
1565
1605
|
function parseSymbols6(opts) {
|
|
1566
1606
|
const { file, content, lang } = opts;
|
|
1567
1607
|
try {
|
|
@@ -1573,7 +1613,7 @@ function parseSymbols6(opts) {
|
|
|
1573
1613
|
function regexParse2(opts) {
|
|
1574
1614
|
const { file, content, lang } = opts;
|
|
1575
1615
|
const symbols = [];
|
|
1576
|
-
const basename5 =
|
|
1616
|
+
const basename5 = path11.basename(file).toLowerCase();
|
|
1577
1617
|
const isPackageJson = basename5 === "package.json";
|
|
1578
1618
|
const isTsconfig = basename5 === "tsconfig.json" || basename5 === "tsconfig.build.json";
|
|
1579
1619
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -1588,22 +1628,22 @@ function regexParse2(opts) {
|
|
|
1588
1628
|
let hi = lineOffsets.length - 1;
|
|
1589
1629
|
while (lo < hi) {
|
|
1590
1630
|
const mid = lo + hi + 1 >>> 1;
|
|
1591
|
-
if (
|
|
1631
|
+
if (expectDefined3(lineOffsets[mid]) <= offset) lo = mid;
|
|
1592
1632
|
else hi = mid - 1;
|
|
1593
1633
|
}
|
|
1594
1634
|
return lo + 1;
|
|
1595
1635
|
}
|
|
1596
1636
|
const rootMatch = content.match(/^\s*\{/m);
|
|
1597
1637
|
if (rootMatch) {
|
|
1598
|
-
const offset =
|
|
1638
|
+
const offset = expectDefined3(rootMatch.index);
|
|
1599
1639
|
const line = lineFromOffset(offset);
|
|
1600
1640
|
symbols.push(
|
|
1601
1641
|
makeSymbol({
|
|
1602
|
-
name:
|
|
1642
|
+
name: path11.basename(file),
|
|
1603
1643
|
kind: "object",
|
|
1604
1644
|
line,
|
|
1605
1645
|
col: 0,
|
|
1606
|
-
signature: `"${
|
|
1646
|
+
signature: `"${path11.basename(file)}" = { ... }`,
|
|
1607
1647
|
file,
|
|
1608
1648
|
lang
|
|
1609
1649
|
})
|
|
@@ -1611,7 +1651,7 @@ function regexParse2(opts) {
|
|
|
1611
1651
|
}
|
|
1612
1652
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
1613
1653
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
1614
|
-
const key =
|
|
1654
|
+
const key = expectDefined3(match[1]);
|
|
1615
1655
|
const offset = match.index ?? 0;
|
|
1616
1656
|
const line = lineFromOffset(offset);
|
|
1617
1657
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1658,7 +1698,7 @@ function regexParse2(opts) {
|
|
|
1658
1698
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
1659
1699
|
const defsMatch = defsRegex.exec(content);
|
|
1660
1700
|
if (defsMatch !== null) {
|
|
1661
|
-
const offset =
|
|
1701
|
+
const offset = expectDefined3(defsMatch.index);
|
|
1662
1702
|
const line = lineFromOffset(offset);
|
|
1663
1703
|
symbols.push(
|
|
1664
1704
|
makeSymbol({
|
|
@@ -1683,7 +1723,7 @@ function regexParse2(opts) {
|
|
|
1683
1723
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
1684
1724
|
const offset = match.index ?? 0;
|
|
1685
1725
|
const line = lineFromOffset(offset);
|
|
1686
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
1726
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined3(match[0]);
|
|
1687
1727
|
symbols.push(
|
|
1688
1728
|
makeSymbol({
|
|
1689
1729
|
name: key,
|
|
@@ -1702,12 +1742,12 @@ function regexParse2(opts) {
|
|
|
1702
1742
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
1703
1743
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
1704
1744
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
1705
|
-
const blockContent =
|
|
1745
|
+
const blockContent = expectDefined3(match[0]);
|
|
1706
1746
|
const blockOffset = match.index ?? 0;
|
|
1707
1747
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
1708
1748
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
1709
|
-
const key =
|
|
1710
|
-
const keyOffset = blockOffset +
|
|
1749
|
+
const key = expectDefined3(scriptMatch[1]);
|
|
1750
|
+
const keyOffset = blockOffset + expectDefined3(scriptMatch.index);
|
|
1711
1751
|
const line = lineFromOffset(keyOffset);
|
|
1712
1752
|
symbols.push(
|
|
1713
1753
|
makeSymbol({
|
|
@@ -1726,12 +1766,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
|
|
|
1726
1766
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
1727
1767
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
1728
1768
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
1729
|
-
const blockContent =
|
|
1769
|
+
const blockContent = expectDefined3(match[0]);
|
|
1730
1770
|
const blockOffset = match.index ?? 0;
|
|
1731
1771
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
1732
1772
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
1733
|
-
const key =
|
|
1734
|
-
const keyOffset = blockOffset +
|
|
1773
|
+
const key = expectDefined3(optMatch[1]);
|
|
1774
|
+
const keyOffset = blockOffset + expectDefined3(optMatch.index);
|
|
1735
1775
|
const line = lineFromOffset(keyOffset);
|
|
1736
1776
|
if (line <= parentLine) continue;
|
|
1737
1777
|
symbols.push(
|
|
@@ -1776,7 +1816,7 @@ __export(yaml_parser_exports, {
|
|
|
1776
1816
|
detectLang: () => detectLang,
|
|
1777
1817
|
parseSymbols: () => parseSymbols7
|
|
1778
1818
|
});
|
|
1779
|
-
import { expectDefined as
|
|
1819
|
+
import { expectDefined as expectDefined4, truncate } from "@wrongstack/core/utils";
|
|
1780
1820
|
function parseSymbols7(opts) {
|
|
1781
1821
|
const { file, content, lang } = opts;
|
|
1782
1822
|
try {
|
|
@@ -1798,14 +1838,14 @@ function regexParse3(opts) {
|
|
|
1798
1838
|
let hi = lineOffsets.length - 1;
|
|
1799
1839
|
while (lo < hi) {
|
|
1800
1840
|
const mid = lo + hi + 1 >>> 1;
|
|
1801
|
-
if (
|
|
1841
|
+
if (expectDefined4(lineOffsets[mid]) <= offset) lo = mid;
|
|
1802
1842
|
else hi = mid - 1;
|
|
1803
1843
|
}
|
|
1804
1844
|
return lo + 1;
|
|
1805
1845
|
}
|
|
1806
1846
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
1807
1847
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
1808
|
-
const name =
|
|
1848
|
+
const name = expectDefined4(match[1]);
|
|
1809
1849
|
const offset = match.index ?? 0;
|
|
1810
1850
|
const line = lineFromOffset(offset);
|
|
1811
1851
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1823,7 +1863,7 @@ function regexParse3(opts) {
|
|
|
1823
1863
|
}
|
|
1824
1864
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
1825
1865
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
1826
|
-
const name =
|
|
1866
|
+
const name = expectDefined4(match[1]);
|
|
1827
1867
|
const offset = match.index ?? 0;
|
|
1828
1868
|
const line = lineFromOffset(offset);
|
|
1829
1869
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1858,7 +1898,7 @@ function regexParse3(opts) {
|
|
|
1858
1898
|
}
|
|
1859
1899
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
1860
1900
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
1861
|
-
const key =
|
|
1901
|
+
const key = expectDefined4(match[2]);
|
|
1862
1902
|
const offset = match.index ?? 0;
|
|
1863
1903
|
const line = lineFromOffset(offset);
|
|
1864
1904
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1878,7 +1918,7 @@ function regexParse3(opts) {
|
|
|
1878
1918
|
}
|
|
1879
1919
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
1880
1920
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
1881
|
-
const key =
|
|
1921
|
+
const key = expectDefined4(match[2]);
|
|
1882
1922
|
const offset = match.index ?? 0;
|
|
1883
1923
|
const line = lineFromOffset(offset);
|
|
1884
1924
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1930,10 +1970,12 @@ var init_yaml_parser = __esm({
|
|
|
1930
1970
|
}
|
|
1931
1971
|
});
|
|
1932
1972
|
|
|
1933
|
-
// src/codebase-index/
|
|
1934
|
-
import
|
|
1935
|
-
import
|
|
1936
|
-
import
|
|
1973
|
+
// src/codebase-index/project-server-client.ts
|
|
1974
|
+
import { spawn } from "node:child_process";
|
|
1975
|
+
import * as fs4 from "node:fs";
|
|
1976
|
+
import * as net from "node:net";
|
|
1977
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1978
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
1937
1979
|
|
|
1938
1980
|
// src/codebase-index/circuit-breaker.ts
|
|
1939
1981
|
var CircuitOpenError = class extends Error {
|
|
@@ -2018,182 +2060,18 @@ function resetIndexCircuitBreaker() {
|
|
|
2018
2060
|
indexCircuitBreaker.reset();
|
|
2019
2061
|
}
|
|
2020
2062
|
|
|
2021
|
-
// src/codebase-index/
|
|
2022
|
-
import {
|
|
2023
|
-
import
|
|
2024
|
-
import * as
|
|
2025
|
-
import
|
|
2026
|
-
import
|
|
2027
|
-
import {
|
|
2028
|
-
DEFAULT_WALK_IGNORE_DIRS,
|
|
2029
|
-
indexParallelBatchSize,
|
|
2030
|
-
isFrugalPerf
|
|
2031
|
-
} from "@wrongstack/core/utils";
|
|
2032
|
-
|
|
2033
|
-
// src/codebase-index/gitignore.ts
|
|
2034
|
-
import * as fs from "node:fs/promises";
|
|
2035
|
-
import * as path from "node:path";
|
|
2036
|
-
import { compileGlob } from "@wrongstack/core/utils";
|
|
2037
|
-
function globBody(glob) {
|
|
2038
|
-
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
2039
|
-
}
|
|
2040
|
-
function compileGitignore(lines) {
|
|
2041
|
-
const rules = [];
|
|
2042
|
-
for (const raw of lines) {
|
|
2043
|
-
let line = raw.replace(/\r$/, "");
|
|
2044
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
2045
|
-
line = line.trim();
|
|
2046
|
-
let negated = false;
|
|
2047
|
-
if (line.startsWith("!")) {
|
|
2048
|
-
negated = true;
|
|
2049
|
-
line = line.slice(1);
|
|
2050
|
-
}
|
|
2051
|
-
let dirOnly = false;
|
|
2052
|
-
if (line.endsWith("/")) {
|
|
2053
|
-
dirOnly = true;
|
|
2054
|
-
line = line.slice(0, -1);
|
|
2055
|
-
}
|
|
2056
|
-
if (!line) continue;
|
|
2057
|
-
const anchored = line.startsWith("/") || line.includes("/");
|
|
2058
|
-
if (line.startsWith("/")) line = line.slice(1);
|
|
2059
|
-
const body = globBody(line);
|
|
2060
|
-
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
2061
|
-
rules.push({
|
|
2062
|
-
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
2063
|
-
under: new RegExp(`${prefix}${body}/.*$`),
|
|
2064
|
-
negated,
|
|
2065
|
-
dirOnly
|
|
2066
|
-
});
|
|
2067
|
-
}
|
|
2068
|
-
return (relPath, isDir) => {
|
|
2069
|
-
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2070
|
-
let ignored = false;
|
|
2071
|
-
for (const r of rules) {
|
|
2072
|
-
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
2073
|
-
if (re.test(p)) ignored = !r.negated;
|
|
2074
|
-
}
|
|
2075
|
-
return ignored;
|
|
2076
|
-
};
|
|
2077
|
-
}
|
|
2078
|
-
async function loadGitignoreMatcher(projectRoot) {
|
|
2079
|
-
let lines = [];
|
|
2080
|
-
try {
|
|
2081
|
-
const raw = await fs.readFile(path.join(projectRoot, ".gitignore"), "utf8");
|
|
2082
|
-
lines = raw.split("\n");
|
|
2083
|
-
} catch {
|
|
2084
|
-
}
|
|
2085
|
-
return compileGitignore(lines);
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
// src/codebase-index/indexer.ts
|
|
2089
|
-
init_languages();
|
|
2090
|
-
|
|
2091
|
-
// src/codebase-index/parser-dispatch.ts
|
|
2092
|
-
async function parseFileContent(file, content, lang) {
|
|
2093
|
-
switch (lang) {
|
|
2094
|
-
case "ts":
|
|
2095
|
-
case "tsx":
|
|
2096
|
-
case "js":
|
|
2097
|
-
case "jsx": {
|
|
2098
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
2099
|
-
return parseSymbols8({ file, content, lang });
|
|
2100
|
-
}
|
|
2101
|
-
case "go": {
|
|
2102
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
2103
|
-
return parseSymbols8({ file, content, lang: "go" });
|
|
2104
|
-
}
|
|
2105
|
-
case "py": {
|
|
2106
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
2107
|
-
return parseSymbols8({ file, content, lang: "py" });
|
|
2108
|
-
}
|
|
2109
|
-
case "rs": {
|
|
2110
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
2111
|
-
return parseSymbols8({ file, content, lang: "rs" });
|
|
2112
|
-
}
|
|
2113
|
-
case "json": {
|
|
2114
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
2115
|
-
return parseSymbols8({ file, content, lang: "json" });
|
|
2116
|
-
}
|
|
2117
|
-
case "yaml": {
|
|
2118
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
2119
|
-
return parseSymbols8({ file, content, lang: "yaml" });
|
|
2120
|
-
}
|
|
2121
|
-
default: {
|
|
2122
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
2123
|
-
return parseSymbols8({ file, content, lang });
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2063
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
2064
|
+
import { createHash } from "node:crypto";
|
|
2065
|
+
import * as fs3 from "node:fs";
|
|
2066
|
+
import * as os from "node:os";
|
|
2067
|
+
import * as path4 from "node:path";
|
|
2068
|
+
import { fileURLToPath } from "node:url";
|
|
2069
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
2127
2070
|
|
|
2128
2071
|
// src/codebase-index/writer.ts
|
|
2129
|
-
import { expectDefined
|
|
2130
|
-
import * as
|
|
2131
|
-
import * as
|
|
2132
|
-
|
|
2133
|
-
// src/codebase-index/schema.ts
|
|
2134
|
-
var SCHEMA_VERSION = 3;
|
|
2135
|
-
|
|
2136
|
-
// src/codebase-index/lsp-kind.ts
|
|
2137
|
-
function lspKindToInternalKind(k) {
|
|
2138
|
-
switch (k) {
|
|
2139
|
-
case 5 /* Class */:
|
|
2140
|
-
return "class";
|
|
2141
|
-
case 6 /* Method */:
|
|
2142
|
-
return "method";
|
|
2143
|
-
case 7 /* Property */:
|
|
2144
|
-
case 8 /* Field */:
|
|
2145
|
-
return "property";
|
|
2146
|
-
case 9 /* Constructor */:
|
|
2147
|
-
return "class";
|
|
2148
|
-
case 10 /* Enum */:
|
|
2149
|
-
return "enum";
|
|
2150
|
-
case 11 /* Interface */:
|
|
2151
|
-
return "interface";
|
|
2152
|
-
case 12 /* Function */:
|
|
2153
|
-
return "function";
|
|
2154
|
-
case 13 /* Variable */:
|
|
2155
|
-
return "var";
|
|
2156
|
-
case 14 /* Constant */:
|
|
2157
|
-
return "const";
|
|
2158
|
-
case 22 /* EnumMember */:
|
|
2159
|
-
return "enum";
|
|
2160
|
-
case 26 /* TypeParameter */:
|
|
2161
|
-
return "type";
|
|
2162
|
-
case 3 /* Namespace */:
|
|
2163
|
-
return "namespace";
|
|
2164
|
-
default:
|
|
2165
|
-
return null;
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
function internalKindToLspKind(k) {
|
|
2169
|
-
switch (k) {
|
|
2170
|
-
case "class":
|
|
2171
|
-
return 5 /* Class */;
|
|
2172
|
-
case "method":
|
|
2173
|
-
return 6 /* Method */;
|
|
2174
|
-
case "property":
|
|
2175
|
-
return 7 /* Property */;
|
|
2176
|
-
case "function":
|
|
2177
|
-
return 12 /* Function */;
|
|
2178
|
-
case "var":
|
|
2179
|
-
return 13 /* Variable */;
|
|
2180
|
-
case "const":
|
|
2181
|
-
return 14 /* Constant */;
|
|
2182
|
-
case "let":
|
|
2183
|
-
return 13 /* Variable */;
|
|
2184
|
-
case "enum":
|
|
2185
|
-
return 10 /* Enum */;
|
|
2186
|
-
case "interface":
|
|
2187
|
-
return 11 /* Interface */;
|
|
2188
|
-
case "namespace":
|
|
2189
|
-
return 3 /* Namespace */;
|
|
2190
|
-
case "type":
|
|
2191
|
-
return 26 /* TypeParameter */;
|
|
2192
|
-
// parameter and other internal-only kinds have no LSP equivalent
|
|
2193
|
-
default:
|
|
2194
|
-
return null;
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2072
|
+
import { expectDefined } from "@wrongstack/core/utils";
|
|
2073
|
+
import * as fs2 from "node:fs";
|
|
2074
|
+
import * as path3 from "node:path";
|
|
2197
2075
|
|
|
2198
2076
|
// src/codebase-index/bm25.ts
|
|
2199
2077
|
var K1 = 1.5;
|
|
@@ -2284,33 +2162,98 @@ var Bm25Index = class {
|
|
|
2284
2162
|
}
|
|
2285
2163
|
};
|
|
2286
2164
|
|
|
2287
|
-
// src/codebase-index/
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2165
|
+
// src/codebase-index/lsp-kind.ts
|
|
2166
|
+
function lspKindToInternalKind(k) {
|
|
2167
|
+
switch (k) {
|
|
2168
|
+
case 5 /* Class */:
|
|
2169
|
+
return "class";
|
|
2170
|
+
case 6 /* Method */:
|
|
2171
|
+
return "method";
|
|
2172
|
+
case 7 /* Property */:
|
|
2173
|
+
case 8 /* Field */:
|
|
2174
|
+
return "property";
|
|
2175
|
+
case 9 /* Constructor */:
|
|
2176
|
+
return "class";
|
|
2177
|
+
case 10 /* Enum */:
|
|
2178
|
+
return "enum";
|
|
2179
|
+
case 11 /* Interface */:
|
|
2180
|
+
return "interface";
|
|
2181
|
+
case 12 /* Function */:
|
|
2182
|
+
return "function";
|
|
2183
|
+
case 13 /* Variable */:
|
|
2184
|
+
return "var";
|
|
2185
|
+
case 14 /* Constant */:
|
|
2186
|
+
return "const";
|
|
2187
|
+
case 22 /* EnumMember */:
|
|
2188
|
+
return "enum";
|
|
2189
|
+
case 26 /* TypeParameter */:
|
|
2190
|
+
return "type";
|
|
2191
|
+
case 3 /* Namespace */:
|
|
2192
|
+
return "namespace";
|
|
2193
|
+
default:
|
|
2194
|
+
return null;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
function internalKindToLspKind(k) {
|
|
2198
|
+
switch (k) {
|
|
2199
|
+
case "class":
|
|
2200
|
+
return 5 /* Class */;
|
|
2201
|
+
case "method":
|
|
2202
|
+
return 6 /* Method */;
|
|
2203
|
+
case "property":
|
|
2204
|
+
return 7 /* Property */;
|
|
2205
|
+
case "function":
|
|
2206
|
+
return 12 /* Function */;
|
|
2207
|
+
case "var":
|
|
2208
|
+
return 13 /* Variable */;
|
|
2209
|
+
case "const":
|
|
2210
|
+
return 14 /* Constant */;
|
|
2211
|
+
case "let":
|
|
2212
|
+
return 13 /* Variable */;
|
|
2213
|
+
case "enum":
|
|
2214
|
+
return 10 /* Enum */;
|
|
2215
|
+
case "interface":
|
|
2216
|
+
return 11 /* Interface */;
|
|
2217
|
+
case "namespace":
|
|
2218
|
+
return 3 /* Namespace */;
|
|
2219
|
+
case "type":
|
|
2220
|
+
return 26 /* TypeParameter */;
|
|
2221
|
+
// parameter and other internal-only kinds have no LSP equivalent
|
|
2222
|
+
default:
|
|
2223
|
+
return null;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
// src/codebase-index/schema.ts
|
|
2228
|
+
var SCHEMA_VERSION = 3;
|
|
2229
|
+
|
|
2230
|
+
// src/codebase-index/sqlite-runtime.ts
|
|
2231
|
+
import { createRequire } from "node:module";
|
|
2232
|
+
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
2233
|
+
var warningSilenced = false;
|
|
2234
|
+
function silenceSqliteExperimentalWarning() {
|
|
2235
|
+
if (warningSilenced) return;
|
|
2236
|
+
warningSilenced = true;
|
|
2237
|
+
const original = process.emitWarning.bind(process);
|
|
2238
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
2239
|
+
const msg = typeof warning === "string" ? warning : warning?.message ?? "";
|
|
2240
|
+
const name = typeof warning === "string" ? String(rest[0] ?? "") : warning?.name ?? "";
|
|
2241
|
+
if (/sqlite/i.test(msg) && /experimental/i.test(`${name} ${msg}`)) return;
|
|
2242
|
+
original(warning, ...rest);
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
var DatabaseSyncCtor;
|
|
2246
|
+
function loadDatabaseSync() {
|
|
2247
|
+
if (DatabaseSyncCtor) return DatabaseSyncCtor;
|
|
2248
|
+
silenceSqliteExperimentalWarning();
|
|
2249
|
+
try {
|
|
2250
|
+
const req = createRequire(import.meta.url);
|
|
2251
|
+
DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
|
|
2252
|
+
} catch (err) {
|
|
2253
|
+
throw new Error(
|
|
2254
|
+
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage(err)}`
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2314
2257
|
return DatabaseSyncCtor;
|
|
2315
2258
|
}
|
|
2316
2259
|
var MAX_LOCK_RETRIES = 3;
|
|
@@ -2351,99 +2294,9 @@ function runSqliteWithRetry(fn) {
|
|
|
2351
2294
|
throw lastError;
|
|
2352
2295
|
}
|
|
2353
2296
|
|
|
2354
|
-
// src/codebase-index/writer-helpers.ts
|
|
2355
|
-
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
2356
|
-
function escapeLike(value) {
|
|
2357
|
-
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
2358
|
-
}
|
|
2359
|
-
function assignRefsToSymbols(refs, symbols) {
|
|
2360
|
-
if (refs.length === 0 || symbols.length === 0) return [];
|
|
2361
|
-
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
2362
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2363
|
-
const assigned = [];
|
|
2364
|
-
for (const ref of refs) {
|
|
2365
|
-
let owner;
|
|
2366
|
-
for (const symbol of ordered) {
|
|
2367
|
-
if (symbol.line > ref.line) break;
|
|
2368
|
-
owner = symbol;
|
|
2369
|
-
}
|
|
2370
|
-
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
2371
|
-
if (!owner || owner.id <= 0) continue;
|
|
2372
|
-
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
2373
|
-
if (seen.has(key)) continue;
|
|
2374
|
-
seen.add(key);
|
|
2375
|
-
assigned.push({ ...ref, fromId: owner.id });
|
|
2376
|
-
}
|
|
2377
|
-
return assigned;
|
|
2378
|
-
}
|
|
2379
|
-
function resolveIndexDir(projectRoot, override) {
|
|
2380
|
-
return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
|
|
2381
|
-
}
|
|
2382
|
-
function codebaseIndexDirOverride(ctx) {
|
|
2383
|
-
const v = ctx.meta?.["codebaseIndexDir"];
|
|
2384
|
-
return typeof v === "string" ? v : void 0;
|
|
2385
|
-
}
|
|
2386
|
-
|
|
2387
|
-
// src/codebase-index/writer-schema.ts
|
|
2388
|
-
var METADATA_TABLE_SQL = `
|
|
2389
|
-
CREATE TABLE IF NOT EXISTS metadata (
|
|
2390
|
-
key TEXT PRIMARY KEY,
|
|
2391
|
-
value TEXT NOT NULL
|
|
2392
|
-
);
|
|
2393
|
-
`;
|
|
2394
|
-
var CORE_TABLES_SQL = `
|
|
2395
|
-
CREATE TABLE IF NOT EXISTS files (
|
|
2396
|
-
file TEXT PRIMARY KEY,
|
|
2397
|
-
lang TEXT NOT NULL,
|
|
2398
|
-
mtime_ms INTEGER NOT NULL,
|
|
2399
|
-
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
2400
|
-
last_indexed INTEGER NOT NULL
|
|
2401
|
-
);
|
|
2402
|
-
CREATE TABLE IF NOT EXISTS symbols (
|
|
2403
|
-
id INTEGER PRIMARY KEY,
|
|
2404
|
-
lang TEXT NOT NULL,
|
|
2405
|
-
kind TEXT NOT NULL,
|
|
2406
|
-
name TEXT NOT NULL,
|
|
2407
|
-
file TEXT NOT NULL,
|
|
2408
|
-
line INTEGER NOT NULL,
|
|
2409
|
-
col INTEGER NOT NULL,
|
|
2410
|
-
signature TEXT NOT NULL DEFAULT '',
|
|
2411
|
-
doc_comment TEXT NOT NULL DEFAULT '',
|
|
2412
|
-
scope TEXT NOT NULL DEFAULT '',
|
|
2413
|
-
text TEXT NOT NULL DEFAULT '',
|
|
2414
|
-
file_fk TEXT NOT NULL
|
|
2415
|
-
);
|
|
2416
|
-
`;
|
|
2417
|
-
var SYMBOL_INDEX_SQL = [
|
|
2418
|
-
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
2419
|
-
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
2420
|
-
"CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)",
|
|
2421
|
-
"CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)",
|
|
2422
|
-
"CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)",
|
|
2423
|
-
"CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)",
|
|
2424
|
-
"CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)"
|
|
2425
|
-
];
|
|
2426
|
-
var REFS_TABLE_SQL = `
|
|
2427
|
-
CREATE TABLE IF NOT EXISTS refs (
|
|
2428
|
-
id INTEGER PRIMARY KEY,
|
|
2429
|
-
from_id INTEGER NOT NULL,
|
|
2430
|
-
to_name TEXT NOT NULL,
|
|
2431
|
-
to_id INTEGER,
|
|
2432
|
-
call_type TEXT NOT NULL,
|
|
2433
|
-
line INTEGER NOT NULL
|
|
2434
|
-
);
|
|
2435
|
-
`;
|
|
2436
|
-
var REFS_INDEX_SQL = [
|
|
2437
|
-
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
2438
|
-
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
2439
|
-
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
2440
|
-
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
2441
|
-
];
|
|
2442
|
-
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
2443
|
-
|
|
2444
2297
|
// src/codebase-index/writer-admin.ts
|
|
2445
|
-
import * as
|
|
2446
|
-
import * as
|
|
2298
|
+
import * as fs from "node:fs";
|
|
2299
|
+
import * as path from "node:path";
|
|
2447
2300
|
var DB_FILE = "index.db";
|
|
2448
2301
|
function getAllIndexableWithStatement(stmt) {
|
|
2449
2302
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -2502,7 +2355,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
2502
2355
|
}
|
|
2503
2356
|
function getIndexDbSizeBytes(indexDir) {
|
|
2504
2357
|
try {
|
|
2505
|
-
return
|
|
2358
|
+
return fs.statSync(path.join(indexDir, DB_FILE)).size;
|
|
2506
2359
|
} catch {
|
|
2507
2360
|
return 0;
|
|
2508
2361
|
}
|
|
@@ -2569,7 +2422,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
2569
2422
|
}
|
|
2570
2423
|
|
|
2571
2424
|
// src/codebase-index/writer-graph-helpers.ts
|
|
2572
|
-
import * as
|
|
2425
|
+
import * as path2 from "node:path";
|
|
2573
2426
|
function derivePackage(filePath) {
|
|
2574
2427
|
const f = filePath.replace(/\\/g, "/");
|
|
2575
2428
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -2684,16 +2537,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
2684
2537
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
2685
2538
|
if (!moduleName.startsWith(".")) return void 0;
|
|
2686
2539
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
2687
|
-
const absolute =
|
|
2688
|
-
|
|
2540
|
+
const absolute = path2.posix.normalize(
|
|
2541
|
+
path2.posix.join(path2.posix.dirname(normalizedFrom), moduleName)
|
|
2689
2542
|
);
|
|
2690
|
-
const extension =
|
|
2543
|
+
const extension = path2.posix.extname(absolute);
|
|
2691
2544
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
2692
2545
|
const candidates = [
|
|
2693
2546
|
absolute,
|
|
2694
2547
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
2695
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
2696
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
2548
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path2.posix.join(absolute, `index${ext}`)),
|
|
2549
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path2.posix.join(base, `index${ext}`))
|
|
2697
2550
|
];
|
|
2698
2551
|
const indexedByPortablePath = new Map(
|
|
2699
2552
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -2913,6 +2766,39 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
|
2913
2766
|
return { nodes, edges };
|
|
2914
2767
|
}
|
|
2915
2768
|
|
|
2769
|
+
// src/codebase-index/writer-helpers.ts
|
|
2770
|
+
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
2771
|
+
function escapeLike(value) {
|
|
2772
|
+
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
2773
|
+
}
|
|
2774
|
+
function assignRefsToSymbols(refs, symbols) {
|
|
2775
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
2776
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
2777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2778
|
+
const assigned = [];
|
|
2779
|
+
for (const ref of refs) {
|
|
2780
|
+
let owner;
|
|
2781
|
+
for (const symbol of ordered) {
|
|
2782
|
+
if (symbol.line > ref.line) break;
|
|
2783
|
+
owner = symbol;
|
|
2784
|
+
}
|
|
2785
|
+
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
2786
|
+
if (!owner || owner.id <= 0) continue;
|
|
2787
|
+
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
2788
|
+
if (seen.has(key)) continue;
|
|
2789
|
+
seen.add(key);
|
|
2790
|
+
assigned.push({ ...ref, fromId: owner.id });
|
|
2791
|
+
}
|
|
2792
|
+
return assigned;
|
|
2793
|
+
}
|
|
2794
|
+
function resolveIndexDir(projectRoot, override) {
|
|
2795
|
+
return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
|
|
2796
|
+
}
|
|
2797
|
+
function codebaseIndexDirOverride(ctx) {
|
|
2798
|
+
const v = ctx.meta?.["codebaseIndexDir"];
|
|
2799
|
+
return typeof v === "string" ? v : void 0;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2916
2802
|
// src/codebase-index/writer-pragmas.ts
|
|
2917
2803
|
import { sqliteCachePragmas } from "@wrongstack/core/utils";
|
|
2918
2804
|
function applyIndexStorePragmas(db) {
|
|
@@ -2931,42 +2817,99 @@ function applyIndexStorePragmas(db) {
|
|
|
2931
2817
|
}
|
|
2932
2818
|
}
|
|
2933
2819
|
|
|
2934
|
-
// src/codebase-index/writer-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2820
|
+
// src/codebase-index/writer-schema.ts
|
|
2821
|
+
var METADATA_TABLE_SQL = `
|
|
2822
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
2823
|
+
key TEXT PRIMARY KEY,
|
|
2824
|
+
value TEXT NOT NULL
|
|
2825
|
+
);
|
|
2826
|
+
`;
|
|
2827
|
+
var CORE_TABLES_SQL = `
|
|
2828
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
2829
|
+
file TEXT PRIMARY KEY,
|
|
2830
|
+
lang TEXT NOT NULL,
|
|
2831
|
+
mtime_ms INTEGER NOT NULL,
|
|
2832
|
+
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
2833
|
+
last_indexed INTEGER NOT NULL
|
|
2834
|
+
);
|
|
2835
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
2836
|
+
id INTEGER PRIMARY KEY,
|
|
2837
|
+
lang TEXT NOT NULL,
|
|
2838
|
+
kind TEXT NOT NULL,
|
|
2839
|
+
name TEXT NOT NULL,
|
|
2840
|
+
file TEXT NOT NULL,
|
|
2841
|
+
line INTEGER NOT NULL,
|
|
2842
|
+
col INTEGER NOT NULL,
|
|
2843
|
+
signature TEXT NOT NULL DEFAULT '',
|
|
2844
|
+
doc_comment TEXT NOT NULL DEFAULT '',
|
|
2845
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
2846
|
+
text TEXT NOT NULL DEFAULT '',
|
|
2847
|
+
file_fk TEXT NOT NULL
|
|
2848
|
+
);
|
|
2849
|
+
`;
|
|
2850
|
+
var SYMBOL_INDEX_SQL = [
|
|
2851
|
+
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
2852
|
+
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
2853
|
+
"CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)",
|
|
2854
|
+
"CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)",
|
|
2855
|
+
"CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)",
|
|
2856
|
+
"CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)",
|
|
2857
|
+
"CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)"
|
|
2858
|
+
];
|
|
2859
|
+
var REFS_TABLE_SQL = `
|
|
2860
|
+
CREATE TABLE IF NOT EXISTS refs (
|
|
2861
|
+
id INTEGER PRIMARY KEY,
|
|
2862
|
+
from_id INTEGER NOT NULL,
|
|
2863
|
+
to_name TEXT NOT NULL,
|
|
2864
|
+
to_id INTEGER,
|
|
2865
|
+
call_type TEXT NOT NULL,
|
|
2866
|
+
line INTEGER NOT NULL
|
|
2867
|
+
);
|
|
2868
|
+
`;
|
|
2869
|
+
var REFS_INDEX_SQL = [
|
|
2870
|
+
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
2871
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
2872
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
2873
|
+
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
2874
|
+
];
|
|
2875
|
+
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
2876
|
+
|
|
2877
|
+
// src/codebase-index/writer-search-helpers.ts
|
|
2878
|
+
function normalizeSearchLimit(limit) {
|
|
2879
|
+
return typeof limit === "number" && Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : void 0;
|
|
2880
|
+
}
|
|
2881
|
+
function buildWriterSearchWhere(query, filter) {
|
|
2882
|
+
const conditions = [];
|
|
2883
|
+
const values = [];
|
|
2884
|
+
let effectiveKind = filter?.kind;
|
|
2885
|
+
if (filter?.lspKind !== void 0) {
|
|
2886
|
+
const mapped = lspKindToInternalKind(filter.lspKind);
|
|
2887
|
+
if (mapped !== null) {
|
|
2888
|
+
effectiveKind = mapped;
|
|
2889
|
+
} else {
|
|
2890
|
+
return null;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
if (effectiveKind) {
|
|
2894
|
+
conditions.push("kind = ?");
|
|
2895
|
+
values.push(effectiveKind);
|
|
2896
|
+
}
|
|
2897
|
+
if (filter?.lang) {
|
|
2898
|
+
conditions.push("lang = ?");
|
|
2899
|
+
values.push(filter.lang);
|
|
2900
|
+
}
|
|
2901
|
+
if (filter?.file) {
|
|
2902
|
+
conditions.push("replace(file, '\\', '/') LIKE ? ESCAPE '\\'");
|
|
2903
|
+
values.push(`%${escapeLike(filter.file.replace(/\\/g, "/"))}%`);
|
|
2904
|
+
}
|
|
2905
|
+
if (query.trim()) {
|
|
2906
|
+
const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
2907
|
+
conditions.push(`(${tokens.map(() => "text LIKE ?").join(" OR ")})`);
|
|
2908
|
+
for (const token of tokens) values.push(`%${token}%`);
|
|
2909
|
+
}
|
|
2910
|
+
return { where: conditions.length ? `WHERE ${conditions.join(" AND ")}` : "", values };
|
|
2911
|
+
}
|
|
2912
|
+
function mapWriterSearchRow(row, lspKind, score = 0, snippet = "") {
|
|
2970
2913
|
return {
|
|
2971
2914
|
id: row.id,
|
|
2972
2915
|
lang: row.lang,
|
|
@@ -3119,9 +3062,9 @@ var IndexStore = class _IndexStore {
|
|
|
3119
3062
|
}
|
|
3120
3063
|
constructor(projectRoot, opts = {}) {
|
|
3121
3064
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
3122
|
-
|
|
3065
|
+
fs2.mkdirSync(this.indexDir, { recursive: true });
|
|
3123
3066
|
const Database = loadDatabaseSync();
|
|
3124
|
-
this.db = new Database(
|
|
3067
|
+
this.db = new Database(path3.join(this.indexDir, DB_FILE2));
|
|
3125
3068
|
applyIndexStorePragmas(this.db);
|
|
3126
3069
|
this.initSchema();
|
|
3127
3070
|
}
|
|
@@ -3139,9 +3082,15 @@ var IndexStore = class _IndexStore {
|
|
|
3139
3082
|
DROP TABLE IF EXISTS refs;
|
|
3140
3083
|
`);
|
|
3141
3084
|
this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
3142
|
-
this.stmt("UPDATE metadata SET value = ? WHERE key = ?").run(
|
|
3085
|
+
this.stmt("UPDATE metadata SET value = ? WHERE key = ?").run(
|
|
3086
|
+
String(SCHEMA_VERSION),
|
|
3087
|
+
"version"
|
|
3088
|
+
);
|
|
3143
3089
|
} else if (storedVersion === null) {
|
|
3144
|
-
this.stmt("INSERT INTO metadata(key, value) VALUES (?, ?)").run(
|
|
3090
|
+
this.stmt("INSERT INTO metadata(key, value) VALUES (?, ?)").run(
|
|
3091
|
+
"version",
|
|
3092
|
+
String(SCHEMA_VERSION)
|
|
3093
|
+
);
|
|
3145
3094
|
}
|
|
3146
3095
|
this.db.exec(CORE_TABLES_SQL);
|
|
3147
3096
|
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
@@ -3158,7 +3107,9 @@ var IndexStore = class _IndexStore {
|
|
|
3158
3107
|
);
|
|
3159
3108
|
if (symbolCount !== ftsCount) {
|
|
3160
3109
|
this.db.exec("DELETE FROM symbols_fts");
|
|
3161
|
-
const rows = this.stmt(
|
|
3110
|
+
const rows = this.stmt(
|
|
3111
|
+
"SELECT id, name, signature, doc_comment FROM symbols ORDER BY id"
|
|
3112
|
+
).all();
|
|
3162
3113
|
bulkInsertFtsWithStatement(
|
|
3163
3114
|
(sql) => this.stmt(sql),
|
|
3164
3115
|
_IndexStore.MAX_SQL_VARS,
|
|
@@ -3380,7 +3331,9 @@ var IndexStore = class _IndexStore {
|
|
|
3380
3331
|
const limitSql = limit !== void 0 ? " LIMIT ?" : "";
|
|
3381
3332
|
const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}${limitSql}`;
|
|
3382
3333
|
const binds = limit !== void 0 ? [...values, limit] : values;
|
|
3383
|
-
const rows = this.stmt(sql).all(
|
|
3334
|
+
const rows = this.stmt(sql).all(
|
|
3335
|
+
...binds
|
|
3336
|
+
);
|
|
3384
3337
|
return rows.map((row) => mapWriterSearchRow(row, filter?.lspKind));
|
|
3385
3338
|
}
|
|
3386
3339
|
/** Shared WHERE builder for {@link search} / empty-query ranked totals. */
|
|
@@ -3526,13 +3479,13 @@ var IndexStore = class _IndexStore {
|
|
|
3526
3479
|
if (rankDiff !== 0) return rankDiff;
|
|
3527
3480
|
const scoreDiff = b.score - a.score;
|
|
3528
3481
|
if (scoreDiff !== 0) return scoreDiff;
|
|
3529
|
-
const left =
|
|
3530
|
-
const right =
|
|
3482
|
+
const left = expectDefined(candidateById.get(a.id));
|
|
3483
|
+
const right = expectDefined(candidateById.get(b.id));
|
|
3531
3484
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
3532
3485
|
});
|
|
3533
3486
|
const qTokens = tokenise(query);
|
|
3534
3487
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
3535
|
-
const c =
|
|
3488
|
+
const c = expectDefined(candidateById.get(id));
|
|
3536
3489
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
3537
3490
|
});
|
|
3538
3491
|
return { results, total: candidates.length };
|
|
@@ -3556,7 +3509,9 @@ var IndexStore = class _IndexStore {
|
|
|
3556
3509
|
}
|
|
3557
3510
|
setLastIndexed(ts2) {
|
|
3558
3511
|
this.runWithRetry(() => {
|
|
3559
|
-
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)").run(
|
|
3512
|
+
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)").run(
|
|
3513
|
+
String(ts2)
|
|
3514
|
+
);
|
|
3560
3515
|
});
|
|
3561
3516
|
}
|
|
3562
3517
|
getMetadata(key) {
|
|
@@ -3667,7 +3622,9 @@ var IndexStore = class _IndexStore {
|
|
|
3667
3622
|
this.stmt(
|
|
3668
3623
|
`DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`
|
|
3669
3624
|
).run(...options.deleteForFiles);
|
|
3670
|
-
this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(
|
|
3625
|
+
this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(
|
|
3626
|
+
...options.deleteForFiles
|
|
3627
|
+
);
|
|
3671
3628
|
}
|
|
3672
3629
|
const totalSymbols = entries.reduce((n, e) => n + e.symbols.length, 0);
|
|
3673
3630
|
let nextId = this.allocateSymbolIds(totalSymbols);
|
|
@@ -3711,11 +3668,7 @@ var IndexStore = class _IndexStore {
|
|
|
3711
3668
|
this.ftsAvailable,
|
|
3712
3669
|
ftsRows
|
|
3713
3670
|
);
|
|
3714
|
-
bulkInsertRefsWithStatement(
|
|
3715
|
-
(sql) => this.stmt(sql),
|
|
3716
|
-
_IndexStore.MAX_SQL_VARS,
|
|
3717
|
-
refsToInsert
|
|
3718
|
-
);
|
|
3671
|
+
bulkInsertRefsWithStatement((sql) => this.stmt(sql), _IndexStore.MAX_SQL_VARS, refsToInsert);
|
|
3719
3672
|
const upsertStmt = this.stmt(
|
|
3720
3673
|
`INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)
|
|
3721
3674
|
VALUES (?, ?, ?, ?, ?)
|
|
@@ -3744,7 +3697,9 @@ var IndexStore = class _IndexStore {
|
|
|
3744
3697
|
*/
|
|
3745
3698
|
deleteRefsForFile(file) {
|
|
3746
3699
|
this.runWithRetry(() => {
|
|
3747
|
-
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
3700
|
+
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
3701
|
+
file
|
|
3702
|
+
);
|
|
3748
3703
|
});
|
|
3749
3704
|
}
|
|
3750
3705
|
/**
|
|
@@ -3899,9 +3854,7 @@ var IndexStore = class _IndexStore {
|
|
|
3899
3854
|
* build the full symbol universe for the reachability scan.
|
|
3900
3855
|
*/
|
|
3901
3856
|
getAllSymbols() {
|
|
3902
|
-
return this.stmt(
|
|
3903
|
-
"SELECT id, name, file, kind, line FROM symbols ORDER BY id"
|
|
3904
|
-
).all().map((r) => ({ ...r, kind: r.kind }));
|
|
3857
|
+
return this.stmt("SELECT id, name, file, kind, line FROM symbols ORDER BY id").all().map((r) => ({ ...r, kind: r.kind }));
|
|
3905
3858
|
}
|
|
3906
3859
|
/**
|
|
3907
3860
|
* Returns every resolved reference (to_id IS NOT NULL). Used by
|
|
@@ -3913,6 +3866,25 @@ var IndexStore = class _IndexStore {
|
|
|
3913
3866
|
"SELECT from_id AS fromId, to_id AS toId, call_type AS callType FROM refs WHERE to_id IS NOT NULL"
|
|
3914
3867
|
).all();
|
|
3915
3868
|
}
|
|
3869
|
+
/**
|
|
3870
|
+
* Returns ALL import refs (including unresolved) with their source-file
|
|
3871
|
+
* path and resolved target id. Used by the dead-code scan's file-level
|
|
3872
|
+
* graph traversal to handle barrel-only entry points where no symbol
|
|
3873
|
+
* carries the ref.
|
|
3874
|
+
*
|
|
3875
|
+
* Refs whose `from_id` doesn't match a known symbol (e.g. pure-barrel
|
|
3876
|
+
* files with no declarations) will have `sourceFile === null`.
|
|
3877
|
+
*/
|
|
3878
|
+
getAllImportRefs() {
|
|
3879
|
+
return this.stmt(
|
|
3880
|
+
`SELECT s.file AS sourceFile, r.to_name AS toName, r.to_id AS toId,
|
|
3881
|
+
r.call_type AS callType, r.line
|
|
3882
|
+
FROM refs r
|
|
3883
|
+
LEFT JOIN symbols s ON r.from_id = s.id
|
|
3884
|
+
WHERE r.call_type = 'import'
|
|
3885
|
+
ORDER BY r.line`
|
|
3886
|
+
).all();
|
|
3887
|
+
}
|
|
3916
3888
|
close() {
|
|
3917
3889
|
this.stmtCache.clear();
|
|
3918
3890
|
this.bm25Dirty = true;
|
|
@@ -3927,1185 +3899,1296 @@ var indexStorePool = new StorePool(
|
|
|
3927
3899
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
3928
3900
|
);
|
|
3929
3901
|
|
|
3930
|
-
// src/codebase-index/
|
|
3931
|
-
var
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
function
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3902
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
3903
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
3904
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
3905
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
3906
|
+
var buildIdCache;
|
|
3907
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
3908
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path4.resolve(entrypoint);
|
|
3909
|
+
try {
|
|
3910
|
+
const stat2 = fs3.statSync(file);
|
|
3911
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat2.mtimeMs && buildIdCache.size === stat2.size) {
|
|
3912
|
+
return buildIdCache.buildId;
|
|
3913
|
+
}
|
|
3914
|
+
const buildId = createHash("sha256").update(fs3.readFileSync(file)).digest("hex").slice(0, 24);
|
|
3915
|
+
buildIdCache = { file, mtimeMs: stat2.mtimeMs, size: stat2.size, buildId };
|
|
3916
|
+
return buildId;
|
|
3917
|
+
} catch {
|
|
3918
|
+
return `unreadable:${path4.basename(file)}`;
|
|
3919
|
+
}
|
|
3942
3920
|
}
|
|
3943
|
-
function
|
|
3944
|
-
|
|
3921
|
+
function normalizeLocalPath(value) {
|
|
3922
|
+
const resolved = path4.resolve(value);
|
|
3923
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
3945
3924
|
}
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
3950
|
-
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
3951
|
-
function isWithinProject(projectRoot, file) {
|
|
3952
|
-
const rel = path11.relative(projectRoot, file);
|
|
3953
|
-
return rel !== "" && !rel.startsWith(`..${path11.sep}`) && rel !== ".." && !path11.isAbsolute(rel);
|
|
3925
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
3926
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
3927
|
+
return createHash("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
3954
3928
|
}
|
|
3955
|
-
function
|
|
3956
|
-
const
|
|
3957
|
-
|
|
3929
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
3930
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
3931
|
+
if (process.platform === "win32") {
|
|
3932
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
3933
|
+
}
|
|
3934
|
+
return path4.join(os.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
3958
3935
|
}
|
|
3959
|
-
function
|
|
3960
|
-
|
|
3961
|
-
|
|
3936
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
3937
|
+
return path4.join(
|
|
3938
|
+
path4.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
3939
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
3940
|
+
);
|
|
3962
3941
|
}
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
encoding: "buffer",
|
|
3970
|
-
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
3971
|
-
windowsHide: true
|
|
3972
|
-
},
|
|
3973
|
-
(error, stdout) => {
|
|
3974
|
-
if (error) reject(error);
|
|
3975
|
-
else resolve4(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
3976
|
-
}
|
|
3977
|
-
);
|
|
3978
|
-
});
|
|
3942
|
+
|
|
3943
|
+
// src/codebase-index/project-server-protocol.ts
|
|
3944
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
3945
|
+
function encodeProjectServerMessage(message) {
|
|
3946
|
+
return `${JSON.stringify(message)}
|
|
3947
|
+
`;
|
|
3979
3948
|
}
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
}
|
|
4013
|
-
const files = [];
|
|
4014
|
-
for (const relative2 of output.toString("utf8").split("\0")) {
|
|
4015
|
-
if (!relative2) continue;
|
|
4016
|
-
const portable = relative2.replace(/\\/g, "/");
|
|
4017
|
-
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path11.posix.basename(portable))) {
|
|
4018
|
-
continue;
|
|
3949
|
+
|
|
3950
|
+
// src/codebase-index/project-server-client.ts
|
|
3951
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
3952
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
3953
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
3954
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
3955
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
3956
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
3957
|
+
constructor(message, pid) {
|
|
3958
|
+
super(message);
|
|
3959
|
+
this.pid = pid;
|
|
3960
|
+
}
|
|
3961
|
+
pid;
|
|
3962
|
+
name = "StaleProjectIndexServerError";
|
|
3963
|
+
};
|
|
3964
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
3965
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
3966
|
+
var latestConnectionState = {
|
|
3967
|
+
status: "offline",
|
|
3968
|
+
connected: false
|
|
3969
|
+
};
|
|
3970
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
3971
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
3972
|
+
return { kind: "inline-requested" };
|
|
3973
|
+
}
|
|
3974
|
+
let builtUrl = null;
|
|
3975
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
3976
|
+
try {
|
|
3977
|
+
const url = new URL(rel, import.meta.url);
|
|
3978
|
+
if (url.protocol === "file:" && fs4.existsSync(fileURLToPath2(url))) {
|
|
3979
|
+
builtUrl = url;
|
|
3980
|
+
break;
|
|
4019
3981
|
}
|
|
4020
|
-
|
|
4021
|
-
if (deleted.has(full)) continue;
|
|
4022
|
-
const ext = path11.extname(relative2).toLowerCase();
|
|
4023
|
-
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
3982
|
+
} catch {
|
|
4024
3983
|
}
|
|
4025
|
-
return {
|
|
4026
|
-
files,
|
|
4027
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
4028
|
-
};
|
|
4029
|
-
} catch {
|
|
4030
|
-
return null;
|
|
4031
3984
|
}
|
|
4032
|
-
}
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
const results = [];
|
|
4044
|
-
const errors = [];
|
|
4045
|
-
let complete = true;
|
|
4046
|
-
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4047
|
-
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
4048
|
-
let dirCount = 0;
|
|
4049
|
-
const walk = async (dir) => {
|
|
4050
|
-
throwIfAborted(signal);
|
|
4051
|
-
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
4052
|
-
await yieldEventLoop();
|
|
4053
|
-
throwIfAborted(signal);
|
|
4054
|
-
}
|
|
4055
|
-
let entries;
|
|
4056
|
-
try {
|
|
4057
|
-
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
4058
|
-
} catch (err) {
|
|
4059
|
-
complete = false;
|
|
4060
|
-
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4061
|
-
return;
|
|
4062
|
-
}
|
|
4063
|
-
dirCount++;
|
|
4064
|
-
for (const e of entries) {
|
|
4065
|
-
if (ignoreSet.has(e.name)) continue;
|
|
4066
|
-
const full = path11.join(dir, e.name);
|
|
4067
|
-
const rel = path11.relative(projectRoot, full).replace(/\\/g, "/");
|
|
4068
|
-
if (e.isDirectory()) {
|
|
4069
|
-
if (isGitIgnored(rel, true)) continue;
|
|
4070
|
-
await walk(full);
|
|
4071
|
-
} else if (e.isFile()) {
|
|
4072
|
-
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
4073
|
-
const ext = path11.extname(e.name).toLowerCase();
|
|
4074
|
-
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
4075
|
-
results.push(full);
|
|
4076
|
-
}
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
};
|
|
4080
|
-
await walk(projectRoot);
|
|
4081
|
-
return { files: results, complete, errors };
|
|
4082
|
-
}
|
|
4083
|
-
function assignRefsToSymbols2(refs, symbols) {
|
|
4084
|
-
if (refs.length === 0 || symbols.length === 0) return [];
|
|
4085
|
-
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
4086
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4087
|
-
const assigned = [];
|
|
4088
|
-
for (const ref of refs) {
|
|
4089
|
-
let owner;
|
|
4090
|
-
for (const symbol of ordered) {
|
|
4091
|
-
if (symbol.line > ref.line) break;
|
|
4092
|
-
owner = symbol;
|
|
3985
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
3986
|
+
if (projectRoot !== void 0) {
|
|
3987
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
3988
|
+
const check = checkUnixSocketPath(endpoint);
|
|
3989
|
+
if (!check.ok) {
|
|
3990
|
+
return {
|
|
3991
|
+
kind: "endpoint-invalid",
|
|
3992
|
+
endpoint,
|
|
3993
|
+
byteLength: check.byteLength,
|
|
3994
|
+
maxBytes: check.maxBytes
|
|
3995
|
+
};
|
|
4093
3996
|
}
|
|
4094
|
-
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
4095
|
-
if (!owner || owner.id <= 0) continue;
|
|
4096
|
-
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
4097
|
-
if (seen.has(key)) continue;
|
|
4098
|
-
seen.add(key);
|
|
4099
|
-
assigned.push({ ...ref, fromId: owner.id });
|
|
4100
3997
|
}
|
|
4101
|
-
return
|
|
3998
|
+
return { kind: "available", url: builtUrl };
|
|
4102
3999
|
}
|
|
4103
|
-
|
|
4104
|
-
const
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4000
|
+
function resolveProjectServerUrl() {
|
|
4001
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
4002
|
+
return availability.kind === "available" ? availability.url : null;
|
|
4003
|
+
}
|
|
4004
|
+
function projectIndexServerExpectedBuildId() {
|
|
4005
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
4006
|
+
if (override) return override;
|
|
4007
|
+
const url = resolveProjectServerUrl();
|
|
4008
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
4009
|
+
}
|
|
4010
|
+
function isProjectIndexServerAvailable() {
|
|
4011
|
+
return resolveProjectServerUrl() !== null;
|
|
4012
|
+
}
|
|
4013
|
+
function publishConnectionState(endpoint, state) {
|
|
4014
|
+
connectionStates.set(endpoint, state);
|
|
4015
|
+
latestConnectionState = state;
|
|
4016
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
4017
|
+
}
|
|
4018
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
4019
|
+
if (projectRoot) {
|
|
4020
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4021
|
+
const existing = connectionStates.get(endpoint);
|
|
4022
|
+
if (existing) return existing;
|
|
4023
|
+
if (!isProjectIndexServerAvailable()) {
|
|
4024
|
+
return { status: "unavailable", connected: false };
|
|
4111
4025
|
}
|
|
4026
|
+
return {
|
|
4027
|
+
status: "offline",
|
|
4028
|
+
connected: false,
|
|
4029
|
+
projectRoot,
|
|
4030
|
+
indexDir,
|
|
4031
|
+
endpoint
|
|
4032
|
+
};
|
|
4112
4033
|
}
|
|
4034
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
4035
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
4036
|
+
return latestConnectionState;
|
|
4113
4037
|
}
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
const
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
}
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4038
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
4039
|
+
connectionStateListeners.add(listener);
|
|
4040
|
+
return () => connectionStateListeners.delete(listener);
|
|
4041
|
+
}
|
|
4042
|
+
function remoteError(message, name) {
|
|
4043
|
+
if (name === "LockError") return new LockError(message);
|
|
4044
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
4045
|
+
const error = new Error(message);
|
|
4046
|
+
if (name && name !== "Error") error.name = name;
|
|
4047
|
+
return error;
|
|
4048
|
+
}
|
|
4049
|
+
function isProjectIndexServerHealth(value) {
|
|
4050
|
+
if (!value || typeof value !== "object") return false;
|
|
4051
|
+
const health = value;
|
|
4052
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
4053
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
4054
|
+
return typeof health.checkedAt === "number" && typeof health.uptimeMs === "number" && typeof memory?.rss === "number" && typeof memory.heapUsed === "number" && typeof memory.heapTotal === "number" && typeof memory.external === "number" && typeof health.clients === "number" && typeof health.activeRequests === "number" && typeof health.activeWrites === "number" && typeof health.queuedWrites === "number" && typeof health.pendingExternalFiles === "number" && typeof health.watchingExternal === "boolean" && typeof activity?.indexing === "boolean" && typeof activity.currentFile === "number" && typeof activity.totalFiles === "number" && typeof activity.generation === "number";
|
|
4055
|
+
}
|
|
4056
|
+
function delay(ms) {
|
|
4057
|
+
return new Promise((resolve4) => {
|
|
4058
|
+
const timer = setTimeout(resolve4, ms);
|
|
4059
|
+
timer.unref?.();
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
function cancellationError(signal) {
|
|
4063
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
4064
|
+
}
|
|
4065
|
+
var ProjectServerConnection = class {
|
|
4066
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
4067
|
+
this.projectRoot = projectRoot;
|
|
4068
|
+
this.indexDir = indexDir;
|
|
4069
|
+
this.endpoint = endpoint;
|
|
4070
|
+
this.transition("offline");
|
|
4143
4071
|
}
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4072
|
+
projectRoot;
|
|
4073
|
+
indexDir;
|
|
4074
|
+
endpoint;
|
|
4075
|
+
socket = null;
|
|
4076
|
+
buffer = "";
|
|
4077
|
+
info = null;
|
|
4078
|
+
activity = null;
|
|
4079
|
+
health = null;
|
|
4080
|
+
healthCheck = null;
|
|
4081
|
+
connecting = null;
|
|
4082
|
+
connectResolve = null;
|
|
4083
|
+
connectReject = null;
|
|
4084
|
+
nextId = 1;
|
|
4085
|
+
pending = /* @__PURE__ */ new Map();
|
|
4086
|
+
transition(status, options = {}) {
|
|
4087
|
+
const previous = connectionStates.get(this.endpoint);
|
|
4088
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
4089
|
+
const lastError = options.error === void 0 ? status === "error" || status === "degraded" || status === "unresponsive" ? previous?.lastError : void 0 : options.error instanceof Error ? options.error.message : String(options.error);
|
|
4090
|
+
publishConnectionState(this.endpoint, {
|
|
4091
|
+
status,
|
|
4092
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
4093
|
+
projectRoot: this.projectRoot,
|
|
4094
|
+
indexDir: this.indexDir,
|
|
4095
|
+
endpoint: this.endpoint,
|
|
4096
|
+
pid,
|
|
4097
|
+
lastError,
|
|
4098
|
+
...this.activity ? { activity: this.activity } : {},
|
|
4099
|
+
...this.health ? { health: this.health } : {}
|
|
4149
4100
|
});
|
|
4150
4101
|
}
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
if (!force) {
|
|
4154
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
4102
|
+
isConnected() {
|
|
4103
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
4155
4104
|
}
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4105
|
+
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
4106
|
+
await this.ensureConnected(spawnIfMissing);
|
|
4107
|
+
if (this.healthCheck) return this.healthCheck;
|
|
4108
|
+
const startedAt = Date.now();
|
|
4109
|
+
this.healthCheck = this.request({ type: "ping" }, { timeoutMs }).then((server) => {
|
|
4110
|
+
const now = Date.now();
|
|
4111
|
+
this.health = {
|
|
4112
|
+
status: "healthy",
|
|
4113
|
+
checkedAt: now,
|
|
4114
|
+
lastHealthyAt: now,
|
|
4115
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
4116
|
+
missedHeartbeats: 0,
|
|
4117
|
+
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
4118
|
+
};
|
|
4119
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4120
|
+
return this.health;
|
|
4121
|
+
}).catch((error) => {
|
|
4122
|
+
if (!this.isConnected()) throw error;
|
|
4123
|
+
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
4124
|
+
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
4125
|
+
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
4126
|
+
this.health = {
|
|
4127
|
+
status,
|
|
4128
|
+
checkedAt: Date.now(),
|
|
4129
|
+
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
4130
|
+
latencyMs: null,
|
|
4131
|
+
missedHeartbeats,
|
|
4132
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
4133
|
+
};
|
|
4134
|
+
this.transition(status, { pid: this.info?.pid, error });
|
|
4135
|
+
return this.health;
|
|
4136
|
+
}).finally(() => {
|
|
4137
|
+
this.healthCheck = null;
|
|
4167
4138
|
});
|
|
4168
|
-
|
|
4139
|
+
return this.healthCheck;
|
|
4169
4140
|
}
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4141
|
+
markResponsive() {
|
|
4142
|
+
const now = Date.now();
|
|
4143
|
+
this.health = {
|
|
4144
|
+
status: "healthy",
|
|
4145
|
+
checkedAt: now,
|
|
4146
|
+
lastHealthyAt: now,
|
|
4147
|
+
latencyMs: this.health?.latencyMs ?? null,
|
|
4148
|
+
missedHeartbeats: 0,
|
|
4149
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
async call(op, args, options) {
|
|
4153
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
4154
|
+
await this.ensureConnected(true);
|
|
4155
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
4156
|
+
return this.request({ type: "request", op, args }, options);
|
|
4157
|
+
}
|
|
4158
|
+
async shutdownRemote(reason) {
|
|
4159
|
+
try {
|
|
4160
|
+
await this.ensureConnected(false);
|
|
4161
|
+
} catch {
|
|
4162
|
+
return { stopped: false, reason: "not-running" };
|
|
4184
4163
|
}
|
|
4185
|
-
const
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
lang,
|
|
4211
|
-
parsed: null,
|
|
4212
|
-
error: `file too large (${stat2.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
4213
|
-
};
|
|
4214
|
-
}
|
|
4215
|
-
const meta = existingMeta.get(file);
|
|
4216
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
|
|
4217
|
-
return { file, stat: stat2, lang, parsed: null, skippedMeta: meta };
|
|
4218
|
-
}
|
|
4219
|
-
let content;
|
|
4220
|
-
try {
|
|
4221
|
-
content = await fs8.readFile(file, { encoding: "utf8", signal });
|
|
4222
|
-
} catch (e) {
|
|
4223
|
-
if (isAbortError(e)) throw e;
|
|
4224
|
-
return {
|
|
4225
|
-
file,
|
|
4226
|
-
stat: stat2,
|
|
4227
|
-
lang,
|
|
4228
|
-
parsed: null,
|
|
4229
|
-
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
4230
|
-
};
|
|
4231
|
-
}
|
|
4232
|
-
let parsed;
|
|
4233
|
-
try {
|
|
4234
|
-
parsed = await parseFileContent(file, content, lang);
|
|
4235
|
-
} catch (e) {
|
|
4236
|
-
return {
|
|
4237
|
-
file,
|
|
4238
|
-
stat: stat2,
|
|
4239
|
-
lang,
|
|
4240
|
-
parsed: null,
|
|
4241
|
-
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
4242
|
-
};
|
|
4243
|
-
}
|
|
4244
|
-
return { file, stat: stat2, lang, parsed, content };
|
|
4245
|
-
}
|
|
4246
|
-
)
|
|
4164
|
+
const pid = this.info?.pid;
|
|
4165
|
+
try {
|
|
4166
|
+
this.transition("stopping", { pid });
|
|
4167
|
+
await this.request(
|
|
4168
|
+
{ type: "shutdown", reason },
|
|
4169
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
4170
|
+
);
|
|
4171
|
+
return { stopped: true, pid };
|
|
4172
|
+
} catch (error) {
|
|
4173
|
+
const forceKilled = this.forceKillKnownServer();
|
|
4174
|
+
return {
|
|
4175
|
+
stopped: forceKilled,
|
|
4176
|
+
pid,
|
|
4177
|
+
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
4178
|
+
};
|
|
4179
|
+
} finally {
|
|
4180
|
+
this.close();
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
async configure(watchExternal, debounceMs) {
|
|
4184
|
+
await this.ensureConnected(true);
|
|
4185
|
+
const startedAt = Date.now();
|
|
4186
|
+
const result = await this.request(
|
|
4187
|
+
{ type: "configure", watchExternal, debounceMs },
|
|
4188
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
4247
4189
|
);
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4190
|
+
if (isProjectIndexServerHealth(result.health)) {
|
|
4191
|
+
const now = Date.now();
|
|
4192
|
+
this.health = {
|
|
4193
|
+
status: "healthy",
|
|
4194
|
+
checkedAt: now,
|
|
4195
|
+
lastHealthyAt: now,
|
|
4196
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
4197
|
+
missedHeartbeats: 0,
|
|
4198
|
+
server: result.health
|
|
4199
|
+
};
|
|
4200
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
close() {
|
|
4204
|
+
const socket = this.socket;
|
|
4205
|
+
this.socket = null;
|
|
4206
|
+
this.info = null;
|
|
4207
|
+
this.activity = null;
|
|
4208
|
+
this.health = null;
|
|
4209
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
4210
|
+
this.connectResolve = null;
|
|
4211
|
+
this.connectReject = null;
|
|
4212
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
4213
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
4214
|
+
this.transition("offline");
|
|
4215
|
+
maybeStopHeartbeatLoop();
|
|
4216
|
+
}
|
|
4217
|
+
request(message, options) {
|
|
4218
|
+
const socket = this.socket;
|
|
4219
|
+
if (!socket || socket.destroyed) {
|
|
4220
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
4221
|
+
}
|
|
4222
|
+
const id = this.nextId++;
|
|
4223
|
+
return new Promise((resolve4, reject) => {
|
|
4224
|
+
const timer = setTimeout(() => {
|
|
4225
|
+
const entry = this.pending.get(id);
|
|
4226
|
+
if (!entry) return;
|
|
4227
|
+
this.pending.delete(id);
|
|
4228
|
+
this.write({ type: "cancel", id });
|
|
4229
|
+
const error = new IndexTimeoutError(
|
|
4230
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
4231
|
+
);
|
|
4232
|
+
this.cleanupPending(entry);
|
|
4233
|
+
entry.reject(error);
|
|
4234
|
+
}, options.timeoutMs);
|
|
4235
|
+
timer.unref?.();
|
|
4236
|
+
const signal = options.signal;
|
|
4237
|
+
const onAbort = signal ? () => {
|
|
4238
|
+
const entry = this.pending.get(id);
|
|
4239
|
+
if (!entry) return;
|
|
4240
|
+
this.pending.delete(id);
|
|
4241
|
+
this.write({ type: "cancel", id });
|
|
4242
|
+
this.cleanupPending(entry);
|
|
4243
|
+
entry.reject(cancellationError(signal));
|
|
4244
|
+
} : void 0;
|
|
4245
|
+
this.pending.set(id, {
|
|
4246
|
+
resolve: resolve4,
|
|
4247
|
+
reject,
|
|
4248
|
+
timer,
|
|
4249
|
+
signal,
|
|
4250
|
+
onAbort,
|
|
4251
|
+
onProgress: options.onProgress
|
|
4252
|
+
});
|
|
4253
|
+
if (signal && onAbort) {
|
|
4254
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4255
|
+
if (signal.aborted) {
|
|
4256
|
+
onAbort();
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4258
4259
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4260
|
+
this.write({ ...message, id });
|
|
4261
|
+
});
|
|
4262
|
+
}
|
|
4263
|
+
async ensureConnected(spawnIfMissing) {
|
|
4264
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
4265
|
+
if (this.connecting) return this.connecting;
|
|
4266
|
+
this.transition("connecting");
|
|
4267
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
4268
|
+
this.transition("error", { error });
|
|
4269
|
+
throw error;
|
|
4270
|
+
}).finally(() => {
|
|
4271
|
+
this.connecting = null;
|
|
4272
|
+
});
|
|
4273
|
+
return this.connecting;
|
|
4274
|
+
}
|
|
4275
|
+
async connectWithElection(spawnIfMissing) {
|
|
4276
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4277
|
+
let spawned = false;
|
|
4278
|
+
let staleAttempts = 0;
|
|
4279
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
4280
|
+
while (Date.now() < deadline) {
|
|
4281
|
+
try {
|
|
4282
|
+
await this.connectOnce();
|
|
4283
|
+
return;
|
|
4284
|
+
} catch (error) {
|
|
4285
|
+
lastError = error;
|
|
4286
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
4287
|
+
staleAttempts++;
|
|
4288
|
+
if (!spawnIfMissing) break;
|
|
4289
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
4290
|
+
spawned = false;
|
|
4291
|
+
await delay(100);
|
|
4292
|
+
continue;
|
|
4293
|
+
}
|
|
4264
4294
|
}
|
|
4265
|
-
|
|
4266
|
-
if (
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
filesIndexed++;
|
|
4270
|
-
continue;
|
|
4295
|
+
if (!spawnIfMissing) break;
|
|
4296
|
+
if (!spawned) {
|
|
4297
|
+
this.spawnDetachedServer();
|
|
4298
|
+
spawned = true;
|
|
4271
4299
|
}
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4300
|
+
await delay(75);
|
|
4301
|
+
}
|
|
4302
|
+
throw lastError;
|
|
4303
|
+
}
|
|
4304
|
+
connectOnce() {
|
|
4305
|
+
this.socket?.destroy();
|
|
4306
|
+
this.socket = null;
|
|
4307
|
+
this.info = null;
|
|
4308
|
+
this.activity = null;
|
|
4309
|
+
this.health = null;
|
|
4310
|
+
this.buffer = "";
|
|
4311
|
+
return new Promise((resolve4, reject) => {
|
|
4312
|
+
const socket = net.createConnection(this.endpoint);
|
|
4313
|
+
this.socket = socket;
|
|
4314
|
+
socket.setEncoding("utf8");
|
|
4315
|
+
const timer = setTimeout(() => {
|
|
4316
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
4317
|
+
socket.destroy();
|
|
4318
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4319
|
+
timer.unref?.();
|
|
4320
|
+
const finishResolve = () => {
|
|
4321
|
+
clearTimeout(timer);
|
|
4322
|
+
this.connectResolve = null;
|
|
4323
|
+
this.connectReject = null;
|
|
4324
|
+
resolve4();
|
|
4325
|
+
};
|
|
4326
|
+
const finishReject = (error) => {
|
|
4327
|
+
clearTimeout(timer);
|
|
4328
|
+
this.connectResolve = null;
|
|
4329
|
+
this.connectReject = null;
|
|
4330
|
+
reject(error);
|
|
4331
|
+
};
|
|
4332
|
+
this.connectResolve = finishResolve;
|
|
4333
|
+
this.connectReject = finishReject;
|
|
4334
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
4335
|
+
socket.on("error", (error) => {
|
|
4336
|
+
if (!this.info) finishReject(error);
|
|
4337
|
+
});
|
|
4338
|
+
socket.on("close", () => this.onClose(socket));
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
onData(socket, chunk) {
|
|
4342
|
+
if (socket !== this.socket) return;
|
|
4343
|
+
this.buffer += chunk;
|
|
4344
|
+
while (true) {
|
|
4345
|
+
const newline = this.buffer.indexOf("\n");
|
|
4346
|
+
if (newline < 0) {
|
|
4347
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
4348
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
4282
4349
|
}
|
|
4283
|
-
|
|
4350
|
+
return;
|
|
4284
4351
|
}
|
|
4285
|
-
if (
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
lang,
|
|
4289
|
-
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
4290
|
-
symbolCount: 0,
|
|
4291
|
-
lastIndexed: Date.now()
|
|
4292
|
-
});
|
|
4293
|
-
filesIndexed++;
|
|
4294
|
-
continue;
|
|
4352
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
4353
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
4354
|
+
return;
|
|
4295
4355
|
}
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
refs: parsed.refs ?? [],
|
|
4301
|
-
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
4302
|
-
symbolCount: parsed.symbols.length
|
|
4303
|
-
});
|
|
4304
|
-
deleteForFiles.push(file);
|
|
4305
|
-
}
|
|
4306
|
-
if (batchEntries.length > 0) {
|
|
4356
|
+
const line = this.buffer.slice(0, newline);
|
|
4357
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
4358
|
+
if (!line) continue;
|
|
4359
|
+
let message;
|
|
4307
4360
|
try {
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
4313
|
-
filesIndexed++;
|
|
4314
|
-
}
|
|
4315
|
-
} catch (err) {
|
|
4316
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4317
|
-
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
4318
|
-
for (const entry of batchEntries) {
|
|
4319
|
-
try {
|
|
4320
|
-
store.deleteRefsForFile(entry.file);
|
|
4321
|
-
store.deleteSymbolsForFile(entry.file);
|
|
4322
|
-
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
4323
|
-
symbolsIndexed += symbolsWithIds.length;
|
|
4324
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
4325
|
-
filesIndexed++;
|
|
4326
|
-
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
4327
|
-
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
4328
|
-
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
4329
|
-
}
|
|
4330
|
-
store.resolveRefsForNames([
|
|
4331
|
-
...entry.symbols.map((symbol) => symbol.name),
|
|
4332
|
-
...entry.refs.map((ref) => ref.toName)
|
|
4333
|
-
]);
|
|
4334
|
-
store.upsertFile({
|
|
4335
|
-
file: entry.file,
|
|
4336
|
-
lang: entry.lang,
|
|
4337
|
-
mtimeMs: entry.mtimeMs,
|
|
4338
|
-
symbolCount: entry.symbolCount,
|
|
4339
|
-
lastIndexed: Date.now()
|
|
4340
|
-
});
|
|
4341
|
-
} catch (innerErr) {
|
|
4342
|
-
errors.push(
|
|
4343
|
-
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
4344
|
-
);
|
|
4345
|
-
}
|
|
4346
|
-
}
|
|
4361
|
+
message = JSON.parse(line);
|
|
4362
|
+
} catch {
|
|
4363
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
4364
|
+
return;
|
|
4347
4365
|
}
|
|
4366
|
+
this.onMessage(message);
|
|
4348
4367
|
}
|
|
4349
4368
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
if (
|
|
4353
|
-
|
|
4369
|
+
onMessage(message) {
|
|
4370
|
+
if (message.type === "hello") {
|
|
4371
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
4372
|
+
this.rejectStaleServer(
|
|
4373
|
+
message,
|
|
4374
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
4375
|
+
);
|
|
4376
|
+
return;
|
|
4377
|
+
}
|
|
4378
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
4379
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
4380
|
+
this.rejectStaleServer(
|
|
4381
|
+
message,
|
|
4382
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
4383
|
+
);
|
|
4384
|
+
return;
|
|
4354
4385
|
}
|
|
4386
|
+
this.info = message;
|
|
4387
|
+
this.markResponsive();
|
|
4388
|
+
this.transition("connected", { pid: message.pid });
|
|
4389
|
+
ensureHeartbeatLoop();
|
|
4390
|
+
this.connectResolve?.();
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
if (message.type === "index-state") {
|
|
4394
|
+
this.activity = message.state;
|
|
4395
|
+
this.markResponsive();
|
|
4396
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
const entry = this.pending.get(message.id);
|
|
4400
|
+
if (!entry) return;
|
|
4401
|
+
this.markResponsive();
|
|
4402
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
4403
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
4404
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4405
|
+
}
|
|
4406
|
+
if (message.type === "progress") {
|
|
4407
|
+
entry.onProgress?.(message.current, message.total);
|
|
4408
|
+
return;
|
|
4409
|
+
}
|
|
4410
|
+
this.pending.delete(message.id);
|
|
4411
|
+
this.cleanupPending(entry);
|
|
4412
|
+
if (message.ok) entry.resolve(message.result);
|
|
4413
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
4414
|
+
}
|
|
4415
|
+
onClose(socket) {
|
|
4416
|
+
if (socket !== this.socket) return;
|
|
4417
|
+
const wasConnected = this.info !== null;
|
|
4418
|
+
this.socket = null;
|
|
4419
|
+
this.info = null;
|
|
4420
|
+
this.activity = null;
|
|
4421
|
+
this.health = null;
|
|
4422
|
+
const error = new Error("codebase-index server connection closed");
|
|
4423
|
+
this.connectReject?.(error);
|
|
4424
|
+
this.connectResolve = null;
|
|
4425
|
+
this.connectReject = null;
|
|
4426
|
+
this.rejectPending(error);
|
|
4427
|
+
if (wasConnected) this.transition("error", { error });
|
|
4428
|
+
maybeStopHeartbeatLoop();
|
|
4429
|
+
}
|
|
4430
|
+
cleanupPending(entry) {
|
|
4431
|
+
clearTimeout(entry.timer);
|
|
4432
|
+
if (entry.signal && entry.onAbort) {
|
|
4433
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
4355
4434
|
}
|
|
4356
4435
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4436
|
+
rejectPending(error) {
|
|
4437
|
+
const entries = [...this.pending.values()];
|
|
4438
|
+
this.pending.clear();
|
|
4439
|
+
for (const entry of entries) {
|
|
4440
|
+
this.cleanupPending(entry);
|
|
4441
|
+
entry.reject(error);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
write(message) {
|
|
4445
|
+
const socket = this.socket;
|
|
4446
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
4447
|
+
}
|
|
4448
|
+
rejectStaleServer(message, reason) {
|
|
4449
|
+
const socket = this.socket;
|
|
4450
|
+
if (socket && !socket.destroyed) {
|
|
4451
|
+
socket.write(
|
|
4452
|
+
encodeProjectServerMessage({
|
|
4453
|
+
type: "shutdown",
|
|
4454
|
+
id: 0,
|
|
4455
|
+
reason: "stale-build-replacement"
|
|
4456
|
+
})
|
|
4457
|
+
);
|
|
4458
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
4459
|
+
timer.unref?.();
|
|
4460
|
+
}
|
|
4461
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
4462
|
+
}
|
|
4463
|
+
spawnDetachedServer() {
|
|
4464
|
+
const url = resolveProjectServerUrl();
|
|
4465
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
4466
|
+
if (process.platform !== "win32") {
|
|
4467
|
+
try {
|
|
4468
|
+
fs4.rmSync(this.endpoint, { force: true });
|
|
4469
|
+
} catch {
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
4473
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
4474
|
+
const child = spawn(process.execPath, args, {
|
|
4475
|
+
detached: true,
|
|
4476
|
+
stdio: "ignore",
|
|
4477
|
+
windowsHide: true,
|
|
4478
|
+
env: process.env
|
|
4386
4479
|
});
|
|
4387
|
-
|
|
4388
|
-
indexStorePool.release(store);
|
|
4480
|
+
child.unref();
|
|
4389
4481
|
}
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4482
|
+
forceKillKnownServer() {
|
|
4483
|
+
const pid = this.info?.pid;
|
|
4484
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
4485
|
+
}
|
|
4486
|
+
forceKillServer(pid) {
|
|
4487
|
+
if (pid === process.pid) return false;
|
|
4488
|
+
try {
|
|
4489
|
+
process.kill(pid);
|
|
4490
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
4491
|
+
try {
|
|
4492
|
+
const metadata = JSON.parse(fs4.readFileSync(metadataPath, "utf8"));
|
|
4493
|
+
if (metadata.pid === pid) fs4.rmSync(metadataPath, { force: true });
|
|
4494
|
+
} catch {
|
|
4495
|
+
}
|
|
4496
|
+
return true;
|
|
4497
|
+
} catch {
|
|
4498
|
+
return false;
|
|
4499
|
+
}
|
|
4406
4500
|
}
|
|
4501
|
+
};
|
|
4502
|
+
var connections = /* @__PURE__ */ new Map();
|
|
4503
|
+
var heartbeatTimer;
|
|
4504
|
+
function ensureHeartbeatLoop() {
|
|
4505
|
+
if (heartbeatTimer) return;
|
|
4506
|
+
heartbeatTimer = setInterval(() => {
|
|
4507
|
+
for (const connection of connections.values()) {
|
|
4508
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
4512
|
+
heartbeatTimer.unref?.();
|
|
4407
4513
|
}
|
|
4408
|
-
function
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
indexStorePool.release(store);
|
|
4414
|
-
}
|
|
4514
|
+
function maybeStopHeartbeatLoop() {
|
|
4515
|
+
if (!heartbeatTimer) return;
|
|
4516
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
4517
|
+
clearInterval(heartbeatTimer);
|
|
4518
|
+
heartbeatTimer = void 0;
|
|
4415
4519
|
}
|
|
4416
|
-
function
|
|
4417
|
-
const
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4520
|
+
function connectionFor(projectRoot, indexDir) {
|
|
4521
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4522
|
+
let connection = connections.get(endpoint);
|
|
4523
|
+
if (!connection) {
|
|
4524
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
4525
|
+
connections.set(endpoint, connection);
|
|
4422
4526
|
}
|
|
4527
|
+
return connection;
|
|
4423
4528
|
}
|
|
4424
|
-
function
|
|
4425
|
-
|
|
4426
|
-
try {
|
|
4427
|
-
return store.getFileGraph(args.packageFilter);
|
|
4428
|
-
} finally {
|
|
4429
|
-
indexStorePool.release(store);
|
|
4430
|
-
}
|
|
4529
|
+
function callProjectIndexServer(op, args, options) {
|
|
4530
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
4431
4531
|
}
|
|
4432
|
-
function
|
|
4433
|
-
|
|
4532
|
+
function ensureProjectIndexServer(options) {
|
|
4533
|
+
return connectionFor(options.projectRoot, options.indexDir).configure(
|
|
4534
|
+
options.watchExternal,
|
|
4535
|
+
options.debounceMs
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
function checkProjectIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
4539
|
+
return connectionFor(projectRoot, indexDir).checkHealth(
|
|
4540
|
+
false,
|
|
4541
|
+
options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS
|
|
4542
|
+
);
|
|
4543
|
+
}
|
|
4544
|
+
async function shutdownProjectIndexServer(projectRoot, indexDir, reason) {
|
|
4545
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4546
|
+
const connection = connectionFor(projectRoot, indexDir);
|
|
4434
4547
|
try {
|
|
4435
|
-
return
|
|
4548
|
+
return await connection.shutdownRemote(reason);
|
|
4436
4549
|
} finally {
|
|
4437
|
-
|
|
4550
|
+
connection.close();
|
|
4551
|
+
connections.delete(endpoint);
|
|
4552
|
+
connectionStates.delete(endpoint);
|
|
4438
4553
|
}
|
|
4439
4554
|
}
|
|
4555
|
+
function closeProjectIndexServerClients() {
|
|
4556
|
+
for (const connection of connections.values()) connection.close();
|
|
4557
|
+
connections.clear();
|
|
4558
|
+
connectionStates.clear();
|
|
4559
|
+
latestConnectionState = {
|
|
4560
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
4561
|
+
connected: false
|
|
4562
|
+
};
|
|
4563
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
4564
|
+
heartbeatTimer = void 0;
|
|
4565
|
+
}
|
|
4440
4566
|
|
|
4441
4567
|
// src/codebase-index/background-indexer.ts
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
import { spawn as spawn4 } from "node:child_process";
|
|
4446
|
-
import * as fs10 from "node:fs";
|
|
4447
|
-
import * as net from "node:net";
|
|
4448
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4568
|
+
import * as fs11 from "node:fs";
|
|
4569
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
4570
|
+
import { Worker } from "node:worker_threads";
|
|
4449
4571
|
|
|
4450
|
-
// src/codebase-index/
|
|
4451
|
-
import {
|
|
4452
|
-
import
|
|
4453
|
-
import * as
|
|
4572
|
+
// src/codebase-index/indexer.ts
|
|
4573
|
+
import { expectDefined as expectDefined5 } from "@wrongstack/core/utils";
|
|
4574
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4575
|
+
import * as fs10 from "node:fs/promises";
|
|
4576
|
+
import { availableParallelism } from "node:os";
|
|
4454
4577
|
import * as path12 from "node:path";
|
|
4455
|
-
import {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4578
|
+
import {
|
|
4579
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
4580
|
+
indexParallelBatchSize,
|
|
4581
|
+
isFrugalPerf
|
|
4582
|
+
} from "@wrongstack/core/utils";
|
|
4583
|
+
|
|
4584
|
+
// src/codebase-index/gitignore.ts
|
|
4585
|
+
import * as fs5 from "node:fs/promises";
|
|
4586
|
+
import * as path5 from "node:path";
|
|
4587
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
4588
|
+
function globBody(glob) {
|
|
4589
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
4590
|
+
}
|
|
4591
|
+
function compileGitignore(lines) {
|
|
4592
|
+
const rules = [];
|
|
4593
|
+
for (const raw of lines) {
|
|
4594
|
+
let line = raw.replace(/\r$/, "");
|
|
4595
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
4596
|
+
line = line.trim();
|
|
4597
|
+
let negated = false;
|
|
4598
|
+
if (line.startsWith("!")) {
|
|
4599
|
+
negated = true;
|
|
4600
|
+
line = line.slice(1);
|
|
4465
4601
|
}
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4602
|
+
let dirOnly = false;
|
|
4603
|
+
if (line.endsWith("/")) {
|
|
4604
|
+
dirOnly = true;
|
|
4605
|
+
line = line.slice(0, -1);
|
|
4606
|
+
}
|
|
4607
|
+
if (!line) continue;
|
|
4608
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
4609
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
4610
|
+
const body = globBody(line);
|
|
4611
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
4612
|
+
rules.push({
|
|
4613
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
4614
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
4615
|
+
negated,
|
|
4616
|
+
dirOnly
|
|
4617
|
+
});
|
|
4471
4618
|
}
|
|
4619
|
+
return (relPath, isDir) => {
|
|
4620
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
4621
|
+
let ignored = false;
|
|
4622
|
+
for (const r of rules) {
|
|
4623
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
4624
|
+
if (re.test(p)) ignored = !r.negated;
|
|
4625
|
+
}
|
|
4626
|
+
return ignored;
|
|
4627
|
+
};
|
|
4472
4628
|
}
|
|
4473
|
-
function
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
return createHash("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
4480
|
-
}
|
|
4481
|
-
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
4482
|
-
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
4483
|
-
if (process.platform === "win32") {
|
|
4484
|
-
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
4629
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
4630
|
+
let lines = [];
|
|
4631
|
+
try {
|
|
4632
|
+
const raw = await fs5.readFile(path5.join(projectRoot, ".gitignore"), "utf8");
|
|
4633
|
+
lines = raw.split("\n");
|
|
4634
|
+
} catch {
|
|
4485
4635
|
}
|
|
4486
|
-
return
|
|
4487
|
-
os3.tmpdir(),
|
|
4488
|
-
`wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`,
|
|
4489
|
-
`${key}.sock`
|
|
4490
|
-
);
|
|
4491
|
-
}
|
|
4492
|
-
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
4493
|
-
return path12.join(
|
|
4494
|
-
path12.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
4495
|
-
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
4496
|
-
);
|
|
4636
|
+
return compileGitignore(lines);
|
|
4497
4637
|
}
|
|
4498
4638
|
|
|
4499
|
-
// src/codebase-index/
|
|
4500
|
-
|
|
4501
|
-
function encodeProjectServerMessage(message) {
|
|
4502
|
-
return `${JSON.stringify(message)}
|
|
4503
|
-
`;
|
|
4504
|
-
}
|
|
4639
|
+
// src/codebase-index/indexer.ts
|
|
4640
|
+
init_languages();
|
|
4505
4641
|
|
|
4506
|
-
// src/codebase-index/
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
}
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
};
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4642
|
+
// src/codebase-index/parser-dispatch.ts
|
|
4643
|
+
async function parseFileContent(file, content, lang) {
|
|
4644
|
+
switch (lang) {
|
|
4645
|
+
case "ts":
|
|
4646
|
+
case "tsx":
|
|
4647
|
+
case "js":
|
|
4648
|
+
case "jsx": {
|
|
4649
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
4650
|
+
return parseSymbols8({ file, content, lang });
|
|
4651
|
+
}
|
|
4652
|
+
case "go": {
|
|
4653
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
4654
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
4655
|
+
}
|
|
4656
|
+
case "py": {
|
|
4657
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
4658
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
4659
|
+
}
|
|
4660
|
+
case "rs": {
|
|
4661
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
4662
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
4663
|
+
}
|
|
4664
|
+
case "json": {
|
|
4665
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
4666
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
4667
|
+
}
|
|
4668
|
+
case "yaml": {
|
|
4669
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
4670
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
4671
|
+
}
|
|
4672
|
+
default: {
|
|
4673
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
4674
|
+
return parseSymbols8({ file, content, lang });
|
|
4537
4675
|
}
|
|
4538
4676
|
}
|
|
4539
|
-
return { kind: "missing-build" };
|
|
4540
|
-
}
|
|
4541
|
-
function resolveProjectServerUrl() {
|
|
4542
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
4543
|
-
return availability.kind === "available" ? availability.url : null;
|
|
4544
|
-
}
|
|
4545
|
-
function projectIndexServerExpectedBuildId() {
|
|
4546
|
-
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
4547
|
-
if (override) return override;
|
|
4548
|
-
const url = resolveProjectServerUrl();
|
|
4549
|
-
return url ? projectIndexServerBuildId(url) : null;
|
|
4550
4677
|
}
|
|
4551
|
-
|
|
4552
|
-
|
|
4678
|
+
|
|
4679
|
+
// src/codebase-index/indexer.ts
|
|
4680
|
+
var YIELD_EVERY_N = 50;
|
|
4681
|
+
function resolveParallelBatch() {
|
|
4682
|
+
return indexParallelBatchSize(availableParallelism());
|
|
4553
4683
|
}
|
|
4554
|
-
function
|
|
4555
|
-
|
|
4556
|
-
latestConnectionState = state;
|
|
4557
|
-
for (const listener of connectionStateListeners) listener(state);
|
|
4684
|
+
function yieldEventLoop() {
|
|
4685
|
+
return new Promise((resolve4) => setImmediate(resolve4));
|
|
4558
4686
|
}
|
|
4559
|
-
function
|
|
4560
|
-
if (
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
if (existing) return existing;
|
|
4564
|
-
if (!isProjectIndexServerAvailable()) {
|
|
4565
|
-
return { status: "unavailable", connected: false };
|
|
4566
|
-
}
|
|
4567
|
-
return {
|
|
4568
|
-
status: "offline",
|
|
4569
|
-
connected: false,
|
|
4570
|
-
projectRoot,
|
|
4571
|
-
indexDir,
|
|
4572
|
-
endpoint
|
|
4573
|
-
};
|
|
4574
|
-
}
|
|
4575
|
-
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
4576
|
-
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
4577
|
-
return latestConnectionState;
|
|
4687
|
+
function throwIfAborted(signal) {
|
|
4688
|
+
if (!signal?.aborted) return;
|
|
4689
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
4690
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
4578
4691
|
}
|
|
4579
|
-
function
|
|
4580
|
-
|
|
4581
|
-
return () => connectionStateListeners.delete(listener);
|
|
4692
|
+
function isAbortError(err) {
|
|
4693
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
4582
4694
|
}
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4695
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
4696
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
4697
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
4698
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
4699
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
4700
|
+
function isWithinProject(projectRoot, file) {
|
|
4701
|
+
const rel = path12.relative(projectRoot, file);
|
|
4702
|
+
return rel !== "" && !rel.startsWith(`..${path12.sep}`) && rel !== ".." && !path12.isAbsolute(rel);
|
|
4589
4703
|
}
|
|
4590
|
-
function
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
4594
|
-
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
4595
|
-
return typeof health.checkedAt === "number" && typeof health.uptimeMs === "number" && typeof memory?.rss === "number" && typeof memory.heapUsed === "number" && typeof memory.heapTotal === "number" && typeof memory.external === "number" && typeof health.clients === "number" && typeof health.activeRequests === "number" && typeof health.activeWrites === "number" && typeof health.queuedWrites === "number" && typeof health.pendingExternalFiles === "number" && typeof health.watchingExternal === "boolean" && typeof activity?.indexing === "boolean" && typeof activity.currentFile === "number" && typeof activity.totalFiles === "number" && typeof activity.generation === "number";
|
|
4704
|
+
function isMissingPathError(err) {
|
|
4705
|
+
const code = err?.code;
|
|
4706
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
4596
4707
|
}
|
|
4597
|
-
function
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
timer.unref?.();
|
|
4601
|
-
});
|
|
4708
|
+
function normalizeComparablePath(value) {
|
|
4709
|
+
const resolved = path12.resolve(value);
|
|
4710
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
4602
4711
|
}
|
|
4603
|
-
function
|
|
4604
|
-
return
|
|
4712
|
+
function gitOutput(projectRoot, args) {
|
|
4713
|
+
return new Promise((resolve4, reject) => {
|
|
4714
|
+
execFile2(
|
|
4715
|
+
"git",
|
|
4716
|
+
["-C", projectRoot, ...args],
|
|
4717
|
+
{
|
|
4718
|
+
encoding: "buffer",
|
|
4719
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
4720
|
+
windowsHide: true
|
|
4721
|
+
},
|
|
4722
|
+
(error, stdout) => {
|
|
4723
|
+
if (error) reject(error);
|
|
4724
|
+
else resolve4(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
4725
|
+
}
|
|
4726
|
+
);
|
|
4727
|
+
});
|
|
4605
4728
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
lastError,
|
|
4639
|
-
...this.activity ? { activity: this.activity } : {},
|
|
4640
|
-
...this.health ? { health: this.health } : {}
|
|
4641
|
-
});
|
|
4642
|
-
}
|
|
4643
|
-
isConnected() {
|
|
4644
|
-
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
4645
|
-
}
|
|
4646
|
-
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
4647
|
-
await this.ensureConnected(spawnIfMissing);
|
|
4648
|
-
if (this.healthCheck) return this.healthCheck;
|
|
4649
|
-
const startedAt = Date.now();
|
|
4650
|
-
this.healthCheck = this.request({ type: "ping" }, { timeoutMs }).then((server) => {
|
|
4651
|
-
const now = Date.now();
|
|
4652
|
-
this.health = {
|
|
4653
|
-
status: "healthy",
|
|
4654
|
-
checkedAt: now,
|
|
4655
|
-
lastHealthyAt: now,
|
|
4656
|
-
latencyMs: Math.max(0, now - startedAt),
|
|
4657
|
-
missedHeartbeats: 0,
|
|
4658
|
-
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
4659
|
-
};
|
|
4660
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
4661
|
-
return this.health;
|
|
4662
|
-
}).catch((error) => {
|
|
4663
|
-
if (!this.isConnected()) throw error;
|
|
4664
|
-
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
4665
|
-
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
4666
|
-
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
4667
|
-
this.health = {
|
|
4668
|
-
status,
|
|
4669
|
-
checkedAt: Date.now(),
|
|
4670
|
-
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
4671
|
-
latencyMs: null,
|
|
4672
|
-
missedHeartbeats,
|
|
4673
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
4674
|
-
};
|
|
4675
|
-
this.transition(status, { pid: this.info?.pid, error });
|
|
4676
|
-
return this.health;
|
|
4677
|
-
}).finally(() => {
|
|
4678
|
-
this.healthCheck = null;
|
|
4679
|
-
});
|
|
4680
|
-
return this.healthCheck;
|
|
4681
|
-
}
|
|
4682
|
-
markResponsive() {
|
|
4683
|
-
const now = Date.now();
|
|
4684
|
-
this.health = {
|
|
4685
|
-
status: "healthy",
|
|
4686
|
-
checkedAt: now,
|
|
4687
|
-
lastHealthyAt: now,
|
|
4688
|
-
latencyMs: this.health?.latencyMs ?? null,
|
|
4689
|
-
missedHeartbeats: 0,
|
|
4690
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
4691
|
-
};
|
|
4692
|
-
}
|
|
4693
|
-
async call(op, args, options) {
|
|
4694
|
-
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
4695
|
-
await this.ensureConnected(true);
|
|
4696
|
-
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
4697
|
-
return this.request({ type: "request", op, args }, options);
|
|
4698
|
-
}
|
|
4699
|
-
async shutdownRemote(reason) {
|
|
4700
|
-
try {
|
|
4701
|
-
await this.ensureConnected(false);
|
|
4702
|
-
} catch {
|
|
4703
|
-
return { stopped: false, reason: "not-running" };
|
|
4704
|
-
}
|
|
4705
|
-
const pid = this.info?.pid;
|
|
4706
|
-
try {
|
|
4707
|
-
this.transition("stopping", { pid });
|
|
4708
|
-
await this.request(
|
|
4709
|
-
{ type: "shutdown", reason },
|
|
4710
|
-
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
4711
|
-
);
|
|
4712
|
-
return { stopped: true, pid };
|
|
4713
|
-
} catch (error) {
|
|
4714
|
-
const forceKilled = this.forceKillKnownServer();
|
|
4715
|
-
return {
|
|
4716
|
-
stopped: forceKilled,
|
|
4717
|
-
pid,
|
|
4718
|
-
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
4719
|
-
};
|
|
4720
|
-
} finally {
|
|
4721
|
-
this.close();
|
|
4729
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
4730
|
+
try {
|
|
4731
|
+
throwIfAborted(signal);
|
|
4732
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
4733
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
4734
|
+
throwIfAborted(signal);
|
|
4735
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4736
|
+
const [output, statusOutput] = await Promise.all([
|
|
4737
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
4738
|
+
gitOutput(projectRoot, [
|
|
4739
|
+
"status",
|
|
4740
|
+
"--porcelain=v1",
|
|
4741
|
+
"-z",
|
|
4742
|
+
"--untracked-files=all",
|
|
4743
|
+
"--ignored=no"
|
|
4744
|
+
])
|
|
4745
|
+
]);
|
|
4746
|
+
throwIfAborted(signal);
|
|
4747
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
4748
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
4749
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
4750
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
4751
|
+
const record = statusRecords[i];
|
|
4752
|
+
if (!record) continue;
|
|
4753
|
+
const status = record.slice(0, 2);
|
|
4754
|
+
const changedPath = path12.resolve(projectRoot, record.slice(3));
|
|
4755
|
+
dirty.add(changedPath);
|
|
4756
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
4757
|
+
if (status.includes("R") || status.includes("C")) {
|
|
4758
|
+
const source = statusRecords[++i];
|
|
4759
|
+
if (source) dirty.add(path12.resolve(projectRoot, source));
|
|
4760
|
+
}
|
|
4722
4761
|
}
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
const
|
|
4733
|
-
|
|
4734
|
-
status: "healthy",
|
|
4735
|
-
checkedAt: now,
|
|
4736
|
-
lastHealthyAt: now,
|
|
4737
|
-
latencyMs: Math.max(0, now - startedAt),
|
|
4738
|
-
missedHeartbeats: 0,
|
|
4739
|
-
server: result.health
|
|
4740
|
-
};
|
|
4741
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
4762
|
+
const files = [];
|
|
4763
|
+
for (const relative2 of output.toString("utf8").split("\0")) {
|
|
4764
|
+
if (!relative2) continue;
|
|
4765
|
+
const portable = relative2.replace(/\\/g, "/");
|
|
4766
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path12.posix.basename(portable))) {
|
|
4767
|
+
continue;
|
|
4768
|
+
}
|
|
4769
|
+
const full = path12.resolve(projectRoot, relative2);
|
|
4770
|
+
if (deleted.has(full)) continue;
|
|
4771
|
+
const ext = path12.extname(relative2).toLowerCase();
|
|
4772
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
4742
4773
|
}
|
|
4774
|
+
return {
|
|
4775
|
+
files,
|
|
4776
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
4777
|
+
};
|
|
4778
|
+
} catch {
|
|
4779
|
+
return null;
|
|
4743
4780
|
}
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
4755
|
-
this.transition("offline");
|
|
4756
|
-
maybeStopHeartbeatLoop();
|
|
4781
|
+
}
|
|
4782
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
4783
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
4784
|
+
if (gitFiles) {
|
|
4785
|
+
return {
|
|
4786
|
+
files: gitFiles.files,
|
|
4787
|
+
complete: true,
|
|
4788
|
+
errors: [],
|
|
4789
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
4790
|
+
};
|
|
4757
4791
|
}
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4792
|
+
const results = [];
|
|
4793
|
+
const errors = [];
|
|
4794
|
+
let complete = true;
|
|
4795
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4796
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
4797
|
+
let dirCount = 0;
|
|
4798
|
+
const walk = async (dir) => {
|
|
4799
|
+
throwIfAborted(signal);
|
|
4800
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
4801
|
+
await yieldEventLoop();
|
|
4802
|
+
throwIfAborted(signal);
|
|
4762
4803
|
}
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
if (
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
entry.reject(cancellationError(signal));
|
|
4785
|
-
} : void 0;
|
|
4786
|
-
this.pending.set(id, {
|
|
4787
|
-
resolve: resolve4,
|
|
4788
|
-
reject,
|
|
4789
|
-
timer,
|
|
4790
|
-
signal,
|
|
4791
|
-
onAbort,
|
|
4792
|
-
onProgress: options.onProgress
|
|
4793
|
-
});
|
|
4794
|
-
if (signal && onAbort) {
|
|
4795
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
4796
|
-
if (signal.aborted) {
|
|
4797
|
-
onAbort();
|
|
4798
|
-
return;
|
|
4804
|
+
let entries;
|
|
4805
|
+
try {
|
|
4806
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
4807
|
+
} catch (err) {
|
|
4808
|
+
complete = false;
|
|
4809
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4810
|
+
return;
|
|
4811
|
+
}
|
|
4812
|
+
dirCount++;
|
|
4813
|
+
for (const e of entries) {
|
|
4814
|
+
if (ignoreSet.has(e.name)) continue;
|
|
4815
|
+
const full = path12.join(dir, e.name);
|
|
4816
|
+
const rel = path12.relative(projectRoot, full).replace(/\\/g, "/");
|
|
4817
|
+
if (e.isDirectory()) {
|
|
4818
|
+
if (isGitIgnored(rel, true)) continue;
|
|
4819
|
+
await walk(full);
|
|
4820
|
+
} else if (e.isFile()) {
|
|
4821
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
4822
|
+
const ext = path12.extname(e.name).toLowerCase();
|
|
4823
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
4824
|
+
results.push(full);
|
|
4799
4825
|
}
|
|
4800
4826
|
}
|
|
4801
|
-
|
|
4827
|
+
}
|
|
4828
|
+
};
|
|
4829
|
+
await walk(projectRoot);
|
|
4830
|
+
return { files: results, complete, errors };
|
|
4831
|
+
}
|
|
4832
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
4833
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
4834
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
4835
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4836
|
+
const assigned = [];
|
|
4837
|
+
for (const ref of refs) {
|
|
4838
|
+
let owner;
|
|
4839
|
+
for (const symbol of ordered) {
|
|
4840
|
+
if (symbol.line > ref.line) break;
|
|
4841
|
+
owner = symbol;
|
|
4842
|
+
}
|
|
4843
|
+
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
4844
|
+
if (!owner || owner.id <= 0) continue;
|
|
4845
|
+
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
4846
|
+
if (seen.has(key)) continue;
|
|
4847
|
+
seen.add(key);
|
|
4848
|
+
assigned.push({ ...ref, fromId: owner.id });
|
|
4849
|
+
}
|
|
4850
|
+
return assigned;
|
|
4851
|
+
}
|
|
4852
|
+
async function runIndexer(_ctx, opts) {
|
|
4853
|
+
const store = new IndexStore(opts.projectRoot, { indexDir: opts.indexDir });
|
|
4854
|
+
try {
|
|
4855
|
+
return await runIndexerWithStore(store, opts);
|
|
4856
|
+
} finally {
|
|
4857
|
+
try {
|
|
4858
|
+
store.close();
|
|
4859
|
+
} catch {
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
async function runIndexerWithStore(store, opts) {
|
|
4864
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
4865
|
+
const relationGraphVersion = "2";
|
|
4866
|
+
const refResolutionVersion = "2";
|
|
4867
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
4868
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
4869
|
+
const startMs = Date.now();
|
|
4870
|
+
const errors = [];
|
|
4871
|
+
const langStats = {};
|
|
4872
|
+
let filesIndexed = 0;
|
|
4873
|
+
let symbolsIndexed = 0;
|
|
4874
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
4875
|
+
let files;
|
|
4876
|
+
let discoveredFiles = null;
|
|
4877
|
+
let discoveryComplete = true;
|
|
4878
|
+
let trustedUnchanged;
|
|
4879
|
+
if (opts.files && opts.files.length > 0) {
|
|
4880
|
+
files = opts.files.map((f) => path12.resolve(projectRoot, f)).filter((f) => {
|
|
4881
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
4882
|
+
const rel = path12.relative(projectRoot, f).replace(/\\/g, "/");
|
|
4883
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path12.basename(f)) && !isGitIgnored(rel, false);
|
|
4802
4884
|
});
|
|
4885
|
+
} else {
|
|
4886
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
4887
|
+
files = discovery.files;
|
|
4888
|
+
errors.push(...discovery.errors);
|
|
4889
|
+
discoveryComplete = discovery.complete;
|
|
4890
|
+
discoveredFiles = new Set(files);
|
|
4891
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
4803
4892
|
}
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
this.transition("error", { error });
|
|
4810
|
-
throw error;
|
|
4811
|
-
}).finally(() => {
|
|
4812
|
-
this.connecting = null;
|
|
4893
|
+
if (langs && langs.length > 0) {
|
|
4894
|
+
const langSet = new Set(langs);
|
|
4895
|
+
files = files.filter((f) => {
|
|
4896
|
+
const lang = detectLang(f);
|
|
4897
|
+
return lang ? langSet.has(lang) : false;
|
|
4813
4898
|
});
|
|
4814
|
-
return this.connecting;
|
|
4815
4899
|
}
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
let lastError = new Error("codebase-index server unavailable");
|
|
4821
|
-
while (Date.now() < deadline) {
|
|
4822
|
-
try {
|
|
4823
|
-
await this.connectOnce();
|
|
4824
|
-
return;
|
|
4825
|
-
} catch (error) {
|
|
4826
|
-
lastError = error;
|
|
4827
|
-
if (error instanceof StaleProjectIndexServerError) {
|
|
4828
|
-
staleAttempts++;
|
|
4829
|
-
if (!spawnIfMissing) break;
|
|
4830
|
-
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
4831
|
-
spawned = false;
|
|
4832
|
-
await delay(100);
|
|
4833
|
-
continue;
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
if (!spawnIfMissing) break;
|
|
4837
|
-
if (!spawned) {
|
|
4838
|
-
this.spawnDetachedServer();
|
|
4839
|
-
spawned = true;
|
|
4840
|
-
}
|
|
4841
|
-
await delay(75);
|
|
4842
|
-
}
|
|
4843
|
-
throw lastError;
|
|
4900
|
+
if (force) store.clearAll();
|
|
4901
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
4902
|
+
if (!force) {
|
|
4903
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
4844
4904
|
}
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
const timer = setTimeout(() => {
|
|
4857
|
-
reject(new Error("codebase-index server handshake timed out"));
|
|
4858
|
-
socket.destroy();
|
|
4859
|
-
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4860
|
-
timer.unref?.();
|
|
4861
|
-
const finishResolve = () => {
|
|
4862
|
-
clearTimeout(timer);
|
|
4863
|
-
this.connectResolve = null;
|
|
4864
|
-
this.connectReject = null;
|
|
4865
|
-
resolve4();
|
|
4866
|
-
};
|
|
4867
|
-
const finishReject = (error) => {
|
|
4868
|
-
clearTimeout(timer);
|
|
4869
|
-
this.connectResolve = null;
|
|
4870
|
-
this.connectReject = null;
|
|
4871
|
-
reject(error);
|
|
4872
|
-
};
|
|
4873
|
-
this.connectResolve = finishResolve;
|
|
4874
|
-
this.connectReject = finishReject;
|
|
4875
|
-
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
4876
|
-
socket.on("error", (error) => {
|
|
4877
|
-
if (!this.info) finishReject(error);
|
|
4878
|
-
});
|
|
4879
|
-
socket.on("close", () => this.onClose(socket));
|
|
4905
|
+
const totalFilesForProgress = files.length;
|
|
4906
|
+
let filesPreSkipped = 0;
|
|
4907
|
+
if (!force && trustedUnchanged) {
|
|
4908
|
+
files = files.filter((file) => {
|
|
4909
|
+
const meta = existingMeta.get(file);
|
|
4910
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
4911
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
4912
|
+
symbolsIndexed += meta.symbolCount;
|
|
4913
|
+
filesIndexed++;
|
|
4914
|
+
filesPreSkipped++;
|
|
4915
|
+
return false;
|
|
4880
4916
|
});
|
|
4917
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
4881
4918
|
}
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4919
|
+
const parallelBatch = resolveParallelBatch();
|
|
4920
|
+
let filesSinceLastYield = 0;
|
|
4921
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
4922
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
4923
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
4924
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
4925
|
+
filesSinceLastYield += batchFiles.length;
|
|
4926
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
4927
|
+
filesSinceLastYield = 0;
|
|
4928
|
+
await yieldEventLoop();
|
|
4929
|
+
if (isFrugalPerf()) {
|
|
4930
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
4931
|
+
}
|
|
4932
|
+
throwIfAborted(signal);
|
|
4933
|
+
}
|
|
4934
|
+
const statOpts = signal ? { signal } : {};
|
|
4935
|
+
const statReadParse = await Promise.allSettled(
|
|
4936
|
+
batchFiles.map(
|
|
4937
|
+
async (file) => {
|
|
4938
|
+
let stat2;
|
|
4939
|
+
try {
|
|
4940
|
+
stat2 = await fs10.stat(file, statOpts);
|
|
4941
|
+
} catch (e) {
|
|
4942
|
+
if (isAbortError(e)) throw e;
|
|
4943
|
+
return {
|
|
4944
|
+
file,
|
|
4945
|
+
stat: null,
|
|
4946
|
+
lang: "",
|
|
4947
|
+
parsed: null,
|
|
4948
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
4949
|
+
missing: isMissingPathError(e)
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
if (!stat2.isFile()) return { file, stat: stat2, lang: "", parsed: null };
|
|
4953
|
+
const lang = detectLang(file);
|
|
4954
|
+
if (!lang) return { file, stat: stat2, lang: "", parsed: null };
|
|
4955
|
+
if (stat2.size > MAX_INDEX_FILE_BYTES) {
|
|
4956
|
+
return {
|
|
4957
|
+
file,
|
|
4958
|
+
stat: stat2,
|
|
4959
|
+
lang,
|
|
4960
|
+
parsed: null,
|
|
4961
|
+
error: `file too large (${stat2.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
const meta = existingMeta.get(file);
|
|
4965
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
|
|
4966
|
+
return { file, stat: stat2, lang, parsed: null, skippedMeta: meta };
|
|
4967
|
+
}
|
|
4968
|
+
let content;
|
|
4969
|
+
try {
|
|
4970
|
+
content = await fs10.readFile(file, { encoding: "utf8", signal });
|
|
4971
|
+
} catch (e) {
|
|
4972
|
+
if (isAbortError(e)) throw e;
|
|
4973
|
+
return {
|
|
4974
|
+
file,
|
|
4975
|
+
stat: stat2,
|
|
4976
|
+
lang,
|
|
4977
|
+
parsed: null,
|
|
4978
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
4979
|
+
};
|
|
4980
|
+
}
|
|
4981
|
+
let parsed;
|
|
4982
|
+
try {
|
|
4983
|
+
parsed = await parseFileContent(file, content, lang);
|
|
4984
|
+
} catch (e) {
|
|
4985
|
+
return {
|
|
4986
|
+
file,
|
|
4987
|
+
stat: stat2,
|
|
4988
|
+
lang,
|
|
4989
|
+
parsed: null,
|
|
4990
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
4991
|
+
};
|
|
4992
|
+
}
|
|
4993
|
+
return { file, stat: stat2, lang, parsed, content };
|
|
4890
4994
|
}
|
|
4891
|
-
|
|
4995
|
+
)
|
|
4996
|
+
);
|
|
4997
|
+
const batchEntries = [];
|
|
4998
|
+
const deleteForFiles = [];
|
|
4999
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
5000
|
+
const settled = statReadParse[fi];
|
|
5001
|
+
const file = expectDefined5(batchFiles[fi]);
|
|
5002
|
+
if (settled.status === "rejected") {
|
|
5003
|
+
const err = settled.reason;
|
|
5004
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
5005
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5006
|
+
continue;
|
|
4892
5007
|
}
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
5008
|
+
const result = settled.value;
|
|
5009
|
+
if (result.error) {
|
|
5010
|
+
if (result.missing) store.deleteFile(file);
|
|
5011
|
+
errors.push(`${file}: ${result.error}`);
|
|
5012
|
+
continue;
|
|
4896
5013
|
}
|
|
4897
|
-
const
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
} catch {
|
|
4904
|
-
socket.destroy(new Error("invalid codebase-index server response"));
|
|
4905
|
-
return;
|
|
5014
|
+
const { stat: stat2, lang, parsed } = result;
|
|
5015
|
+
if (result.skippedMeta) {
|
|
5016
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
5017
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
5018
|
+
filesIndexed++;
|
|
5019
|
+
continue;
|
|
4906
5020
|
}
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
5021
|
+
if (!lang || !parsed) {
|
|
5022
|
+
if (lang) {
|
|
5023
|
+
store.upsertFile({
|
|
5024
|
+
file,
|
|
5025
|
+
lang,
|
|
5026
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5027
|
+
symbolCount: 0,
|
|
5028
|
+
lastIndexed: Date.now()
|
|
5029
|
+
});
|
|
5030
|
+
filesIndexed++;
|
|
5031
|
+
}
|
|
5032
|
+
continue;
|
|
4918
5033
|
}
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
5034
|
+
if (parsed.symbols.length === 0) {
|
|
5035
|
+
store.replaceEmptyFile({
|
|
5036
|
+
file,
|
|
5037
|
+
lang,
|
|
5038
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5039
|
+
symbolCount: 0,
|
|
5040
|
+
lastIndexed: Date.now()
|
|
5041
|
+
});
|
|
5042
|
+
filesIndexed++;
|
|
5043
|
+
continue;
|
|
4926
5044
|
}
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
this.markResponsive();
|
|
4937
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
4938
|
-
return;
|
|
4939
|
-
}
|
|
4940
|
-
const entry = this.pending.get(message.id);
|
|
4941
|
-
if (!entry) return;
|
|
4942
|
-
this.markResponsive();
|
|
4943
|
-
const status = connectionStates.get(this.endpoint)?.status;
|
|
4944
|
-
if (status === "degraded" || status === "unresponsive") {
|
|
4945
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
4946
|
-
}
|
|
4947
|
-
if (message.type === "progress") {
|
|
4948
|
-
entry.onProgress?.(message.current, message.total);
|
|
4949
|
-
return;
|
|
4950
|
-
}
|
|
4951
|
-
this.pending.delete(message.id);
|
|
4952
|
-
this.cleanupPending(entry);
|
|
4953
|
-
if (message.ok) entry.resolve(message.result);
|
|
4954
|
-
else entry.reject(remoteError(message.error, message.errorName));
|
|
4955
|
-
}
|
|
4956
|
-
onClose(socket) {
|
|
4957
|
-
if (socket !== this.socket) return;
|
|
4958
|
-
const wasConnected = this.info !== null;
|
|
4959
|
-
this.socket = null;
|
|
4960
|
-
this.info = null;
|
|
4961
|
-
this.activity = null;
|
|
4962
|
-
this.health = null;
|
|
4963
|
-
const error = new Error("codebase-index server connection closed");
|
|
4964
|
-
this.connectReject?.(error);
|
|
4965
|
-
this.connectResolve = null;
|
|
4966
|
-
this.connectReject = null;
|
|
4967
|
-
this.rejectPending(error);
|
|
4968
|
-
if (wasConnected) this.transition("error", { error });
|
|
4969
|
-
maybeStopHeartbeatLoop();
|
|
4970
|
-
}
|
|
4971
|
-
cleanupPending(entry) {
|
|
4972
|
-
clearTimeout(entry.timer);
|
|
4973
|
-
if (entry.signal && entry.onAbort) {
|
|
4974
|
-
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
4975
|
-
}
|
|
4976
|
-
}
|
|
4977
|
-
rejectPending(error) {
|
|
4978
|
-
const entries = [...this.pending.values()];
|
|
4979
|
-
this.pending.clear();
|
|
4980
|
-
for (const entry of entries) {
|
|
4981
|
-
this.cleanupPending(entry);
|
|
4982
|
-
entry.reject(error);
|
|
4983
|
-
}
|
|
4984
|
-
}
|
|
4985
|
-
write(message) {
|
|
4986
|
-
const socket = this.socket;
|
|
4987
|
-
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
4988
|
-
}
|
|
4989
|
-
rejectStaleServer(message, reason) {
|
|
4990
|
-
const socket = this.socket;
|
|
4991
|
-
if (socket && !socket.destroyed) {
|
|
4992
|
-
socket.write(
|
|
4993
|
-
encodeProjectServerMessage({
|
|
4994
|
-
type: "shutdown",
|
|
4995
|
-
id: 0,
|
|
4996
|
-
reason: "stale-build-replacement"
|
|
4997
|
-
})
|
|
4998
|
-
);
|
|
4999
|
-
const timer = setTimeout(() => socket.destroy(), 25);
|
|
5000
|
-
timer.unref?.();
|
|
5045
|
+
batchEntries.push({
|
|
5046
|
+
file,
|
|
5047
|
+
lang,
|
|
5048
|
+
symbols: parsed.symbols,
|
|
5049
|
+
refs: parsed.refs ?? [],
|
|
5050
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5051
|
+
symbolCount: parsed.symbols.length
|
|
5052
|
+
});
|
|
5053
|
+
deleteForFiles.push(file);
|
|
5001
5054
|
}
|
|
5002
|
-
|
|
5003
|
-
}
|
|
5004
|
-
spawnDetachedServer() {
|
|
5005
|
-
const url = resolveProjectServerUrl();
|
|
5006
|
-
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
5007
|
-
if (process.platform !== "win32") {
|
|
5055
|
+
if (batchEntries.length > 0) {
|
|
5008
5056
|
try {
|
|
5009
|
-
|
|
5010
|
-
|
|
5057
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
5058
|
+
for (const entry of batchEntries) {
|
|
5059
|
+
const count = entry.symbols.length;
|
|
5060
|
+
symbolsIndexed += count;
|
|
5061
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
5062
|
+
filesIndexed++;
|
|
5063
|
+
}
|
|
5064
|
+
} catch (err) {
|
|
5065
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5066
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
5067
|
+
for (const entry of batchEntries) {
|
|
5068
|
+
try {
|
|
5069
|
+
store.deleteRefsForFile(entry.file);
|
|
5070
|
+
store.deleteSymbolsForFile(entry.file);
|
|
5071
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
5072
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
5073
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
5074
|
+
filesIndexed++;
|
|
5075
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
5076
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
5077
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
5078
|
+
}
|
|
5079
|
+
store.resolveRefsForNames([
|
|
5080
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
5081
|
+
...entry.refs.map((ref) => ref.toName)
|
|
5082
|
+
]);
|
|
5083
|
+
store.upsertFile({
|
|
5084
|
+
file: entry.file,
|
|
5085
|
+
lang: entry.lang,
|
|
5086
|
+
mtimeMs: entry.mtimeMs,
|
|
5087
|
+
symbolCount: entry.symbolCount,
|
|
5088
|
+
lastIndexed: Date.now()
|
|
5089
|
+
});
|
|
5090
|
+
} catch (innerErr) {
|
|
5091
|
+
errors.push(
|
|
5092
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
5093
|
+
);
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5011
5096
|
}
|
|
5012
5097
|
}
|
|
5013
|
-
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
5014
|
-
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
5015
|
-
const child = spawn4(process.execPath, args, {
|
|
5016
|
-
detached: true,
|
|
5017
|
-
stdio: "ignore",
|
|
5018
|
-
windowsHide: true,
|
|
5019
|
-
env: process.env
|
|
5020
|
-
});
|
|
5021
|
-
child.unref();
|
|
5022
|
-
}
|
|
5023
|
-
forceKillKnownServer() {
|
|
5024
|
-
const pid = this.info?.pid;
|
|
5025
|
-
return pid ? this.forceKillServer(pid) : false;
|
|
5026
5098
|
}
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
5032
|
-
try {
|
|
5033
|
-
const metadata = JSON.parse(fs10.readFileSync(metadataPath, "utf8"));
|
|
5034
|
-
if (metadata.pid === pid) fs10.rmSync(metadataPath, { force: true });
|
|
5035
|
-
} catch {
|
|
5099
|
+
if (discoveredFiles && discoveryComplete) {
|
|
5100
|
+
for (const [file_] of existingMeta) {
|
|
5101
|
+
if (!discoveredFiles.has(file_)) {
|
|
5102
|
+
store.deleteFile(file_);
|
|
5036
5103
|
}
|
|
5037
|
-
return true;
|
|
5038
|
-
} catch {
|
|
5039
|
-
return false;
|
|
5040
5104
|
}
|
|
5041
5105
|
}
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
if (!heartbeatTimer) return;
|
|
5057
|
-
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
5058
|
-
clearInterval(heartbeatTimer);
|
|
5059
|
-
heartbeatTimer = void 0;
|
|
5106
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
5107
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
5108
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
5109
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
5110
|
+
store.setLastIndexed(Date.now());
|
|
5111
|
+
if (!opts.files) store.compactIfNeeded();
|
|
5112
|
+
const durationMs = Date.now() - startMs;
|
|
5113
|
+
return {
|
|
5114
|
+
filesIndexed,
|
|
5115
|
+
symbolsIndexed,
|
|
5116
|
+
langStats,
|
|
5117
|
+
durationMs,
|
|
5118
|
+
errors
|
|
5119
|
+
};
|
|
5060
5120
|
}
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5121
|
+
|
|
5122
|
+
// src/codebase-index/index-service.ts
|
|
5123
|
+
async function indexService(args, hooks = {}) {
|
|
5124
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5125
|
+
try {
|
|
5126
|
+
return await runIndexerWithStore(store, {
|
|
5127
|
+
projectRoot: args.projectRoot,
|
|
5128
|
+
indexDir: args.indexDir,
|
|
5129
|
+
files: args.files,
|
|
5130
|
+
force: args.force,
|
|
5131
|
+
langs: args.langs,
|
|
5132
|
+
ignore: args.ignore,
|
|
5133
|
+
signal: hooks.signal,
|
|
5134
|
+
onProgress: hooks.onProgress
|
|
5135
|
+
});
|
|
5136
|
+
} finally {
|
|
5137
|
+
indexStorePool.release(store);
|
|
5067
5138
|
}
|
|
5068
|
-
return connection;
|
|
5069
5139
|
}
|
|
5070
|
-
function
|
|
5071
|
-
|
|
5140
|
+
function searchService(args) {
|
|
5141
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5142
|
+
try {
|
|
5143
|
+
return store.searchRanked(
|
|
5144
|
+
args.query,
|
|
5145
|
+
{
|
|
5146
|
+
kind: args.kind,
|
|
5147
|
+
lang: args.lang,
|
|
5148
|
+
file: args.file,
|
|
5149
|
+
lspKind: args.lspKind
|
|
5150
|
+
},
|
|
5151
|
+
args.limit
|
|
5152
|
+
);
|
|
5153
|
+
} finally {
|
|
5154
|
+
indexStorePool.release(store);
|
|
5155
|
+
}
|
|
5072
5156
|
}
|
|
5073
|
-
function
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5157
|
+
function statsService(args) {
|
|
5158
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5159
|
+
try {
|
|
5160
|
+
return store.getStats();
|
|
5161
|
+
} finally {
|
|
5162
|
+
indexStorePool.release(store);
|
|
5163
|
+
}
|
|
5078
5164
|
}
|
|
5079
|
-
function
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5165
|
+
function packageGraphService(args) {
|
|
5166
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5167
|
+
try {
|
|
5168
|
+
return store.getPackageGraph();
|
|
5169
|
+
} finally {
|
|
5170
|
+
indexStorePool.release(store);
|
|
5171
|
+
}
|
|
5084
5172
|
}
|
|
5085
|
-
|
|
5086
|
-
const
|
|
5087
|
-
const connection = connectionFor(projectRoot, indexDir);
|
|
5173
|
+
function fileGraphService(args) {
|
|
5174
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5088
5175
|
try {
|
|
5089
|
-
return
|
|
5176
|
+
return store.getFileGraph(args.packageFilter);
|
|
5090
5177
|
} finally {
|
|
5091
|
-
|
|
5092
|
-
connections.delete(endpoint);
|
|
5093
|
-
connectionStates.delete(endpoint);
|
|
5178
|
+
indexStorePool.release(store);
|
|
5094
5179
|
}
|
|
5095
5180
|
}
|
|
5096
|
-
function
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
};
|
|
5104
|
-
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
5105
|
-
heartbeatTimer = void 0;
|
|
5181
|
+
function symbolGraphService(args) {
|
|
5182
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5183
|
+
try {
|
|
5184
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
5185
|
+
} finally {
|
|
5186
|
+
indexStorePool.release(store);
|
|
5187
|
+
}
|
|
5106
5188
|
}
|
|
5107
5189
|
|
|
5108
5190
|
// src/codebase-index/background-indexer.ts
|
|
5191
|
+
init_languages();
|
|
5109
5192
|
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 24e4;
|
|
5110
5193
|
var DEFAULT_INCREMENTAL_TIMEOUT_MS = 6e4;
|
|
5111
5194
|
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
@@ -5238,8 +5321,17 @@ async function shutdownCodebaseIndexHost() {
|
|
|
5238
5321
|
}
|
|
5239
5322
|
}
|
|
5240
5323
|
}
|
|
5324
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
5325
|
+
function warnEndpointInvalidOnce(availability) {
|
|
5326
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
5327
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
5328
|
+
process.stderr.write(
|
|
5329
|
+
`codebase-index: socket path is ${availability.byteLength} bytes, over this platform's ${availability.maxBytes}-byte sun_path limit (${availability.endpoint}). Subsequent calls will reject until TMPDIR is shortened to restore the shared daemon.
|
|
5330
|
+
`
|
|
5331
|
+
);
|
|
5332
|
+
}
|
|
5241
5333
|
function callIndexOp(op, args, opts) {
|
|
5242
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
5334
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
5243
5335
|
if (availability.kind === "available") {
|
|
5244
5336
|
return callProjectIndexServer(op, args, opts);
|
|
5245
5337
|
}
|
|
@@ -5250,6 +5342,14 @@ function callIndexOp(op, args, opts) {
|
|
|
5250
5342
|
)
|
|
5251
5343
|
);
|
|
5252
5344
|
}
|
|
5345
|
+
if (availability.kind === "endpoint-invalid") {
|
|
5346
|
+
warnEndpointInvalidOnce(availability);
|
|
5347
|
+
return Promise.reject(
|
|
5348
|
+
new Error(
|
|
5349
|
+
`codebase-index: socket path is ${availability.byteLength} bytes, over this platform's ${availability.maxBytes}-byte sun_path limit (${availability.endpoint}). Set a shorter TMPDIR to relocate the shared daemon, or set WRONGSTACK_INDEX_INLINE=1 to explicitly opt into a process-local index.`
|
|
5350
|
+
)
|
|
5351
|
+
);
|
|
5352
|
+
}
|
|
5253
5353
|
const w = ensureWorker();
|
|
5254
5354
|
if (!w) return callInline(op, args, opts);
|
|
5255
5355
|
if (opts.signal?.aborted) {
|
|
@@ -5523,7 +5623,11 @@ function checkCodebaseIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
|
5523
5623
|
return checkProjectIndexServerHealth(projectRoot, indexDir, options);
|
|
5524
5624
|
}
|
|
5525
5625
|
function ensureCodebaseIndexServer(options) {
|
|
5526
|
-
|
|
5626
|
+
const availability = resolveProjectIndexDaemonAvailability(options.projectRoot, options.indexDir);
|
|
5627
|
+
if (availability.kind !== "available") {
|
|
5628
|
+
if (availability.kind === "endpoint-invalid") warnEndpointInvalidOnce(availability);
|
|
5629
|
+
return Promise.resolve();
|
|
5630
|
+
}
|
|
5527
5631
|
return ensureProjectIndexServer({
|
|
5528
5632
|
projectRoot: options.projectRoot,
|
|
5529
5633
|
indexDir: options.indexDir,
|
|
@@ -5808,6 +5912,7 @@ var codebaseStatsTool = {
|
|
|
5808
5912
|
};
|
|
5809
5913
|
|
|
5810
5914
|
// src/codebase-index/dead-code-scan.ts
|
|
5915
|
+
init_languages();
|
|
5811
5916
|
import * as fs12 from "node:fs";
|
|
5812
5917
|
import * as path13 from "node:path";
|
|
5813
5918
|
var deadCodeScanTool = {
|
|
@@ -5876,7 +5981,15 @@ function discoverEntryPoints(projectRoot, userEntryPoints) {
|
|
|
5876
5981
|
if (rootPkg) {
|
|
5877
5982
|
addPkgJsonEntryPoints(projectRoot, rootPkg, entries);
|
|
5878
5983
|
}
|
|
5879
|
-
|
|
5984
|
+
let workspaces;
|
|
5985
|
+
if (rootPkg) {
|
|
5986
|
+
workspaces = extractWorkspaceGlobs(rootPkg, projectRoot);
|
|
5987
|
+
if (workspaces.length === 0) {
|
|
5988
|
+
workspaces = extractPnpmWorkspaceDirs(projectRoot);
|
|
5989
|
+
}
|
|
5990
|
+
} else {
|
|
5991
|
+
workspaces = [];
|
|
5992
|
+
}
|
|
5880
5993
|
for (const wsDir of workspaces) {
|
|
5881
5994
|
const pkgJsonPath = path13.join(wsDir, "package.json");
|
|
5882
5995
|
const pkg = tryReadJson(pkgJsonPath);
|
|
@@ -5898,77 +6011,189 @@ function discoverEntryPoints(projectRoot, userEntryPoints) {
|
|
|
5898
6011
|
}
|
|
5899
6012
|
return [...entries];
|
|
5900
6013
|
}
|
|
6014
|
+
var BUILD_OUTPUT_DIRS = ["dist", "out", "build", "release"];
|
|
6015
|
+
var BUILD_OUTPUT_DIR_NAMES = BUILD_OUTPUT_DIRS.map((d) => `${path13.sep}${d}${path13.sep}`);
|
|
6016
|
+
function trySourceEquivalent(resolved) {
|
|
6017
|
+
resolved = resolved.replace(/[/\\]/g, path13.sep);
|
|
6018
|
+
for (const marker of BUILD_OUTPUT_DIR_NAMES) {
|
|
6019
|
+
const idx = resolved.indexOf(marker);
|
|
6020
|
+
if (idx === -1) continue;
|
|
6021
|
+
const base = resolved.replace(marker, `${path13.sep}src${path13.sep}`);
|
|
6022
|
+
const candidate = base.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
6023
|
+
if (candidate !== base && fs12.existsSync(candidate)) {
|
|
6024
|
+
return candidate;
|
|
6025
|
+
}
|
|
6026
|
+
const dtsStripped = base.replace(/\.d\.ts$/, "");
|
|
6027
|
+
const candidateDts = dtsStripped + ".ts";
|
|
6028
|
+
if (candidateDts !== base && candidateDts !== candidate && fs12.existsSync(candidateDts)) {
|
|
6029
|
+
return candidateDts;
|
|
6030
|
+
}
|
|
6031
|
+
const candidateNoExt = base + ".ts";
|
|
6032
|
+
if (candidate !== candidateNoExt && candidateNoExt !== candidateDts && fs12.existsSync(candidateNoExt)) {
|
|
6033
|
+
return candidateNoExt;
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
return null;
|
|
6037
|
+
}
|
|
6038
|
+
function tryAddEntryPath(pkgDir, rawPath, entries) {
|
|
6039
|
+
const resolved = resolveAgainst(pkgDir, rawPath);
|
|
6040
|
+
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6041
|
+
const tsResolved = resolved.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
6042
|
+
if (tsResolved !== resolved && fs12.existsSync(tsResolved)) {
|
|
6043
|
+
entries.add(tsResolved);
|
|
6044
|
+
}
|
|
6045
|
+
const srcAlt = trySourceEquivalent(resolved);
|
|
6046
|
+
if (srcAlt) entries.add(srcAlt);
|
|
6047
|
+
}
|
|
5901
6048
|
function addPkgJsonEntryPoints(pkgDir, pkg, entries) {
|
|
5902
6049
|
if (typeof pkg.main === "string") {
|
|
5903
|
-
|
|
5904
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
5905
|
-
const tsResolved = resolved.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
5906
|
-
if (tsResolved !== resolved && fs12.existsSync(tsResolved)) {
|
|
5907
|
-
entries.add(tsResolved);
|
|
5908
|
-
}
|
|
6050
|
+
tryAddEntryPath(pkgDir, pkg.main, entries);
|
|
5909
6051
|
}
|
|
5910
6052
|
const bin = pkg.bin;
|
|
5911
6053
|
if (typeof bin === "string") {
|
|
5912
|
-
|
|
5913
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6054
|
+
tryAddEntryPath(pkgDir, bin, entries);
|
|
5914
6055
|
} else if (bin && typeof bin === "object") {
|
|
5915
6056
|
for (const value of Object.values(bin)) {
|
|
5916
6057
|
if (typeof value === "string") {
|
|
5917
|
-
|
|
5918
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6058
|
+
tryAddEntryPath(pkgDir, value, entries);
|
|
5919
6059
|
}
|
|
5920
6060
|
}
|
|
5921
6061
|
}
|
|
5922
6062
|
for (const key of ["types", "typings"]) {
|
|
5923
6063
|
if (typeof pkg[key] === "string") {
|
|
5924
|
-
|
|
5925
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6064
|
+
tryAddEntryPath(pkgDir, pkg[key], entries);
|
|
5926
6065
|
}
|
|
5927
6066
|
}
|
|
5928
6067
|
const exports_ = pkg.exports;
|
|
5929
6068
|
if (exports_ && typeof exports_ === "object") {
|
|
5930
6069
|
for (const value of Object.values(exports_)) {
|
|
5931
6070
|
if (typeof value === "string") {
|
|
5932
|
-
|
|
5933
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6071
|
+
tryAddEntryPath(pkgDir, value, entries);
|
|
5934
6072
|
} else if (value && typeof value === "object") {
|
|
5935
|
-
for (const nested of Object.values(
|
|
5936
|
-
value
|
|
5937
|
-
)) {
|
|
6073
|
+
for (const nested of Object.values(value)) {
|
|
5938
6074
|
if (typeof nested === "string") {
|
|
5939
|
-
|
|
5940
|
-
if (fs12.existsSync(resolved)) entries.add(resolved);
|
|
6075
|
+
tryAddEntryPath(pkgDir, nested, entries);
|
|
5941
6076
|
}
|
|
5942
6077
|
}
|
|
5943
6078
|
}
|
|
5944
6079
|
}
|
|
5945
6080
|
}
|
|
5946
6081
|
}
|
|
6082
|
+
function expandGlobPattern(entry, projectRoot) {
|
|
6083
|
+
const dirs = [];
|
|
6084
|
+
if (entry.includes("*")) {
|
|
6085
|
+
const base = entry.replace(/\/\*+$/, "");
|
|
6086
|
+
const baseDir = path13.resolve(projectRoot, base);
|
|
6087
|
+
try {
|
|
6088
|
+
const children = fs12.readdirSync(baseDir, { withFileTypes: true });
|
|
6089
|
+
for (const child of children) {
|
|
6090
|
+
if (child.isDirectory()) {
|
|
6091
|
+
dirs.push(path13.join(baseDir, child.name));
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
} catch {
|
|
6095
|
+
}
|
|
6096
|
+
} else {
|
|
6097
|
+
dirs.push(path13.resolve(projectRoot, entry));
|
|
6098
|
+
}
|
|
6099
|
+
return dirs;
|
|
6100
|
+
}
|
|
5947
6101
|
function extractWorkspaceGlobs(pkg, projectRoot) {
|
|
5948
6102
|
const dirs = [];
|
|
5949
6103
|
const workspaces = pkg.workspaces;
|
|
5950
6104
|
if (Array.isArray(workspaces)) {
|
|
5951
6105
|
for (const entry of workspaces) {
|
|
5952
6106
|
if (typeof entry === "string") {
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6107
|
+
dirs.push(...expandGlobPattern(entry, projectRoot));
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
return dirs;
|
|
6112
|
+
}
|
|
6113
|
+
function extractPnpmWorkspaceDirs(projectRoot) {
|
|
6114
|
+
const yamlPath = path13.join(projectRoot, "pnpm-workspace.yaml");
|
|
6115
|
+
if (!fs12.existsSync(yamlPath)) return [];
|
|
6116
|
+
try {
|
|
6117
|
+
const content = fs12.readFileSync(yamlPath, "utf8");
|
|
6118
|
+
const dirs = [];
|
|
6119
|
+
let inPackages = false;
|
|
6120
|
+
const lines = content.split("\n");
|
|
6121
|
+
const itemRe = /^\s+-\s+"([^"]+)"|^\s+-\s+'([^']+)'|^\s+-\s+(\S+)/;
|
|
6122
|
+
for (const line of lines) {
|
|
6123
|
+
const trimmed = line.trim();
|
|
6124
|
+
if (/^packages\s*:\s*$/.test(trimmed)) {
|
|
6125
|
+
inPackages = true;
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
6128
|
+
if (inPackages && trimmed.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
6129
|
+
if (!trimmed.startsWith("-")) {
|
|
6130
|
+
inPackages = false;
|
|
6131
|
+
continue;
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
if (inPackages) {
|
|
6135
|
+
const m = itemRe.exec(line);
|
|
6136
|
+
if (m) {
|
|
6137
|
+
const entry = m[1] ?? m[2] ?? m[3];
|
|
6138
|
+
if (entry) {
|
|
6139
|
+
dirs.push(...expandGlobPattern(entry, projectRoot));
|
|
5964
6140
|
}
|
|
5965
|
-
} else {
|
|
5966
|
-
dirs.push(path13.resolve(projectRoot, entry));
|
|
5967
6141
|
}
|
|
5968
6142
|
}
|
|
5969
6143
|
}
|
|
6144
|
+
return dirs;
|
|
6145
|
+
} catch {
|
|
6146
|
+
return [];
|
|
5970
6147
|
}
|
|
5971
|
-
|
|
6148
|
+
}
|
|
6149
|
+
function resolveModulePath(importerPath, moduleSpecifier, indexedFiles) {
|
|
6150
|
+
if (!moduleSpecifier.startsWith(".")) return [];
|
|
6151
|
+
const dir = path13.dirname(importerPath);
|
|
6152
|
+
const base = path13.resolve(dir, moduleSpecifier);
|
|
6153
|
+
const results = [];
|
|
6154
|
+
const stripped = base.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
|
|
6155
|
+
const skipBase = stripped !== base && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(base);
|
|
6156
|
+
const candidates = skipBase ? [stripped] : [base];
|
|
6157
|
+
for (const candidate of candidates) {
|
|
6158
|
+
if (indexedFiles.has(candidate + ".ts")) results.push(candidate + ".ts");
|
|
6159
|
+
if (indexedFiles.has(candidate + ".tsx")) results.push(candidate + ".tsx");
|
|
6160
|
+
if (indexedFiles.has(candidate + ".js")) results.push(candidate + ".js");
|
|
6161
|
+
if (indexedFiles.has(candidate + ".jsx")) results.push(candidate + ".jsx");
|
|
6162
|
+
if (indexedFiles.has(candidate + ".mjs")) results.push(candidate + ".mjs");
|
|
6163
|
+
if (indexedFiles.has(candidate + ".cjs")) results.push(candidate + ".cjs");
|
|
6164
|
+
if (indexedFiles.has(path13.join(candidate, "index.ts")))
|
|
6165
|
+
results.push(path13.join(candidate, "index.ts"));
|
|
6166
|
+
if (indexedFiles.has(path13.join(candidate, "index.tsx")))
|
|
6167
|
+
results.push(path13.join(candidate, "index.tsx"));
|
|
6168
|
+
if (indexedFiles.has(path13.join(candidate, "index.js")))
|
|
6169
|
+
results.push(path13.join(candidate, "index.js"));
|
|
6170
|
+
if (indexedFiles.has(path13.join(candidate, "index.jsx")))
|
|
6171
|
+
results.push(path13.join(candidate, "index.jsx"));
|
|
6172
|
+
if (indexedFiles.has(path13.join(candidate, "index.mjs")))
|
|
6173
|
+
results.push(path13.join(candidate, "index.mjs"));
|
|
6174
|
+
if (indexedFiles.has(path13.join(candidate, "index.cjs")))
|
|
6175
|
+
results.push(path13.join(candidate, "index.cjs"));
|
|
6176
|
+
}
|
|
6177
|
+
return [...new Set(results)];
|
|
6178
|
+
}
|
|
6179
|
+
function parseNamedExportSymbols(matchText) {
|
|
6180
|
+
const braceStart = matchText.indexOf("{");
|
|
6181
|
+
if (braceStart === -1) return null;
|
|
6182
|
+
const braceEnd = matchText.indexOf("}", braceStart);
|
|
6183
|
+
if (braceEnd === -1) return null;
|
|
6184
|
+
const inner = matchText.slice(braceStart + 1, braceEnd);
|
|
6185
|
+
const symbols = [];
|
|
6186
|
+
for (const part of inner.split(",")) {
|
|
6187
|
+
let s = part.trim();
|
|
6188
|
+
if (!s) continue;
|
|
6189
|
+
s = s.replace(/^type\s+/, "");
|
|
6190
|
+
const asIdx = s.search(/\s+as\s+/);
|
|
6191
|
+
if (asIdx !== -1) {
|
|
6192
|
+
s = s.slice(0, asIdx).trim();
|
|
6193
|
+
}
|
|
6194
|
+
if (s) symbols.push(s);
|
|
6195
|
+
}
|
|
6196
|
+
return symbols;
|
|
5972
6197
|
}
|
|
5973
6198
|
function runDeadCodeScan(projectRoot, opts = {}) {
|
|
5974
6199
|
const store = opts.store ?? indexStorePool.acquire(projectRoot, { indexDir: opts.indexDir });
|
|
@@ -5990,12 +6215,71 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
5990
6215
|
}
|
|
5991
6216
|
const discoveredFiles = discoverEntryPoints(projectRoot, opts.userEntryPoints);
|
|
5992
6217
|
const entryFileSet = new Set(discoveredFiles.map((f) => path13.resolve(f)));
|
|
6218
|
+
const indexedFiles = /* @__PURE__ */ new Set();
|
|
6219
|
+
for (const s of allSymbols) indexedFiles.add(s.file);
|
|
6220
|
+
for (const fm of store.getAllFileMetas()) indexedFiles.add(fm.file);
|
|
5993
6221
|
const seedIds = /* @__PURE__ */ new Set();
|
|
5994
6222
|
for (const s of allSymbols) {
|
|
5995
6223
|
if (entryFileSet.has(s.file)) {
|
|
5996
6224
|
seedIds.add(s.id);
|
|
5997
6225
|
}
|
|
5998
6226
|
}
|
|
6227
|
+
const fileToSymbolIds = /* @__PURE__ */ new Map();
|
|
6228
|
+
for (const s of allSymbols) {
|
|
6229
|
+
let byFile = fileToSymbolIds.get(s.file);
|
|
6230
|
+
if (!byFile) {
|
|
6231
|
+
byFile = [];
|
|
6232
|
+
fileToSymbolIds.set(s.file, byFile);
|
|
6233
|
+
}
|
|
6234
|
+
byFile.push(s.id);
|
|
6235
|
+
}
|
|
6236
|
+
const scannedBarrels = /* @__PURE__ */ new Set();
|
|
6237
|
+
const barrelWorkList = [...entryFileSet];
|
|
6238
|
+
while (barrelWorkList.length > 0) {
|
|
6239
|
+
const epFile = barrelWorkList.pop();
|
|
6240
|
+
if (scannedBarrels.has(epFile)) continue;
|
|
6241
|
+
scannedBarrels.add(epFile);
|
|
6242
|
+
try {
|
|
6243
|
+
const content = fs12.readFileSync(epFile, "utf8");
|
|
6244
|
+
const strippedContent = content.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/\/\/[^\n]*/g, (m) => " ".repeat(m.length));
|
|
6245
|
+
const reExportRe = /export\s+(?:(?:type\s+)?\{[\s\S]*?\}\s+from|\*\s+as\s+\w+\s+from|\*\s+from)\s+['"]([^'"]+)['"]/g;
|
|
6246
|
+
let match;
|
|
6247
|
+
while ((match = reExportRe.exec(strippedContent)) !== null) {
|
|
6248
|
+
const moduleSpec = match[1];
|
|
6249
|
+
const resolvedFiles = resolveModulePath(epFile, moduleSpec, indexedFiles);
|
|
6250
|
+
for (const rf of resolvedFiles) {
|
|
6251
|
+
const fileSyms = fileToSymbolIds.get(rf);
|
|
6252
|
+
if (fileSyms) {
|
|
6253
|
+
const namedSymbols = parseNamedExportSymbols(match[0]);
|
|
6254
|
+
if (namedSymbols) {
|
|
6255
|
+
const nameSet = new Set(namedSymbols);
|
|
6256
|
+
for (const sid of fileSyms) {
|
|
6257
|
+
const sym = symbolById.get(sid);
|
|
6258
|
+
if (sym && nameSet.has(sym.name)) seedIds.add(sid);
|
|
6259
|
+
}
|
|
6260
|
+
} else {
|
|
6261
|
+
for (const sid of fileSyms) seedIds.add(sid);
|
|
6262
|
+
}
|
|
6263
|
+
}
|
|
6264
|
+
if (!scannedBarrels.has(rf)) {
|
|
6265
|
+
barrelWorkList.push(rf);
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6269
|
+
} catch (err) {
|
|
6270
|
+
if (err instanceof Error && err.code !== "ENOENT") {
|
|
6271
|
+
console.warn(
|
|
6272
|
+
JSON.stringify({
|
|
6273
|
+
level: "warn",
|
|
6274
|
+
event: "dead_code_scan_barrel_read_failed",
|
|
6275
|
+
message: err.message,
|
|
6276
|
+
file: epFile,
|
|
6277
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6278
|
+
})
|
|
6279
|
+
);
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
5999
6283
|
const alive = new Set(seedIds);
|
|
6000
6284
|
const frontier = [...seedIds];
|
|
6001
6285
|
const visitedEdges = /* @__PURE__ */ new Set();
|
|
@@ -6029,8 +6313,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
6029
6313
|
dead.push({
|
|
6030
6314
|
name: s.name,
|
|
6031
6315
|
kind: s.kind,
|
|
6032
|
-
lang: "ts",
|
|
6033
|
-
// populated from symbol file metadata
|
|
6316
|
+
lang: detectLang(s.file) ?? "ts",
|
|
6034
6317
|
file: s.file,
|
|
6035
6318
|
line: s.line,
|
|
6036
6319
|
reason
|
|
@@ -6055,16 +6338,14 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
6055
6338
|
deadFiles.push({
|
|
6056
6339
|
file,
|
|
6057
6340
|
symbolCount: syms.length,
|
|
6058
|
-
lang:
|
|
6341
|
+
lang: detectLang(file) ?? "ts"
|
|
6059
6342
|
});
|
|
6060
6343
|
}
|
|
6061
6344
|
}
|
|
6062
6345
|
const deadPackages = [];
|
|
6063
6346
|
const pkgEntries = findPackageEntries(projectRoot);
|
|
6064
6347
|
for (const [pkgName, pkgDir] of pkgEntries) {
|
|
6065
|
-
const pkgFiles = allSymbols.filter(
|
|
6066
|
-
(s) => s.file.startsWith(pkgDir + path13.sep)
|
|
6067
|
-
);
|
|
6348
|
+
const pkgFiles = allSymbols.filter((s) => s.file.startsWith(pkgDir + path13.sep));
|
|
6068
6349
|
if (pkgFiles.length === 0) continue;
|
|
6069
6350
|
const pkgUsed = pkgFiles.filter((s) => alive.has(s.id));
|
|
6070
6351
|
if (pkgUsed.length === 0) {
|
|
@@ -6105,7 +6386,10 @@ function findPackageEntries(projectRoot) {
|
|
|
6105
6386
|
pkgMap.set(rootPkg.name, projectRoot);
|
|
6106
6387
|
}
|
|
6107
6388
|
if (rootPkg) {
|
|
6108
|
-
|
|
6389
|
+
let wsDirs = extractWorkspaceGlobs(rootPkg, projectRoot);
|
|
6390
|
+
if (wsDirs.length === 0) {
|
|
6391
|
+
wsDirs = extractPnpmWorkspaceDirs(projectRoot);
|
|
6392
|
+
}
|
|
6109
6393
|
for (const wsDir of wsDirs) {
|
|
6110
6394
|
const wsPkg = tryReadJson(path13.join(wsDir, "package.json"));
|
|
6111
6395
|
if (wsPkg && typeof wsPkg.name === "string") {
|
|
@@ -6151,6 +6435,7 @@ export {
|
|
|
6151
6435
|
packageGraphService2 as packageGraphService,
|
|
6152
6436
|
resetIndexCircuitBreaker,
|
|
6153
6437
|
resolveIndexDir,
|
|
6438
|
+
resolveProjectIndexDaemonAvailability,
|
|
6154
6439
|
runDeadCodeScan,
|
|
6155
6440
|
runIndexer,
|
|
6156
6441
|
runStartupIndex,
|