@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
package/dist/index.js
CHANGED
|
@@ -4896,16 +4896,16 @@ var init_legacy_bridge = __esm({
|
|
|
4896
4896
|
});
|
|
4897
4897
|
|
|
4898
4898
|
// src/codebase-index/languages.ts
|
|
4899
|
-
import * as
|
|
4899
|
+
import * as path18 from "node:path";
|
|
4900
4900
|
function detectLang(file) {
|
|
4901
|
-
const base =
|
|
4901
|
+
const base = path18.basename(file);
|
|
4902
4902
|
const lowerBase = base.toLowerCase();
|
|
4903
4903
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4904
4904
|
return "ts";
|
|
4905
4905
|
}
|
|
4906
4906
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4907
4907
|
if (special) return special;
|
|
4908
|
-
const ext =
|
|
4908
|
+
const ext = path18.extname(base).toLowerCase();
|
|
4909
4909
|
if (!ext) return null;
|
|
4910
4910
|
return EXT_TO_LANG[ext] ?? null;
|
|
4911
4911
|
}
|
|
@@ -5137,11 +5137,18 @@ async function parseSymbols(opts) {
|
|
|
5137
5137
|
} else if (ts.isHeritageClause(node)) {
|
|
5138
5138
|
for (const t of node.types) {
|
|
5139
5139
|
const name = getTypeName(t.expression);
|
|
5140
|
-
if (name)
|
|
5140
|
+
if (name)
|
|
5141
|
+
refs.push({
|
|
5142
|
+
fromId: 0,
|
|
5143
|
+
toName: name,
|
|
5144
|
+
callType: node.token === ts.SyntaxKind.ExtendsKeyword ? "inherit" : "implement",
|
|
5145
|
+
line: lineNum
|
|
5146
|
+
});
|
|
5141
5147
|
}
|
|
5142
5148
|
} else if (ts.isImportDeclaration(node)) {
|
|
5143
|
-
|
|
5144
|
-
|
|
5149
|
+
emitImportSpecifierRefs(node, refs, lineNum);
|
|
5150
|
+
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
|
|
5151
|
+
emitExportSpecifierRefs(node, refs, lineNum);
|
|
5145
5152
|
}
|
|
5146
5153
|
const scopeIdx = scopeParts.length;
|
|
5147
5154
|
pushScopeName(node, scopeParts);
|
|
@@ -5157,11 +5164,6 @@ function getTypeName(name) {
|
|
|
5157
5164
|
if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;
|
|
5158
5165
|
return "";
|
|
5159
5166
|
}
|
|
5160
|
-
function getModuleName(node) {
|
|
5161
|
-
const moduleSpecifier = node.moduleSpecifier;
|
|
5162
|
-
if (ts.isStringLiteral(moduleSpecifier)) return moduleSpecifier.text;
|
|
5163
|
-
return "";
|
|
5164
|
-
}
|
|
5165
5167
|
function deduplicateRefs(refs) {
|
|
5166
5168
|
const seen = /* @__PURE__ */ new Set();
|
|
5167
5169
|
return refs.filter((r) => {
|
|
@@ -5171,6 +5173,44 @@ function deduplicateRefs(refs) {
|
|
|
5171
5173
|
return true;
|
|
5172
5174
|
});
|
|
5173
5175
|
}
|
|
5176
|
+
function getImportSpecifierName(spec) {
|
|
5177
|
+
return spec.propertyName?.text ?? spec.name.text;
|
|
5178
|
+
}
|
|
5179
|
+
function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
5180
|
+
const clause = node.importClause;
|
|
5181
|
+
if (!clause) return;
|
|
5182
|
+
if (clause.name) {
|
|
5183
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5184
|
+
}
|
|
5185
|
+
const bindings2 = clause.namedBindings;
|
|
5186
|
+
if (!bindings2) return;
|
|
5187
|
+
if (ts.isNamedImports(bindings2)) {
|
|
5188
|
+
for (const element of bindings2.elements) {
|
|
5189
|
+
refs.push({
|
|
5190
|
+
fromId: 0,
|
|
5191
|
+
toName: getImportSpecifierName(element),
|
|
5192
|
+
callType: "import",
|
|
5193
|
+
line: lineNum
|
|
5194
|
+
});
|
|
5195
|
+
}
|
|
5196
|
+
} else if (ts.isNamespaceImport(bindings2)) {
|
|
5197
|
+
refs.push({ fromId: 0, toName: bindings2.name.text, callType: "import", line: lineNum });
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
function emitExportSpecifierRefs(node, refs, lineNum) {
|
|
5201
|
+
const clause = node.exportClause;
|
|
5202
|
+
if (clause && ts.isNamespaceExport(clause)) {
|
|
5203
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum });
|
|
5204
|
+
return;
|
|
5205
|
+
}
|
|
5206
|
+
if (clause && ts.isNamedExports(clause)) {
|
|
5207
|
+
for (const element of clause.elements) {
|
|
5208
|
+
const originalName = element.propertyName?.text ?? element.name.text;
|
|
5209
|
+
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum });
|
|
5210
|
+
}
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5174
5214
|
var ts, tsLoad, kindMapCache;
|
|
5175
5215
|
var init_ts_parser = __esm({
|
|
5176
5216
|
"src/codebase-index/ts-parser.ts"() {
|
|
@@ -5204,10 +5244,10 @@ __export(go_parser_exports, {
|
|
|
5204
5244
|
detectLang: () => detectLang,
|
|
5205
5245
|
parseSymbols: () => parseSymbols2
|
|
5206
5246
|
});
|
|
5207
|
-
import { spawn as
|
|
5208
|
-
import * as
|
|
5209
|
-
import * as
|
|
5210
|
-
import * as
|
|
5247
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5248
|
+
import * as os6 from "node:os";
|
|
5249
|
+
import * as path19 from "node:path";
|
|
5250
|
+
import * as fs14 from "node:fs/promises";
|
|
5211
5251
|
async function parseSymbols2(opts) {
|
|
5212
5252
|
const { file, content, lang } = opts;
|
|
5213
5253
|
try {
|
|
@@ -5279,16 +5319,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5279
5319
|
try {
|
|
5280
5320
|
let scriptPath = _cachedGoScriptPath;
|
|
5281
5321
|
if (!scriptPath) {
|
|
5282
|
-
const tmpDir = await
|
|
5283
|
-
scriptPath =
|
|
5284
|
-
await
|
|
5322
|
+
const tmpDir = await fs14.mkdtemp(path19.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5323
|
+
scriptPath = path19.join(tmpDir, "parse.go");
|
|
5324
|
+
await fs14.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5285
5325
|
_cachedGoScriptPath = scriptPath;
|
|
5286
5326
|
}
|
|
5287
5327
|
const goBinary = resolveWin32Command("go");
|
|
5288
5328
|
const goResult = await new Promise(
|
|
5289
5329
|
(resolve17, reject) => {
|
|
5290
5330
|
let settled = false;
|
|
5291
|
-
const proc =
|
|
5331
|
+
const proc = spawn5(goBinary, ["run", scriptPath], {
|
|
5292
5332
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5293
5333
|
windowsHide: true
|
|
5294
5334
|
});
|
|
@@ -5891,10 +5931,10 @@ __export(py_parser_exports, {
|
|
|
5891
5931
|
detectLang: () => detectLang,
|
|
5892
5932
|
parseSymbols: () => parseSymbols4
|
|
5893
5933
|
});
|
|
5894
|
-
import { spawn as
|
|
5895
|
-
import * as
|
|
5896
|
-
import * as
|
|
5897
|
-
import * as
|
|
5934
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
5935
|
+
import * as fs15 from "node:fs/promises";
|
|
5936
|
+
import * as os7 from "node:os";
|
|
5937
|
+
import * as path20 from "node:path";
|
|
5898
5938
|
async function parseSymbols4(opts) {
|
|
5899
5939
|
const { file, content, lang } = opts;
|
|
5900
5940
|
try {
|
|
@@ -5916,7 +5956,7 @@ async function resolvePython() {
|
|
|
5916
5956
|
function commandIsAvailable(command) {
|
|
5917
5957
|
return new Promise((resolve17) => {
|
|
5918
5958
|
let settled = false;
|
|
5919
|
-
const proc =
|
|
5959
|
+
const proc = spawn6(command, ["--version"], {
|
|
5920
5960
|
stdio: "ignore",
|
|
5921
5961
|
windowsHide: true
|
|
5922
5962
|
});
|
|
@@ -5938,7 +5978,7 @@ function commandIsAvailable(command) {
|
|
|
5938
5978
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
5939
5979
|
return new Promise((resolve17, reject) => {
|
|
5940
5980
|
let settled = false;
|
|
5941
|
-
const proc =
|
|
5981
|
+
const proc = spawn6(pyBinary, [scriptPath, filePath], {
|
|
5942
5982
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5943
5983
|
windowsHide: true
|
|
5944
5984
|
});
|
|
@@ -5972,10 +6012,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
5972
6012
|
async function syncPyParse(filePath, content, lang) {
|
|
5973
6013
|
try {
|
|
5974
6014
|
if (!_cachedScriptPath) {
|
|
5975
|
-
const tmpDir =
|
|
5976
|
-
await
|
|
5977
|
-
_cachedScriptPath =
|
|
5978
|
-
await
|
|
6015
|
+
const tmpDir = path20.join(os7.tmpdir(), "ws-py-parse");
|
|
6016
|
+
await fs15.mkdir(tmpDir, { recursive: true });
|
|
6017
|
+
_cachedScriptPath = path20.join(tmpDir, "parse.py");
|
|
6018
|
+
await fs15.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
5979
6019
|
}
|
|
5980
6020
|
cachedPyBinary ??= resolvePython();
|
|
5981
6021
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6229,10 +6269,10 @@ __export(rs_parser_exports, {
|
|
|
6229
6269
|
detectLang: () => detectLang,
|
|
6230
6270
|
parseSymbols: () => parseSymbols5
|
|
6231
6271
|
});
|
|
6232
|
-
import { expectDefined as
|
|
6233
|
-
import { execFile, spawn as
|
|
6234
|
-
import * as
|
|
6235
|
-
import * as
|
|
6272
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6273
|
+
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6274
|
+
import * as fs16 from "node:fs/promises";
|
|
6275
|
+
import * as path21 from "node:path";
|
|
6236
6276
|
async function parseSymbols5(opts) {
|
|
6237
6277
|
const { file, content, lang } = opts;
|
|
6238
6278
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -6254,7 +6294,7 @@ function checkNativeParser() {
|
|
|
6254
6294
|
nativeParserAvailability ??= (async () => {
|
|
6255
6295
|
try {
|
|
6256
6296
|
await probe("rustc", ["--version"]);
|
|
6257
|
-
const toolsDir =
|
|
6297
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6258
6298
|
await probe(
|
|
6259
6299
|
"cargo",
|
|
6260
6300
|
[
|
|
@@ -6263,7 +6303,7 @@ function checkNativeParser() {
|
|
|
6263
6303
|
"--format-version",
|
|
6264
6304
|
"1",
|
|
6265
6305
|
"--manifest-path",
|
|
6266
|
-
|
|
6306
|
+
path21.join(toolsDir, "Cargo.toml")
|
|
6267
6307
|
]
|
|
6268
6308
|
);
|
|
6269
6309
|
return true;
|
|
@@ -6275,17 +6315,17 @@ function checkNativeParser() {
|
|
|
6275
6315
|
}
|
|
6276
6316
|
async function tryNativeParse(file, content) {
|
|
6277
6317
|
try {
|
|
6278
|
-
const toolsDir =
|
|
6279
|
-
const crateDir =
|
|
6280
|
-
const tmpFile =
|
|
6281
|
-
await
|
|
6318
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6319
|
+
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6320
|
+
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6321
|
+
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6282
6322
|
const cargoBinary = resolveWin32Command("cargo");
|
|
6283
6323
|
const result = await new Promise(
|
|
6284
6324
|
(resolve17, reject) => {
|
|
6285
6325
|
let settled = false;
|
|
6286
|
-
const proc =
|
|
6326
|
+
const proc = spawn7(
|
|
6287
6327
|
cargoBinary,
|
|
6288
|
-
["run", "--manifest-path",
|
|
6328
|
+
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6289
6329
|
{
|
|
6290
6330
|
cwd: process.cwd(),
|
|
6291
6331
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -6344,7 +6384,7 @@ function regexParse(opts) {
|
|
|
6344
6384
|
let hi = lineOffsets2.length - 1;
|
|
6345
6385
|
while (lo < hi) {
|
|
6346
6386
|
const mid = lo + hi + 1 >>> 1;
|
|
6347
|
-
if (
|
|
6387
|
+
if (expectDefined3(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6348
6388
|
else hi = mid - 1;
|
|
6349
6389
|
}
|
|
6350
6390
|
return lo + 1;
|
|
@@ -6356,7 +6396,7 @@ function regexParse(opts) {
|
|
|
6356
6396
|
for (const pattern of RS_PATTERNS) {
|
|
6357
6397
|
pattern.regex.lastIndex = 0;
|
|
6358
6398
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
6359
|
-
const name =
|
|
6399
|
+
const name = expectDefined3(match[1]);
|
|
6360
6400
|
const offset = match.index ?? 0;
|
|
6361
6401
|
const line = lineFromOffset(offset);
|
|
6362
6402
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6413,8 +6453,8 @@ __export(json_parser_exports, {
|
|
|
6413
6453
|
detectLang: () => detectLang,
|
|
6414
6454
|
parseSymbols: () => parseSymbols6
|
|
6415
6455
|
});
|
|
6416
|
-
import { expectDefined as
|
|
6417
|
-
import * as
|
|
6456
|
+
import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
|
|
6457
|
+
import * as path22 from "node:path";
|
|
6418
6458
|
function parseSymbols6(opts) {
|
|
6419
6459
|
const { file, content, lang } = opts;
|
|
6420
6460
|
try {
|
|
@@ -6426,7 +6466,7 @@ function parseSymbols6(opts) {
|
|
|
6426
6466
|
function regexParse2(opts) {
|
|
6427
6467
|
const { file, content, lang } = opts;
|
|
6428
6468
|
const symbols = [];
|
|
6429
|
-
const basename13 =
|
|
6469
|
+
const basename13 = path22.basename(file).toLowerCase();
|
|
6430
6470
|
const isPackageJson = basename13 === "package.json";
|
|
6431
6471
|
const isTsconfig = basename13 === "tsconfig.json" || basename13 === "tsconfig.build.json";
|
|
6432
6472
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -6441,22 +6481,22 @@ function regexParse2(opts) {
|
|
|
6441
6481
|
let hi = lineOffsets2.length - 1;
|
|
6442
6482
|
while (lo < hi) {
|
|
6443
6483
|
const mid = lo + hi + 1 >>> 1;
|
|
6444
|
-
if (
|
|
6484
|
+
if (expectDefined4(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6445
6485
|
else hi = mid - 1;
|
|
6446
6486
|
}
|
|
6447
6487
|
return lo + 1;
|
|
6448
6488
|
}
|
|
6449
6489
|
const rootMatch = content.match(/^\s*\{/m);
|
|
6450
6490
|
if (rootMatch) {
|
|
6451
|
-
const offset =
|
|
6491
|
+
const offset = expectDefined4(rootMatch.index);
|
|
6452
6492
|
const line = lineFromOffset(offset);
|
|
6453
6493
|
symbols.push(
|
|
6454
6494
|
makeSymbol({
|
|
6455
|
-
name:
|
|
6495
|
+
name: path22.basename(file),
|
|
6456
6496
|
kind: "object",
|
|
6457
6497
|
line,
|
|
6458
6498
|
col: 0,
|
|
6459
|
-
signature: `"${
|
|
6499
|
+
signature: `"${path22.basename(file)}" = { ... }`,
|
|
6460
6500
|
file,
|
|
6461
6501
|
lang
|
|
6462
6502
|
})
|
|
@@ -6464,7 +6504,7 @@ function regexParse2(opts) {
|
|
|
6464
6504
|
}
|
|
6465
6505
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
6466
6506
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
6467
|
-
const key =
|
|
6507
|
+
const key = expectDefined4(match[1]);
|
|
6468
6508
|
const offset = match.index ?? 0;
|
|
6469
6509
|
const line = lineFromOffset(offset);
|
|
6470
6510
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6511,7 +6551,7 @@ function regexParse2(opts) {
|
|
|
6511
6551
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
6512
6552
|
const defsMatch = defsRegex.exec(content);
|
|
6513
6553
|
if (defsMatch !== null) {
|
|
6514
|
-
const offset =
|
|
6554
|
+
const offset = expectDefined4(defsMatch.index);
|
|
6515
6555
|
const line = lineFromOffset(offset);
|
|
6516
6556
|
symbols.push(
|
|
6517
6557
|
makeSymbol({
|
|
@@ -6536,7 +6576,7 @@ function regexParse2(opts) {
|
|
|
6536
6576
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
6537
6577
|
const offset = match.index ?? 0;
|
|
6538
6578
|
const line = lineFromOffset(offset);
|
|
6539
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
6579
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined4(match[0]);
|
|
6540
6580
|
symbols.push(
|
|
6541
6581
|
makeSymbol({
|
|
6542
6582
|
name: key,
|
|
@@ -6555,12 +6595,12 @@ function regexParse2(opts) {
|
|
|
6555
6595
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineFromOffset) {
|
|
6556
6596
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
6557
6597
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
6558
|
-
const blockContent =
|
|
6598
|
+
const blockContent = expectDefined4(match[0]);
|
|
6559
6599
|
const blockOffset = match.index ?? 0;
|
|
6560
6600
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
6561
6601
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
6562
|
-
const key =
|
|
6563
|
-
const keyOffset = blockOffset +
|
|
6602
|
+
const key = expectDefined4(scriptMatch[1]);
|
|
6603
|
+
const keyOffset = blockOffset + expectDefined4(scriptMatch.index);
|
|
6564
6604
|
const line = lineFromOffset(keyOffset);
|
|
6565
6605
|
symbols.push(
|
|
6566
6606
|
makeSymbol({
|
|
@@ -6579,12 +6619,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineF
|
|
|
6579
6619
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets2, parentLine, lineFromOffset) {
|
|
6580
6620
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
6581
6621
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
6582
|
-
const blockContent =
|
|
6622
|
+
const blockContent = expectDefined4(match[0]);
|
|
6583
6623
|
const blockOffset = match.index ?? 0;
|
|
6584
6624
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
6585
6625
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
6586
|
-
const key =
|
|
6587
|
-
const keyOffset = blockOffset +
|
|
6626
|
+
const key = expectDefined4(optMatch[1]);
|
|
6627
|
+
const keyOffset = blockOffset + expectDefined4(optMatch.index);
|
|
6588
6628
|
const line = lineFromOffset(keyOffset);
|
|
6589
6629
|
if (line <= parentLine) continue;
|
|
6590
6630
|
symbols.push(
|
|
@@ -6629,7 +6669,7 @@ __export(yaml_parser_exports, {
|
|
|
6629
6669
|
detectLang: () => detectLang,
|
|
6630
6670
|
parseSymbols: () => parseSymbols7
|
|
6631
6671
|
});
|
|
6632
|
-
import { expectDefined as
|
|
6672
|
+
import { expectDefined as expectDefined5, truncate } from "@wrongstack/core/utils";
|
|
6633
6673
|
function parseSymbols7(opts) {
|
|
6634
6674
|
const { file, content, lang } = opts;
|
|
6635
6675
|
try {
|
|
@@ -6651,14 +6691,14 @@ function regexParse3(opts) {
|
|
|
6651
6691
|
let hi = lineOffsets2.length - 1;
|
|
6652
6692
|
while (lo < hi) {
|
|
6653
6693
|
const mid = lo + hi + 1 >>> 1;
|
|
6654
|
-
if (
|
|
6694
|
+
if (expectDefined5(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6655
6695
|
else hi = mid - 1;
|
|
6656
6696
|
}
|
|
6657
6697
|
return lo + 1;
|
|
6658
6698
|
}
|
|
6659
6699
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
6660
6700
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
6661
|
-
const name =
|
|
6701
|
+
const name = expectDefined5(match[1]);
|
|
6662
6702
|
const offset = match.index ?? 0;
|
|
6663
6703
|
const line = lineFromOffset(offset);
|
|
6664
6704
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6676,7 +6716,7 @@ function regexParse3(opts) {
|
|
|
6676
6716
|
}
|
|
6677
6717
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
6678
6718
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
6679
|
-
const name =
|
|
6719
|
+
const name = expectDefined5(match[1]);
|
|
6680
6720
|
const offset = match.index ?? 0;
|
|
6681
6721
|
const line = lineFromOffset(offset);
|
|
6682
6722
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6711,7 +6751,7 @@ function regexParse3(opts) {
|
|
|
6711
6751
|
}
|
|
6712
6752
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6713
6753
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
6714
|
-
const key =
|
|
6754
|
+
const key = expectDefined5(match[2]);
|
|
6715
6755
|
const offset = match.index ?? 0;
|
|
6716
6756
|
const line = lineFromOffset(offset);
|
|
6717
6757
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6731,7 +6771,7 @@ function regexParse3(opts) {
|
|
|
6731
6771
|
}
|
|
6732
6772
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
6733
6773
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
6734
|
-
const key =
|
|
6774
|
+
const key = expectDefined5(match[2]);
|
|
6735
6775
|
const offset = match.index ?? 0;
|
|
6736
6776
|
const line = lineFromOffset(offset);
|
|
6737
6777
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -8766,9 +8806,50 @@ import { createReadStream } from "node:fs";
|
|
|
8766
8806
|
import * as fs7 from "node:fs/promises";
|
|
8767
8807
|
import * as path10 from "node:path";
|
|
8768
8808
|
import { atomicWrite, ulid } from "@wrongstack/core/utils";
|
|
8809
|
+
var ARTIFACT_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
8810
|
+
var sweptRoots = /* @__PURE__ */ new Set();
|
|
8811
|
+
function sweepOldArtifacts(root) {
|
|
8812
|
+
if (sweptRoots.has(root)) return;
|
|
8813
|
+
sweptRoots.add(root);
|
|
8814
|
+
void (async () => {
|
|
8815
|
+
const cutoff = Date.now() - ARTIFACT_RETENTION_MS;
|
|
8816
|
+
let sessionDirs;
|
|
8817
|
+
try {
|
|
8818
|
+
const entries = await fs7.readdir(root, { withFileTypes: true });
|
|
8819
|
+
sessionDirs = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
8820
|
+
} catch {
|
|
8821
|
+
return;
|
|
8822
|
+
}
|
|
8823
|
+
for (const sessionDir of sessionDirs) {
|
|
8824
|
+
const dir = path10.join(root, sessionDir);
|
|
8825
|
+
let names;
|
|
8826
|
+
try {
|
|
8827
|
+
names = await fs7.readdir(dir);
|
|
8828
|
+
} catch {
|
|
8829
|
+
continue;
|
|
8830
|
+
}
|
|
8831
|
+
let removed = 0;
|
|
8832
|
+
for (const name of names) {
|
|
8833
|
+
const target = path10.join(dir, name);
|
|
8834
|
+
try {
|
|
8835
|
+
const stat18 = await fs7.stat(target);
|
|
8836
|
+
if (stat18.isFile() && stat18.mtimeMs < cutoff) {
|
|
8837
|
+
await fs7.rm(target, { force: true });
|
|
8838
|
+
removed++;
|
|
8839
|
+
}
|
|
8840
|
+
} catch {
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
if (removed === names.length && names.length > 0) {
|
|
8844
|
+
await fs7.rmdir(dir).catch(() => void 0);
|
|
8845
|
+
}
|
|
8846
|
+
}
|
|
8847
|
+
})();
|
|
8848
|
+
}
|
|
8769
8849
|
var BrowserArtifactStore = class {
|
|
8770
8850
|
constructor(root) {
|
|
8771
8851
|
this.root = root;
|
|
8852
|
+
sweepOldArtifacts(root);
|
|
8772
8853
|
}
|
|
8773
8854
|
root;
|
|
8774
8855
|
async write(sessionId, kind, extension, mimeType, content) {
|
|
@@ -10014,10 +10095,12 @@ async function shutdownBrowserTools() {
|
|
|
10014
10095
|
await Promise.all(active.map((manager) => manager.dispose()));
|
|
10015
10096
|
}
|
|
10016
10097
|
|
|
10017
|
-
// src/codebase-index/
|
|
10018
|
-
import
|
|
10019
|
-
import
|
|
10020
|
-
import
|
|
10098
|
+
// src/codebase-index/project-server-client.ts
|
|
10099
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
10100
|
+
import * as fs12 from "node:fs";
|
|
10101
|
+
import * as net3 from "node:net";
|
|
10102
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10103
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
10021
10104
|
|
|
10022
10105
|
// src/codebase-index/circuit-breaker.ts
|
|
10023
10106
|
var CircuitOpenError = class extends Error {
|
|
@@ -10102,153 +10185,18 @@ function resetIndexCircuitBreaker() {
|
|
|
10102
10185
|
indexCircuitBreaker.reset();
|
|
10103
10186
|
}
|
|
10104
10187
|
|
|
10105
|
-
// src/codebase-index/
|
|
10106
|
-
import {
|
|
10107
|
-
import
|
|
10108
|
-
import * as
|
|
10109
|
-
import
|
|
10110
|
-
import
|
|
10111
|
-
import {
|
|
10112
|
-
DEFAULT_WALK_IGNORE_DIRS,
|
|
10113
|
-
indexParallelBatchSize,
|
|
10114
|
-
isFrugalPerf
|
|
10115
|
-
} from "@wrongstack/core/utils";
|
|
10116
|
-
|
|
10117
|
-
// src/codebase-index/gitignore.ts
|
|
10118
|
-
import * as fs9 from "node:fs/promises";
|
|
10119
|
-
import * as path13 from "node:path";
|
|
10120
|
-
import { compileGlob } from "@wrongstack/core/utils";
|
|
10121
|
-
function globBody(glob) {
|
|
10122
|
-
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
10123
|
-
}
|
|
10124
|
-
function compileGitignore(lines) {
|
|
10125
|
-
const rules = [];
|
|
10126
|
-
for (const raw of lines) {
|
|
10127
|
-
let line = raw.replace(/\r$/, "");
|
|
10128
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
10129
|
-
line = line.trim();
|
|
10130
|
-
let negated = false;
|
|
10131
|
-
if (line.startsWith("!")) {
|
|
10132
|
-
negated = true;
|
|
10133
|
-
line = line.slice(1);
|
|
10134
|
-
}
|
|
10135
|
-
let dirOnly = false;
|
|
10136
|
-
if (line.endsWith("/")) {
|
|
10137
|
-
dirOnly = true;
|
|
10138
|
-
line = line.slice(0, -1);
|
|
10139
|
-
}
|
|
10140
|
-
if (!line) continue;
|
|
10141
|
-
const anchored = line.startsWith("/") || line.includes("/");
|
|
10142
|
-
if (line.startsWith("/")) line = line.slice(1);
|
|
10143
|
-
const body = globBody(line);
|
|
10144
|
-
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
10145
|
-
rules.push({
|
|
10146
|
-
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
10147
|
-
under: new RegExp(`${prefix}${body}/.*$`),
|
|
10148
|
-
negated,
|
|
10149
|
-
dirOnly
|
|
10150
|
-
});
|
|
10151
|
-
}
|
|
10152
|
-
return (relPath, isDir) => {
|
|
10153
|
-
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
10154
|
-
let ignored = false;
|
|
10155
|
-
for (const r of rules) {
|
|
10156
|
-
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
10157
|
-
if (re.test(p)) ignored = !r.negated;
|
|
10158
|
-
}
|
|
10159
|
-
return ignored;
|
|
10160
|
-
};
|
|
10161
|
-
}
|
|
10162
|
-
async function loadGitignoreMatcher(projectRoot) {
|
|
10163
|
-
let lines = [];
|
|
10164
|
-
try {
|
|
10165
|
-
const raw = await fs9.readFile(path13.join(projectRoot, ".gitignore"), "utf8");
|
|
10166
|
-
lines = raw.split("\n");
|
|
10167
|
-
} catch {
|
|
10168
|
-
}
|
|
10169
|
-
return compileGitignore(lines);
|
|
10170
|
-
}
|
|
10171
|
-
|
|
10172
|
-
// src/codebase-index/indexer.ts
|
|
10173
|
-
init_languages2();
|
|
10174
|
-
|
|
10175
|
-
// src/codebase-index/parser-dispatch.ts
|
|
10176
|
-
async function parseFileContent(file, content, lang) {
|
|
10177
|
-
switch (lang) {
|
|
10178
|
-
case "ts":
|
|
10179
|
-
case "tsx":
|
|
10180
|
-
case "js":
|
|
10181
|
-
case "jsx": {
|
|
10182
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
10183
|
-
return parseSymbols8({ file, content, lang });
|
|
10184
|
-
}
|
|
10185
|
-
case "go": {
|
|
10186
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
10187
|
-
return parseSymbols8({ file, content, lang: "go" });
|
|
10188
|
-
}
|
|
10189
|
-
case "py": {
|
|
10190
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
10191
|
-
return parseSymbols8({ file, content, lang: "py" });
|
|
10192
|
-
}
|
|
10193
|
-
case "rs": {
|
|
10194
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
10195
|
-
return parseSymbols8({ file, content, lang: "rs" });
|
|
10196
|
-
}
|
|
10197
|
-
case "json": {
|
|
10198
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
10199
|
-
return parseSymbols8({ file, content, lang: "json" });
|
|
10200
|
-
}
|
|
10201
|
-
case "yaml": {
|
|
10202
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
10203
|
-
return parseSymbols8({ file, content, lang: "yaml" });
|
|
10204
|
-
}
|
|
10205
|
-
default: {
|
|
10206
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
10207
|
-
return parseSymbols8({ file, content, lang });
|
|
10208
|
-
}
|
|
10209
|
-
}
|
|
10210
|
-
}
|
|
10188
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
10189
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
10190
|
+
import * as fs11 from "node:fs";
|
|
10191
|
+
import * as os5 from "node:os";
|
|
10192
|
+
import * as path16 from "node:path";
|
|
10193
|
+
import { fileURLToPath } from "node:url";
|
|
10194
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
10211
10195
|
|
|
10212
10196
|
// src/codebase-index/writer.ts
|
|
10213
|
-
import { expectDefined as
|
|
10214
|
-
import * as
|
|
10215
|
-
import * as
|
|
10216
|
-
|
|
10217
|
-
// src/codebase-index/schema.ts
|
|
10218
|
-
var SCHEMA_VERSION = 3;
|
|
10219
|
-
|
|
10220
|
-
// src/codebase-index/lsp-kind.ts
|
|
10221
|
-
function lspKindToInternalKind(k) {
|
|
10222
|
-
switch (k) {
|
|
10223
|
-
case 5 /* Class */:
|
|
10224
|
-
return "class";
|
|
10225
|
-
case 6 /* Method */:
|
|
10226
|
-
return "method";
|
|
10227
|
-
case 7 /* Property */:
|
|
10228
|
-
case 8 /* Field */:
|
|
10229
|
-
return "property";
|
|
10230
|
-
case 9 /* Constructor */:
|
|
10231
|
-
return "class";
|
|
10232
|
-
case 10 /* Enum */:
|
|
10233
|
-
return "enum";
|
|
10234
|
-
case 11 /* Interface */:
|
|
10235
|
-
return "interface";
|
|
10236
|
-
case 12 /* Function */:
|
|
10237
|
-
return "function";
|
|
10238
|
-
case 13 /* Variable */:
|
|
10239
|
-
return "var";
|
|
10240
|
-
case 14 /* Constant */:
|
|
10241
|
-
return "const";
|
|
10242
|
-
case 22 /* EnumMember */:
|
|
10243
|
-
return "enum";
|
|
10244
|
-
case 26 /* TypeParameter */:
|
|
10245
|
-
return "type";
|
|
10246
|
-
case 3 /* Namespace */:
|
|
10247
|
-
return "namespace";
|
|
10248
|
-
default:
|
|
10249
|
-
return null;
|
|
10250
|
-
}
|
|
10251
|
-
}
|
|
10197
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
10198
|
+
import * as fs10 from "node:fs";
|
|
10199
|
+
import * as path15 from "node:path";
|
|
10252
10200
|
|
|
10253
10201
|
// src/codebase-index/bm25.ts
|
|
10254
10202
|
var K1 = 1.5;
|
|
@@ -10339,6 +10287,42 @@ var Bm25Index = class {
|
|
|
10339
10287
|
}
|
|
10340
10288
|
};
|
|
10341
10289
|
|
|
10290
|
+
// src/codebase-index/lsp-kind.ts
|
|
10291
|
+
function lspKindToInternalKind(k) {
|
|
10292
|
+
switch (k) {
|
|
10293
|
+
case 5 /* Class */:
|
|
10294
|
+
return "class";
|
|
10295
|
+
case 6 /* Method */:
|
|
10296
|
+
return "method";
|
|
10297
|
+
case 7 /* Property */:
|
|
10298
|
+
case 8 /* Field */:
|
|
10299
|
+
return "property";
|
|
10300
|
+
case 9 /* Constructor */:
|
|
10301
|
+
return "class";
|
|
10302
|
+
case 10 /* Enum */:
|
|
10303
|
+
return "enum";
|
|
10304
|
+
case 11 /* Interface */:
|
|
10305
|
+
return "interface";
|
|
10306
|
+
case 12 /* Function */:
|
|
10307
|
+
return "function";
|
|
10308
|
+
case 13 /* Variable */:
|
|
10309
|
+
return "var";
|
|
10310
|
+
case 14 /* Constant */:
|
|
10311
|
+
return "const";
|
|
10312
|
+
case 22 /* EnumMember */:
|
|
10313
|
+
return "enum";
|
|
10314
|
+
case 26 /* TypeParameter */:
|
|
10315
|
+
return "type";
|
|
10316
|
+
case 3 /* Namespace */:
|
|
10317
|
+
return "namespace";
|
|
10318
|
+
default:
|
|
10319
|
+
return null;
|
|
10320
|
+
}
|
|
10321
|
+
}
|
|
10322
|
+
|
|
10323
|
+
// src/codebase-index/schema.ts
|
|
10324
|
+
var SCHEMA_VERSION = 3;
|
|
10325
|
+
|
|
10342
10326
|
// src/codebase-index/sqlite-runtime.ts
|
|
10343
10327
|
import { createRequire } from "node:module";
|
|
10344
10328
|
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
@@ -10406,99 +10390,9 @@ function runSqliteWithRetry(fn) {
|
|
|
10406
10390
|
throw lastError;
|
|
10407
10391
|
}
|
|
10408
10392
|
|
|
10409
|
-
// src/codebase-index/writer-helpers.ts
|
|
10410
|
-
import { resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
|
|
10411
|
-
function escapeLike(value) {
|
|
10412
|
-
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
10413
|
-
}
|
|
10414
|
-
function assignRefsToSymbols(refs, symbols) {
|
|
10415
|
-
if (refs.length === 0 || symbols.length === 0) return [];
|
|
10416
|
-
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
10417
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10418
|
-
const assigned = [];
|
|
10419
|
-
for (const ref of refs) {
|
|
10420
|
-
let owner2;
|
|
10421
|
-
for (const symbol of ordered) {
|
|
10422
|
-
if (symbol.line > ref.line) break;
|
|
10423
|
-
owner2 = symbol;
|
|
10424
|
-
}
|
|
10425
|
-
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
10426
|
-
if (!owner2 || owner2.id <= 0) continue;
|
|
10427
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
10428
|
-
if (seen.has(key)) continue;
|
|
10429
|
-
seen.add(key);
|
|
10430
|
-
assigned.push({ ...ref, fromId: owner2.id });
|
|
10431
|
-
}
|
|
10432
|
-
return assigned;
|
|
10433
|
-
}
|
|
10434
|
-
function resolveIndexDir(projectRoot, override) {
|
|
10435
|
-
return override ?? resolveWstackPaths2({ projectRoot }).projectCodebaseIndex;
|
|
10436
|
-
}
|
|
10437
|
-
function codebaseIndexDirOverride(ctx) {
|
|
10438
|
-
const v = ctx.meta?.["codebaseIndexDir"];
|
|
10439
|
-
return typeof v === "string" ? v : void 0;
|
|
10440
|
-
}
|
|
10441
|
-
|
|
10442
|
-
// src/codebase-index/writer-schema.ts
|
|
10443
|
-
var METADATA_TABLE_SQL = `
|
|
10444
|
-
CREATE TABLE IF NOT EXISTS metadata (
|
|
10445
|
-
key TEXT PRIMARY KEY,
|
|
10446
|
-
value TEXT NOT NULL
|
|
10447
|
-
);
|
|
10448
|
-
`;
|
|
10449
|
-
var CORE_TABLES_SQL = `
|
|
10450
|
-
CREATE TABLE IF NOT EXISTS files (
|
|
10451
|
-
file TEXT PRIMARY KEY,
|
|
10452
|
-
lang TEXT NOT NULL,
|
|
10453
|
-
mtime_ms INTEGER NOT NULL,
|
|
10454
|
-
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
10455
|
-
last_indexed INTEGER NOT NULL
|
|
10456
|
-
);
|
|
10457
|
-
CREATE TABLE IF NOT EXISTS symbols (
|
|
10458
|
-
id INTEGER PRIMARY KEY,
|
|
10459
|
-
lang TEXT NOT NULL,
|
|
10460
|
-
kind TEXT NOT NULL,
|
|
10461
|
-
name TEXT NOT NULL,
|
|
10462
|
-
file TEXT NOT NULL,
|
|
10463
|
-
line INTEGER NOT NULL,
|
|
10464
|
-
col INTEGER NOT NULL,
|
|
10465
|
-
signature TEXT NOT NULL DEFAULT '',
|
|
10466
|
-
doc_comment TEXT NOT NULL DEFAULT '',
|
|
10467
|
-
scope TEXT NOT NULL DEFAULT '',
|
|
10468
|
-
text TEXT NOT NULL DEFAULT '',
|
|
10469
|
-
file_fk TEXT NOT NULL
|
|
10470
|
-
);
|
|
10471
|
-
`;
|
|
10472
|
-
var SYMBOL_INDEX_SQL = [
|
|
10473
|
-
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
10474
|
-
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
10475
|
-
"CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)",
|
|
10476
|
-
"CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)",
|
|
10477
|
-
"CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)",
|
|
10478
|
-
"CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)",
|
|
10479
|
-
"CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)"
|
|
10480
|
-
];
|
|
10481
|
-
var REFS_TABLE_SQL = `
|
|
10482
|
-
CREATE TABLE IF NOT EXISTS refs (
|
|
10483
|
-
id INTEGER PRIMARY KEY,
|
|
10484
|
-
from_id INTEGER NOT NULL,
|
|
10485
|
-
to_name TEXT NOT NULL,
|
|
10486
|
-
to_id INTEGER,
|
|
10487
|
-
call_type TEXT NOT NULL,
|
|
10488
|
-
line INTEGER NOT NULL
|
|
10489
|
-
);
|
|
10490
|
-
`;
|
|
10491
|
-
var REFS_INDEX_SQL = [
|
|
10492
|
-
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
10493
|
-
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
10494
|
-
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
10495
|
-
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
10496
|
-
];
|
|
10497
|
-
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
10498
|
-
|
|
10499
10393
|
// src/codebase-index/writer-admin.ts
|
|
10500
|
-
import * as
|
|
10501
|
-
import * as
|
|
10394
|
+
import * as fs9 from "node:fs";
|
|
10395
|
+
import * as path13 from "node:path";
|
|
10502
10396
|
var DB_FILE = "index.db";
|
|
10503
10397
|
function getAllIndexableWithStatement(stmt) {
|
|
10504
10398
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10557,7 +10451,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10557
10451
|
}
|
|
10558
10452
|
function getIndexDbSizeBytes(indexDir) {
|
|
10559
10453
|
try {
|
|
10560
|
-
return
|
|
10454
|
+
return fs9.statSync(path13.join(indexDir, DB_FILE)).size;
|
|
10561
10455
|
} catch {
|
|
10562
10456
|
return 0;
|
|
10563
10457
|
}
|
|
@@ -10624,7 +10518,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
10624
10518
|
}
|
|
10625
10519
|
|
|
10626
10520
|
// src/codebase-index/writer-graph-helpers.ts
|
|
10627
|
-
import * as
|
|
10521
|
+
import * as path14 from "node:path";
|
|
10628
10522
|
function derivePackage(filePath) {
|
|
10629
10523
|
const f = filePath.replace(/\\/g, "/");
|
|
10630
10524
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -10739,16 +10633,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10739
10633
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10740
10634
|
if (!moduleName.startsWith(".")) return void 0;
|
|
10741
10635
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10742
|
-
const absolute =
|
|
10743
|
-
|
|
10636
|
+
const absolute = path14.posix.normalize(
|
|
10637
|
+
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10744
10638
|
);
|
|
10745
|
-
const extension =
|
|
10639
|
+
const extension = path14.posix.extname(absolute);
|
|
10746
10640
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10747
10641
|
const candidates = [
|
|
10748
10642
|
absolute,
|
|
10749
10643
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10750
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10751
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10644
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10645
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10752
10646
|
];
|
|
10753
10647
|
const indexedByPortablePath = new Map(
|
|
10754
10648
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -10968,6 +10862,39 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
|
|
|
10968
10862
|
return { nodes, edges };
|
|
10969
10863
|
}
|
|
10970
10864
|
|
|
10865
|
+
// src/codebase-index/writer-helpers.ts
|
|
10866
|
+
import { resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
|
|
10867
|
+
function escapeLike(value) {
|
|
10868
|
+
return value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
10869
|
+
}
|
|
10870
|
+
function assignRefsToSymbols(refs, symbols) {
|
|
10871
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
10872
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
10873
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10874
|
+
const assigned = [];
|
|
10875
|
+
for (const ref of refs) {
|
|
10876
|
+
let owner2;
|
|
10877
|
+
for (const symbol of ordered) {
|
|
10878
|
+
if (symbol.line > ref.line) break;
|
|
10879
|
+
owner2 = symbol;
|
|
10880
|
+
}
|
|
10881
|
+
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
10882
|
+
if (!owner2 || owner2.id <= 0) continue;
|
|
10883
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
10884
|
+
if (seen.has(key)) continue;
|
|
10885
|
+
seen.add(key);
|
|
10886
|
+
assigned.push({ ...ref, fromId: owner2.id });
|
|
10887
|
+
}
|
|
10888
|
+
return assigned;
|
|
10889
|
+
}
|
|
10890
|
+
function resolveIndexDir(projectRoot, override) {
|
|
10891
|
+
return override ?? resolveWstackPaths2({ projectRoot }).projectCodebaseIndex;
|
|
10892
|
+
}
|
|
10893
|
+
function codebaseIndexDirOverride(ctx) {
|
|
10894
|
+
const v = ctx.meta?.["codebaseIndexDir"];
|
|
10895
|
+
return typeof v === "string" ? v : void 0;
|
|
10896
|
+
}
|
|
10897
|
+
|
|
10971
10898
|
// src/codebase-index/writer-pragmas.ts
|
|
10972
10899
|
import { sqliteCachePragmas } from "@wrongstack/core/utils";
|
|
10973
10900
|
function applyIndexStorePragmas(db) {
|
|
@@ -10986,6 +10913,63 @@ function applyIndexStorePragmas(db) {
|
|
|
10986
10913
|
}
|
|
10987
10914
|
}
|
|
10988
10915
|
|
|
10916
|
+
// src/codebase-index/writer-schema.ts
|
|
10917
|
+
var METADATA_TABLE_SQL = `
|
|
10918
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
10919
|
+
key TEXT PRIMARY KEY,
|
|
10920
|
+
value TEXT NOT NULL
|
|
10921
|
+
);
|
|
10922
|
+
`;
|
|
10923
|
+
var CORE_TABLES_SQL = `
|
|
10924
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
10925
|
+
file TEXT PRIMARY KEY,
|
|
10926
|
+
lang TEXT NOT NULL,
|
|
10927
|
+
mtime_ms INTEGER NOT NULL,
|
|
10928
|
+
symbol_count INTEGER NOT NULL DEFAULT 0,
|
|
10929
|
+
last_indexed INTEGER NOT NULL
|
|
10930
|
+
);
|
|
10931
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
10932
|
+
id INTEGER PRIMARY KEY,
|
|
10933
|
+
lang TEXT NOT NULL,
|
|
10934
|
+
kind TEXT NOT NULL,
|
|
10935
|
+
name TEXT NOT NULL,
|
|
10936
|
+
file TEXT NOT NULL,
|
|
10937
|
+
line INTEGER NOT NULL,
|
|
10938
|
+
col INTEGER NOT NULL,
|
|
10939
|
+
signature TEXT NOT NULL DEFAULT '',
|
|
10940
|
+
doc_comment TEXT NOT NULL DEFAULT '',
|
|
10941
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
10942
|
+
text TEXT NOT NULL DEFAULT '',
|
|
10943
|
+
file_fk TEXT NOT NULL
|
|
10944
|
+
);
|
|
10945
|
+
`;
|
|
10946
|
+
var SYMBOL_INDEX_SQL = [
|
|
10947
|
+
"CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)",
|
|
10948
|
+
"CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)",
|
|
10949
|
+
"CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)",
|
|
10950
|
+
"CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)",
|
|
10951
|
+
"CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)",
|
|
10952
|
+
"CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)",
|
|
10953
|
+
"CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)"
|
|
10954
|
+
];
|
|
10955
|
+
var REFS_TABLE_SQL = `
|
|
10956
|
+
CREATE TABLE IF NOT EXISTS refs (
|
|
10957
|
+
id INTEGER PRIMARY KEY,
|
|
10958
|
+
from_id INTEGER NOT NULL,
|
|
10959
|
+
to_name TEXT NOT NULL,
|
|
10960
|
+
to_id INTEGER,
|
|
10961
|
+
call_type TEXT NOT NULL,
|
|
10962
|
+
line INTEGER NOT NULL
|
|
10963
|
+
);
|
|
10964
|
+
`;
|
|
10965
|
+
var REFS_INDEX_SQL = [
|
|
10966
|
+
"CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)",
|
|
10967
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)",
|
|
10968
|
+
"CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)",
|
|
10969
|
+
"CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)"
|
|
10970
|
+
];
|
|
10971
|
+
var SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
10972
|
+
|
|
10989
10973
|
// src/codebase-index/writer-search-helpers.ts
|
|
10990
10974
|
function normalizeSearchLimit(limit) {
|
|
10991
10975
|
return typeof limit === "number" && Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : void 0;
|
|
@@ -11174,9 +11158,9 @@ var IndexStore = class _IndexStore {
|
|
|
11174
11158
|
}
|
|
11175
11159
|
constructor(projectRoot, opts = {}) {
|
|
11176
11160
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
11177
|
-
|
|
11161
|
+
fs10.mkdirSync(this.indexDir, { recursive: true });
|
|
11178
11162
|
const Database = loadDatabaseSync();
|
|
11179
|
-
this.db = new Database(
|
|
11163
|
+
this.db = new Database(path15.join(this.indexDir, DB_FILE2));
|
|
11180
11164
|
applyIndexStorePragmas(this.db);
|
|
11181
11165
|
this.initSchema();
|
|
11182
11166
|
}
|
|
@@ -11194,9 +11178,15 @@ var IndexStore = class _IndexStore {
|
|
|
11194
11178
|
DROP TABLE IF EXISTS refs;
|
|
11195
11179
|
`);
|
|
11196
11180
|
this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
11197
|
-
this.stmt("UPDATE metadata SET value = ? WHERE key = ?").run(
|
|
11181
|
+
this.stmt("UPDATE metadata SET value = ? WHERE key = ?").run(
|
|
11182
|
+
String(SCHEMA_VERSION),
|
|
11183
|
+
"version"
|
|
11184
|
+
);
|
|
11198
11185
|
} else if (storedVersion === null) {
|
|
11199
|
-
this.stmt("INSERT INTO metadata(key, value) VALUES (?, ?)").run(
|
|
11186
|
+
this.stmt("INSERT INTO metadata(key, value) VALUES (?, ?)").run(
|
|
11187
|
+
"version",
|
|
11188
|
+
String(SCHEMA_VERSION)
|
|
11189
|
+
);
|
|
11200
11190
|
}
|
|
11201
11191
|
this.db.exec(CORE_TABLES_SQL);
|
|
11202
11192
|
for (const sql of SYMBOL_INDEX_SQL) this.db.exec(sql);
|
|
@@ -11213,7 +11203,9 @@ var IndexStore = class _IndexStore {
|
|
|
11213
11203
|
);
|
|
11214
11204
|
if (symbolCount !== ftsCount) {
|
|
11215
11205
|
this.db.exec("DELETE FROM symbols_fts");
|
|
11216
|
-
const rows = this.stmt(
|
|
11206
|
+
const rows = this.stmt(
|
|
11207
|
+
"SELECT id, name, signature, doc_comment FROM symbols ORDER BY id"
|
|
11208
|
+
).all();
|
|
11217
11209
|
bulkInsertFtsWithStatement(
|
|
11218
11210
|
(sql) => this.stmt(sql),
|
|
11219
11211
|
_IndexStore.MAX_SQL_VARS,
|
|
@@ -11435,7 +11427,9 @@ var IndexStore = class _IndexStore {
|
|
|
11435
11427
|
const limitSql = limit !== void 0 ? " LIMIT ?" : "";
|
|
11436
11428
|
const sql = `SELECT id, lang, kind, name, file, line, col, signature, doc_comment, text FROM symbols ${where}${limitSql}`;
|
|
11437
11429
|
const binds = limit !== void 0 ? [...values, limit] : values;
|
|
11438
|
-
const rows = this.stmt(sql).all(
|
|
11430
|
+
const rows = this.stmt(sql).all(
|
|
11431
|
+
...binds
|
|
11432
|
+
);
|
|
11439
11433
|
return rows.map((row) => mapWriterSearchRow(row, filter?.lspKind));
|
|
11440
11434
|
}
|
|
11441
11435
|
/** Shared WHERE builder for {@link search} / empty-query ranked totals. */
|
|
@@ -11581,13 +11575,13 @@ var IndexStore = class _IndexStore {
|
|
|
11581
11575
|
if (rankDiff !== 0) return rankDiff;
|
|
11582
11576
|
const scoreDiff = b.score - a.score;
|
|
11583
11577
|
if (scoreDiff !== 0) return scoreDiff;
|
|
11584
|
-
const left =
|
|
11585
|
-
const right =
|
|
11578
|
+
const left = expectDefined2(candidateById.get(a.id));
|
|
11579
|
+
const right = expectDefined2(candidateById.get(b.id));
|
|
11586
11580
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
11587
11581
|
});
|
|
11588
11582
|
const qTokens = tokenise(query);
|
|
11589
11583
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
11590
|
-
const c =
|
|
11584
|
+
const c = expectDefined2(candidateById.get(id));
|
|
11591
11585
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
11592
11586
|
});
|
|
11593
11587
|
return { results, total: candidates.length };
|
|
@@ -11611,7 +11605,9 @@ var IndexStore = class _IndexStore {
|
|
|
11611
11605
|
}
|
|
11612
11606
|
setLastIndexed(ts2) {
|
|
11613
11607
|
this.runWithRetry(() => {
|
|
11614
|
-
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)").run(
|
|
11608
|
+
this.stmt("INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)").run(
|
|
11609
|
+
String(ts2)
|
|
11610
|
+
);
|
|
11615
11611
|
});
|
|
11616
11612
|
}
|
|
11617
11613
|
getMetadata(key) {
|
|
@@ -11722,7 +11718,9 @@ var IndexStore = class _IndexStore {
|
|
|
11722
11718
|
this.stmt(
|
|
11723
11719
|
`DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file IN (${placeholders}))`
|
|
11724
11720
|
).run(...options.deleteForFiles);
|
|
11725
|
-
this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(
|
|
11721
|
+
this.stmt(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(
|
|
11722
|
+
...options.deleteForFiles
|
|
11723
|
+
);
|
|
11726
11724
|
}
|
|
11727
11725
|
const totalSymbols = entries.reduce((n, e) => n + e.symbols.length, 0);
|
|
11728
11726
|
let nextId = this.allocateSymbolIds(totalSymbols);
|
|
@@ -11766,11 +11764,7 @@ var IndexStore = class _IndexStore {
|
|
|
11766
11764
|
this.ftsAvailable,
|
|
11767
11765
|
ftsRows
|
|
11768
11766
|
);
|
|
11769
|
-
bulkInsertRefsWithStatement(
|
|
11770
|
-
(sql) => this.stmt(sql),
|
|
11771
|
-
_IndexStore.MAX_SQL_VARS,
|
|
11772
|
-
refsToInsert
|
|
11773
|
-
);
|
|
11767
|
+
bulkInsertRefsWithStatement((sql) => this.stmt(sql), _IndexStore.MAX_SQL_VARS, refsToInsert);
|
|
11774
11768
|
const upsertStmt = this.stmt(
|
|
11775
11769
|
`INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)
|
|
11776
11770
|
VALUES (?, ?, ?, ?, ?)
|
|
@@ -11799,7 +11793,9 @@ var IndexStore = class _IndexStore {
|
|
|
11799
11793
|
*/
|
|
11800
11794
|
deleteRefsForFile(file) {
|
|
11801
11795
|
this.runWithRetry(() => {
|
|
11802
|
-
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
11796
|
+
this.stmt("DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)").run(
|
|
11797
|
+
file
|
|
11798
|
+
);
|
|
11803
11799
|
});
|
|
11804
11800
|
}
|
|
11805
11801
|
/**
|
|
@@ -11954,9 +11950,7 @@ var IndexStore = class _IndexStore {
|
|
|
11954
11950
|
* build the full symbol universe for the reachability scan.
|
|
11955
11951
|
*/
|
|
11956
11952
|
getAllSymbols() {
|
|
11957
|
-
return this.stmt(
|
|
11958
|
-
"SELECT id, name, file, kind, line FROM symbols ORDER BY id"
|
|
11959
|
-
).all().map((r) => ({ ...r, kind: r.kind }));
|
|
11953
|
+
return this.stmt("SELECT id, name, file, kind, line FROM symbols ORDER BY id").all().map((r) => ({ ...r, kind: r.kind }));
|
|
11960
11954
|
}
|
|
11961
11955
|
/**
|
|
11962
11956
|
* Returns every resolved reference (to_id IS NOT NULL). Used by
|
|
@@ -11968,6 +11962,25 @@ var IndexStore = class _IndexStore {
|
|
|
11968
11962
|
"SELECT from_id AS fromId, to_id AS toId, call_type AS callType FROM refs WHERE to_id IS NOT NULL"
|
|
11969
11963
|
).all();
|
|
11970
11964
|
}
|
|
11965
|
+
/**
|
|
11966
|
+
* Returns ALL import refs (including unresolved) with their source-file
|
|
11967
|
+
* path and resolved target id. Used by the dead-code scan's file-level
|
|
11968
|
+
* graph traversal to handle barrel-only entry points where no symbol
|
|
11969
|
+
* carries the ref.
|
|
11970
|
+
*
|
|
11971
|
+
* Refs whose `from_id` doesn't match a known symbol (e.g. pure-barrel
|
|
11972
|
+
* files with no declarations) will have `sourceFile === null`.
|
|
11973
|
+
*/
|
|
11974
|
+
getAllImportRefs() {
|
|
11975
|
+
return this.stmt(
|
|
11976
|
+
`SELECT s.file AS sourceFile, r.to_name AS toName, r.to_id AS toId,
|
|
11977
|
+
r.call_type AS callType, r.line
|
|
11978
|
+
FROM refs r
|
|
11979
|
+
LEFT JOIN symbols s ON r.from_id = s.id
|
|
11980
|
+
WHERE r.call_type = 'import'
|
|
11981
|
+
ORDER BY r.line`
|
|
11982
|
+
).all();
|
|
11983
|
+
}
|
|
11971
11984
|
close() {
|
|
11972
11985
|
this.stmtCache.clear();
|
|
11973
11986
|
this.bm25Dirty = true;
|
|
@@ -11982,1174 +11995,1285 @@ var indexStorePool = new StorePool(
|
|
|
11982
11995
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
11983
11996
|
);
|
|
11984
11997
|
|
|
11985
|
-
// src/codebase-index/
|
|
11986
|
-
var
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
function
|
|
11991
|
-
|
|
11998
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
11999
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
12000
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
12001
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
12002
|
+
var buildIdCache;
|
|
12003
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
12004
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path16.resolve(entrypoint);
|
|
12005
|
+
try {
|
|
12006
|
+
const stat18 = fs11.statSync(file);
|
|
12007
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
12008
|
+
return buildIdCache.buildId;
|
|
12009
|
+
}
|
|
12010
|
+
const buildId = createHash4("sha256").update(fs11.readFileSync(file)).digest("hex").slice(0, 24);
|
|
12011
|
+
buildIdCache = { file, mtimeMs: stat18.mtimeMs, size: stat18.size, buildId };
|
|
12012
|
+
return buildId;
|
|
12013
|
+
} catch {
|
|
12014
|
+
return `unreadable:${path16.basename(file)}`;
|
|
12015
|
+
}
|
|
11992
12016
|
}
|
|
11993
|
-
function
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12017
|
+
function normalizeLocalPath(value) {
|
|
12018
|
+
const resolved = path16.resolve(value);
|
|
12019
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11997
12020
|
}
|
|
11998
|
-
function
|
|
11999
|
-
|
|
12021
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
12022
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
12023
|
+
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
12000
12024
|
}
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
const rel = path22.relative(projectRoot, file);
|
|
12008
|
-
return rel !== "" && !rel.startsWith(`..${path22.sep}`) && rel !== ".." && !path22.isAbsolute(rel);
|
|
12025
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
12026
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
12027
|
+
if (process.platform === "win32") {
|
|
12028
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
12029
|
+
}
|
|
12030
|
+
return path16.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
12009
12031
|
}
|
|
12010
|
-
function
|
|
12011
|
-
|
|
12012
|
-
|
|
12032
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
12033
|
+
return path16.join(
|
|
12034
|
+
path16.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
12035
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
12036
|
+
);
|
|
12013
12037
|
}
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12038
|
+
|
|
12039
|
+
// src/codebase-index/project-server-protocol.ts
|
|
12040
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
12041
|
+
function encodeProjectServerMessage(message) {
|
|
12042
|
+
return `${JSON.stringify(message)}
|
|
12043
|
+
`;
|
|
12017
12044
|
}
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
])
|
|
12051
|
-
]);
|
|
12052
|
-
throwIfAborted(signal);
|
|
12053
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
12054
|
-
const deleted = /* @__PURE__ */ new Set();
|
|
12055
|
-
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12056
|
-
for (let i = 0; i < statusRecords.length; i++) {
|
|
12057
|
-
const record = statusRecords[i];
|
|
12058
|
-
if (!record) continue;
|
|
12059
|
-
const status = record.slice(0, 2);
|
|
12060
|
-
const changedPath = path22.resolve(projectRoot, record.slice(3));
|
|
12061
|
-
dirty.add(changedPath);
|
|
12062
|
-
if (status.includes("D")) deleted.add(changedPath);
|
|
12063
|
-
if (status.includes("R") || status.includes("C")) {
|
|
12064
|
-
const source = statusRecords[++i];
|
|
12065
|
-
if (source) dirty.add(path22.resolve(projectRoot, source));
|
|
12045
|
+
|
|
12046
|
+
// src/codebase-index/project-server-client.ts
|
|
12047
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
12048
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
12049
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
12050
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
12051
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
12052
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
12053
|
+
constructor(message, pid) {
|
|
12054
|
+
super(message);
|
|
12055
|
+
this.pid = pid;
|
|
12056
|
+
}
|
|
12057
|
+
pid;
|
|
12058
|
+
name = "StaleProjectIndexServerError";
|
|
12059
|
+
};
|
|
12060
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
12061
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
12062
|
+
var latestConnectionState = {
|
|
12063
|
+
status: "offline",
|
|
12064
|
+
connected: false
|
|
12065
|
+
};
|
|
12066
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
12067
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
12068
|
+
return { kind: "inline-requested" };
|
|
12069
|
+
}
|
|
12070
|
+
let builtUrl = null;
|
|
12071
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
12072
|
+
try {
|
|
12073
|
+
const url = new URL(rel, import.meta.url);
|
|
12074
|
+
if (url.protocol === "file:" && fs12.existsSync(fileURLToPath2(url))) {
|
|
12075
|
+
builtUrl = url;
|
|
12076
|
+
break;
|
|
12066
12077
|
}
|
|
12078
|
+
} catch {
|
|
12067
12079
|
}
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12080
|
+
}
|
|
12081
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
12082
|
+
if (projectRoot !== void 0) {
|
|
12083
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12084
|
+
const check = checkUnixSocketPath(endpoint);
|
|
12085
|
+
if (!check.ok) {
|
|
12086
|
+
return {
|
|
12087
|
+
kind: "endpoint-invalid",
|
|
12088
|
+
endpoint,
|
|
12089
|
+
byteLength: check.byteLength,
|
|
12090
|
+
maxBytes: check.maxBytes
|
|
12091
|
+
};
|
|
12079
12092
|
}
|
|
12080
|
-
return {
|
|
12081
|
-
files,
|
|
12082
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12083
|
-
};
|
|
12084
|
-
} catch {
|
|
12085
|
-
return null;
|
|
12086
12093
|
}
|
|
12094
|
+
return { kind: "available", url: builtUrl };
|
|
12087
12095
|
}
|
|
12088
|
-
|
|
12089
|
-
const
|
|
12090
|
-
|
|
12096
|
+
function resolveProjectServerUrl() {
|
|
12097
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
12098
|
+
return availability.kind === "available" ? availability.url : null;
|
|
12099
|
+
}
|
|
12100
|
+
function projectIndexServerExpectedBuildId() {
|
|
12101
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
12102
|
+
if (override) return override;
|
|
12103
|
+
const url = resolveProjectServerUrl();
|
|
12104
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
12105
|
+
}
|
|
12106
|
+
function isProjectIndexServerAvailable() {
|
|
12107
|
+
return resolveProjectServerUrl() !== null;
|
|
12108
|
+
}
|
|
12109
|
+
function publishConnectionState(endpoint, state) {
|
|
12110
|
+
connectionStates.set(endpoint, state);
|
|
12111
|
+
latestConnectionState = state;
|
|
12112
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
12113
|
+
}
|
|
12114
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
12115
|
+
if (projectRoot) {
|
|
12116
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12117
|
+
const existing = connectionStates.get(endpoint);
|
|
12118
|
+
if (existing) return existing;
|
|
12119
|
+
if (!isProjectIndexServerAvailable()) {
|
|
12120
|
+
return { status: "unavailable", connected: false };
|
|
12121
|
+
}
|
|
12091
12122
|
return {
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12123
|
+
status: "offline",
|
|
12124
|
+
connected: false,
|
|
12125
|
+
projectRoot,
|
|
12126
|
+
indexDir,
|
|
12127
|
+
endpoint
|
|
12096
12128
|
};
|
|
12097
12129
|
}
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12102
|
-
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12103
|
-
let dirCount = 0;
|
|
12104
|
-
const walk2 = async (dir) => {
|
|
12105
|
-
throwIfAborted(signal);
|
|
12106
|
-
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12107
|
-
await yieldEventLoop();
|
|
12108
|
-
throwIfAborted(signal);
|
|
12109
|
-
}
|
|
12110
|
-
let entries;
|
|
12111
|
-
try {
|
|
12112
|
-
entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
12113
|
-
} catch (err) {
|
|
12114
|
-
complete = false;
|
|
12115
|
-
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12116
|
-
return;
|
|
12117
|
-
}
|
|
12118
|
-
dirCount++;
|
|
12119
|
-
for (const e of entries) {
|
|
12120
|
-
if (ignoreSet.has(e.name)) continue;
|
|
12121
|
-
const full = path22.join(dir, e.name);
|
|
12122
|
-
const rel = path22.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12123
|
-
if (e.isDirectory()) {
|
|
12124
|
-
if (isGitIgnored(rel, true)) continue;
|
|
12125
|
-
await walk2(full);
|
|
12126
|
-
} else if (e.isFile()) {
|
|
12127
|
-
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12128
|
-
const ext = path22.extname(e.name).toLowerCase();
|
|
12129
|
-
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12130
|
-
results.push(full);
|
|
12131
|
-
}
|
|
12132
|
-
}
|
|
12133
|
-
}
|
|
12134
|
-
};
|
|
12135
|
-
await walk2(projectRoot);
|
|
12136
|
-
return { files: results, complete, errors };
|
|
12130
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
12131
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
12132
|
+
return latestConnectionState;
|
|
12137
12133
|
}
|
|
12138
|
-
function
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
const seen = /* @__PURE__ */ new Set();
|
|
12142
|
-
const assigned = [];
|
|
12143
|
-
for (const ref of refs) {
|
|
12144
|
-
let owner2;
|
|
12145
|
-
for (const symbol of ordered) {
|
|
12146
|
-
if (symbol.line > ref.line) break;
|
|
12147
|
-
owner2 = symbol;
|
|
12148
|
-
}
|
|
12149
|
-
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12150
|
-
if (!owner2 || owner2.id <= 0) continue;
|
|
12151
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12152
|
-
if (seen.has(key)) continue;
|
|
12153
|
-
seen.add(key);
|
|
12154
|
-
assigned.push({ ...ref, fromId: owner2.id });
|
|
12155
|
-
}
|
|
12156
|
-
return assigned;
|
|
12134
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
12135
|
+
connectionStateListeners.add(listener);
|
|
12136
|
+
return () => connectionStateListeners.delete(listener);
|
|
12157
12137
|
}
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
const
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
const
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
}
|
|
12188
|
-
if (langs && langs.length > 0) {
|
|
12189
|
-
const langSet = new Set(langs);
|
|
12190
|
-
files = files.filter((f) => {
|
|
12191
|
-
const lang = detectLang(f);
|
|
12192
|
-
return lang ? langSet.has(lang) : false;
|
|
12193
|
-
});
|
|
12194
|
-
}
|
|
12195
|
-
if (force) store.clearAll();
|
|
12196
|
-
const existingMeta = /* @__PURE__ */ new Map();
|
|
12197
|
-
if (!force) {
|
|
12198
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
12138
|
+
function remoteError(message, name) {
|
|
12139
|
+
if (name === "LockError") return new LockError(message);
|
|
12140
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
12141
|
+
const error = new Error(message);
|
|
12142
|
+
if (name && name !== "Error") error.name = name;
|
|
12143
|
+
return error;
|
|
12144
|
+
}
|
|
12145
|
+
function isProjectIndexServerHealth(value) {
|
|
12146
|
+
if (!value || typeof value !== "object") return false;
|
|
12147
|
+
const health = value;
|
|
12148
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
12149
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
12150
|
+
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";
|
|
12151
|
+
}
|
|
12152
|
+
function delay(ms) {
|
|
12153
|
+
return new Promise((resolve17) => {
|
|
12154
|
+
const timer = setTimeout(resolve17, ms);
|
|
12155
|
+
timer.unref?.();
|
|
12156
|
+
});
|
|
12157
|
+
}
|
|
12158
|
+
function cancellationError(signal) {
|
|
12159
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
12160
|
+
}
|
|
12161
|
+
var ProjectServerConnection = class {
|
|
12162
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
12163
|
+
this.projectRoot = projectRoot;
|
|
12164
|
+
this.indexDir = indexDir;
|
|
12165
|
+
this.endpoint = endpoint;
|
|
12166
|
+
this.transition("offline");
|
|
12199
12167
|
}
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12168
|
+
projectRoot;
|
|
12169
|
+
indexDir;
|
|
12170
|
+
endpoint;
|
|
12171
|
+
socket = null;
|
|
12172
|
+
buffer = "";
|
|
12173
|
+
info = null;
|
|
12174
|
+
activity = null;
|
|
12175
|
+
health = null;
|
|
12176
|
+
healthCheck = null;
|
|
12177
|
+
connecting = null;
|
|
12178
|
+
connectResolve = null;
|
|
12179
|
+
connectReject = null;
|
|
12180
|
+
nextId = 1;
|
|
12181
|
+
pending = /* @__PURE__ */ new Map();
|
|
12182
|
+
transition(status, options = {}) {
|
|
12183
|
+
const previous = connectionStates.get(this.endpoint);
|
|
12184
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
12185
|
+
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);
|
|
12186
|
+
publishConnectionState(this.endpoint, {
|
|
12187
|
+
status,
|
|
12188
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
12189
|
+
projectRoot: this.projectRoot,
|
|
12190
|
+
indexDir: this.indexDir,
|
|
12191
|
+
endpoint: this.endpoint,
|
|
12192
|
+
pid,
|
|
12193
|
+
lastError,
|
|
12194
|
+
...this.activity ? { activity: this.activity } : {},
|
|
12195
|
+
...this.health ? { health: this.health } : {}
|
|
12211
12196
|
});
|
|
12212
|
-
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
12213
12197
|
}
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12198
|
+
isConnected() {
|
|
12199
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
12200
|
+
}
|
|
12201
|
+
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
12202
|
+
await this.ensureConnected(spawnIfMissing);
|
|
12203
|
+
if (this.healthCheck) return this.healthCheck;
|
|
12204
|
+
const startedAt = Date.now();
|
|
12205
|
+
this.healthCheck = this.request({ type: "ping" }, { timeoutMs }).then((server) => {
|
|
12206
|
+
const now2 = Date.now();
|
|
12207
|
+
this.health = {
|
|
12208
|
+
status: "healthy",
|
|
12209
|
+
checkedAt: now2,
|
|
12210
|
+
lastHealthyAt: now2,
|
|
12211
|
+
latencyMs: Math.max(0, now2 - startedAt),
|
|
12212
|
+
missedHeartbeats: 0,
|
|
12213
|
+
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
12214
|
+
};
|
|
12215
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12216
|
+
return this.health;
|
|
12217
|
+
}).catch((error) => {
|
|
12218
|
+
if (!this.isConnected()) throw error;
|
|
12219
|
+
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
12220
|
+
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
12221
|
+
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
12222
|
+
this.health = {
|
|
12223
|
+
status,
|
|
12224
|
+
checkedAt: Date.now(),
|
|
12225
|
+
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
12226
|
+
latencyMs: null,
|
|
12227
|
+
missedHeartbeats,
|
|
12228
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
12229
|
+
};
|
|
12230
|
+
this.transition(status, { pid: this.info?.pid, error });
|
|
12231
|
+
return this.health;
|
|
12232
|
+
}).finally(() => {
|
|
12233
|
+
this.healthCheck = null;
|
|
12234
|
+
});
|
|
12235
|
+
return this.healthCheck;
|
|
12236
|
+
}
|
|
12237
|
+
markResponsive() {
|
|
12238
|
+
const now2 = Date.now();
|
|
12239
|
+
this.health = {
|
|
12240
|
+
status: "healthy",
|
|
12241
|
+
checkedAt: now2,
|
|
12242
|
+
lastHealthyAt: now2,
|
|
12243
|
+
latencyMs: this.health?.latencyMs ?? null,
|
|
12244
|
+
missedHeartbeats: 0,
|
|
12245
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
12246
|
+
};
|
|
12247
|
+
}
|
|
12248
|
+
async call(op, args, options) {
|
|
12249
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
12250
|
+
await this.ensureConnected(true);
|
|
12251
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
12252
|
+
return this.request({ type: "request", op, args }, options);
|
|
12253
|
+
}
|
|
12254
|
+
async shutdownRemote(reason) {
|
|
12255
|
+
try {
|
|
12256
|
+
await this.ensureConnected(false);
|
|
12257
|
+
} catch {
|
|
12258
|
+
return { stopped: false, reason: "not-running" };
|
|
12228
12259
|
}
|
|
12229
|
-
const
|
|
12230
|
-
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
lang,
|
|
12255
|
-
parsed: null,
|
|
12256
|
-
error: `file too large (${stat18.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
12257
|
-
};
|
|
12258
|
-
}
|
|
12259
|
-
const meta = existingMeta.get(file);
|
|
12260
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat18.mtimeMs)) {
|
|
12261
|
-
return { file, stat: stat18, lang, parsed: null, skippedMeta: meta };
|
|
12262
|
-
}
|
|
12263
|
-
let content;
|
|
12264
|
-
try {
|
|
12265
|
-
content = await fs15.readFile(file, { encoding: "utf8", signal });
|
|
12266
|
-
} catch (e) {
|
|
12267
|
-
if (isAbortError(e)) throw e;
|
|
12268
|
-
return {
|
|
12269
|
-
file,
|
|
12270
|
-
stat: stat18,
|
|
12271
|
-
lang,
|
|
12272
|
-
parsed: null,
|
|
12273
|
-
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
12274
|
-
};
|
|
12275
|
-
}
|
|
12276
|
-
let parsed;
|
|
12277
|
-
try {
|
|
12278
|
-
parsed = await parseFileContent(file, content, lang);
|
|
12279
|
-
} catch (e) {
|
|
12280
|
-
return {
|
|
12281
|
-
file,
|
|
12282
|
-
stat: stat18,
|
|
12283
|
-
lang,
|
|
12284
|
-
parsed: null,
|
|
12285
|
-
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
12286
|
-
};
|
|
12287
|
-
}
|
|
12288
|
-
return { file, stat: stat18, lang, parsed, content };
|
|
12289
|
-
}
|
|
12290
|
-
)
|
|
12260
|
+
const pid = this.info?.pid;
|
|
12261
|
+
try {
|
|
12262
|
+
this.transition("stopping", { pid });
|
|
12263
|
+
await this.request(
|
|
12264
|
+
{ type: "shutdown", reason },
|
|
12265
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
12266
|
+
);
|
|
12267
|
+
return { stopped: true, pid };
|
|
12268
|
+
} catch (error) {
|
|
12269
|
+
const forceKilled = this.forceKillKnownServer();
|
|
12270
|
+
return {
|
|
12271
|
+
stopped: forceKilled,
|
|
12272
|
+
pid,
|
|
12273
|
+
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
12274
|
+
};
|
|
12275
|
+
} finally {
|
|
12276
|
+
this.close();
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
async configure(watchExternal, debounceMs) {
|
|
12280
|
+
await this.ensureConnected(true);
|
|
12281
|
+
const startedAt = Date.now();
|
|
12282
|
+
const result = await this.request(
|
|
12283
|
+
{ type: "configure", watchExternal, debounceMs },
|
|
12284
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
12291
12285
|
);
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
}
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12286
|
+
if (isProjectIndexServerHealth(result.health)) {
|
|
12287
|
+
const now2 = Date.now();
|
|
12288
|
+
this.health = {
|
|
12289
|
+
status: "healthy",
|
|
12290
|
+
checkedAt: now2,
|
|
12291
|
+
lastHealthyAt: now2,
|
|
12292
|
+
latencyMs: Math.max(0, now2 - startedAt),
|
|
12293
|
+
missedHeartbeats: 0,
|
|
12294
|
+
server: result.health
|
|
12295
|
+
};
|
|
12296
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12297
|
+
}
|
|
12298
|
+
}
|
|
12299
|
+
close() {
|
|
12300
|
+
const socket = this.socket;
|
|
12301
|
+
this.socket = null;
|
|
12302
|
+
this.info = null;
|
|
12303
|
+
this.activity = null;
|
|
12304
|
+
this.health = null;
|
|
12305
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
12306
|
+
this.connectResolve = null;
|
|
12307
|
+
this.connectReject = null;
|
|
12308
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
12309
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
12310
|
+
this.transition("offline");
|
|
12311
|
+
maybeStopHeartbeatLoop();
|
|
12312
|
+
}
|
|
12313
|
+
request(message, options) {
|
|
12314
|
+
const socket = this.socket;
|
|
12315
|
+
if (!socket || socket.destroyed) {
|
|
12316
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
12317
|
+
}
|
|
12318
|
+
const id = this.nextId++;
|
|
12319
|
+
return new Promise((resolve17, reject) => {
|
|
12320
|
+
const timer = setTimeout(() => {
|
|
12321
|
+
const entry = this.pending.get(id);
|
|
12322
|
+
if (!entry) return;
|
|
12323
|
+
this.pending.delete(id);
|
|
12324
|
+
this.write({ type: "cancel", id });
|
|
12325
|
+
const error = new IndexTimeoutError(
|
|
12326
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
12327
|
+
);
|
|
12328
|
+
this.cleanupPending(entry);
|
|
12329
|
+
entry.reject(error);
|
|
12330
|
+
}, options.timeoutMs);
|
|
12331
|
+
timer.unref?.();
|
|
12332
|
+
const signal = options.signal;
|
|
12333
|
+
const onAbort = signal ? () => {
|
|
12334
|
+
const entry = this.pending.get(id);
|
|
12335
|
+
if (!entry) return;
|
|
12336
|
+
this.pending.delete(id);
|
|
12337
|
+
this.write({ type: "cancel", id });
|
|
12338
|
+
this.cleanupPending(entry);
|
|
12339
|
+
entry.reject(cancellationError(signal));
|
|
12340
|
+
} : void 0;
|
|
12341
|
+
this.pending.set(id, {
|
|
12342
|
+
resolve: resolve17,
|
|
12343
|
+
reject,
|
|
12344
|
+
timer,
|
|
12345
|
+
signal,
|
|
12346
|
+
onAbort,
|
|
12347
|
+
onProgress: options.onProgress
|
|
12348
|
+
});
|
|
12349
|
+
if (signal && onAbort) {
|
|
12350
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
12351
|
+
if (signal.aborted) {
|
|
12352
|
+
onAbort();
|
|
12353
|
+
return;
|
|
12354
|
+
}
|
|
12315
12355
|
}
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12356
|
+
this.write({ ...message, id });
|
|
12357
|
+
});
|
|
12358
|
+
}
|
|
12359
|
+
async ensureConnected(spawnIfMissing) {
|
|
12360
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
12361
|
+
if (this.connecting) return this.connecting;
|
|
12362
|
+
this.transition("connecting");
|
|
12363
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
12364
|
+
this.transition("error", { error });
|
|
12365
|
+
throw error;
|
|
12366
|
+
}).finally(() => {
|
|
12367
|
+
this.connecting = null;
|
|
12368
|
+
});
|
|
12369
|
+
return this.connecting;
|
|
12370
|
+
}
|
|
12371
|
+
async connectWithElection(spawnIfMissing) {
|
|
12372
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12373
|
+
let spawned = false;
|
|
12374
|
+
let staleAttempts = 0;
|
|
12375
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
12376
|
+
while (Date.now() < deadline) {
|
|
12377
|
+
try {
|
|
12378
|
+
await this.connectOnce();
|
|
12379
|
+
return;
|
|
12380
|
+
} catch (error) {
|
|
12381
|
+
lastError = error;
|
|
12382
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
12383
|
+
staleAttempts++;
|
|
12384
|
+
if (!spawnIfMissing) break;
|
|
12385
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12386
|
+
spawned = false;
|
|
12387
|
+
await delay(100);
|
|
12388
|
+
continue;
|
|
12326
12389
|
}
|
|
12327
|
-
continue;
|
|
12328
12390
|
}
|
|
12329
|
-
if (
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
12334
|
-
symbolCount: 0,
|
|
12335
|
-
lastIndexed: Date.now()
|
|
12336
|
-
});
|
|
12337
|
-
filesIndexed++;
|
|
12338
|
-
continue;
|
|
12391
|
+
if (!spawnIfMissing) break;
|
|
12392
|
+
if (!spawned) {
|
|
12393
|
+
this.spawnDetachedServer();
|
|
12394
|
+
spawned = true;
|
|
12339
12395
|
}
|
|
12340
|
-
|
|
12341
|
-
file,
|
|
12342
|
-
lang,
|
|
12343
|
-
symbols: parsed.symbols,
|
|
12344
|
-
refs: parsed.refs ?? [],
|
|
12345
|
-
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
12346
|
-
symbolCount: parsed.symbols.length
|
|
12347
|
-
});
|
|
12348
|
-
deleteForFiles.push(file);
|
|
12396
|
+
await delay(75);
|
|
12349
12397
|
}
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12398
|
+
throw lastError;
|
|
12399
|
+
}
|
|
12400
|
+
connectOnce() {
|
|
12401
|
+
this.socket?.destroy();
|
|
12402
|
+
this.socket = null;
|
|
12403
|
+
this.info = null;
|
|
12404
|
+
this.activity = null;
|
|
12405
|
+
this.health = null;
|
|
12406
|
+
this.buffer = "";
|
|
12407
|
+
return new Promise((resolve17, reject) => {
|
|
12408
|
+
const socket = net3.createConnection(this.endpoint);
|
|
12409
|
+
this.socket = socket;
|
|
12410
|
+
socket.setEncoding("utf8");
|
|
12411
|
+
const timer = setTimeout(() => {
|
|
12412
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
12413
|
+
socket.destroy();
|
|
12414
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12415
|
+
timer.unref?.();
|
|
12416
|
+
const finishResolve = () => {
|
|
12417
|
+
clearTimeout(timer);
|
|
12418
|
+
this.connectResolve = null;
|
|
12419
|
+
this.connectReject = null;
|
|
12420
|
+
resolve17();
|
|
12421
|
+
};
|
|
12422
|
+
const finishReject = (error) => {
|
|
12423
|
+
clearTimeout(timer);
|
|
12424
|
+
this.connectResolve = null;
|
|
12425
|
+
this.connectReject = null;
|
|
12426
|
+
reject(error);
|
|
12427
|
+
};
|
|
12428
|
+
this.connectResolve = finishResolve;
|
|
12429
|
+
this.connectReject = finishReject;
|
|
12430
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
12431
|
+
socket.on("error", (error) => {
|
|
12432
|
+
if (!this.info) finishReject(error);
|
|
12433
|
+
});
|
|
12434
|
+
socket.on("close", () => this.onClose(socket));
|
|
12435
|
+
});
|
|
12436
|
+
}
|
|
12437
|
+
onData(socket, chunk) {
|
|
12438
|
+
if (socket !== this.socket) return;
|
|
12439
|
+
this.buffer += chunk;
|
|
12440
|
+
while (true) {
|
|
12441
|
+
const newline = this.buffer.indexOf("\n");
|
|
12442
|
+
if (newline < 0) {
|
|
12443
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12444
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12390
12445
|
}
|
|
12446
|
+
return;
|
|
12447
|
+
}
|
|
12448
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12449
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12450
|
+
return;
|
|
12391
12451
|
}
|
|
12452
|
+
const line = this.buffer.slice(0, newline);
|
|
12453
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
12454
|
+
if (!line) continue;
|
|
12455
|
+
let message;
|
|
12456
|
+
try {
|
|
12457
|
+
message = JSON.parse(line);
|
|
12458
|
+
} catch {
|
|
12459
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12460
|
+
return;
|
|
12461
|
+
}
|
|
12462
|
+
this.onMessage(message);
|
|
12392
12463
|
}
|
|
12393
12464
|
}
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
if (
|
|
12397
|
-
|
|
12465
|
+
onMessage(message) {
|
|
12466
|
+
if (message.type === "hello") {
|
|
12467
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
12468
|
+
this.rejectStaleServer(
|
|
12469
|
+
message,
|
|
12470
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
12471
|
+
);
|
|
12472
|
+
return;
|
|
12473
|
+
}
|
|
12474
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
12475
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
12476
|
+
this.rejectStaleServer(
|
|
12477
|
+
message,
|
|
12478
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
12479
|
+
);
|
|
12480
|
+
return;
|
|
12398
12481
|
}
|
|
12482
|
+
this.info = message;
|
|
12483
|
+
this.markResponsive();
|
|
12484
|
+
this.transition("connected", { pid: message.pid });
|
|
12485
|
+
ensureHeartbeatLoop();
|
|
12486
|
+
this.connectResolve?.();
|
|
12487
|
+
return;
|
|
12399
12488
|
}
|
|
12489
|
+
if (message.type === "index-state") {
|
|
12490
|
+
this.activity = message.state;
|
|
12491
|
+
this.markResponsive();
|
|
12492
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12493
|
+
return;
|
|
12494
|
+
}
|
|
12495
|
+
const entry = this.pending.get(message.id);
|
|
12496
|
+
if (!entry) return;
|
|
12497
|
+
this.markResponsive();
|
|
12498
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
12499
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
12500
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12501
|
+
}
|
|
12502
|
+
if (message.type === "progress") {
|
|
12503
|
+
entry.onProgress?.(message.current, message.total);
|
|
12504
|
+
return;
|
|
12505
|
+
}
|
|
12506
|
+
this.pending.delete(message.id);
|
|
12507
|
+
this.cleanupPending(entry);
|
|
12508
|
+
if (message.ok) entry.resolve(message.result);
|
|
12509
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
12400
12510
|
}
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
}
|
|
12416
|
-
|
|
12417
|
-
// src/codebase-index/index-service.ts
|
|
12418
|
-
async function indexService(args, hooks = {}) {
|
|
12419
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12420
|
-
try {
|
|
12421
|
-
return await runIndexerWithStore(store, {
|
|
12422
|
-
projectRoot: args.projectRoot,
|
|
12423
|
-
indexDir: args.indexDir,
|
|
12424
|
-
files: args.files,
|
|
12425
|
-
force: args.force,
|
|
12426
|
-
langs: args.langs,
|
|
12427
|
-
ignore: args.ignore,
|
|
12428
|
-
signal: hooks.signal,
|
|
12429
|
-
onProgress: hooks.onProgress
|
|
12430
|
-
});
|
|
12431
|
-
} finally {
|
|
12432
|
-
indexStorePool.release(store);
|
|
12433
|
-
}
|
|
12434
|
-
}
|
|
12435
|
-
function searchService(args) {
|
|
12436
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12437
|
-
try {
|
|
12438
|
-
return store.searchRanked(
|
|
12439
|
-
args.query,
|
|
12440
|
-
{
|
|
12441
|
-
kind: args.kind,
|
|
12442
|
-
lang: args.lang,
|
|
12443
|
-
file: args.file,
|
|
12444
|
-
lspKind: args.lspKind
|
|
12445
|
-
},
|
|
12446
|
-
args.limit
|
|
12447
|
-
);
|
|
12448
|
-
} finally {
|
|
12449
|
-
indexStorePool.release(store);
|
|
12450
|
-
}
|
|
12451
|
-
}
|
|
12452
|
-
function statsService(args) {
|
|
12453
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12454
|
-
try {
|
|
12455
|
-
return store.getStats();
|
|
12456
|
-
} finally {
|
|
12457
|
-
indexStorePool.release(store);
|
|
12458
|
-
}
|
|
12459
|
-
}
|
|
12460
|
-
function packageGraphService(args) {
|
|
12461
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12462
|
-
try {
|
|
12463
|
-
return store.getPackageGraph();
|
|
12464
|
-
} finally {
|
|
12465
|
-
indexStorePool.release(store);
|
|
12466
|
-
}
|
|
12467
|
-
}
|
|
12468
|
-
function fileGraphService(args) {
|
|
12469
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12470
|
-
try {
|
|
12471
|
-
return store.getFileGraph(args.packageFilter);
|
|
12472
|
-
} finally {
|
|
12473
|
-
indexStorePool.release(store);
|
|
12474
|
-
}
|
|
12475
|
-
}
|
|
12476
|
-
function symbolGraphService(args) {
|
|
12477
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12478
|
-
try {
|
|
12479
|
-
return store.getSymbolGraph(args.fileFilter);
|
|
12480
|
-
} finally {
|
|
12481
|
-
indexStorePool.release(store);
|
|
12511
|
+
onClose(socket) {
|
|
12512
|
+
if (socket !== this.socket) return;
|
|
12513
|
+
const wasConnected = this.info !== null;
|
|
12514
|
+
this.socket = null;
|
|
12515
|
+
this.info = null;
|
|
12516
|
+
this.activity = null;
|
|
12517
|
+
this.health = null;
|
|
12518
|
+
const error = new Error("codebase-index server connection closed");
|
|
12519
|
+
this.connectReject?.(error);
|
|
12520
|
+
this.connectResolve = null;
|
|
12521
|
+
this.connectReject = null;
|
|
12522
|
+
this.rejectPending(error);
|
|
12523
|
+
if (wasConnected) this.transition("error", { error });
|
|
12524
|
+
maybeStopHeartbeatLoop();
|
|
12482
12525
|
}
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
// src/codebase-index/project-server-client.ts
|
|
12489
|
-
import { spawn as spawn7 } from "node:child_process";
|
|
12490
|
-
import * as fs17 from "node:fs";
|
|
12491
|
-
import * as net3 from "node:net";
|
|
12492
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12493
|
-
|
|
12494
|
-
// src/codebase-index/project-server-endpoint.ts
|
|
12495
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
12496
|
-
import * as fs16 from "node:fs";
|
|
12497
|
-
import * as os7 from "node:os";
|
|
12498
|
-
import * as path23 from "node:path";
|
|
12499
|
-
import { fileURLToPath } from "node:url";
|
|
12500
|
-
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
12501
|
-
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
12502
|
-
var buildIdCache;
|
|
12503
|
-
function projectIndexServerBuildId(entrypoint) {
|
|
12504
|
-
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path23.resolve(entrypoint);
|
|
12505
|
-
try {
|
|
12506
|
-
const stat18 = fs16.statSync(file);
|
|
12507
|
-
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
12508
|
-
return buildIdCache.buildId;
|
|
12526
|
+
cleanupPending(entry) {
|
|
12527
|
+
clearTimeout(entry.timer);
|
|
12528
|
+
if (entry.signal && entry.onAbort) {
|
|
12529
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
12509
12530
|
}
|
|
12510
|
-
const buildId = createHash4("sha256").update(fs16.readFileSync(file)).digest("hex").slice(0, 24);
|
|
12511
|
-
buildIdCache = { file, mtimeMs: stat18.mtimeMs, size: stat18.size, buildId };
|
|
12512
|
-
return buildId;
|
|
12513
|
-
} catch {
|
|
12514
|
-
return `unreadable:${path23.basename(file)}`;
|
|
12515
|
-
}
|
|
12516
|
-
}
|
|
12517
|
-
function normalizeLocalPath(value) {
|
|
12518
|
-
const resolved = path23.resolve(value);
|
|
12519
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12520
|
-
}
|
|
12521
|
-
function projectIndexServerKey(projectRoot, indexDir) {
|
|
12522
|
-
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
12523
|
-
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
12524
|
-
}
|
|
12525
|
-
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
12526
|
-
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
12527
|
-
if (process.platform === "win32") {
|
|
12528
|
-
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
12529
12531
|
}
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
return path23.join(
|
|
12538
|
-
path23.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
12539
|
-
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
12540
|
-
);
|
|
12541
|
-
}
|
|
12542
|
-
|
|
12543
|
-
// src/codebase-index/project-server-protocol.ts
|
|
12544
|
-
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
12545
|
-
function encodeProjectServerMessage(message) {
|
|
12546
|
-
return `${JSON.stringify(message)}
|
|
12547
|
-
`;
|
|
12548
|
-
}
|
|
12549
|
-
|
|
12550
|
-
// src/codebase-index/project-server-client.ts
|
|
12551
|
-
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
12552
|
-
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
12553
|
-
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
12554
|
-
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
12555
|
-
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
12556
|
-
var StaleProjectIndexServerError = class extends Error {
|
|
12557
|
-
constructor(message, pid) {
|
|
12558
|
-
super(message);
|
|
12559
|
-
this.pid = pid;
|
|
12532
|
+
rejectPending(error) {
|
|
12533
|
+
const entries = [...this.pending.values()];
|
|
12534
|
+
this.pending.clear();
|
|
12535
|
+
for (const entry of entries) {
|
|
12536
|
+
this.cleanupPending(entry);
|
|
12537
|
+
entry.reject(error);
|
|
12538
|
+
}
|
|
12560
12539
|
}
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
var connectionStates = /* @__PURE__ */ new Map();
|
|
12565
|
-
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
12566
|
-
var latestConnectionState = {
|
|
12567
|
-
status: "offline",
|
|
12568
|
-
connected: false
|
|
12569
|
-
};
|
|
12570
|
-
function resolveProjectIndexDaemonAvailability() {
|
|
12571
|
-
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
12572
|
-
return { kind: "inline-requested" };
|
|
12540
|
+
write(message) {
|
|
12541
|
+
const socket = this.socket;
|
|
12542
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
12573
12543
|
}
|
|
12574
|
-
|
|
12544
|
+
rejectStaleServer(message, reason) {
|
|
12545
|
+
const socket = this.socket;
|
|
12546
|
+
if (socket && !socket.destroyed) {
|
|
12547
|
+
socket.write(
|
|
12548
|
+
encodeProjectServerMessage({
|
|
12549
|
+
type: "shutdown",
|
|
12550
|
+
id: 0,
|
|
12551
|
+
reason: "stale-build-replacement"
|
|
12552
|
+
})
|
|
12553
|
+
);
|
|
12554
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
12555
|
+
timer.unref?.();
|
|
12556
|
+
}
|
|
12557
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12558
|
+
}
|
|
12559
|
+
spawnDetachedServer() {
|
|
12560
|
+
const url = resolveProjectServerUrl();
|
|
12561
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12562
|
+
if (process.platform !== "win32") {
|
|
12563
|
+
try {
|
|
12564
|
+
fs12.rmSync(this.endpoint, { force: true });
|
|
12565
|
+
} catch {
|
|
12566
|
+
}
|
|
12567
|
+
}
|
|
12568
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
12569
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
12570
|
+
const child = spawn4(process.execPath, args, {
|
|
12571
|
+
detached: true,
|
|
12572
|
+
stdio: "ignore",
|
|
12573
|
+
windowsHide: true,
|
|
12574
|
+
env: process.env
|
|
12575
|
+
});
|
|
12576
|
+
child.unref();
|
|
12577
|
+
}
|
|
12578
|
+
forceKillKnownServer() {
|
|
12579
|
+
const pid = this.info?.pid;
|
|
12580
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
12581
|
+
}
|
|
12582
|
+
forceKillServer(pid) {
|
|
12583
|
+
if (pid === process.pid) return false;
|
|
12575
12584
|
try {
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12585
|
+
process.kill(pid);
|
|
12586
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12587
|
+
try {
|
|
12588
|
+
const metadata = JSON.parse(fs12.readFileSync(metadataPath, "utf8"));
|
|
12589
|
+
if (metadata.pid === pid) fs12.rmSync(metadataPath, { force: true });
|
|
12590
|
+
} catch {
|
|
12579
12591
|
}
|
|
12592
|
+
return true;
|
|
12580
12593
|
} catch {
|
|
12594
|
+
return false;
|
|
12581
12595
|
}
|
|
12582
12596
|
}
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
function isProjectIndexServerAvailable() {
|
|
12596
|
-
return resolveProjectServerUrl() !== null;
|
|
12597
|
+
};
|
|
12598
|
+
var connections = /* @__PURE__ */ new Map();
|
|
12599
|
+
var heartbeatTimer;
|
|
12600
|
+
function ensureHeartbeatLoop() {
|
|
12601
|
+
if (heartbeatTimer) return;
|
|
12602
|
+
heartbeatTimer = setInterval(() => {
|
|
12603
|
+
for (const connection of connections.values()) {
|
|
12604
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
12605
|
+
});
|
|
12606
|
+
}
|
|
12607
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
12608
|
+
heartbeatTimer.unref?.();
|
|
12597
12609
|
}
|
|
12598
|
-
function
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12610
|
+
function maybeStopHeartbeatLoop() {
|
|
12611
|
+
if (!heartbeatTimer) return;
|
|
12612
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
12613
|
+
clearInterval(heartbeatTimer);
|
|
12614
|
+
heartbeatTimer = void 0;
|
|
12602
12615
|
}
|
|
12603
|
-
function
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
return { status: "unavailable", connected: false };
|
|
12610
|
-
}
|
|
12611
|
-
return {
|
|
12612
|
-
status: "offline",
|
|
12613
|
-
connected: false,
|
|
12614
|
-
projectRoot,
|
|
12615
|
-
indexDir,
|
|
12616
|
-
endpoint
|
|
12617
|
-
};
|
|
12616
|
+
function connectionFor(projectRoot, indexDir) {
|
|
12617
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12618
|
+
let connection = connections.get(endpoint);
|
|
12619
|
+
if (!connection) {
|
|
12620
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
12621
|
+
connections.set(endpoint, connection);
|
|
12618
12622
|
}
|
|
12619
|
-
|
|
12620
|
-
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
12621
|
-
return latestConnectionState;
|
|
12623
|
+
return connection;
|
|
12622
12624
|
}
|
|
12623
|
-
function
|
|
12624
|
-
|
|
12625
|
-
return () => connectionStateListeners.delete(listener);
|
|
12625
|
+
function callProjectIndexServer(op, args, options) {
|
|
12626
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
12626
12627
|
}
|
|
12627
|
-
function
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
return error;
|
|
12628
|
+
function ensureProjectIndexServer(options) {
|
|
12629
|
+
return connectionFor(options.projectRoot, options.indexDir).configure(
|
|
12630
|
+
options.watchExternal,
|
|
12631
|
+
options.debounceMs
|
|
12632
|
+
);
|
|
12633
12633
|
}
|
|
12634
|
-
function
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
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";
|
|
12634
|
+
function checkProjectIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
12635
|
+
return connectionFor(projectRoot, indexDir).checkHealth(
|
|
12636
|
+
false,
|
|
12637
|
+
options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS
|
|
12638
|
+
);
|
|
12640
12639
|
}
|
|
12641
|
-
function
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12640
|
+
async function shutdownProjectIndexServer(projectRoot, indexDir, reason) {
|
|
12641
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12642
|
+
const connection = connectionFor(projectRoot, indexDir);
|
|
12643
|
+
try {
|
|
12644
|
+
return await connection.shutdownRemote(reason);
|
|
12645
|
+
} finally {
|
|
12646
|
+
connection.close();
|
|
12647
|
+
connections.delete(endpoint);
|
|
12648
|
+
connectionStates.delete(endpoint);
|
|
12649
|
+
}
|
|
12646
12650
|
}
|
|
12647
|
-
function
|
|
12648
|
-
|
|
12651
|
+
function closeProjectIndexServerClients() {
|
|
12652
|
+
for (const connection of connections.values()) connection.close();
|
|
12653
|
+
connections.clear();
|
|
12654
|
+
connectionStates.clear();
|
|
12655
|
+
latestConnectionState = {
|
|
12656
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
12657
|
+
connected: false
|
|
12658
|
+
};
|
|
12659
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
12660
|
+
heartbeatTimer = void 0;
|
|
12649
12661
|
}
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
}
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
missedHeartbeats: 0,
|
|
12702
|
-
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
12703
|
-
};
|
|
12704
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
12705
|
-
return this.health;
|
|
12706
|
-
}).catch((error) => {
|
|
12707
|
-
if (!this.isConnected()) throw error;
|
|
12708
|
-
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
12709
|
-
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
12710
|
-
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
12711
|
-
this.health = {
|
|
12712
|
-
status,
|
|
12713
|
-
checkedAt: Date.now(),
|
|
12714
|
-
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
12715
|
-
latencyMs: null,
|
|
12716
|
-
missedHeartbeats,
|
|
12717
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
12718
|
-
};
|
|
12719
|
-
this.transition(status, { pid: this.info?.pid, error });
|
|
12720
|
-
return this.health;
|
|
12721
|
-
}).finally(() => {
|
|
12722
|
-
this.healthCheck = null;
|
|
12662
|
+
|
|
12663
|
+
// src/codebase-index/background-indexer.ts
|
|
12664
|
+
import * as fs18 from "node:fs";
|
|
12665
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12666
|
+
import { Worker } from "node:worker_threads";
|
|
12667
|
+
|
|
12668
|
+
// src/codebase-index/indexer.ts
|
|
12669
|
+
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12670
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
12671
|
+
import * as fs17 from "node:fs/promises";
|
|
12672
|
+
import { availableParallelism } from "node:os";
|
|
12673
|
+
import * as path23 from "node:path";
|
|
12674
|
+
import {
|
|
12675
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
12676
|
+
indexParallelBatchSize,
|
|
12677
|
+
isFrugalPerf
|
|
12678
|
+
} from "@wrongstack/core/utils";
|
|
12679
|
+
|
|
12680
|
+
// src/codebase-index/gitignore.ts
|
|
12681
|
+
import * as fs13 from "node:fs/promises";
|
|
12682
|
+
import * as path17 from "node:path";
|
|
12683
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
12684
|
+
function globBody(glob) {
|
|
12685
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
12686
|
+
}
|
|
12687
|
+
function compileGitignore(lines) {
|
|
12688
|
+
const rules = [];
|
|
12689
|
+
for (const raw of lines) {
|
|
12690
|
+
let line = raw.replace(/\r$/, "");
|
|
12691
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
12692
|
+
line = line.trim();
|
|
12693
|
+
let negated = false;
|
|
12694
|
+
if (line.startsWith("!")) {
|
|
12695
|
+
negated = true;
|
|
12696
|
+
line = line.slice(1);
|
|
12697
|
+
}
|
|
12698
|
+
let dirOnly = false;
|
|
12699
|
+
if (line.endsWith("/")) {
|
|
12700
|
+
dirOnly = true;
|
|
12701
|
+
line = line.slice(0, -1);
|
|
12702
|
+
}
|
|
12703
|
+
if (!line) continue;
|
|
12704
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
12705
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
12706
|
+
const body = globBody(line);
|
|
12707
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
12708
|
+
rules.push({
|
|
12709
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
12710
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
12711
|
+
negated,
|
|
12712
|
+
dirOnly
|
|
12723
12713
|
});
|
|
12724
|
-
return this.healthCheck;
|
|
12725
|
-
}
|
|
12726
|
-
markResponsive() {
|
|
12727
|
-
const now2 = Date.now();
|
|
12728
|
-
this.health = {
|
|
12729
|
-
status: "healthy",
|
|
12730
|
-
checkedAt: now2,
|
|
12731
|
-
lastHealthyAt: now2,
|
|
12732
|
-
latencyMs: this.health?.latencyMs ?? null,
|
|
12733
|
-
missedHeartbeats: 0,
|
|
12734
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
12735
|
-
};
|
|
12736
12714
|
}
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12715
|
+
return (relPath, isDir) => {
|
|
12716
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
12717
|
+
let ignored = false;
|
|
12718
|
+
for (const r of rules) {
|
|
12719
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
12720
|
+
if (re.test(p)) ignored = !r.negated;
|
|
12721
|
+
}
|
|
12722
|
+
return ignored;
|
|
12723
|
+
};
|
|
12724
|
+
}
|
|
12725
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
12726
|
+
let lines = [];
|
|
12727
|
+
try {
|
|
12728
|
+
const raw = await fs13.readFile(path17.join(projectRoot, ".gitignore"), "utf8");
|
|
12729
|
+
lines = raw.split("\n");
|
|
12730
|
+
} catch {
|
|
12742
12731
|
}
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12732
|
+
return compileGitignore(lines);
|
|
12733
|
+
}
|
|
12734
|
+
|
|
12735
|
+
// src/codebase-index/indexer.ts
|
|
12736
|
+
init_languages2();
|
|
12737
|
+
|
|
12738
|
+
// src/codebase-index/parser-dispatch.ts
|
|
12739
|
+
async function parseFileContent(file, content, lang) {
|
|
12740
|
+
switch (lang) {
|
|
12741
|
+
case "ts":
|
|
12742
|
+
case "tsx":
|
|
12743
|
+
case "js":
|
|
12744
|
+
case "jsx": {
|
|
12745
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
12746
|
+
return parseSymbols8({ file, content, lang });
|
|
12748
12747
|
}
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
);
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
const
|
|
12759
|
-
return {
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
};
|
|
12764
|
-
}
|
|
12765
|
-
|
|
12748
|
+
case "go": {
|
|
12749
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
12750
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
12751
|
+
}
|
|
12752
|
+
case "py": {
|
|
12753
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
12754
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
12755
|
+
}
|
|
12756
|
+
case "rs": {
|
|
12757
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
12758
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
12759
|
+
}
|
|
12760
|
+
case "json": {
|
|
12761
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
12762
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
12763
|
+
}
|
|
12764
|
+
case "yaml": {
|
|
12765
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
12766
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
12767
|
+
}
|
|
12768
|
+
default: {
|
|
12769
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
12770
|
+
return parseSymbols8({ file, content, lang });
|
|
12766
12771
|
}
|
|
12767
12772
|
}
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12773
|
+
}
|
|
12774
|
+
|
|
12775
|
+
// src/codebase-index/indexer.ts
|
|
12776
|
+
var YIELD_EVERY_N = 50;
|
|
12777
|
+
function resolveParallelBatch() {
|
|
12778
|
+
return indexParallelBatchSize(availableParallelism());
|
|
12779
|
+
}
|
|
12780
|
+
function yieldEventLoop() {
|
|
12781
|
+
return new Promise((resolve17) => setImmediate(resolve17));
|
|
12782
|
+
}
|
|
12783
|
+
function throwIfAborted(signal) {
|
|
12784
|
+
if (!signal?.aborted) return;
|
|
12785
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
12786
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12787
|
+
}
|
|
12788
|
+
function isAbortError(err) {
|
|
12789
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
12790
|
+
}
|
|
12791
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
12792
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
12793
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
12794
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
12795
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
12796
|
+
function isWithinProject(projectRoot, file) {
|
|
12797
|
+
const rel = path23.relative(projectRoot, file);
|
|
12798
|
+
return rel !== "" && !rel.startsWith(`..${path23.sep}`) && rel !== ".." && !path23.isAbsolute(rel);
|
|
12799
|
+
}
|
|
12800
|
+
function isMissingPathError(err) {
|
|
12801
|
+
const code = err?.code;
|
|
12802
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
12803
|
+
}
|
|
12804
|
+
function normalizeComparablePath(value) {
|
|
12805
|
+
const resolved = path23.resolve(value);
|
|
12806
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12807
|
+
}
|
|
12808
|
+
function gitOutput(projectRoot, args) {
|
|
12809
|
+
return new Promise((resolve17, reject) => {
|
|
12810
|
+
execFile2(
|
|
12811
|
+
"git",
|
|
12812
|
+
["-C", projectRoot, ...args],
|
|
12813
|
+
{
|
|
12814
|
+
encoding: "buffer",
|
|
12815
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
12816
|
+
windowsHide: true
|
|
12817
|
+
},
|
|
12818
|
+
(error, stdout) => {
|
|
12819
|
+
if (error) reject(error);
|
|
12820
|
+
else resolve17(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
12821
|
+
}
|
|
12774
12822
|
);
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12823
|
+
});
|
|
12824
|
+
}
|
|
12825
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
12826
|
+
try {
|
|
12827
|
+
throwIfAborted(signal);
|
|
12828
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
12829
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
12830
|
+
throwIfAborted(signal);
|
|
12831
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12832
|
+
const [output, statusOutput] = await Promise.all([
|
|
12833
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
12834
|
+
gitOutput(projectRoot, [
|
|
12835
|
+
"status",
|
|
12836
|
+
"--porcelain=v1",
|
|
12837
|
+
"-z",
|
|
12838
|
+
"--untracked-files=all",
|
|
12839
|
+
"--ignored=no"
|
|
12840
|
+
])
|
|
12841
|
+
]);
|
|
12842
|
+
throwIfAborted(signal);
|
|
12843
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
12844
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
12845
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12846
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
12847
|
+
const record = statusRecords[i];
|
|
12848
|
+
if (!record) continue;
|
|
12849
|
+
const status = record.slice(0, 2);
|
|
12850
|
+
const changedPath = path23.resolve(projectRoot, record.slice(3));
|
|
12851
|
+
dirty.add(changedPath);
|
|
12852
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
12853
|
+
if (status.includes("R") || status.includes("C")) {
|
|
12854
|
+
const source = statusRecords[++i];
|
|
12855
|
+
if (source) dirty.add(path23.resolve(projectRoot, source));
|
|
12856
|
+
}
|
|
12786
12857
|
}
|
|
12858
|
+
const files = [];
|
|
12859
|
+
for (const relative13 of output.toString("utf8").split("\0")) {
|
|
12860
|
+
if (!relative13) continue;
|
|
12861
|
+
const portable = relative13.replace(/\\/g, "/");
|
|
12862
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path23.posix.basename(portable))) {
|
|
12863
|
+
continue;
|
|
12864
|
+
}
|
|
12865
|
+
const full = path23.resolve(projectRoot, relative13);
|
|
12866
|
+
if (deleted.has(full)) continue;
|
|
12867
|
+
const ext = path23.extname(relative13).toLowerCase();
|
|
12868
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
12869
|
+
}
|
|
12870
|
+
return {
|
|
12871
|
+
files,
|
|
12872
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12873
|
+
};
|
|
12874
|
+
} catch {
|
|
12875
|
+
return null;
|
|
12787
12876
|
}
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12877
|
+
}
|
|
12878
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
12879
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
12880
|
+
if (gitFiles) {
|
|
12881
|
+
return {
|
|
12882
|
+
files: gitFiles.files,
|
|
12883
|
+
complete: true,
|
|
12884
|
+
errors: [],
|
|
12885
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
12886
|
+
};
|
|
12887
|
+
}
|
|
12888
|
+
const results = [];
|
|
12889
|
+
const errors = [];
|
|
12890
|
+
let complete = true;
|
|
12891
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12892
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12893
|
+
let dirCount = 0;
|
|
12894
|
+
const walk2 = async (dir) => {
|
|
12895
|
+
throwIfAborted(signal);
|
|
12896
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12897
|
+
await yieldEventLoop();
|
|
12898
|
+
throwIfAborted(signal);
|
|
12806
12899
|
}
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
if (
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
entry.reject(cancellationError(signal));
|
|
12829
|
-
} : void 0;
|
|
12830
|
-
this.pending.set(id, {
|
|
12831
|
-
resolve: resolve17,
|
|
12832
|
-
reject,
|
|
12833
|
-
timer,
|
|
12834
|
-
signal,
|
|
12835
|
-
onAbort,
|
|
12836
|
-
onProgress: options.onProgress
|
|
12837
|
-
});
|
|
12838
|
-
if (signal && onAbort) {
|
|
12839
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
12840
|
-
if (signal.aborted) {
|
|
12841
|
-
onAbort();
|
|
12842
|
-
return;
|
|
12900
|
+
let entries;
|
|
12901
|
+
try {
|
|
12902
|
+
entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
12903
|
+
} catch (err) {
|
|
12904
|
+
complete = false;
|
|
12905
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12906
|
+
return;
|
|
12907
|
+
}
|
|
12908
|
+
dirCount++;
|
|
12909
|
+
for (const e of entries) {
|
|
12910
|
+
if (ignoreSet.has(e.name)) continue;
|
|
12911
|
+
const full = path23.join(dir, e.name);
|
|
12912
|
+
const rel = path23.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12913
|
+
if (e.isDirectory()) {
|
|
12914
|
+
if (isGitIgnored(rel, true)) continue;
|
|
12915
|
+
await walk2(full);
|
|
12916
|
+
} else if (e.isFile()) {
|
|
12917
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12918
|
+
const ext = path23.extname(e.name).toLowerCase();
|
|
12919
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12920
|
+
results.push(full);
|
|
12843
12921
|
}
|
|
12844
12922
|
}
|
|
12845
|
-
|
|
12923
|
+
}
|
|
12924
|
+
};
|
|
12925
|
+
await walk2(projectRoot);
|
|
12926
|
+
return { files: results, complete, errors };
|
|
12927
|
+
}
|
|
12928
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
12929
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
12930
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
12931
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12932
|
+
const assigned = [];
|
|
12933
|
+
for (const ref of refs) {
|
|
12934
|
+
let owner2;
|
|
12935
|
+
for (const symbol of ordered) {
|
|
12936
|
+
if (symbol.line > ref.line) break;
|
|
12937
|
+
owner2 = symbol;
|
|
12938
|
+
}
|
|
12939
|
+
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12940
|
+
if (!owner2 || owner2.id <= 0) continue;
|
|
12941
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12942
|
+
if (seen.has(key)) continue;
|
|
12943
|
+
seen.add(key);
|
|
12944
|
+
assigned.push({ ...ref, fromId: owner2.id });
|
|
12945
|
+
}
|
|
12946
|
+
return assigned;
|
|
12947
|
+
}
|
|
12948
|
+
async function runIndexerWithStore(store, opts) {
|
|
12949
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
12950
|
+
const relationGraphVersion = "2";
|
|
12951
|
+
const refResolutionVersion = "2";
|
|
12952
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
12953
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
12954
|
+
const startMs = Date.now();
|
|
12955
|
+
const errors = [];
|
|
12956
|
+
const langStats = {};
|
|
12957
|
+
let filesIndexed = 0;
|
|
12958
|
+
let symbolsIndexed = 0;
|
|
12959
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
12960
|
+
let files;
|
|
12961
|
+
let discoveredFiles = null;
|
|
12962
|
+
let discoveryComplete = true;
|
|
12963
|
+
let trustedUnchanged;
|
|
12964
|
+
if (opts.files && opts.files.length > 0) {
|
|
12965
|
+
files = opts.files.map((f) => path23.resolve(projectRoot, f)).filter((f) => {
|
|
12966
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
12967
|
+
const rel = path23.relative(projectRoot, f).replace(/\\/g, "/");
|
|
12968
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path23.basename(f)) && !isGitIgnored(rel, false);
|
|
12846
12969
|
});
|
|
12970
|
+
} else {
|
|
12971
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
12972
|
+
files = discovery.files;
|
|
12973
|
+
errors.push(...discovery.errors);
|
|
12974
|
+
discoveryComplete = discovery.complete;
|
|
12975
|
+
discoveredFiles = new Set(files);
|
|
12976
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
12847
12977
|
}
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
this.transition("error", { error });
|
|
12854
|
-
throw error;
|
|
12855
|
-
}).finally(() => {
|
|
12856
|
-
this.connecting = null;
|
|
12978
|
+
if (langs && langs.length > 0) {
|
|
12979
|
+
const langSet = new Set(langs);
|
|
12980
|
+
files = files.filter((f) => {
|
|
12981
|
+
const lang = detectLang(f);
|
|
12982
|
+
return lang ? langSet.has(lang) : false;
|
|
12857
12983
|
});
|
|
12858
|
-
return this.connecting;
|
|
12859
12984
|
}
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
|
|
12864
|
-
let lastError = new Error("codebase-index server unavailable");
|
|
12865
|
-
while (Date.now() < deadline) {
|
|
12866
|
-
try {
|
|
12867
|
-
await this.connectOnce();
|
|
12868
|
-
return;
|
|
12869
|
-
} catch (error) {
|
|
12870
|
-
lastError = error;
|
|
12871
|
-
if (error instanceof StaleProjectIndexServerError) {
|
|
12872
|
-
staleAttempts++;
|
|
12873
|
-
if (!spawnIfMissing) break;
|
|
12874
|
-
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12875
|
-
spawned = false;
|
|
12876
|
-
await delay(100);
|
|
12877
|
-
continue;
|
|
12878
|
-
}
|
|
12879
|
-
}
|
|
12880
|
-
if (!spawnIfMissing) break;
|
|
12881
|
-
if (!spawned) {
|
|
12882
|
-
this.spawnDetachedServer();
|
|
12883
|
-
spawned = true;
|
|
12884
|
-
}
|
|
12885
|
-
await delay(75);
|
|
12886
|
-
}
|
|
12887
|
-
throw lastError;
|
|
12985
|
+
if (force) store.clearAll();
|
|
12986
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
12987
|
+
if (!force) {
|
|
12988
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
12888
12989
|
}
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
const timer = setTimeout(() => {
|
|
12901
|
-
reject(new Error("codebase-index server handshake timed out"));
|
|
12902
|
-
socket.destroy();
|
|
12903
|
-
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12904
|
-
timer.unref?.();
|
|
12905
|
-
const finishResolve = () => {
|
|
12906
|
-
clearTimeout(timer);
|
|
12907
|
-
this.connectResolve = null;
|
|
12908
|
-
this.connectReject = null;
|
|
12909
|
-
resolve17();
|
|
12910
|
-
};
|
|
12911
|
-
const finishReject = (error) => {
|
|
12912
|
-
clearTimeout(timer);
|
|
12913
|
-
this.connectResolve = null;
|
|
12914
|
-
this.connectReject = null;
|
|
12915
|
-
reject(error);
|
|
12916
|
-
};
|
|
12917
|
-
this.connectResolve = finishResolve;
|
|
12918
|
-
this.connectReject = finishReject;
|
|
12919
|
-
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
12920
|
-
socket.on("error", (error) => {
|
|
12921
|
-
if (!this.info) finishReject(error);
|
|
12922
|
-
});
|
|
12923
|
-
socket.on("close", () => this.onClose(socket));
|
|
12990
|
+
const totalFilesForProgress = files.length;
|
|
12991
|
+
let filesPreSkipped = 0;
|
|
12992
|
+
if (!force && trustedUnchanged) {
|
|
12993
|
+
files = files.filter((file) => {
|
|
12994
|
+
const meta = existingMeta.get(file);
|
|
12995
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
12996
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
12997
|
+
symbolsIndexed += meta.symbolCount;
|
|
12998
|
+
filesIndexed++;
|
|
12999
|
+
filesPreSkipped++;
|
|
13000
|
+
return false;
|
|
12924
13001
|
});
|
|
13002
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
12925
13003
|
}
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
13004
|
+
const parallelBatch = resolveParallelBatch();
|
|
13005
|
+
let filesSinceLastYield = 0;
|
|
13006
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
13007
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
13008
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
13009
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
13010
|
+
filesSinceLastYield += batchFiles.length;
|
|
13011
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
13012
|
+
filesSinceLastYield = 0;
|
|
13013
|
+
await yieldEventLoop();
|
|
13014
|
+
if (isFrugalPerf()) {
|
|
13015
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
13016
|
+
}
|
|
13017
|
+
throwIfAborted(signal);
|
|
13018
|
+
}
|
|
13019
|
+
const statOpts = signal ? { signal } : {};
|
|
13020
|
+
const statReadParse = await Promise.allSettled(
|
|
13021
|
+
batchFiles.map(
|
|
13022
|
+
async (file) => {
|
|
13023
|
+
let stat18;
|
|
13024
|
+
try {
|
|
13025
|
+
stat18 = await fs17.stat(file, statOpts);
|
|
13026
|
+
} catch (e) {
|
|
13027
|
+
if (isAbortError(e)) throw e;
|
|
13028
|
+
return {
|
|
13029
|
+
file,
|
|
13030
|
+
stat: null,
|
|
13031
|
+
lang: "",
|
|
13032
|
+
parsed: null,
|
|
13033
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
13034
|
+
missing: isMissingPathError(e)
|
|
13035
|
+
};
|
|
13036
|
+
}
|
|
13037
|
+
if (!stat18.isFile()) return { file, stat: stat18, lang: "", parsed: null };
|
|
13038
|
+
const lang = detectLang(file);
|
|
13039
|
+
if (!lang) return { file, stat: stat18, lang: "", parsed: null };
|
|
13040
|
+
if (stat18.size > MAX_INDEX_FILE_BYTES) {
|
|
13041
|
+
return {
|
|
13042
|
+
file,
|
|
13043
|
+
stat: stat18,
|
|
13044
|
+
lang,
|
|
13045
|
+
parsed: null,
|
|
13046
|
+
error: `file too large (${stat18.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
13047
|
+
};
|
|
13048
|
+
}
|
|
13049
|
+
const meta = existingMeta.get(file);
|
|
13050
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat18.mtimeMs)) {
|
|
13051
|
+
return { file, stat: stat18, lang, parsed: null, skippedMeta: meta };
|
|
13052
|
+
}
|
|
13053
|
+
let content;
|
|
13054
|
+
try {
|
|
13055
|
+
content = await fs17.readFile(file, { encoding: "utf8", signal });
|
|
13056
|
+
} catch (e) {
|
|
13057
|
+
if (isAbortError(e)) throw e;
|
|
13058
|
+
return {
|
|
13059
|
+
file,
|
|
13060
|
+
stat: stat18,
|
|
13061
|
+
lang,
|
|
13062
|
+
parsed: null,
|
|
13063
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
13064
|
+
};
|
|
13065
|
+
}
|
|
13066
|
+
let parsed;
|
|
13067
|
+
try {
|
|
13068
|
+
parsed = await parseFileContent(file, content, lang);
|
|
13069
|
+
} catch (e) {
|
|
13070
|
+
return {
|
|
13071
|
+
file,
|
|
13072
|
+
stat: stat18,
|
|
13073
|
+
lang,
|
|
13074
|
+
parsed: null,
|
|
13075
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
13076
|
+
};
|
|
13077
|
+
}
|
|
13078
|
+
return { file, stat: stat18, lang, parsed, content };
|
|
12934
13079
|
}
|
|
12935
|
-
|
|
13080
|
+
)
|
|
13081
|
+
);
|
|
13082
|
+
const batchEntries = [];
|
|
13083
|
+
const deleteForFiles = [];
|
|
13084
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
13085
|
+
const settled = statReadParse[fi];
|
|
13086
|
+
const file = expectDefined6(batchFiles[fi]);
|
|
13087
|
+
if (settled.status === "rejected") {
|
|
13088
|
+
const err = settled.reason;
|
|
13089
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
13090
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13091
|
+
continue;
|
|
12936
13092
|
}
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
13093
|
+
const result = settled.value;
|
|
13094
|
+
if (result.error) {
|
|
13095
|
+
if (result.missing) store.deleteFile(file);
|
|
13096
|
+
errors.push(`${file}: ${result.error}`);
|
|
13097
|
+
continue;
|
|
12940
13098
|
}
|
|
12941
|
-
const
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
} catch {
|
|
12948
|
-
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12949
|
-
return;
|
|
13099
|
+
const { stat: stat18, lang, parsed } = result;
|
|
13100
|
+
if (result.skippedMeta) {
|
|
13101
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
13102
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
13103
|
+
filesIndexed++;
|
|
13104
|
+
continue;
|
|
12950
13105
|
}
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
13106
|
+
if (!lang || !parsed) {
|
|
13107
|
+
if (lang) {
|
|
13108
|
+
store.upsertFile({
|
|
13109
|
+
file,
|
|
13110
|
+
lang,
|
|
13111
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13112
|
+
symbolCount: 0,
|
|
13113
|
+
lastIndexed: Date.now()
|
|
13114
|
+
});
|
|
13115
|
+
filesIndexed++;
|
|
13116
|
+
}
|
|
13117
|
+
continue;
|
|
12962
13118
|
}
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
13119
|
+
if (parsed.symbols.length === 0) {
|
|
13120
|
+
store.replaceEmptyFile({
|
|
13121
|
+
file,
|
|
13122
|
+
lang,
|
|
13123
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13124
|
+
symbolCount: 0,
|
|
13125
|
+
lastIndexed: Date.now()
|
|
13126
|
+
});
|
|
13127
|
+
filesIndexed++;
|
|
13128
|
+
continue;
|
|
12970
13129
|
}
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
this.markResponsive();
|
|
12981
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
12982
|
-
return;
|
|
12983
|
-
}
|
|
12984
|
-
const entry = this.pending.get(message.id);
|
|
12985
|
-
if (!entry) return;
|
|
12986
|
-
this.markResponsive();
|
|
12987
|
-
const status = connectionStates.get(this.endpoint)?.status;
|
|
12988
|
-
if (status === "degraded" || status === "unresponsive") {
|
|
12989
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
12990
|
-
}
|
|
12991
|
-
if (message.type === "progress") {
|
|
12992
|
-
entry.onProgress?.(message.current, message.total);
|
|
12993
|
-
return;
|
|
12994
|
-
}
|
|
12995
|
-
this.pending.delete(message.id);
|
|
12996
|
-
this.cleanupPending(entry);
|
|
12997
|
-
if (message.ok) entry.resolve(message.result);
|
|
12998
|
-
else entry.reject(remoteError(message.error, message.errorName));
|
|
12999
|
-
}
|
|
13000
|
-
onClose(socket) {
|
|
13001
|
-
if (socket !== this.socket) return;
|
|
13002
|
-
const wasConnected = this.info !== null;
|
|
13003
|
-
this.socket = null;
|
|
13004
|
-
this.info = null;
|
|
13005
|
-
this.activity = null;
|
|
13006
|
-
this.health = null;
|
|
13007
|
-
const error = new Error("codebase-index server connection closed");
|
|
13008
|
-
this.connectReject?.(error);
|
|
13009
|
-
this.connectResolve = null;
|
|
13010
|
-
this.connectReject = null;
|
|
13011
|
-
this.rejectPending(error);
|
|
13012
|
-
if (wasConnected) this.transition("error", { error });
|
|
13013
|
-
maybeStopHeartbeatLoop();
|
|
13014
|
-
}
|
|
13015
|
-
cleanupPending(entry) {
|
|
13016
|
-
clearTimeout(entry.timer);
|
|
13017
|
-
if (entry.signal && entry.onAbort) {
|
|
13018
|
-
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
13019
|
-
}
|
|
13020
|
-
}
|
|
13021
|
-
rejectPending(error) {
|
|
13022
|
-
const entries = [...this.pending.values()];
|
|
13023
|
-
this.pending.clear();
|
|
13024
|
-
for (const entry of entries) {
|
|
13025
|
-
this.cleanupPending(entry);
|
|
13026
|
-
entry.reject(error);
|
|
13027
|
-
}
|
|
13028
|
-
}
|
|
13029
|
-
write(message) {
|
|
13030
|
-
const socket = this.socket;
|
|
13031
|
-
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
13032
|
-
}
|
|
13033
|
-
rejectStaleServer(message, reason) {
|
|
13034
|
-
const socket = this.socket;
|
|
13035
|
-
if (socket && !socket.destroyed) {
|
|
13036
|
-
socket.write(
|
|
13037
|
-
encodeProjectServerMessage({
|
|
13038
|
-
type: "shutdown",
|
|
13039
|
-
id: 0,
|
|
13040
|
-
reason: "stale-build-replacement"
|
|
13041
|
-
})
|
|
13042
|
-
);
|
|
13043
|
-
const timer = setTimeout(() => socket.destroy(), 25);
|
|
13044
|
-
timer.unref?.();
|
|
13130
|
+
batchEntries.push({
|
|
13131
|
+
file,
|
|
13132
|
+
lang,
|
|
13133
|
+
symbols: parsed.symbols,
|
|
13134
|
+
refs: parsed.refs ?? [],
|
|
13135
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13136
|
+
symbolCount: parsed.symbols.length
|
|
13137
|
+
});
|
|
13138
|
+
deleteForFiles.push(file);
|
|
13045
13139
|
}
|
|
13046
|
-
|
|
13047
|
-
}
|
|
13048
|
-
spawnDetachedServer() {
|
|
13049
|
-
const url = resolveProjectServerUrl();
|
|
13050
|
-
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
13051
|
-
if (process.platform !== "win32") {
|
|
13140
|
+
if (batchEntries.length > 0) {
|
|
13052
13141
|
try {
|
|
13053
|
-
|
|
13054
|
-
|
|
13142
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
13143
|
+
for (const entry of batchEntries) {
|
|
13144
|
+
const count = entry.symbols.length;
|
|
13145
|
+
symbolsIndexed += count;
|
|
13146
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
13147
|
+
filesIndexed++;
|
|
13148
|
+
}
|
|
13149
|
+
} catch (err) {
|
|
13150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13151
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
13152
|
+
for (const entry of batchEntries) {
|
|
13153
|
+
try {
|
|
13154
|
+
store.deleteRefsForFile(entry.file);
|
|
13155
|
+
store.deleteSymbolsForFile(entry.file);
|
|
13156
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
13157
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
13158
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
13159
|
+
filesIndexed++;
|
|
13160
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
13161
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
13162
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
13163
|
+
}
|
|
13164
|
+
store.resolveRefsForNames([
|
|
13165
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
13166
|
+
...entry.refs.map((ref) => ref.toName)
|
|
13167
|
+
]);
|
|
13168
|
+
store.upsertFile({
|
|
13169
|
+
file: entry.file,
|
|
13170
|
+
lang: entry.lang,
|
|
13171
|
+
mtimeMs: entry.mtimeMs,
|
|
13172
|
+
symbolCount: entry.symbolCount,
|
|
13173
|
+
lastIndexed: Date.now()
|
|
13174
|
+
});
|
|
13175
|
+
} catch (innerErr) {
|
|
13176
|
+
errors.push(
|
|
13177
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
13178
|
+
);
|
|
13179
|
+
}
|
|
13180
|
+
}
|
|
13055
13181
|
}
|
|
13056
13182
|
}
|
|
13057
|
-
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
13058
|
-
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
13059
|
-
const child = spawn7(process.execPath, args, {
|
|
13060
|
-
detached: true,
|
|
13061
|
-
stdio: "ignore",
|
|
13062
|
-
windowsHide: true,
|
|
13063
|
-
env: process.env
|
|
13064
|
-
});
|
|
13065
|
-
child.unref();
|
|
13066
|
-
}
|
|
13067
|
-
forceKillKnownServer() {
|
|
13068
|
-
const pid = this.info?.pid;
|
|
13069
|
-
return pid ? this.forceKillServer(pid) : false;
|
|
13070
13183
|
}
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
13076
|
-
try {
|
|
13077
|
-
const metadata = JSON.parse(fs17.readFileSync(metadataPath, "utf8"));
|
|
13078
|
-
if (metadata.pid === pid) fs17.rmSync(metadataPath, { force: true });
|
|
13079
|
-
} catch {
|
|
13184
|
+
if (discoveredFiles && discoveryComplete) {
|
|
13185
|
+
for (const [file_] of existingMeta) {
|
|
13186
|
+
if (!discoveredFiles.has(file_)) {
|
|
13187
|
+
store.deleteFile(file_);
|
|
13080
13188
|
}
|
|
13081
|
-
return true;
|
|
13082
|
-
} catch {
|
|
13083
|
-
return false;
|
|
13084
13189
|
}
|
|
13085
13190
|
}
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
if (!heartbeatTimer) return;
|
|
13101
|
-
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
13102
|
-
clearInterval(heartbeatTimer);
|
|
13103
|
-
heartbeatTimer = void 0;
|
|
13191
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
13192
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
13193
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
13194
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
13195
|
+
store.setLastIndexed(Date.now());
|
|
13196
|
+
if (!opts.files) store.compactIfNeeded();
|
|
13197
|
+
const durationMs = Date.now() - startMs;
|
|
13198
|
+
return {
|
|
13199
|
+
filesIndexed,
|
|
13200
|
+
symbolsIndexed,
|
|
13201
|
+
langStats,
|
|
13202
|
+
durationMs,
|
|
13203
|
+
errors
|
|
13204
|
+
};
|
|
13104
13205
|
}
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13206
|
+
|
|
13207
|
+
// src/codebase-index/index-service.ts
|
|
13208
|
+
async function indexService(args, hooks = {}) {
|
|
13209
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13210
|
+
try {
|
|
13211
|
+
return await runIndexerWithStore(store, {
|
|
13212
|
+
projectRoot: args.projectRoot,
|
|
13213
|
+
indexDir: args.indexDir,
|
|
13214
|
+
files: args.files,
|
|
13215
|
+
force: args.force,
|
|
13216
|
+
langs: args.langs,
|
|
13217
|
+
ignore: args.ignore,
|
|
13218
|
+
signal: hooks.signal,
|
|
13219
|
+
onProgress: hooks.onProgress
|
|
13220
|
+
});
|
|
13221
|
+
} finally {
|
|
13222
|
+
indexStorePool.release(store);
|
|
13111
13223
|
}
|
|
13112
|
-
return connection;
|
|
13113
13224
|
}
|
|
13114
|
-
function
|
|
13115
|
-
|
|
13225
|
+
function searchService(args) {
|
|
13226
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13227
|
+
try {
|
|
13228
|
+
return store.searchRanked(
|
|
13229
|
+
args.query,
|
|
13230
|
+
{
|
|
13231
|
+
kind: args.kind,
|
|
13232
|
+
lang: args.lang,
|
|
13233
|
+
file: args.file,
|
|
13234
|
+
lspKind: args.lspKind
|
|
13235
|
+
},
|
|
13236
|
+
args.limit
|
|
13237
|
+
);
|
|
13238
|
+
} finally {
|
|
13239
|
+
indexStorePool.release(store);
|
|
13240
|
+
}
|
|
13116
13241
|
}
|
|
13117
|
-
function
|
|
13118
|
-
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13242
|
+
function statsService(args) {
|
|
13243
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13244
|
+
try {
|
|
13245
|
+
return store.getStats();
|
|
13246
|
+
} finally {
|
|
13247
|
+
indexStorePool.release(store);
|
|
13248
|
+
}
|
|
13122
13249
|
}
|
|
13123
|
-
function
|
|
13124
|
-
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13250
|
+
function packageGraphService(args) {
|
|
13251
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13252
|
+
try {
|
|
13253
|
+
return store.getPackageGraph();
|
|
13254
|
+
} finally {
|
|
13255
|
+
indexStorePool.release(store);
|
|
13256
|
+
}
|
|
13128
13257
|
}
|
|
13129
|
-
|
|
13130
|
-
const
|
|
13131
|
-
const connection = connectionFor(projectRoot, indexDir);
|
|
13258
|
+
function fileGraphService(args) {
|
|
13259
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13132
13260
|
try {
|
|
13133
|
-
return
|
|
13261
|
+
return store.getFileGraph(args.packageFilter);
|
|
13134
13262
|
} finally {
|
|
13135
|
-
|
|
13136
|
-
connections.delete(endpoint);
|
|
13137
|
-
connectionStates.delete(endpoint);
|
|
13263
|
+
indexStorePool.release(store);
|
|
13138
13264
|
}
|
|
13139
13265
|
}
|
|
13140
|
-
function
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
};
|
|
13148
|
-
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
13149
|
-
heartbeatTimer = void 0;
|
|
13266
|
+
function symbolGraphService(args) {
|
|
13267
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13268
|
+
try {
|
|
13269
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
13270
|
+
} finally {
|
|
13271
|
+
indexStorePool.release(store);
|
|
13272
|
+
}
|
|
13150
13273
|
}
|
|
13151
13274
|
|
|
13152
13275
|
// src/codebase-index/background-indexer.ts
|
|
13276
|
+
init_languages2();
|
|
13153
13277
|
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 24e4;
|
|
13154
13278
|
var DEFAULT_INCREMENTAL_TIMEOUT_MS = 6e4;
|
|
13155
13279
|
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
@@ -13282,8 +13406,17 @@ async function shutdownCodebaseIndexHost() {
|
|
|
13282
13406
|
}
|
|
13283
13407
|
}
|
|
13284
13408
|
}
|
|
13409
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
13410
|
+
function warnEndpointInvalidOnce(availability) {
|
|
13411
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
13412
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
13413
|
+
process.stderr.write(
|
|
13414
|
+
`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.
|
|
13415
|
+
`
|
|
13416
|
+
);
|
|
13417
|
+
}
|
|
13285
13418
|
function callIndexOp(op, args, opts) {
|
|
13286
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
13419
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
13287
13420
|
if (availability.kind === "available") {
|
|
13288
13421
|
return callProjectIndexServer(op, args, opts);
|
|
13289
13422
|
}
|
|
@@ -13294,6 +13427,14 @@ function callIndexOp(op, args, opts) {
|
|
|
13294
13427
|
)
|
|
13295
13428
|
);
|
|
13296
13429
|
}
|
|
13430
|
+
if (availability.kind === "endpoint-invalid") {
|
|
13431
|
+
warnEndpointInvalidOnce(availability);
|
|
13432
|
+
return Promise.reject(
|
|
13433
|
+
new Error(
|
|
13434
|
+
`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.`
|
|
13435
|
+
)
|
|
13436
|
+
);
|
|
13437
|
+
}
|
|
13297
13438
|
const w = ensureWorker();
|
|
13298
13439
|
if (!w) return callInline(op, args, opts);
|
|
13299
13440
|
if (opts.signal?.aborted) {
|
|
@@ -13567,7 +13708,11 @@ function checkCodebaseIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
|
13567
13708
|
return checkProjectIndexServerHealth(projectRoot, indexDir, options);
|
|
13568
13709
|
}
|
|
13569
13710
|
function ensureCodebaseIndexServer(options) {
|
|
13570
|
-
|
|
13711
|
+
const availability = resolveProjectIndexDaemonAvailability(options.projectRoot, options.indexDir);
|
|
13712
|
+
if (availability.kind !== "available") {
|
|
13713
|
+
if (availability.kind === "endpoint-invalid") warnEndpointInvalidOnce(availability);
|
|
13714
|
+
return Promise.resolve();
|
|
13715
|
+
}
|
|
13571
13716
|
return ensureProjectIndexServer({
|
|
13572
13717
|
projectRoot: options.projectRoot,
|
|
13573
13718
|
indexDir: options.indexDir,
|
|
@@ -13852,6 +13997,7 @@ var codebaseStatsTool = {
|
|
|
13852
13997
|
};
|
|
13853
13998
|
|
|
13854
13999
|
// src/codebase-index/dead-code-scan.ts
|
|
14000
|
+
init_languages2();
|
|
13855
14001
|
import * as fs19 from "node:fs";
|
|
13856
14002
|
import * as path24 from "node:path";
|
|
13857
14003
|
var deadCodeScanTool = {
|
|
@@ -13920,7 +14066,15 @@ function discoverEntryPoints(projectRoot, userEntryPoints) {
|
|
|
13920
14066
|
if (rootPkg) {
|
|
13921
14067
|
addPkgJsonEntryPoints(projectRoot, rootPkg, entries);
|
|
13922
14068
|
}
|
|
13923
|
-
|
|
14069
|
+
let workspaces;
|
|
14070
|
+
if (rootPkg) {
|
|
14071
|
+
workspaces = extractWorkspaceGlobs(rootPkg, projectRoot);
|
|
14072
|
+
if (workspaces.length === 0) {
|
|
14073
|
+
workspaces = extractPnpmWorkspaceDirs(projectRoot);
|
|
14074
|
+
}
|
|
14075
|
+
} else {
|
|
14076
|
+
workspaces = [];
|
|
14077
|
+
}
|
|
13924
14078
|
for (const wsDir of workspaces) {
|
|
13925
14079
|
const pkgJsonPath = path24.join(wsDir, "package.json");
|
|
13926
14080
|
const pkg = tryReadJson(pkgJsonPath);
|
|
@@ -13942,77 +14096,189 @@ function discoverEntryPoints(projectRoot, userEntryPoints) {
|
|
|
13942
14096
|
}
|
|
13943
14097
|
return [...entries];
|
|
13944
14098
|
}
|
|
14099
|
+
var BUILD_OUTPUT_DIRS = ["dist", "out", "build", "release"];
|
|
14100
|
+
var BUILD_OUTPUT_DIR_NAMES = BUILD_OUTPUT_DIRS.map((d) => `${path24.sep}${d}${path24.sep}`);
|
|
14101
|
+
function trySourceEquivalent(resolved) {
|
|
14102
|
+
resolved = resolved.replace(/[/\\]/g, path24.sep);
|
|
14103
|
+
for (const marker of BUILD_OUTPUT_DIR_NAMES) {
|
|
14104
|
+
const idx = resolved.indexOf(marker);
|
|
14105
|
+
if (idx === -1) continue;
|
|
14106
|
+
const base = resolved.replace(marker, `${path24.sep}src${path24.sep}`);
|
|
14107
|
+
const candidate = base.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
14108
|
+
if (candidate !== base && fs19.existsSync(candidate)) {
|
|
14109
|
+
return candidate;
|
|
14110
|
+
}
|
|
14111
|
+
const dtsStripped = base.replace(/\.d\.ts$/, "");
|
|
14112
|
+
const candidateDts = dtsStripped + ".ts";
|
|
14113
|
+
if (candidateDts !== base && candidateDts !== candidate && fs19.existsSync(candidateDts)) {
|
|
14114
|
+
return candidateDts;
|
|
14115
|
+
}
|
|
14116
|
+
const candidateNoExt = base + ".ts";
|
|
14117
|
+
if (candidate !== candidateNoExt && candidateNoExt !== candidateDts && fs19.existsSync(candidateNoExt)) {
|
|
14118
|
+
return candidateNoExt;
|
|
14119
|
+
}
|
|
14120
|
+
}
|
|
14121
|
+
return null;
|
|
14122
|
+
}
|
|
14123
|
+
function tryAddEntryPath(pkgDir, rawPath, entries) {
|
|
14124
|
+
const resolved = resolveAgainst(pkgDir, rawPath);
|
|
14125
|
+
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14126
|
+
const tsResolved = resolved.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
14127
|
+
if (tsResolved !== resolved && fs19.existsSync(tsResolved)) {
|
|
14128
|
+
entries.add(tsResolved);
|
|
14129
|
+
}
|
|
14130
|
+
const srcAlt = trySourceEquivalent(resolved);
|
|
14131
|
+
if (srcAlt) entries.add(srcAlt);
|
|
14132
|
+
}
|
|
13945
14133
|
function addPkgJsonEntryPoints(pkgDir, pkg, entries) {
|
|
13946
14134
|
if (typeof pkg.main === "string") {
|
|
13947
|
-
|
|
13948
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
13949
|
-
const tsResolved = resolved.replace(/\.(js|mjs|cjs)$/, ".ts");
|
|
13950
|
-
if (tsResolved !== resolved && fs19.existsSync(tsResolved)) {
|
|
13951
|
-
entries.add(tsResolved);
|
|
13952
|
-
}
|
|
14135
|
+
tryAddEntryPath(pkgDir, pkg.main, entries);
|
|
13953
14136
|
}
|
|
13954
14137
|
const bin = pkg.bin;
|
|
13955
14138
|
if (typeof bin === "string") {
|
|
13956
|
-
|
|
13957
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14139
|
+
tryAddEntryPath(pkgDir, bin, entries);
|
|
13958
14140
|
} else if (bin && typeof bin === "object") {
|
|
13959
14141
|
for (const value of Object.values(bin)) {
|
|
13960
14142
|
if (typeof value === "string") {
|
|
13961
|
-
|
|
13962
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14143
|
+
tryAddEntryPath(pkgDir, value, entries);
|
|
13963
14144
|
}
|
|
13964
14145
|
}
|
|
13965
14146
|
}
|
|
13966
14147
|
for (const key of ["types", "typings"]) {
|
|
13967
14148
|
if (typeof pkg[key] === "string") {
|
|
13968
|
-
|
|
13969
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14149
|
+
tryAddEntryPath(pkgDir, pkg[key], entries);
|
|
13970
14150
|
}
|
|
13971
14151
|
}
|
|
13972
14152
|
const exports_ = pkg.exports;
|
|
13973
14153
|
if (exports_ && typeof exports_ === "object") {
|
|
13974
14154
|
for (const value of Object.values(exports_)) {
|
|
13975
14155
|
if (typeof value === "string") {
|
|
13976
|
-
|
|
13977
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14156
|
+
tryAddEntryPath(pkgDir, value, entries);
|
|
13978
14157
|
} else if (value && typeof value === "object") {
|
|
13979
|
-
for (const nested of Object.values(
|
|
13980
|
-
value
|
|
13981
|
-
)) {
|
|
14158
|
+
for (const nested of Object.values(value)) {
|
|
13982
14159
|
if (typeof nested === "string") {
|
|
13983
|
-
|
|
13984
|
-
if (fs19.existsSync(resolved)) entries.add(resolved);
|
|
14160
|
+
tryAddEntryPath(pkgDir, nested, entries);
|
|
13985
14161
|
}
|
|
13986
14162
|
}
|
|
13987
14163
|
}
|
|
13988
14164
|
}
|
|
13989
14165
|
}
|
|
13990
14166
|
}
|
|
14167
|
+
function expandGlobPattern(entry, projectRoot) {
|
|
14168
|
+
const dirs = [];
|
|
14169
|
+
if (entry.includes("*")) {
|
|
14170
|
+
const base = entry.replace(/\/\*+$/, "");
|
|
14171
|
+
const baseDir = path24.resolve(projectRoot, base);
|
|
14172
|
+
try {
|
|
14173
|
+
const children = fs19.readdirSync(baseDir, { withFileTypes: true });
|
|
14174
|
+
for (const child of children) {
|
|
14175
|
+
if (child.isDirectory()) {
|
|
14176
|
+
dirs.push(path24.join(baseDir, child.name));
|
|
14177
|
+
}
|
|
14178
|
+
}
|
|
14179
|
+
} catch {
|
|
14180
|
+
}
|
|
14181
|
+
} else {
|
|
14182
|
+
dirs.push(path24.resolve(projectRoot, entry));
|
|
14183
|
+
}
|
|
14184
|
+
return dirs;
|
|
14185
|
+
}
|
|
13991
14186
|
function extractWorkspaceGlobs(pkg, projectRoot) {
|
|
13992
14187
|
const dirs = [];
|
|
13993
14188
|
const workspaces = pkg.workspaces;
|
|
13994
14189
|
if (Array.isArray(workspaces)) {
|
|
13995
14190
|
for (const entry of workspaces) {
|
|
13996
14191
|
if (typeof entry === "string") {
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
|
|
14002
|
-
|
|
14003
|
-
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14192
|
+
dirs.push(...expandGlobPattern(entry, projectRoot));
|
|
14193
|
+
}
|
|
14194
|
+
}
|
|
14195
|
+
}
|
|
14196
|
+
return dirs;
|
|
14197
|
+
}
|
|
14198
|
+
function extractPnpmWorkspaceDirs(projectRoot) {
|
|
14199
|
+
const yamlPath = path24.join(projectRoot, "pnpm-workspace.yaml");
|
|
14200
|
+
if (!fs19.existsSync(yamlPath)) return [];
|
|
14201
|
+
try {
|
|
14202
|
+
const content = fs19.readFileSync(yamlPath, "utf8");
|
|
14203
|
+
const dirs = [];
|
|
14204
|
+
let inPackages = false;
|
|
14205
|
+
const lines = content.split("\n");
|
|
14206
|
+
const itemRe = /^\s+-\s+"([^"]+)"|^\s+-\s+'([^']+)'|^\s+-\s+(\S+)/;
|
|
14207
|
+
for (const line of lines) {
|
|
14208
|
+
const trimmed = line.trim();
|
|
14209
|
+
if (/^packages\s*:\s*$/.test(trimmed)) {
|
|
14210
|
+
inPackages = true;
|
|
14211
|
+
continue;
|
|
14212
|
+
}
|
|
14213
|
+
if (inPackages && trimmed.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
14214
|
+
if (!trimmed.startsWith("-")) {
|
|
14215
|
+
inPackages = false;
|
|
14216
|
+
continue;
|
|
14217
|
+
}
|
|
14218
|
+
}
|
|
14219
|
+
if (inPackages) {
|
|
14220
|
+
const m = itemRe.exec(line);
|
|
14221
|
+
if (m) {
|
|
14222
|
+
const entry = m[1] ?? m[2] ?? m[3];
|
|
14223
|
+
if (entry) {
|
|
14224
|
+
dirs.push(...expandGlobPattern(entry, projectRoot));
|
|
14008
14225
|
}
|
|
14009
|
-
} else {
|
|
14010
|
-
dirs.push(path24.resolve(projectRoot, entry));
|
|
14011
14226
|
}
|
|
14012
14227
|
}
|
|
14013
14228
|
}
|
|
14229
|
+
return dirs;
|
|
14230
|
+
} catch {
|
|
14231
|
+
return [];
|
|
14014
14232
|
}
|
|
14015
|
-
|
|
14233
|
+
}
|
|
14234
|
+
function resolveModulePath(importerPath, moduleSpecifier, indexedFiles) {
|
|
14235
|
+
if (!moduleSpecifier.startsWith(".")) return [];
|
|
14236
|
+
const dir = path24.dirname(importerPath);
|
|
14237
|
+
const base = path24.resolve(dir, moduleSpecifier);
|
|
14238
|
+
const results = [];
|
|
14239
|
+
const stripped = base.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
|
|
14240
|
+
const skipBase = stripped !== base && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(base);
|
|
14241
|
+
const candidates = skipBase ? [stripped] : [base];
|
|
14242
|
+
for (const candidate of candidates) {
|
|
14243
|
+
if (indexedFiles.has(candidate + ".ts")) results.push(candidate + ".ts");
|
|
14244
|
+
if (indexedFiles.has(candidate + ".tsx")) results.push(candidate + ".tsx");
|
|
14245
|
+
if (indexedFiles.has(candidate + ".js")) results.push(candidate + ".js");
|
|
14246
|
+
if (indexedFiles.has(candidate + ".jsx")) results.push(candidate + ".jsx");
|
|
14247
|
+
if (indexedFiles.has(candidate + ".mjs")) results.push(candidate + ".mjs");
|
|
14248
|
+
if (indexedFiles.has(candidate + ".cjs")) results.push(candidate + ".cjs");
|
|
14249
|
+
if (indexedFiles.has(path24.join(candidate, "index.ts")))
|
|
14250
|
+
results.push(path24.join(candidate, "index.ts"));
|
|
14251
|
+
if (indexedFiles.has(path24.join(candidate, "index.tsx")))
|
|
14252
|
+
results.push(path24.join(candidate, "index.tsx"));
|
|
14253
|
+
if (indexedFiles.has(path24.join(candidate, "index.js")))
|
|
14254
|
+
results.push(path24.join(candidate, "index.js"));
|
|
14255
|
+
if (indexedFiles.has(path24.join(candidate, "index.jsx")))
|
|
14256
|
+
results.push(path24.join(candidate, "index.jsx"));
|
|
14257
|
+
if (indexedFiles.has(path24.join(candidate, "index.mjs")))
|
|
14258
|
+
results.push(path24.join(candidate, "index.mjs"));
|
|
14259
|
+
if (indexedFiles.has(path24.join(candidate, "index.cjs")))
|
|
14260
|
+
results.push(path24.join(candidate, "index.cjs"));
|
|
14261
|
+
}
|
|
14262
|
+
return [...new Set(results)];
|
|
14263
|
+
}
|
|
14264
|
+
function parseNamedExportSymbols(matchText) {
|
|
14265
|
+
const braceStart = matchText.indexOf("{");
|
|
14266
|
+
if (braceStart === -1) return null;
|
|
14267
|
+
const braceEnd = matchText.indexOf("}", braceStart);
|
|
14268
|
+
if (braceEnd === -1) return null;
|
|
14269
|
+
const inner = matchText.slice(braceStart + 1, braceEnd);
|
|
14270
|
+
const symbols = [];
|
|
14271
|
+
for (const part of inner.split(",")) {
|
|
14272
|
+
let s = part.trim();
|
|
14273
|
+
if (!s) continue;
|
|
14274
|
+
s = s.replace(/^type\s+/, "");
|
|
14275
|
+
const asIdx = s.search(/\s+as\s+/);
|
|
14276
|
+
if (asIdx !== -1) {
|
|
14277
|
+
s = s.slice(0, asIdx).trim();
|
|
14278
|
+
}
|
|
14279
|
+
if (s) symbols.push(s);
|
|
14280
|
+
}
|
|
14281
|
+
return symbols;
|
|
14016
14282
|
}
|
|
14017
14283
|
function runDeadCodeScan(projectRoot, opts = {}) {
|
|
14018
14284
|
const store = opts.store ?? indexStorePool.acquire(projectRoot, { indexDir: opts.indexDir });
|
|
@@ -14034,12 +14300,71 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
14034
14300
|
}
|
|
14035
14301
|
const discoveredFiles = discoverEntryPoints(projectRoot, opts.userEntryPoints);
|
|
14036
14302
|
const entryFileSet = new Set(discoveredFiles.map((f) => path24.resolve(f)));
|
|
14303
|
+
const indexedFiles = /* @__PURE__ */ new Set();
|
|
14304
|
+
for (const s of allSymbols) indexedFiles.add(s.file);
|
|
14305
|
+
for (const fm of store.getAllFileMetas()) indexedFiles.add(fm.file);
|
|
14037
14306
|
const seedIds = /* @__PURE__ */ new Set();
|
|
14038
14307
|
for (const s of allSymbols) {
|
|
14039
14308
|
if (entryFileSet.has(s.file)) {
|
|
14040
14309
|
seedIds.add(s.id);
|
|
14041
14310
|
}
|
|
14042
14311
|
}
|
|
14312
|
+
const fileToSymbolIds = /* @__PURE__ */ new Map();
|
|
14313
|
+
for (const s of allSymbols) {
|
|
14314
|
+
let byFile = fileToSymbolIds.get(s.file);
|
|
14315
|
+
if (!byFile) {
|
|
14316
|
+
byFile = [];
|
|
14317
|
+
fileToSymbolIds.set(s.file, byFile);
|
|
14318
|
+
}
|
|
14319
|
+
byFile.push(s.id);
|
|
14320
|
+
}
|
|
14321
|
+
const scannedBarrels = /* @__PURE__ */ new Set();
|
|
14322
|
+
const barrelWorkList = [...entryFileSet];
|
|
14323
|
+
while (barrelWorkList.length > 0) {
|
|
14324
|
+
const epFile = barrelWorkList.pop();
|
|
14325
|
+
if (scannedBarrels.has(epFile)) continue;
|
|
14326
|
+
scannedBarrels.add(epFile);
|
|
14327
|
+
try {
|
|
14328
|
+
const content = fs19.readFileSync(epFile, "utf8");
|
|
14329
|
+
const strippedContent = content.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/\/\/[^\n]*/g, (m) => " ".repeat(m.length));
|
|
14330
|
+
const reExportRe = /export\s+(?:(?:type\s+)?\{[\s\S]*?\}\s+from|\*\s+as\s+\w+\s+from|\*\s+from)\s+['"]([^'"]+)['"]/g;
|
|
14331
|
+
let match;
|
|
14332
|
+
while ((match = reExportRe.exec(strippedContent)) !== null) {
|
|
14333
|
+
const moduleSpec = match[1];
|
|
14334
|
+
const resolvedFiles = resolveModulePath(epFile, moduleSpec, indexedFiles);
|
|
14335
|
+
for (const rf of resolvedFiles) {
|
|
14336
|
+
const fileSyms = fileToSymbolIds.get(rf);
|
|
14337
|
+
if (fileSyms) {
|
|
14338
|
+
const namedSymbols = parseNamedExportSymbols(match[0]);
|
|
14339
|
+
if (namedSymbols) {
|
|
14340
|
+
const nameSet = new Set(namedSymbols);
|
|
14341
|
+
for (const sid of fileSyms) {
|
|
14342
|
+
const sym = symbolById.get(sid);
|
|
14343
|
+
if (sym && nameSet.has(sym.name)) seedIds.add(sid);
|
|
14344
|
+
}
|
|
14345
|
+
} else {
|
|
14346
|
+
for (const sid of fileSyms) seedIds.add(sid);
|
|
14347
|
+
}
|
|
14348
|
+
}
|
|
14349
|
+
if (!scannedBarrels.has(rf)) {
|
|
14350
|
+
barrelWorkList.push(rf);
|
|
14351
|
+
}
|
|
14352
|
+
}
|
|
14353
|
+
}
|
|
14354
|
+
} catch (err) {
|
|
14355
|
+
if (err instanceof Error && err.code !== "ENOENT") {
|
|
14356
|
+
console.warn(
|
|
14357
|
+
JSON.stringify({
|
|
14358
|
+
level: "warn",
|
|
14359
|
+
event: "dead_code_scan_barrel_read_failed",
|
|
14360
|
+
message: err.message,
|
|
14361
|
+
file: epFile,
|
|
14362
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
14363
|
+
})
|
|
14364
|
+
);
|
|
14365
|
+
}
|
|
14366
|
+
}
|
|
14367
|
+
}
|
|
14043
14368
|
const alive = new Set(seedIds);
|
|
14044
14369
|
const frontier = [...seedIds];
|
|
14045
14370
|
const visitedEdges = /* @__PURE__ */ new Set();
|
|
@@ -14073,8 +14398,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
14073
14398
|
dead.push({
|
|
14074
14399
|
name: s.name,
|
|
14075
14400
|
kind: s.kind,
|
|
14076
|
-
lang: "ts",
|
|
14077
|
-
// populated from symbol file metadata
|
|
14401
|
+
lang: detectLang(s.file) ?? "ts",
|
|
14078
14402
|
file: s.file,
|
|
14079
14403
|
line: s.line,
|
|
14080
14404
|
reason
|
|
@@ -14099,16 +14423,14 @@ function runDeadCodeScan(projectRoot, opts = {}) {
|
|
|
14099
14423
|
deadFiles.push({
|
|
14100
14424
|
file,
|
|
14101
14425
|
symbolCount: syms.length,
|
|
14102
|
-
lang:
|
|
14426
|
+
lang: detectLang(file) ?? "ts"
|
|
14103
14427
|
});
|
|
14104
14428
|
}
|
|
14105
14429
|
}
|
|
14106
14430
|
const deadPackages = [];
|
|
14107
14431
|
const pkgEntries = findPackageEntries(projectRoot);
|
|
14108
14432
|
for (const [pkgName, pkgDir] of pkgEntries) {
|
|
14109
|
-
const pkgFiles = allSymbols.filter(
|
|
14110
|
-
(s) => s.file.startsWith(pkgDir + path24.sep)
|
|
14111
|
-
);
|
|
14433
|
+
const pkgFiles = allSymbols.filter((s) => s.file.startsWith(pkgDir + path24.sep));
|
|
14112
14434
|
if (pkgFiles.length === 0) continue;
|
|
14113
14435
|
const pkgUsed = pkgFiles.filter((s) => alive.has(s.id));
|
|
14114
14436
|
if (pkgUsed.length === 0) {
|
|
@@ -14149,7 +14471,10 @@ function findPackageEntries(projectRoot) {
|
|
|
14149
14471
|
pkgMap.set(rootPkg.name, projectRoot);
|
|
14150
14472
|
}
|
|
14151
14473
|
if (rootPkg) {
|
|
14152
|
-
|
|
14474
|
+
let wsDirs = extractWorkspaceGlobs(rootPkg, projectRoot);
|
|
14475
|
+
if (wsDirs.length === 0) {
|
|
14476
|
+
wsDirs = extractPnpmWorkspaceDirs(projectRoot);
|
|
14477
|
+
}
|
|
14153
14478
|
for (const wsDir of wsDirs) {
|
|
14154
14479
|
const wsPkg = tryReadJson(path24.join(wsDir, "package.json"));
|
|
14155
14480
|
if (wsPkg && typeof wsPkg.name === "string") {
|
|
@@ -14766,7 +15091,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
|
|
|
14766
15091
|
|
|
14767
15092
|
// src/e2e.ts
|
|
14768
15093
|
init_util();
|
|
14769
|
-
import { open, readdir as
|
|
15094
|
+
import { open, readdir as readdir6 } from "node:fs/promises";
|
|
14770
15095
|
import * as path27 from "node:path";
|
|
14771
15096
|
async function readBoundedText(filePath, maxBytes) {
|
|
14772
15097
|
let handle;
|
|
@@ -14884,7 +15209,7 @@ async function scanWorkspace(root, maxDepth, signal) {
|
|
|
14884
15209
|
}
|
|
14885
15210
|
let entries;
|
|
14886
15211
|
try {
|
|
14887
|
-
entries = await
|
|
15212
|
+
entries = await readdir6(current.directory, { withFileTypes: true });
|
|
14888
15213
|
} catch {
|
|
14889
15214
|
continue;
|
|
14890
15215
|
}
|
|
@@ -14943,7 +15268,7 @@ async function detectPackageManager3(projectRoot, scanRoot, declared) {
|
|
|
14943
15268
|
while (true) {
|
|
14944
15269
|
const names = /* @__PURE__ */ new Set();
|
|
14945
15270
|
try {
|
|
14946
|
-
for (const entry of await
|
|
15271
|
+
for (const entry of await readdir6(directory)) names.add(entry);
|
|
14947
15272
|
} catch {
|
|
14948
15273
|
}
|
|
14949
15274
|
if (names.has("pnpm-lock.yaml")) return "pnpm";
|
|
@@ -15011,7 +15336,7 @@ async function collectSpecs(root, framework, testDirectory, signal) {
|
|
|
15011
15336
|
if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };
|
|
15012
15337
|
let entries;
|
|
15013
15338
|
try {
|
|
15014
|
-
entries = await
|
|
15339
|
+
entries = await readdir6(directory, { withFileTypes: true });
|
|
15015
15340
|
} catch {
|
|
15016
15341
|
continue;
|
|
15017
15342
|
}
|
|
@@ -17468,7 +17793,7 @@ async function detectFixer(cwd) {
|
|
|
17468
17793
|
init_util();
|
|
17469
17794
|
import { spawn as spawn10 } from "node:child_process";
|
|
17470
17795
|
import { statSync as statSync4 } from "node:fs";
|
|
17471
|
-
import { dirname as
|
|
17796
|
+
import { dirname as dirname14, resolve as resolve13, sep as sep6 } from "node:path";
|
|
17472
17797
|
import { assessCommitSafety } from "@wrongstack/core/coordination";
|
|
17473
17798
|
import { buildChildEnv as buildChildEnv4 } from "@wrongstack/core/utils";
|
|
17474
17799
|
var TIMEOUT_MS2 = 3e4;
|
|
@@ -17630,7 +17955,7 @@ function findGitDir2(cwd, projectRoot) {
|
|
|
17630
17955
|
} catch {
|
|
17631
17956
|
}
|
|
17632
17957
|
if (dir === root) break;
|
|
17633
|
-
const parent =
|
|
17958
|
+
const parent = dirname14(dir);
|
|
17634
17959
|
if (parent === dir) break;
|
|
17635
17960
|
dir = parent;
|
|
17636
17961
|
}
|
|
@@ -19774,7 +20099,7 @@ function createKanbanPresenceWrapper(projectRoot, input, ctx) {
|
|
|
19774
20099
|
|
|
19775
20100
|
// src/session-kanban.ts
|
|
19776
20101
|
import { watch } from "node:fs";
|
|
19777
|
-
import { basename as basename12, dirname as
|
|
20102
|
+
import { basename as basename12, dirname as dirname15 } from "node:path";
|
|
19778
20103
|
import { getSharedProjectMailbox } from "@wrongstack/core/coordination";
|
|
19779
20104
|
import {
|
|
19780
20105
|
loadPlan,
|
|
@@ -20246,7 +20571,7 @@ function attachSessionKanbanMirror(context) {
|
|
|
20246
20571
|
const configureWatcher = () => {
|
|
20247
20572
|
const planPath = context.meta["plan.path"];
|
|
20248
20573
|
const taskPath = context.meta["task.path"];
|
|
20249
|
-
const candidate = typeof planPath === "string" && planPath ?
|
|
20574
|
+
const candidate = typeof planPath === "string" && planPath ? dirname15(planPath) : typeof taskPath === "string" && taskPath ? dirname15(taskPath) : "";
|
|
20250
20575
|
if (!candidate || candidate === watchedDir) return;
|
|
20251
20576
|
watcher?.close();
|
|
20252
20577
|
watcher = null;
|
|
@@ -22128,12 +22453,13 @@ init_util();
|
|
|
22128
22453
|
import * as fs28 from "node:fs/promises";
|
|
22129
22454
|
import { FsError, ToolValidationError as ToolValidationError5 } from "@wrongstack/core/types";
|
|
22130
22455
|
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
22456
|
+
var ADVANCED_MODE_META_KEY = "tools.read.advancedMode";
|
|
22131
22457
|
var MAX_BYTES2 = 5 * 1024 * 1024;
|
|
22132
22458
|
var readTool = {
|
|
22133
22459
|
name: "read",
|
|
22134
22460
|
category: "Filesystem",
|
|
22135
|
-
description: "Read the contents of a file with line numbers. This is the primary way to inspect source code, configuration, or any text file before making changes. Lines are returned 1-indexed with a ` N| ` prefix for easy reference in edits.",
|
|
22136
|
-
usageHint: "FOUNDATIONAL TOOL \u2014 call this before almost any edit operation.\n\nBest practices:\n- Always read a file before using `edit`, `replace`, or `write` on it (the system often requires it for safety).\n- Use `offset` + `limit` for very large files instead of reading everything at once.\n- Default limit is generous (2000 lines) but can be increased.\n- The output format is designed to be directly usable as context for `edit` operations.",
|
|
22461
|
+
description: "Read the contents of a file with line numbers. This is the primary way to inspect source code, configuration, or any text file before making changes. Lines are returned 1-indexed with a ` N| ` prefix for easy reference in edits. When advanced mode is on or `includeSymbols` is set, the result also includes a `symbols` field listing codebase-index symbol names, kinds, and line numbers for the file (not file content).",
|
|
22462
|
+
usageHint: "FOUNDATIONAL TOOL \u2014 call this before almost any edit operation.\n\nBest practices:\n- Always read a file before using `edit`, `replace`, or `write` on it (the system often requires it for safety).\n- Use `offset` + `limit` for very large files instead of reading everything at once.\n- Default limit is generous (2000 lines) but can be increased.\n- The output format is designed to be directly usable as context for `edit` operations.\n- Set `includeSymbols: true` to also receive the codebase-index symbol listing for the file.\n- Enable advanced mode (`ctx.meta['tools.read.advancedMode'] = true`) to auto-inject symbols on every read.",
|
|
22137
22463
|
selection: {
|
|
22138
22464
|
doNotUseWhen: "you need to search many files for matching content.",
|
|
22139
22465
|
useInstead: ["grep"]
|
|
@@ -22163,11 +22489,15 @@ var readTool = {
|
|
|
22163
22489
|
type: "string",
|
|
22164
22490
|
enum: ["content", "summary"],
|
|
22165
22491
|
description: "Return full line-numbered content (default) or a compact file summary with imports/exports/symbols."
|
|
22492
|
+
},
|
|
22493
|
+
includeSymbols: {
|
|
22494
|
+
type: "boolean",
|
|
22495
|
+
description: "When true, include the codebase-index symbol list for this file as a structured `symbols` field in the result. Overrides the advanced-mode meta flag per-call."
|
|
22166
22496
|
}
|
|
22167
22497
|
},
|
|
22168
22498
|
required: ["path"]
|
|
22169
22499
|
},
|
|
22170
|
-
async execute(input, ctx) {
|
|
22500
|
+
async execute(input, ctx, execOpts) {
|
|
22171
22501
|
if (!input?.path) {
|
|
22172
22502
|
throw new ToolValidationError5({
|
|
22173
22503
|
message: "read: path is required",
|
|
@@ -22175,6 +22505,7 @@ var readTool = {
|
|
|
22175
22505
|
});
|
|
22176
22506
|
}
|
|
22177
22507
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
22508
|
+
const shouldIncludeSymbols = input.includeSymbols === true || input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true;
|
|
22178
22509
|
let stat18;
|
|
22179
22510
|
try {
|
|
22180
22511
|
stat18 = await fs28.stat(absPath);
|
|
@@ -22218,13 +22549,15 @@ var readTool = {
|
|
|
22218
22549
|
const requestedEnd = prior ? Math.min(offset + limit - 1, prior.totalLines) : offset + limit - 1;
|
|
22219
22550
|
if (input.mode !== "summary" && limit > 0 && prior && coversRange(prior, stat18.mtimeMs, offset, requestedEnd)) {
|
|
22220
22551
|
ctx.recordRead(absPath, stat18.mtimeMs);
|
|
22552
|
+
const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal) : void 0;
|
|
22221
22553
|
return {
|
|
22222
22554
|
text: `[unchanged since previous read: "${input.path}" mtime=${Math.round(stat18.mtimeMs)}; requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,
|
|
22223
22555
|
total_lines: prior.totalLines,
|
|
22224
22556
|
encoding: "utf8",
|
|
22225
22557
|
truncated: requestedEnd < prior.totalLines,
|
|
22226
22558
|
cached: true,
|
|
22227
|
-
note: "Repeated read suppressed to save tokens."
|
|
22559
|
+
note: mergeSymbolNote("Repeated read suppressed to save tokens.", symResult2?.note),
|
|
22560
|
+
...symResult2?.symbols ? { symbols: symResult2.symbols } : {}
|
|
22228
22561
|
};
|
|
22229
22562
|
}
|
|
22230
22563
|
const buf = await fs28.readFile(absPath);
|
|
@@ -22238,27 +22571,43 @@ var readTool = {
|
|
|
22238
22571
|
if (input.mode === "summary") {
|
|
22239
22572
|
ctx.recordRead(absPath, stat18.mtimeMs, "user", contentHash);
|
|
22240
22573
|
rememberReadRange(ctx, absPath, stat18.mtimeMs, total, 1, Math.min(total, 200));
|
|
22574
|
+
const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal) : void 0;
|
|
22241
22575
|
return {
|
|
22242
22576
|
text: summarizeFile(input.path, stat18.size, allLines),
|
|
22243
22577
|
total_lines: total,
|
|
22244
22578
|
encoding: "utf8",
|
|
22245
22579
|
truncated: total > 200,
|
|
22246
|
-
note:
|
|
22580
|
+
note: mergeSymbolNote(
|
|
22581
|
+
"Summary mode returned compact structure instead of full file content.",
|
|
22582
|
+
symResult2?.note
|
|
22583
|
+
),
|
|
22584
|
+
...symResult2?.symbols ? { symbols: symResult2.symbols } : {}
|
|
22247
22585
|
};
|
|
22248
22586
|
}
|
|
22249
22587
|
if (limit === 0) {
|
|
22250
22588
|
ctx.recordRead(absPath, stat18.mtimeMs, "user", contentHash);
|
|
22251
22589
|
rememberReadRange(ctx, absPath, stat18.mtimeMs, total, 1, 0);
|
|
22252
|
-
|
|
22590
|
+
const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal) : void 0;
|
|
22591
|
+
return {
|
|
22592
|
+
text: "",
|
|
22593
|
+
total_lines: total,
|
|
22594
|
+
encoding: "utf8",
|
|
22595
|
+
truncated: total > 0,
|
|
22596
|
+
...symResult2?.symbols ? { symbols: symResult2.symbols } : {},
|
|
22597
|
+
...symResult2?.note ? { note: symResult2.note } : {}
|
|
22598
|
+
};
|
|
22253
22599
|
}
|
|
22254
22600
|
if (offset > total) {
|
|
22255
22601
|
ctx.recordRead(absPath, stat18.mtimeMs, "user", contentHash);
|
|
22256
22602
|
rememberReadRange(ctx, absPath, stat18.mtimeMs, total, total + 1, total + 1);
|
|
22603
|
+
const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal) : void 0;
|
|
22257
22604
|
return {
|
|
22258
22605
|
text: `[offset ${offset} is past end of file "${input.path}" \u2014 file has ${total} line(s). Do not retry this offset.]`,
|
|
22259
22606
|
total_lines: total,
|
|
22260
22607
|
encoding: "utf8",
|
|
22261
|
-
truncated: false
|
|
22608
|
+
truncated: false,
|
|
22609
|
+
...symResult2?.symbols ? { symbols: symResult2.symbols } : {},
|
|
22610
|
+
...symResult2?.note ? { note: symResult2.note } : {}
|
|
22262
22611
|
};
|
|
22263
22612
|
}
|
|
22264
22613
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
@@ -22267,14 +22616,53 @@ var readTool = {
|
|
|
22267
22616
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
22268
22617
|
ctx.recordRead(absPath, stat18.mtimeMs, "user", contentHash);
|
|
22269
22618
|
rememberReadRange(ctx, absPath, stat18.mtimeMs, total, offset, offset + slice.length - 1);
|
|
22619
|
+
const symResult = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, execOpts?.signal) : void 0;
|
|
22270
22620
|
return {
|
|
22271
22621
|
text: numbered,
|
|
22272
22622
|
total_lines: total,
|
|
22273
22623
|
encoding: "utf8",
|
|
22274
|
-
truncated
|
|
22624
|
+
truncated,
|
|
22625
|
+
...symResult?.symbols ? { symbols: symResult.symbols } : {},
|
|
22626
|
+
...symResult?.note ? { note: symResult.note } : {}
|
|
22275
22627
|
};
|
|
22276
22628
|
}
|
|
22277
22629
|
};
|
|
22630
|
+
async function fetchSymbolsForFile(absPath, ctx, signal) {
|
|
22631
|
+
try {
|
|
22632
|
+
const state = getIndexState();
|
|
22633
|
+
if (!state.ready) return {};
|
|
22634
|
+
const { results, total } = await searchCodebaseIndex(
|
|
22635
|
+
{
|
|
22636
|
+
projectRoot: ctx.projectRoot,
|
|
22637
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
22638
|
+
query: "",
|
|
22639
|
+
file: absPath,
|
|
22640
|
+
limit: 500
|
|
22641
|
+
},
|
|
22642
|
+
{ signal }
|
|
22643
|
+
);
|
|
22644
|
+
if (results.length === 0) return {};
|
|
22645
|
+
const sorted = results.map((r) => ({
|
|
22646
|
+
name: r.name,
|
|
22647
|
+
kind: r.kind,
|
|
22648
|
+
line: r.line,
|
|
22649
|
+
col: r.col,
|
|
22650
|
+
signature: r.signature
|
|
22651
|
+
})).sort((a, b) => a.line - b.line || a.col - b.col);
|
|
22652
|
+
const result = { symbols: sorted };
|
|
22653
|
+
if (total > results.length) {
|
|
22654
|
+
result.note = `Symbol listing truncated to ${results.length} of ${total} entries.`;
|
|
22655
|
+
}
|
|
22656
|
+
return result;
|
|
22657
|
+
} catch {
|
|
22658
|
+
return {};
|
|
22659
|
+
}
|
|
22660
|
+
}
|
|
22661
|
+
function mergeSymbolNote(note, symNote) {
|
|
22662
|
+
if (!symNote) return note;
|
|
22663
|
+
if (!note) return symNote;
|
|
22664
|
+
return `${note} ${symNote}`;
|
|
22665
|
+
}
|
|
22278
22666
|
var READ_RANGES_META_KEY = "tools.read.ranges.v1";
|
|
22279
22667
|
function getReadRanges(ctx) {
|
|
22280
22668
|
const existing = ctx.meta[READ_RANGES_META_KEY];
|
|
@@ -26097,6 +26485,7 @@ export {
|
|
|
26097
26485
|
resetIndexCircuitBreaker,
|
|
26098
26486
|
resetPersistentProcessRegistry,
|
|
26099
26487
|
resolvePinnedBrowserTarget,
|
|
26488
|
+
resolveProjectIndexDaemonAvailability,
|
|
26100
26489
|
resolveSessionShell,
|
|
26101
26490
|
runDeadCodeScan,
|
|
26102
26491
|
runStartupIndex,
|