@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/builtin.js
CHANGED
|
@@ -479,6 +479,7 @@ var init_process_registry = __esm({
|
|
|
479
479
|
}
|
|
480
480
|
/** Get all tracked processes. */
|
|
481
481
|
list() {
|
|
482
|
+
this._pruneAllStale();
|
|
482
483
|
return Array.from(this.processes.values());
|
|
483
484
|
}
|
|
484
485
|
/** Get processes filtered by name (e.g. 'bash', 'exec'). */
|
|
@@ -509,6 +510,7 @@ var init_process_registry = __esm({
|
|
|
509
510
|
* Combined stats for observability — used by /ps and the TUI status bar.
|
|
510
511
|
*/
|
|
511
512
|
stats() {
|
|
513
|
+
this._pruneAllStale();
|
|
512
514
|
return {
|
|
513
515
|
activeCount: this.activeCount,
|
|
514
516
|
backgroundCount: this.activeBackgroundCount,
|
|
@@ -748,6 +750,19 @@ var init_process_registry = __esm({
|
|
|
748
750
|
this.processes.delete(pid);
|
|
749
751
|
}
|
|
750
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Remove every stale entry, not just one PID. `list()`/`stats()` — the
|
|
755
|
+
* surfaces the TUI status bar and `/ps` poll — must prune too: a child
|
|
756
|
+
* whose 'close' event never fires (e.g. Windows grandchildren holding stdio
|
|
757
|
+
* open) would otherwise linger in the registry until someone looks up its
|
|
758
|
+
* exact PID, and PID reuse meanwhile makes `get()`/`kill()` target the
|
|
759
|
+
* wrong process. RAM-leak audit 2026-07-31, MEDIUM.
|
|
760
|
+
*/
|
|
761
|
+
_pruneAllStale() {
|
|
762
|
+
for (const [pid, entry] of this.processes) {
|
|
763
|
+
if (this._isStaleEntry(entry)) this.processes.delete(pid);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
751
766
|
};
|
|
752
767
|
}
|
|
753
768
|
});
|
|
@@ -4893,16 +4908,16 @@ var init_legacy_bridge = __esm({
|
|
|
4893
4908
|
});
|
|
4894
4909
|
|
|
4895
4910
|
// src/codebase-index/languages.ts
|
|
4896
|
-
import * as
|
|
4911
|
+
import * as path18 from "node:path";
|
|
4897
4912
|
function detectLang(file) {
|
|
4898
|
-
const base =
|
|
4913
|
+
const base = path18.basename(file);
|
|
4899
4914
|
const lowerBase = base.toLowerCase();
|
|
4900
4915
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4901
4916
|
return "ts";
|
|
4902
4917
|
}
|
|
4903
4918
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4904
4919
|
if (special) return special;
|
|
4905
|
-
const ext =
|
|
4920
|
+
const ext = path18.extname(base).toLowerCase();
|
|
4906
4921
|
if (!ext) return null;
|
|
4907
4922
|
return EXT_TO_LANG[ext] ?? null;
|
|
4908
4923
|
}
|
|
@@ -5238,10 +5253,10 @@ __export(go_parser_exports, {
|
|
|
5238
5253
|
detectLang: () => detectLang,
|
|
5239
5254
|
parseSymbols: () => parseSymbols2
|
|
5240
5255
|
});
|
|
5241
|
-
import { spawn as
|
|
5242
|
-
import * as
|
|
5243
|
-
import * as
|
|
5244
|
-
import * as
|
|
5256
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5257
|
+
import * as os6 from "node:os";
|
|
5258
|
+
import * as path19 from "node:path";
|
|
5259
|
+
import * as fs14 from "node:fs/promises";
|
|
5245
5260
|
async function parseSymbols2(opts) {
|
|
5246
5261
|
const { file, content, lang } = opts;
|
|
5247
5262
|
try {
|
|
@@ -5313,16 +5328,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5313
5328
|
try {
|
|
5314
5329
|
let scriptPath = _cachedGoScriptPath;
|
|
5315
5330
|
if (!scriptPath) {
|
|
5316
|
-
const tmpDir = await
|
|
5317
|
-
scriptPath =
|
|
5318
|
-
await
|
|
5331
|
+
const tmpDir = await fs14.mkdtemp(path19.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5332
|
+
scriptPath = path19.join(tmpDir, "parse.go");
|
|
5333
|
+
await fs14.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5319
5334
|
_cachedGoScriptPath = scriptPath;
|
|
5320
5335
|
}
|
|
5321
5336
|
const goBinary = resolveWin32Command("go");
|
|
5322
5337
|
const goResult = await new Promise(
|
|
5323
5338
|
(resolve16, reject) => {
|
|
5324
5339
|
let settled = false;
|
|
5325
|
-
const proc =
|
|
5340
|
+
const proc = spawn5(goBinary, ["run", scriptPath], {
|
|
5326
5341
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5327
5342
|
windowsHide: true
|
|
5328
5343
|
});
|
|
@@ -5925,10 +5940,10 @@ __export(py_parser_exports, {
|
|
|
5925
5940
|
detectLang: () => detectLang,
|
|
5926
5941
|
parseSymbols: () => parseSymbols4
|
|
5927
5942
|
});
|
|
5928
|
-
import { spawn as
|
|
5929
|
-
import * as
|
|
5930
|
-
import * as
|
|
5931
|
-
import * as
|
|
5943
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
5944
|
+
import * as fs15 from "node:fs/promises";
|
|
5945
|
+
import * as os7 from "node:os";
|
|
5946
|
+
import * as path20 from "node:path";
|
|
5932
5947
|
async function parseSymbols4(opts) {
|
|
5933
5948
|
const { file, content, lang } = opts;
|
|
5934
5949
|
try {
|
|
@@ -5950,7 +5965,7 @@ async function resolvePython() {
|
|
|
5950
5965
|
function commandIsAvailable(command) {
|
|
5951
5966
|
return new Promise((resolve16) => {
|
|
5952
5967
|
let settled = false;
|
|
5953
|
-
const proc =
|
|
5968
|
+
const proc = spawn6(command, ["--version"], {
|
|
5954
5969
|
stdio: "ignore",
|
|
5955
5970
|
windowsHide: true
|
|
5956
5971
|
});
|
|
@@ -5972,7 +5987,7 @@ function commandIsAvailable(command) {
|
|
|
5972
5987
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
5973
5988
|
return new Promise((resolve16, reject) => {
|
|
5974
5989
|
let settled = false;
|
|
5975
|
-
const proc =
|
|
5990
|
+
const proc = spawn6(pyBinary, [scriptPath, filePath], {
|
|
5976
5991
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5977
5992
|
windowsHide: true
|
|
5978
5993
|
});
|
|
@@ -6006,10 +6021,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
6006
6021
|
async function syncPyParse(filePath, content, lang) {
|
|
6007
6022
|
try {
|
|
6008
6023
|
if (!_cachedScriptPath) {
|
|
6009
|
-
const tmpDir =
|
|
6010
|
-
await
|
|
6011
|
-
_cachedScriptPath =
|
|
6012
|
-
await
|
|
6024
|
+
const tmpDir = path20.join(os7.tmpdir(), "ws-py-parse");
|
|
6025
|
+
await fs15.mkdir(tmpDir, { recursive: true });
|
|
6026
|
+
_cachedScriptPath = path20.join(tmpDir, "parse.py");
|
|
6027
|
+
await fs15.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
6013
6028
|
}
|
|
6014
6029
|
cachedPyBinary ??= resolvePython();
|
|
6015
6030
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6263,10 +6278,10 @@ __export(rs_parser_exports, {
|
|
|
6263
6278
|
detectLang: () => detectLang,
|
|
6264
6279
|
parseSymbols: () => parseSymbols5
|
|
6265
6280
|
});
|
|
6266
|
-
import { expectDefined as
|
|
6267
|
-
import { execFile, spawn as
|
|
6268
|
-
import * as
|
|
6269
|
-
import * as
|
|
6281
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6282
|
+
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6283
|
+
import * as fs16 from "node:fs/promises";
|
|
6284
|
+
import * as path21 from "node:path";
|
|
6270
6285
|
async function parseSymbols5(opts) {
|
|
6271
6286
|
const { file, content, lang } = opts;
|
|
6272
6287
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -6288,7 +6303,7 @@ function checkNativeParser() {
|
|
|
6288
6303
|
nativeParserAvailability ??= (async () => {
|
|
6289
6304
|
try {
|
|
6290
6305
|
await probe("rustc", ["--version"]);
|
|
6291
|
-
const toolsDir =
|
|
6306
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6292
6307
|
await probe(
|
|
6293
6308
|
"cargo",
|
|
6294
6309
|
[
|
|
@@ -6297,7 +6312,7 @@ function checkNativeParser() {
|
|
|
6297
6312
|
"--format-version",
|
|
6298
6313
|
"1",
|
|
6299
6314
|
"--manifest-path",
|
|
6300
|
-
|
|
6315
|
+
path21.join(toolsDir, "Cargo.toml")
|
|
6301
6316
|
]
|
|
6302
6317
|
);
|
|
6303
6318
|
return true;
|
|
@@ -6309,17 +6324,17 @@ function checkNativeParser() {
|
|
|
6309
6324
|
}
|
|
6310
6325
|
async function tryNativeParse(file, content) {
|
|
6311
6326
|
try {
|
|
6312
|
-
const toolsDir =
|
|
6313
|
-
const crateDir =
|
|
6314
|
-
const tmpFile =
|
|
6315
|
-
await
|
|
6327
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6328
|
+
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6329
|
+
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6330
|
+
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6316
6331
|
const cargoBinary = resolveWin32Command("cargo");
|
|
6317
6332
|
const result = await new Promise(
|
|
6318
6333
|
(resolve16, reject) => {
|
|
6319
6334
|
let settled = false;
|
|
6320
|
-
const proc =
|
|
6335
|
+
const proc = spawn7(
|
|
6321
6336
|
cargoBinary,
|
|
6322
|
-
["run", "--manifest-path",
|
|
6337
|
+
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6323
6338
|
{
|
|
6324
6339
|
cwd: process.cwd(),
|
|
6325
6340
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -6378,7 +6393,7 @@ function regexParse(opts) {
|
|
|
6378
6393
|
let hi = lineOffsets2.length - 1;
|
|
6379
6394
|
while (lo < hi) {
|
|
6380
6395
|
const mid = lo + hi + 1 >>> 1;
|
|
6381
|
-
if (
|
|
6396
|
+
if (expectDefined3(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6382
6397
|
else hi = mid - 1;
|
|
6383
6398
|
}
|
|
6384
6399
|
return lo + 1;
|
|
@@ -6390,7 +6405,7 @@ function regexParse(opts) {
|
|
|
6390
6405
|
for (const pattern of RS_PATTERNS) {
|
|
6391
6406
|
pattern.regex.lastIndex = 0;
|
|
6392
6407
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
6393
|
-
const name =
|
|
6408
|
+
const name = expectDefined3(match[1]);
|
|
6394
6409
|
const offset = match.index ?? 0;
|
|
6395
6410
|
const line = lineFromOffset(offset);
|
|
6396
6411
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6447,8 +6462,8 @@ __export(json_parser_exports, {
|
|
|
6447
6462
|
detectLang: () => detectLang,
|
|
6448
6463
|
parseSymbols: () => parseSymbols6
|
|
6449
6464
|
});
|
|
6450
|
-
import { expectDefined as
|
|
6451
|
-
import * as
|
|
6465
|
+
import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
|
|
6466
|
+
import * as path22 from "node:path";
|
|
6452
6467
|
function parseSymbols6(opts) {
|
|
6453
6468
|
const { file, content, lang } = opts;
|
|
6454
6469
|
try {
|
|
@@ -6460,7 +6475,7 @@ function parseSymbols6(opts) {
|
|
|
6460
6475
|
function regexParse2(opts) {
|
|
6461
6476
|
const { file, content, lang } = opts;
|
|
6462
6477
|
const symbols = [];
|
|
6463
|
-
const basename12 =
|
|
6478
|
+
const basename12 = path22.basename(file).toLowerCase();
|
|
6464
6479
|
const isPackageJson = basename12 === "package.json";
|
|
6465
6480
|
const isTsconfig = basename12 === "tsconfig.json" || basename12 === "tsconfig.build.json";
|
|
6466
6481
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -6475,22 +6490,22 @@ function regexParse2(opts) {
|
|
|
6475
6490
|
let hi = lineOffsets2.length - 1;
|
|
6476
6491
|
while (lo < hi) {
|
|
6477
6492
|
const mid = lo + hi + 1 >>> 1;
|
|
6478
|
-
if (
|
|
6493
|
+
if (expectDefined4(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6479
6494
|
else hi = mid - 1;
|
|
6480
6495
|
}
|
|
6481
6496
|
return lo + 1;
|
|
6482
6497
|
}
|
|
6483
6498
|
const rootMatch = content.match(/^\s*\{/m);
|
|
6484
6499
|
if (rootMatch) {
|
|
6485
|
-
const offset =
|
|
6500
|
+
const offset = expectDefined4(rootMatch.index);
|
|
6486
6501
|
const line = lineFromOffset(offset);
|
|
6487
6502
|
symbols.push(
|
|
6488
6503
|
makeSymbol({
|
|
6489
|
-
name:
|
|
6504
|
+
name: path22.basename(file),
|
|
6490
6505
|
kind: "object",
|
|
6491
6506
|
line,
|
|
6492
6507
|
col: 0,
|
|
6493
|
-
signature: `"${
|
|
6508
|
+
signature: `"${path22.basename(file)}" = { ... }`,
|
|
6494
6509
|
file,
|
|
6495
6510
|
lang
|
|
6496
6511
|
})
|
|
@@ -6498,7 +6513,7 @@ function regexParse2(opts) {
|
|
|
6498
6513
|
}
|
|
6499
6514
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
6500
6515
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
6501
|
-
const key =
|
|
6516
|
+
const key = expectDefined4(match[1]);
|
|
6502
6517
|
const offset = match.index ?? 0;
|
|
6503
6518
|
const line = lineFromOffset(offset);
|
|
6504
6519
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6545,7 +6560,7 @@ function regexParse2(opts) {
|
|
|
6545
6560
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
6546
6561
|
const defsMatch = defsRegex.exec(content);
|
|
6547
6562
|
if (defsMatch !== null) {
|
|
6548
|
-
const offset =
|
|
6563
|
+
const offset = expectDefined4(defsMatch.index);
|
|
6549
6564
|
const line = lineFromOffset(offset);
|
|
6550
6565
|
symbols.push(
|
|
6551
6566
|
makeSymbol({
|
|
@@ -6570,7 +6585,7 @@ function regexParse2(opts) {
|
|
|
6570
6585
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
6571
6586
|
const offset = match.index ?? 0;
|
|
6572
6587
|
const line = lineFromOffset(offset);
|
|
6573
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
6588
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined4(match[0]);
|
|
6574
6589
|
symbols.push(
|
|
6575
6590
|
makeSymbol({
|
|
6576
6591
|
name: key,
|
|
@@ -6589,12 +6604,12 @@ function regexParse2(opts) {
|
|
|
6589
6604
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineFromOffset) {
|
|
6590
6605
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
6591
6606
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
6592
|
-
const blockContent =
|
|
6607
|
+
const blockContent = expectDefined4(match[0]);
|
|
6593
6608
|
const blockOffset = match.index ?? 0;
|
|
6594
6609
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
6595
6610
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
6596
|
-
const key =
|
|
6597
|
-
const keyOffset = blockOffset +
|
|
6611
|
+
const key = expectDefined4(scriptMatch[1]);
|
|
6612
|
+
const keyOffset = blockOffset + expectDefined4(scriptMatch.index);
|
|
6598
6613
|
const line = lineFromOffset(keyOffset);
|
|
6599
6614
|
symbols.push(
|
|
6600
6615
|
makeSymbol({
|
|
@@ -6613,12 +6628,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineF
|
|
|
6613
6628
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets2, parentLine, lineFromOffset) {
|
|
6614
6629
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
6615
6630
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
6616
|
-
const blockContent =
|
|
6631
|
+
const blockContent = expectDefined4(match[0]);
|
|
6617
6632
|
const blockOffset = match.index ?? 0;
|
|
6618
6633
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
6619
6634
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
6620
|
-
const key =
|
|
6621
|
-
const keyOffset = blockOffset +
|
|
6635
|
+
const key = expectDefined4(optMatch[1]);
|
|
6636
|
+
const keyOffset = blockOffset + expectDefined4(optMatch.index);
|
|
6622
6637
|
const line = lineFromOffset(keyOffset);
|
|
6623
6638
|
if (line <= parentLine) continue;
|
|
6624
6639
|
symbols.push(
|
|
@@ -6663,7 +6678,7 @@ __export(yaml_parser_exports, {
|
|
|
6663
6678
|
detectLang: () => detectLang,
|
|
6664
6679
|
parseSymbols: () => parseSymbols7
|
|
6665
6680
|
});
|
|
6666
|
-
import { expectDefined as
|
|
6681
|
+
import { expectDefined as expectDefined5, truncate } from "@wrongstack/core/utils";
|
|
6667
6682
|
function parseSymbols7(opts) {
|
|
6668
6683
|
const { file, content, lang } = opts;
|
|
6669
6684
|
try {
|
|
@@ -6685,14 +6700,14 @@ function regexParse3(opts) {
|
|
|
6685
6700
|
let hi = lineOffsets2.length - 1;
|
|
6686
6701
|
while (lo < hi) {
|
|
6687
6702
|
const mid = lo + hi + 1 >>> 1;
|
|
6688
|
-
if (
|
|
6703
|
+
if (expectDefined5(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6689
6704
|
else hi = mid - 1;
|
|
6690
6705
|
}
|
|
6691
6706
|
return lo + 1;
|
|
6692
6707
|
}
|
|
6693
6708
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
6694
6709
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
6695
|
-
const name =
|
|
6710
|
+
const name = expectDefined5(match[1]);
|
|
6696
6711
|
const offset = match.index ?? 0;
|
|
6697
6712
|
const line = lineFromOffset(offset);
|
|
6698
6713
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6710,7 +6725,7 @@ function regexParse3(opts) {
|
|
|
6710
6725
|
}
|
|
6711
6726
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
6712
6727
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
6713
|
-
const name =
|
|
6728
|
+
const name = expectDefined5(match[1]);
|
|
6714
6729
|
const offset = match.index ?? 0;
|
|
6715
6730
|
const line = lineFromOffset(offset);
|
|
6716
6731
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6745,7 +6760,7 @@ function regexParse3(opts) {
|
|
|
6745
6760
|
}
|
|
6746
6761
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6747
6762
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
6748
|
-
const key =
|
|
6763
|
+
const key = expectDefined5(match[2]);
|
|
6749
6764
|
const offset = match.index ?? 0;
|
|
6750
6765
|
const line = lineFromOffset(offset);
|
|
6751
6766
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6765,7 +6780,7 @@ function regexParse3(opts) {
|
|
|
6765
6780
|
}
|
|
6766
6781
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
6767
6782
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
6768
|
-
const key =
|
|
6783
|
+
const key = expectDefined5(match[2]);
|
|
6769
6784
|
const offset = match.index ?? 0;
|
|
6770
6785
|
const line = lineFromOffset(offset);
|
|
6771
6786
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -9770,10 +9785,12 @@ var browserTools = [
|
|
|
9770
9785
|
for (const tool of browserTools) tool.icon = "web";
|
|
9771
9786
|
for (const tool of browserTools) tool.timeoutMs ??= 6e4;
|
|
9772
9787
|
|
|
9773
|
-
// src/codebase-index/
|
|
9774
|
-
import
|
|
9775
|
-
import
|
|
9776
|
-
import
|
|
9788
|
+
// src/codebase-index/project-server-client.ts
|
|
9789
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
9790
|
+
import * as fs12 from "node:fs";
|
|
9791
|
+
import * as net3 from "node:net";
|
|
9792
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
9793
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
9777
9794
|
|
|
9778
9795
|
// src/codebase-index/circuit-breaker.ts
|
|
9779
9796
|
var CircuitOpenError = class extends Error {
|
|
@@ -9855,117 +9872,18 @@ var IndexCircuitBreaker = class {
|
|
|
9855
9872
|
};
|
|
9856
9873
|
var indexCircuitBreaker = new IndexCircuitBreaker();
|
|
9857
9874
|
|
|
9858
|
-
// src/codebase-index/
|
|
9859
|
-
import {
|
|
9860
|
-
import
|
|
9861
|
-
import * as
|
|
9862
|
-
import
|
|
9863
|
-
import
|
|
9864
|
-
import {
|
|
9865
|
-
DEFAULT_WALK_IGNORE_DIRS,
|
|
9866
|
-
indexParallelBatchSize,
|
|
9867
|
-
isFrugalPerf
|
|
9868
|
-
} from "@wrongstack/core/utils";
|
|
9869
|
-
|
|
9870
|
-
// src/codebase-index/gitignore.ts
|
|
9871
|
-
import * as fs9 from "node:fs/promises";
|
|
9872
|
-
import * as path13 from "node:path";
|
|
9873
|
-
import { compileGlob } from "@wrongstack/core/utils";
|
|
9874
|
-
function globBody(glob) {
|
|
9875
|
-
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
9876
|
-
}
|
|
9877
|
-
function compileGitignore(lines) {
|
|
9878
|
-
const rules = [];
|
|
9879
|
-
for (const raw of lines) {
|
|
9880
|
-
let line = raw.replace(/\r$/, "");
|
|
9881
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
9882
|
-
line = line.trim();
|
|
9883
|
-
let negated = false;
|
|
9884
|
-
if (line.startsWith("!")) {
|
|
9885
|
-
negated = true;
|
|
9886
|
-
line = line.slice(1);
|
|
9887
|
-
}
|
|
9888
|
-
let dirOnly = false;
|
|
9889
|
-
if (line.endsWith("/")) {
|
|
9890
|
-
dirOnly = true;
|
|
9891
|
-
line = line.slice(0, -1);
|
|
9892
|
-
}
|
|
9893
|
-
if (!line) continue;
|
|
9894
|
-
const anchored = line.startsWith("/") || line.includes("/");
|
|
9895
|
-
if (line.startsWith("/")) line = line.slice(1);
|
|
9896
|
-
const body = globBody(line);
|
|
9897
|
-
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
9898
|
-
rules.push({
|
|
9899
|
-
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
9900
|
-
under: new RegExp(`${prefix}${body}/.*$`),
|
|
9901
|
-
negated,
|
|
9902
|
-
dirOnly
|
|
9903
|
-
});
|
|
9904
|
-
}
|
|
9905
|
-
return (relPath, isDir) => {
|
|
9906
|
-
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
9907
|
-
let ignored = false;
|
|
9908
|
-
for (const r of rules) {
|
|
9909
|
-
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
9910
|
-
if (re.test(p)) ignored = !r.negated;
|
|
9911
|
-
}
|
|
9912
|
-
return ignored;
|
|
9913
|
-
};
|
|
9914
|
-
}
|
|
9915
|
-
async function loadGitignoreMatcher(projectRoot) {
|
|
9916
|
-
let lines = [];
|
|
9917
|
-
try {
|
|
9918
|
-
const raw = await fs9.readFile(path13.join(projectRoot, ".gitignore"), "utf8");
|
|
9919
|
-
lines = raw.split("\n");
|
|
9920
|
-
} catch {
|
|
9921
|
-
}
|
|
9922
|
-
return compileGitignore(lines);
|
|
9923
|
-
}
|
|
9924
|
-
|
|
9925
|
-
// src/codebase-index/indexer.ts
|
|
9926
|
-
init_languages2();
|
|
9927
|
-
|
|
9928
|
-
// src/codebase-index/parser-dispatch.ts
|
|
9929
|
-
async function parseFileContent(file, content, lang) {
|
|
9930
|
-
switch (lang) {
|
|
9931
|
-
case "ts":
|
|
9932
|
-
case "tsx":
|
|
9933
|
-
case "js":
|
|
9934
|
-
case "jsx": {
|
|
9935
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
9936
|
-
return parseSymbols8({ file, content, lang });
|
|
9937
|
-
}
|
|
9938
|
-
case "go": {
|
|
9939
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
9940
|
-
return parseSymbols8({ file, content, lang: "go" });
|
|
9941
|
-
}
|
|
9942
|
-
case "py": {
|
|
9943
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
9944
|
-
return parseSymbols8({ file, content, lang: "py" });
|
|
9945
|
-
}
|
|
9946
|
-
case "rs": {
|
|
9947
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
9948
|
-
return parseSymbols8({ file, content, lang: "rs" });
|
|
9949
|
-
}
|
|
9950
|
-
case "json": {
|
|
9951
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
9952
|
-
return parseSymbols8({ file, content, lang: "json" });
|
|
9953
|
-
}
|
|
9954
|
-
case "yaml": {
|
|
9955
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
9956
|
-
return parseSymbols8({ file, content, lang: "yaml" });
|
|
9957
|
-
}
|
|
9958
|
-
default: {
|
|
9959
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
9960
|
-
return parseSymbols8({ file, content, lang });
|
|
9961
|
-
}
|
|
9962
|
-
}
|
|
9963
|
-
}
|
|
9875
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
9876
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9877
|
+
import * as fs11 from "node:fs";
|
|
9878
|
+
import * as os5 from "node:os";
|
|
9879
|
+
import * as path16 from "node:path";
|
|
9880
|
+
import { fileURLToPath } from "node:url";
|
|
9881
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
9964
9882
|
|
|
9965
9883
|
// src/codebase-index/writer.ts
|
|
9966
|
-
import { expectDefined as
|
|
9967
|
-
import * as
|
|
9968
|
-
import * as
|
|
9884
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
9885
|
+
import * as fs10 from "node:fs";
|
|
9886
|
+
import * as path15 from "node:path";
|
|
9969
9887
|
|
|
9970
9888
|
// src/codebase-index/bm25.ts
|
|
9971
9889
|
var K1 = 1.5;
|
|
@@ -10160,8 +10078,8 @@ function runSqliteWithRetry(fn) {
|
|
|
10160
10078
|
}
|
|
10161
10079
|
|
|
10162
10080
|
// src/codebase-index/writer-admin.ts
|
|
10163
|
-
import * as
|
|
10164
|
-
import * as
|
|
10081
|
+
import * as fs9 from "node:fs";
|
|
10082
|
+
import * as path13 from "node:path";
|
|
10165
10083
|
var DB_FILE = "index.db";
|
|
10166
10084
|
function getAllIndexableWithStatement(stmt) {
|
|
10167
10085
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10220,7 +10138,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10220
10138
|
}
|
|
10221
10139
|
function getIndexDbSizeBytes(indexDir) {
|
|
10222
10140
|
try {
|
|
10223
|
-
return
|
|
10141
|
+
return fs9.statSync(path13.join(indexDir, DB_FILE)).size;
|
|
10224
10142
|
} catch {
|
|
10225
10143
|
return 0;
|
|
10226
10144
|
}
|
|
@@ -10287,7 +10205,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
10287
10205
|
}
|
|
10288
10206
|
|
|
10289
10207
|
// src/codebase-index/writer-graph-helpers.ts
|
|
10290
|
-
import * as
|
|
10208
|
+
import * as path14 from "node:path";
|
|
10291
10209
|
function derivePackage(filePath) {
|
|
10292
10210
|
const f = filePath.replace(/\\/g, "/");
|
|
10293
10211
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -10402,16 +10320,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10402
10320
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10403
10321
|
if (!moduleName.startsWith(".")) return void 0;
|
|
10404
10322
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10405
|
-
const absolute =
|
|
10406
|
-
|
|
10323
|
+
const absolute = path14.posix.normalize(
|
|
10324
|
+
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10407
10325
|
);
|
|
10408
|
-
const extension =
|
|
10326
|
+
const extension = path14.posix.extname(absolute);
|
|
10409
10327
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10410
10328
|
const candidates = [
|
|
10411
10329
|
absolute,
|
|
10412
10330
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10413
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10414
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10331
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10332
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10415
10333
|
];
|
|
10416
10334
|
const indexedByPortablePath = new Map(
|
|
10417
10335
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -10927,9 +10845,9 @@ var IndexStore = class _IndexStore {
|
|
|
10927
10845
|
}
|
|
10928
10846
|
constructor(projectRoot, opts = {}) {
|
|
10929
10847
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
10930
|
-
|
|
10848
|
+
fs10.mkdirSync(this.indexDir, { recursive: true });
|
|
10931
10849
|
const Database = loadDatabaseSync();
|
|
10932
|
-
this.db = new Database(
|
|
10850
|
+
this.db = new Database(path15.join(this.indexDir, DB_FILE2));
|
|
10933
10851
|
applyIndexStorePragmas(this.db);
|
|
10934
10852
|
this.initSchema();
|
|
10935
10853
|
}
|
|
@@ -11344,13 +11262,13 @@ var IndexStore = class _IndexStore {
|
|
|
11344
11262
|
if (rankDiff !== 0) return rankDiff;
|
|
11345
11263
|
const scoreDiff = b.score - a.score;
|
|
11346
11264
|
if (scoreDiff !== 0) return scoreDiff;
|
|
11347
|
-
const left =
|
|
11348
|
-
const right =
|
|
11265
|
+
const left = expectDefined2(candidateById.get(a.id));
|
|
11266
|
+
const right = expectDefined2(candidateById.get(b.id));
|
|
11349
11267
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
11350
11268
|
});
|
|
11351
11269
|
const qTokens = tokenise(query);
|
|
11352
11270
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
11353
|
-
const c =
|
|
11271
|
+
const c = expectDefined2(candidateById.get(id));
|
|
11354
11272
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
11355
11273
|
});
|
|
11356
11274
|
return { results, total: candidates.length };
|
|
@@ -11764,1134 +11682,1274 @@ var indexStorePool = new StorePool(
|
|
|
11764
11682
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
11765
11683
|
);
|
|
11766
11684
|
|
|
11767
|
-
// src/codebase-index/
|
|
11768
|
-
var
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
function
|
|
11773
|
-
|
|
11685
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
11686
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
11687
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
11688
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
11689
|
+
var buildIdCache;
|
|
11690
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
11691
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path16.resolve(entrypoint);
|
|
11692
|
+
try {
|
|
11693
|
+
const stat17 = fs11.statSync(file);
|
|
11694
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat17.mtimeMs && buildIdCache.size === stat17.size) {
|
|
11695
|
+
return buildIdCache.buildId;
|
|
11696
|
+
}
|
|
11697
|
+
const buildId = createHash4("sha256").update(fs11.readFileSync(file)).digest("hex").slice(0, 24);
|
|
11698
|
+
buildIdCache = { file, mtimeMs: stat17.mtimeMs, size: stat17.size, buildId };
|
|
11699
|
+
return buildId;
|
|
11700
|
+
} catch {
|
|
11701
|
+
return `unreadable:${path16.basename(file)}`;
|
|
11702
|
+
}
|
|
11774
11703
|
}
|
|
11775
|
-
function
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
11704
|
+
function normalizeLocalPath(value) {
|
|
11705
|
+
const resolved = path16.resolve(value);
|
|
11706
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11779
11707
|
}
|
|
11780
|
-
function
|
|
11781
|
-
|
|
11708
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
11709
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
11710
|
+
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
11782
11711
|
}
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
const rel = path22.relative(projectRoot, file);
|
|
11790
|
-
return rel !== "" && !rel.startsWith(`..${path22.sep}`) && rel !== ".." && !path22.isAbsolute(rel);
|
|
11712
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
11713
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
11714
|
+
if (process.platform === "win32") {
|
|
11715
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
11716
|
+
}
|
|
11717
|
+
return path16.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
11791
11718
|
}
|
|
11792
|
-
function
|
|
11793
|
-
|
|
11794
|
-
|
|
11719
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
11720
|
+
return path16.join(
|
|
11721
|
+
path16.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
11722
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
11723
|
+
);
|
|
11795
11724
|
}
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11725
|
+
|
|
11726
|
+
// src/codebase-index/project-server-protocol.ts
|
|
11727
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
11728
|
+
function encodeProjectServerMessage(message) {
|
|
11729
|
+
return `${JSON.stringify(message)}
|
|
11730
|
+
`;
|
|
11799
11731
|
}
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
])
|
|
11833
|
-
]);
|
|
11834
|
-
throwIfAborted(signal);
|
|
11835
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
11836
|
-
const deleted = /* @__PURE__ */ new Set();
|
|
11837
|
-
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
11838
|
-
for (let i = 0; i < statusRecords.length; i++) {
|
|
11839
|
-
const record = statusRecords[i];
|
|
11840
|
-
if (!record) continue;
|
|
11841
|
-
const status = record.slice(0, 2);
|
|
11842
|
-
const changedPath = path22.resolve(projectRoot, record.slice(3));
|
|
11843
|
-
dirty.add(changedPath);
|
|
11844
|
-
if (status.includes("D")) deleted.add(changedPath);
|
|
11845
|
-
if (status.includes("R") || status.includes("C")) {
|
|
11846
|
-
const source = statusRecords[++i];
|
|
11847
|
-
if (source) dirty.add(path22.resolve(projectRoot, source));
|
|
11732
|
+
|
|
11733
|
+
// src/codebase-index/project-server-client.ts
|
|
11734
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
11735
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
11736
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
11737
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
11738
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
11739
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
11740
|
+
constructor(message, pid) {
|
|
11741
|
+
super(message);
|
|
11742
|
+
this.pid = pid;
|
|
11743
|
+
}
|
|
11744
|
+
pid;
|
|
11745
|
+
name = "StaleProjectIndexServerError";
|
|
11746
|
+
};
|
|
11747
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
11748
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
11749
|
+
var latestConnectionState = {
|
|
11750
|
+
status: "offline",
|
|
11751
|
+
connected: false
|
|
11752
|
+
};
|
|
11753
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
11754
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
11755
|
+
return { kind: "inline-requested" };
|
|
11756
|
+
}
|
|
11757
|
+
let builtUrl = null;
|
|
11758
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
11759
|
+
try {
|
|
11760
|
+
const url = new URL(rel, import.meta.url);
|
|
11761
|
+
if (url.protocol === "file:" && fs12.existsSync(fileURLToPath2(url))) {
|
|
11762
|
+
builtUrl = url;
|
|
11763
|
+
break;
|
|
11848
11764
|
}
|
|
11765
|
+
} catch {
|
|
11849
11766
|
}
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11767
|
+
}
|
|
11768
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
11769
|
+
if (projectRoot !== void 0) {
|
|
11770
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
11771
|
+
const check = checkUnixSocketPath(endpoint);
|
|
11772
|
+
if (!check.ok) {
|
|
11773
|
+
return {
|
|
11774
|
+
kind: "endpoint-invalid",
|
|
11775
|
+
endpoint,
|
|
11776
|
+
byteLength: check.byteLength,
|
|
11777
|
+
maxBytes: check.maxBytes
|
|
11778
|
+
};
|
|
11861
11779
|
}
|
|
11862
|
-
return {
|
|
11863
|
-
files,
|
|
11864
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
11865
|
-
};
|
|
11866
|
-
} catch {
|
|
11867
|
-
return null;
|
|
11868
11780
|
}
|
|
11781
|
+
return { kind: "available", url: builtUrl };
|
|
11869
11782
|
}
|
|
11870
|
-
|
|
11871
|
-
const
|
|
11872
|
-
|
|
11783
|
+
function resolveProjectServerUrl() {
|
|
11784
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
11785
|
+
return availability.kind === "available" ? availability.url : null;
|
|
11786
|
+
}
|
|
11787
|
+
function projectIndexServerExpectedBuildId() {
|
|
11788
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
11789
|
+
if (override) return override;
|
|
11790
|
+
const url = resolveProjectServerUrl();
|
|
11791
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
11792
|
+
}
|
|
11793
|
+
function isProjectIndexServerAvailable() {
|
|
11794
|
+
return resolveProjectServerUrl() !== null;
|
|
11795
|
+
}
|
|
11796
|
+
function publishConnectionState(endpoint, state) {
|
|
11797
|
+
connectionStates.set(endpoint, state);
|
|
11798
|
+
latestConnectionState = state;
|
|
11799
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
11800
|
+
}
|
|
11801
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
11802
|
+
if (projectRoot) {
|
|
11803
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
11804
|
+
const existing = connectionStates.get(endpoint);
|
|
11805
|
+
if (existing) return existing;
|
|
11806
|
+
if (!isProjectIndexServerAvailable()) {
|
|
11807
|
+
return { status: "unavailable", connected: false };
|
|
11808
|
+
}
|
|
11873
11809
|
return {
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11810
|
+
status: "offline",
|
|
11811
|
+
connected: false,
|
|
11812
|
+
projectRoot,
|
|
11813
|
+
indexDir,
|
|
11814
|
+
endpoint
|
|
11878
11815
|
};
|
|
11879
11816
|
}
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
11884
|
-
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
11885
|
-
let dirCount = 0;
|
|
11886
|
-
const walk = async (dir) => {
|
|
11887
|
-
throwIfAborted(signal);
|
|
11888
|
-
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
11889
|
-
await yieldEventLoop();
|
|
11890
|
-
throwIfAborted(signal);
|
|
11891
|
-
}
|
|
11892
|
-
let entries;
|
|
11893
|
-
try {
|
|
11894
|
-
entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
11895
|
-
} catch (err) {
|
|
11896
|
-
complete = false;
|
|
11897
|
-
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11898
|
-
return;
|
|
11899
|
-
}
|
|
11900
|
-
dirCount++;
|
|
11901
|
-
for (const e of entries) {
|
|
11902
|
-
if (ignoreSet.has(e.name)) continue;
|
|
11903
|
-
const full = path22.join(dir, e.name);
|
|
11904
|
-
const rel = path22.relative(projectRoot, full).replace(/\\/g, "/");
|
|
11905
|
-
if (e.isDirectory()) {
|
|
11906
|
-
if (isGitIgnored(rel, true)) continue;
|
|
11907
|
-
await walk(full);
|
|
11908
|
-
} else if (e.isFile()) {
|
|
11909
|
-
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
11910
|
-
const ext = path22.extname(e.name).toLowerCase();
|
|
11911
|
-
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
11912
|
-
results.push(full);
|
|
11913
|
-
}
|
|
11914
|
-
}
|
|
11915
|
-
}
|
|
11916
|
-
};
|
|
11917
|
-
await walk(projectRoot);
|
|
11918
|
-
return { files: results, complete, errors };
|
|
11817
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
11818
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
11819
|
+
return latestConnectionState;
|
|
11919
11820
|
}
|
|
11920
|
-
function
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11924
|
-
const assigned = [];
|
|
11925
|
-
for (const ref of refs) {
|
|
11926
|
-
let owner2;
|
|
11927
|
-
for (const symbol of ordered) {
|
|
11928
|
-
if (symbol.line > ref.line) break;
|
|
11929
|
-
owner2 = symbol;
|
|
11930
|
-
}
|
|
11931
|
-
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
11932
|
-
if (!owner2 || owner2.id <= 0) continue;
|
|
11933
|
-
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
11934
|
-
if (seen.has(key)) continue;
|
|
11935
|
-
seen.add(key);
|
|
11936
|
-
assigned.push({ ...ref, fromId: owner2.id });
|
|
11937
|
-
}
|
|
11938
|
-
return assigned;
|
|
11821
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
11822
|
+
connectionStateListeners.add(listener);
|
|
11823
|
+
return () => connectionStateListeners.delete(listener);
|
|
11939
11824
|
}
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
const
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
const
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11825
|
+
function remoteError(message, name) {
|
|
11826
|
+
if (name === "LockError") return new LockError(message);
|
|
11827
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
11828
|
+
const error = new Error(message);
|
|
11829
|
+
if (name && name !== "Error") error.name = name;
|
|
11830
|
+
return error;
|
|
11831
|
+
}
|
|
11832
|
+
function isProjectIndexServerHealth(value) {
|
|
11833
|
+
if (!value || typeof value !== "object") return false;
|
|
11834
|
+
const health = value;
|
|
11835
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
11836
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
11837
|
+
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";
|
|
11838
|
+
}
|
|
11839
|
+
function delay(ms) {
|
|
11840
|
+
return new Promise((resolve16) => {
|
|
11841
|
+
const timer = setTimeout(resolve16, ms);
|
|
11842
|
+
timer.unref?.();
|
|
11843
|
+
});
|
|
11844
|
+
}
|
|
11845
|
+
function cancellationError(signal) {
|
|
11846
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
11847
|
+
}
|
|
11848
|
+
var ProjectServerConnection = class {
|
|
11849
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
11850
|
+
this.projectRoot = projectRoot;
|
|
11851
|
+
this.indexDir = indexDir;
|
|
11852
|
+
this.endpoint = endpoint;
|
|
11853
|
+
this.transition("offline");
|
|
11969
11854
|
}
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11855
|
+
projectRoot;
|
|
11856
|
+
indexDir;
|
|
11857
|
+
endpoint;
|
|
11858
|
+
socket = null;
|
|
11859
|
+
buffer = "";
|
|
11860
|
+
info = null;
|
|
11861
|
+
activity = null;
|
|
11862
|
+
health = null;
|
|
11863
|
+
healthCheck = null;
|
|
11864
|
+
connecting = null;
|
|
11865
|
+
connectResolve = null;
|
|
11866
|
+
connectReject = null;
|
|
11867
|
+
nextId = 1;
|
|
11868
|
+
pending = /* @__PURE__ */ new Map();
|
|
11869
|
+
transition(status, options = {}) {
|
|
11870
|
+
const previous = connectionStates.get(this.endpoint);
|
|
11871
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
11872
|
+
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);
|
|
11873
|
+
publishConnectionState(this.endpoint, {
|
|
11874
|
+
status,
|
|
11875
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
11876
|
+
projectRoot: this.projectRoot,
|
|
11877
|
+
indexDir: this.indexDir,
|
|
11878
|
+
endpoint: this.endpoint,
|
|
11879
|
+
pid,
|
|
11880
|
+
lastError,
|
|
11881
|
+
...this.activity ? { activity: this.activity } : {},
|
|
11882
|
+
...this.health ? { health: this.health } : {}
|
|
11975
11883
|
});
|
|
11976
11884
|
}
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
if (!force) {
|
|
11980
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
11885
|
+
isConnected() {
|
|
11886
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
11981
11887
|
}
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
files = files.filter((file) => {
|
|
11986
|
-
const meta = existingMeta.get(file);
|
|
11987
|
-
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
11988
|
-
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
11989
|
-
symbolsIndexed += meta.symbolCount;
|
|
11990
|
-
filesIndexed++;
|
|
11991
|
-
filesPreSkipped++;
|
|
11992
|
-
return false;
|
|
11993
|
-
});
|
|
11994
|
-
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
11888
|
+
/** Safe LRU candidate: no request, connect, or health probe is in flight. */
|
|
11889
|
+
isEvictable() {
|
|
11890
|
+
return this.pending.size === 0 && this.connecting === null && this.healthCheck === null;
|
|
11995
11891
|
}
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
const
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
11892
|
+
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
11893
|
+
await this.ensureConnected(spawnIfMissing);
|
|
11894
|
+
if (this.healthCheck) return this.healthCheck;
|
|
11895
|
+
const startedAt = Date.now();
|
|
11896
|
+
this.healthCheck = this.request({ type: "ping" }, { timeoutMs }).then((server) => {
|
|
11897
|
+
const now = Date.now();
|
|
11898
|
+
this.health = {
|
|
11899
|
+
status: "healthy",
|
|
11900
|
+
checkedAt: now,
|
|
11901
|
+
lastHealthyAt: now,
|
|
11902
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
11903
|
+
missedHeartbeats: 0,
|
|
11904
|
+
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
11905
|
+
};
|
|
11906
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
11907
|
+
return this.health;
|
|
11908
|
+
}).catch((error) => {
|
|
11909
|
+
if (!this.isConnected()) throw error;
|
|
11910
|
+
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
11911
|
+
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
11912
|
+
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
11913
|
+
this.health = {
|
|
11914
|
+
status,
|
|
11915
|
+
checkedAt: Date.now(),
|
|
11916
|
+
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
11917
|
+
latencyMs: null,
|
|
11918
|
+
missedHeartbeats,
|
|
11919
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
11920
|
+
};
|
|
11921
|
+
this.transition(status, { pid: this.info?.pid, error });
|
|
11922
|
+
return this.health;
|
|
11923
|
+
}).finally(() => {
|
|
11924
|
+
this.healthCheck = null;
|
|
11925
|
+
});
|
|
11926
|
+
return this.healthCheck;
|
|
11927
|
+
}
|
|
11928
|
+
markResponsive() {
|
|
11929
|
+
const now = Date.now();
|
|
11930
|
+
this.health = {
|
|
11931
|
+
status: "healthy",
|
|
11932
|
+
checkedAt: now,
|
|
11933
|
+
lastHealthyAt: now,
|
|
11934
|
+
latencyMs: this.health?.latencyMs ?? null,
|
|
11935
|
+
missedHeartbeats: 0,
|
|
11936
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
11937
|
+
};
|
|
11938
|
+
}
|
|
11939
|
+
async call(op, args, options) {
|
|
11940
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
11941
|
+
await this.ensureConnected(true);
|
|
11942
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
11943
|
+
return this.request({ type: "request", op, args }, options);
|
|
11944
|
+
}
|
|
11945
|
+
async shutdownRemote(reason) {
|
|
11946
|
+
try {
|
|
11947
|
+
await this.ensureConnected(false);
|
|
11948
|
+
} catch {
|
|
11949
|
+
return { stopped: false, reason: "not-running" };
|
|
12010
11950
|
}
|
|
12011
|
-
const
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
lang,
|
|
12037
|
-
parsed: null,
|
|
12038
|
-
error: `file too large (${stat17.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
12039
|
-
};
|
|
12040
|
-
}
|
|
12041
|
-
const meta = existingMeta.get(file);
|
|
12042
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat17.mtimeMs)) {
|
|
12043
|
-
return { file, stat: stat17, lang, parsed: null, skippedMeta: meta };
|
|
12044
|
-
}
|
|
12045
|
-
let content;
|
|
12046
|
-
try {
|
|
12047
|
-
content = await fs15.readFile(file, { encoding: "utf8", signal });
|
|
12048
|
-
} catch (e) {
|
|
12049
|
-
if (isAbortError(e)) throw e;
|
|
12050
|
-
return {
|
|
12051
|
-
file,
|
|
12052
|
-
stat: stat17,
|
|
12053
|
-
lang,
|
|
12054
|
-
parsed: null,
|
|
12055
|
-
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
12056
|
-
};
|
|
12057
|
-
}
|
|
12058
|
-
let parsed;
|
|
12059
|
-
try {
|
|
12060
|
-
parsed = await parseFileContent(file, content, lang);
|
|
12061
|
-
} catch (e) {
|
|
12062
|
-
return {
|
|
12063
|
-
file,
|
|
12064
|
-
stat: stat17,
|
|
12065
|
-
lang,
|
|
12066
|
-
parsed: null,
|
|
12067
|
-
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
12068
|
-
};
|
|
12069
|
-
}
|
|
12070
|
-
return { file, stat: stat17, lang, parsed, content };
|
|
12071
|
-
}
|
|
12072
|
-
)
|
|
11951
|
+
const pid = this.info?.pid;
|
|
11952
|
+
try {
|
|
11953
|
+
this.transition("stopping", { pid });
|
|
11954
|
+
await this.request(
|
|
11955
|
+
{ type: "shutdown", reason },
|
|
11956
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
11957
|
+
);
|
|
11958
|
+
return { stopped: true, pid };
|
|
11959
|
+
} catch (error) {
|
|
11960
|
+
const forceKilled = this.forceKillKnownServer();
|
|
11961
|
+
return {
|
|
11962
|
+
stopped: forceKilled,
|
|
11963
|
+
pid,
|
|
11964
|
+
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
11965
|
+
};
|
|
11966
|
+
} finally {
|
|
11967
|
+
this.close();
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
async configure(watchExternal, debounceMs) {
|
|
11971
|
+
await this.ensureConnected(true);
|
|
11972
|
+
const startedAt = Date.now();
|
|
11973
|
+
const result = await this.request(
|
|
11974
|
+
{ type: "configure", watchExternal, debounceMs },
|
|
11975
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
12073
11976
|
);
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
11977
|
+
if (isProjectIndexServerHealth(result.health)) {
|
|
11978
|
+
const now = Date.now();
|
|
11979
|
+
this.health = {
|
|
11980
|
+
status: "healthy",
|
|
11981
|
+
checkedAt: now,
|
|
11982
|
+
lastHealthyAt: now,
|
|
11983
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
11984
|
+
missedHeartbeats: 0,
|
|
11985
|
+
server: result.health
|
|
11986
|
+
};
|
|
11987
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
11988
|
+
}
|
|
11989
|
+
}
|
|
11990
|
+
close() {
|
|
11991
|
+
const socket = this.socket;
|
|
11992
|
+
this.socket = null;
|
|
11993
|
+
this.info = null;
|
|
11994
|
+
this.activity = null;
|
|
11995
|
+
this.health = null;
|
|
11996
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
11997
|
+
this.connectResolve = null;
|
|
11998
|
+
this.connectReject = null;
|
|
11999
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
12000
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
12001
|
+
this.transition("offline");
|
|
12002
|
+
maybeStopHeartbeatLoop();
|
|
12003
|
+
}
|
|
12004
|
+
request(message, options) {
|
|
12005
|
+
const socket = this.socket;
|
|
12006
|
+
if (!socket || socket.destroyed) {
|
|
12007
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
12008
|
+
}
|
|
12009
|
+
const id = this.nextId++;
|
|
12010
|
+
return new Promise((resolve16, reject) => {
|
|
12011
|
+
const timer = setTimeout(() => {
|
|
12012
|
+
const entry = this.pending.get(id);
|
|
12013
|
+
if (!entry) return;
|
|
12014
|
+
this.pending.delete(id);
|
|
12015
|
+
this.write({ type: "cancel", id });
|
|
12016
|
+
const error = new IndexTimeoutError(
|
|
12017
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
12018
|
+
);
|
|
12019
|
+
this.cleanupPending(entry);
|
|
12020
|
+
entry.reject(error);
|
|
12021
|
+
}, options.timeoutMs);
|
|
12022
|
+
timer.unref?.();
|
|
12023
|
+
const signal = options.signal;
|
|
12024
|
+
const onAbort = signal ? () => {
|
|
12025
|
+
const entry = this.pending.get(id);
|
|
12026
|
+
if (!entry) return;
|
|
12027
|
+
this.pending.delete(id);
|
|
12028
|
+
this.write({ type: "cancel", id });
|
|
12029
|
+
this.cleanupPending(entry);
|
|
12030
|
+
entry.reject(cancellationError(signal));
|
|
12031
|
+
} : void 0;
|
|
12032
|
+
this.pending.set(id, {
|
|
12033
|
+
resolve: resolve16,
|
|
12034
|
+
reject,
|
|
12035
|
+
timer,
|
|
12036
|
+
signal,
|
|
12037
|
+
onAbort,
|
|
12038
|
+
onProgress: options.onProgress
|
|
12039
|
+
});
|
|
12040
|
+
if (signal && onAbort) {
|
|
12041
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
12042
|
+
if (signal.aborted) {
|
|
12043
|
+
onAbort();
|
|
12044
|
+
return;
|
|
12045
|
+
}
|
|
12084
12046
|
}
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12047
|
+
this.write({ ...message, id });
|
|
12048
|
+
});
|
|
12049
|
+
}
|
|
12050
|
+
async ensureConnected(spawnIfMissing) {
|
|
12051
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
12052
|
+
if (this.connecting) return this.connecting;
|
|
12053
|
+
this.transition("connecting");
|
|
12054
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
12055
|
+
this.transition("error", { error });
|
|
12056
|
+
throw error;
|
|
12057
|
+
}).finally(() => {
|
|
12058
|
+
this.connecting = null;
|
|
12059
|
+
});
|
|
12060
|
+
return this.connecting;
|
|
12061
|
+
}
|
|
12062
|
+
async connectWithElection(spawnIfMissing) {
|
|
12063
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12064
|
+
let spawned = false;
|
|
12065
|
+
let staleAttempts = 0;
|
|
12066
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
12067
|
+
while (Date.now() < deadline) {
|
|
12068
|
+
try {
|
|
12069
|
+
await this.connectOnce();
|
|
12070
|
+
return;
|
|
12071
|
+
} catch (error) {
|
|
12072
|
+
lastError = error;
|
|
12073
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
12074
|
+
staleAttempts++;
|
|
12075
|
+
if (!spawnIfMissing) break;
|
|
12076
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12077
|
+
spawned = false;
|
|
12078
|
+
await delay(100);
|
|
12079
|
+
continue;
|
|
12080
|
+
}
|
|
12090
12081
|
}
|
|
12091
|
-
|
|
12092
|
-
if (
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
filesIndexed++;
|
|
12096
|
-
continue;
|
|
12082
|
+
if (!spawnIfMissing) break;
|
|
12083
|
+
if (!spawned) {
|
|
12084
|
+
this.spawnDetachedServer();
|
|
12085
|
+
spawned = true;
|
|
12097
12086
|
}
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12087
|
+
await delay(75);
|
|
12088
|
+
}
|
|
12089
|
+
throw lastError;
|
|
12090
|
+
}
|
|
12091
|
+
connectOnce() {
|
|
12092
|
+
this.socket?.destroy();
|
|
12093
|
+
this.socket = null;
|
|
12094
|
+
this.info = null;
|
|
12095
|
+
this.activity = null;
|
|
12096
|
+
this.health = null;
|
|
12097
|
+
this.buffer = "";
|
|
12098
|
+
return new Promise((resolve16, reject) => {
|
|
12099
|
+
const socket = net3.createConnection(this.endpoint);
|
|
12100
|
+
this.socket = socket;
|
|
12101
|
+
socket.setEncoding("utf8");
|
|
12102
|
+
const timer = setTimeout(() => {
|
|
12103
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
12104
|
+
socket.destroy();
|
|
12105
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12106
|
+
timer.unref?.();
|
|
12107
|
+
const finishResolve = () => {
|
|
12108
|
+
clearTimeout(timer);
|
|
12109
|
+
this.connectResolve = null;
|
|
12110
|
+
this.connectReject = null;
|
|
12111
|
+
resolve16();
|
|
12112
|
+
};
|
|
12113
|
+
const finishReject = (error) => {
|
|
12114
|
+
clearTimeout(timer);
|
|
12115
|
+
this.connectResolve = null;
|
|
12116
|
+
this.connectReject = null;
|
|
12117
|
+
reject(error);
|
|
12118
|
+
};
|
|
12119
|
+
this.connectResolve = finishResolve;
|
|
12120
|
+
this.connectReject = finishReject;
|
|
12121
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
12122
|
+
socket.on("error", (error) => {
|
|
12123
|
+
if (!this.info) finishReject(error);
|
|
12124
|
+
});
|
|
12125
|
+
socket.on("close", () => this.onClose(socket));
|
|
12126
|
+
});
|
|
12127
|
+
}
|
|
12128
|
+
onData(socket, chunk) {
|
|
12129
|
+
if (socket !== this.socket) return;
|
|
12130
|
+
this.buffer += chunk;
|
|
12131
|
+
while (true) {
|
|
12132
|
+
const newline = this.buffer.indexOf("\n");
|
|
12133
|
+
if (newline < 0) {
|
|
12134
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12135
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12108
12136
|
}
|
|
12109
|
-
|
|
12137
|
+
return;
|
|
12110
12138
|
}
|
|
12111
|
-
if (
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
lang,
|
|
12115
|
-
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12116
|
-
symbolCount: 0,
|
|
12117
|
-
lastIndexed: Date.now()
|
|
12118
|
-
});
|
|
12119
|
-
filesIndexed++;
|
|
12120
|
-
continue;
|
|
12139
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12140
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12141
|
+
return;
|
|
12121
12142
|
}
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12143
|
+
const line = this.buffer.slice(0, newline);
|
|
12144
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
12145
|
+
if (!line) continue;
|
|
12146
|
+
let message;
|
|
12147
|
+
try {
|
|
12148
|
+
message = JSON.parse(line);
|
|
12149
|
+
} catch {
|
|
12150
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12151
|
+
return;
|
|
12152
|
+
}
|
|
12153
|
+
this.onMessage(message);
|
|
12131
12154
|
}
|
|
12132
|
-
|
|
12155
|
+
}
|
|
12156
|
+
onMessage(message) {
|
|
12157
|
+
if (message.type === "hello") {
|
|
12158
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
12159
|
+
this.rejectStaleServer(
|
|
12160
|
+
message,
|
|
12161
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
12162
|
+
);
|
|
12163
|
+
return;
|
|
12164
|
+
}
|
|
12165
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
12166
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
12167
|
+
this.rejectStaleServer(
|
|
12168
|
+
message,
|
|
12169
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
12170
|
+
);
|
|
12171
|
+
return;
|
|
12172
|
+
}
|
|
12173
|
+
this.info = message;
|
|
12174
|
+
this.markResponsive();
|
|
12175
|
+
this.transition("connected", { pid: message.pid });
|
|
12176
|
+
ensureHeartbeatLoop();
|
|
12177
|
+
this.connectResolve?.();
|
|
12178
|
+
return;
|
|
12179
|
+
}
|
|
12180
|
+
if (message.type === "index-state") {
|
|
12181
|
+
this.activity = message.state;
|
|
12182
|
+
this.markResponsive();
|
|
12183
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12184
|
+
return;
|
|
12185
|
+
}
|
|
12186
|
+
const entry = this.pending.get(message.id);
|
|
12187
|
+
if (!entry) return;
|
|
12188
|
+
this.markResponsive();
|
|
12189
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
12190
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
12191
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12192
|
+
}
|
|
12193
|
+
if (message.type === "progress") {
|
|
12194
|
+
entry.onProgress?.(message.current, message.total);
|
|
12195
|
+
return;
|
|
12196
|
+
}
|
|
12197
|
+
this.pending.delete(message.id);
|
|
12198
|
+
this.cleanupPending(entry);
|
|
12199
|
+
if (message.ok) entry.resolve(message.result);
|
|
12200
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
12201
|
+
}
|
|
12202
|
+
onClose(socket) {
|
|
12203
|
+
if (socket !== this.socket) return;
|
|
12204
|
+
const wasConnected = this.info !== null;
|
|
12205
|
+
this.socket = null;
|
|
12206
|
+
this.info = null;
|
|
12207
|
+
this.activity = null;
|
|
12208
|
+
this.health = null;
|
|
12209
|
+
const error = new Error("codebase-index server connection closed");
|
|
12210
|
+
this.connectReject?.(error);
|
|
12211
|
+
this.connectResolve = null;
|
|
12212
|
+
this.connectReject = null;
|
|
12213
|
+
this.rejectPending(error);
|
|
12214
|
+
if (wasConnected) this.transition("error", { error });
|
|
12215
|
+
maybeStopHeartbeatLoop();
|
|
12216
|
+
}
|
|
12217
|
+
cleanupPending(entry) {
|
|
12218
|
+
clearTimeout(entry.timer);
|
|
12219
|
+
if (entry.signal && entry.onAbort) {
|
|
12220
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
12221
|
+
}
|
|
12222
|
+
}
|
|
12223
|
+
rejectPending(error) {
|
|
12224
|
+
const entries = [...this.pending.values()];
|
|
12225
|
+
this.pending.clear();
|
|
12226
|
+
for (const entry of entries) {
|
|
12227
|
+
this.cleanupPending(entry);
|
|
12228
|
+
entry.reject(error);
|
|
12229
|
+
}
|
|
12230
|
+
}
|
|
12231
|
+
write(message) {
|
|
12232
|
+
const socket = this.socket;
|
|
12233
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
12234
|
+
}
|
|
12235
|
+
rejectStaleServer(message, reason) {
|
|
12236
|
+
const socket = this.socket;
|
|
12237
|
+
if (socket && !socket.destroyed) {
|
|
12238
|
+
socket.write(
|
|
12239
|
+
encodeProjectServerMessage({
|
|
12240
|
+
type: "shutdown",
|
|
12241
|
+
id: 0,
|
|
12242
|
+
reason: "stale-build-replacement"
|
|
12243
|
+
})
|
|
12244
|
+
);
|
|
12245
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
12246
|
+
timer.unref?.();
|
|
12247
|
+
}
|
|
12248
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12249
|
+
}
|
|
12250
|
+
spawnDetachedServer() {
|
|
12251
|
+
const url = resolveProjectServerUrl();
|
|
12252
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12253
|
+
if (process.platform !== "win32") {
|
|
12133
12254
|
try {
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
const count = entry.symbols.length;
|
|
12137
|
-
symbolsIndexed += count;
|
|
12138
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
12139
|
-
filesIndexed++;
|
|
12140
|
-
}
|
|
12141
|
-
} catch (err) {
|
|
12142
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
12143
|
-
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
12144
|
-
for (const entry of batchEntries) {
|
|
12145
|
-
try {
|
|
12146
|
-
store.deleteRefsForFile(entry.file);
|
|
12147
|
-
store.deleteSymbolsForFile(entry.file);
|
|
12148
|
-
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
12149
|
-
symbolsIndexed += symbolsWithIds.length;
|
|
12150
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
12151
|
-
filesIndexed++;
|
|
12152
|
-
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
12153
|
-
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
12154
|
-
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
12155
|
-
}
|
|
12156
|
-
store.resolveRefsForNames([
|
|
12157
|
-
...entry.symbols.map((symbol) => symbol.name),
|
|
12158
|
-
...entry.refs.map((ref) => ref.toName)
|
|
12159
|
-
]);
|
|
12160
|
-
store.upsertFile({
|
|
12161
|
-
file: entry.file,
|
|
12162
|
-
lang: entry.lang,
|
|
12163
|
-
mtimeMs: entry.mtimeMs,
|
|
12164
|
-
symbolCount: entry.symbolCount,
|
|
12165
|
-
lastIndexed: Date.now()
|
|
12166
|
-
});
|
|
12167
|
-
} catch (innerErr) {
|
|
12168
|
-
errors.push(
|
|
12169
|
-
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
12170
|
-
);
|
|
12171
|
-
}
|
|
12172
|
-
}
|
|
12255
|
+
fs12.rmSync(this.endpoint, { force: true });
|
|
12256
|
+
} catch {
|
|
12173
12257
|
}
|
|
12174
12258
|
}
|
|
12259
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
12260
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
12261
|
+
const child = spawn4(process.execPath, args, {
|
|
12262
|
+
detached: true,
|
|
12263
|
+
stdio: "ignore",
|
|
12264
|
+
windowsHide: true,
|
|
12265
|
+
env: process.env
|
|
12266
|
+
});
|
|
12267
|
+
child.unref();
|
|
12175
12268
|
}
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12269
|
+
forceKillKnownServer() {
|
|
12270
|
+
const pid = this.info?.pid;
|
|
12271
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
12272
|
+
}
|
|
12273
|
+
forceKillServer(pid) {
|
|
12274
|
+
if (pid === process.pid) return false;
|
|
12275
|
+
try {
|
|
12276
|
+
process.kill(pid);
|
|
12277
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12278
|
+
try {
|
|
12279
|
+
const metadata = JSON.parse(fs12.readFileSync(metadataPath, "utf8"));
|
|
12280
|
+
if (metadata.pid === pid) fs12.rmSync(metadataPath, { force: true });
|
|
12281
|
+
} catch {
|
|
12180
12282
|
}
|
|
12283
|
+
return true;
|
|
12284
|
+
} catch {
|
|
12285
|
+
return false;
|
|
12181
12286
|
}
|
|
12182
12287
|
}
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
if (
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
errors
|
|
12288
|
+
};
|
|
12289
|
+
var connections = /* @__PURE__ */ new Map();
|
|
12290
|
+
var MAX_CACHED_CONNECTIONS = 8;
|
|
12291
|
+
var heartbeatTimer;
|
|
12292
|
+
function forgetConnection(endpoint, connection) {
|
|
12293
|
+
if (connections.get(endpoint) === connection) connections.delete(endpoint);
|
|
12294
|
+
connection.close();
|
|
12295
|
+
connectionStates.delete(endpoint);
|
|
12296
|
+
if (latestConnectionState.endpoint !== endpoint) return;
|
|
12297
|
+
latestConnectionState = [...connectionStates.values()].at(-1) ?? {
|
|
12298
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
12299
|
+
connected: false
|
|
12196
12300
|
};
|
|
12197
12301
|
}
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
projectRoot: args.projectRoot,
|
|
12205
|
-
indexDir: args.indexDir,
|
|
12206
|
-
files: args.files,
|
|
12207
|
-
force: args.force,
|
|
12208
|
-
langs: args.langs,
|
|
12209
|
-
ignore: args.ignore,
|
|
12210
|
-
signal: hooks.signal,
|
|
12211
|
-
onProgress: hooks.onProgress
|
|
12212
|
-
});
|
|
12213
|
-
} finally {
|
|
12214
|
-
indexStorePool.release(store);
|
|
12215
|
-
}
|
|
12216
|
-
}
|
|
12217
|
-
function searchService(args) {
|
|
12218
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12219
|
-
try {
|
|
12220
|
-
return store.searchRanked(
|
|
12221
|
-
args.query,
|
|
12222
|
-
{
|
|
12223
|
-
kind: args.kind,
|
|
12224
|
-
lang: args.lang,
|
|
12225
|
-
file: args.file,
|
|
12226
|
-
lspKind: args.lspKind
|
|
12227
|
-
},
|
|
12228
|
-
args.limit
|
|
12229
|
-
);
|
|
12230
|
-
} finally {
|
|
12231
|
-
indexStorePool.release(store);
|
|
12302
|
+
function trimConnectionCache(protectedConnection) {
|
|
12303
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) return;
|
|
12304
|
+
for (const [endpoint, connection] of connections) {
|
|
12305
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) break;
|
|
12306
|
+
if (connection === protectedConnection || !connection.isEvictable()) continue;
|
|
12307
|
+
forgetConnection(endpoint, connection);
|
|
12232
12308
|
}
|
|
12233
12309
|
}
|
|
12234
|
-
function
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12310
|
+
function ensureHeartbeatLoop() {
|
|
12311
|
+
if (heartbeatTimer) return;
|
|
12312
|
+
heartbeatTimer = setInterval(() => {
|
|
12313
|
+
for (const connection of connections.values()) {
|
|
12314
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
12315
|
+
});
|
|
12316
|
+
}
|
|
12317
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
12318
|
+
heartbeatTimer.unref?.();
|
|
12241
12319
|
}
|
|
12242
|
-
function
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
indexStorePool.release(store);
|
|
12248
|
-
}
|
|
12320
|
+
function maybeStopHeartbeatLoop() {
|
|
12321
|
+
if (!heartbeatTimer) return;
|
|
12322
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
12323
|
+
clearInterval(heartbeatTimer);
|
|
12324
|
+
heartbeatTimer = void 0;
|
|
12249
12325
|
}
|
|
12250
|
-
function
|
|
12251
|
-
const
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12326
|
+
function connectionFor(projectRoot, indexDir) {
|
|
12327
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12328
|
+
let connection = connections.get(endpoint);
|
|
12329
|
+
if (!connection) {
|
|
12330
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
12331
|
+
connections.set(endpoint, connection);
|
|
12332
|
+
} else {
|
|
12333
|
+
connections.delete(endpoint);
|
|
12334
|
+
connections.set(endpoint, connection);
|
|
12256
12335
|
}
|
|
12336
|
+
trimConnectionCache(connection);
|
|
12337
|
+
return connection;
|
|
12257
12338
|
}
|
|
12258
|
-
function
|
|
12259
|
-
|
|
12260
|
-
try {
|
|
12261
|
-
return store.getSymbolGraph(args.fileFilter);
|
|
12262
|
-
} finally {
|
|
12263
|
-
indexStorePool.release(store);
|
|
12264
|
-
}
|
|
12339
|
+
function callProjectIndexServer(op, args, options) {
|
|
12340
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
12265
12341
|
}
|
|
12266
12342
|
|
|
12267
|
-
// src/codebase-index/
|
|
12268
|
-
import
|
|
12269
|
-
import
|
|
12270
|
-
import
|
|
12271
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12343
|
+
// src/codebase-index/background-indexer.ts
|
|
12344
|
+
import * as fs18 from "node:fs";
|
|
12345
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12346
|
+
import { Worker } from "node:worker_threads";
|
|
12272
12347
|
|
|
12273
|
-
// src/codebase-index/
|
|
12274
|
-
import {
|
|
12275
|
-
import
|
|
12276
|
-
import * as
|
|
12348
|
+
// src/codebase-index/indexer.ts
|
|
12349
|
+
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12350
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
12351
|
+
import * as fs17 from "node:fs/promises";
|
|
12352
|
+
import { availableParallelism } from "node:os";
|
|
12277
12353
|
import * as path23 from "node:path";
|
|
12278
|
-
import {
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
buildIdCache = { file, mtimeMs: stat17.mtimeMs, size: stat17.size, buildId };
|
|
12291
|
-
return buildId;
|
|
12292
|
-
} catch {
|
|
12293
|
-
return `unreadable:${path23.basename(file)}`;
|
|
12294
|
-
}
|
|
12295
|
-
}
|
|
12296
|
-
function normalizeLocalPath(value) {
|
|
12297
|
-
const resolved = path23.resolve(value);
|
|
12298
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12299
|
-
}
|
|
12300
|
-
function projectIndexServerKey(projectRoot, indexDir) {
|
|
12301
|
-
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
12302
|
-
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
12354
|
+
import {
|
|
12355
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
12356
|
+
indexParallelBatchSize,
|
|
12357
|
+
isFrugalPerf
|
|
12358
|
+
} from "@wrongstack/core/utils";
|
|
12359
|
+
|
|
12360
|
+
// src/codebase-index/gitignore.ts
|
|
12361
|
+
import * as fs13 from "node:fs/promises";
|
|
12362
|
+
import * as path17 from "node:path";
|
|
12363
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
12364
|
+
function globBody(glob) {
|
|
12365
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
12303
12366
|
}
|
|
12304
|
-
function
|
|
12305
|
-
const
|
|
12306
|
-
|
|
12307
|
-
|
|
12367
|
+
function compileGitignore(lines) {
|
|
12368
|
+
const rules = [];
|
|
12369
|
+
for (const raw of lines) {
|
|
12370
|
+
let line = raw.replace(/\r$/, "");
|
|
12371
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
12372
|
+
line = line.trim();
|
|
12373
|
+
let negated = false;
|
|
12374
|
+
if (line.startsWith("!")) {
|
|
12375
|
+
negated = true;
|
|
12376
|
+
line = line.slice(1);
|
|
12377
|
+
}
|
|
12378
|
+
let dirOnly = false;
|
|
12379
|
+
if (line.endsWith("/")) {
|
|
12380
|
+
dirOnly = true;
|
|
12381
|
+
line = line.slice(0, -1);
|
|
12382
|
+
}
|
|
12383
|
+
if (!line) continue;
|
|
12384
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
12385
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
12386
|
+
const body = globBody(line);
|
|
12387
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
12388
|
+
rules.push({
|
|
12389
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
12390
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
12391
|
+
negated,
|
|
12392
|
+
dirOnly
|
|
12393
|
+
});
|
|
12308
12394
|
}
|
|
12309
|
-
return
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12395
|
+
return (relPath, isDir) => {
|
|
12396
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
12397
|
+
let ignored = false;
|
|
12398
|
+
for (const r of rules) {
|
|
12399
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
12400
|
+
if (re.test(p)) ignored = !r.negated;
|
|
12401
|
+
}
|
|
12402
|
+
return ignored;
|
|
12403
|
+
};
|
|
12314
12404
|
}
|
|
12315
|
-
function
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12405
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
12406
|
+
let lines = [];
|
|
12407
|
+
try {
|
|
12408
|
+
const raw = await fs13.readFile(path17.join(projectRoot, ".gitignore"), "utf8");
|
|
12409
|
+
lines = raw.split("\n");
|
|
12410
|
+
} catch {
|
|
12411
|
+
}
|
|
12412
|
+
return compileGitignore(lines);
|
|
12320
12413
|
}
|
|
12321
12414
|
|
|
12322
|
-
// src/codebase-index/
|
|
12323
|
-
|
|
12324
|
-
function encodeProjectServerMessage(message) {
|
|
12325
|
-
return `${JSON.stringify(message)}
|
|
12326
|
-
`;
|
|
12327
|
-
}
|
|
12415
|
+
// src/codebase-index/indexer.ts
|
|
12416
|
+
init_languages2();
|
|
12328
12417
|
|
|
12329
|
-
// src/codebase-index/
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
}
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
};
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12418
|
+
// src/codebase-index/parser-dispatch.ts
|
|
12419
|
+
async function parseFileContent(file, content, lang) {
|
|
12420
|
+
switch (lang) {
|
|
12421
|
+
case "ts":
|
|
12422
|
+
case "tsx":
|
|
12423
|
+
case "js":
|
|
12424
|
+
case "jsx": {
|
|
12425
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
12426
|
+
return parseSymbols8({ file, content, lang });
|
|
12427
|
+
}
|
|
12428
|
+
case "go": {
|
|
12429
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
12430
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
12431
|
+
}
|
|
12432
|
+
case "py": {
|
|
12433
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
12434
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
12435
|
+
}
|
|
12436
|
+
case "rs": {
|
|
12437
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
12438
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
12439
|
+
}
|
|
12440
|
+
case "json": {
|
|
12441
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
12442
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
12443
|
+
}
|
|
12444
|
+
case "yaml": {
|
|
12445
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
12446
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
12447
|
+
}
|
|
12448
|
+
default: {
|
|
12449
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
12450
|
+
return parseSymbols8({ file, content, lang });
|
|
12360
12451
|
}
|
|
12361
12452
|
}
|
|
12362
|
-
return { kind: "missing-build" };
|
|
12363
|
-
}
|
|
12364
|
-
function resolveProjectServerUrl() {
|
|
12365
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
12366
|
-
return availability.kind === "available" ? availability.url : null;
|
|
12367
|
-
}
|
|
12368
|
-
function projectIndexServerExpectedBuildId() {
|
|
12369
|
-
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
12370
|
-
if (override) return override;
|
|
12371
|
-
const url = resolveProjectServerUrl();
|
|
12372
|
-
return url ? projectIndexServerBuildId(url) : null;
|
|
12373
|
-
}
|
|
12374
|
-
function isProjectIndexServerAvailable() {
|
|
12375
|
-
return resolveProjectServerUrl() !== null;
|
|
12376
12453
|
}
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12454
|
+
|
|
12455
|
+
// src/codebase-index/indexer.ts
|
|
12456
|
+
var YIELD_EVERY_N = 50;
|
|
12457
|
+
function resolveParallelBatch() {
|
|
12458
|
+
return indexParallelBatchSize(availableParallelism());
|
|
12381
12459
|
}
|
|
12382
|
-
function
|
|
12383
|
-
|
|
12384
|
-
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12385
|
-
const existing = connectionStates.get(endpoint);
|
|
12386
|
-
if (existing) return existing;
|
|
12387
|
-
if (!isProjectIndexServerAvailable()) {
|
|
12388
|
-
return { status: "unavailable", connected: false };
|
|
12389
|
-
}
|
|
12390
|
-
return {
|
|
12391
|
-
status: "offline",
|
|
12392
|
-
connected: false,
|
|
12393
|
-
projectRoot,
|
|
12394
|
-
indexDir,
|
|
12395
|
-
endpoint
|
|
12396
|
-
};
|
|
12397
|
-
}
|
|
12398
|
-
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
12399
|
-
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
12400
|
-
return latestConnectionState;
|
|
12460
|
+
function yieldEventLoop() {
|
|
12461
|
+
return new Promise((resolve16) => setImmediate(resolve16));
|
|
12401
12462
|
}
|
|
12402
|
-
function
|
|
12403
|
-
|
|
12404
|
-
|
|
12463
|
+
function throwIfAborted(signal) {
|
|
12464
|
+
if (!signal?.aborted) return;
|
|
12465
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
12466
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12405
12467
|
}
|
|
12406
|
-
function
|
|
12407
|
-
|
|
12408
|
-
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
12409
|
-
const error = new Error(message);
|
|
12410
|
-
if (name && name !== "Error") error.name = name;
|
|
12411
|
-
return error;
|
|
12468
|
+
function isAbortError(err) {
|
|
12469
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
12412
12470
|
}
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12471
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
12472
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
12473
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
12474
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
12475
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
12476
|
+
function isWithinProject(projectRoot, file) {
|
|
12477
|
+
const rel = path23.relative(projectRoot, file);
|
|
12478
|
+
return rel !== "" && !rel.startsWith(`..${path23.sep}`) && rel !== ".." && !path23.isAbsolute(rel);
|
|
12419
12479
|
}
|
|
12420
|
-
function
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
timer.unref?.();
|
|
12424
|
-
});
|
|
12480
|
+
function isMissingPathError(err) {
|
|
12481
|
+
const code = err?.code;
|
|
12482
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
12425
12483
|
}
|
|
12426
|
-
function
|
|
12427
|
-
|
|
12484
|
+
function normalizeComparablePath(value) {
|
|
12485
|
+
const resolved = path23.resolve(value);
|
|
12486
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12428
12487
|
}
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
const
|
|
12453
|
-
const
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
if ((
|
|
12488
|
-
const
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
latencyMs: null,
|
|
12495
|
-
missedHeartbeats,
|
|
12496
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
12497
|
-
};
|
|
12498
|
-
this.transition(status, { pid: this.info?.pid, error });
|
|
12499
|
-
return this.health;
|
|
12500
|
-
}).finally(() => {
|
|
12501
|
-
this.healthCheck = null;
|
|
12502
|
-
});
|
|
12503
|
-
return this.healthCheck;
|
|
12504
|
-
}
|
|
12505
|
-
markResponsive() {
|
|
12506
|
-
const now = Date.now();
|
|
12507
|
-
this.health = {
|
|
12508
|
-
status: "healthy",
|
|
12509
|
-
checkedAt: now,
|
|
12510
|
-
lastHealthyAt: now,
|
|
12511
|
-
latencyMs: this.health?.latencyMs ?? null,
|
|
12512
|
-
missedHeartbeats: 0,
|
|
12513
|
-
...this.health?.server ? { server: this.health.server } : {}
|
|
12488
|
+
function gitOutput(projectRoot, args) {
|
|
12489
|
+
return new Promise((resolve16, reject) => {
|
|
12490
|
+
execFile2(
|
|
12491
|
+
"git",
|
|
12492
|
+
["-C", projectRoot, ...args],
|
|
12493
|
+
{
|
|
12494
|
+
encoding: "buffer",
|
|
12495
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
12496
|
+
windowsHide: true
|
|
12497
|
+
},
|
|
12498
|
+
(error, stdout) => {
|
|
12499
|
+
if (error) reject(error);
|
|
12500
|
+
else resolve16(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
12501
|
+
}
|
|
12502
|
+
);
|
|
12503
|
+
});
|
|
12504
|
+
}
|
|
12505
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
12506
|
+
try {
|
|
12507
|
+
throwIfAborted(signal);
|
|
12508
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
12509
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
12510
|
+
throwIfAborted(signal);
|
|
12511
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12512
|
+
const [output, statusOutput] = await Promise.all([
|
|
12513
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
12514
|
+
gitOutput(projectRoot, [
|
|
12515
|
+
"status",
|
|
12516
|
+
"--porcelain=v1",
|
|
12517
|
+
"-z",
|
|
12518
|
+
"--untracked-files=all",
|
|
12519
|
+
"--ignored=no"
|
|
12520
|
+
])
|
|
12521
|
+
]);
|
|
12522
|
+
throwIfAborted(signal);
|
|
12523
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
12524
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
12525
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12526
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
12527
|
+
const record = statusRecords[i];
|
|
12528
|
+
if (!record) continue;
|
|
12529
|
+
const status = record.slice(0, 2);
|
|
12530
|
+
const changedPath = path23.resolve(projectRoot, record.slice(3));
|
|
12531
|
+
dirty.add(changedPath);
|
|
12532
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
12533
|
+
if (status.includes("R") || status.includes("C")) {
|
|
12534
|
+
const source = statusRecords[++i];
|
|
12535
|
+
if (source) dirty.add(path23.resolve(projectRoot, source));
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
12538
|
+
const files = [];
|
|
12539
|
+
for (const relative12 of output.toString("utf8").split("\0")) {
|
|
12540
|
+
if (!relative12) continue;
|
|
12541
|
+
const portable = relative12.replace(/\\/g, "/");
|
|
12542
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path23.posix.basename(portable))) {
|
|
12543
|
+
continue;
|
|
12544
|
+
}
|
|
12545
|
+
const full = path23.resolve(projectRoot, relative12);
|
|
12546
|
+
if (deleted.has(full)) continue;
|
|
12547
|
+
const ext = path23.extname(relative12).toLowerCase();
|
|
12548
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
12549
|
+
}
|
|
12550
|
+
return {
|
|
12551
|
+
files,
|
|
12552
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12514
12553
|
};
|
|
12554
|
+
} catch {
|
|
12555
|
+
return null;
|
|
12515
12556
|
}
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
return
|
|
12557
|
+
}
|
|
12558
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
12559
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
12560
|
+
if (gitFiles) {
|
|
12561
|
+
return {
|
|
12562
|
+
files: gitFiles.files,
|
|
12563
|
+
complete: true,
|
|
12564
|
+
errors: [],
|
|
12565
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
12566
|
+
};
|
|
12521
12567
|
}
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12568
|
+
const results = [];
|
|
12569
|
+
const errors = [];
|
|
12570
|
+
let complete = true;
|
|
12571
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12572
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12573
|
+
let dirCount = 0;
|
|
12574
|
+
const walk = async (dir) => {
|
|
12575
|
+
throwIfAborted(signal);
|
|
12576
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12577
|
+
await yieldEventLoop();
|
|
12578
|
+
throwIfAborted(signal);
|
|
12527
12579
|
}
|
|
12528
|
-
|
|
12580
|
+
let entries;
|
|
12529
12581
|
try {
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
return { stopped: true, pid };
|
|
12536
|
-
} catch (error) {
|
|
12537
|
-
const forceKilled = this.forceKillKnownServer();
|
|
12538
|
-
return {
|
|
12539
|
-
stopped: forceKilled,
|
|
12540
|
-
pid,
|
|
12541
|
-
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
12542
|
-
};
|
|
12543
|
-
} finally {
|
|
12544
|
-
this.close();
|
|
12582
|
+
entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
12583
|
+
} catch (err) {
|
|
12584
|
+
complete = false;
|
|
12585
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12586
|
+
return;
|
|
12545
12587
|
}
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12588
|
+
dirCount++;
|
|
12589
|
+
for (const e of entries) {
|
|
12590
|
+
if (ignoreSet.has(e.name)) continue;
|
|
12591
|
+
const full = path23.join(dir, e.name);
|
|
12592
|
+
const rel = path23.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12593
|
+
if (e.isDirectory()) {
|
|
12594
|
+
if (isGitIgnored(rel, true)) continue;
|
|
12595
|
+
await walk(full);
|
|
12596
|
+
} else if (e.isFile()) {
|
|
12597
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12598
|
+
const ext = path23.extname(e.name).toLowerCase();
|
|
12599
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12600
|
+
results.push(full);
|
|
12601
|
+
}
|
|
12602
|
+
}
|
|
12603
|
+
}
|
|
12604
|
+
};
|
|
12605
|
+
await walk(projectRoot);
|
|
12606
|
+
return { files: results, complete, errors };
|
|
12607
|
+
}
|
|
12608
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
12609
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
12610
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
12611
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12612
|
+
const assigned = [];
|
|
12613
|
+
for (const ref of refs) {
|
|
12614
|
+
let owner2;
|
|
12615
|
+
for (const symbol of ordered) {
|
|
12616
|
+
if (symbol.line > ref.line) break;
|
|
12617
|
+
owner2 = symbol;
|
|
12565
12618
|
}
|
|
12619
|
+
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12620
|
+
if (!owner2 || owner2.id <= 0) continue;
|
|
12621
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12622
|
+
if (seen.has(key)) continue;
|
|
12623
|
+
seen.add(key);
|
|
12624
|
+
assigned.push({ ...ref, fromId: owner2.id });
|
|
12566
12625
|
}
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12626
|
+
return assigned;
|
|
12627
|
+
}
|
|
12628
|
+
async function runIndexerWithStore(store, opts) {
|
|
12629
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
12630
|
+
const relationGraphVersion = "2";
|
|
12631
|
+
const refResolutionVersion = "2";
|
|
12632
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
12633
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
12634
|
+
const startMs = Date.now();
|
|
12635
|
+
const errors = [];
|
|
12636
|
+
const langStats = {};
|
|
12637
|
+
let filesIndexed = 0;
|
|
12638
|
+
let symbolsIndexed = 0;
|
|
12639
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
12640
|
+
let files;
|
|
12641
|
+
let discoveredFiles = null;
|
|
12642
|
+
let discoveryComplete = true;
|
|
12643
|
+
let trustedUnchanged;
|
|
12644
|
+
if (opts.files && opts.files.length > 0) {
|
|
12645
|
+
files = opts.files.map((f) => path23.resolve(projectRoot, f)).filter((f) => {
|
|
12646
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
12647
|
+
const rel = path23.relative(projectRoot, f).replace(/\\/g, "/");
|
|
12648
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path23.basename(f)) && !isGitIgnored(rel, false);
|
|
12649
|
+
});
|
|
12650
|
+
} else {
|
|
12651
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
12652
|
+
files = discovery.files;
|
|
12653
|
+
errors.push(...discovery.errors);
|
|
12654
|
+
discoveryComplete = discovery.complete;
|
|
12655
|
+
discoveredFiles = new Set(files);
|
|
12656
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
12580
12657
|
}
|
|
12581
|
-
|
|
12582
|
-
const
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
const id = this.nextId++;
|
|
12587
|
-
return new Promise((resolve16, reject) => {
|
|
12588
|
-
const timer = setTimeout(() => {
|
|
12589
|
-
const entry = this.pending.get(id);
|
|
12590
|
-
if (!entry) return;
|
|
12591
|
-
this.pending.delete(id);
|
|
12592
|
-
this.write({ type: "cancel", id });
|
|
12593
|
-
const error = new IndexTimeoutError(
|
|
12594
|
-
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
12595
|
-
);
|
|
12596
|
-
this.cleanupPending(entry);
|
|
12597
|
-
entry.reject(error);
|
|
12598
|
-
}, options.timeoutMs);
|
|
12599
|
-
timer.unref?.();
|
|
12600
|
-
const signal = options.signal;
|
|
12601
|
-
const onAbort = signal ? () => {
|
|
12602
|
-
const entry = this.pending.get(id);
|
|
12603
|
-
if (!entry) return;
|
|
12604
|
-
this.pending.delete(id);
|
|
12605
|
-
this.write({ type: "cancel", id });
|
|
12606
|
-
this.cleanupPending(entry);
|
|
12607
|
-
entry.reject(cancellationError(signal));
|
|
12608
|
-
} : void 0;
|
|
12609
|
-
this.pending.set(id, {
|
|
12610
|
-
resolve: resolve16,
|
|
12611
|
-
reject,
|
|
12612
|
-
timer,
|
|
12613
|
-
signal,
|
|
12614
|
-
onAbort,
|
|
12615
|
-
onProgress: options.onProgress
|
|
12616
|
-
});
|
|
12617
|
-
if (signal && onAbort) {
|
|
12618
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
12619
|
-
if (signal.aborted) {
|
|
12620
|
-
onAbort();
|
|
12621
|
-
return;
|
|
12622
|
-
}
|
|
12623
|
-
}
|
|
12624
|
-
this.write({ ...message, id });
|
|
12658
|
+
if (langs && langs.length > 0) {
|
|
12659
|
+
const langSet = new Set(langs);
|
|
12660
|
+
files = files.filter((f) => {
|
|
12661
|
+
const lang = detectLang(f);
|
|
12662
|
+
return lang ? langSet.has(lang) : false;
|
|
12625
12663
|
});
|
|
12626
12664
|
}
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12665
|
+
if (force) store.clearAll();
|
|
12666
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
12667
|
+
if (!force) {
|
|
12668
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
12669
|
+
}
|
|
12670
|
+
const totalFilesForProgress = files.length;
|
|
12671
|
+
let filesPreSkipped = 0;
|
|
12672
|
+
if (!force && trustedUnchanged) {
|
|
12673
|
+
files = files.filter((file) => {
|
|
12674
|
+
const meta = existingMeta.get(file);
|
|
12675
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
12676
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
12677
|
+
symbolsIndexed += meta.symbolCount;
|
|
12678
|
+
filesIndexed++;
|
|
12679
|
+
filesPreSkipped++;
|
|
12680
|
+
return false;
|
|
12636
12681
|
});
|
|
12637
|
-
|
|
12682
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
12638
12683
|
}
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12684
|
+
const parallelBatch = resolveParallelBatch();
|
|
12685
|
+
let filesSinceLastYield = 0;
|
|
12686
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
12687
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
12688
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
12689
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
12690
|
+
filesSinceLastYield += batchFiles.length;
|
|
12691
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
12692
|
+
filesSinceLastYield = 0;
|
|
12693
|
+
await yieldEventLoop();
|
|
12694
|
+
if (isFrugalPerf()) {
|
|
12695
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
12696
|
+
}
|
|
12697
|
+
throwIfAborted(signal);
|
|
12698
|
+
}
|
|
12699
|
+
const statOpts = signal ? { signal } : {};
|
|
12700
|
+
const statReadParse = await Promise.allSettled(
|
|
12701
|
+
batchFiles.map(
|
|
12702
|
+
async (file) => {
|
|
12703
|
+
let stat17;
|
|
12704
|
+
try {
|
|
12705
|
+
stat17 = await fs17.stat(file, statOpts);
|
|
12706
|
+
} catch (e) {
|
|
12707
|
+
if (isAbortError(e)) throw e;
|
|
12708
|
+
return {
|
|
12709
|
+
file,
|
|
12710
|
+
stat: null,
|
|
12711
|
+
lang: "",
|
|
12712
|
+
parsed: null,
|
|
12713
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
12714
|
+
missing: isMissingPathError(e)
|
|
12715
|
+
};
|
|
12716
|
+
}
|
|
12717
|
+
if (!stat17.isFile()) return { file, stat: stat17, lang: "", parsed: null };
|
|
12718
|
+
const lang = detectLang(file);
|
|
12719
|
+
if (!lang) return { file, stat: stat17, lang: "", parsed: null };
|
|
12720
|
+
if (stat17.size > MAX_INDEX_FILE_BYTES) {
|
|
12721
|
+
return {
|
|
12722
|
+
file,
|
|
12723
|
+
stat: stat17,
|
|
12724
|
+
lang,
|
|
12725
|
+
parsed: null,
|
|
12726
|
+
error: `file too large (${stat17.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
12727
|
+
};
|
|
12728
|
+
}
|
|
12729
|
+
const meta = existingMeta.get(file);
|
|
12730
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat17.mtimeMs)) {
|
|
12731
|
+
return { file, stat: stat17, lang, parsed: null, skippedMeta: meta };
|
|
12732
|
+
}
|
|
12733
|
+
let content;
|
|
12734
|
+
try {
|
|
12735
|
+
content = await fs17.readFile(file, { encoding: "utf8", signal });
|
|
12736
|
+
} catch (e) {
|
|
12737
|
+
if (isAbortError(e)) throw e;
|
|
12738
|
+
return {
|
|
12739
|
+
file,
|
|
12740
|
+
stat: stat17,
|
|
12741
|
+
lang,
|
|
12742
|
+
parsed: null,
|
|
12743
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
12744
|
+
};
|
|
12745
|
+
}
|
|
12746
|
+
let parsed;
|
|
12747
|
+
try {
|
|
12748
|
+
parsed = await parseFileContent(file, content, lang);
|
|
12749
|
+
} catch (e) {
|
|
12750
|
+
return {
|
|
12751
|
+
file,
|
|
12752
|
+
stat: stat17,
|
|
12753
|
+
lang,
|
|
12754
|
+
parsed: null,
|
|
12755
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
12756
|
+
};
|
|
12757
|
+
}
|
|
12758
|
+
return { file, stat: stat17, lang, parsed, content };
|
|
12657
12759
|
}
|
|
12760
|
+
)
|
|
12761
|
+
);
|
|
12762
|
+
const batchEntries = [];
|
|
12763
|
+
const deleteForFiles = [];
|
|
12764
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
12765
|
+
const settled = statReadParse[fi];
|
|
12766
|
+
const file = expectDefined6(batchFiles[fi]);
|
|
12767
|
+
if (settled.status === "rejected") {
|
|
12768
|
+
const err = settled.reason;
|
|
12769
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
12770
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12771
|
+
continue;
|
|
12658
12772
|
}
|
|
12659
|
-
|
|
12660
|
-
if (
|
|
12661
|
-
|
|
12662
|
-
|
|
12773
|
+
const result = settled.value;
|
|
12774
|
+
if (result.error) {
|
|
12775
|
+
if (result.missing) store.deleteFile(file);
|
|
12776
|
+
errors.push(`${file}: ${result.error}`);
|
|
12777
|
+
continue;
|
|
12663
12778
|
}
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12779
|
+
const { stat: stat17, lang, parsed } = result;
|
|
12780
|
+
if (result.skippedMeta) {
|
|
12781
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
12782
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
12783
|
+
filesIndexed++;
|
|
12784
|
+
continue;
|
|
12785
|
+
}
|
|
12786
|
+
if (!lang || !parsed) {
|
|
12787
|
+
if (lang) {
|
|
12788
|
+
store.upsertFile({
|
|
12789
|
+
file,
|
|
12790
|
+
lang,
|
|
12791
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12792
|
+
symbolCount: 0,
|
|
12793
|
+
lastIndexed: Date.now()
|
|
12794
|
+
});
|
|
12795
|
+
filesIndexed++;
|
|
12796
|
+
}
|
|
12797
|
+
continue;
|
|
12798
|
+
}
|
|
12799
|
+
if (parsed.symbols.length === 0) {
|
|
12800
|
+
store.replaceEmptyFile({
|
|
12801
|
+
file,
|
|
12802
|
+
lang,
|
|
12803
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12804
|
+
symbolCount: 0,
|
|
12805
|
+
lastIndexed: Date.now()
|
|
12806
|
+
});
|
|
12807
|
+
filesIndexed++;
|
|
12808
|
+
continue;
|
|
12809
|
+
}
|
|
12810
|
+
batchEntries.push({
|
|
12811
|
+
file,
|
|
12812
|
+
lang,
|
|
12813
|
+
symbols: parsed.symbols,
|
|
12814
|
+
refs: parsed.refs ?? [],
|
|
12815
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12816
|
+
symbolCount: parsed.symbols.length
|
|
12701
12817
|
});
|
|
12702
|
-
|
|
12703
|
-
}
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12818
|
+
deleteForFiles.push(file);
|
|
12819
|
+
}
|
|
12820
|
+
if (batchEntries.length > 0) {
|
|
12821
|
+
try {
|
|
12822
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
12823
|
+
for (const entry of batchEntries) {
|
|
12824
|
+
const count = entry.symbols.length;
|
|
12825
|
+
symbolsIndexed += count;
|
|
12826
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
12827
|
+
filesIndexed++;
|
|
12828
|
+
}
|
|
12829
|
+
} catch (err) {
|
|
12830
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12831
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
12832
|
+
for (const entry of batchEntries) {
|
|
12833
|
+
try {
|
|
12834
|
+
store.deleteRefsForFile(entry.file);
|
|
12835
|
+
store.deleteSymbolsForFile(entry.file);
|
|
12836
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
12837
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
12838
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
12839
|
+
filesIndexed++;
|
|
12840
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
12841
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
12842
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
12843
|
+
}
|
|
12844
|
+
store.resolveRefsForNames([
|
|
12845
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
12846
|
+
...entry.refs.map((ref) => ref.toName)
|
|
12847
|
+
]);
|
|
12848
|
+
store.upsertFile({
|
|
12849
|
+
file: entry.file,
|
|
12850
|
+
lang: entry.lang,
|
|
12851
|
+
mtimeMs: entry.mtimeMs,
|
|
12852
|
+
symbolCount: entry.symbolCount,
|
|
12853
|
+
lastIndexed: Date.now()
|
|
12854
|
+
});
|
|
12855
|
+
} catch (innerErr) {
|
|
12856
|
+
errors.push(
|
|
12857
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
12858
|
+
);
|
|
12859
|
+
}
|
|
12713
12860
|
}
|
|
12714
|
-
return;
|
|
12715
|
-
}
|
|
12716
|
-
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12717
|
-
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12718
|
-
return;
|
|
12719
|
-
}
|
|
12720
|
-
const line = this.buffer.slice(0, newline);
|
|
12721
|
-
this.buffer = this.buffer.slice(newline + 1);
|
|
12722
|
-
if (!line) continue;
|
|
12723
|
-
let message;
|
|
12724
|
-
try {
|
|
12725
|
-
message = JSON.parse(line);
|
|
12726
|
-
} catch {
|
|
12727
|
-
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12728
|
-
return;
|
|
12729
12861
|
}
|
|
12730
|
-
this.onMessage(message);
|
|
12731
12862
|
}
|
|
12732
12863
|
}
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
if (
|
|
12736
|
-
|
|
12737
|
-
message,
|
|
12738
|
-
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
12739
|
-
);
|
|
12740
|
-
return;
|
|
12741
|
-
}
|
|
12742
|
-
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
12743
|
-
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
12744
|
-
this.rejectStaleServer(
|
|
12745
|
-
message,
|
|
12746
|
-
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
12747
|
-
);
|
|
12748
|
-
return;
|
|
12864
|
+
if (discoveredFiles && discoveryComplete) {
|
|
12865
|
+
for (const [file_] of existingMeta) {
|
|
12866
|
+
if (!discoveredFiles.has(file_)) {
|
|
12867
|
+
store.deleteFile(file_);
|
|
12749
12868
|
}
|
|
12750
|
-
this.info = message;
|
|
12751
|
-
this.markResponsive();
|
|
12752
|
-
this.transition("connected", { pid: message.pid });
|
|
12753
|
-
ensureHeartbeatLoop();
|
|
12754
|
-
this.connectResolve?.();
|
|
12755
|
-
return;
|
|
12756
|
-
}
|
|
12757
|
-
if (message.type === "index-state") {
|
|
12758
|
-
this.activity = message.state;
|
|
12759
|
-
this.markResponsive();
|
|
12760
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
12761
|
-
return;
|
|
12762
|
-
}
|
|
12763
|
-
const entry = this.pending.get(message.id);
|
|
12764
|
-
if (!entry) return;
|
|
12765
|
-
this.markResponsive();
|
|
12766
|
-
const status = connectionStates.get(this.endpoint)?.status;
|
|
12767
|
-
if (status === "degraded" || status === "unresponsive") {
|
|
12768
|
-
this.transition("connected", { pid: this.info?.pid });
|
|
12769
|
-
}
|
|
12770
|
-
if (message.type === "progress") {
|
|
12771
|
-
entry.onProgress?.(message.current, message.total);
|
|
12772
|
-
return;
|
|
12773
|
-
}
|
|
12774
|
-
this.pending.delete(message.id);
|
|
12775
|
-
this.cleanupPending(entry);
|
|
12776
|
-
if (message.ok) entry.resolve(message.result);
|
|
12777
|
-
else entry.reject(remoteError(message.error, message.errorName));
|
|
12778
|
-
}
|
|
12779
|
-
onClose(socket) {
|
|
12780
|
-
if (socket !== this.socket) return;
|
|
12781
|
-
const wasConnected = this.info !== null;
|
|
12782
|
-
this.socket = null;
|
|
12783
|
-
this.info = null;
|
|
12784
|
-
this.activity = null;
|
|
12785
|
-
this.health = null;
|
|
12786
|
-
const error = new Error("codebase-index server connection closed");
|
|
12787
|
-
this.connectReject?.(error);
|
|
12788
|
-
this.connectResolve = null;
|
|
12789
|
-
this.connectReject = null;
|
|
12790
|
-
this.rejectPending(error);
|
|
12791
|
-
if (wasConnected) this.transition("error", { error });
|
|
12792
|
-
maybeStopHeartbeatLoop();
|
|
12793
|
-
}
|
|
12794
|
-
cleanupPending(entry) {
|
|
12795
|
-
clearTimeout(entry.timer);
|
|
12796
|
-
if (entry.signal && entry.onAbort) {
|
|
12797
|
-
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
12798
|
-
}
|
|
12799
|
-
}
|
|
12800
|
-
rejectPending(error) {
|
|
12801
|
-
const entries = [...this.pending.values()];
|
|
12802
|
-
this.pending.clear();
|
|
12803
|
-
for (const entry of entries) {
|
|
12804
|
-
this.cleanupPending(entry);
|
|
12805
|
-
entry.reject(error);
|
|
12806
|
-
}
|
|
12807
|
-
}
|
|
12808
|
-
write(message) {
|
|
12809
|
-
const socket = this.socket;
|
|
12810
|
-
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
12811
|
-
}
|
|
12812
|
-
rejectStaleServer(message, reason) {
|
|
12813
|
-
const socket = this.socket;
|
|
12814
|
-
if (socket && !socket.destroyed) {
|
|
12815
|
-
socket.write(
|
|
12816
|
-
encodeProjectServerMessage({
|
|
12817
|
-
type: "shutdown",
|
|
12818
|
-
id: 0,
|
|
12819
|
-
reason: "stale-build-replacement"
|
|
12820
|
-
})
|
|
12821
|
-
);
|
|
12822
|
-
const timer = setTimeout(() => socket.destroy(), 25);
|
|
12823
|
-
timer.unref?.();
|
|
12824
12869
|
}
|
|
12825
|
-
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12826
12870
|
}
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12871
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
12872
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
12873
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
12874
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
12875
|
+
store.setLastIndexed(Date.now());
|
|
12876
|
+
if (!opts.files) store.compactIfNeeded();
|
|
12877
|
+
const durationMs = Date.now() - startMs;
|
|
12878
|
+
return {
|
|
12879
|
+
filesIndexed,
|
|
12880
|
+
symbolsIndexed,
|
|
12881
|
+
langStats,
|
|
12882
|
+
durationMs,
|
|
12883
|
+
errors
|
|
12884
|
+
};
|
|
12885
|
+
}
|
|
12886
|
+
|
|
12887
|
+
// src/codebase-index/index-service.ts
|
|
12888
|
+
async function indexService(args, hooks = {}) {
|
|
12889
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12890
|
+
try {
|
|
12891
|
+
return await runIndexerWithStore(store, {
|
|
12892
|
+
projectRoot: args.projectRoot,
|
|
12893
|
+
indexDir: args.indexDir,
|
|
12894
|
+
files: args.files,
|
|
12895
|
+
force: args.force,
|
|
12896
|
+
langs: args.langs,
|
|
12897
|
+
ignore: args.ignore,
|
|
12898
|
+
signal: hooks.signal,
|
|
12899
|
+
onProgress: hooks.onProgress
|
|
12843
12900
|
});
|
|
12844
|
-
|
|
12901
|
+
} finally {
|
|
12902
|
+
indexStorePool.release(store);
|
|
12845
12903
|
}
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12904
|
+
}
|
|
12905
|
+
function searchService(args) {
|
|
12906
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12907
|
+
try {
|
|
12908
|
+
return store.searchRanked(
|
|
12909
|
+
args.query,
|
|
12910
|
+
{
|
|
12911
|
+
kind: args.kind,
|
|
12912
|
+
lang: args.lang,
|
|
12913
|
+
file: args.file,
|
|
12914
|
+
lspKind: args.lspKind
|
|
12915
|
+
},
|
|
12916
|
+
args.limit
|
|
12917
|
+
);
|
|
12918
|
+
} finally {
|
|
12919
|
+
indexStorePool.release(store);
|
|
12849
12920
|
}
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
if (metadata.pid === pid) fs17.rmSync(metadataPath, { force: true });
|
|
12858
|
-
} catch {
|
|
12859
|
-
}
|
|
12860
|
-
return true;
|
|
12861
|
-
} catch {
|
|
12862
|
-
return false;
|
|
12863
|
-
}
|
|
12921
|
+
}
|
|
12922
|
+
function statsService(args) {
|
|
12923
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12924
|
+
try {
|
|
12925
|
+
return store.getStats();
|
|
12926
|
+
} finally {
|
|
12927
|
+
indexStorePool.release(store);
|
|
12864
12928
|
}
|
|
12865
|
-
};
|
|
12866
|
-
var connections = /* @__PURE__ */ new Map();
|
|
12867
|
-
var heartbeatTimer;
|
|
12868
|
-
function ensureHeartbeatLoop() {
|
|
12869
|
-
if (heartbeatTimer) return;
|
|
12870
|
-
heartbeatTimer = setInterval(() => {
|
|
12871
|
-
for (const connection of connections.values()) {
|
|
12872
|
-
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
12873
|
-
});
|
|
12874
|
-
}
|
|
12875
|
-
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
12876
|
-
heartbeatTimer.unref?.();
|
|
12877
12929
|
}
|
|
12878
|
-
function
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12930
|
+
function packageGraphService(args) {
|
|
12931
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12932
|
+
try {
|
|
12933
|
+
return store.getPackageGraph();
|
|
12934
|
+
} finally {
|
|
12935
|
+
indexStorePool.release(store);
|
|
12936
|
+
}
|
|
12883
12937
|
}
|
|
12884
|
-
function
|
|
12885
|
-
const
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12938
|
+
function fileGraphService(args) {
|
|
12939
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12940
|
+
try {
|
|
12941
|
+
return store.getFileGraph(args.packageFilter);
|
|
12942
|
+
} finally {
|
|
12943
|
+
indexStorePool.release(store);
|
|
12890
12944
|
}
|
|
12891
|
-
return connection;
|
|
12892
12945
|
}
|
|
12893
|
-
function
|
|
12894
|
-
|
|
12946
|
+
function symbolGraphService(args) {
|
|
12947
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12948
|
+
try {
|
|
12949
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
12950
|
+
} finally {
|
|
12951
|
+
indexStorePool.release(store);
|
|
12952
|
+
}
|
|
12895
12953
|
}
|
|
12896
12954
|
|
|
12897
12955
|
// src/codebase-index/background-indexer.ts
|
|
@@ -13002,8 +13060,17 @@ function terminateWorker(reason) {
|
|
|
13002
13060
|
if (w) void w.terminate().catch(() => {
|
|
13003
13061
|
});
|
|
13004
13062
|
}
|
|
13063
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
13064
|
+
function warnEndpointInvalidOnce(availability) {
|
|
13065
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
13066
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
13067
|
+
process.stderr.write(
|
|
13068
|
+
`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.
|
|
13069
|
+
`
|
|
13070
|
+
);
|
|
13071
|
+
}
|
|
13005
13072
|
function callIndexOp(op, args, opts) {
|
|
13006
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
13073
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
13007
13074
|
if (availability.kind === "available") {
|
|
13008
13075
|
return callProjectIndexServer(op, args, opts);
|
|
13009
13076
|
}
|
|
@@ -13014,6 +13081,14 @@ function callIndexOp(op, args, opts) {
|
|
|
13014
13081
|
)
|
|
13015
13082
|
);
|
|
13016
13083
|
}
|
|
13084
|
+
if (availability.kind === "endpoint-invalid") {
|
|
13085
|
+
warnEndpointInvalidOnce(availability);
|
|
13086
|
+
return Promise.reject(
|
|
13087
|
+
new Error(
|
|
13088
|
+
`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.`
|
|
13089
|
+
)
|
|
13090
|
+
);
|
|
13091
|
+
}
|
|
13017
13092
|
const w = ensureWorker();
|
|
13018
13093
|
if (!w) return callInline(op, args, opts);
|
|
13019
13094
|
if (opts.signal?.aborted) {
|