@wrongstack/tools 0.296.3 → 0.297.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +15 -0
- package/dist/audit.js.map +2 -2
- package/dist/bash.js +15 -0
- package/dist/bash.js.map +2 -2
- package/dist/builtin.js +1291 -1216
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.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 +1259 -1196
- 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 +20 -5
- package/dist/codebase-index/project-server.js.map +2 -2
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/exec.js +15 -0
- package/dist/exec.js.map +2 -2
- package/dist/format.js +15 -0
- package/dist/format.js.map +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1260 -1182
- package/dist/index.js.map +4 -4
- package/dist/install.js +15 -0
- package/dist/install.js.map +2 -2
- package/dist/languages/index.js +15 -0
- package/dist/languages/index.js.map +2 -2
- package/dist/lint.js +15 -0
- package/dist/lint.js.map +2 -2
- package/dist/next-steps.d.ts +23 -0
- package/dist/next-steps.d.ts.map +1 -1
- package/dist/next-steps.js +4 -0
- package/dist/next-steps.js.map +2 -2
- package/dist/outdated.js +15 -0
- package/dist/outdated.js.map +2 -2
- package/dist/pack.js +1291 -1216
- package/dist/pack.js.map +4 -4
- package/dist/process-registry.d.ts +9 -0
- package/dist/process-registry.d.ts.map +1 -1
- package/dist/process-registry.js +15 -0
- package/dist/process-registry.js.map +2 -2
- package/dist/ps-slash.js +15 -0
- package/dist/ps-slash.js.map +2 -2
- package/dist/read.js +67 -9
- package/dist/read.js.map +2 -2
- package/dist/test.js +15 -0
- package/dist/test.js.map +2 -2
- package/dist/tool-tier.js +1291 -1216
- package/dist/tool-tier.js.map +4 -4
- package/dist/typecheck.js +15 -0
- package/dist/typecheck.js.map +2 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -549,6 +549,7 @@ var init_process_registry = __esm({
|
|
|
549
549
|
}
|
|
550
550
|
/** Get all tracked processes. */
|
|
551
551
|
list() {
|
|
552
|
+
this._pruneAllStale();
|
|
552
553
|
return Array.from(this.processes.values());
|
|
553
554
|
}
|
|
554
555
|
/** Get processes filtered by name (e.g. 'bash', 'exec'). */
|
|
@@ -579,6 +580,7 @@ var init_process_registry = __esm({
|
|
|
579
580
|
* Combined stats for observability — used by /ps and the TUI status bar.
|
|
580
581
|
*/
|
|
581
582
|
stats() {
|
|
583
|
+
this._pruneAllStale();
|
|
582
584
|
return {
|
|
583
585
|
activeCount: this.activeCount,
|
|
584
586
|
backgroundCount: this.activeBackgroundCount,
|
|
@@ -818,6 +820,19 @@ var init_process_registry = __esm({
|
|
|
818
820
|
this.processes.delete(pid);
|
|
819
821
|
}
|
|
820
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Remove every stale entry, not just one PID. `list()`/`stats()` — the
|
|
825
|
+
* surfaces the TUI status bar and `/ps` poll — must prune too: a child
|
|
826
|
+
* whose 'close' event never fires (e.g. Windows grandchildren holding stdio
|
|
827
|
+
* open) would otherwise linger in the registry until someone looks up its
|
|
828
|
+
* exact PID, and PID reuse meanwhile makes `get()`/`kill()` target the
|
|
829
|
+
* wrong process. RAM-leak audit 2026-07-31, MEDIUM.
|
|
830
|
+
*/
|
|
831
|
+
_pruneAllStale() {
|
|
832
|
+
for (const [pid, entry] of this.processes) {
|
|
833
|
+
if (this._isStaleEntry(entry)) this.processes.delete(pid);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
821
836
|
};
|
|
822
837
|
}
|
|
823
838
|
});
|
|
@@ -4896,16 +4911,16 @@ var init_legacy_bridge = __esm({
|
|
|
4896
4911
|
});
|
|
4897
4912
|
|
|
4898
4913
|
// src/codebase-index/languages.ts
|
|
4899
|
-
import * as
|
|
4914
|
+
import * as path18 from "node:path";
|
|
4900
4915
|
function detectLang(file) {
|
|
4901
|
-
const base =
|
|
4916
|
+
const base = path18.basename(file);
|
|
4902
4917
|
const lowerBase = base.toLowerCase();
|
|
4903
4918
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4904
4919
|
return "ts";
|
|
4905
4920
|
}
|
|
4906
4921
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4907
4922
|
if (special) return special;
|
|
4908
|
-
const ext =
|
|
4923
|
+
const ext = path18.extname(base).toLowerCase();
|
|
4909
4924
|
if (!ext) return null;
|
|
4910
4925
|
return EXT_TO_LANG[ext] ?? null;
|
|
4911
4926
|
}
|
|
@@ -5244,10 +5259,10 @@ __export(go_parser_exports, {
|
|
|
5244
5259
|
detectLang: () => detectLang,
|
|
5245
5260
|
parseSymbols: () => parseSymbols2
|
|
5246
5261
|
});
|
|
5247
|
-
import { spawn as
|
|
5248
|
-
import * as
|
|
5249
|
-
import * as
|
|
5250
|
-
import * as
|
|
5262
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5263
|
+
import * as os6 from "node:os";
|
|
5264
|
+
import * as path19 from "node:path";
|
|
5265
|
+
import * as fs14 from "node:fs/promises";
|
|
5251
5266
|
async function parseSymbols2(opts) {
|
|
5252
5267
|
const { file, content, lang } = opts;
|
|
5253
5268
|
try {
|
|
@@ -5319,16 +5334,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5319
5334
|
try {
|
|
5320
5335
|
let scriptPath = _cachedGoScriptPath;
|
|
5321
5336
|
if (!scriptPath) {
|
|
5322
|
-
const tmpDir = await
|
|
5323
|
-
scriptPath =
|
|
5324
|
-
await
|
|
5337
|
+
const tmpDir = await fs14.mkdtemp(path19.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5338
|
+
scriptPath = path19.join(tmpDir, "parse.go");
|
|
5339
|
+
await fs14.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5325
5340
|
_cachedGoScriptPath = scriptPath;
|
|
5326
5341
|
}
|
|
5327
5342
|
const goBinary = resolveWin32Command("go");
|
|
5328
5343
|
const goResult = await new Promise(
|
|
5329
5344
|
(resolve17, reject) => {
|
|
5330
5345
|
let settled = false;
|
|
5331
|
-
const proc =
|
|
5346
|
+
const proc = spawn5(goBinary, ["run", scriptPath], {
|
|
5332
5347
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5333
5348
|
windowsHide: true
|
|
5334
5349
|
});
|
|
@@ -5931,10 +5946,10 @@ __export(py_parser_exports, {
|
|
|
5931
5946
|
detectLang: () => detectLang,
|
|
5932
5947
|
parseSymbols: () => parseSymbols4
|
|
5933
5948
|
});
|
|
5934
|
-
import { spawn as
|
|
5935
|
-
import * as
|
|
5936
|
-
import * as
|
|
5937
|
-
import * as
|
|
5949
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
5950
|
+
import * as fs15 from "node:fs/promises";
|
|
5951
|
+
import * as os7 from "node:os";
|
|
5952
|
+
import * as path20 from "node:path";
|
|
5938
5953
|
async function parseSymbols4(opts) {
|
|
5939
5954
|
const { file, content, lang } = opts;
|
|
5940
5955
|
try {
|
|
@@ -5956,7 +5971,7 @@ async function resolvePython() {
|
|
|
5956
5971
|
function commandIsAvailable(command) {
|
|
5957
5972
|
return new Promise((resolve17) => {
|
|
5958
5973
|
let settled = false;
|
|
5959
|
-
const proc =
|
|
5974
|
+
const proc = spawn6(command, ["--version"], {
|
|
5960
5975
|
stdio: "ignore",
|
|
5961
5976
|
windowsHide: true
|
|
5962
5977
|
});
|
|
@@ -5978,7 +5993,7 @@ function commandIsAvailable(command) {
|
|
|
5978
5993
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
5979
5994
|
return new Promise((resolve17, reject) => {
|
|
5980
5995
|
let settled = false;
|
|
5981
|
-
const proc =
|
|
5996
|
+
const proc = spawn6(pyBinary, [scriptPath, filePath], {
|
|
5982
5997
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5983
5998
|
windowsHide: true
|
|
5984
5999
|
});
|
|
@@ -6012,10 +6027,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
6012
6027
|
async function syncPyParse(filePath, content, lang) {
|
|
6013
6028
|
try {
|
|
6014
6029
|
if (!_cachedScriptPath) {
|
|
6015
|
-
const tmpDir =
|
|
6016
|
-
await
|
|
6017
|
-
_cachedScriptPath =
|
|
6018
|
-
await
|
|
6030
|
+
const tmpDir = path20.join(os7.tmpdir(), "ws-py-parse");
|
|
6031
|
+
await fs15.mkdir(tmpDir, { recursive: true });
|
|
6032
|
+
_cachedScriptPath = path20.join(tmpDir, "parse.py");
|
|
6033
|
+
await fs15.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
6019
6034
|
}
|
|
6020
6035
|
cachedPyBinary ??= resolvePython();
|
|
6021
6036
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6269,10 +6284,10 @@ __export(rs_parser_exports, {
|
|
|
6269
6284
|
detectLang: () => detectLang,
|
|
6270
6285
|
parseSymbols: () => parseSymbols5
|
|
6271
6286
|
});
|
|
6272
|
-
import { expectDefined as
|
|
6273
|
-
import { execFile, spawn as
|
|
6274
|
-
import * as
|
|
6275
|
-
import * as
|
|
6287
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6288
|
+
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6289
|
+
import * as fs16 from "node:fs/promises";
|
|
6290
|
+
import * as path21 from "node:path";
|
|
6276
6291
|
async function parseSymbols5(opts) {
|
|
6277
6292
|
const { file, content, lang } = opts;
|
|
6278
6293
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -6294,7 +6309,7 @@ function checkNativeParser() {
|
|
|
6294
6309
|
nativeParserAvailability ??= (async () => {
|
|
6295
6310
|
try {
|
|
6296
6311
|
await probe("rustc", ["--version"]);
|
|
6297
|
-
const toolsDir =
|
|
6312
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6298
6313
|
await probe(
|
|
6299
6314
|
"cargo",
|
|
6300
6315
|
[
|
|
@@ -6303,7 +6318,7 @@ function checkNativeParser() {
|
|
|
6303
6318
|
"--format-version",
|
|
6304
6319
|
"1",
|
|
6305
6320
|
"--manifest-path",
|
|
6306
|
-
|
|
6321
|
+
path21.join(toolsDir, "Cargo.toml")
|
|
6307
6322
|
]
|
|
6308
6323
|
);
|
|
6309
6324
|
return true;
|
|
@@ -6315,17 +6330,17 @@ function checkNativeParser() {
|
|
|
6315
6330
|
}
|
|
6316
6331
|
async function tryNativeParse(file, content) {
|
|
6317
6332
|
try {
|
|
6318
|
-
const toolsDir =
|
|
6319
|
-
const crateDir =
|
|
6320
|
-
const tmpFile =
|
|
6321
|
-
await
|
|
6333
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6334
|
+
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6335
|
+
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6336
|
+
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6322
6337
|
const cargoBinary = resolveWin32Command("cargo");
|
|
6323
6338
|
const result = await new Promise(
|
|
6324
6339
|
(resolve17, reject) => {
|
|
6325
6340
|
let settled = false;
|
|
6326
|
-
const proc =
|
|
6341
|
+
const proc = spawn7(
|
|
6327
6342
|
cargoBinary,
|
|
6328
|
-
["run", "--manifest-path",
|
|
6343
|
+
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6329
6344
|
{
|
|
6330
6345
|
cwd: process.cwd(),
|
|
6331
6346
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -6384,7 +6399,7 @@ function regexParse(opts) {
|
|
|
6384
6399
|
let hi = lineOffsets2.length - 1;
|
|
6385
6400
|
while (lo < hi) {
|
|
6386
6401
|
const mid = lo + hi + 1 >>> 1;
|
|
6387
|
-
if (
|
|
6402
|
+
if (expectDefined3(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6388
6403
|
else hi = mid - 1;
|
|
6389
6404
|
}
|
|
6390
6405
|
return lo + 1;
|
|
@@ -6396,7 +6411,7 @@ function regexParse(opts) {
|
|
|
6396
6411
|
for (const pattern of RS_PATTERNS) {
|
|
6397
6412
|
pattern.regex.lastIndex = 0;
|
|
6398
6413
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
6399
|
-
const name =
|
|
6414
|
+
const name = expectDefined3(match[1]);
|
|
6400
6415
|
const offset = match.index ?? 0;
|
|
6401
6416
|
const line = lineFromOffset(offset);
|
|
6402
6417
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6453,8 +6468,8 @@ __export(json_parser_exports, {
|
|
|
6453
6468
|
detectLang: () => detectLang,
|
|
6454
6469
|
parseSymbols: () => parseSymbols6
|
|
6455
6470
|
});
|
|
6456
|
-
import { expectDefined as
|
|
6457
|
-
import * as
|
|
6471
|
+
import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
|
|
6472
|
+
import * as path22 from "node:path";
|
|
6458
6473
|
function parseSymbols6(opts) {
|
|
6459
6474
|
const { file, content, lang } = opts;
|
|
6460
6475
|
try {
|
|
@@ -6466,7 +6481,7 @@ function parseSymbols6(opts) {
|
|
|
6466
6481
|
function regexParse2(opts) {
|
|
6467
6482
|
const { file, content, lang } = opts;
|
|
6468
6483
|
const symbols = [];
|
|
6469
|
-
const basename13 =
|
|
6484
|
+
const basename13 = path22.basename(file).toLowerCase();
|
|
6470
6485
|
const isPackageJson = basename13 === "package.json";
|
|
6471
6486
|
const isTsconfig = basename13 === "tsconfig.json" || basename13 === "tsconfig.build.json";
|
|
6472
6487
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -6481,22 +6496,22 @@ function regexParse2(opts) {
|
|
|
6481
6496
|
let hi = lineOffsets2.length - 1;
|
|
6482
6497
|
while (lo < hi) {
|
|
6483
6498
|
const mid = lo + hi + 1 >>> 1;
|
|
6484
|
-
if (
|
|
6499
|
+
if (expectDefined4(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6485
6500
|
else hi = mid - 1;
|
|
6486
6501
|
}
|
|
6487
6502
|
return lo + 1;
|
|
6488
6503
|
}
|
|
6489
6504
|
const rootMatch = content.match(/^\s*\{/m);
|
|
6490
6505
|
if (rootMatch) {
|
|
6491
|
-
const offset =
|
|
6506
|
+
const offset = expectDefined4(rootMatch.index);
|
|
6492
6507
|
const line = lineFromOffset(offset);
|
|
6493
6508
|
symbols.push(
|
|
6494
6509
|
makeSymbol({
|
|
6495
|
-
name:
|
|
6510
|
+
name: path22.basename(file),
|
|
6496
6511
|
kind: "object",
|
|
6497
6512
|
line,
|
|
6498
6513
|
col: 0,
|
|
6499
|
-
signature: `"${
|
|
6514
|
+
signature: `"${path22.basename(file)}" = { ... }`,
|
|
6500
6515
|
file,
|
|
6501
6516
|
lang
|
|
6502
6517
|
})
|
|
@@ -6504,7 +6519,7 @@ function regexParse2(opts) {
|
|
|
6504
6519
|
}
|
|
6505
6520
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
6506
6521
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
6507
|
-
const key =
|
|
6522
|
+
const key = expectDefined4(match[1]);
|
|
6508
6523
|
const offset = match.index ?? 0;
|
|
6509
6524
|
const line = lineFromOffset(offset);
|
|
6510
6525
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6551,7 +6566,7 @@ function regexParse2(opts) {
|
|
|
6551
6566
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
6552
6567
|
const defsMatch = defsRegex.exec(content);
|
|
6553
6568
|
if (defsMatch !== null) {
|
|
6554
|
-
const offset =
|
|
6569
|
+
const offset = expectDefined4(defsMatch.index);
|
|
6555
6570
|
const line = lineFromOffset(offset);
|
|
6556
6571
|
symbols.push(
|
|
6557
6572
|
makeSymbol({
|
|
@@ -6576,7 +6591,7 @@ function regexParse2(opts) {
|
|
|
6576
6591
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
6577
6592
|
const offset = match.index ?? 0;
|
|
6578
6593
|
const line = lineFromOffset(offset);
|
|
6579
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
6594
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined4(match[0]);
|
|
6580
6595
|
symbols.push(
|
|
6581
6596
|
makeSymbol({
|
|
6582
6597
|
name: key,
|
|
@@ -6595,12 +6610,12 @@ function regexParse2(opts) {
|
|
|
6595
6610
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineFromOffset) {
|
|
6596
6611
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
6597
6612
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
6598
|
-
const blockContent =
|
|
6613
|
+
const blockContent = expectDefined4(match[0]);
|
|
6599
6614
|
const blockOffset = match.index ?? 0;
|
|
6600
6615
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
6601
6616
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
6602
|
-
const key =
|
|
6603
|
-
const keyOffset = blockOffset +
|
|
6617
|
+
const key = expectDefined4(scriptMatch[1]);
|
|
6618
|
+
const keyOffset = blockOffset + expectDefined4(scriptMatch.index);
|
|
6604
6619
|
const line = lineFromOffset(keyOffset);
|
|
6605
6620
|
symbols.push(
|
|
6606
6621
|
makeSymbol({
|
|
@@ -6619,12 +6634,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineF
|
|
|
6619
6634
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets2, parentLine, lineFromOffset) {
|
|
6620
6635
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
6621
6636
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
6622
|
-
const blockContent =
|
|
6637
|
+
const blockContent = expectDefined4(match[0]);
|
|
6623
6638
|
const blockOffset = match.index ?? 0;
|
|
6624
6639
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
6625
6640
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
6626
|
-
const key =
|
|
6627
|
-
const keyOffset = blockOffset +
|
|
6641
|
+
const key = expectDefined4(optMatch[1]);
|
|
6642
|
+
const keyOffset = blockOffset + expectDefined4(optMatch.index);
|
|
6628
6643
|
const line = lineFromOffset(keyOffset);
|
|
6629
6644
|
if (line <= parentLine) continue;
|
|
6630
6645
|
symbols.push(
|
|
@@ -6669,7 +6684,7 @@ __export(yaml_parser_exports, {
|
|
|
6669
6684
|
detectLang: () => detectLang,
|
|
6670
6685
|
parseSymbols: () => parseSymbols7
|
|
6671
6686
|
});
|
|
6672
|
-
import { expectDefined as
|
|
6687
|
+
import { expectDefined as expectDefined5, truncate } from "@wrongstack/core/utils";
|
|
6673
6688
|
function parseSymbols7(opts) {
|
|
6674
6689
|
const { file, content, lang } = opts;
|
|
6675
6690
|
try {
|
|
@@ -6691,14 +6706,14 @@ function regexParse3(opts) {
|
|
|
6691
6706
|
let hi = lineOffsets2.length - 1;
|
|
6692
6707
|
while (lo < hi) {
|
|
6693
6708
|
const mid = lo + hi + 1 >>> 1;
|
|
6694
|
-
if (
|
|
6709
|
+
if (expectDefined5(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6695
6710
|
else hi = mid - 1;
|
|
6696
6711
|
}
|
|
6697
6712
|
return lo + 1;
|
|
6698
6713
|
}
|
|
6699
6714
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
6700
6715
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
6701
|
-
const name =
|
|
6716
|
+
const name = expectDefined5(match[1]);
|
|
6702
6717
|
const offset = match.index ?? 0;
|
|
6703
6718
|
const line = lineFromOffset(offset);
|
|
6704
6719
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6716,7 +6731,7 @@ function regexParse3(opts) {
|
|
|
6716
6731
|
}
|
|
6717
6732
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
6718
6733
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
6719
|
-
const name =
|
|
6734
|
+
const name = expectDefined5(match[1]);
|
|
6720
6735
|
const offset = match.index ?? 0;
|
|
6721
6736
|
const line = lineFromOffset(offset);
|
|
6722
6737
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6751,7 +6766,7 @@ function regexParse3(opts) {
|
|
|
6751
6766
|
}
|
|
6752
6767
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6753
6768
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
6754
|
-
const key =
|
|
6769
|
+
const key = expectDefined5(match[2]);
|
|
6755
6770
|
const offset = match.index ?? 0;
|
|
6756
6771
|
const line = lineFromOffset(offset);
|
|
6757
6772
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6771,7 +6786,7 @@ function regexParse3(opts) {
|
|
|
6771
6786
|
}
|
|
6772
6787
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
6773
6788
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
6774
|
-
const key =
|
|
6789
|
+
const key = expectDefined5(match[2]);
|
|
6775
6790
|
const offset = match.index ?? 0;
|
|
6776
6791
|
const line = lineFromOffset(offset);
|
|
6777
6792
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -10095,10 +10110,12 @@ async function shutdownBrowserTools() {
|
|
|
10095
10110
|
await Promise.all(active.map((manager) => manager.dispose()));
|
|
10096
10111
|
}
|
|
10097
10112
|
|
|
10098
|
-
// src/codebase-index/
|
|
10099
|
-
import
|
|
10100
|
-
import
|
|
10101
|
-
import
|
|
10113
|
+
// src/codebase-index/project-server-client.ts
|
|
10114
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
10115
|
+
import * as fs12 from "node:fs";
|
|
10116
|
+
import * as net3 from "node:net";
|
|
10117
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10118
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
10102
10119
|
|
|
10103
10120
|
// src/codebase-index/circuit-breaker.ts
|
|
10104
10121
|
var CircuitOpenError = class extends Error {
|
|
@@ -10183,117 +10200,18 @@ function resetIndexCircuitBreaker() {
|
|
|
10183
10200
|
indexCircuitBreaker.reset();
|
|
10184
10201
|
}
|
|
10185
10202
|
|
|
10186
|
-
// src/codebase-index/
|
|
10187
|
-
import {
|
|
10188
|
-
import
|
|
10189
|
-
import * as
|
|
10190
|
-
import
|
|
10191
|
-
import
|
|
10192
|
-
import {
|
|
10193
|
-
DEFAULT_WALK_IGNORE_DIRS,
|
|
10194
|
-
indexParallelBatchSize,
|
|
10195
|
-
isFrugalPerf
|
|
10196
|
-
} from "@wrongstack/core/utils";
|
|
10197
|
-
|
|
10198
|
-
// src/codebase-index/gitignore.ts
|
|
10199
|
-
import * as fs9 from "node:fs/promises";
|
|
10200
|
-
import * as path13 from "node:path";
|
|
10201
|
-
import { compileGlob } from "@wrongstack/core/utils";
|
|
10202
|
-
function globBody(glob) {
|
|
10203
|
-
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
10204
|
-
}
|
|
10205
|
-
function compileGitignore(lines) {
|
|
10206
|
-
const rules = [];
|
|
10207
|
-
for (const raw of lines) {
|
|
10208
|
-
let line = raw.replace(/\r$/, "");
|
|
10209
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
10210
|
-
line = line.trim();
|
|
10211
|
-
let negated = false;
|
|
10212
|
-
if (line.startsWith("!")) {
|
|
10213
|
-
negated = true;
|
|
10214
|
-
line = line.slice(1);
|
|
10215
|
-
}
|
|
10216
|
-
let dirOnly = false;
|
|
10217
|
-
if (line.endsWith("/")) {
|
|
10218
|
-
dirOnly = true;
|
|
10219
|
-
line = line.slice(0, -1);
|
|
10220
|
-
}
|
|
10221
|
-
if (!line) continue;
|
|
10222
|
-
const anchored = line.startsWith("/") || line.includes("/");
|
|
10223
|
-
if (line.startsWith("/")) line = line.slice(1);
|
|
10224
|
-
const body = globBody(line);
|
|
10225
|
-
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
10226
|
-
rules.push({
|
|
10227
|
-
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
10228
|
-
under: new RegExp(`${prefix}${body}/.*$`),
|
|
10229
|
-
negated,
|
|
10230
|
-
dirOnly
|
|
10231
|
-
});
|
|
10232
|
-
}
|
|
10233
|
-
return (relPath, isDir) => {
|
|
10234
|
-
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
10235
|
-
let ignored = false;
|
|
10236
|
-
for (const r of rules) {
|
|
10237
|
-
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
10238
|
-
if (re.test(p)) ignored = !r.negated;
|
|
10239
|
-
}
|
|
10240
|
-
return ignored;
|
|
10241
|
-
};
|
|
10242
|
-
}
|
|
10243
|
-
async function loadGitignoreMatcher(projectRoot) {
|
|
10244
|
-
let lines = [];
|
|
10245
|
-
try {
|
|
10246
|
-
const raw = await fs9.readFile(path13.join(projectRoot, ".gitignore"), "utf8");
|
|
10247
|
-
lines = raw.split("\n");
|
|
10248
|
-
} catch {
|
|
10249
|
-
}
|
|
10250
|
-
return compileGitignore(lines);
|
|
10251
|
-
}
|
|
10252
|
-
|
|
10253
|
-
// src/codebase-index/indexer.ts
|
|
10254
|
-
init_languages2();
|
|
10255
|
-
|
|
10256
|
-
// src/codebase-index/parser-dispatch.ts
|
|
10257
|
-
async function parseFileContent(file, content, lang) {
|
|
10258
|
-
switch (lang) {
|
|
10259
|
-
case "ts":
|
|
10260
|
-
case "tsx":
|
|
10261
|
-
case "js":
|
|
10262
|
-
case "jsx": {
|
|
10263
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
10264
|
-
return parseSymbols8({ file, content, lang });
|
|
10265
|
-
}
|
|
10266
|
-
case "go": {
|
|
10267
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
10268
|
-
return parseSymbols8({ file, content, lang: "go" });
|
|
10269
|
-
}
|
|
10270
|
-
case "py": {
|
|
10271
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
10272
|
-
return parseSymbols8({ file, content, lang: "py" });
|
|
10273
|
-
}
|
|
10274
|
-
case "rs": {
|
|
10275
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
10276
|
-
return parseSymbols8({ file, content, lang: "rs" });
|
|
10277
|
-
}
|
|
10278
|
-
case "json": {
|
|
10279
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
10280
|
-
return parseSymbols8({ file, content, lang: "json" });
|
|
10281
|
-
}
|
|
10282
|
-
case "yaml": {
|
|
10283
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
10284
|
-
return parseSymbols8({ file, content, lang: "yaml" });
|
|
10285
|
-
}
|
|
10286
|
-
default: {
|
|
10287
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
10288
|
-
return parseSymbols8({ file, content, lang });
|
|
10289
|
-
}
|
|
10290
|
-
}
|
|
10291
|
-
}
|
|
10203
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
10204
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
10205
|
+
import * as fs11 from "node:fs";
|
|
10206
|
+
import * as os5 from "node:os";
|
|
10207
|
+
import * as path16 from "node:path";
|
|
10208
|
+
import { fileURLToPath } from "node:url";
|
|
10209
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
10292
10210
|
|
|
10293
10211
|
// src/codebase-index/writer.ts
|
|
10294
|
-
import { expectDefined as
|
|
10295
|
-
import * as
|
|
10296
|
-
import * as
|
|
10212
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
10213
|
+
import * as fs10 from "node:fs";
|
|
10214
|
+
import * as path15 from "node:path";
|
|
10297
10215
|
|
|
10298
10216
|
// src/codebase-index/bm25.ts
|
|
10299
10217
|
var K1 = 1.5;
|
|
@@ -10488,8 +10406,8 @@ function runSqliteWithRetry(fn) {
|
|
|
10488
10406
|
}
|
|
10489
10407
|
|
|
10490
10408
|
// src/codebase-index/writer-admin.ts
|
|
10491
|
-
import * as
|
|
10492
|
-
import * as
|
|
10409
|
+
import * as fs9 from "node:fs";
|
|
10410
|
+
import * as path13 from "node:path";
|
|
10493
10411
|
var DB_FILE = "index.db";
|
|
10494
10412
|
function getAllIndexableWithStatement(stmt) {
|
|
10495
10413
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10548,7 +10466,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10548
10466
|
}
|
|
10549
10467
|
function getIndexDbSizeBytes(indexDir) {
|
|
10550
10468
|
try {
|
|
10551
|
-
return
|
|
10469
|
+
return fs9.statSync(path13.join(indexDir, DB_FILE)).size;
|
|
10552
10470
|
} catch {
|
|
10553
10471
|
return 0;
|
|
10554
10472
|
}
|
|
@@ -10615,7 +10533,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
10615
10533
|
}
|
|
10616
10534
|
|
|
10617
10535
|
// src/codebase-index/writer-graph-helpers.ts
|
|
10618
|
-
import * as
|
|
10536
|
+
import * as path14 from "node:path";
|
|
10619
10537
|
function derivePackage(filePath) {
|
|
10620
10538
|
const f = filePath.replace(/\\/g, "/");
|
|
10621
10539
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -10730,16 +10648,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10730
10648
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10731
10649
|
if (!moduleName.startsWith(".")) return void 0;
|
|
10732
10650
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10733
|
-
const absolute =
|
|
10734
|
-
|
|
10651
|
+
const absolute = path14.posix.normalize(
|
|
10652
|
+
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10735
10653
|
);
|
|
10736
|
-
const extension =
|
|
10654
|
+
const extension = path14.posix.extname(absolute);
|
|
10737
10655
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10738
10656
|
const candidates = [
|
|
10739
10657
|
absolute,
|
|
10740
10658
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10741
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10742
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10659
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10660
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10743
10661
|
];
|
|
10744
10662
|
const indexedByPortablePath = new Map(
|
|
10745
10663
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -11255,9 +11173,9 @@ var IndexStore = class _IndexStore {
|
|
|
11255
11173
|
}
|
|
11256
11174
|
constructor(projectRoot, opts = {}) {
|
|
11257
11175
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
11258
|
-
|
|
11176
|
+
fs10.mkdirSync(this.indexDir, { recursive: true });
|
|
11259
11177
|
const Database = loadDatabaseSync();
|
|
11260
|
-
this.db = new Database(
|
|
11178
|
+
this.db = new Database(path15.join(this.indexDir, DB_FILE2));
|
|
11261
11179
|
applyIndexStorePragmas(this.db);
|
|
11262
11180
|
this.initSchema();
|
|
11263
11181
|
}
|
|
@@ -11672,13 +11590,13 @@ var IndexStore = class _IndexStore {
|
|
|
11672
11590
|
if (rankDiff !== 0) return rankDiff;
|
|
11673
11591
|
const scoreDiff = b.score - a.score;
|
|
11674
11592
|
if (scoreDiff !== 0) return scoreDiff;
|
|
11675
|
-
const left =
|
|
11676
|
-
const right =
|
|
11593
|
+
const left = expectDefined2(candidateById.get(a.id));
|
|
11594
|
+
const right = expectDefined2(candidateById.get(b.id));
|
|
11677
11595
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
11678
11596
|
});
|
|
11679
11597
|
const qTokens = tokenise(query);
|
|
11680
11598
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
11681
|
-
const c =
|
|
11599
|
+
const c = expectDefined2(candidateById.get(id));
|
|
11682
11600
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
11683
11601
|
});
|
|
11684
11602
|
return { results, total: candidates.length };
|
|
@@ -12092,710 +12010,212 @@ var indexStorePool = new StorePool(
|
|
|
12092
12010
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
12093
12011
|
);
|
|
12094
12012
|
|
|
12095
|
-
// src/codebase-index/
|
|
12096
|
-
var
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
function
|
|
12101
|
-
|
|
12013
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
12014
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
12015
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
12016
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
12017
|
+
var buildIdCache;
|
|
12018
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
12019
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path16.resolve(entrypoint);
|
|
12020
|
+
try {
|
|
12021
|
+
const stat18 = fs11.statSync(file);
|
|
12022
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
12023
|
+
return buildIdCache.buildId;
|
|
12024
|
+
}
|
|
12025
|
+
const buildId = createHash4("sha256").update(fs11.readFileSync(file)).digest("hex").slice(0, 24);
|
|
12026
|
+
buildIdCache = { file, mtimeMs: stat18.mtimeMs, size: stat18.size, buildId };
|
|
12027
|
+
return buildId;
|
|
12028
|
+
} catch {
|
|
12029
|
+
return `unreadable:${path16.basename(file)}`;
|
|
12030
|
+
}
|
|
12102
12031
|
}
|
|
12103
|
-
function
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12032
|
+
function normalizeLocalPath(value) {
|
|
12033
|
+
const resolved = path16.resolve(value);
|
|
12034
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12107
12035
|
}
|
|
12108
|
-
function
|
|
12109
|
-
|
|
12036
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
12037
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
12038
|
+
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
12110
12039
|
}
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
const rel = path22.relative(projectRoot, file);
|
|
12118
|
-
return rel !== "" && !rel.startsWith(`..${path22.sep}`) && rel !== ".." && !path22.isAbsolute(rel);
|
|
12040
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
12041
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
12042
|
+
if (process.platform === "win32") {
|
|
12043
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
12044
|
+
}
|
|
12045
|
+
return path16.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
12119
12046
|
}
|
|
12120
|
-
function
|
|
12121
|
-
|
|
12122
|
-
|
|
12047
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
12048
|
+
return path16.join(
|
|
12049
|
+
path16.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
12050
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
12051
|
+
);
|
|
12123
12052
|
}
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12053
|
+
|
|
12054
|
+
// src/codebase-index/project-server-protocol.ts
|
|
12055
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
12056
|
+
function encodeProjectServerMessage(message) {
|
|
12057
|
+
return `${JSON.stringify(message)}
|
|
12058
|
+
`;
|
|
12127
12059
|
}
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
])
|
|
12161
|
-
]);
|
|
12162
|
-
throwIfAborted(signal);
|
|
12163
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
12164
|
-
const deleted = /* @__PURE__ */ new Set();
|
|
12165
|
-
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12166
|
-
for (let i = 0; i < statusRecords.length; i++) {
|
|
12167
|
-
const record = statusRecords[i];
|
|
12168
|
-
if (!record) continue;
|
|
12169
|
-
const status = record.slice(0, 2);
|
|
12170
|
-
const changedPath = path22.resolve(projectRoot, record.slice(3));
|
|
12171
|
-
dirty.add(changedPath);
|
|
12172
|
-
if (status.includes("D")) deleted.add(changedPath);
|
|
12173
|
-
if (status.includes("R") || status.includes("C")) {
|
|
12174
|
-
const source = statusRecords[++i];
|
|
12175
|
-
if (source) dirty.add(path22.resolve(projectRoot, source));
|
|
12060
|
+
|
|
12061
|
+
// src/codebase-index/project-server-client.ts
|
|
12062
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
12063
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
12064
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
12065
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
12066
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
12067
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
12068
|
+
constructor(message, pid) {
|
|
12069
|
+
super(message);
|
|
12070
|
+
this.pid = pid;
|
|
12071
|
+
}
|
|
12072
|
+
pid;
|
|
12073
|
+
name = "StaleProjectIndexServerError";
|
|
12074
|
+
};
|
|
12075
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
12076
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
12077
|
+
var latestConnectionState = {
|
|
12078
|
+
status: "offline",
|
|
12079
|
+
connected: false
|
|
12080
|
+
};
|
|
12081
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
12082
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
12083
|
+
return { kind: "inline-requested" };
|
|
12084
|
+
}
|
|
12085
|
+
let builtUrl = null;
|
|
12086
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
12087
|
+
try {
|
|
12088
|
+
const url = new URL(rel, import.meta.url);
|
|
12089
|
+
if (url.protocol === "file:" && fs12.existsSync(fileURLToPath2(url))) {
|
|
12090
|
+
builtUrl = url;
|
|
12091
|
+
break;
|
|
12176
12092
|
}
|
|
12093
|
+
} catch {
|
|
12177
12094
|
}
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12095
|
+
}
|
|
12096
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
12097
|
+
if (projectRoot !== void 0) {
|
|
12098
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12099
|
+
const check = checkUnixSocketPath(endpoint);
|
|
12100
|
+
if (!check.ok) {
|
|
12101
|
+
return {
|
|
12102
|
+
kind: "endpoint-invalid",
|
|
12103
|
+
endpoint,
|
|
12104
|
+
byteLength: check.byteLength,
|
|
12105
|
+
maxBytes: check.maxBytes
|
|
12106
|
+
};
|
|
12189
12107
|
}
|
|
12190
|
-
return {
|
|
12191
|
-
files,
|
|
12192
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12193
|
-
};
|
|
12194
|
-
} catch {
|
|
12195
|
-
return null;
|
|
12196
12108
|
}
|
|
12109
|
+
return { kind: "available", url: builtUrl };
|
|
12197
12110
|
}
|
|
12198
|
-
|
|
12199
|
-
const
|
|
12200
|
-
|
|
12111
|
+
function resolveProjectServerUrl() {
|
|
12112
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
12113
|
+
return availability.kind === "available" ? availability.url : null;
|
|
12114
|
+
}
|
|
12115
|
+
function projectIndexServerExpectedBuildId() {
|
|
12116
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
12117
|
+
if (override) return override;
|
|
12118
|
+
const url = resolveProjectServerUrl();
|
|
12119
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
12120
|
+
}
|
|
12121
|
+
function isProjectIndexServerAvailable() {
|
|
12122
|
+
return resolveProjectServerUrl() !== null;
|
|
12123
|
+
}
|
|
12124
|
+
function publishConnectionState(endpoint, state) {
|
|
12125
|
+
connectionStates.set(endpoint, state);
|
|
12126
|
+
latestConnectionState = state;
|
|
12127
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
12128
|
+
}
|
|
12129
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
12130
|
+
if (projectRoot) {
|
|
12131
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12132
|
+
const existing = connectionStates.get(endpoint);
|
|
12133
|
+
if (existing) return existing;
|
|
12134
|
+
if (!isProjectIndexServerAvailable()) {
|
|
12135
|
+
return { status: "unavailable", connected: false };
|
|
12136
|
+
}
|
|
12201
12137
|
return {
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12138
|
+
status: "offline",
|
|
12139
|
+
connected: false,
|
|
12140
|
+
projectRoot,
|
|
12141
|
+
indexDir,
|
|
12142
|
+
endpoint
|
|
12206
12143
|
};
|
|
12207
12144
|
}
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12212
|
-
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12213
|
-
let dirCount = 0;
|
|
12214
|
-
const walk2 = async (dir) => {
|
|
12215
|
-
throwIfAborted(signal);
|
|
12216
|
-
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12217
|
-
await yieldEventLoop();
|
|
12218
|
-
throwIfAborted(signal);
|
|
12219
|
-
}
|
|
12220
|
-
let entries;
|
|
12221
|
-
try {
|
|
12222
|
-
entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
12223
|
-
} catch (err) {
|
|
12224
|
-
complete = false;
|
|
12225
|
-
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12226
|
-
return;
|
|
12227
|
-
}
|
|
12228
|
-
dirCount++;
|
|
12229
|
-
for (const e of entries) {
|
|
12230
|
-
if (ignoreSet.has(e.name)) continue;
|
|
12231
|
-
const full = path22.join(dir, e.name);
|
|
12232
|
-
const rel = path22.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12233
|
-
if (e.isDirectory()) {
|
|
12234
|
-
if (isGitIgnored(rel, true)) continue;
|
|
12235
|
-
await walk2(full);
|
|
12236
|
-
} else if (e.isFile()) {
|
|
12237
|
-
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12238
|
-
const ext = path22.extname(e.name).toLowerCase();
|
|
12239
|
-
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12240
|
-
results.push(full);
|
|
12241
|
-
}
|
|
12242
|
-
}
|
|
12243
|
-
}
|
|
12244
|
-
};
|
|
12245
|
-
await walk2(projectRoot);
|
|
12246
|
-
return { files: results, complete, errors };
|
|
12145
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
12146
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
12147
|
+
return latestConnectionState;
|
|
12247
12148
|
}
|
|
12248
|
-
function
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
const seen = /* @__PURE__ */ new Set();
|
|
12252
|
-
const assigned = [];
|
|
12253
|
-
for (const ref of refs) {
|
|
12254
|
-
let owner2;
|
|
12255
|
-
for (const symbol of ordered) {
|
|
12256
|
-
if (symbol.line > ref.line) break;
|
|
12257
|
-
owner2 = symbol;
|
|
12258
|
-
}
|
|
12259
|
-
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12260
|
-
if (!owner2 || owner2.id <= 0) continue;
|
|
12261
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12262
|
-
if (seen.has(key)) continue;
|
|
12263
|
-
seen.add(key);
|
|
12264
|
-
assigned.push({ ...ref, fromId: owner2.id });
|
|
12265
|
-
}
|
|
12266
|
-
return assigned;
|
|
12149
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
12150
|
+
connectionStateListeners.add(listener);
|
|
12151
|
+
return () => connectionStateListeners.delete(listener);
|
|
12267
12152
|
}
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
const
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
const
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12153
|
+
function remoteError(message, name) {
|
|
12154
|
+
if (name === "LockError") return new LockError(message);
|
|
12155
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
12156
|
+
const error = new Error(message);
|
|
12157
|
+
if (name && name !== "Error") error.name = name;
|
|
12158
|
+
return error;
|
|
12159
|
+
}
|
|
12160
|
+
function isProjectIndexServerHealth(value) {
|
|
12161
|
+
if (!value || typeof value !== "object") return false;
|
|
12162
|
+
const health = value;
|
|
12163
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
12164
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
12165
|
+
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";
|
|
12166
|
+
}
|
|
12167
|
+
function delay(ms) {
|
|
12168
|
+
return new Promise((resolve17) => {
|
|
12169
|
+
const timer = setTimeout(resolve17, ms);
|
|
12170
|
+
timer.unref?.();
|
|
12171
|
+
});
|
|
12172
|
+
}
|
|
12173
|
+
function cancellationError(signal) {
|
|
12174
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
12175
|
+
}
|
|
12176
|
+
var ProjectServerConnection = class {
|
|
12177
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
12178
|
+
this.projectRoot = projectRoot;
|
|
12179
|
+
this.indexDir = indexDir;
|
|
12180
|
+
this.endpoint = endpoint;
|
|
12181
|
+
this.transition("offline");
|
|
12297
12182
|
}
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12183
|
+
projectRoot;
|
|
12184
|
+
indexDir;
|
|
12185
|
+
endpoint;
|
|
12186
|
+
socket = null;
|
|
12187
|
+
buffer = "";
|
|
12188
|
+
info = null;
|
|
12189
|
+
activity = null;
|
|
12190
|
+
health = null;
|
|
12191
|
+
healthCheck = null;
|
|
12192
|
+
connecting = null;
|
|
12193
|
+
connectResolve = null;
|
|
12194
|
+
connectReject = null;
|
|
12195
|
+
nextId = 1;
|
|
12196
|
+
pending = /* @__PURE__ */ new Map();
|
|
12197
|
+
transition(status, options = {}) {
|
|
12198
|
+
const previous = connectionStates.get(this.endpoint);
|
|
12199
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
12200
|
+
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);
|
|
12201
|
+
publishConnectionState(this.endpoint, {
|
|
12202
|
+
status,
|
|
12203
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
12204
|
+
projectRoot: this.projectRoot,
|
|
12205
|
+
indexDir: this.indexDir,
|
|
12206
|
+
endpoint: this.endpoint,
|
|
12207
|
+
pid,
|
|
12208
|
+
lastError,
|
|
12209
|
+
...this.activity ? { activity: this.activity } : {},
|
|
12210
|
+
...this.health ? { health: this.health } : {}
|
|
12303
12211
|
});
|
|
12304
12212
|
}
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
if (!force) {
|
|
12308
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
12213
|
+
isConnected() {
|
|
12214
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
12309
12215
|
}
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
files = files.filter((file) => {
|
|
12314
|
-
const meta = existingMeta.get(file);
|
|
12315
|
-
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
12316
|
-
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
12317
|
-
symbolsIndexed += meta.symbolCount;
|
|
12318
|
-
filesIndexed++;
|
|
12319
|
-
filesPreSkipped++;
|
|
12320
|
-
return false;
|
|
12321
|
-
});
|
|
12322
|
-
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
12323
|
-
}
|
|
12324
|
-
const parallelBatch = resolveParallelBatch();
|
|
12325
|
-
let filesSinceLastYield = 0;
|
|
12326
|
-
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
12327
|
-
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
12328
|
-
const batchFiles = files.slice(batchStart, batchEnd);
|
|
12329
|
-
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
12330
|
-
filesSinceLastYield += batchFiles.length;
|
|
12331
|
-
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
12332
|
-
filesSinceLastYield = 0;
|
|
12333
|
-
await yieldEventLoop();
|
|
12334
|
-
if (isFrugalPerf()) {
|
|
12335
|
-
await new Promise((r) => setTimeout(r, 8));
|
|
12336
|
-
}
|
|
12337
|
-
throwIfAborted(signal);
|
|
12338
|
-
}
|
|
12339
|
-
const statOpts = signal ? { signal } : {};
|
|
12340
|
-
const statReadParse = await Promise.allSettled(
|
|
12341
|
-
batchFiles.map(
|
|
12342
|
-
async (file) => {
|
|
12343
|
-
let stat18;
|
|
12344
|
-
try {
|
|
12345
|
-
stat18 = await fs15.stat(file, statOpts);
|
|
12346
|
-
} catch (e) {
|
|
12347
|
-
if (isAbortError(e)) throw e;
|
|
12348
|
-
return {
|
|
12349
|
-
file,
|
|
12350
|
-
stat: null,
|
|
12351
|
-
lang: "",
|
|
12352
|
-
parsed: null,
|
|
12353
|
-
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
12354
|
-
missing: isMissingPathError(e)
|
|
12355
|
-
};
|
|
12356
|
-
}
|
|
12357
|
-
if (!stat18.isFile()) return { file, stat: stat18, lang: "", parsed: null };
|
|
12358
|
-
const lang = detectLang(file);
|
|
12359
|
-
if (!lang) return { file, stat: stat18, lang: "", parsed: null };
|
|
12360
|
-
if (stat18.size > MAX_INDEX_FILE_BYTES) {
|
|
12361
|
-
return {
|
|
12362
|
-
file,
|
|
12363
|
-
stat: stat18,
|
|
12364
|
-
lang,
|
|
12365
|
-
parsed: null,
|
|
12366
|
-
error: `file too large (${stat18.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
12367
|
-
};
|
|
12368
|
-
}
|
|
12369
|
-
const meta = existingMeta.get(file);
|
|
12370
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat18.mtimeMs)) {
|
|
12371
|
-
return { file, stat: stat18, lang, parsed: null, skippedMeta: meta };
|
|
12372
|
-
}
|
|
12373
|
-
let content;
|
|
12374
|
-
try {
|
|
12375
|
-
content = await fs15.readFile(file, { encoding: "utf8", signal });
|
|
12376
|
-
} catch (e) {
|
|
12377
|
-
if (isAbortError(e)) throw e;
|
|
12378
|
-
return {
|
|
12379
|
-
file,
|
|
12380
|
-
stat: stat18,
|
|
12381
|
-
lang,
|
|
12382
|
-
parsed: null,
|
|
12383
|
-
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
12384
|
-
};
|
|
12385
|
-
}
|
|
12386
|
-
let parsed;
|
|
12387
|
-
try {
|
|
12388
|
-
parsed = await parseFileContent(file, content, lang);
|
|
12389
|
-
} catch (e) {
|
|
12390
|
-
return {
|
|
12391
|
-
file,
|
|
12392
|
-
stat: stat18,
|
|
12393
|
-
lang,
|
|
12394
|
-
parsed: null,
|
|
12395
|
-
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
12396
|
-
};
|
|
12397
|
-
}
|
|
12398
|
-
return { file, stat: stat18, lang, parsed, content };
|
|
12399
|
-
}
|
|
12400
|
-
)
|
|
12401
|
-
);
|
|
12402
|
-
const batchEntries = [];
|
|
12403
|
-
const deleteForFiles = [];
|
|
12404
|
-
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
12405
|
-
const settled = statReadParse[fi];
|
|
12406
|
-
const file = expectDefined6(batchFiles[fi]);
|
|
12407
|
-
if (settled.status === "rejected") {
|
|
12408
|
-
const err = settled.reason;
|
|
12409
|
-
if (err instanceof Error && isAbortError(err)) throw err;
|
|
12410
|
-
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12411
|
-
continue;
|
|
12412
|
-
}
|
|
12413
|
-
const result = settled.value;
|
|
12414
|
-
if (result.error) {
|
|
12415
|
-
if (result.missing) store.deleteFile(file);
|
|
12416
|
-
errors.push(`${file}: ${result.error}`);
|
|
12417
|
-
continue;
|
|
12418
|
-
}
|
|
12419
|
-
const { stat: stat18, lang, parsed } = result;
|
|
12420
|
-
if (result.skippedMeta) {
|
|
12421
|
-
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
12422
|
-
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
12423
|
-
filesIndexed++;
|
|
12424
|
-
continue;
|
|
12425
|
-
}
|
|
12426
|
-
if (!lang || !parsed) {
|
|
12427
|
-
if (lang) {
|
|
12428
|
-
store.upsertFile({
|
|
12429
|
-
file,
|
|
12430
|
-
lang,
|
|
12431
|
-
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
12432
|
-
symbolCount: 0,
|
|
12433
|
-
lastIndexed: Date.now()
|
|
12434
|
-
});
|
|
12435
|
-
filesIndexed++;
|
|
12436
|
-
}
|
|
12437
|
-
continue;
|
|
12438
|
-
}
|
|
12439
|
-
if (parsed.symbols.length === 0) {
|
|
12440
|
-
store.replaceEmptyFile({
|
|
12441
|
-
file,
|
|
12442
|
-
lang,
|
|
12443
|
-
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
12444
|
-
symbolCount: 0,
|
|
12445
|
-
lastIndexed: Date.now()
|
|
12446
|
-
});
|
|
12447
|
-
filesIndexed++;
|
|
12448
|
-
continue;
|
|
12449
|
-
}
|
|
12450
|
-
batchEntries.push({
|
|
12451
|
-
file,
|
|
12452
|
-
lang,
|
|
12453
|
-
symbols: parsed.symbols,
|
|
12454
|
-
refs: parsed.refs ?? [],
|
|
12455
|
-
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
12456
|
-
symbolCount: parsed.symbols.length
|
|
12457
|
-
});
|
|
12458
|
-
deleteForFiles.push(file);
|
|
12459
|
-
}
|
|
12460
|
-
if (batchEntries.length > 0) {
|
|
12461
|
-
try {
|
|
12462
|
-
store.commitBatch(batchEntries, { deleteForFiles });
|
|
12463
|
-
for (const entry of batchEntries) {
|
|
12464
|
-
const count = entry.symbols.length;
|
|
12465
|
-
symbolsIndexed += count;
|
|
12466
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
12467
|
-
filesIndexed++;
|
|
12468
|
-
}
|
|
12469
|
-
} catch (err) {
|
|
12470
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12471
|
-
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
12472
|
-
for (const entry of batchEntries) {
|
|
12473
|
-
try {
|
|
12474
|
-
store.deleteRefsForFile(entry.file);
|
|
12475
|
-
store.deleteSymbolsForFile(entry.file);
|
|
12476
|
-
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
12477
|
-
symbolsIndexed += symbolsWithIds.length;
|
|
12478
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
12479
|
-
filesIndexed++;
|
|
12480
|
-
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
12481
|
-
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
12482
|
-
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
12483
|
-
}
|
|
12484
|
-
store.resolveRefsForNames([
|
|
12485
|
-
...entry.symbols.map((symbol) => symbol.name),
|
|
12486
|
-
...entry.refs.map((ref) => ref.toName)
|
|
12487
|
-
]);
|
|
12488
|
-
store.upsertFile({
|
|
12489
|
-
file: entry.file,
|
|
12490
|
-
lang: entry.lang,
|
|
12491
|
-
mtimeMs: entry.mtimeMs,
|
|
12492
|
-
symbolCount: entry.symbolCount,
|
|
12493
|
-
lastIndexed: Date.now()
|
|
12494
|
-
});
|
|
12495
|
-
} catch (innerErr) {
|
|
12496
|
-
errors.push(
|
|
12497
|
-
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
12498
|
-
);
|
|
12499
|
-
}
|
|
12500
|
-
}
|
|
12501
|
-
}
|
|
12502
|
-
}
|
|
12503
|
-
}
|
|
12504
|
-
if (discoveredFiles && discoveryComplete) {
|
|
12505
|
-
for (const [file_] of existingMeta) {
|
|
12506
|
-
if (!discoveredFiles.has(file_)) {
|
|
12507
|
-
store.deleteFile(file_);
|
|
12508
|
-
}
|
|
12509
|
-
}
|
|
12510
|
-
}
|
|
12511
|
-
if (needsFullRefResolution) store.resolveRefs();
|
|
12512
|
-
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
12513
|
-
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
12514
|
-
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
12515
|
-
store.setLastIndexed(Date.now());
|
|
12516
|
-
if (!opts.files) store.compactIfNeeded();
|
|
12517
|
-
const durationMs = Date.now() - startMs;
|
|
12518
|
-
return {
|
|
12519
|
-
filesIndexed,
|
|
12520
|
-
symbolsIndexed,
|
|
12521
|
-
langStats,
|
|
12522
|
-
durationMs,
|
|
12523
|
-
errors
|
|
12524
|
-
};
|
|
12525
|
-
}
|
|
12526
|
-
|
|
12527
|
-
// src/codebase-index/index-service.ts
|
|
12528
|
-
async function indexService(args, hooks = {}) {
|
|
12529
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12530
|
-
try {
|
|
12531
|
-
return await runIndexerWithStore(store, {
|
|
12532
|
-
projectRoot: args.projectRoot,
|
|
12533
|
-
indexDir: args.indexDir,
|
|
12534
|
-
files: args.files,
|
|
12535
|
-
force: args.force,
|
|
12536
|
-
langs: args.langs,
|
|
12537
|
-
ignore: args.ignore,
|
|
12538
|
-
signal: hooks.signal,
|
|
12539
|
-
onProgress: hooks.onProgress
|
|
12540
|
-
});
|
|
12541
|
-
} finally {
|
|
12542
|
-
indexStorePool.release(store);
|
|
12543
|
-
}
|
|
12544
|
-
}
|
|
12545
|
-
function searchService(args) {
|
|
12546
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12547
|
-
try {
|
|
12548
|
-
return store.searchRanked(
|
|
12549
|
-
args.query,
|
|
12550
|
-
{
|
|
12551
|
-
kind: args.kind,
|
|
12552
|
-
lang: args.lang,
|
|
12553
|
-
file: args.file,
|
|
12554
|
-
lspKind: args.lspKind
|
|
12555
|
-
},
|
|
12556
|
-
args.limit
|
|
12557
|
-
);
|
|
12558
|
-
} finally {
|
|
12559
|
-
indexStorePool.release(store);
|
|
12560
|
-
}
|
|
12561
|
-
}
|
|
12562
|
-
function statsService(args) {
|
|
12563
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12564
|
-
try {
|
|
12565
|
-
return store.getStats();
|
|
12566
|
-
} finally {
|
|
12567
|
-
indexStorePool.release(store);
|
|
12568
|
-
}
|
|
12569
|
-
}
|
|
12570
|
-
function packageGraphService(args) {
|
|
12571
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12572
|
-
try {
|
|
12573
|
-
return store.getPackageGraph();
|
|
12574
|
-
} finally {
|
|
12575
|
-
indexStorePool.release(store);
|
|
12576
|
-
}
|
|
12577
|
-
}
|
|
12578
|
-
function fileGraphService(args) {
|
|
12579
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12580
|
-
try {
|
|
12581
|
-
return store.getFileGraph(args.packageFilter);
|
|
12582
|
-
} finally {
|
|
12583
|
-
indexStorePool.release(store);
|
|
12584
|
-
}
|
|
12585
|
-
}
|
|
12586
|
-
function symbolGraphService(args) {
|
|
12587
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12588
|
-
try {
|
|
12589
|
-
return store.getSymbolGraph(args.fileFilter);
|
|
12590
|
-
} finally {
|
|
12591
|
-
indexStorePool.release(store);
|
|
12592
|
-
}
|
|
12593
|
-
}
|
|
12594
|
-
|
|
12595
|
-
// src/codebase-index/background-indexer.ts
|
|
12596
|
-
init_languages2();
|
|
12597
|
-
|
|
12598
|
-
// src/codebase-index/project-server-client.ts
|
|
12599
|
-
import { spawn as spawn7 } from "node:child_process";
|
|
12600
|
-
import * as fs17 from "node:fs";
|
|
12601
|
-
import * as net3 from "node:net";
|
|
12602
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12603
|
-
|
|
12604
|
-
// src/codebase-index/project-server-endpoint.ts
|
|
12605
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
12606
|
-
import * as fs16 from "node:fs";
|
|
12607
|
-
import * as os7 from "node:os";
|
|
12608
|
-
import * as path23 from "node:path";
|
|
12609
|
-
import { fileURLToPath } from "node:url";
|
|
12610
|
-
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
12611
|
-
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
12612
|
-
var buildIdCache;
|
|
12613
|
-
function projectIndexServerBuildId(entrypoint) {
|
|
12614
|
-
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path23.resolve(entrypoint);
|
|
12615
|
-
try {
|
|
12616
|
-
const stat18 = fs16.statSync(file);
|
|
12617
|
-
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
12618
|
-
return buildIdCache.buildId;
|
|
12619
|
-
}
|
|
12620
|
-
const buildId = createHash4("sha256").update(fs16.readFileSync(file)).digest("hex").slice(0, 24);
|
|
12621
|
-
buildIdCache = { file, mtimeMs: stat18.mtimeMs, size: stat18.size, buildId };
|
|
12622
|
-
return buildId;
|
|
12623
|
-
} catch {
|
|
12624
|
-
return `unreadable:${path23.basename(file)}`;
|
|
12625
|
-
}
|
|
12626
|
-
}
|
|
12627
|
-
function normalizeLocalPath(value) {
|
|
12628
|
-
const resolved = path23.resolve(value);
|
|
12629
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12630
|
-
}
|
|
12631
|
-
function projectIndexServerKey(projectRoot, indexDir) {
|
|
12632
|
-
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
12633
|
-
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
12634
|
-
}
|
|
12635
|
-
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
12636
|
-
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
12637
|
-
if (process.platform === "win32") {
|
|
12638
|
-
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
12639
|
-
}
|
|
12640
|
-
return path23.join(
|
|
12641
|
-
os7.tmpdir(),
|
|
12642
|
-
`wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`,
|
|
12643
|
-
`${key}.sock`
|
|
12644
|
-
);
|
|
12645
|
-
}
|
|
12646
|
-
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
12647
|
-
return path23.join(
|
|
12648
|
-
path23.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
12649
|
-
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
12650
|
-
);
|
|
12651
|
-
}
|
|
12652
|
-
|
|
12653
|
-
// src/codebase-index/project-server-protocol.ts
|
|
12654
|
-
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
12655
|
-
function encodeProjectServerMessage(message) {
|
|
12656
|
-
return `${JSON.stringify(message)}
|
|
12657
|
-
`;
|
|
12658
|
-
}
|
|
12659
|
-
|
|
12660
|
-
// src/codebase-index/project-server-client.ts
|
|
12661
|
-
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
12662
|
-
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
12663
|
-
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
12664
|
-
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
12665
|
-
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
12666
|
-
var StaleProjectIndexServerError = class extends Error {
|
|
12667
|
-
constructor(message, pid) {
|
|
12668
|
-
super(message);
|
|
12669
|
-
this.pid = pid;
|
|
12670
|
-
}
|
|
12671
|
-
pid;
|
|
12672
|
-
name = "StaleProjectIndexServerError";
|
|
12673
|
-
};
|
|
12674
|
-
var connectionStates = /* @__PURE__ */ new Map();
|
|
12675
|
-
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
12676
|
-
var latestConnectionState = {
|
|
12677
|
-
status: "offline",
|
|
12678
|
-
connected: false
|
|
12679
|
-
};
|
|
12680
|
-
function resolveProjectIndexDaemonAvailability() {
|
|
12681
|
-
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
12682
|
-
return { kind: "inline-requested" };
|
|
12683
|
-
}
|
|
12684
|
-
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
12685
|
-
try {
|
|
12686
|
-
const url = new URL(rel, import.meta.url);
|
|
12687
|
-
if (url.protocol === "file:" && fs17.existsSync(fileURLToPath2(url))) {
|
|
12688
|
-
return { kind: "available", url };
|
|
12689
|
-
}
|
|
12690
|
-
} catch {
|
|
12691
|
-
}
|
|
12692
|
-
}
|
|
12693
|
-
return { kind: "missing-build" };
|
|
12694
|
-
}
|
|
12695
|
-
function resolveProjectServerUrl() {
|
|
12696
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
12697
|
-
return availability.kind === "available" ? availability.url : null;
|
|
12698
|
-
}
|
|
12699
|
-
function projectIndexServerExpectedBuildId() {
|
|
12700
|
-
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
12701
|
-
if (override) return override;
|
|
12702
|
-
const url = resolveProjectServerUrl();
|
|
12703
|
-
return url ? projectIndexServerBuildId(url) : null;
|
|
12704
|
-
}
|
|
12705
|
-
function isProjectIndexServerAvailable() {
|
|
12706
|
-
return resolveProjectServerUrl() !== null;
|
|
12707
|
-
}
|
|
12708
|
-
function publishConnectionState(endpoint, state) {
|
|
12709
|
-
connectionStates.set(endpoint, state);
|
|
12710
|
-
latestConnectionState = state;
|
|
12711
|
-
for (const listener of connectionStateListeners) listener(state);
|
|
12712
|
-
}
|
|
12713
|
-
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
12714
|
-
if (projectRoot) {
|
|
12715
|
-
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12716
|
-
const existing = connectionStates.get(endpoint);
|
|
12717
|
-
if (existing) return existing;
|
|
12718
|
-
if (!isProjectIndexServerAvailable()) {
|
|
12719
|
-
return { status: "unavailable", connected: false };
|
|
12720
|
-
}
|
|
12721
|
-
return {
|
|
12722
|
-
status: "offline",
|
|
12723
|
-
connected: false,
|
|
12724
|
-
projectRoot,
|
|
12725
|
-
indexDir,
|
|
12726
|
-
endpoint
|
|
12727
|
-
};
|
|
12728
|
-
}
|
|
12729
|
-
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
12730
|
-
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
12731
|
-
return latestConnectionState;
|
|
12732
|
-
}
|
|
12733
|
-
function onProjectIndexServerConnectionStateChange(listener) {
|
|
12734
|
-
connectionStateListeners.add(listener);
|
|
12735
|
-
return () => connectionStateListeners.delete(listener);
|
|
12736
|
-
}
|
|
12737
|
-
function remoteError(message, name) {
|
|
12738
|
-
if (name === "LockError") return new LockError(message);
|
|
12739
|
-
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
12740
|
-
const error = new Error(message);
|
|
12741
|
-
if (name && name !== "Error") error.name = name;
|
|
12742
|
-
return error;
|
|
12743
|
-
}
|
|
12744
|
-
function isProjectIndexServerHealth(value) {
|
|
12745
|
-
if (!value || typeof value !== "object") return false;
|
|
12746
|
-
const health = value;
|
|
12747
|
-
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
12748
|
-
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
12749
|
-
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";
|
|
12750
|
-
}
|
|
12751
|
-
function delay(ms) {
|
|
12752
|
-
return new Promise((resolve17) => {
|
|
12753
|
-
const timer = setTimeout(resolve17, ms);
|
|
12754
|
-
timer.unref?.();
|
|
12755
|
-
});
|
|
12756
|
-
}
|
|
12757
|
-
function cancellationError(signal) {
|
|
12758
|
-
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
12759
|
-
}
|
|
12760
|
-
var ProjectServerConnection = class {
|
|
12761
|
-
constructor(projectRoot, indexDir, endpoint) {
|
|
12762
|
-
this.projectRoot = projectRoot;
|
|
12763
|
-
this.indexDir = indexDir;
|
|
12764
|
-
this.endpoint = endpoint;
|
|
12765
|
-
this.transition("offline");
|
|
12766
|
-
}
|
|
12767
|
-
projectRoot;
|
|
12768
|
-
indexDir;
|
|
12769
|
-
endpoint;
|
|
12770
|
-
socket = null;
|
|
12771
|
-
buffer = "";
|
|
12772
|
-
info = null;
|
|
12773
|
-
activity = null;
|
|
12774
|
-
health = null;
|
|
12775
|
-
healthCheck = null;
|
|
12776
|
-
connecting = null;
|
|
12777
|
-
connectResolve = null;
|
|
12778
|
-
connectReject = null;
|
|
12779
|
-
nextId = 1;
|
|
12780
|
-
pending = /* @__PURE__ */ new Map();
|
|
12781
|
-
transition(status, options = {}) {
|
|
12782
|
-
const previous = connectionStates.get(this.endpoint);
|
|
12783
|
-
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
12784
|
-
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);
|
|
12785
|
-
publishConnectionState(this.endpoint, {
|
|
12786
|
-
status,
|
|
12787
|
-
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
12788
|
-
projectRoot: this.projectRoot,
|
|
12789
|
-
indexDir: this.indexDir,
|
|
12790
|
-
endpoint: this.endpoint,
|
|
12791
|
-
pid,
|
|
12792
|
-
lastError,
|
|
12793
|
-
...this.activity ? { activity: this.activity } : {},
|
|
12794
|
-
...this.health ? { health: this.health } : {}
|
|
12795
|
-
});
|
|
12796
|
-
}
|
|
12797
|
-
isConnected() {
|
|
12798
|
-
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
12216
|
+
/** Safe LRU candidate: no request, connect, or health probe is in flight. */
|
|
12217
|
+
isEvictable() {
|
|
12218
|
+
return this.pending.size === 0 && this.connecting === null && this.healthCheck === null;
|
|
12799
12219
|
}
|
|
12800
12220
|
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
12801
12221
|
await this.ensureConnected(spawnIfMissing);
|
|
@@ -12895,371 +12315,1007 @@ var ProjectServerConnection = class {
|
|
|
12895
12315
|
this.transition("connected", { pid: this.info?.pid });
|
|
12896
12316
|
}
|
|
12897
12317
|
}
|
|
12898
|
-
close() {
|
|
12899
|
-
const socket = this.socket;
|
|
12318
|
+
close() {
|
|
12319
|
+
const socket = this.socket;
|
|
12320
|
+
this.socket = null;
|
|
12321
|
+
this.info = null;
|
|
12322
|
+
this.activity = null;
|
|
12323
|
+
this.health = null;
|
|
12324
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
12325
|
+
this.connectResolve = null;
|
|
12326
|
+
this.connectReject = null;
|
|
12327
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
12328
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
12329
|
+
this.transition("offline");
|
|
12330
|
+
maybeStopHeartbeatLoop();
|
|
12331
|
+
}
|
|
12332
|
+
request(message, options) {
|
|
12333
|
+
const socket = this.socket;
|
|
12334
|
+
if (!socket || socket.destroyed) {
|
|
12335
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
12336
|
+
}
|
|
12337
|
+
const id = this.nextId++;
|
|
12338
|
+
return new Promise((resolve17, reject) => {
|
|
12339
|
+
const timer = setTimeout(() => {
|
|
12340
|
+
const entry = this.pending.get(id);
|
|
12341
|
+
if (!entry) return;
|
|
12342
|
+
this.pending.delete(id);
|
|
12343
|
+
this.write({ type: "cancel", id });
|
|
12344
|
+
const error = new IndexTimeoutError(
|
|
12345
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
12346
|
+
);
|
|
12347
|
+
this.cleanupPending(entry);
|
|
12348
|
+
entry.reject(error);
|
|
12349
|
+
}, options.timeoutMs);
|
|
12350
|
+
timer.unref?.();
|
|
12351
|
+
const signal = options.signal;
|
|
12352
|
+
const onAbort = signal ? () => {
|
|
12353
|
+
const entry = this.pending.get(id);
|
|
12354
|
+
if (!entry) return;
|
|
12355
|
+
this.pending.delete(id);
|
|
12356
|
+
this.write({ type: "cancel", id });
|
|
12357
|
+
this.cleanupPending(entry);
|
|
12358
|
+
entry.reject(cancellationError(signal));
|
|
12359
|
+
} : void 0;
|
|
12360
|
+
this.pending.set(id, {
|
|
12361
|
+
resolve: resolve17,
|
|
12362
|
+
reject,
|
|
12363
|
+
timer,
|
|
12364
|
+
signal,
|
|
12365
|
+
onAbort,
|
|
12366
|
+
onProgress: options.onProgress
|
|
12367
|
+
});
|
|
12368
|
+
if (signal && onAbort) {
|
|
12369
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
12370
|
+
if (signal.aborted) {
|
|
12371
|
+
onAbort();
|
|
12372
|
+
return;
|
|
12373
|
+
}
|
|
12374
|
+
}
|
|
12375
|
+
this.write({ ...message, id });
|
|
12376
|
+
});
|
|
12377
|
+
}
|
|
12378
|
+
async ensureConnected(spawnIfMissing) {
|
|
12379
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
12380
|
+
if (this.connecting) return this.connecting;
|
|
12381
|
+
this.transition("connecting");
|
|
12382
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
12383
|
+
this.transition("error", { error });
|
|
12384
|
+
throw error;
|
|
12385
|
+
}).finally(() => {
|
|
12386
|
+
this.connecting = null;
|
|
12387
|
+
});
|
|
12388
|
+
return this.connecting;
|
|
12389
|
+
}
|
|
12390
|
+
async connectWithElection(spawnIfMissing) {
|
|
12391
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12392
|
+
let spawned = false;
|
|
12393
|
+
let staleAttempts = 0;
|
|
12394
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
12395
|
+
while (Date.now() < deadline) {
|
|
12396
|
+
try {
|
|
12397
|
+
await this.connectOnce();
|
|
12398
|
+
return;
|
|
12399
|
+
} catch (error) {
|
|
12400
|
+
lastError = error;
|
|
12401
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
12402
|
+
staleAttempts++;
|
|
12403
|
+
if (!spawnIfMissing) break;
|
|
12404
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12405
|
+
spawned = false;
|
|
12406
|
+
await delay(100);
|
|
12407
|
+
continue;
|
|
12408
|
+
}
|
|
12409
|
+
}
|
|
12410
|
+
if (!spawnIfMissing) break;
|
|
12411
|
+
if (!spawned) {
|
|
12412
|
+
this.spawnDetachedServer();
|
|
12413
|
+
spawned = true;
|
|
12414
|
+
}
|
|
12415
|
+
await delay(75);
|
|
12416
|
+
}
|
|
12417
|
+
throw lastError;
|
|
12418
|
+
}
|
|
12419
|
+
connectOnce() {
|
|
12420
|
+
this.socket?.destroy();
|
|
12421
|
+
this.socket = null;
|
|
12422
|
+
this.info = null;
|
|
12423
|
+
this.activity = null;
|
|
12424
|
+
this.health = null;
|
|
12425
|
+
this.buffer = "";
|
|
12426
|
+
return new Promise((resolve17, reject) => {
|
|
12427
|
+
const socket = net3.createConnection(this.endpoint);
|
|
12428
|
+
this.socket = socket;
|
|
12429
|
+
socket.setEncoding("utf8");
|
|
12430
|
+
const timer = setTimeout(() => {
|
|
12431
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
12432
|
+
socket.destroy();
|
|
12433
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12434
|
+
timer.unref?.();
|
|
12435
|
+
const finishResolve = () => {
|
|
12436
|
+
clearTimeout(timer);
|
|
12437
|
+
this.connectResolve = null;
|
|
12438
|
+
this.connectReject = null;
|
|
12439
|
+
resolve17();
|
|
12440
|
+
};
|
|
12441
|
+
const finishReject = (error) => {
|
|
12442
|
+
clearTimeout(timer);
|
|
12443
|
+
this.connectResolve = null;
|
|
12444
|
+
this.connectReject = null;
|
|
12445
|
+
reject(error);
|
|
12446
|
+
};
|
|
12447
|
+
this.connectResolve = finishResolve;
|
|
12448
|
+
this.connectReject = finishReject;
|
|
12449
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
12450
|
+
socket.on("error", (error) => {
|
|
12451
|
+
if (!this.info) finishReject(error);
|
|
12452
|
+
});
|
|
12453
|
+
socket.on("close", () => this.onClose(socket));
|
|
12454
|
+
});
|
|
12455
|
+
}
|
|
12456
|
+
onData(socket, chunk) {
|
|
12457
|
+
if (socket !== this.socket) return;
|
|
12458
|
+
this.buffer += chunk;
|
|
12459
|
+
while (true) {
|
|
12460
|
+
const newline = this.buffer.indexOf("\n");
|
|
12461
|
+
if (newline < 0) {
|
|
12462
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12463
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12464
|
+
}
|
|
12465
|
+
return;
|
|
12466
|
+
}
|
|
12467
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12468
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12469
|
+
return;
|
|
12470
|
+
}
|
|
12471
|
+
const line = this.buffer.slice(0, newline);
|
|
12472
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
12473
|
+
if (!line) continue;
|
|
12474
|
+
let message;
|
|
12475
|
+
try {
|
|
12476
|
+
message = JSON.parse(line);
|
|
12477
|
+
} catch {
|
|
12478
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12479
|
+
return;
|
|
12480
|
+
}
|
|
12481
|
+
this.onMessage(message);
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
onMessage(message) {
|
|
12485
|
+
if (message.type === "hello") {
|
|
12486
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
12487
|
+
this.rejectStaleServer(
|
|
12488
|
+
message,
|
|
12489
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
12490
|
+
);
|
|
12491
|
+
return;
|
|
12492
|
+
}
|
|
12493
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
12494
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
12495
|
+
this.rejectStaleServer(
|
|
12496
|
+
message,
|
|
12497
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
12498
|
+
);
|
|
12499
|
+
return;
|
|
12500
|
+
}
|
|
12501
|
+
this.info = message;
|
|
12502
|
+
this.markResponsive();
|
|
12503
|
+
this.transition("connected", { pid: message.pid });
|
|
12504
|
+
ensureHeartbeatLoop();
|
|
12505
|
+
this.connectResolve?.();
|
|
12506
|
+
return;
|
|
12507
|
+
}
|
|
12508
|
+
if (message.type === "index-state") {
|
|
12509
|
+
this.activity = message.state;
|
|
12510
|
+
this.markResponsive();
|
|
12511
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12512
|
+
return;
|
|
12513
|
+
}
|
|
12514
|
+
const entry = this.pending.get(message.id);
|
|
12515
|
+
if (!entry) return;
|
|
12516
|
+
this.markResponsive();
|
|
12517
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
12518
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
12519
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12520
|
+
}
|
|
12521
|
+
if (message.type === "progress") {
|
|
12522
|
+
entry.onProgress?.(message.current, message.total);
|
|
12523
|
+
return;
|
|
12524
|
+
}
|
|
12525
|
+
this.pending.delete(message.id);
|
|
12526
|
+
this.cleanupPending(entry);
|
|
12527
|
+
if (message.ok) entry.resolve(message.result);
|
|
12528
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
12529
|
+
}
|
|
12530
|
+
onClose(socket) {
|
|
12531
|
+
if (socket !== this.socket) return;
|
|
12532
|
+
const wasConnected = this.info !== null;
|
|
12900
12533
|
this.socket = null;
|
|
12901
12534
|
this.info = null;
|
|
12902
12535
|
this.activity = null;
|
|
12903
12536
|
this.health = null;
|
|
12904
|
-
|
|
12537
|
+
const error = new Error("codebase-index server connection closed");
|
|
12538
|
+
this.connectReject?.(error);
|
|
12905
12539
|
this.connectResolve = null;
|
|
12906
12540
|
this.connectReject = null;
|
|
12907
|
-
|
|
12908
|
-
this.
|
|
12909
|
-
this.transition("offline");
|
|
12541
|
+
this.rejectPending(error);
|
|
12542
|
+
if (wasConnected) this.transition("error", { error });
|
|
12910
12543
|
maybeStopHeartbeatLoop();
|
|
12911
12544
|
}
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
if (
|
|
12915
|
-
|
|
12545
|
+
cleanupPending(entry) {
|
|
12546
|
+
clearTimeout(entry.timer);
|
|
12547
|
+
if (entry.signal && entry.onAbort) {
|
|
12548
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
12916
12549
|
}
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12550
|
+
}
|
|
12551
|
+
rejectPending(error) {
|
|
12552
|
+
const entries = [...this.pending.values()];
|
|
12553
|
+
this.pending.clear();
|
|
12554
|
+
for (const entry of entries) {
|
|
12555
|
+
this.cleanupPending(entry);
|
|
12556
|
+
entry.reject(error);
|
|
12557
|
+
}
|
|
12558
|
+
}
|
|
12559
|
+
write(message) {
|
|
12560
|
+
const socket = this.socket;
|
|
12561
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
12562
|
+
}
|
|
12563
|
+
rejectStaleServer(message, reason) {
|
|
12564
|
+
const socket = this.socket;
|
|
12565
|
+
if (socket && !socket.destroyed) {
|
|
12566
|
+
socket.write(
|
|
12567
|
+
encodeProjectServerMessage({
|
|
12568
|
+
type: "shutdown",
|
|
12569
|
+
id: 0,
|
|
12570
|
+
reason: "stale-build-replacement"
|
|
12571
|
+
})
|
|
12572
|
+
);
|
|
12573
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
12930
12574
|
timer.unref?.();
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
resolve: resolve17,
|
|
12942
|
-
reject,
|
|
12943
|
-
timer,
|
|
12944
|
-
signal,
|
|
12945
|
-
onAbort,
|
|
12946
|
-
onProgress: options.onProgress
|
|
12947
|
-
});
|
|
12948
|
-
if (signal && onAbort) {
|
|
12949
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
12950
|
-
if (signal.aborted) {
|
|
12951
|
-
onAbort();
|
|
12952
|
-
return;
|
|
12953
|
-
}
|
|
12575
|
+
}
|
|
12576
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12577
|
+
}
|
|
12578
|
+
spawnDetachedServer() {
|
|
12579
|
+
const url = resolveProjectServerUrl();
|
|
12580
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12581
|
+
if (process.platform !== "win32") {
|
|
12582
|
+
try {
|
|
12583
|
+
fs12.rmSync(this.endpoint, { force: true });
|
|
12584
|
+
} catch {
|
|
12954
12585
|
}
|
|
12955
|
-
|
|
12586
|
+
}
|
|
12587
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
12588
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
12589
|
+
const child = spawn4(process.execPath, args, {
|
|
12590
|
+
detached: true,
|
|
12591
|
+
stdio: "ignore",
|
|
12592
|
+
windowsHide: true,
|
|
12593
|
+
env: process.env
|
|
12956
12594
|
});
|
|
12595
|
+
child.unref();
|
|
12957
12596
|
}
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
this.
|
|
12597
|
+
forceKillKnownServer() {
|
|
12598
|
+
const pid = this.info?.pid;
|
|
12599
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
12600
|
+
}
|
|
12601
|
+
forceKillServer(pid) {
|
|
12602
|
+
if (pid === process.pid) return false;
|
|
12603
|
+
try {
|
|
12604
|
+
process.kill(pid);
|
|
12605
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12606
|
+
try {
|
|
12607
|
+
const metadata = JSON.parse(fs12.readFileSync(metadataPath, "utf8"));
|
|
12608
|
+
if (metadata.pid === pid) fs12.rmSync(metadataPath, { force: true });
|
|
12609
|
+
} catch {
|
|
12610
|
+
}
|
|
12611
|
+
return true;
|
|
12612
|
+
} catch {
|
|
12613
|
+
return false;
|
|
12614
|
+
}
|
|
12615
|
+
}
|
|
12616
|
+
};
|
|
12617
|
+
var connections = /* @__PURE__ */ new Map();
|
|
12618
|
+
var MAX_CACHED_CONNECTIONS = 8;
|
|
12619
|
+
var heartbeatTimer;
|
|
12620
|
+
function forgetConnection(endpoint, connection) {
|
|
12621
|
+
if (connections.get(endpoint) === connection) connections.delete(endpoint);
|
|
12622
|
+
connection.close();
|
|
12623
|
+
connectionStates.delete(endpoint);
|
|
12624
|
+
if (latestConnectionState.endpoint !== endpoint) return;
|
|
12625
|
+
latestConnectionState = [...connectionStates.values()].at(-1) ?? {
|
|
12626
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
12627
|
+
connected: false
|
|
12628
|
+
};
|
|
12629
|
+
}
|
|
12630
|
+
function trimConnectionCache(protectedConnection) {
|
|
12631
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) return;
|
|
12632
|
+
for (const [endpoint, connection] of connections) {
|
|
12633
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) break;
|
|
12634
|
+
if (connection === protectedConnection || !connection.isEvictable()) continue;
|
|
12635
|
+
forgetConnection(endpoint, connection);
|
|
12636
|
+
}
|
|
12637
|
+
}
|
|
12638
|
+
function ensureHeartbeatLoop() {
|
|
12639
|
+
if (heartbeatTimer) return;
|
|
12640
|
+
heartbeatTimer = setInterval(() => {
|
|
12641
|
+
for (const connection of connections.values()) {
|
|
12642
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
12643
|
+
});
|
|
12644
|
+
}
|
|
12645
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
12646
|
+
heartbeatTimer.unref?.();
|
|
12647
|
+
}
|
|
12648
|
+
function maybeStopHeartbeatLoop() {
|
|
12649
|
+
if (!heartbeatTimer) return;
|
|
12650
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
12651
|
+
clearInterval(heartbeatTimer);
|
|
12652
|
+
heartbeatTimer = void 0;
|
|
12653
|
+
}
|
|
12654
|
+
function connectionFor(projectRoot, indexDir) {
|
|
12655
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12656
|
+
let connection = connections.get(endpoint);
|
|
12657
|
+
if (!connection) {
|
|
12658
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
12659
|
+
connections.set(endpoint, connection);
|
|
12660
|
+
} else {
|
|
12661
|
+
connections.delete(endpoint);
|
|
12662
|
+
connections.set(endpoint, connection);
|
|
12663
|
+
}
|
|
12664
|
+
trimConnectionCache(connection);
|
|
12665
|
+
return connection;
|
|
12666
|
+
}
|
|
12667
|
+
function callProjectIndexServer(op, args, options) {
|
|
12668
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
12669
|
+
}
|
|
12670
|
+
function ensureProjectIndexServer(options) {
|
|
12671
|
+
return connectionFor(options.projectRoot, options.indexDir).configure(
|
|
12672
|
+
options.watchExternal,
|
|
12673
|
+
options.debounceMs
|
|
12674
|
+
);
|
|
12675
|
+
}
|
|
12676
|
+
function checkProjectIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
12677
|
+
return connectionFor(projectRoot, indexDir).checkHealth(
|
|
12678
|
+
false,
|
|
12679
|
+
options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS
|
|
12680
|
+
);
|
|
12681
|
+
}
|
|
12682
|
+
async function shutdownProjectIndexServer(projectRoot, indexDir, reason) {
|
|
12683
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12684
|
+
const connection = connectionFor(projectRoot, indexDir);
|
|
12685
|
+
try {
|
|
12686
|
+
return await connection.shutdownRemote(reason);
|
|
12687
|
+
} finally {
|
|
12688
|
+
connection.close();
|
|
12689
|
+
connections.delete(endpoint);
|
|
12690
|
+
connectionStates.delete(endpoint);
|
|
12691
|
+
}
|
|
12692
|
+
}
|
|
12693
|
+
function closeProjectIndexServerClients() {
|
|
12694
|
+
for (const connection of connections.values()) connection.close();
|
|
12695
|
+
connections.clear();
|
|
12696
|
+
connectionStates.clear();
|
|
12697
|
+
latestConnectionState = {
|
|
12698
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
12699
|
+
connected: false
|
|
12700
|
+
};
|
|
12701
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
12702
|
+
heartbeatTimer = void 0;
|
|
12703
|
+
}
|
|
12704
|
+
|
|
12705
|
+
// src/codebase-index/background-indexer.ts
|
|
12706
|
+
import * as fs18 from "node:fs";
|
|
12707
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12708
|
+
import { Worker } from "node:worker_threads";
|
|
12709
|
+
|
|
12710
|
+
// src/codebase-index/indexer.ts
|
|
12711
|
+
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12712
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
12713
|
+
import * as fs17 from "node:fs/promises";
|
|
12714
|
+
import { availableParallelism } from "node:os";
|
|
12715
|
+
import * as path23 from "node:path";
|
|
12716
|
+
import {
|
|
12717
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
12718
|
+
indexParallelBatchSize,
|
|
12719
|
+
isFrugalPerf
|
|
12720
|
+
} from "@wrongstack/core/utils";
|
|
12721
|
+
|
|
12722
|
+
// src/codebase-index/gitignore.ts
|
|
12723
|
+
import * as fs13 from "node:fs/promises";
|
|
12724
|
+
import * as path17 from "node:path";
|
|
12725
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
12726
|
+
function globBody(glob) {
|
|
12727
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
12728
|
+
}
|
|
12729
|
+
function compileGitignore(lines) {
|
|
12730
|
+
const rules = [];
|
|
12731
|
+
for (const raw of lines) {
|
|
12732
|
+
let line = raw.replace(/\r$/, "");
|
|
12733
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
12734
|
+
line = line.trim();
|
|
12735
|
+
let negated = false;
|
|
12736
|
+
if (line.startsWith("!")) {
|
|
12737
|
+
negated = true;
|
|
12738
|
+
line = line.slice(1);
|
|
12739
|
+
}
|
|
12740
|
+
let dirOnly = false;
|
|
12741
|
+
if (line.endsWith("/")) {
|
|
12742
|
+
dirOnly = true;
|
|
12743
|
+
line = line.slice(0, -1);
|
|
12744
|
+
}
|
|
12745
|
+
if (!line) continue;
|
|
12746
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
12747
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
12748
|
+
const body = globBody(line);
|
|
12749
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
12750
|
+
rules.push({
|
|
12751
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
12752
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
12753
|
+
negated,
|
|
12754
|
+
dirOnly
|
|
12967
12755
|
});
|
|
12968
|
-
return this.connecting;
|
|
12969
12756
|
}
|
|
12970
|
-
|
|
12971
|
-
const
|
|
12972
|
-
let
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
try {
|
|
12977
|
-
await this.connectOnce();
|
|
12978
|
-
return;
|
|
12979
|
-
} catch (error) {
|
|
12980
|
-
lastError = error;
|
|
12981
|
-
if (error instanceof StaleProjectIndexServerError) {
|
|
12982
|
-
staleAttempts++;
|
|
12983
|
-
if (!spawnIfMissing) break;
|
|
12984
|
-
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12985
|
-
spawned = false;
|
|
12986
|
-
await delay(100);
|
|
12987
|
-
continue;
|
|
12988
|
-
}
|
|
12989
|
-
}
|
|
12990
|
-
if (!spawnIfMissing) break;
|
|
12991
|
-
if (!spawned) {
|
|
12992
|
-
this.spawnDetachedServer();
|
|
12993
|
-
spawned = true;
|
|
12994
|
-
}
|
|
12995
|
-
await delay(75);
|
|
12757
|
+
return (relPath, isDir) => {
|
|
12758
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
12759
|
+
let ignored = false;
|
|
12760
|
+
for (const r of rules) {
|
|
12761
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
12762
|
+
if (re.test(p)) ignored = !r.negated;
|
|
12996
12763
|
}
|
|
12997
|
-
|
|
12764
|
+
return ignored;
|
|
12765
|
+
};
|
|
12766
|
+
}
|
|
12767
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
12768
|
+
let lines = [];
|
|
12769
|
+
try {
|
|
12770
|
+
const raw = await fs13.readFile(path17.join(projectRoot, ".gitignore"), "utf8");
|
|
12771
|
+
lines = raw.split("\n");
|
|
12772
|
+
} catch {
|
|
12998
12773
|
}
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
};
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
});
|
|
13033
|
-
|
|
13034
|
-
}
|
|
12774
|
+
return compileGitignore(lines);
|
|
12775
|
+
}
|
|
12776
|
+
|
|
12777
|
+
// src/codebase-index/indexer.ts
|
|
12778
|
+
init_languages2();
|
|
12779
|
+
|
|
12780
|
+
// src/codebase-index/parser-dispatch.ts
|
|
12781
|
+
async function parseFileContent(file, content, lang) {
|
|
12782
|
+
switch (lang) {
|
|
12783
|
+
case "ts":
|
|
12784
|
+
case "tsx":
|
|
12785
|
+
case "js":
|
|
12786
|
+
case "jsx": {
|
|
12787
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
12788
|
+
return parseSymbols8({ file, content, lang });
|
|
12789
|
+
}
|
|
12790
|
+
case "go": {
|
|
12791
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
12792
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
12793
|
+
}
|
|
12794
|
+
case "py": {
|
|
12795
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
12796
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
12797
|
+
}
|
|
12798
|
+
case "rs": {
|
|
12799
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
12800
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
12801
|
+
}
|
|
12802
|
+
case "json": {
|
|
12803
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
12804
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
12805
|
+
}
|
|
12806
|
+
case "yaml": {
|
|
12807
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
12808
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
12809
|
+
}
|
|
12810
|
+
default: {
|
|
12811
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
12812
|
+
return parseSymbols8({ file, content, lang });
|
|
12813
|
+
}
|
|
13035
12814
|
}
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
12815
|
+
}
|
|
12816
|
+
|
|
12817
|
+
// src/codebase-index/indexer.ts
|
|
12818
|
+
var YIELD_EVERY_N = 50;
|
|
12819
|
+
function resolveParallelBatch() {
|
|
12820
|
+
return indexParallelBatchSize(availableParallelism());
|
|
12821
|
+
}
|
|
12822
|
+
function yieldEventLoop() {
|
|
12823
|
+
return new Promise((resolve17) => setImmediate(resolve17));
|
|
12824
|
+
}
|
|
12825
|
+
function throwIfAborted(signal) {
|
|
12826
|
+
if (!signal?.aborted) return;
|
|
12827
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
12828
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12829
|
+
}
|
|
12830
|
+
function isAbortError(err) {
|
|
12831
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
12832
|
+
}
|
|
12833
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
12834
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
12835
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
12836
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
12837
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
12838
|
+
function isWithinProject(projectRoot, file) {
|
|
12839
|
+
const rel = path23.relative(projectRoot, file);
|
|
12840
|
+
return rel !== "" && !rel.startsWith(`..${path23.sep}`) && rel !== ".." && !path23.isAbsolute(rel);
|
|
12841
|
+
}
|
|
12842
|
+
function isMissingPathError(err) {
|
|
12843
|
+
const code = err?.code;
|
|
12844
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
12845
|
+
}
|
|
12846
|
+
function normalizeComparablePath(value) {
|
|
12847
|
+
const resolved = path23.resolve(value);
|
|
12848
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12849
|
+
}
|
|
12850
|
+
function gitOutput(projectRoot, args) {
|
|
12851
|
+
return new Promise((resolve17, reject) => {
|
|
12852
|
+
execFile2(
|
|
12853
|
+
"git",
|
|
12854
|
+
["-C", projectRoot, ...args],
|
|
12855
|
+
{
|
|
12856
|
+
encoding: "buffer",
|
|
12857
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
12858
|
+
windowsHide: true
|
|
12859
|
+
},
|
|
12860
|
+
(error, stdout) => {
|
|
12861
|
+
if (error) reject(error);
|
|
12862
|
+
else resolve17(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
13050
12863
|
}
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
12864
|
+
);
|
|
12865
|
+
});
|
|
12866
|
+
}
|
|
12867
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
12868
|
+
try {
|
|
12869
|
+
throwIfAborted(signal);
|
|
12870
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
12871
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
12872
|
+
throwIfAborted(signal);
|
|
12873
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12874
|
+
const [output, statusOutput] = await Promise.all([
|
|
12875
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
12876
|
+
gitOutput(projectRoot, [
|
|
12877
|
+
"status",
|
|
12878
|
+
"--porcelain=v1",
|
|
12879
|
+
"-z",
|
|
12880
|
+
"--untracked-files=all",
|
|
12881
|
+
"--ignored=no"
|
|
12882
|
+
])
|
|
12883
|
+
]);
|
|
12884
|
+
throwIfAborted(signal);
|
|
12885
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
12886
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
12887
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12888
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
12889
|
+
const record = statusRecords[i];
|
|
12890
|
+
if (!record) continue;
|
|
12891
|
+
const status = record.slice(0, 2);
|
|
12892
|
+
const changedPath = path23.resolve(projectRoot, record.slice(3));
|
|
12893
|
+
dirty.add(changedPath);
|
|
12894
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
12895
|
+
if (status.includes("R") || status.includes("C")) {
|
|
12896
|
+
const source = statusRecords[++i];
|
|
12897
|
+
if (source) dirty.add(path23.resolve(projectRoot, source));
|
|
13060
12898
|
}
|
|
13061
|
-
this.onMessage(message);
|
|
13062
12899
|
}
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
13070
|
-
);
|
|
13071
|
-
return;
|
|
13072
|
-
}
|
|
13073
|
-
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
13074
|
-
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
13075
|
-
this.rejectStaleServer(
|
|
13076
|
-
message,
|
|
13077
|
-
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
13078
|
-
);
|
|
13079
|
-
return;
|
|
12900
|
+
const files = [];
|
|
12901
|
+
for (const relative13 of output.toString("utf8").split("\0")) {
|
|
12902
|
+
if (!relative13) continue;
|
|
12903
|
+
const portable = relative13.replace(/\\/g, "/");
|
|
12904
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path23.posix.basename(portable))) {
|
|
12905
|
+
continue;
|
|
13080
12906
|
}
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
12907
|
+
const full = path23.resolve(projectRoot, relative13);
|
|
12908
|
+
if (deleted.has(full)) continue;
|
|
12909
|
+
const ext = path23.extname(relative13).toLowerCase();
|
|
12910
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
12911
|
+
}
|
|
12912
|
+
return {
|
|
12913
|
+
files,
|
|
12914
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12915
|
+
};
|
|
12916
|
+
} catch {
|
|
12917
|
+
return null;
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
12921
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
12922
|
+
if (gitFiles) {
|
|
12923
|
+
return {
|
|
12924
|
+
files: gitFiles.files,
|
|
12925
|
+
complete: true,
|
|
12926
|
+
errors: [],
|
|
12927
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
12928
|
+
};
|
|
12929
|
+
}
|
|
12930
|
+
const results = [];
|
|
12931
|
+
const errors = [];
|
|
12932
|
+
let complete = true;
|
|
12933
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12934
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12935
|
+
let dirCount = 0;
|
|
12936
|
+
const walk2 = async (dir) => {
|
|
12937
|
+
throwIfAborted(signal);
|
|
12938
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12939
|
+
await yieldEventLoop();
|
|
12940
|
+
throwIfAborted(signal);
|
|
13087
12941
|
}
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
12942
|
+
let entries;
|
|
12943
|
+
try {
|
|
12944
|
+
entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
12945
|
+
} catch (err) {
|
|
12946
|
+
complete = false;
|
|
12947
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13092
12948
|
return;
|
|
13093
12949
|
}
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
12950
|
+
dirCount++;
|
|
12951
|
+
for (const e of entries) {
|
|
12952
|
+
if (ignoreSet.has(e.name)) continue;
|
|
12953
|
+
const full = path23.join(dir, e.name);
|
|
12954
|
+
const rel = path23.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12955
|
+
if (e.isDirectory()) {
|
|
12956
|
+
if (isGitIgnored(rel, true)) continue;
|
|
12957
|
+
await walk2(full);
|
|
12958
|
+
} else if (e.isFile()) {
|
|
12959
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12960
|
+
const ext = path23.extname(e.name).toLowerCase();
|
|
12961
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12962
|
+
results.push(full);
|
|
12963
|
+
}
|
|
12964
|
+
}
|
|
13100
12965
|
}
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
12966
|
+
};
|
|
12967
|
+
await walk2(projectRoot);
|
|
12968
|
+
return { files: results, complete, errors };
|
|
12969
|
+
}
|
|
12970
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
12971
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
12972
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
12973
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12974
|
+
const assigned = [];
|
|
12975
|
+
for (const ref of refs) {
|
|
12976
|
+
let owner2;
|
|
12977
|
+
for (const symbol of ordered) {
|
|
12978
|
+
if (symbol.line > ref.line) break;
|
|
12979
|
+
owner2 = symbol;
|
|
13104
12980
|
}
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
12981
|
+
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12982
|
+
if (!owner2 || owner2.id <= 0) continue;
|
|
12983
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12984
|
+
if (seen.has(key)) continue;
|
|
12985
|
+
seen.add(key);
|
|
12986
|
+
assigned.push({ ...ref, fromId: owner2.id });
|
|
13109
12987
|
}
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
|
|
12988
|
+
return assigned;
|
|
12989
|
+
}
|
|
12990
|
+
async function runIndexerWithStore(store, opts) {
|
|
12991
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
12992
|
+
const relationGraphVersion = "2";
|
|
12993
|
+
const refResolutionVersion = "2";
|
|
12994
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
12995
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
12996
|
+
const startMs = Date.now();
|
|
12997
|
+
const errors = [];
|
|
12998
|
+
const langStats = {};
|
|
12999
|
+
let filesIndexed = 0;
|
|
13000
|
+
let symbolsIndexed = 0;
|
|
13001
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
13002
|
+
let files;
|
|
13003
|
+
let discoveredFiles = null;
|
|
13004
|
+
let discoveryComplete = true;
|
|
13005
|
+
let trustedUnchanged;
|
|
13006
|
+
if (opts.files && opts.files.length > 0) {
|
|
13007
|
+
files = opts.files.map((f) => path23.resolve(projectRoot, f)).filter((f) => {
|
|
13008
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
13009
|
+
const rel = path23.relative(projectRoot, f).replace(/\\/g, "/");
|
|
13010
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path23.basename(f)) && !isGitIgnored(rel, false);
|
|
13011
|
+
});
|
|
13012
|
+
} else {
|
|
13013
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
13014
|
+
files = discovery.files;
|
|
13015
|
+
errors.push(...discovery.errors);
|
|
13016
|
+
discoveryComplete = discovery.complete;
|
|
13017
|
+
discoveredFiles = new Set(files);
|
|
13018
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
13124
13019
|
}
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
13020
|
+
if (langs && langs.length > 0) {
|
|
13021
|
+
const langSet = new Set(langs);
|
|
13022
|
+
files = files.filter((f) => {
|
|
13023
|
+
const lang = detectLang(f);
|
|
13024
|
+
return lang ? langSet.has(lang) : false;
|
|
13025
|
+
});
|
|
13130
13026
|
}
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
for (const
|
|
13135
|
-
this.cleanupPending(entry);
|
|
13136
|
-
entry.reject(error);
|
|
13137
|
-
}
|
|
13027
|
+
if (force) store.clearAll();
|
|
13028
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
13029
|
+
if (!force) {
|
|
13030
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
13138
13031
|
}
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
|
|
13032
|
+
const totalFilesForProgress = files.length;
|
|
13033
|
+
let filesPreSkipped = 0;
|
|
13034
|
+
if (!force && trustedUnchanged) {
|
|
13035
|
+
files = files.filter((file) => {
|
|
13036
|
+
const meta = existingMeta.get(file);
|
|
13037
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
13038
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
13039
|
+
symbolsIndexed += meta.symbolCount;
|
|
13040
|
+
filesIndexed++;
|
|
13041
|
+
filesPreSkipped++;
|
|
13042
|
+
return false;
|
|
13043
|
+
});
|
|
13044
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
13142
13045
|
}
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13151
|
-
|
|
13152
|
-
);
|
|
13153
|
-
|
|
13154
|
-
|
|
13046
|
+
const parallelBatch = resolveParallelBatch();
|
|
13047
|
+
let filesSinceLastYield = 0;
|
|
13048
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
13049
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
13050
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
13051
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
13052
|
+
filesSinceLastYield += batchFiles.length;
|
|
13053
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
13054
|
+
filesSinceLastYield = 0;
|
|
13055
|
+
await yieldEventLoop();
|
|
13056
|
+
if (isFrugalPerf()) {
|
|
13057
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
13058
|
+
}
|
|
13059
|
+
throwIfAborted(signal);
|
|
13060
|
+
}
|
|
13061
|
+
const statOpts = signal ? { signal } : {};
|
|
13062
|
+
const statReadParse = await Promise.allSettled(
|
|
13063
|
+
batchFiles.map(
|
|
13064
|
+
async (file) => {
|
|
13065
|
+
let stat18;
|
|
13066
|
+
try {
|
|
13067
|
+
stat18 = await fs17.stat(file, statOpts);
|
|
13068
|
+
} catch (e) {
|
|
13069
|
+
if (isAbortError(e)) throw e;
|
|
13070
|
+
return {
|
|
13071
|
+
file,
|
|
13072
|
+
stat: null,
|
|
13073
|
+
lang: "",
|
|
13074
|
+
parsed: null,
|
|
13075
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
13076
|
+
missing: isMissingPathError(e)
|
|
13077
|
+
};
|
|
13078
|
+
}
|
|
13079
|
+
if (!stat18.isFile()) return { file, stat: stat18, lang: "", parsed: null };
|
|
13080
|
+
const lang = detectLang(file);
|
|
13081
|
+
if (!lang) return { file, stat: stat18, lang: "", parsed: null };
|
|
13082
|
+
if (stat18.size > MAX_INDEX_FILE_BYTES) {
|
|
13083
|
+
return {
|
|
13084
|
+
file,
|
|
13085
|
+
stat: stat18,
|
|
13086
|
+
lang,
|
|
13087
|
+
parsed: null,
|
|
13088
|
+
error: `file too large (${stat18.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
13089
|
+
};
|
|
13090
|
+
}
|
|
13091
|
+
const meta = existingMeta.get(file);
|
|
13092
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat18.mtimeMs)) {
|
|
13093
|
+
return { file, stat: stat18, lang, parsed: null, skippedMeta: meta };
|
|
13094
|
+
}
|
|
13095
|
+
let content;
|
|
13096
|
+
try {
|
|
13097
|
+
content = await fs17.readFile(file, { encoding: "utf8", signal });
|
|
13098
|
+
} catch (e) {
|
|
13099
|
+
if (isAbortError(e)) throw e;
|
|
13100
|
+
return {
|
|
13101
|
+
file,
|
|
13102
|
+
stat: stat18,
|
|
13103
|
+
lang,
|
|
13104
|
+
parsed: null,
|
|
13105
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
13106
|
+
};
|
|
13107
|
+
}
|
|
13108
|
+
let parsed;
|
|
13109
|
+
try {
|
|
13110
|
+
parsed = await parseFileContent(file, content, lang);
|
|
13111
|
+
} catch (e) {
|
|
13112
|
+
return {
|
|
13113
|
+
file,
|
|
13114
|
+
stat: stat18,
|
|
13115
|
+
lang,
|
|
13116
|
+
parsed: null,
|
|
13117
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
13118
|
+
};
|
|
13119
|
+
}
|
|
13120
|
+
return { file, stat: stat18, lang, parsed, content };
|
|
13121
|
+
}
|
|
13122
|
+
)
|
|
13123
|
+
);
|
|
13124
|
+
const batchEntries = [];
|
|
13125
|
+
const deleteForFiles = [];
|
|
13126
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
13127
|
+
const settled = statReadParse[fi];
|
|
13128
|
+
const file = expectDefined6(batchFiles[fi]);
|
|
13129
|
+
if (settled.status === "rejected") {
|
|
13130
|
+
const err = settled.reason;
|
|
13131
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
13132
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13133
|
+
continue;
|
|
13134
|
+
}
|
|
13135
|
+
const result = settled.value;
|
|
13136
|
+
if (result.error) {
|
|
13137
|
+
if (result.missing) store.deleteFile(file);
|
|
13138
|
+
errors.push(`${file}: ${result.error}`);
|
|
13139
|
+
continue;
|
|
13140
|
+
}
|
|
13141
|
+
const { stat: stat18, lang, parsed } = result;
|
|
13142
|
+
if (result.skippedMeta) {
|
|
13143
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
13144
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
13145
|
+
filesIndexed++;
|
|
13146
|
+
continue;
|
|
13147
|
+
}
|
|
13148
|
+
if (!lang || !parsed) {
|
|
13149
|
+
if (lang) {
|
|
13150
|
+
store.upsertFile({
|
|
13151
|
+
file,
|
|
13152
|
+
lang,
|
|
13153
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13154
|
+
symbolCount: 0,
|
|
13155
|
+
lastIndexed: Date.now()
|
|
13156
|
+
});
|
|
13157
|
+
filesIndexed++;
|
|
13158
|
+
}
|
|
13159
|
+
continue;
|
|
13160
|
+
}
|
|
13161
|
+
if (parsed.symbols.length === 0) {
|
|
13162
|
+
store.replaceEmptyFile({
|
|
13163
|
+
file,
|
|
13164
|
+
lang,
|
|
13165
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13166
|
+
symbolCount: 0,
|
|
13167
|
+
lastIndexed: Date.now()
|
|
13168
|
+
});
|
|
13169
|
+
filesIndexed++;
|
|
13170
|
+
continue;
|
|
13171
|
+
}
|
|
13172
|
+
batchEntries.push({
|
|
13173
|
+
file,
|
|
13174
|
+
lang,
|
|
13175
|
+
symbols: parsed.symbols,
|
|
13176
|
+
refs: parsed.refs ?? [],
|
|
13177
|
+
mtimeMs: Math.floor(stat18.mtimeMs),
|
|
13178
|
+
symbolCount: parsed.symbols.length
|
|
13179
|
+
});
|
|
13180
|
+
deleteForFiles.push(file);
|
|
13155
13181
|
}
|
|
13156
|
-
|
|
13157
|
-
}
|
|
13158
|
-
spawnDetachedServer() {
|
|
13159
|
-
const url = resolveProjectServerUrl();
|
|
13160
|
-
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
13161
|
-
if (process.platform !== "win32") {
|
|
13182
|
+
if (batchEntries.length > 0) {
|
|
13162
13183
|
try {
|
|
13163
|
-
|
|
13164
|
-
|
|
13184
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
13185
|
+
for (const entry of batchEntries) {
|
|
13186
|
+
const count = entry.symbols.length;
|
|
13187
|
+
symbolsIndexed += count;
|
|
13188
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
13189
|
+
filesIndexed++;
|
|
13190
|
+
}
|
|
13191
|
+
} catch (err) {
|
|
13192
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13193
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
13194
|
+
for (const entry of batchEntries) {
|
|
13195
|
+
try {
|
|
13196
|
+
store.deleteRefsForFile(entry.file);
|
|
13197
|
+
store.deleteSymbolsForFile(entry.file);
|
|
13198
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
13199
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
13200
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
13201
|
+
filesIndexed++;
|
|
13202
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
13203
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
13204
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
13205
|
+
}
|
|
13206
|
+
store.resolveRefsForNames([
|
|
13207
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
13208
|
+
...entry.refs.map((ref) => ref.toName)
|
|
13209
|
+
]);
|
|
13210
|
+
store.upsertFile({
|
|
13211
|
+
file: entry.file,
|
|
13212
|
+
lang: entry.lang,
|
|
13213
|
+
mtimeMs: entry.mtimeMs,
|
|
13214
|
+
symbolCount: entry.symbolCount,
|
|
13215
|
+
lastIndexed: Date.now()
|
|
13216
|
+
});
|
|
13217
|
+
} catch (innerErr) {
|
|
13218
|
+
errors.push(
|
|
13219
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
13220
|
+
);
|
|
13221
|
+
}
|
|
13222
|
+
}
|
|
13165
13223
|
}
|
|
13166
13224
|
}
|
|
13167
|
-
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
13168
|
-
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
13169
|
-
const child = spawn7(process.execPath, args, {
|
|
13170
|
-
detached: true,
|
|
13171
|
-
stdio: "ignore",
|
|
13172
|
-
windowsHide: true,
|
|
13173
|
-
env: process.env
|
|
13174
|
-
});
|
|
13175
|
-
child.unref();
|
|
13176
|
-
}
|
|
13177
|
-
forceKillKnownServer() {
|
|
13178
|
-
const pid = this.info?.pid;
|
|
13179
|
-
return pid ? this.forceKillServer(pid) : false;
|
|
13180
13225
|
}
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
13186
|
-
try {
|
|
13187
|
-
const metadata = JSON.parse(fs17.readFileSync(metadataPath, "utf8"));
|
|
13188
|
-
if (metadata.pid === pid) fs17.rmSync(metadataPath, { force: true });
|
|
13189
|
-
} catch {
|
|
13226
|
+
if (discoveredFiles && discoveryComplete) {
|
|
13227
|
+
for (const [file_] of existingMeta) {
|
|
13228
|
+
if (!discoveredFiles.has(file_)) {
|
|
13229
|
+
store.deleteFile(file_);
|
|
13190
13230
|
}
|
|
13191
|
-
return true;
|
|
13192
|
-
} catch {
|
|
13193
|
-
return false;
|
|
13194
13231
|
}
|
|
13195
13232
|
}
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
if (!heartbeatTimer) return;
|
|
13211
|
-
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
13212
|
-
clearInterval(heartbeatTimer);
|
|
13213
|
-
heartbeatTimer = void 0;
|
|
13233
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
13234
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
13235
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
13236
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
13237
|
+
store.setLastIndexed(Date.now());
|
|
13238
|
+
if (!opts.files) store.compactIfNeeded();
|
|
13239
|
+
const durationMs = Date.now() - startMs;
|
|
13240
|
+
return {
|
|
13241
|
+
filesIndexed,
|
|
13242
|
+
symbolsIndexed,
|
|
13243
|
+
langStats,
|
|
13244
|
+
durationMs,
|
|
13245
|
+
errors
|
|
13246
|
+
};
|
|
13214
13247
|
}
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13248
|
+
|
|
13249
|
+
// src/codebase-index/index-service.ts
|
|
13250
|
+
async function indexService(args, hooks = {}) {
|
|
13251
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13252
|
+
try {
|
|
13253
|
+
return await runIndexerWithStore(store, {
|
|
13254
|
+
projectRoot: args.projectRoot,
|
|
13255
|
+
indexDir: args.indexDir,
|
|
13256
|
+
files: args.files,
|
|
13257
|
+
force: args.force,
|
|
13258
|
+
langs: args.langs,
|
|
13259
|
+
ignore: args.ignore,
|
|
13260
|
+
signal: hooks.signal,
|
|
13261
|
+
onProgress: hooks.onProgress
|
|
13262
|
+
});
|
|
13263
|
+
} finally {
|
|
13264
|
+
indexStorePool.release(store);
|
|
13221
13265
|
}
|
|
13222
|
-
return connection;
|
|
13223
13266
|
}
|
|
13224
|
-
function
|
|
13225
|
-
|
|
13267
|
+
function searchService(args) {
|
|
13268
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13269
|
+
try {
|
|
13270
|
+
return store.searchRanked(
|
|
13271
|
+
args.query,
|
|
13272
|
+
{
|
|
13273
|
+
kind: args.kind,
|
|
13274
|
+
lang: args.lang,
|
|
13275
|
+
file: args.file,
|
|
13276
|
+
lspKind: args.lspKind
|
|
13277
|
+
},
|
|
13278
|
+
args.limit
|
|
13279
|
+
);
|
|
13280
|
+
} finally {
|
|
13281
|
+
indexStorePool.release(store);
|
|
13282
|
+
}
|
|
13226
13283
|
}
|
|
13227
|
-
function
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13284
|
+
function statsService(args) {
|
|
13285
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13286
|
+
try {
|
|
13287
|
+
return store.getStats();
|
|
13288
|
+
} finally {
|
|
13289
|
+
indexStorePool.release(store);
|
|
13290
|
+
}
|
|
13232
13291
|
}
|
|
13233
|
-
function
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13292
|
+
function packageGraphService(args) {
|
|
13293
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13294
|
+
try {
|
|
13295
|
+
return store.getPackageGraph();
|
|
13296
|
+
} finally {
|
|
13297
|
+
indexStorePool.release(store);
|
|
13298
|
+
}
|
|
13238
13299
|
}
|
|
13239
|
-
|
|
13240
|
-
const
|
|
13241
|
-
const connection = connectionFor(projectRoot, indexDir);
|
|
13300
|
+
function fileGraphService(args) {
|
|
13301
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13242
13302
|
try {
|
|
13243
|
-
return
|
|
13303
|
+
return store.getFileGraph(args.packageFilter);
|
|
13244
13304
|
} finally {
|
|
13245
|
-
|
|
13246
|
-
connections.delete(endpoint);
|
|
13247
|
-
connectionStates.delete(endpoint);
|
|
13305
|
+
indexStorePool.release(store);
|
|
13248
13306
|
}
|
|
13249
13307
|
}
|
|
13250
|
-
function
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
};
|
|
13258
|
-
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
13259
|
-
heartbeatTimer = void 0;
|
|
13308
|
+
function symbolGraphService(args) {
|
|
13309
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
13310
|
+
try {
|
|
13311
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
13312
|
+
} finally {
|
|
13313
|
+
indexStorePool.release(store);
|
|
13314
|
+
}
|
|
13260
13315
|
}
|
|
13261
13316
|
|
|
13262
13317
|
// src/codebase-index/background-indexer.ts
|
|
13318
|
+
init_languages2();
|
|
13263
13319
|
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 24e4;
|
|
13264
13320
|
var DEFAULT_INCREMENTAL_TIMEOUT_MS = 6e4;
|
|
13265
13321
|
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
@@ -13392,8 +13448,17 @@ async function shutdownCodebaseIndexHost() {
|
|
|
13392
13448
|
}
|
|
13393
13449
|
}
|
|
13394
13450
|
}
|
|
13451
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
13452
|
+
function warnEndpointInvalidOnce(availability) {
|
|
13453
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
13454
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
13455
|
+
process.stderr.write(
|
|
13456
|
+
`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.
|
|
13457
|
+
`
|
|
13458
|
+
);
|
|
13459
|
+
}
|
|
13395
13460
|
function callIndexOp(op, args, opts) {
|
|
13396
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
13461
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
13397
13462
|
if (availability.kind === "available") {
|
|
13398
13463
|
return callProjectIndexServer(op, args, opts);
|
|
13399
13464
|
}
|
|
@@ -13404,6 +13469,14 @@ function callIndexOp(op, args, opts) {
|
|
|
13404
13469
|
)
|
|
13405
13470
|
);
|
|
13406
13471
|
}
|
|
13472
|
+
if (availability.kind === "endpoint-invalid") {
|
|
13473
|
+
warnEndpointInvalidOnce(availability);
|
|
13474
|
+
return Promise.reject(
|
|
13475
|
+
new Error(
|
|
13476
|
+
`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.`
|
|
13477
|
+
)
|
|
13478
|
+
);
|
|
13479
|
+
}
|
|
13407
13480
|
const w = ensureWorker();
|
|
13408
13481
|
if (!w) return callInline(op, args, opts);
|
|
13409
13482
|
if (opts.signal?.aborted) {
|
|
@@ -13677,7 +13750,11 @@ function checkCodebaseIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
|
13677
13750
|
return checkProjectIndexServerHealth(projectRoot, indexDir, options);
|
|
13678
13751
|
}
|
|
13679
13752
|
function ensureCodebaseIndexServer(options) {
|
|
13680
|
-
|
|
13753
|
+
const availability = resolveProjectIndexDaemonAvailability(options.projectRoot, options.indexDir);
|
|
13754
|
+
if (availability.kind !== "available") {
|
|
13755
|
+
if (availability.kind === "endpoint-invalid") warnEndpointInvalidOnce(availability);
|
|
13756
|
+
return Promise.resolve();
|
|
13757
|
+
}
|
|
13681
13758
|
return ensureProjectIndexServer({
|
|
13682
13759
|
projectRoot: options.projectRoot,
|
|
13683
13760
|
indexDir: options.indexDir,
|
|
@@ -26450,6 +26527,7 @@ export {
|
|
|
26450
26527
|
resetIndexCircuitBreaker,
|
|
26451
26528
|
resetPersistentProcessRegistry,
|
|
26452
26529
|
resolvePinnedBrowserTarget,
|
|
26530
|
+
resolveProjectIndexDaemonAvailability,
|
|
26453
26531
|
resolveSessionShell,
|
|
26454
26532
|
runDeadCodeScan,
|
|
26455
26533
|
runStartupIndex,
|