@wrongstack/tools 0.296.3 → 0.296.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin.js +1253 -1220
- 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 +1233 -1197
- 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 +4 -5
- package/dist/codebase-index/project-server.js.map +2 -2
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1220 -1184
- package/dist/index.js.map +4 -4
- package/dist/pack.js +1253 -1220
- package/dist/pack.js.map +4 -4
- package/dist/read.js +40 -9
- package/dist/read.js.map +2 -2
- package/dist/tool-tier.js +1253 -1220
- package/dist/tool-tier.js.map +4 -4
- package/package.json +3 -3
package/dist/builtin.js
CHANGED
|
@@ -4893,16 +4893,16 @@ var init_legacy_bridge = __esm({
|
|
|
4893
4893
|
});
|
|
4894
4894
|
|
|
4895
4895
|
// src/codebase-index/languages.ts
|
|
4896
|
-
import * as
|
|
4896
|
+
import * as path18 from "node:path";
|
|
4897
4897
|
function detectLang(file) {
|
|
4898
|
-
const base =
|
|
4898
|
+
const base = path18.basename(file);
|
|
4899
4899
|
const lowerBase = base.toLowerCase();
|
|
4900
4900
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
4901
4901
|
return "ts";
|
|
4902
4902
|
}
|
|
4903
4903
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
4904
4904
|
if (special) return special;
|
|
4905
|
-
const ext =
|
|
4905
|
+
const ext = path18.extname(base).toLowerCase();
|
|
4906
4906
|
if (!ext) return null;
|
|
4907
4907
|
return EXT_TO_LANG[ext] ?? null;
|
|
4908
4908
|
}
|
|
@@ -5238,10 +5238,10 @@ __export(go_parser_exports, {
|
|
|
5238
5238
|
detectLang: () => detectLang,
|
|
5239
5239
|
parseSymbols: () => parseSymbols2
|
|
5240
5240
|
});
|
|
5241
|
-
import { spawn as
|
|
5242
|
-
import * as
|
|
5243
|
-
import * as
|
|
5244
|
-
import * as
|
|
5241
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5242
|
+
import * as os6 from "node:os";
|
|
5243
|
+
import * as path19 from "node:path";
|
|
5244
|
+
import * as fs14 from "node:fs/promises";
|
|
5245
5245
|
async function parseSymbols2(opts) {
|
|
5246
5246
|
const { file, content, lang } = opts;
|
|
5247
5247
|
try {
|
|
@@ -5313,16 +5313,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
5313
5313
|
try {
|
|
5314
5314
|
let scriptPath = _cachedGoScriptPath;
|
|
5315
5315
|
if (!scriptPath) {
|
|
5316
|
-
const tmpDir = await
|
|
5317
|
-
scriptPath =
|
|
5318
|
-
await
|
|
5316
|
+
const tmpDir = await fs14.mkdtemp(path19.join(os6.tmpdir(), "ws-go-parse-"));
|
|
5317
|
+
scriptPath = path19.join(tmpDir, "parse.go");
|
|
5318
|
+
await fs14.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
5319
5319
|
_cachedGoScriptPath = scriptPath;
|
|
5320
5320
|
}
|
|
5321
5321
|
const goBinary = resolveWin32Command("go");
|
|
5322
5322
|
const goResult = await new Promise(
|
|
5323
5323
|
(resolve16, reject) => {
|
|
5324
5324
|
let settled = false;
|
|
5325
|
-
const proc =
|
|
5325
|
+
const proc = spawn5(goBinary, ["run", scriptPath], {
|
|
5326
5326
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5327
5327
|
windowsHide: true
|
|
5328
5328
|
});
|
|
@@ -5925,10 +5925,10 @@ __export(py_parser_exports, {
|
|
|
5925
5925
|
detectLang: () => detectLang,
|
|
5926
5926
|
parseSymbols: () => parseSymbols4
|
|
5927
5927
|
});
|
|
5928
|
-
import { spawn as
|
|
5929
|
-
import * as
|
|
5930
|
-
import * as
|
|
5931
|
-
import * as
|
|
5928
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
5929
|
+
import * as fs15 from "node:fs/promises";
|
|
5930
|
+
import * as os7 from "node:os";
|
|
5931
|
+
import * as path20 from "node:path";
|
|
5932
5932
|
async function parseSymbols4(opts) {
|
|
5933
5933
|
const { file, content, lang } = opts;
|
|
5934
5934
|
try {
|
|
@@ -5950,7 +5950,7 @@ async function resolvePython() {
|
|
|
5950
5950
|
function commandIsAvailable(command) {
|
|
5951
5951
|
return new Promise((resolve16) => {
|
|
5952
5952
|
let settled = false;
|
|
5953
|
-
const proc =
|
|
5953
|
+
const proc = spawn6(command, ["--version"], {
|
|
5954
5954
|
stdio: "ignore",
|
|
5955
5955
|
windowsHide: true
|
|
5956
5956
|
});
|
|
@@ -5972,7 +5972,7 @@ function commandIsAvailable(command) {
|
|
|
5972
5972
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
5973
5973
|
return new Promise((resolve16, reject) => {
|
|
5974
5974
|
let settled = false;
|
|
5975
|
-
const proc =
|
|
5975
|
+
const proc = spawn6(pyBinary, [scriptPath, filePath], {
|
|
5976
5976
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5977
5977
|
windowsHide: true
|
|
5978
5978
|
});
|
|
@@ -6006,10 +6006,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
6006
6006
|
async function syncPyParse(filePath, content, lang) {
|
|
6007
6007
|
try {
|
|
6008
6008
|
if (!_cachedScriptPath) {
|
|
6009
|
-
const tmpDir =
|
|
6010
|
-
await
|
|
6011
|
-
_cachedScriptPath =
|
|
6012
|
-
await
|
|
6009
|
+
const tmpDir = path20.join(os7.tmpdir(), "ws-py-parse");
|
|
6010
|
+
await fs15.mkdir(tmpDir, { recursive: true });
|
|
6011
|
+
_cachedScriptPath = path20.join(tmpDir, "parse.py");
|
|
6012
|
+
await fs15.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
6013
6013
|
}
|
|
6014
6014
|
cachedPyBinary ??= resolvePython();
|
|
6015
6015
|
const pyBinary = await cachedPyBinary;
|
|
@@ -6263,10 +6263,10 @@ __export(rs_parser_exports, {
|
|
|
6263
6263
|
detectLang: () => detectLang,
|
|
6264
6264
|
parseSymbols: () => parseSymbols5
|
|
6265
6265
|
});
|
|
6266
|
-
import { expectDefined as
|
|
6267
|
-
import { execFile, spawn as
|
|
6268
|
-
import * as
|
|
6269
|
-
import * as
|
|
6266
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
6267
|
+
import { execFile, spawn as spawn7 } from "node:child_process";
|
|
6268
|
+
import * as fs16 from "node:fs/promises";
|
|
6269
|
+
import * as path21 from "node:path";
|
|
6270
6270
|
async function parseSymbols5(opts) {
|
|
6271
6271
|
const { file, content, lang } = opts;
|
|
6272
6272
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -6288,7 +6288,7 @@ function checkNativeParser() {
|
|
|
6288
6288
|
nativeParserAvailability ??= (async () => {
|
|
6289
6289
|
try {
|
|
6290
6290
|
await probe("rustc", ["--version"]);
|
|
6291
|
-
const toolsDir =
|
|
6291
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6292
6292
|
await probe(
|
|
6293
6293
|
"cargo",
|
|
6294
6294
|
[
|
|
@@ -6297,7 +6297,7 @@ function checkNativeParser() {
|
|
|
6297
6297
|
"--format-version",
|
|
6298
6298
|
"1",
|
|
6299
6299
|
"--manifest-path",
|
|
6300
|
-
|
|
6300
|
+
path21.join(toolsDir, "Cargo.toml")
|
|
6301
6301
|
]
|
|
6302
6302
|
);
|
|
6303
6303
|
return true;
|
|
@@ -6309,17 +6309,17 @@ function checkNativeParser() {
|
|
|
6309
6309
|
}
|
|
6310
6310
|
async function tryNativeParse(file, content) {
|
|
6311
6311
|
try {
|
|
6312
|
-
const toolsDir =
|
|
6313
|
-
const crateDir =
|
|
6314
|
-
const tmpFile =
|
|
6315
|
-
await
|
|
6312
|
+
const toolsDir = path21.join(process.cwd(), "tools");
|
|
6313
|
+
const crateDir = path21.join(toolsDir, "syn-parser");
|
|
6314
|
+
const tmpFile = path21.join(crateDir, "src", "input.rs");
|
|
6315
|
+
await fs16.writeFile(tmpFile, content, "utf8");
|
|
6316
6316
|
const cargoBinary = resolveWin32Command("cargo");
|
|
6317
6317
|
const result = await new Promise(
|
|
6318
6318
|
(resolve16, reject) => {
|
|
6319
6319
|
let settled = false;
|
|
6320
|
-
const proc =
|
|
6320
|
+
const proc = spawn7(
|
|
6321
6321
|
cargoBinary,
|
|
6322
|
-
["run", "--manifest-path",
|
|
6322
|
+
["run", "--manifest-path", path21.join(toolsDir, "Cargo.toml")],
|
|
6323
6323
|
{
|
|
6324
6324
|
cwd: process.cwd(),
|
|
6325
6325
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -6378,7 +6378,7 @@ function regexParse(opts) {
|
|
|
6378
6378
|
let hi = lineOffsets2.length - 1;
|
|
6379
6379
|
while (lo < hi) {
|
|
6380
6380
|
const mid = lo + hi + 1 >>> 1;
|
|
6381
|
-
if (
|
|
6381
|
+
if (expectDefined3(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6382
6382
|
else hi = mid - 1;
|
|
6383
6383
|
}
|
|
6384
6384
|
return lo + 1;
|
|
@@ -6390,7 +6390,7 @@ function regexParse(opts) {
|
|
|
6390
6390
|
for (const pattern of RS_PATTERNS) {
|
|
6391
6391
|
pattern.regex.lastIndex = 0;
|
|
6392
6392
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
6393
|
-
const name =
|
|
6393
|
+
const name = expectDefined3(match[1]);
|
|
6394
6394
|
const offset = match.index ?? 0;
|
|
6395
6395
|
const line = lineFromOffset(offset);
|
|
6396
6396
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6447,8 +6447,8 @@ __export(json_parser_exports, {
|
|
|
6447
6447
|
detectLang: () => detectLang,
|
|
6448
6448
|
parseSymbols: () => parseSymbols6
|
|
6449
6449
|
});
|
|
6450
|
-
import { expectDefined as
|
|
6451
|
-
import * as
|
|
6450
|
+
import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
|
|
6451
|
+
import * as path22 from "node:path";
|
|
6452
6452
|
function parseSymbols6(opts) {
|
|
6453
6453
|
const { file, content, lang } = opts;
|
|
6454
6454
|
try {
|
|
@@ -6460,7 +6460,7 @@ function parseSymbols6(opts) {
|
|
|
6460
6460
|
function regexParse2(opts) {
|
|
6461
6461
|
const { file, content, lang } = opts;
|
|
6462
6462
|
const symbols = [];
|
|
6463
|
-
const basename12 =
|
|
6463
|
+
const basename12 = path22.basename(file).toLowerCase();
|
|
6464
6464
|
const isPackageJson = basename12 === "package.json";
|
|
6465
6465
|
const isTsconfig = basename12 === "tsconfig.json" || basename12 === "tsconfig.build.json";
|
|
6466
6466
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -6475,22 +6475,22 @@ function regexParse2(opts) {
|
|
|
6475
6475
|
let hi = lineOffsets2.length - 1;
|
|
6476
6476
|
while (lo < hi) {
|
|
6477
6477
|
const mid = lo + hi + 1 >>> 1;
|
|
6478
|
-
if (
|
|
6478
|
+
if (expectDefined4(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6479
6479
|
else hi = mid - 1;
|
|
6480
6480
|
}
|
|
6481
6481
|
return lo + 1;
|
|
6482
6482
|
}
|
|
6483
6483
|
const rootMatch = content.match(/^\s*\{/m);
|
|
6484
6484
|
if (rootMatch) {
|
|
6485
|
-
const offset =
|
|
6485
|
+
const offset = expectDefined4(rootMatch.index);
|
|
6486
6486
|
const line = lineFromOffset(offset);
|
|
6487
6487
|
symbols.push(
|
|
6488
6488
|
makeSymbol({
|
|
6489
|
-
name:
|
|
6489
|
+
name: path22.basename(file),
|
|
6490
6490
|
kind: "object",
|
|
6491
6491
|
line,
|
|
6492
6492
|
col: 0,
|
|
6493
|
-
signature: `"${
|
|
6493
|
+
signature: `"${path22.basename(file)}" = { ... }`,
|
|
6494
6494
|
file,
|
|
6495
6495
|
lang
|
|
6496
6496
|
})
|
|
@@ -6498,7 +6498,7 @@ function regexParse2(opts) {
|
|
|
6498
6498
|
}
|
|
6499
6499
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
6500
6500
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
6501
|
-
const key =
|
|
6501
|
+
const key = expectDefined4(match[1]);
|
|
6502
6502
|
const offset = match.index ?? 0;
|
|
6503
6503
|
const line = lineFromOffset(offset);
|
|
6504
6504
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6545,7 +6545,7 @@ function regexParse2(opts) {
|
|
|
6545
6545
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
6546
6546
|
const defsMatch = defsRegex.exec(content);
|
|
6547
6547
|
if (defsMatch !== null) {
|
|
6548
|
-
const offset =
|
|
6548
|
+
const offset = expectDefined4(defsMatch.index);
|
|
6549
6549
|
const line = lineFromOffset(offset);
|
|
6550
6550
|
symbols.push(
|
|
6551
6551
|
makeSymbol({
|
|
@@ -6570,7 +6570,7 @@ function regexParse2(opts) {
|
|
|
6570
6570
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
6571
6571
|
const offset = match.index ?? 0;
|
|
6572
6572
|
const line = lineFromOffset(offset);
|
|
6573
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
6573
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined4(match[0]);
|
|
6574
6574
|
symbols.push(
|
|
6575
6575
|
makeSymbol({
|
|
6576
6576
|
name: key,
|
|
@@ -6589,12 +6589,12 @@ function regexParse2(opts) {
|
|
|
6589
6589
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineFromOffset) {
|
|
6590
6590
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
6591
6591
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
6592
|
-
const blockContent =
|
|
6592
|
+
const blockContent = expectDefined4(match[0]);
|
|
6593
6593
|
const blockOffset = match.index ?? 0;
|
|
6594
6594
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
6595
6595
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
6596
|
-
const key =
|
|
6597
|
-
const keyOffset = blockOffset +
|
|
6596
|
+
const key = expectDefined4(scriptMatch[1]);
|
|
6597
|
+
const keyOffset = blockOffset + expectDefined4(scriptMatch.index);
|
|
6598
6598
|
const line = lineFromOffset(keyOffset);
|
|
6599
6599
|
symbols.push(
|
|
6600
6600
|
makeSymbol({
|
|
@@ -6613,12 +6613,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets2, lineF
|
|
|
6613
6613
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets2, parentLine, lineFromOffset) {
|
|
6614
6614
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
6615
6615
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
6616
|
-
const blockContent =
|
|
6616
|
+
const blockContent = expectDefined4(match[0]);
|
|
6617
6617
|
const blockOffset = match.index ?? 0;
|
|
6618
6618
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
6619
6619
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
6620
|
-
const key =
|
|
6621
|
-
const keyOffset = blockOffset +
|
|
6620
|
+
const key = expectDefined4(optMatch[1]);
|
|
6621
|
+
const keyOffset = blockOffset + expectDefined4(optMatch.index);
|
|
6622
6622
|
const line = lineFromOffset(keyOffset);
|
|
6623
6623
|
if (line <= parentLine) continue;
|
|
6624
6624
|
symbols.push(
|
|
@@ -6663,7 +6663,7 @@ __export(yaml_parser_exports, {
|
|
|
6663
6663
|
detectLang: () => detectLang,
|
|
6664
6664
|
parseSymbols: () => parseSymbols7
|
|
6665
6665
|
});
|
|
6666
|
-
import { expectDefined as
|
|
6666
|
+
import { expectDefined as expectDefined5, truncate } from "@wrongstack/core/utils";
|
|
6667
6667
|
function parseSymbols7(opts) {
|
|
6668
6668
|
const { file, content, lang } = opts;
|
|
6669
6669
|
try {
|
|
@@ -6685,14 +6685,14 @@ function regexParse3(opts) {
|
|
|
6685
6685
|
let hi = lineOffsets2.length - 1;
|
|
6686
6686
|
while (lo < hi) {
|
|
6687
6687
|
const mid = lo + hi + 1 >>> 1;
|
|
6688
|
-
if (
|
|
6688
|
+
if (expectDefined5(lineOffsets2[mid]) <= offset) lo = mid;
|
|
6689
6689
|
else hi = mid - 1;
|
|
6690
6690
|
}
|
|
6691
6691
|
return lo + 1;
|
|
6692
6692
|
}
|
|
6693
6693
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
6694
6694
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
6695
|
-
const name =
|
|
6695
|
+
const name = expectDefined5(match[1]);
|
|
6696
6696
|
const offset = match.index ?? 0;
|
|
6697
6697
|
const line = lineFromOffset(offset);
|
|
6698
6698
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6710,7 +6710,7 @@ function regexParse3(opts) {
|
|
|
6710
6710
|
}
|
|
6711
6711
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
6712
6712
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
6713
|
-
const name =
|
|
6713
|
+
const name = expectDefined5(match[1]);
|
|
6714
6714
|
const offset = match.index ?? 0;
|
|
6715
6715
|
const line = lineFromOffset(offset);
|
|
6716
6716
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6745,7 +6745,7 @@ function regexParse3(opts) {
|
|
|
6745
6745
|
}
|
|
6746
6746
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
6747
6747
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
6748
|
-
const key =
|
|
6748
|
+
const key = expectDefined5(match[2]);
|
|
6749
6749
|
const offset = match.index ?? 0;
|
|
6750
6750
|
const line = lineFromOffset(offset);
|
|
6751
6751
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -6765,7 +6765,7 @@ function regexParse3(opts) {
|
|
|
6765
6765
|
}
|
|
6766
6766
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
6767
6767
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
6768
|
-
const key =
|
|
6768
|
+
const key = expectDefined5(match[2]);
|
|
6769
6769
|
const offset = match.index ?? 0;
|
|
6770
6770
|
const line = lineFromOffset(offset);
|
|
6771
6771
|
const col = offset - (lineOffsets2[line - 1] ?? 0);
|
|
@@ -9770,10 +9770,12 @@ var browserTools = [
|
|
|
9770
9770
|
for (const tool of browserTools) tool.icon = "web";
|
|
9771
9771
|
for (const tool of browserTools) tool.timeoutMs ??= 6e4;
|
|
9772
9772
|
|
|
9773
|
-
// src/codebase-index/
|
|
9774
|
-
import
|
|
9775
|
-
import
|
|
9776
|
-
import
|
|
9773
|
+
// src/codebase-index/project-server-client.ts
|
|
9774
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
9775
|
+
import * as fs12 from "node:fs";
|
|
9776
|
+
import * as net3 from "node:net";
|
|
9777
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
9778
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
9777
9779
|
|
|
9778
9780
|
// src/codebase-index/circuit-breaker.ts
|
|
9779
9781
|
var CircuitOpenError = class extends Error {
|
|
@@ -9855,117 +9857,18 @@ var IndexCircuitBreaker = class {
|
|
|
9855
9857
|
};
|
|
9856
9858
|
var indexCircuitBreaker = new IndexCircuitBreaker();
|
|
9857
9859
|
|
|
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
|
-
}
|
|
9860
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
9861
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9862
|
+
import * as fs11 from "node:fs";
|
|
9863
|
+
import * as os5 from "node:os";
|
|
9864
|
+
import * as path16 from "node:path";
|
|
9865
|
+
import { fileURLToPath } from "node:url";
|
|
9866
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
9964
9867
|
|
|
9965
9868
|
// src/codebase-index/writer.ts
|
|
9966
|
-
import { expectDefined as
|
|
9967
|
-
import * as
|
|
9968
|
-
import * as
|
|
9869
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
9870
|
+
import * as fs10 from "node:fs";
|
|
9871
|
+
import * as path15 from "node:path";
|
|
9969
9872
|
|
|
9970
9873
|
// src/codebase-index/bm25.ts
|
|
9971
9874
|
var K1 = 1.5;
|
|
@@ -10160,8 +10063,8 @@ function runSqliteWithRetry(fn) {
|
|
|
10160
10063
|
}
|
|
10161
10064
|
|
|
10162
10065
|
// src/codebase-index/writer-admin.ts
|
|
10163
|
-
import * as
|
|
10164
|
-
import * as
|
|
10066
|
+
import * as fs9 from "node:fs";
|
|
10067
|
+
import * as path13 from "node:path";
|
|
10165
10068
|
var DB_FILE = "index.db";
|
|
10166
10069
|
function getAllIndexableWithStatement(stmt) {
|
|
10167
10070
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -10220,7 +10123,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
10220
10123
|
}
|
|
10221
10124
|
function getIndexDbSizeBytes(indexDir) {
|
|
10222
10125
|
try {
|
|
10223
|
-
return
|
|
10126
|
+
return fs9.statSync(path13.join(indexDir, DB_FILE)).size;
|
|
10224
10127
|
} catch {
|
|
10225
10128
|
return 0;
|
|
10226
10129
|
}
|
|
@@ -10287,7 +10190,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
10287
10190
|
}
|
|
10288
10191
|
|
|
10289
10192
|
// src/codebase-index/writer-graph-helpers.ts
|
|
10290
|
-
import * as
|
|
10193
|
+
import * as path14 from "node:path";
|
|
10291
10194
|
function derivePackage(filePath) {
|
|
10292
10195
|
const f = filePath.replace(/\\/g, "/");
|
|
10293
10196
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -10402,16 +10305,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
10402
10305
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
10403
10306
|
if (!moduleName.startsWith(".")) return void 0;
|
|
10404
10307
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
10405
|
-
const absolute =
|
|
10406
|
-
|
|
10308
|
+
const absolute = path14.posix.normalize(
|
|
10309
|
+
path14.posix.join(path14.posix.dirname(normalizedFrom), moduleName)
|
|
10407
10310
|
);
|
|
10408
|
-
const extension =
|
|
10311
|
+
const extension = path14.posix.extname(absolute);
|
|
10409
10312
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
10410
10313
|
const candidates = [
|
|
10411
10314
|
absolute,
|
|
10412
10315
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
10413
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10414
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
10316
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(absolute, `index${ext}`)),
|
|
10317
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path14.posix.join(base, `index${ext}`))
|
|
10415
10318
|
];
|
|
10416
10319
|
const indexedByPortablePath = new Map(
|
|
10417
10320
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -10927,9 +10830,9 @@ var IndexStore = class _IndexStore {
|
|
|
10927
10830
|
}
|
|
10928
10831
|
constructor(projectRoot, opts = {}) {
|
|
10929
10832
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
10930
|
-
|
|
10833
|
+
fs10.mkdirSync(this.indexDir, { recursive: true });
|
|
10931
10834
|
const Database = loadDatabaseSync();
|
|
10932
|
-
this.db = new Database(
|
|
10835
|
+
this.db = new Database(path15.join(this.indexDir, DB_FILE2));
|
|
10933
10836
|
applyIndexStorePragmas(this.db);
|
|
10934
10837
|
this.initSchema();
|
|
10935
10838
|
}
|
|
@@ -11344,13 +11247,13 @@ var IndexStore = class _IndexStore {
|
|
|
11344
11247
|
if (rankDiff !== 0) return rankDiff;
|
|
11345
11248
|
const scoreDiff = b.score - a.score;
|
|
11346
11249
|
if (scoreDiff !== 0) return scoreDiff;
|
|
11347
|
-
const left =
|
|
11348
|
-
const right =
|
|
11250
|
+
const left = expectDefined2(candidateById.get(a.id));
|
|
11251
|
+
const right = expectDefined2(candidateById.get(b.id));
|
|
11349
11252
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
11350
11253
|
});
|
|
11351
11254
|
const qTokens = tokenise(query);
|
|
11352
11255
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
11353
|
-
const c =
|
|
11256
|
+
const c = expectDefined2(candidateById.get(id));
|
|
11354
11257
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
11355
11258
|
});
|
|
11356
11259
|
return { results, total: candidates.length };
|
|
@@ -11764,1134 +11667,1247 @@ var indexStorePool = new StorePool(
|
|
|
11764
11667
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
11765
11668
|
);
|
|
11766
11669
|
|
|
11767
|
-
// src/codebase-index/
|
|
11768
|
-
var
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
function
|
|
11773
|
-
|
|
11670
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
11671
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
11672
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
11673
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
11674
|
+
var buildIdCache;
|
|
11675
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
11676
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path16.resolve(entrypoint);
|
|
11677
|
+
try {
|
|
11678
|
+
const stat17 = fs11.statSync(file);
|
|
11679
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat17.mtimeMs && buildIdCache.size === stat17.size) {
|
|
11680
|
+
return buildIdCache.buildId;
|
|
11681
|
+
}
|
|
11682
|
+
const buildId = createHash4("sha256").update(fs11.readFileSync(file)).digest("hex").slice(0, 24);
|
|
11683
|
+
buildIdCache = { file, mtimeMs: stat17.mtimeMs, size: stat17.size, buildId };
|
|
11684
|
+
return buildId;
|
|
11685
|
+
} catch {
|
|
11686
|
+
return `unreadable:${path16.basename(file)}`;
|
|
11687
|
+
}
|
|
11774
11688
|
}
|
|
11775
|
-
function
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
11689
|
+
function normalizeLocalPath(value) {
|
|
11690
|
+
const resolved = path16.resolve(value);
|
|
11691
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11779
11692
|
}
|
|
11780
|
-
function
|
|
11781
|
-
|
|
11693
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
11694
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
11695
|
+
return createHash4("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
11782
11696
|
}
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
const rel = path22.relative(projectRoot, file);
|
|
11790
|
-
return rel !== "" && !rel.startsWith(`..${path22.sep}`) && rel !== ".." && !path22.isAbsolute(rel);
|
|
11697
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
11698
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
11699
|
+
if (process.platform === "win32") {
|
|
11700
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
11701
|
+
}
|
|
11702
|
+
return path16.join(os5.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
11791
11703
|
}
|
|
11792
|
-
function
|
|
11793
|
-
|
|
11794
|
-
|
|
11704
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
11705
|
+
return path16.join(
|
|
11706
|
+
path16.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
11707
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
11708
|
+
);
|
|
11795
11709
|
}
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11710
|
+
|
|
11711
|
+
// src/codebase-index/project-server-protocol.ts
|
|
11712
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
11713
|
+
function encodeProjectServerMessage(message) {
|
|
11714
|
+
return `${JSON.stringify(message)}
|
|
11715
|
+
`;
|
|
11799
11716
|
}
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11717
|
+
|
|
11718
|
+
// src/codebase-index/project-server-client.ts
|
|
11719
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
11720
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
11721
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
11722
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
11723
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
11724
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
11725
|
+
constructor(message, pid) {
|
|
11726
|
+
super(message);
|
|
11727
|
+
this.pid = pid;
|
|
11728
|
+
}
|
|
11729
|
+
pid;
|
|
11730
|
+
name = "StaleProjectIndexServerError";
|
|
11731
|
+
};
|
|
11732
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
11733
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
11734
|
+
var latestConnectionState = {
|
|
11735
|
+
status: "offline",
|
|
11736
|
+
connected: false
|
|
11737
|
+
};
|
|
11738
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
11739
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
11740
|
+
return { kind: "inline-requested" };
|
|
11741
|
+
}
|
|
11742
|
+
let builtUrl = null;
|
|
11743
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
11744
|
+
try {
|
|
11745
|
+
const url = new URL(rel, import.meta.url);
|
|
11746
|
+
if (url.protocol === "file:" && fs12.existsSync(fileURLToPath2(url))) {
|
|
11747
|
+
builtUrl = url;
|
|
11748
|
+
break;
|
|
11813
11749
|
}
|
|
11814
|
-
|
|
11815
|
-
|
|
11750
|
+
} catch {
|
|
11751
|
+
}
|
|
11752
|
+
}
|
|
11753
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
11754
|
+
if (projectRoot !== void 0) {
|
|
11755
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
11756
|
+
const check = checkUnixSocketPath(endpoint);
|
|
11757
|
+
if (!check.ok) {
|
|
11758
|
+
return {
|
|
11759
|
+
kind: "endpoint-invalid",
|
|
11760
|
+
endpoint,
|
|
11761
|
+
byteLength: check.byteLength,
|
|
11762
|
+
maxBytes: check.maxBytes
|
|
11763
|
+
};
|
|
11764
|
+
}
|
|
11765
|
+
}
|
|
11766
|
+
return { kind: "available", url: builtUrl };
|
|
11816
11767
|
}
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
11837
|
-
const
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
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));
|
|
11848
|
-
}
|
|
11849
|
-
}
|
|
11850
|
-
const files = [];
|
|
11851
|
-
for (const relative12 of output.toString("utf8").split("\0")) {
|
|
11852
|
-
if (!relative12) continue;
|
|
11853
|
-
const portable = relative12.replace(/\\/g, "/");
|
|
11854
|
-
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path22.posix.basename(portable))) {
|
|
11855
|
-
continue;
|
|
11856
|
-
}
|
|
11857
|
-
const full = path22.resolve(projectRoot, relative12);
|
|
11858
|
-
if (deleted.has(full)) continue;
|
|
11859
|
-
const ext = path22.extname(relative12).toLowerCase();
|
|
11860
|
-
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
11768
|
+
function resolveProjectServerUrl() {
|
|
11769
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
11770
|
+
return availability.kind === "available" ? availability.url : null;
|
|
11771
|
+
}
|
|
11772
|
+
function projectIndexServerExpectedBuildId() {
|
|
11773
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
11774
|
+
if (override) return override;
|
|
11775
|
+
const url = resolveProjectServerUrl();
|
|
11776
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
11777
|
+
}
|
|
11778
|
+
function isProjectIndexServerAvailable() {
|
|
11779
|
+
return resolveProjectServerUrl() !== null;
|
|
11780
|
+
}
|
|
11781
|
+
function publishConnectionState(endpoint, state) {
|
|
11782
|
+
connectionStates.set(endpoint, state);
|
|
11783
|
+
latestConnectionState = state;
|
|
11784
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
11785
|
+
}
|
|
11786
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
11787
|
+
if (projectRoot) {
|
|
11788
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
11789
|
+
const existing = connectionStates.get(endpoint);
|
|
11790
|
+
if (existing) return existing;
|
|
11791
|
+
if (!isProjectIndexServerAvailable()) {
|
|
11792
|
+
return { status: "unavailable", connected: false };
|
|
11861
11793
|
}
|
|
11862
11794
|
return {
|
|
11863
|
-
|
|
11864
|
-
|
|
11795
|
+
status: "offline",
|
|
11796
|
+
connected: false,
|
|
11797
|
+
projectRoot,
|
|
11798
|
+
indexDir,
|
|
11799
|
+
endpoint
|
|
11865
11800
|
};
|
|
11866
|
-
} catch {
|
|
11867
|
-
return null;
|
|
11868
11801
|
}
|
|
11802
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
11803
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
11804
|
+
return latestConnectionState;
|
|
11869
11805
|
}
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
return {
|
|
11874
|
-
files: gitFiles.files,
|
|
11875
|
-
complete: true,
|
|
11876
|
-
errors: [],
|
|
11877
|
-
trustedUnchanged: gitFiles.trustedUnchanged
|
|
11878
|
-
};
|
|
11879
|
-
}
|
|
11880
|
-
const results = [];
|
|
11881
|
-
const errors = [];
|
|
11882
|
-
let complete = true;
|
|
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 };
|
|
11806
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
11807
|
+
connectionStateListeners.add(listener);
|
|
11808
|
+
return () => connectionStateListeners.delete(listener);
|
|
11919
11809
|
}
|
|
11920
|
-
function
|
|
11921
|
-
if (
|
|
11922
|
-
|
|
11923
|
-
const
|
|
11924
|
-
|
|
11925
|
-
|
|
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;
|
|
11810
|
+
function remoteError(message, name) {
|
|
11811
|
+
if (name === "LockError") return new LockError(message);
|
|
11812
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
11813
|
+
const error = new Error(message);
|
|
11814
|
+
if (name && name !== "Error") error.name = name;
|
|
11815
|
+
return error;
|
|
11939
11816
|
}
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
const
|
|
11943
|
-
const
|
|
11944
|
-
const
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
} else {
|
|
11963
|
-
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
11964
|
-
files = discovery.files;
|
|
11965
|
-
errors.push(...discovery.errors);
|
|
11966
|
-
discoveryComplete = discovery.complete;
|
|
11967
|
-
discoveredFiles = new Set(files);
|
|
11968
|
-
trustedUnchanged = discovery.trustedUnchanged;
|
|
11817
|
+
function isProjectIndexServerHealth(value) {
|
|
11818
|
+
if (!value || typeof value !== "object") return false;
|
|
11819
|
+
const health = value;
|
|
11820
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
11821
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
11822
|
+
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";
|
|
11823
|
+
}
|
|
11824
|
+
function delay(ms) {
|
|
11825
|
+
return new Promise((resolve16) => {
|
|
11826
|
+
const timer = setTimeout(resolve16, ms);
|
|
11827
|
+
timer.unref?.();
|
|
11828
|
+
});
|
|
11829
|
+
}
|
|
11830
|
+
function cancellationError(signal) {
|
|
11831
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
11832
|
+
}
|
|
11833
|
+
var ProjectServerConnection = class {
|
|
11834
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
11835
|
+
this.projectRoot = projectRoot;
|
|
11836
|
+
this.indexDir = indexDir;
|
|
11837
|
+
this.endpoint = endpoint;
|
|
11838
|
+
this.transition("offline");
|
|
11969
11839
|
}
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11840
|
+
projectRoot;
|
|
11841
|
+
indexDir;
|
|
11842
|
+
endpoint;
|
|
11843
|
+
socket = null;
|
|
11844
|
+
buffer = "";
|
|
11845
|
+
info = null;
|
|
11846
|
+
activity = null;
|
|
11847
|
+
health = null;
|
|
11848
|
+
healthCheck = null;
|
|
11849
|
+
connecting = null;
|
|
11850
|
+
connectResolve = null;
|
|
11851
|
+
connectReject = null;
|
|
11852
|
+
nextId = 1;
|
|
11853
|
+
pending = /* @__PURE__ */ new Map();
|
|
11854
|
+
transition(status, options = {}) {
|
|
11855
|
+
const previous = connectionStates.get(this.endpoint);
|
|
11856
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
11857
|
+
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);
|
|
11858
|
+
publishConnectionState(this.endpoint, {
|
|
11859
|
+
status,
|
|
11860
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
11861
|
+
projectRoot: this.projectRoot,
|
|
11862
|
+
indexDir: this.indexDir,
|
|
11863
|
+
endpoint: this.endpoint,
|
|
11864
|
+
pid,
|
|
11865
|
+
lastError,
|
|
11866
|
+
...this.activity ? { activity: this.activity } : {},
|
|
11867
|
+
...this.health ? { health: this.health } : {}
|
|
11975
11868
|
});
|
|
11976
11869
|
}
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
if (!force) {
|
|
11980
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
11981
|
-
}
|
|
11982
|
-
const totalFilesForProgress = files.length;
|
|
11983
|
-
let filesPreSkipped = 0;
|
|
11984
|
-
if (!force && trustedUnchanged) {
|
|
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);
|
|
11870
|
+
isConnected() {
|
|
11871
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
11995
11872
|
}
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
const
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
11873
|
+
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
11874
|
+
await this.ensureConnected(spawnIfMissing);
|
|
11875
|
+
if (this.healthCheck) return this.healthCheck;
|
|
11876
|
+
const startedAt = Date.now();
|
|
11877
|
+
this.healthCheck = this.request({ type: "ping" }, { timeoutMs }).then((server) => {
|
|
11878
|
+
const now = Date.now();
|
|
11879
|
+
this.health = {
|
|
11880
|
+
status: "healthy",
|
|
11881
|
+
checkedAt: now,
|
|
11882
|
+
lastHealthyAt: now,
|
|
11883
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
11884
|
+
missedHeartbeats: 0,
|
|
11885
|
+
...isProjectIndexServerHealth(server) ? { server } : {}
|
|
11886
|
+
};
|
|
11887
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
11888
|
+
return this.health;
|
|
11889
|
+
}).catch((error) => {
|
|
11890
|
+
if (!this.isConnected()) throw error;
|
|
11891
|
+
if ((this.health?.lastHealthyAt ?? 0) > startedAt) return this.health;
|
|
11892
|
+
const missedHeartbeats = (this.health?.missedHeartbeats ?? 0) + 1;
|
|
11893
|
+
const status = missedHeartbeats >= 3 ? "unresponsive" : "degraded";
|
|
11894
|
+
this.health = {
|
|
11895
|
+
status,
|
|
11896
|
+
checkedAt: Date.now(),
|
|
11897
|
+
lastHealthyAt: this.health?.lastHealthyAt ?? null,
|
|
11898
|
+
latencyMs: null,
|
|
11899
|
+
missedHeartbeats,
|
|
11900
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
11901
|
+
};
|
|
11902
|
+
this.transition(status, { pid: this.info?.pid, error });
|
|
11903
|
+
return this.health;
|
|
11904
|
+
}).finally(() => {
|
|
11905
|
+
this.healthCheck = null;
|
|
11906
|
+
});
|
|
11907
|
+
return this.healthCheck;
|
|
11908
|
+
}
|
|
11909
|
+
markResponsive() {
|
|
11910
|
+
const now = Date.now();
|
|
11911
|
+
this.health = {
|
|
11912
|
+
status: "healthy",
|
|
11913
|
+
checkedAt: now,
|
|
11914
|
+
lastHealthyAt: now,
|
|
11915
|
+
latencyMs: this.health?.latencyMs ?? null,
|
|
11916
|
+
missedHeartbeats: 0,
|
|
11917
|
+
...this.health?.server ? { server: this.health.server } : {}
|
|
11918
|
+
};
|
|
11919
|
+
}
|
|
11920
|
+
async call(op, args, options) {
|
|
11921
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
11922
|
+
await this.ensureConnected(true);
|
|
11923
|
+
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
11924
|
+
return this.request({ type: "request", op, args }, options);
|
|
11925
|
+
}
|
|
11926
|
+
async shutdownRemote(reason) {
|
|
11927
|
+
try {
|
|
11928
|
+
await this.ensureConnected(false);
|
|
11929
|
+
} catch {
|
|
11930
|
+
return { stopped: false, reason: "not-running" };
|
|
12010
11931
|
}
|
|
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
|
-
)
|
|
11932
|
+
const pid = this.info?.pid;
|
|
11933
|
+
try {
|
|
11934
|
+
this.transition("stopping", { pid });
|
|
11935
|
+
await this.request(
|
|
11936
|
+
{ type: "shutdown", reason },
|
|
11937
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
11938
|
+
);
|
|
11939
|
+
return { stopped: true, pid };
|
|
11940
|
+
} catch (error) {
|
|
11941
|
+
const forceKilled = this.forceKillKnownServer();
|
|
11942
|
+
return {
|
|
11943
|
+
stopped: forceKilled,
|
|
11944
|
+
pid,
|
|
11945
|
+
reason: forceKilled ? `force-killed after graceful shutdown failed: ${error instanceof Error ? error.message : String(error)}` : error instanceof Error ? error.message : String(error)
|
|
11946
|
+
};
|
|
11947
|
+
} finally {
|
|
11948
|
+
this.close();
|
|
11949
|
+
}
|
|
11950
|
+
}
|
|
11951
|
+
async configure(watchExternal, debounceMs) {
|
|
11952
|
+
await this.ensureConnected(true);
|
|
11953
|
+
const startedAt = Date.now();
|
|
11954
|
+
const result = await this.request(
|
|
11955
|
+
{ type: "configure", watchExternal, debounceMs },
|
|
11956
|
+
{ timeoutMs: SERVER_CONTROL_TIMEOUT_MS }
|
|
12073
11957
|
);
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
11958
|
+
if (isProjectIndexServerHealth(result.health)) {
|
|
11959
|
+
const now = Date.now();
|
|
11960
|
+
this.health = {
|
|
11961
|
+
status: "healthy",
|
|
11962
|
+
checkedAt: now,
|
|
11963
|
+
lastHealthyAt: now,
|
|
11964
|
+
latencyMs: Math.max(0, now - startedAt),
|
|
11965
|
+
missedHeartbeats: 0,
|
|
11966
|
+
server: result.health
|
|
11967
|
+
};
|
|
11968
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
11971
|
+
close() {
|
|
11972
|
+
const socket = this.socket;
|
|
11973
|
+
this.socket = null;
|
|
11974
|
+
this.info = null;
|
|
11975
|
+
this.activity = null;
|
|
11976
|
+
this.health = null;
|
|
11977
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
11978
|
+
this.connectResolve = null;
|
|
11979
|
+
this.connectReject = null;
|
|
11980
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
11981
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
11982
|
+
this.transition("offline");
|
|
11983
|
+
maybeStopHeartbeatLoop();
|
|
11984
|
+
}
|
|
11985
|
+
request(message, options) {
|
|
11986
|
+
const socket = this.socket;
|
|
11987
|
+
if (!socket || socket.destroyed) {
|
|
11988
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
11989
|
+
}
|
|
11990
|
+
const id = this.nextId++;
|
|
11991
|
+
return new Promise((resolve16, reject) => {
|
|
11992
|
+
const timer = setTimeout(() => {
|
|
11993
|
+
const entry = this.pending.get(id);
|
|
11994
|
+
if (!entry) return;
|
|
11995
|
+
this.pending.delete(id);
|
|
11996
|
+
this.write({ type: "cancel", id });
|
|
11997
|
+
const error = new IndexTimeoutError(
|
|
11998
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
11999
|
+
);
|
|
12000
|
+
this.cleanupPending(entry);
|
|
12001
|
+
entry.reject(error);
|
|
12002
|
+
}, options.timeoutMs);
|
|
12003
|
+
timer.unref?.();
|
|
12004
|
+
const signal = options.signal;
|
|
12005
|
+
const onAbort = signal ? () => {
|
|
12006
|
+
const entry = this.pending.get(id);
|
|
12007
|
+
if (!entry) return;
|
|
12008
|
+
this.pending.delete(id);
|
|
12009
|
+
this.write({ type: "cancel", id });
|
|
12010
|
+
this.cleanupPending(entry);
|
|
12011
|
+
entry.reject(cancellationError(signal));
|
|
12012
|
+
} : void 0;
|
|
12013
|
+
this.pending.set(id, {
|
|
12014
|
+
resolve: resolve16,
|
|
12015
|
+
reject,
|
|
12016
|
+
timer,
|
|
12017
|
+
signal,
|
|
12018
|
+
onAbort,
|
|
12019
|
+
onProgress: options.onProgress
|
|
12020
|
+
});
|
|
12021
|
+
if (signal && onAbort) {
|
|
12022
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
12023
|
+
if (signal.aborted) {
|
|
12024
|
+
onAbort();
|
|
12025
|
+
return;
|
|
12026
|
+
}
|
|
12084
12027
|
}
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12028
|
+
this.write({ ...message, id });
|
|
12029
|
+
});
|
|
12030
|
+
}
|
|
12031
|
+
async ensureConnected(spawnIfMissing) {
|
|
12032
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
12033
|
+
if (this.connecting) return this.connecting;
|
|
12034
|
+
this.transition("connecting");
|
|
12035
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
12036
|
+
this.transition("error", { error });
|
|
12037
|
+
throw error;
|
|
12038
|
+
}).finally(() => {
|
|
12039
|
+
this.connecting = null;
|
|
12040
|
+
});
|
|
12041
|
+
return this.connecting;
|
|
12042
|
+
}
|
|
12043
|
+
async connectWithElection(spawnIfMissing) {
|
|
12044
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12045
|
+
let spawned = false;
|
|
12046
|
+
let staleAttempts = 0;
|
|
12047
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
12048
|
+
while (Date.now() < deadline) {
|
|
12049
|
+
try {
|
|
12050
|
+
await this.connectOnce();
|
|
12051
|
+
return;
|
|
12052
|
+
} catch (error) {
|
|
12053
|
+
lastError = error;
|
|
12054
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
12055
|
+
staleAttempts++;
|
|
12056
|
+
if (!spawnIfMissing) break;
|
|
12057
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
12058
|
+
spawned = false;
|
|
12059
|
+
await delay(100);
|
|
12060
|
+
continue;
|
|
12061
|
+
}
|
|
12090
12062
|
}
|
|
12091
|
-
|
|
12092
|
-
if (
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
filesIndexed++;
|
|
12096
|
-
continue;
|
|
12063
|
+
if (!spawnIfMissing) break;
|
|
12064
|
+
if (!spawned) {
|
|
12065
|
+
this.spawnDetachedServer();
|
|
12066
|
+
spawned = true;
|
|
12097
12067
|
}
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12068
|
+
await delay(75);
|
|
12069
|
+
}
|
|
12070
|
+
throw lastError;
|
|
12071
|
+
}
|
|
12072
|
+
connectOnce() {
|
|
12073
|
+
this.socket?.destroy();
|
|
12074
|
+
this.socket = null;
|
|
12075
|
+
this.info = null;
|
|
12076
|
+
this.activity = null;
|
|
12077
|
+
this.health = null;
|
|
12078
|
+
this.buffer = "";
|
|
12079
|
+
return new Promise((resolve16, reject) => {
|
|
12080
|
+
const socket = net3.createConnection(this.endpoint);
|
|
12081
|
+
this.socket = socket;
|
|
12082
|
+
socket.setEncoding("utf8");
|
|
12083
|
+
const timer = setTimeout(() => {
|
|
12084
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
12085
|
+
socket.destroy();
|
|
12086
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
12087
|
+
timer.unref?.();
|
|
12088
|
+
const finishResolve = () => {
|
|
12089
|
+
clearTimeout(timer);
|
|
12090
|
+
this.connectResolve = null;
|
|
12091
|
+
this.connectReject = null;
|
|
12092
|
+
resolve16();
|
|
12093
|
+
};
|
|
12094
|
+
const finishReject = (error) => {
|
|
12095
|
+
clearTimeout(timer);
|
|
12096
|
+
this.connectResolve = null;
|
|
12097
|
+
this.connectReject = null;
|
|
12098
|
+
reject(error);
|
|
12099
|
+
};
|
|
12100
|
+
this.connectResolve = finishResolve;
|
|
12101
|
+
this.connectReject = finishReject;
|
|
12102
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
12103
|
+
socket.on("error", (error) => {
|
|
12104
|
+
if (!this.info) finishReject(error);
|
|
12105
|
+
});
|
|
12106
|
+
socket.on("close", () => this.onClose(socket));
|
|
12107
|
+
});
|
|
12108
|
+
}
|
|
12109
|
+
onData(socket, chunk) {
|
|
12110
|
+
if (socket !== this.socket) return;
|
|
12111
|
+
this.buffer += chunk;
|
|
12112
|
+
while (true) {
|
|
12113
|
+
const newline = this.buffer.indexOf("\n");
|
|
12114
|
+
if (newline < 0) {
|
|
12115
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12116
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12108
12117
|
}
|
|
12109
|
-
|
|
12118
|
+
return;
|
|
12110
12119
|
}
|
|
12111
|
-
if (
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
lang,
|
|
12115
|
-
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12116
|
-
symbolCount: 0,
|
|
12117
|
-
lastIndexed: Date.now()
|
|
12118
|
-
});
|
|
12119
|
-
filesIndexed++;
|
|
12120
|
-
continue;
|
|
12120
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
12121
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
12122
|
+
return;
|
|
12121
12123
|
}
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
refs: parsed.refs ?? [],
|
|
12127
|
-
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12128
|
-
symbolCount: parsed.symbols.length
|
|
12129
|
-
});
|
|
12130
|
-
deleteForFiles.push(file);
|
|
12131
|
-
}
|
|
12132
|
-
if (batchEntries.length > 0) {
|
|
12124
|
+
const line = this.buffer.slice(0, newline);
|
|
12125
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
12126
|
+
if (!line) continue;
|
|
12127
|
+
let message;
|
|
12133
12128
|
try {
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
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
|
-
}
|
|
12129
|
+
message = JSON.parse(line);
|
|
12130
|
+
} catch {
|
|
12131
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
12132
|
+
return;
|
|
12133
|
+
}
|
|
12134
|
+
this.onMessage(message);
|
|
12135
|
+
}
|
|
12136
|
+
}
|
|
12137
|
+
onMessage(message) {
|
|
12138
|
+
if (message.type === "hello") {
|
|
12139
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
12140
|
+
this.rejectStaleServer(
|
|
12141
|
+
message,
|
|
12142
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
12143
|
+
);
|
|
12144
|
+
return;
|
|
12173
12145
|
}
|
|
12146
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
12147
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
12148
|
+
this.rejectStaleServer(
|
|
12149
|
+
message,
|
|
12150
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
12151
|
+
);
|
|
12152
|
+
return;
|
|
12153
|
+
}
|
|
12154
|
+
this.info = message;
|
|
12155
|
+
this.markResponsive();
|
|
12156
|
+
this.transition("connected", { pid: message.pid });
|
|
12157
|
+
ensureHeartbeatLoop();
|
|
12158
|
+
this.connectResolve?.();
|
|
12159
|
+
return;
|
|
12160
|
+
}
|
|
12161
|
+
if (message.type === "index-state") {
|
|
12162
|
+
this.activity = message.state;
|
|
12163
|
+
this.markResponsive();
|
|
12164
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12165
|
+
return;
|
|
12166
|
+
}
|
|
12167
|
+
const entry = this.pending.get(message.id);
|
|
12168
|
+
if (!entry) return;
|
|
12169
|
+
this.markResponsive();
|
|
12170
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
12171
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
12172
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
12173
|
+
}
|
|
12174
|
+
if (message.type === "progress") {
|
|
12175
|
+
entry.onProgress?.(message.current, message.total);
|
|
12176
|
+
return;
|
|
12177
|
+
}
|
|
12178
|
+
this.pending.delete(message.id);
|
|
12179
|
+
this.cleanupPending(entry);
|
|
12180
|
+
if (message.ok) entry.resolve(message.result);
|
|
12181
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
12182
|
+
}
|
|
12183
|
+
onClose(socket) {
|
|
12184
|
+
if (socket !== this.socket) return;
|
|
12185
|
+
const wasConnected = this.info !== null;
|
|
12186
|
+
this.socket = null;
|
|
12187
|
+
this.info = null;
|
|
12188
|
+
this.activity = null;
|
|
12189
|
+
this.health = null;
|
|
12190
|
+
const error = new Error("codebase-index server connection closed");
|
|
12191
|
+
this.connectReject?.(error);
|
|
12192
|
+
this.connectResolve = null;
|
|
12193
|
+
this.connectReject = null;
|
|
12194
|
+
this.rejectPending(error);
|
|
12195
|
+
if (wasConnected) this.transition("error", { error });
|
|
12196
|
+
maybeStopHeartbeatLoop();
|
|
12197
|
+
}
|
|
12198
|
+
cleanupPending(entry) {
|
|
12199
|
+
clearTimeout(entry.timer);
|
|
12200
|
+
if (entry.signal && entry.onAbort) {
|
|
12201
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
12174
12202
|
}
|
|
12175
12203
|
}
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12204
|
+
rejectPending(error) {
|
|
12205
|
+
const entries = [...this.pending.values()];
|
|
12206
|
+
this.pending.clear();
|
|
12207
|
+
for (const entry of entries) {
|
|
12208
|
+
this.cleanupPending(entry);
|
|
12209
|
+
entry.reject(error);
|
|
12181
12210
|
}
|
|
12182
12211
|
}
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
+
write(message) {
|
|
12213
|
+
const socket = this.socket;
|
|
12214
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
12215
|
+
}
|
|
12216
|
+
rejectStaleServer(message, reason) {
|
|
12217
|
+
const socket = this.socket;
|
|
12218
|
+
if (socket && !socket.destroyed) {
|
|
12219
|
+
socket.write(
|
|
12220
|
+
encodeProjectServerMessage({
|
|
12221
|
+
type: "shutdown",
|
|
12222
|
+
id: 0,
|
|
12223
|
+
reason: "stale-build-replacement"
|
|
12224
|
+
})
|
|
12225
|
+
);
|
|
12226
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
12227
|
+
timer.unref?.();
|
|
12228
|
+
}
|
|
12229
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12230
|
+
}
|
|
12231
|
+
spawnDetachedServer() {
|
|
12232
|
+
const url = resolveProjectServerUrl();
|
|
12233
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
12234
|
+
if (process.platform !== "win32") {
|
|
12235
|
+
try {
|
|
12236
|
+
fs12.rmSync(this.endpoint, { force: true });
|
|
12237
|
+
} catch {
|
|
12238
|
+
}
|
|
12239
|
+
}
|
|
12240
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
12241
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
12242
|
+
const child = spawn4(process.execPath, args, {
|
|
12243
|
+
detached: true,
|
|
12244
|
+
stdio: "ignore",
|
|
12245
|
+
windowsHide: true,
|
|
12246
|
+
env: process.env
|
|
12212
12247
|
});
|
|
12213
|
-
|
|
12214
|
-
indexStorePool.release(store);
|
|
12248
|
+
child.unref();
|
|
12215
12249
|
}
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
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);
|
|
12250
|
+
forceKillKnownServer() {
|
|
12251
|
+
const pid = this.info?.pid;
|
|
12252
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
12232
12253
|
}
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12254
|
+
forceKillServer(pid) {
|
|
12255
|
+
if (pid === process.pid) return false;
|
|
12256
|
+
try {
|
|
12257
|
+
process.kill(pid);
|
|
12258
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
12259
|
+
try {
|
|
12260
|
+
const metadata = JSON.parse(fs12.readFileSync(metadataPath, "utf8"));
|
|
12261
|
+
if (metadata.pid === pid) fs12.rmSync(metadataPath, { force: true });
|
|
12262
|
+
} catch {
|
|
12263
|
+
}
|
|
12264
|
+
return true;
|
|
12265
|
+
} catch {
|
|
12266
|
+
return false;
|
|
12267
|
+
}
|
|
12240
12268
|
}
|
|
12269
|
+
};
|
|
12270
|
+
var connections = /* @__PURE__ */ new Map();
|
|
12271
|
+
var heartbeatTimer;
|
|
12272
|
+
function ensureHeartbeatLoop() {
|
|
12273
|
+
if (heartbeatTimer) return;
|
|
12274
|
+
heartbeatTimer = setInterval(() => {
|
|
12275
|
+
for (const connection of connections.values()) {
|
|
12276
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
12277
|
+
});
|
|
12278
|
+
}
|
|
12279
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
12280
|
+
heartbeatTimer.unref?.();
|
|
12241
12281
|
}
|
|
12242
|
-
function
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
indexStorePool.release(store);
|
|
12248
|
-
}
|
|
12282
|
+
function maybeStopHeartbeatLoop() {
|
|
12283
|
+
if (!heartbeatTimer) return;
|
|
12284
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
12285
|
+
clearInterval(heartbeatTimer);
|
|
12286
|
+
heartbeatTimer = void 0;
|
|
12249
12287
|
}
|
|
12250
|
-
function
|
|
12251
|
-
const
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12288
|
+
function connectionFor(projectRoot, indexDir) {
|
|
12289
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
12290
|
+
let connection = connections.get(endpoint);
|
|
12291
|
+
if (!connection) {
|
|
12292
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
12293
|
+
connections.set(endpoint, connection);
|
|
12256
12294
|
}
|
|
12295
|
+
return connection;
|
|
12257
12296
|
}
|
|
12258
|
-
function
|
|
12259
|
-
|
|
12260
|
-
try {
|
|
12261
|
-
return store.getSymbolGraph(args.fileFilter);
|
|
12262
|
-
} finally {
|
|
12263
|
-
indexStorePool.release(store);
|
|
12264
|
-
}
|
|
12297
|
+
function callProjectIndexServer(op, args, options) {
|
|
12298
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
12265
12299
|
}
|
|
12266
12300
|
|
|
12267
|
-
// src/codebase-index/
|
|
12268
|
-
import
|
|
12269
|
-
import
|
|
12270
|
-
import
|
|
12271
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12301
|
+
// src/codebase-index/background-indexer.ts
|
|
12302
|
+
import * as fs18 from "node:fs";
|
|
12303
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12304
|
+
import { Worker } from "node:worker_threads";
|
|
12272
12305
|
|
|
12273
|
-
// src/codebase-index/
|
|
12274
|
-
import {
|
|
12275
|
-
import
|
|
12276
|
-
import * as
|
|
12306
|
+
// src/codebase-index/indexer.ts
|
|
12307
|
+
import { expectDefined as expectDefined6 } from "@wrongstack/core/utils";
|
|
12308
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
12309
|
+
import * as fs17 from "node:fs/promises";
|
|
12310
|
+
import { availableParallelism } from "node:os";
|
|
12277
12311
|
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);
|
|
12312
|
+
import {
|
|
12313
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
12314
|
+
indexParallelBatchSize,
|
|
12315
|
+
isFrugalPerf
|
|
12316
|
+
} from "@wrongstack/core/utils";
|
|
12317
|
+
|
|
12318
|
+
// src/codebase-index/gitignore.ts
|
|
12319
|
+
import * as fs13 from "node:fs/promises";
|
|
12320
|
+
import * as path17 from "node:path";
|
|
12321
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
12322
|
+
function globBody(glob) {
|
|
12323
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
12303
12324
|
}
|
|
12304
|
-
function
|
|
12305
|
-
const
|
|
12306
|
-
|
|
12307
|
-
|
|
12325
|
+
function compileGitignore(lines) {
|
|
12326
|
+
const rules = [];
|
|
12327
|
+
for (const raw of lines) {
|
|
12328
|
+
let line = raw.replace(/\r$/, "");
|
|
12329
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
12330
|
+
line = line.trim();
|
|
12331
|
+
let negated = false;
|
|
12332
|
+
if (line.startsWith("!")) {
|
|
12333
|
+
negated = true;
|
|
12334
|
+
line = line.slice(1);
|
|
12335
|
+
}
|
|
12336
|
+
let dirOnly = false;
|
|
12337
|
+
if (line.endsWith("/")) {
|
|
12338
|
+
dirOnly = true;
|
|
12339
|
+
line = line.slice(0, -1);
|
|
12340
|
+
}
|
|
12341
|
+
if (!line) continue;
|
|
12342
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
12343
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
12344
|
+
const body = globBody(line);
|
|
12345
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
12346
|
+
rules.push({
|
|
12347
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
12348
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
12349
|
+
negated,
|
|
12350
|
+
dirOnly
|
|
12351
|
+
});
|
|
12308
12352
|
}
|
|
12309
|
-
return
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12353
|
+
return (relPath, isDir) => {
|
|
12354
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
12355
|
+
let ignored = false;
|
|
12356
|
+
for (const r of rules) {
|
|
12357
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
12358
|
+
if (re.test(p)) ignored = !r.negated;
|
|
12359
|
+
}
|
|
12360
|
+
return ignored;
|
|
12361
|
+
};
|
|
12314
12362
|
}
|
|
12315
|
-
function
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12363
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
12364
|
+
let lines = [];
|
|
12365
|
+
try {
|
|
12366
|
+
const raw = await fs13.readFile(path17.join(projectRoot, ".gitignore"), "utf8");
|
|
12367
|
+
lines = raw.split("\n");
|
|
12368
|
+
} catch {
|
|
12369
|
+
}
|
|
12370
|
+
return compileGitignore(lines);
|
|
12320
12371
|
}
|
|
12321
12372
|
|
|
12322
|
-
// src/codebase-index/
|
|
12323
|
-
|
|
12324
|
-
function encodeProjectServerMessage(message) {
|
|
12325
|
-
return `${JSON.stringify(message)}
|
|
12326
|
-
`;
|
|
12327
|
-
}
|
|
12373
|
+
// src/codebase-index/indexer.ts
|
|
12374
|
+
init_languages2();
|
|
12328
12375
|
|
|
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
|
-
|
|
12376
|
+
// src/codebase-index/parser-dispatch.ts
|
|
12377
|
+
async function parseFileContent(file, content, lang) {
|
|
12378
|
+
switch (lang) {
|
|
12379
|
+
case "ts":
|
|
12380
|
+
case "tsx":
|
|
12381
|
+
case "js":
|
|
12382
|
+
case "jsx": {
|
|
12383
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
12384
|
+
return parseSymbols8({ file, content, lang });
|
|
12385
|
+
}
|
|
12386
|
+
case "go": {
|
|
12387
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
12388
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
12389
|
+
}
|
|
12390
|
+
case "py": {
|
|
12391
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
12392
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
12393
|
+
}
|
|
12394
|
+
case "rs": {
|
|
12395
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
12396
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
12397
|
+
}
|
|
12398
|
+
case "json": {
|
|
12399
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
12400
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
12401
|
+
}
|
|
12402
|
+
case "yaml": {
|
|
12403
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
12404
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
12405
|
+
}
|
|
12406
|
+
default: {
|
|
12407
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
12408
|
+
return parseSymbols8({ file, content, lang });
|
|
12360
12409
|
}
|
|
12361
12410
|
}
|
|
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
12411
|
}
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12412
|
+
|
|
12413
|
+
// src/codebase-index/indexer.ts
|
|
12414
|
+
var YIELD_EVERY_N = 50;
|
|
12415
|
+
function resolveParallelBatch() {
|
|
12416
|
+
return indexParallelBatchSize(availableParallelism());
|
|
12381
12417
|
}
|
|
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;
|
|
12418
|
+
function yieldEventLoop() {
|
|
12419
|
+
return new Promise((resolve16) => setImmediate(resolve16));
|
|
12401
12420
|
}
|
|
12402
|
-
function
|
|
12403
|
-
|
|
12404
|
-
|
|
12421
|
+
function throwIfAborted(signal) {
|
|
12422
|
+
if (!signal?.aborted) return;
|
|
12423
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
12424
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
12405
12425
|
}
|
|
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;
|
|
12426
|
+
function isAbortError(err) {
|
|
12427
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
12412
12428
|
}
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12429
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
12430
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
12431
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
12432
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
12433
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
12434
|
+
function isWithinProject(projectRoot, file) {
|
|
12435
|
+
const rel = path23.relative(projectRoot, file);
|
|
12436
|
+
return rel !== "" && !rel.startsWith(`..${path23.sep}`) && rel !== ".." && !path23.isAbsolute(rel);
|
|
12419
12437
|
}
|
|
12420
|
-
function
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
timer.unref?.();
|
|
12424
|
-
});
|
|
12438
|
+
function isMissingPathError(err) {
|
|
12439
|
+
const code = err?.code;
|
|
12440
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
12425
12441
|
}
|
|
12426
|
-
function
|
|
12427
|
-
|
|
12442
|
+
function normalizeComparablePath(value) {
|
|
12443
|
+
const resolved = path23.resolve(value);
|
|
12444
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12428
12445
|
}
|
|
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 } : {}
|
|
12446
|
+
function gitOutput(projectRoot, args) {
|
|
12447
|
+
return new Promise((resolve16, reject) => {
|
|
12448
|
+
execFile2(
|
|
12449
|
+
"git",
|
|
12450
|
+
["-C", projectRoot, ...args],
|
|
12451
|
+
{
|
|
12452
|
+
encoding: "buffer",
|
|
12453
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
12454
|
+
windowsHide: true
|
|
12455
|
+
},
|
|
12456
|
+
(error, stdout) => {
|
|
12457
|
+
if (error) reject(error);
|
|
12458
|
+
else resolve16(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
12459
|
+
}
|
|
12460
|
+
);
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
12463
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
12464
|
+
try {
|
|
12465
|
+
throwIfAborted(signal);
|
|
12466
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
12467
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
12468
|
+
throwIfAborted(signal);
|
|
12469
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12470
|
+
const [output, statusOutput] = await Promise.all([
|
|
12471
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
12472
|
+
gitOutput(projectRoot, [
|
|
12473
|
+
"status",
|
|
12474
|
+
"--porcelain=v1",
|
|
12475
|
+
"-z",
|
|
12476
|
+
"--untracked-files=all",
|
|
12477
|
+
"--ignored=no"
|
|
12478
|
+
])
|
|
12479
|
+
]);
|
|
12480
|
+
throwIfAborted(signal);
|
|
12481
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
12482
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
12483
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
12484
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
12485
|
+
const record = statusRecords[i];
|
|
12486
|
+
if (!record) continue;
|
|
12487
|
+
const status = record.slice(0, 2);
|
|
12488
|
+
const changedPath = path23.resolve(projectRoot, record.slice(3));
|
|
12489
|
+
dirty.add(changedPath);
|
|
12490
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
12491
|
+
if (status.includes("R") || status.includes("C")) {
|
|
12492
|
+
const source = statusRecords[++i];
|
|
12493
|
+
if (source) dirty.add(path23.resolve(projectRoot, source));
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
const files = [];
|
|
12497
|
+
for (const relative12 of output.toString("utf8").split("\0")) {
|
|
12498
|
+
if (!relative12) continue;
|
|
12499
|
+
const portable = relative12.replace(/\\/g, "/");
|
|
12500
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path23.posix.basename(portable))) {
|
|
12501
|
+
continue;
|
|
12502
|
+
}
|
|
12503
|
+
const full = path23.resolve(projectRoot, relative12);
|
|
12504
|
+
if (deleted.has(full)) continue;
|
|
12505
|
+
const ext = path23.extname(relative12).toLowerCase();
|
|
12506
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
12507
|
+
}
|
|
12508
|
+
return {
|
|
12509
|
+
files,
|
|
12510
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
12514
12511
|
};
|
|
12512
|
+
} catch {
|
|
12513
|
+
return null;
|
|
12515
12514
|
}
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
return
|
|
12515
|
+
}
|
|
12516
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
12517
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
12518
|
+
if (gitFiles) {
|
|
12519
|
+
return {
|
|
12520
|
+
files: gitFiles.files,
|
|
12521
|
+
complete: true,
|
|
12522
|
+
errors: [],
|
|
12523
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
12524
|
+
};
|
|
12521
12525
|
}
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12526
|
+
const results = [];
|
|
12527
|
+
const errors = [];
|
|
12528
|
+
let complete = true;
|
|
12529
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
12530
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
12531
|
+
let dirCount = 0;
|
|
12532
|
+
const walk = async (dir) => {
|
|
12533
|
+
throwIfAborted(signal);
|
|
12534
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
12535
|
+
await yieldEventLoop();
|
|
12536
|
+
throwIfAborted(signal);
|
|
12527
12537
|
}
|
|
12528
|
-
|
|
12538
|
+
let entries;
|
|
12529
12539
|
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();
|
|
12540
|
+
entries = await fs17.readdir(dir, { withFileTypes: true });
|
|
12541
|
+
} catch (err) {
|
|
12542
|
+
complete = false;
|
|
12543
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12544
|
+
return;
|
|
12545
12545
|
}
|
|
12546
|
-
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12546
|
+
dirCount++;
|
|
12547
|
+
for (const e of entries) {
|
|
12548
|
+
if (ignoreSet.has(e.name)) continue;
|
|
12549
|
+
const full = path23.join(dir, e.name);
|
|
12550
|
+
const rel = path23.relative(projectRoot, full).replace(/\\/g, "/");
|
|
12551
|
+
if (e.isDirectory()) {
|
|
12552
|
+
if (isGitIgnored(rel, true)) continue;
|
|
12553
|
+
await walk(full);
|
|
12554
|
+
} else if (e.isFile()) {
|
|
12555
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
12556
|
+
const ext = path23.extname(e.name).toLowerCase();
|
|
12557
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
12558
|
+
results.push(full);
|
|
12559
|
+
}
|
|
12560
|
+
}
|
|
12561
|
+
}
|
|
12562
|
+
};
|
|
12563
|
+
await walk(projectRoot);
|
|
12564
|
+
return { files: results, complete, errors };
|
|
12565
|
+
}
|
|
12566
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
12567
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
12568
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
12569
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12570
|
+
const assigned = [];
|
|
12571
|
+
for (const ref of refs) {
|
|
12572
|
+
let owner2;
|
|
12573
|
+
for (const symbol of ordered) {
|
|
12574
|
+
if (symbol.line > ref.line) break;
|
|
12575
|
+
owner2 = symbol;
|
|
12565
12576
|
}
|
|
12577
|
+
if (!owner2 && ref.callType === "import") owner2 = ordered[0];
|
|
12578
|
+
if (!owner2 || owner2.id <= 0) continue;
|
|
12579
|
+
const key = `${owner2.id}:${ref.toName}:${ref.callType}`;
|
|
12580
|
+
if (seen.has(key)) continue;
|
|
12581
|
+
seen.add(key);
|
|
12582
|
+
assigned.push({ ...ref, fromId: owner2.id });
|
|
12566
12583
|
}
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12584
|
+
return assigned;
|
|
12585
|
+
}
|
|
12586
|
+
async function runIndexerWithStore(store, opts) {
|
|
12587
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
12588
|
+
const relationGraphVersion = "2";
|
|
12589
|
+
const refResolutionVersion = "2";
|
|
12590
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
12591
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
12592
|
+
const startMs = Date.now();
|
|
12593
|
+
const errors = [];
|
|
12594
|
+
const langStats = {};
|
|
12595
|
+
let filesIndexed = 0;
|
|
12596
|
+
let symbolsIndexed = 0;
|
|
12597
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
12598
|
+
let files;
|
|
12599
|
+
let discoveredFiles = null;
|
|
12600
|
+
let discoveryComplete = true;
|
|
12601
|
+
let trustedUnchanged;
|
|
12602
|
+
if (opts.files && opts.files.length > 0) {
|
|
12603
|
+
files = opts.files.map((f) => path23.resolve(projectRoot, f)).filter((f) => {
|
|
12604
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
12605
|
+
const rel = path23.relative(projectRoot, f).replace(/\\/g, "/");
|
|
12606
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path23.basename(f)) && !isGitIgnored(rel, false);
|
|
12607
|
+
});
|
|
12608
|
+
} else {
|
|
12609
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
12610
|
+
files = discovery.files;
|
|
12611
|
+
errors.push(...discovery.errors);
|
|
12612
|
+
discoveryComplete = discovery.complete;
|
|
12613
|
+
discoveredFiles = new Set(files);
|
|
12614
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
12580
12615
|
}
|
|
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 });
|
|
12616
|
+
if (langs && langs.length > 0) {
|
|
12617
|
+
const langSet = new Set(langs);
|
|
12618
|
+
files = files.filter((f) => {
|
|
12619
|
+
const lang = detectLang(f);
|
|
12620
|
+
return lang ? langSet.has(lang) : false;
|
|
12625
12621
|
});
|
|
12626
12622
|
}
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12623
|
+
if (force) store.clearAll();
|
|
12624
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
12625
|
+
if (!force) {
|
|
12626
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
12627
|
+
}
|
|
12628
|
+
const totalFilesForProgress = files.length;
|
|
12629
|
+
let filesPreSkipped = 0;
|
|
12630
|
+
if (!force && trustedUnchanged) {
|
|
12631
|
+
files = files.filter((file) => {
|
|
12632
|
+
const meta = existingMeta.get(file);
|
|
12633
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
12634
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
12635
|
+
symbolsIndexed += meta.symbolCount;
|
|
12636
|
+
filesIndexed++;
|
|
12637
|
+
filesPreSkipped++;
|
|
12638
|
+
return false;
|
|
12636
12639
|
});
|
|
12637
|
-
|
|
12640
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
12638
12641
|
}
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12642
|
+
const parallelBatch = resolveParallelBatch();
|
|
12643
|
+
let filesSinceLastYield = 0;
|
|
12644
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
12645
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
12646
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
12647
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
12648
|
+
filesSinceLastYield += batchFiles.length;
|
|
12649
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
12650
|
+
filesSinceLastYield = 0;
|
|
12651
|
+
await yieldEventLoop();
|
|
12652
|
+
if (isFrugalPerf()) {
|
|
12653
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
12654
|
+
}
|
|
12655
|
+
throwIfAborted(signal);
|
|
12656
|
+
}
|
|
12657
|
+
const statOpts = signal ? { signal } : {};
|
|
12658
|
+
const statReadParse = await Promise.allSettled(
|
|
12659
|
+
batchFiles.map(
|
|
12660
|
+
async (file) => {
|
|
12661
|
+
let stat17;
|
|
12662
|
+
try {
|
|
12663
|
+
stat17 = await fs17.stat(file, statOpts);
|
|
12664
|
+
} catch (e) {
|
|
12665
|
+
if (isAbortError(e)) throw e;
|
|
12666
|
+
return {
|
|
12667
|
+
file,
|
|
12668
|
+
stat: null,
|
|
12669
|
+
lang: "",
|
|
12670
|
+
parsed: null,
|
|
12671
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
12672
|
+
missing: isMissingPathError(e)
|
|
12673
|
+
};
|
|
12674
|
+
}
|
|
12675
|
+
if (!stat17.isFile()) return { file, stat: stat17, lang: "", parsed: null };
|
|
12676
|
+
const lang = detectLang(file);
|
|
12677
|
+
if (!lang) return { file, stat: stat17, lang: "", parsed: null };
|
|
12678
|
+
if (stat17.size > MAX_INDEX_FILE_BYTES) {
|
|
12679
|
+
return {
|
|
12680
|
+
file,
|
|
12681
|
+
stat: stat17,
|
|
12682
|
+
lang,
|
|
12683
|
+
parsed: null,
|
|
12684
|
+
error: `file too large (${stat17.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
12685
|
+
};
|
|
12686
|
+
}
|
|
12687
|
+
const meta = existingMeta.get(file);
|
|
12688
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat17.mtimeMs)) {
|
|
12689
|
+
return { file, stat: stat17, lang, parsed: null, skippedMeta: meta };
|
|
12690
|
+
}
|
|
12691
|
+
let content;
|
|
12692
|
+
try {
|
|
12693
|
+
content = await fs17.readFile(file, { encoding: "utf8", signal });
|
|
12694
|
+
} catch (e) {
|
|
12695
|
+
if (isAbortError(e)) throw e;
|
|
12696
|
+
return {
|
|
12697
|
+
file,
|
|
12698
|
+
stat: stat17,
|
|
12699
|
+
lang,
|
|
12700
|
+
parsed: null,
|
|
12701
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
12702
|
+
};
|
|
12703
|
+
}
|
|
12704
|
+
let parsed;
|
|
12705
|
+
try {
|
|
12706
|
+
parsed = await parseFileContent(file, content, lang);
|
|
12707
|
+
} catch (e) {
|
|
12708
|
+
return {
|
|
12709
|
+
file,
|
|
12710
|
+
stat: stat17,
|
|
12711
|
+
lang,
|
|
12712
|
+
parsed: null,
|
|
12713
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
12714
|
+
};
|
|
12715
|
+
}
|
|
12716
|
+
return { file, stat: stat17, lang, parsed, content };
|
|
12657
12717
|
}
|
|
12718
|
+
)
|
|
12719
|
+
);
|
|
12720
|
+
const batchEntries = [];
|
|
12721
|
+
const deleteForFiles = [];
|
|
12722
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
12723
|
+
const settled = statReadParse[fi];
|
|
12724
|
+
const file = expectDefined6(batchFiles[fi]);
|
|
12725
|
+
if (settled.status === "rejected") {
|
|
12726
|
+
const err = settled.reason;
|
|
12727
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
12728
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12729
|
+
continue;
|
|
12658
12730
|
}
|
|
12659
|
-
|
|
12660
|
-
if (
|
|
12661
|
-
|
|
12662
|
-
|
|
12731
|
+
const result = settled.value;
|
|
12732
|
+
if (result.error) {
|
|
12733
|
+
if (result.missing) store.deleteFile(file);
|
|
12734
|
+
errors.push(`${file}: ${result.error}`);
|
|
12735
|
+
continue;
|
|
12663
12736
|
}
|
|
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
|
-
|
|
12737
|
+
const { stat: stat17, lang, parsed } = result;
|
|
12738
|
+
if (result.skippedMeta) {
|
|
12739
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
12740
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
12741
|
+
filesIndexed++;
|
|
12742
|
+
continue;
|
|
12743
|
+
}
|
|
12744
|
+
if (!lang || !parsed) {
|
|
12745
|
+
if (lang) {
|
|
12746
|
+
store.upsertFile({
|
|
12747
|
+
file,
|
|
12748
|
+
lang,
|
|
12749
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12750
|
+
symbolCount: 0,
|
|
12751
|
+
lastIndexed: Date.now()
|
|
12752
|
+
});
|
|
12753
|
+
filesIndexed++;
|
|
12754
|
+
}
|
|
12755
|
+
continue;
|
|
12756
|
+
}
|
|
12757
|
+
if (parsed.symbols.length === 0) {
|
|
12758
|
+
store.replaceEmptyFile({
|
|
12759
|
+
file,
|
|
12760
|
+
lang,
|
|
12761
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12762
|
+
symbolCount: 0,
|
|
12763
|
+
lastIndexed: Date.now()
|
|
12764
|
+
});
|
|
12765
|
+
filesIndexed++;
|
|
12766
|
+
continue;
|
|
12767
|
+
}
|
|
12768
|
+
batchEntries.push({
|
|
12769
|
+
file,
|
|
12770
|
+
lang,
|
|
12771
|
+
symbols: parsed.symbols,
|
|
12772
|
+
refs: parsed.refs ?? [],
|
|
12773
|
+
mtimeMs: Math.floor(stat17.mtimeMs),
|
|
12774
|
+
symbolCount: parsed.symbols.length
|
|
12701
12775
|
});
|
|
12702
|
-
|
|
12703
|
-
}
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12776
|
+
deleteForFiles.push(file);
|
|
12777
|
+
}
|
|
12778
|
+
if (batchEntries.length > 0) {
|
|
12779
|
+
try {
|
|
12780
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
12781
|
+
for (const entry of batchEntries) {
|
|
12782
|
+
const count = entry.symbols.length;
|
|
12783
|
+
symbolsIndexed += count;
|
|
12784
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
12785
|
+
filesIndexed++;
|
|
12786
|
+
}
|
|
12787
|
+
} catch (err) {
|
|
12788
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12789
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
12790
|
+
for (const entry of batchEntries) {
|
|
12791
|
+
try {
|
|
12792
|
+
store.deleteRefsForFile(entry.file);
|
|
12793
|
+
store.deleteSymbolsForFile(entry.file);
|
|
12794
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
12795
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
12796
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
12797
|
+
filesIndexed++;
|
|
12798
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
12799
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
12800
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
12801
|
+
}
|
|
12802
|
+
store.resolveRefsForNames([
|
|
12803
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
12804
|
+
...entry.refs.map((ref) => ref.toName)
|
|
12805
|
+
]);
|
|
12806
|
+
store.upsertFile({
|
|
12807
|
+
file: entry.file,
|
|
12808
|
+
lang: entry.lang,
|
|
12809
|
+
mtimeMs: entry.mtimeMs,
|
|
12810
|
+
symbolCount: entry.symbolCount,
|
|
12811
|
+
lastIndexed: Date.now()
|
|
12812
|
+
});
|
|
12813
|
+
} catch (innerErr) {
|
|
12814
|
+
errors.push(
|
|
12815
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
12816
|
+
);
|
|
12817
|
+
}
|
|
12713
12818
|
}
|
|
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
12819
|
}
|
|
12730
|
-
this.onMessage(message);
|
|
12731
12820
|
}
|
|
12732
12821
|
}
|
|
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;
|
|
12822
|
+
if (discoveredFiles && discoveryComplete) {
|
|
12823
|
+
for (const [file_] of existingMeta) {
|
|
12824
|
+
if (!discoveredFiles.has(file_)) {
|
|
12825
|
+
store.deleteFile(file_);
|
|
12749
12826
|
}
|
|
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
12827
|
}
|
|
12825
|
-
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
12826
12828
|
}
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12829
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
12830
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
12831
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
12832
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
12833
|
+
store.setLastIndexed(Date.now());
|
|
12834
|
+
if (!opts.files) store.compactIfNeeded();
|
|
12835
|
+
const durationMs = Date.now() - startMs;
|
|
12836
|
+
return {
|
|
12837
|
+
filesIndexed,
|
|
12838
|
+
symbolsIndexed,
|
|
12839
|
+
langStats,
|
|
12840
|
+
durationMs,
|
|
12841
|
+
errors
|
|
12842
|
+
};
|
|
12843
|
+
}
|
|
12844
|
+
|
|
12845
|
+
// src/codebase-index/index-service.ts
|
|
12846
|
+
async function indexService(args, hooks = {}) {
|
|
12847
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12848
|
+
try {
|
|
12849
|
+
return await runIndexerWithStore(store, {
|
|
12850
|
+
projectRoot: args.projectRoot,
|
|
12851
|
+
indexDir: args.indexDir,
|
|
12852
|
+
files: args.files,
|
|
12853
|
+
force: args.force,
|
|
12854
|
+
langs: args.langs,
|
|
12855
|
+
ignore: args.ignore,
|
|
12856
|
+
signal: hooks.signal,
|
|
12857
|
+
onProgress: hooks.onProgress
|
|
12843
12858
|
});
|
|
12844
|
-
|
|
12859
|
+
} finally {
|
|
12860
|
+
indexStorePool.release(store);
|
|
12845
12861
|
}
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12862
|
+
}
|
|
12863
|
+
function searchService(args) {
|
|
12864
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12865
|
+
try {
|
|
12866
|
+
return store.searchRanked(
|
|
12867
|
+
args.query,
|
|
12868
|
+
{
|
|
12869
|
+
kind: args.kind,
|
|
12870
|
+
lang: args.lang,
|
|
12871
|
+
file: args.file,
|
|
12872
|
+
lspKind: args.lspKind
|
|
12873
|
+
},
|
|
12874
|
+
args.limit
|
|
12875
|
+
);
|
|
12876
|
+
} finally {
|
|
12877
|
+
indexStorePool.release(store);
|
|
12849
12878
|
}
|
|
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
|
-
}
|
|
12879
|
+
}
|
|
12880
|
+
function statsService(args) {
|
|
12881
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12882
|
+
try {
|
|
12883
|
+
return store.getStats();
|
|
12884
|
+
} finally {
|
|
12885
|
+
indexStorePool.release(store);
|
|
12864
12886
|
}
|
|
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
12887
|
}
|
|
12878
|
-
function
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12888
|
+
function packageGraphService(args) {
|
|
12889
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12890
|
+
try {
|
|
12891
|
+
return store.getPackageGraph();
|
|
12892
|
+
} finally {
|
|
12893
|
+
indexStorePool.release(store);
|
|
12894
|
+
}
|
|
12883
12895
|
}
|
|
12884
|
-
function
|
|
12885
|
-
const
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12896
|
+
function fileGraphService(args) {
|
|
12897
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12898
|
+
try {
|
|
12899
|
+
return store.getFileGraph(args.packageFilter);
|
|
12900
|
+
} finally {
|
|
12901
|
+
indexStorePool.release(store);
|
|
12890
12902
|
}
|
|
12891
|
-
return connection;
|
|
12892
12903
|
}
|
|
12893
|
-
function
|
|
12894
|
-
|
|
12904
|
+
function symbolGraphService(args) {
|
|
12905
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
12906
|
+
try {
|
|
12907
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
12908
|
+
} finally {
|
|
12909
|
+
indexStorePool.release(store);
|
|
12910
|
+
}
|
|
12895
12911
|
}
|
|
12896
12912
|
|
|
12897
12913
|
// src/codebase-index/background-indexer.ts
|
|
@@ -13002,8 +13018,17 @@ function terminateWorker(reason) {
|
|
|
13002
13018
|
if (w) void w.terminate().catch(() => {
|
|
13003
13019
|
});
|
|
13004
13020
|
}
|
|
13021
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
13022
|
+
function warnEndpointInvalidOnce(availability) {
|
|
13023
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
13024
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
13025
|
+
process.stderr.write(
|
|
13026
|
+
`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.
|
|
13027
|
+
`
|
|
13028
|
+
);
|
|
13029
|
+
}
|
|
13005
13030
|
function callIndexOp(op, args, opts) {
|
|
13006
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
13031
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
13007
13032
|
if (availability.kind === "available") {
|
|
13008
13033
|
return callProjectIndexServer(op, args, opts);
|
|
13009
13034
|
}
|
|
@@ -13014,6 +13039,14 @@ function callIndexOp(op, args, opts) {
|
|
|
13014
13039
|
)
|
|
13015
13040
|
);
|
|
13016
13041
|
}
|
|
13042
|
+
if (availability.kind === "endpoint-invalid") {
|
|
13043
|
+
warnEndpointInvalidOnce(availability);
|
|
13044
|
+
return Promise.reject(
|
|
13045
|
+
new Error(
|
|
13046
|
+
`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.`
|
|
13047
|
+
)
|
|
13048
|
+
);
|
|
13049
|
+
}
|
|
13017
13050
|
const w = ensureWorker();
|
|
13018
13051
|
if (!w) return callInline(op, args, opts);
|
|
13019
13052
|
if (opts.signal?.aborted) {
|