@wrongstack/tools 0.296.3 → 0.297.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +15 -0
- package/dist/audit.js.map +2 -2
- package/dist/bash.js +15 -0
- package/dist/bash.js.map +2 -2
- package/dist/builtin.js +1291 -1216
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/index.d.ts +1 -0
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +1259 -1196
- package/dist/codebase-index/index.js.map +4 -4
- package/dist/codebase-index/project-server-client.d.ts +13 -1
- package/dist/codebase-index/project-server-client.d.ts.map +1 -1
- package/dist/codebase-index/project-server-endpoint.d.ts +29 -1
- package/dist/codebase-index/project-server-endpoint.d.ts.map +1 -1
- package/dist/codebase-index/project-server.js +20 -5
- package/dist/codebase-index/project-server.js.map +2 -2
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/exec.js +15 -0
- package/dist/exec.js.map +2 -2
- package/dist/format.js +15 -0
- package/dist/format.js.map +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1260 -1182
- package/dist/index.js.map +4 -4
- package/dist/install.js +15 -0
- package/dist/install.js.map +2 -2
- package/dist/languages/index.js +15 -0
- package/dist/languages/index.js.map +2 -2
- package/dist/lint.js +15 -0
- package/dist/lint.js.map +2 -2
- package/dist/next-steps.d.ts +23 -0
- package/dist/next-steps.d.ts.map +1 -1
- package/dist/next-steps.js +4 -0
- package/dist/next-steps.js.map +2 -2
- package/dist/outdated.js +15 -0
- package/dist/outdated.js.map +2 -2
- package/dist/pack.js +1291 -1216
- package/dist/pack.js.map +4 -4
- package/dist/process-registry.d.ts +9 -0
- package/dist/process-registry.d.ts.map +1 -1
- package/dist/process-registry.js +15 -0
- package/dist/process-registry.js.map +2 -2
- package/dist/ps-slash.js +15 -0
- package/dist/ps-slash.js.map +2 -2
- package/dist/read.js +67 -9
- package/dist/read.js.map +2 -2
- package/dist/test.js +15 -0
- package/dist/test.js.map +2 -2
- package/dist/tool-tier.js +1291 -1216
- package/dist/tool-tier.js.map +4 -4
- package/dist/typecheck.js +15 -0
- package/dist/typecheck.js.map +2 -2
- package/package.json +3 -3
|
@@ -14,16 +14,16 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// src/codebase-index/languages.ts
|
|
17
|
-
import * as
|
|
17
|
+
import * as path6 from "node:path";
|
|
18
18
|
function detectLang(file) {
|
|
19
|
-
const base =
|
|
19
|
+
const base = path6.basename(file);
|
|
20
20
|
const lowerBase = base.toLowerCase();
|
|
21
21
|
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
22
22
|
return "ts";
|
|
23
23
|
}
|
|
24
24
|
const special = SPECIAL_FILENAMES[lowerBase];
|
|
25
25
|
if (special) return special;
|
|
26
|
-
const ext =
|
|
26
|
+
const ext = path6.extname(base).toLowerCase();
|
|
27
27
|
if (!ext) return null;
|
|
28
28
|
return EXT_TO_LANG[ext] ?? null;
|
|
29
29
|
}
|
|
@@ -340,21 +340,21 @@ var init_ts_parser = __esm({
|
|
|
340
340
|
});
|
|
341
341
|
|
|
342
342
|
// src/_win32-resolve.ts
|
|
343
|
-
import * as
|
|
344
|
-
import * as
|
|
343
|
+
import * as fs6 from "node:fs";
|
|
344
|
+
import * as path7 from "node:path";
|
|
345
345
|
function resolveWin32Command(cmd) {
|
|
346
346
|
if (process.platform !== "win32") return cmd;
|
|
347
|
-
if (cmd.includes("/") || cmd.includes("\\") ||
|
|
347
|
+
if (cmd.includes("/") || cmd.includes("\\") || path7.extname(cmd.replace(/\//g, "\\"))) {
|
|
348
348
|
return cmd;
|
|
349
349
|
}
|
|
350
350
|
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
351
|
-
const pathDirs = (process.env["PATH"] ?? "").split(
|
|
351
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path7.delimiter);
|
|
352
352
|
for (const dir of pathDirs) {
|
|
353
|
-
const base =
|
|
353
|
+
const base = path7.join(dir, cmd);
|
|
354
354
|
for (const ext of pathext) {
|
|
355
355
|
const full = `${base}${ext}`;
|
|
356
356
|
try {
|
|
357
|
-
|
|
357
|
+
fs6.accessSync(full, fs6.constants.X_OK);
|
|
358
358
|
return full;
|
|
359
359
|
} catch {
|
|
360
360
|
}
|
|
@@ -391,10 +391,10 @@ __export(go_parser_exports, {
|
|
|
391
391
|
detectLang: () => detectLang,
|
|
392
392
|
parseSymbols: () => parseSymbols2
|
|
393
393
|
});
|
|
394
|
-
import { spawn } from "node:child_process";
|
|
395
|
-
import * as
|
|
396
|
-
import * as
|
|
397
|
-
import * as
|
|
394
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
395
|
+
import * as os2 from "node:os";
|
|
396
|
+
import * as path8 from "node:path";
|
|
397
|
+
import * as fs7 from "node:fs/promises";
|
|
398
398
|
async function parseSymbols2(opts) {
|
|
399
399
|
const { file, content, lang } = opts;
|
|
400
400
|
try {
|
|
@@ -466,16 +466,16 @@ async function syncGoParse(filePath, content, lang) {
|
|
|
466
466
|
try {
|
|
467
467
|
let scriptPath = _cachedGoScriptPath;
|
|
468
468
|
if (!scriptPath) {
|
|
469
|
-
const tmpDir = await
|
|
470
|
-
scriptPath =
|
|
471
|
-
await
|
|
469
|
+
const tmpDir = await fs7.mkdtemp(path8.join(os2.tmpdir(), "ws-go-parse-"));
|
|
470
|
+
scriptPath = path8.join(tmpDir, "parse.go");
|
|
471
|
+
await fs7.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
472
472
|
_cachedGoScriptPath = scriptPath;
|
|
473
473
|
}
|
|
474
474
|
const goBinary = resolveWin32Command("go");
|
|
475
475
|
const goResult = await new Promise(
|
|
476
476
|
(resolve4, reject) => {
|
|
477
477
|
let settled = false;
|
|
478
|
-
const proc =
|
|
478
|
+
const proc = spawn2(goBinary, ["run", scriptPath], {
|
|
479
479
|
stdio: ["pipe", "pipe", "pipe"],
|
|
480
480
|
windowsHide: true
|
|
481
481
|
});
|
|
@@ -1078,10 +1078,10 @@ __export(py_parser_exports, {
|
|
|
1078
1078
|
detectLang: () => detectLang,
|
|
1079
1079
|
parseSymbols: () => parseSymbols4
|
|
1080
1080
|
});
|
|
1081
|
-
import { spawn as
|
|
1082
|
-
import * as
|
|
1083
|
-
import * as
|
|
1084
|
-
import * as
|
|
1081
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1082
|
+
import * as fs8 from "node:fs/promises";
|
|
1083
|
+
import * as os3 from "node:os";
|
|
1084
|
+
import * as path9 from "node:path";
|
|
1085
1085
|
async function parseSymbols4(opts) {
|
|
1086
1086
|
const { file, content, lang } = opts;
|
|
1087
1087
|
try {
|
|
@@ -1103,7 +1103,7 @@ async function resolvePython() {
|
|
|
1103
1103
|
function commandIsAvailable(command) {
|
|
1104
1104
|
return new Promise((resolve4) => {
|
|
1105
1105
|
let settled = false;
|
|
1106
|
-
const proc =
|
|
1106
|
+
const proc = spawn3(command, ["--version"], {
|
|
1107
1107
|
stdio: "ignore",
|
|
1108
1108
|
windowsHide: true
|
|
1109
1109
|
});
|
|
@@ -1125,7 +1125,7 @@ function commandIsAvailable(command) {
|
|
|
1125
1125
|
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
1126
1126
|
return new Promise((resolve4, reject) => {
|
|
1127
1127
|
let settled = false;
|
|
1128
|
-
const proc =
|
|
1128
|
+
const proc = spawn3(pyBinary, [scriptPath, filePath], {
|
|
1129
1129
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1130
1130
|
windowsHide: true
|
|
1131
1131
|
});
|
|
@@ -1159,10 +1159,10 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
|
1159
1159
|
async function syncPyParse(filePath, content, lang) {
|
|
1160
1160
|
try {
|
|
1161
1161
|
if (!_cachedScriptPath) {
|
|
1162
|
-
const tmpDir =
|
|
1163
|
-
await
|
|
1164
|
-
_cachedScriptPath =
|
|
1165
|
-
await
|
|
1162
|
+
const tmpDir = path9.join(os3.tmpdir(), "ws-py-parse");
|
|
1163
|
+
await fs8.mkdir(tmpDir, { recursive: true });
|
|
1164
|
+
_cachedScriptPath = path9.join(tmpDir, "parse.py");
|
|
1165
|
+
await fs8.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
1166
1166
|
}
|
|
1167
1167
|
cachedPyBinary ??= resolvePython();
|
|
1168
1168
|
const pyBinary = await cachedPyBinary;
|
|
@@ -1416,10 +1416,10 @@ __export(rs_parser_exports, {
|
|
|
1416
1416
|
detectLang: () => detectLang,
|
|
1417
1417
|
parseSymbols: () => parseSymbols5
|
|
1418
1418
|
});
|
|
1419
|
-
import { expectDefined } from "@wrongstack/core/utils";
|
|
1420
|
-
import { execFile, spawn as
|
|
1421
|
-
import * as
|
|
1422
|
-
import * as
|
|
1419
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
1420
|
+
import { execFile, spawn as spawn4 } from "node:child_process";
|
|
1421
|
+
import * as fs9 from "node:fs/promises";
|
|
1422
|
+
import * as path10 from "node:path";
|
|
1423
1423
|
async function parseSymbols5(opts) {
|
|
1424
1424
|
const { file, content, lang } = opts;
|
|
1425
1425
|
const nativeAvailable = await checkNativeParser();
|
|
@@ -1441,7 +1441,7 @@ function checkNativeParser() {
|
|
|
1441
1441
|
nativeParserAvailability ??= (async () => {
|
|
1442
1442
|
try {
|
|
1443
1443
|
await probe("rustc", ["--version"]);
|
|
1444
|
-
const toolsDir =
|
|
1444
|
+
const toolsDir = path10.join(process.cwd(), "tools");
|
|
1445
1445
|
await probe(
|
|
1446
1446
|
"cargo",
|
|
1447
1447
|
[
|
|
@@ -1450,7 +1450,7 @@ function checkNativeParser() {
|
|
|
1450
1450
|
"--format-version",
|
|
1451
1451
|
"1",
|
|
1452
1452
|
"--manifest-path",
|
|
1453
|
-
|
|
1453
|
+
path10.join(toolsDir, "Cargo.toml")
|
|
1454
1454
|
]
|
|
1455
1455
|
);
|
|
1456
1456
|
return true;
|
|
@@ -1462,17 +1462,17 @@ function checkNativeParser() {
|
|
|
1462
1462
|
}
|
|
1463
1463
|
async function tryNativeParse(file, content) {
|
|
1464
1464
|
try {
|
|
1465
|
-
const toolsDir =
|
|
1466
|
-
const crateDir =
|
|
1467
|
-
const tmpFile =
|
|
1468
|
-
await
|
|
1465
|
+
const toolsDir = path10.join(process.cwd(), "tools");
|
|
1466
|
+
const crateDir = path10.join(toolsDir, "syn-parser");
|
|
1467
|
+
const tmpFile = path10.join(crateDir, "src", "input.rs");
|
|
1468
|
+
await fs9.writeFile(tmpFile, content, "utf8");
|
|
1469
1469
|
const cargoBinary = resolveWin32Command("cargo");
|
|
1470
1470
|
const result = await new Promise(
|
|
1471
1471
|
(resolve4, reject) => {
|
|
1472
1472
|
let settled = false;
|
|
1473
|
-
const proc =
|
|
1473
|
+
const proc = spawn4(
|
|
1474
1474
|
cargoBinary,
|
|
1475
|
-
["run", "--manifest-path",
|
|
1475
|
+
["run", "--manifest-path", path10.join(toolsDir, "Cargo.toml")],
|
|
1476
1476
|
{
|
|
1477
1477
|
cwd: process.cwd(),
|
|
1478
1478
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1531,7 +1531,7 @@ function regexParse(opts) {
|
|
|
1531
1531
|
let hi = lineOffsets.length - 1;
|
|
1532
1532
|
while (lo < hi) {
|
|
1533
1533
|
const mid = lo + hi + 1 >>> 1;
|
|
1534
|
-
if (
|
|
1534
|
+
if (expectDefined2(lineOffsets[mid]) <= offset) lo = mid;
|
|
1535
1535
|
else hi = mid - 1;
|
|
1536
1536
|
}
|
|
1537
1537
|
return lo + 1;
|
|
@@ -1543,7 +1543,7 @@ function regexParse(opts) {
|
|
|
1543
1543
|
for (const pattern of RS_PATTERNS) {
|
|
1544
1544
|
pattern.regex.lastIndex = 0;
|
|
1545
1545
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
1546
|
-
const name =
|
|
1546
|
+
const name = expectDefined2(match[1]);
|
|
1547
1547
|
const offset = match.index ?? 0;
|
|
1548
1548
|
const line = lineFromOffset(offset);
|
|
1549
1549
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1600,8 +1600,8 @@ __export(json_parser_exports, {
|
|
|
1600
1600
|
detectLang: () => detectLang,
|
|
1601
1601
|
parseSymbols: () => parseSymbols6
|
|
1602
1602
|
});
|
|
1603
|
-
import { expectDefined as
|
|
1604
|
-
import * as
|
|
1603
|
+
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
1604
|
+
import * as path11 from "node:path";
|
|
1605
1605
|
function parseSymbols6(opts) {
|
|
1606
1606
|
const { file, content, lang } = opts;
|
|
1607
1607
|
try {
|
|
@@ -1613,7 +1613,7 @@ function parseSymbols6(opts) {
|
|
|
1613
1613
|
function regexParse2(opts) {
|
|
1614
1614
|
const { file, content, lang } = opts;
|
|
1615
1615
|
const symbols = [];
|
|
1616
|
-
const basename5 =
|
|
1616
|
+
const basename5 = path11.basename(file).toLowerCase();
|
|
1617
1617
|
const isPackageJson = basename5 === "package.json";
|
|
1618
1618
|
const isTsconfig = basename5 === "tsconfig.json" || basename5 === "tsconfig.build.json";
|
|
1619
1619
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -1628,22 +1628,22 @@ function regexParse2(opts) {
|
|
|
1628
1628
|
let hi = lineOffsets.length - 1;
|
|
1629
1629
|
while (lo < hi) {
|
|
1630
1630
|
const mid = lo + hi + 1 >>> 1;
|
|
1631
|
-
if (
|
|
1631
|
+
if (expectDefined3(lineOffsets[mid]) <= offset) lo = mid;
|
|
1632
1632
|
else hi = mid - 1;
|
|
1633
1633
|
}
|
|
1634
1634
|
return lo + 1;
|
|
1635
1635
|
}
|
|
1636
1636
|
const rootMatch = content.match(/^\s*\{/m);
|
|
1637
1637
|
if (rootMatch) {
|
|
1638
|
-
const offset =
|
|
1638
|
+
const offset = expectDefined3(rootMatch.index);
|
|
1639
1639
|
const line = lineFromOffset(offset);
|
|
1640
1640
|
symbols.push(
|
|
1641
1641
|
makeSymbol({
|
|
1642
|
-
name:
|
|
1642
|
+
name: path11.basename(file),
|
|
1643
1643
|
kind: "object",
|
|
1644
1644
|
line,
|
|
1645
1645
|
col: 0,
|
|
1646
|
-
signature: `"${
|
|
1646
|
+
signature: `"${path11.basename(file)}" = { ... }`,
|
|
1647
1647
|
file,
|
|
1648
1648
|
lang
|
|
1649
1649
|
})
|
|
@@ -1651,7 +1651,7 @@ function regexParse2(opts) {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
1653
1653
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
1654
|
-
const key =
|
|
1654
|
+
const key = expectDefined3(match[1]);
|
|
1655
1655
|
const offset = match.index ?? 0;
|
|
1656
1656
|
const line = lineFromOffset(offset);
|
|
1657
1657
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1698,7 +1698,7 @@ function regexParse2(opts) {
|
|
|
1698
1698
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
1699
1699
|
const defsMatch = defsRegex.exec(content);
|
|
1700
1700
|
if (defsMatch !== null) {
|
|
1701
|
-
const offset =
|
|
1701
|
+
const offset = expectDefined3(defsMatch.index);
|
|
1702
1702
|
const line = lineFromOffset(offset);
|
|
1703
1703
|
symbols.push(
|
|
1704
1704
|
makeSymbol({
|
|
@@ -1723,7 +1723,7 @@ function regexParse2(opts) {
|
|
|
1723
1723
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
1724
1724
|
const offset = match.index ?? 0;
|
|
1725
1725
|
const line = lineFromOffset(offset);
|
|
1726
|
-
const key = match[0]?.match(/"([^"]+)"/)?.[1] ??
|
|
1726
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined3(match[0]);
|
|
1727
1727
|
symbols.push(
|
|
1728
1728
|
makeSymbol({
|
|
1729
1729
|
name: key,
|
|
@@ -1742,12 +1742,12 @@ function regexParse2(opts) {
|
|
|
1742
1742
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
1743
1743
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
1744
1744
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
1745
|
-
const blockContent =
|
|
1745
|
+
const blockContent = expectDefined3(match[0]);
|
|
1746
1746
|
const blockOffset = match.index ?? 0;
|
|
1747
1747
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
1748
1748
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
1749
|
-
const key =
|
|
1750
|
-
const keyOffset = blockOffset +
|
|
1749
|
+
const key = expectDefined3(scriptMatch[1]);
|
|
1750
|
+
const keyOffset = blockOffset + expectDefined3(scriptMatch.index);
|
|
1751
1751
|
const line = lineFromOffset(keyOffset);
|
|
1752
1752
|
symbols.push(
|
|
1753
1753
|
makeSymbol({
|
|
@@ -1766,12 +1766,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
|
|
|
1766
1766
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
1767
1767
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
1768
1768
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
1769
|
-
const blockContent =
|
|
1769
|
+
const blockContent = expectDefined3(match[0]);
|
|
1770
1770
|
const blockOffset = match.index ?? 0;
|
|
1771
1771
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
1772
1772
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
1773
|
-
const key =
|
|
1774
|
-
const keyOffset = blockOffset +
|
|
1773
|
+
const key = expectDefined3(optMatch[1]);
|
|
1774
|
+
const keyOffset = blockOffset + expectDefined3(optMatch.index);
|
|
1775
1775
|
const line = lineFromOffset(keyOffset);
|
|
1776
1776
|
if (line <= parentLine) continue;
|
|
1777
1777
|
symbols.push(
|
|
@@ -1816,7 +1816,7 @@ __export(yaml_parser_exports, {
|
|
|
1816
1816
|
detectLang: () => detectLang,
|
|
1817
1817
|
parseSymbols: () => parseSymbols7
|
|
1818
1818
|
});
|
|
1819
|
-
import { expectDefined as
|
|
1819
|
+
import { expectDefined as expectDefined4, truncate } from "@wrongstack/core/utils";
|
|
1820
1820
|
function parseSymbols7(opts) {
|
|
1821
1821
|
const { file, content, lang } = opts;
|
|
1822
1822
|
try {
|
|
@@ -1838,14 +1838,14 @@ function regexParse3(opts) {
|
|
|
1838
1838
|
let hi = lineOffsets.length - 1;
|
|
1839
1839
|
while (lo < hi) {
|
|
1840
1840
|
const mid = lo + hi + 1 >>> 1;
|
|
1841
|
-
if (
|
|
1841
|
+
if (expectDefined4(lineOffsets[mid]) <= offset) lo = mid;
|
|
1842
1842
|
else hi = mid - 1;
|
|
1843
1843
|
}
|
|
1844
1844
|
return lo + 1;
|
|
1845
1845
|
}
|
|
1846
1846
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
1847
1847
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
1848
|
-
const name =
|
|
1848
|
+
const name = expectDefined4(match[1]);
|
|
1849
1849
|
const offset = match.index ?? 0;
|
|
1850
1850
|
const line = lineFromOffset(offset);
|
|
1851
1851
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1863,7 +1863,7 @@ function regexParse3(opts) {
|
|
|
1863
1863
|
}
|
|
1864
1864
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
1865
1865
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
1866
|
-
const name =
|
|
1866
|
+
const name = expectDefined4(match[1]);
|
|
1867
1867
|
const offset = match.index ?? 0;
|
|
1868
1868
|
const line = lineFromOffset(offset);
|
|
1869
1869
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1898,7 +1898,7 @@ function regexParse3(opts) {
|
|
|
1898
1898
|
}
|
|
1899
1899
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
1900
1900
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
1901
|
-
const key =
|
|
1901
|
+
const key = expectDefined4(match[2]);
|
|
1902
1902
|
const offset = match.index ?? 0;
|
|
1903
1903
|
const line = lineFromOffset(offset);
|
|
1904
1904
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1918,7 +1918,7 @@ function regexParse3(opts) {
|
|
|
1918
1918
|
}
|
|
1919
1919
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
1920
1920
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
1921
|
-
const key =
|
|
1921
|
+
const key = expectDefined4(match[2]);
|
|
1922
1922
|
const offset = match.index ?? 0;
|
|
1923
1923
|
const line = lineFromOffset(offset);
|
|
1924
1924
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
@@ -1970,10 +1970,12 @@ var init_yaml_parser = __esm({
|
|
|
1970
1970
|
}
|
|
1971
1971
|
});
|
|
1972
1972
|
|
|
1973
|
-
// src/codebase-index/
|
|
1974
|
-
import
|
|
1975
|
-
import
|
|
1976
|
-
import
|
|
1973
|
+
// src/codebase-index/project-server-client.ts
|
|
1974
|
+
import { spawn } from "node:child_process";
|
|
1975
|
+
import * as fs4 from "node:fs";
|
|
1976
|
+
import * as net from "node:net";
|
|
1977
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1978
|
+
import { checkUnixSocketPath } from "@wrongstack/core/utils";
|
|
1977
1979
|
|
|
1978
1980
|
// src/codebase-index/circuit-breaker.ts
|
|
1979
1981
|
var CircuitOpenError = class extends Error {
|
|
@@ -2058,117 +2060,18 @@ function resetIndexCircuitBreaker() {
|
|
|
2058
2060
|
indexCircuitBreaker.reset();
|
|
2059
2061
|
}
|
|
2060
2062
|
|
|
2061
|
-
// src/codebase-index/
|
|
2062
|
-
import {
|
|
2063
|
-
import
|
|
2064
|
-
import * as
|
|
2065
|
-
import
|
|
2066
|
-
import
|
|
2067
|
-
import {
|
|
2068
|
-
DEFAULT_WALK_IGNORE_DIRS,
|
|
2069
|
-
indexParallelBatchSize,
|
|
2070
|
-
isFrugalPerf
|
|
2071
|
-
} from "@wrongstack/core/utils";
|
|
2072
|
-
|
|
2073
|
-
// src/codebase-index/gitignore.ts
|
|
2074
|
-
import * as fs from "node:fs/promises";
|
|
2075
|
-
import * as path from "node:path";
|
|
2076
|
-
import { compileGlob } from "@wrongstack/core/utils";
|
|
2077
|
-
function globBody(glob) {
|
|
2078
|
-
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
2079
|
-
}
|
|
2080
|
-
function compileGitignore(lines) {
|
|
2081
|
-
const rules = [];
|
|
2082
|
-
for (const raw of lines) {
|
|
2083
|
-
let line = raw.replace(/\r$/, "");
|
|
2084
|
-
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
2085
|
-
line = line.trim();
|
|
2086
|
-
let negated = false;
|
|
2087
|
-
if (line.startsWith("!")) {
|
|
2088
|
-
negated = true;
|
|
2089
|
-
line = line.slice(1);
|
|
2090
|
-
}
|
|
2091
|
-
let dirOnly = false;
|
|
2092
|
-
if (line.endsWith("/")) {
|
|
2093
|
-
dirOnly = true;
|
|
2094
|
-
line = line.slice(0, -1);
|
|
2095
|
-
}
|
|
2096
|
-
if (!line) continue;
|
|
2097
|
-
const anchored = line.startsWith("/") || line.includes("/");
|
|
2098
|
-
if (line.startsWith("/")) line = line.slice(1);
|
|
2099
|
-
const body = globBody(line);
|
|
2100
|
-
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
2101
|
-
rules.push({
|
|
2102
|
-
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
2103
|
-
under: new RegExp(`${prefix}${body}/.*$`),
|
|
2104
|
-
negated,
|
|
2105
|
-
dirOnly
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
return (relPath, isDir) => {
|
|
2109
|
-
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2110
|
-
let ignored = false;
|
|
2111
|
-
for (const r of rules) {
|
|
2112
|
-
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
2113
|
-
if (re.test(p)) ignored = !r.negated;
|
|
2114
|
-
}
|
|
2115
|
-
return ignored;
|
|
2116
|
-
};
|
|
2117
|
-
}
|
|
2118
|
-
async function loadGitignoreMatcher(projectRoot) {
|
|
2119
|
-
let lines = [];
|
|
2120
|
-
try {
|
|
2121
|
-
const raw = await fs.readFile(path.join(projectRoot, ".gitignore"), "utf8");
|
|
2122
|
-
lines = raw.split("\n");
|
|
2123
|
-
} catch {
|
|
2124
|
-
}
|
|
2125
|
-
return compileGitignore(lines);
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
// src/codebase-index/indexer.ts
|
|
2129
|
-
init_languages();
|
|
2130
|
-
|
|
2131
|
-
// src/codebase-index/parser-dispatch.ts
|
|
2132
|
-
async function parseFileContent(file, content, lang) {
|
|
2133
|
-
switch (lang) {
|
|
2134
|
-
case "ts":
|
|
2135
|
-
case "tsx":
|
|
2136
|
-
case "js":
|
|
2137
|
-
case "jsx": {
|
|
2138
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
2139
|
-
return parseSymbols8({ file, content, lang });
|
|
2140
|
-
}
|
|
2141
|
-
case "go": {
|
|
2142
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
2143
|
-
return parseSymbols8({ file, content, lang: "go" });
|
|
2144
|
-
}
|
|
2145
|
-
case "py": {
|
|
2146
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
2147
|
-
return parseSymbols8({ file, content, lang: "py" });
|
|
2148
|
-
}
|
|
2149
|
-
case "rs": {
|
|
2150
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
2151
|
-
return parseSymbols8({ file, content, lang: "rs" });
|
|
2152
|
-
}
|
|
2153
|
-
case "json": {
|
|
2154
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
2155
|
-
return parseSymbols8({ file, content, lang: "json" });
|
|
2156
|
-
}
|
|
2157
|
-
case "yaml": {
|
|
2158
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
2159
|
-
return parseSymbols8({ file, content, lang: "yaml" });
|
|
2160
|
-
}
|
|
2161
|
-
default: {
|
|
2162
|
-
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
2163
|
-
return parseSymbols8({ file, content, lang });
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2063
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
2064
|
+
import { createHash } from "node:crypto";
|
|
2065
|
+
import * as fs3 from "node:fs";
|
|
2066
|
+
import * as os from "node:os";
|
|
2067
|
+
import * as path4 from "node:path";
|
|
2068
|
+
import { fileURLToPath } from "node:url";
|
|
2069
|
+
import { assertUnixSocketPathWithinLimit } from "@wrongstack/core/utils";
|
|
2167
2070
|
|
|
2168
2071
|
// src/codebase-index/writer.ts
|
|
2169
|
-
import { expectDefined
|
|
2170
|
-
import * as
|
|
2171
|
-
import * as
|
|
2072
|
+
import { expectDefined } from "@wrongstack/core/utils";
|
|
2073
|
+
import * as fs2 from "node:fs";
|
|
2074
|
+
import * as path3 from "node:path";
|
|
2172
2075
|
|
|
2173
2076
|
// src/codebase-index/bm25.ts
|
|
2174
2077
|
var K1 = 1.5;
|
|
@@ -2392,8 +2295,8 @@ function runSqliteWithRetry(fn) {
|
|
|
2392
2295
|
}
|
|
2393
2296
|
|
|
2394
2297
|
// src/codebase-index/writer-admin.ts
|
|
2395
|
-
import * as
|
|
2396
|
-
import * as
|
|
2298
|
+
import * as fs from "node:fs";
|
|
2299
|
+
import * as path from "node:path";
|
|
2397
2300
|
var DB_FILE = "index.db";
|
|
2398
2301
|
function getAllIndexableWithStatement(stmt) {
|
|
2399
2302
|
return stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
@@ -2452,7 +2355,7 @@ function getAllFileMetasWithStatement(stmt) {
|
|
|
2452
2355
|
}
|
|
2453
2356
|
function getIndexDbSizeBytes(indexDir) {
|
|
2454
2357
|
try {
|
|
2455
|
-
return
|
|
2358
|
+
return fs.statSync(path.join(indexDir, DB_FILE)).size;
|
|
2456
2359
|
} catch {
|
|
2457
2360
|
return 0;
|
|
2458
2361
|
}
|
|
@@ -2519,7 +2422,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
|
|
|
2519
2422
|
}
|
|
2520
2423
|
|
|
2521
2424
|
// src/codebase-index/writer-graph-helpers.ts
|
|
2522
|
-
import * as
|
|
2425
|
+
import * as path2 from "node:path";
|
|
2523
2426
|
function derivePackage(filePath) {
|
|
2524
2427
|
const f = filePath.replace(/\\/g, "/");
|
|
2525
2428
|
const pkgsIdx = f.indexOf("/packages/");
|
|
@@ -2634,16 +2537,16 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter) {
|
|
|
2634
2537
|
function resolveRelativeImport(fromFile, moduleName, indexedFiles) {
|
|
2635
2538
|
if (!moduleName.startsWith(".")) return void 0;
|
|
2636
2539
|
const normalizedFrom = fromFile.replace(/\\/g, "/");
|
|
2637
|
-
const absolute =
|
|
2638
|
-
|
|
2540
|
+
const absolute = path2.posix.normalize(
|
|
2541
|
+
path2.posix.join(path2.posix.dirname(normalizedFrom), moduleName)
|
|
2639
2542
|
);
|
|
2640
|
-
const extension =
|
|
2543
|
+
const extension = path2.posix.extname(absolute);
|
|
2641
2544
|
const base = extension ? absolute.slice(0, -extension.length) : absolute;
|
|
2642
2545
|
const candidates = [
|
|
2643
2546
|
absolute,
|
|
2644
2547
|
...[".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"].map((ext) => `${base}${ext}`),
|
|
2645
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
2646
|
-
...[".ts", ".tsx", ".js", ".jsx"].map((ext) =>
|
|
2548
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path2.posix.join(absolute, `index${ext}`)),
|
|
2549
|
+
...[".ts", ".tsx", ".js", ".jsx"].map((ext) => path2.posix.join(base, `index${ext}`))
|
|
2647
2550
|
];
|
|
2648
2551
|
const indexedByPortablePath = new Map(
|
|
2649
2552
|
[...indexedFiles].map((file) => [file.replace(/\\/g, "/").toLocaleLowerCase(), file])
|
|
@@ -3159,9 +3062,9 @@ var IndexStore = class _IndexStore {
|
|
|
3159
3062
|
}
|
|
3160
3063
|
constructor(projectRoot, opts = {}) {
|
|
3161
3064
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
3162
|
-
|
|
3065
|
+
fs2.mkdirSync(this.indexDir, { recursive: true });
|
|
3163
3066
|
const Database = loadDatabaseSync();
|
|
3164
|
-
this.db = new Database(
|
|
3067
|
+
this.db = new Database(path3.join(this.indexDir, DB_FILE2));
|
|
3165
3068
|
applyIndexStorePragmas(this.db);
|
|
3166
3069
|
this.initSchema();
|
|
3167
3070
|
}
|
|
@@ -3576,13 +3479,13 @@ var IndexStore = class _IndexStore {
|
|
|
3576
3479
|
if (rankDiff !== 0) return rankDiff;
|
|
3577
3480
|
const scoreDiff = b.score - a.score;
|
|
3578
3481
|
if (scoreDiff !== 0) return scoreDiff;
|
|
3579
|
-
const left =
|
|
3580
|
-
const right =
|
|
3482
|
+
const left = expectDefined(candidateById.get(a.id));
|
|
3483
|
+
const right = expectDefined(candidateById.get(b.id));
|
|
3581
3484
|
return left.name.localeCompare(right.name) || left.file.localeCompare(right.file) || left.line - right.line || left.col - right.col || left.id - right.id;
|
|
3582
3485
|
});
|
|
3583
3486
|
const qTokens = tokenise(query);
|
|
3584
3487
|
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
3585
|
-
const c =
|
|
3488
|
+
const c = expectDefined(candidateById.get(id));
|
|
3586
3489
|
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
3587
3490
|
});
|
|
3588
3491
|
return { results, total: candidates.length };
|
|
@@ -3996,721 +3899,212 @@ var indexStorePool = new StorePool(
|
|
|
3996
3899
|
(projectRoot, opts) => new IndexStore(projectRoot, opts)
|
|
3997
3900
|
);
|
|
3998
3901
|
|
|
3999
|
-
// src/codebase-index/
|
|
4000
|
-
var
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
function
|
|
4005
|
-
|
|
3902
|
+
// src/codebase-index/project-server-endpoint.ts
|
|
3903
|
+
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
3904
|
+
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
3905
|
+
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
3906
|
+
var buildIdCache;
|
|
3907
|
+
function projectIndexServerBuildId(entrypoint) {
|
|
3908
|
+
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path4.resolve(entrypoint);
|
|
3909
|
+
try {
|
|
3910
|
+
const stat2 = fs3.statSync(file);
|
|
3911
|
+
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat2.mtimeMs && buildIdCache.size === stat2.size) {
|
|
3912
|
+
return buildIdCache.buildId;
|
|
3913
|
+
}
|
|
3914
|
+
const buildId = createHash("sha256").update(fs3.readFileSync(file)).digest("hex").slice(0, 24);
|
|
3915
|
+
buildIdCache = { file, mtimeMs: stat2.mtimeMs, size: stat2.size, buildId };
|
|
3916
|
+
return buildId;
|
|
3917
|
+
} catch {
|
|
3918
|
+
return `unreadable:${path4.basename(file)}`;
|
|
3919
|
+
}
|
|
4006
3920
|
}
|
|
4007
|
-
function
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
3921
|
+
function normalizeLocalPath(value) {
|
|
3922
|
+
const resolved = path4.resolve(value);
|
|
3923
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
4011
3924
|
}
|
|
4012
|
-
function
|
|
4013
|
-
|
|
3925
|
+
function projectIndexServerKey(projectRoot, indexDir) {
|
|
3926
|
+
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
3927
|
+
return createHash("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
4014
3928
|
}
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
const rel = path11.relative(projectRoot, file);
|
|
4022
|
-
return rel !== "" && !rel.startsWith(`..${path11.sep}`) && rel !== ".." && !path11.isAbsolute(rel);
|
|
3929
|
+
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
3930
|
+
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
3931
|
+
if (process.platform === "win32") {
|
|
3932
|
+
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
3933
|
+
}
|
|
3934
|
+
return path4.join(os.tmpdir(), PROJECT_INDEX_SERVER_SOCKET_DIR, `${key}.sock`);
|
|
4023
3935
|
}
|
|
4024
|
-
function
|
|
4025
|
-
|
|
4026
|
-
|
|
3936
|
+
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
3937
|
+
return path4.join(
|
|
3938
|
+
path4.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
3939
|
+
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
3940
|
+
);
|
|
4027
3941
|
}
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
3942
|
+
|
|
3943
|
+
// src/codebase-index/project-server-protocol.ts
|
|
3944
|
+
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
3945
|
+
function encodeProjectServerMessage(message) {
|
|
3946
|
+
return `${JSON.stringify(message)}
|
|
3947
|
+
`;
|
|
4031
3948
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
])
|
|
4065
|
-
]);
|
|
4066
|
-
throwIfAborted(signal);
|
|
4067
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
4068
|
-
const deleted = /* @__PURE__ */ new Set();
|
|
4069
|
-
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
4070
|
-
for (let i = 0; i < statusRecords.length; i++) {
|
|
4071
|
-
const record = statusRecords[i];
|
|
4072
|
-
if (!record) continue;
|
|
4073
|
-
const status = record.slice(0, 2);
|
|
4074
|
-
const changedPath = path11.resolve(projectRoot, record.slice(3));
|
|
4075
|
-
dirty.add(changedPath);
|
|
4076
|
-
if (status.includes("D")) deleted.add(changedPath);
|
|
4077
|
-
if (status.includes("R") || status.includes("C")) {
|
|
4078
|
-
const source = statusRecords[++i];
|
|
4079
|
-
if (source) dirty.add(path11.resolve(projectRoot, source));
|
|
3949
|
+
|
|
3950
|
+
// src/codebase-index/project-server-client.ts
|
|
3951
|
+
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
3952
|
+
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
3953
|
+
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
3954
|
+
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
3955
|
+
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
3956
|
+
var StaleProjectIndexServerError = class extends Error {
|
|
3957
|
+
constructor(message, pid) {
|
|
3958
|
+
super(message);
|
|
3959
|
+
this.pid = pid;
|
|
3960
|
+
}
|
|
3961
|
+
pid;
|
|
3962
|
+
name = "StaleProjectIndexServerError";
|
|
3963
|
+
};
|
|
3964
|
+
var connectionStates = /* @__PURE__ */ new Map();
|
|
3965
|
+
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
3966
|
+
var latestConnectionState = {
|
|
3967
|
+
status: "offline",
|
|
3968
|
+
connected: false
|
|
3969
|
+
};
|
|
3970
|
+
function resolveProjectIndexDaemonAvailability(projectRoot, indexDir) {
|
|
3971
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
3972
|
+
return { kind: "inline-requested" };
|
|
3973
|
+
}
|
|
3974
|
+
let builtUrl = null;
|
|
3975
|
+
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
3976
|
+
try {
|
|
3977
|
+
const url = new URL(rel, import.meta.url);
|
|
3978
|
+
if (url.protocol === "file:" && fs4.existsSync(fileURLToPath2(url))) {
|
|
3979
|
+
builtUrl = url;
|
|
3980
|
+
break;
|
|
4080
3981
|
}
|
|
3982
|
+
} catch {
|
|
4081
3983
|
}
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
3984
|
+
}
|
|
3985
|
+
if (builtUrl === null) return { kind: "missing-build" };
|
|
3986
|
+
if (projectRoot !== void 0) {
|
|
3987
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
3988
|
+
const check = checkUnixSocketPath(endpoint);
|
|
3989
|
+
if (!check.ok) {
|
|
3990
|
+
return {
|
|
3991
|
+
kind: "endpoint-invalid",
|
|
3992
|
+
endpoint,
|
|
3993
|
+
byteLength: check.byteLength,
|
|
3994
|
+
maxBytes: check.maxBytes
|
|
3995
|
+
};
|
|
4093
3996
|
}
|
|
4094
|
-
return {
|
|
4095
|
-
files,
|
|
4096
|
-
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
4097
|
-
};
|
|
4098
|
-
} catch {
|
|
4099
|
-
return null;
|
|
4100
3997
|
}
|
|
3998
|
+
return { kind: "available", url: builtUrl };
|
|
4101
3999
|
}
|
|
4102
|
-
|
|
4103
|
-
const
|
|
4104
|
-
|
|
4000
|
+
function resolveProjectServerUrl() {
|
|
4001
|
+
const availability = resolveProjectIndexDaemonAvailability();
|
|
4002
|
+
return availability.kind === "available" ? availability.url : null;
|
|
4003
|
+
}
|
|
4004
|
+
function projectIndexServerExpectedBuildId() {
|
|
4005
|
+
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
4006
|
+
if (override) return override;
|
|
4007
|
+
const url = resolveProjectServerUrl();
|
|
4008
|
+
return url ? projectIndexServerBuildId(url) : null;
|
|
4009
|
+
}
|
|
4010
|
+
function isProjectIndexServerAvailable() {
|
|
4011
|
+
return resolveProjectServerUrl() !== null;
|
|
4012
|
+
}
|
|
4013
|
+
function publishConnectionState(endpoint, state) {
|
|
4014
|
+
connectionStates.set(endpoint, state);
|
|
4015
|
+
latestConnectionState = state;
|
|
4016
|
+
for (const listener of connectionStateListeners) listener(state);
|
|
4017
|
+
}
|
|
4018
|
+
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
4019
|
+
if (projectRoot) {
|
|
4020
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4021
|
+
const existing = connectionStates.get(endpoint);
|
|
4022
|
+
if (existing) return existing;
|
|
4023
|
+
if (!isProjectIndexServerAvailable()) {
|
|
4024
|
+
return { status: "unavailable", connected: false };
|
|
4025
|
+
}
|
|
4105
4026
|
return {
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4027
|
+
status: "offline",
|
|
4028
|
+
connected: false,
|
|
4029
|
+
projectRoot,
|
|
4030
|
+
indexDir,
|
|
4031
|
+
endpoint
|
|
4110
4032
|
};
|
|
4111
4033
|
}
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4116
|
-
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
4117
|
-
let dirCount = 0;
|
|
4118
|
-
const walk = async (dir) => {
|
|
4119
|
-
throwIfAborted(signal);
|
|
4120
|
-
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
4121
|
-
await yieldEventLoop();
|
|
4122
|
-
throwIfAborted(signal);
|
|
4123
|
-
}
|
|
4124
|
-
let entries;
|
|
4125
|
-
try {
|
|
4126
|
-
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
4127
|
-
} catch (err) {
|
|
4128
|
-
complete = false;
|
|
4129
|
-
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4130
|
-
return;
|
|
4131
|
-
}
|
|
4132
|
-
dirCount++;
|
|
4133
|
-
for (const e of entries) {
|
|
4134
|
-
if (ignoreSet.has(e.name)) continue;
|
|
4135
|
-
const full = path11.join(dir, e.name);
|
|
4136
|
-
const rel = path11.relative(projectRoot, full).replace(/\\/g, "/");
|
|
4137
|
-
if (e.isDirectory()) {
|
|
4138
|
-
if (isGitIgnored(rel, true)) continue;
|
|
4139
|
-
await walk(full);
|
|
4140
|
-
} else if (e.isFile()) {
|
|
4141
|
-
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
4142
|
-
const ext = path11.extname(e.name).toLowerCase();
|
|
4143
|
-
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
4144
|
-
results.push(full);
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
}
|
|
4148
|
-
};
|
|
4149
|
-
await walk(projectRoot);
|
|
4150
|
-
return { files: results, complete, errors };
|
|
4034
|
+
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
4035
|
+
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
4036
|
+
return latestConnectionState;
|
|
4151
4037
|
}
|
|
4152
|
-
function
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4156
|
-
const assigned = [];
|
|
4157
|
-
for (const ref of refs) {
|
|
4158
|
-
let owner;
|
|
4159
|
-
for (const symbol of ordered) {
|
|
4160
|
-
if (symbol.line > ref.line) break;
|
|
4161
|
-
owner = symbol;
|
|
4162
|
-
}
|
|
4163
|
-
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
4164
|
-
if (!owner || owner.id <= 0) continue;
|
|
4165
|
-
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
4166
|
-
if (seen.has(key)) continue;
|
|
4167
|
-
seen.add(key);
|
|
4168
|
-
assigned.push({ ...ref, fromId: owner.id });
|
|
4169
|
-
}
|
|
4170
|
-
return assigned;
|
|
4038
|
+
function onProjectIndexServerConnectionStateChange(listener) {
|
|
4039
|
+
connectionStateListeners.add(listener);
|
|
4040
|
+
return () => connectionStateListeners.delete(listener);
|
|
4171
4041
|
}
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
store.close();
|
|
4179
|
-
} catch {
|
|
4180
|
-
}
|
|
4181
|
-
}
|
|
4042
|
+
function remoteError(message, name) {
|
|
4043
|
+
if (name === "LockError") return new LockError(message);
|
|
4044
|
+
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
4045
|
+
const error = new Error(message);
|
|
4046
|
+
if (name && name !== "Error") error.name = name;
|
|
4047
|
+
return error;
|
|
4182
4048
|
}
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
const
|
|
4186
|
-
const
|
|
4187
|
-
const
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
} else {
|
|
4206
|
-
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
4207
|
-
files = discovery.files;
|
|
4208
|
-
errors.push(...discovery.errors);
|
|
4209
|
-
discoveryComplete = discovery.complete;
|
|
4210
|
-
discoveredFiles = new Set(files);
|
|
4211
|
-
trustedUnchanged = discovery.trustedUnchanged;
|
|
4049
|
+
function isProjectIndexServerHealth(value) {
|
|
4050
|
+
if (!value || typeof value !== "object") return false;
|
|
4051
|
+
const health = value;
|
|
4052
|
+
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
4053
|
+
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
4054
|
+
return typeof health.checkedAt === "number" && typeof health.uptimeMs === "number" && typeof memory?.rss === "number" && typeof memory.heapUsed === "number" && typeof memory.heapTotal === "number" && typeof memory.external === "number" && typeof health.clients === "number" && typeof health.activeRequests === "number" && typeof health.activeWrites === "number" && typeof health.queuedWrites === "number" && typeof health.pendingExternalFiles === "number" && typeof health.watchingExternal === "boolean" && typeof activity?.indexing === "boolean" && typeof activity.currentFile === "number" && typeof activity.totalFiles === "number" && typeof activity.generation === "number";
|
|
4055
|
+
}
|
|
4056
|
+
function delay(ms) {
|
|
4057
|
+
return new Promise((resolve4) => {
|
|
4058
|
+
const timer = setTimeout(resolve4, ms);
|
|
4059
|
+
timer.unref?.();
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
function cancellationError(signal) {
|
|
4063
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
4064
|
+
}
|
|
4065
|
+
var ProjectServerConnection = class {
|
|
4066
|
+
constructor(projectRoot, indexDir, endpoint) {
|
|
4067
|
+
this.projectRoot = projectRoot;
|
|
4068
|
+
this.indexDir = indexDir;
|
|
4069
|
+
this.endpoint = endpoint;
|
|
4070
|
+
this.transition("offline");
|
|
4212
4071
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4072
|
+
projectRoot;
|
|
4073
|
+
indexDir;
|
|
4074
|
+
endpoint;
|
|
4075
|
+
socket = null;
|
|
4076
|
+
buffer = "";
|
|
4077
|
+
info = null;
|
|
4078
|
+
activity = null;
|
|
4079
|
+
health = null;
|
|
4080
|
+
healthCheck = null;
|
|
4081
|
+
connecting = null;
|
|
4082
|
+
connectResolve = null;
|
|
4083
|
+
connectReject = null;
|
|
4084
|
+
nextId = 1;
|
|
4085
|
+
pending = /* @__PURE__ */ new Map();
|
|
4086
|
+
transition(status, options = {}) {
|
|
4087
|
+
const previous = connectionStates.get(this.endpoint);
|
|
4088
|
+
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
4089
|
+
const lastError = options.error === void 0 ? status === "error" || status === "degraded" || status === "unresponsive" ? previous?.lastError : void 0 : options.error instanceof Error ? options.error.message : String(options.error);
|
|
4090
|
+
publishConnectionState(this.endpoint, {
|
|
4091
|
+
status,
|
|
4092
|
+
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
4093
|
+
projectRoot: this.projectRoot,
|
|
4094
|
+
indexDir: this.indexDir,
|
|
4095
|
+
endpoint: this.endpoint,
|
|
4096
|
+
pid,
|
|
4097
|
+
lastError,
|
|
4098
|
+
...this.activity ? { activity: this.activity } : {},
|
|
4099
|
+
...this.health ? { health: this.health } : {}
|
|
4218
4100
|
});
|
|
4219
4101
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
if (!force) {
|
|
4223
|
-
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
4102
|
+
isConnected() {
|
|
4103
|
+
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
4224
4104
|
}
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
files = files.filter((file) => {
|
|
4229
|
-
const meta = existingMeta.get(file);
|
|
4230
|
-
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
4231
|
-
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
4232
|
-
symbolsIndexed += meta.symbolCount;
|
|
4233
|
-
filesIndexed++;
|
|
4234
|
-
filesPreSkipped++;
|
|
4235
|
-
return false;
|
|
4236
|
-
});
|
|
4237
|
-
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
4238
|
-
}
|
|
4239
|
-
const parallelBatch = resolveParallelBatch();
|
|
4240
|
-
let filesSinceLastYield = 0;
|
|
4241
|
-
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
4242
|
-
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
4243
|
-
const batchFiles = files.slice(batchStart, batchEnd);
|
|
4244
|
-
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
4245
|
-
filesSinceLastYield += batchFiles.length;
|
|
4246
|
-
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
4247
|
-
filesSinceLastYield = 0;
|
|
4248
|
-
await yieldEventLoop();
|
|
4249
|
-
if (isFrugalPerf()) {
|
|
4250
|
-
await new Promise((r) => setTimeout(r, 8));
|
|
4251
|
-
}
|
|
4252
|
-
throwIfAborted(signal);
|
|
4253
|
-
}
|
|
4254
|
-
const statOpts = signal ? { signal } : {};
|
|
4255
|
-
const statReadParse = await Promise.allSettled(
|
|
4256
|
-
batchFiles.map(
|
|
4257
|
-
async (file) => {
|
|
4258
|
-
let stat2;
|
|
4259
|
-
try {
|
|
4260
|
-
stat2 = await fs8.stat(file, statOpts);
|
|
4261
|
-
} catch (e) {
|
|
4262
|
-
if (isAbortError(e)) throw e;
|
|
4263
|
-
return {
|
|
4264
|
-
file,
|
|
4265
|
-
stat: null,
|
|
4266
|
-
lang: "",
|
|
4267
|
-
parsed: null,
|
|
4268
|
-
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
4269
|
-
missing: isMissingPathError(e)
|
|
4270
|
-
};
|
|
4271
|
-
}
|
|
4272
|
-
if (!stat2.isFile()) return { file, stat: stat2, lang: "", parsed: null };
|
|
4273
|
-
const lang = detectLang(file);
|
|
4274
|
-
if (!lang) return { file, stat: stat2, lang: "", parsed: null };
|
|
4275
|
-
if (stat2.size > MAX_INDEX_FILE_BYTES) {
|
|
4276
|
-
return {
|
|
4277
|
-
file,
|
|
4278
|
-
stat: stat2,
|
|
4279
|
-
lang,
|
|
4280
|
-
parsed: null,
|
|
4281
|
-
error: `file too large (${stat2.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
4282
|
-
};
|
|
4283
|
-
}
|
|
4284
|
-
const meta = existingMeta.get(file);
|
|
4285
|
-
if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
|
|
4286
|
-
return { file, stat: stat2, lang, parsed: null, skippedMeta: meta };
|
|
4287
|
-
}
|
|
4288
|
-
let content;
|
|
4289
|
-
try {
|
|
4290
|
-
content = await fs8.readFile(file, { encoding: "utf8", signal });
|
|
4291
|
-
} catch (e) {
|
|
4292
|
-
if (isAbortError(e)) throw e;
|
|
4293
|
-
return {
|
|
4294
|
-
file,
|
|
4295
|
-
stat: stat2,
|
|
4296
|
-
lang,
|
|
4297
|
-
parsed: null,
|
|
4298
|
-
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
4299
|
-
};
|
|
4300
|
-
}
|
|
4301
|
-
let parsed;
|
|
4302
|
-
try {
|
|
4303
|
-
parsed = await parseFileContent(file, content, lang);
|
|
4304
|
-
} catch (e) {
|
|
4305
|
-
return {
|
|
4306
|
-
file,
|
|
4307
|
-
stat: stat2,
|
|
4308
|
-
lang,
|
|
4309
|
-
parsed: null,
|
|
4310
|
-
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
4311
|
-
};
|
|
4312
|
-
}
|
|
4313
|
-
return { file, stat: stat2, lang, parsed, content };
|
|
4314
|
-
}
|
|
4315
|
-
)
|
|
4316
|
-
);
|
|
4317
|
-
const batchEntries = [];
|
|
4318
|
-
const deleteForFiles = [];
|
|
4319
|
-
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
4320
|
-
const settled = statReadParse[fi];
|
|
4321
|
-
const file = expectDefined5(batchFiles[fi]);
|
|
4322
|
-
if (settled.status === "rejected") {
|
|
4323
|
-
const err = settled.reason;
|
|
4324
|
-
if (err instanceof Error && isAbortError(err)) throw err;
|
|
4325
|
-
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4326
|
-
continue;
|
|
4327
|
-
}
|
|
4328
|
-
const result = settled.value;
|
|
4329
|
-
if (result.error) {
|
|
4330
|
-
if (result.missing) store.deleteFile(file);
|
|
4331
|
-
errors.push(`${file}: ${result.error}`);
|
|
4332
|
-
continue;
|
|
4333
|
-
}
|
|
4334
|
-
const { stat: stat2, lang, parsed } = result;
|
|
4335
|
-
if (result.skippedMeta) {
|
|
4336
|
-
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
4337
|
-
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
4338
|
-
filesIndexed++;
|
|
4339
|
-
continue;
|
|
4340
|
-
}
|
|
4341
|
-
if (!lang || !parsed) {
|
|
4342
|
-
if (lang) {
|
|
4343
|
-
store.upsertFile({
|
|
4344
|
-
file,
|
|
4345
|
-
lang,
|
|
4346
|
-
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
4347
|
-
symbolCount: 0,
|
|
4348
|
-
lastIndexed: Date.now()
|
|
4349
|
-
});
|
|
4350
|
-
filesIndexed++;
|
|
4351
|
-
}
|
|
4352
|
-
continue;
|
|
4353
|
-
}
|
|
4354
|
-
if (parsed.symbols.length === 0) {
|
|
4355
|
-
store.replaceEmptyFile({
|
|
4356
|
-
file,
|
|
4357
|
-
lang,
|
|
4358
|
-
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
4359
|
-
symbolCount: 0,
|
|
4360
|
-
lastIndexed: Date.now()
|
|
4361
|
-
});
|
|
4362
|
-
filesIndexed++;
|
|
4363
|
-
continue;
|
|
4364
|
-
}
|
|
4365
|
-
batchEntries.push({
|
|
4366
|
-
file,
|
|
4367
|
-
lang,
|
|
4368
|
-
symbols: parsed.symbols,
|
|
4369
|
-
refs: parsed.refs ?? [],
|
|
4370
|
-
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
4371
|
-
symbolCount: parsed.symbols.length
|
|
4372
|
-
});
|
|
4373
|
-
deleteForFiles.push(file);
|
|
4374
|
-
}
|
|
4375
|
-
if (batchEntries.length > 0) {
|
|
4376
|
-
try {
|
|
4377
|
-
store.commitBatch(batchEntries, { deleteForFiles });
|
|
4378
|
-
for (const entry of batchEntries) {
|
|
4379
|
-
const count = entry.symbols.length;
|
|
4380
|
-
symbolsIndexed += count;
|
|
4381
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
4382
|
-
filesIndexed++;
|
|
4383
|
-
}
|
|
4384
|
-
} catch (err) {
|
|
4385
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4386
|
-
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
4387
|
-
for (const entry of batchEntries) {
|
|
4388
|
-
try {
|
|
4389
|
-
store.deleteRefsForFile(entry.file);
|
|
4390
|
-
store.deleteSymbolsForFile(entry.file);
|
|
4391
|
-
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
4392
|
-
symbolsIndexed += symbolsWithIds.length;
|
|
4393
|
-
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
4394
|
-
filesIndexed++;
|
|
4395
|
-
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
4396
|
-
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
4397
|
-
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
4398
|
-
}
|
|
4399
|
-
store.resolveRefsForNames([
|
|
4400
|
-
...entry.symbols.map((symbol) => symbol.name),
|
|
4401
|
-
...entry.refs.map((ref) => ref.toName)
|
|
4402
|
-
]);
|
|
4403
|
-
store.upsertFile({
|
|
4404
|
-
file: entry.file,
|
|
4405
|
-
lang: entry.lang,
|
|
4406
|
-
mtimeMs: entry.mtimeMs,
|
|
4407
|
-
symbolCount: entry.symbolCount,
|
|
4408
|
-
lastIndexed: Date.now()
|
|
4409
|
-
});
|
|
4410
|
-
} catch (innerErr) {
|
|
4411
|
-
errors.push(
|
|
4412
|
-
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
4413
|
-
);
|
|
4414
|
-
}
|
|
4415
|
-
}
|
|
4416
|
-
}
|
|
4417
|
-
}
|
|
4418
|
-
}
|
|
4419
|
-
if (discoveredFiles && discoveryComplete) {
|
|
4420
|
-
for (const [file_] of existingMeta) {
|
|
4421
|
-
if (!discoveredFiles.has(file_)) {
|
|
4422
|
-
store.deleteFile(file_);
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
}
|
|
4426
|
-
if (needsFullRefResolution) store.resolveRefs();
|
|
4427
|
-
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
4428
|
-
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
4429
|
-
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
4430
|
-
store.setLastIndexed(Date.now());
|
|
4431
|
-
if (!opts.files) store.compactIfNeeded();
|
|
4432
|
-
const durationMs = Date.now() - startMs;
|
|
4433
|
-
return {
|
|
4434
|
-
filesIndexed,
|
|
4435
|
-
symbolsIndexed,
|
|
4436
|
-
langStats,
|
|
4437
|
-
durationMs,
|
|
4438
|
-
errors
|
|
4439
|
-
};
|
|
4440
|
-
}
|
|
4441
|
-
|
|
4442
|
-
// src/codebase-index/index-service.ts
|
|
4443
|
-
async function indexService(args, hooks = {}) {
|
|
4444
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4445
|
-
try {
|
|
4446
|
-
return await runIndexerWithStore(store, {
|
|
4447
|
-
projectRoot: args.projectRoot,
|
|
4448
|
-
indexDir: args.indexDir,
|
|
4449
|
-
files: args.files,
|
|
4450
|
-
force: args.force,
|
|
4451
|
-
langs: args.langs,
|
|
4452
|
-
ignore: args.ignore,
|
|
4453
|
-
signal: hooks.signal,
|
|
4454
|
-
onProgress: hooks.onProgress
|
|
4455
|
-
});
|
|
4456
|
-
} finally {
|
|
4457
|
-
indexStorePool.release(store);
|
|
4458
|
-
}
|
|
4459
|
-
}
|
|
4460
|
-
function searchService(args) {
|
|
4461
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4462
|
-
try {
|
|
4463
|
-
return store.searchRanked(
|
|
4464
|
-
args.query,
|
|
4465
|
-
{
|
|
4466
|
-
kind: args.kind,
|
|
4467
|
-
lang: args.lang,
|
|
4468
|
-
file: args.file,
|
|
4469
|
-
lspKind: args.lspKind
|
|
4470
|
-
},
|
|
4471
|
-
args.limit
|
|
4472
|
-
);
|
|
4473
|
-
} finally {
|
|
4474
|
-
indexStorePool.release(store);
|
|
4475
|
-
}
|
|
4476
|
-
}
|
|
4477
|
-
function statsService(args) {
|
|
4478
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4479
|
-
try {
|
|
4480
|
-
return store.getStats();
|
|
4481
|
-
} finally {
|
|
4482
|
-
indexStorePool.release(store);
|
|
4483
|
-
}
|
|
4484
|
-
}
|
|
4485
|
-
function packageGraphService(args) {
|
|
4486
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4487
|
-
try {
|
|
4488
|
-
return store.getPackageGraph();
|
|
4489
|
-
} finally {
|
|
4490
|
-
indexStorePool.release(store);
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
function fileGraphService(args) {
|
|
4494
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4495
|
-
try {
|
|
4496
|
-
return store.getFileGraph(args.packageFilter);
|
|
4497
|
-
} finally {
|
|
4498
|
-
indexStorePool.release(store);
|
|
4499
|
-
}
|
|
4500
|
-
}
|
|
4501
|
-
function symbolGraphService(args) {
|
|
4502
|
-
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
4503
|
-
try {
|
|
4504
|
-
return store.getSymbolGraph(args.fileFilter);
|
|
4505
|
-
} finally {
|
|
4506
|
-
indexStorePool.release(store);
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
|
|
4510
|
-
// src/codebase-index/background-indexer.ts
|
|
4511
|
-
init_languages();
|
|
4512
|
-
|
|
4513
|
-
// src/codebase-index/project-server-client.ts
|
|
4514
|
-
import { spawn as spawn4 } from "node:child_process";
|
|
4515
|
-
import * as fs10 from "node:fs";
|
|
4516
|
-
import * as net from "node:net";
|
|
4517
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4518
|
-
|
|
4519
|
-
// src/codebase-index/project-server-endpoint.ts
|
|
4520
|
-
import { createHash } from "node:crypto";
|
|
4521
|
-
import * as fs9 from "node:fs";
|
|
4522
|
-
import * as os3 from "node:os";
|
|
4523
|
-
import * as path12 from "node:path";
|
|
4524
|
-
import { fileURLToPath } from "node:url";
|
|
4525
|
-
var PROJECT_INDEX_SERVER_PROTOCOL_VERSION = 1;
|
|
4526
|
-
var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
4527
|
-
var buildIdCache;
|
|
4528
|
-
function projectIndexServerBuildId(entrypoint) {
|
|
4529
|
-
const file = entrypoint instanceof URL || entrypoint.startsWith("file:") ? fileURLToPath(entrypoint) : path12.resolve(entrypoint);
|
|
4530
|
-
try {
|
|
4531
|
-
const stat2 = fs9.statSync(file);
|
|
4532
|
-
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat2.mtimeMs && buildIdCache.size === stat2.size) {
|
|
4533
|
-
return buildIdCache.buildId;
|
|
4534
|
-
}
|
|
4535
|
-
const buildId = createHash("sha256").update(fs9.readFileSync(file)).digest("hex").slice(0, 24);
|
|
4536
|
-
buildIdCache = { file, mtimeMs: stat2.mtimeMs, size: stat2.size, buildId };
|
|
4537
|
-
return buildId;
|
|
4538
|
-
} catch {
|
|
4539
|
-
return `unreadable:${path12.basename(file)}`;
|
|
4540
|
-
}
|
|
4541
|
-
}
|
|
4542
|
-
function normalizeLocalPath(value) {
|
|
4543
|
-
const resolved = path12.resolve(value);
|
|
4544
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
4545
|
-
}
|
|
4546
|
-
function projectIndexServerKey(projectRoot, indexDir) {
|
|
4547
|
-
const resolvedIndexDir = normalizeLocalPath(resolveIndexDir(projectRoot, indexDir));
|
|
4548
|
-
return createHash("sha256").update(resolvedIndexDir).digest("hex").slice(0, 24);
|
|
4549
|
-
}
|
|
4550
|
-
function projectIndexServerEndpoint(projectRoot, indexDir) {
|
|
4551
|
-
const key = projectIndexServerKey(projectRoot, indexDir);
|
|
4552
|
-
if (process.platform === "win32") {
|
|
4553
|
-
return `\\\\.\\pipe\\wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}-${key}`;
|
|
4554
|
-
}
|
|
4555
|
-
return path12.join(
|
|
4556
|
-
os3.tmpdir(),
|
|
4557
|
-
`wrongstack-codebase-index-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`,
|
|
4558
|
-
`${key}.sock`
|
|
4559
|
-
);
|
|
4560
|
-
}
|
|
4561
|
-
function projectIndexServerMetadataPath(projectRoot, indexDir) {
|
|
4562
|
-
return path12.join(
|
|
4563
|
-
path12.resolve(resolveIndexDir(projectRoot, indexDir)),
|
|
4564
|
-
PROJECT_INDEX_SERVER_METADATA_FILE
|
|
4565
|
-
);
|
|
4566
|
-
}
|
|
4567
|
-
|
|
4568
|
-
// src/codebase-index/project-server-protocol.ts
|
|
4569
|
-
var PROJECT_INDEX_SERVER_MAX_FRAME_CHARS = 64 * 1024 * 1024;
|
|
4570
|
-
function encodeProjectServerMessage(message) {
|
|
4571
|
-
return `${JSON.stringify(message)}
|
|
4572
|
-
`;
|
|
4573
|
-
}
|
|
4574
|
-
|
|
4575
|
-
// src/codebase-index/project-server-client.ts
|
|
4576
|
-
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
4577
|
-
var SERVER_START_TIMEOUT_MS = 1e4;
|
|
4578
|
-
var SERVER_CONTROL_TIMEOUT_MS = 5e3;
|
|
4579
|
-
var SERVER_HEALTH_TIMEOUT_MS = 3e3;
|
|
4580
|
-
var SERVER_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
4581
|
-
var StaleProjectIndexServerError = class extends Error {
|
|
4582
|
-
constructor(message, pid) {
|
|
4583
|
-
super(message);
|
|
4584
|
-
this.pid = pid;
|
|
4585
|
-
}
|
|
4586
|
-
pid;
|
|
4587
|
-
name = "StaleProjectIndexServerError";
|
|
4588
|
-
};
|
|
4589
|
-
var connectionStates = /* @__PURE__ */ new Map();
|
|
4590
|
-
var connectionStateListeners = /* @__PURE__ */ new Set();
|
|
4591
|
-
var latestConnectionState = {
|
|
4592
|
-
status: "offline",
|
|
4593
|
-
connected: false
|
|
4594
|
-
};
|
|
4595
|
-
function resolveProjectIndexDaemonAvailability() {
|
|
4596
|
-
if (process.env["WRONGSTACK_INDEX_INLINE"] || process.env["WRONGSTACK_INDEX_SERVER"] === "0") {
|
|
4597
|
-
return { kind: "inline-requested" };
|
|
4598
|
-
}
|
|
4599
|
-
for (const rel of ["./project-server.js", "./codebase-index/project-server.js"]) {
|
|
4600
|
-
try {
|
|
4601
|
-
const url = new URL(rel, import.meta.url);
|
|
4602
|
-
if (url.protocol === "file:" && fs10.existsSync(fileURLToPath2(url))) {
|
|
4603
|
-
return { kind: "available", url };
|
|
4604
|
-
}
|
|
4605
|
-
} catch {
|
|
4606
|
-
}
|
|
4607
|
-
}
|
|
4608
|
-
return { kind: "missing-build" };
|
|
4609
|
-
}
|
|
4610
|
-
function resolveProjectServerUrl() {
|
|
4611
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
4612
|
-
return availability.kind === "available" ? availability.url : null;
|
|
4613
|
-
}
|
|
4614
|
-
function projectIndexServerExpectedBuildId() {
|
|
4615
|
-
const override = process.env["WRONGSTACK_INDEX_SERVER_BUILD_ID"]?.trim();
|
|
4616
|
-
if (override) return override;
|
|
4617
|
-
const url = resolveProjectServerUrl();
|
|
4618
|
-
return url ? projectIndexServerBuildId(url) : null;
|
|
4619
|
-
}
|
|
4620
|
-
function isProjectIndexServerAvailable() {
|
|
4621
|
-
return resolveProjectServerUrl() !== null;
|
|
4622
|
-
}
|
|
4623
|
-
function publishConnectionState(endpoint, state) {
|
|
4624
|
-
connectionStates.set(endpoint, state);
|
|
4625
|
-
latestConnectionState = state;
|
|
4626
|
-
for (const listener of connectionStateListeners) listener(state);
|
|
4627
|
-
}
|
|
4628
|
-
function getProjectIndexServerConnectionState(projectRoot, indexDir) {
|
|
4629
|
-
if (projectRoot) {
|
|
4630
|
-
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4631
|
-
const existing = connectionStates.get(endpoint);
|
|
4632
|
-
if (existing) return existing;
|
|
4633
|
-
if (!isProjectIndexServerAvailable()) {
|
|
4634
|
-
return { status: "unavailable", connected: false };
|
|
4635
|
-
}
|
|
4636
|
-
return {
|
|
4637
|
-
status: "offline",
|
|
4638
|
-
connected: false,
|
|
4639
|
-
projectRoot,
|
|
4640
|
-
indexDir,
|
|
4641
|
-
endpoint
|
|
4642
|
-
};
|
|
4643
|
-
}
|
|
4644
|
-
if (latestConnectionState.endpoint) return latestConnectionState;
|
|
4645
|
-
if (!isProjectIndexServerAvailable()) return { status: "unavailable", connected: false };
|
|
4646
|
-
return latestConnectionState;
|
|
4647
|
-
}
|
|
4648
|
-
function onProjectIndexServerConnectionStateChange(listener) {
|
|
4649
|
-
connectionStateListeners.add(listener);
|
|
4650
|
-
return () => connectionStateListeners.delete(listener);
|
|
4651
|
-
}
|
|
4652
|
-
function remoteError(message, name) {
|
|
4653
|
-
if (name === "LockError") return new LockError(message);
|
|
4654
|
-
if (name === "IndexTimeoutError") return new IndexTimeoutError(message);
|
|
4655
|
-
const error = new Error(message);
|
|
4656
|
-
if (name && name !== "Error") error.name = name;
|
|
4657
|
-
return error;
|
|
4658
|
-
}
|
|
4659
|
-
function isProjectIndexServerHealth(value) {
|
|
4660
|
-
if (!value || typeof value !== "object") return false;
|
|
4661
|
-
const health = value;
|
|
4662
|
-
const memory = health.memory && typeof health.memory === "object" ? health.memory : void 0;
|
|
4663
|
-
const activity = health.activity && typeof health.activity === "object" ? health.activity : void 0;
|
|
4664
|
-
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";
|
|
4665
|
-
}
|
|
4666
|
-
function delay(ms) {
|
|
4667
|
-
return new Promise((resolve4) => {
|
|
4668
|
-
const timer = setTimeout(resolve4, ms);
|
|
4669
|
-
timer.unref?.();
|
|
4670
|
-
});
|
|
4671
|
-
}
|
|
4672
|
-
function cancellationError(signal) {
|
|
4673
|
-
return signal.reason instanceof Error ? signal.reason : new Error("Indexing cancelled");
|
|
4674
|
-
}
|
|
4675
|
-
var ProjectServerConnection = class {
|
|
4676
|
-
constructor(projectRoot, indexDir, endpoint) {
|
|
4677
|
-
this.projectRoot = projectRoot;
|
|
4678
|
-
this.indexDir = indexDir;
|
|
4679
|
-
this.endpoint = endpoint;
|
|
4680
|
-
this.transition("offline");
|
|
4681
|
-
}
|
|
4682
|
-
projectRoot;
|
|
4683
|
-
indexDir;
|
|
4684
|
-
endpoint;
|
|
4685
|
-
socket = null;
|
|
4686
|
-
buffer = "";
|
|
4687
|
-
info = null;
|
|
4688
|
-
activity = null;
|
|
4689
|
-
health = null;
|
|
4690
|
-
healthCheck = null;
|
|
4691
|
-
connecting = null;
|
|
4692
|
-
connectResolve = null;
|
|
4693
|
-
connectReject = null;
|
|
4694
|
-
nextId = 1;
|
|
4695
|
-
pending = /* @__PURE__ */ new Map();
|
|
4696
|
-
transition(status, options = {}) {
|
|
4697
|
-
const previous = connectionStates.get(this.endpoint);
|
|
4698
|
-
const pid = options.pid ?? (status === "connected" ? this.info?.pid : void 0);
|
|
4699
|
-
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);
|
|
4700
|
-
publishConnectionState(this.endpoint, {
|
|
4701
|
-
status,
|
|
4702
|
-
connected: status === "connected" || status === "degraded" || status === "unresponsive",
|
|
4703
|
-
projectRoot: this.projectRoot,
|
|
4704
|
-
indexDir: this.indexDir,
|
|
4705
|
-
endpoint: this.endpoint,
|
|
4706
|
-
pid,
|
|
4707
|
-
lastError,
|
|
4708
|
-
...this.activity ? { activity: this.activity } : {},
|
|
4709
|
-
...this.health ? { health: this.health } : {}
|
|
4710
|
-
});
|
|
4711
|
-
}
|
|
4712
|
-
isConnected() {
|
|
4713
|
-
return this.socket !== null && !this.socket.destroyed && this.info !== null;
|
|
4105
|
+
/** Safe LRU candidate: no request, connect, or health probe is in flight. */
|
|
4106
|
+
isEvictable() {
|
|
4107
|
+
return this.pending.size === 0 && this.connecting === null && this.healthCheck === null;
|
|
4714
4108
|
}
|
|
4715
4109
|
async checkHealth(spawnIfMissing = false, timeoutMs = SERVER_HEALTH_TIMEOUT_MS) {
|
|
4716
4110
|
await this.ensureConnected(spawnIfMissing);
|
|
@@ -4810,371 +4204,1018 @@ var ProjectServerConnection = class {
|
|
|
4810
4204
|
this.transition("connected", { pid: this.info?.pid });
|
|
4811
4205
|
}
|
|
4812
4206
|
}
|
|
4813
|
-
close() {
|
|
4814
|
-
const socket = this.socket;
|
|
4207
|
+
close() {
|
|
4208
|
+
const socket = this.socket;
|
|
4209
|
+
this.socket = null;
|
|
4210
|
+
this.info = null;
|
|
4211
|
+
this.activity = null;
|
|
4212
|
+
this.health = null;
|
|
4213
|
+
this.connectReject?.(new Error("codebase-index client disconnected"));
|
|
4214
|
+
this.connectResolve = null;
|
|
4215
|
+
this.connectReject = null;
|
|
4216
|
+
if (socket && !socket.destroyed) socket.destroy();
|
|
4217
|
+
this.rejectPending(new Error("codebase-index client disconnected"));
|
|
4218
|
+
this.transition("offline");
|
|
4219
|
+
maybeStopHeartbeatLoop();
|
|
4220
|
+
}
|
|
4221
|
+
request(message, options) {
|
|
4222
|
+
const socket = this.socket;
|
|
4223
|
+
if (!socket || socket.destroyed) {
|
|
4224
|
+
return Promise.reject(new Error("codebase-index server connection is not available"));
|
|
4225
|
+
}
|
|
4226
|
+
const id = this.nextId++;
|
|
4227
|
+
return new Promise((resolve4, reject) => {
|
|
4228
|
+
const timer = setTimeout(() => {
|
|
4229
|
+
const entry = this.pending.get(id);
|
|
4230
|
+
if (!entry) return;
|
|
4231
|
+
this.pending.delete(id);
|
|
4232
|
+
this.write({ type: "cancel", id });
|
|
4233
|
+
const error = new IndexTimeoutError(
|
|
4234
|
+
`Index ${message.type === "request" ? message.op : message.type} exceeded its ${options.timeoutMs}ms watchdog timeout`
|
|
4235
|
+
);
|
|
4236
|
+
this.cleanupPending(entry);
|
|
4237
|
+
entry.reject(error);
|
|
4238
|
+
}, options.timeoutMs);
|
|
4239
|
+
timer.unref?.();
|
|
4240
|
+
const signal = options.signal;
|
|
4241
|
+
const onAbort = signal ? () => {
|
|
4242
|
+
const entry = this.pending.get(id);
|
|
4243
|
+
if (!entry) return;
|
|
4244
|
+
this.pending.delete(id);
|
|
4245
|
+
this.write({ type: "cancel", id });
|
|
4246
|
+
this.cleanupPending(entry);
|
|
4247
|
+
entry.reject(cancellationError(signal));
|
|
4248
|
+
} : void 0;
|
|
4249
|
+
this.pending.set(id, {
|
|
4250
|
+
resolve: resolve4,
|
|
4251
|
+
reject,
|
|
4252
|
+
timer,
|
|
4253
|
+
signal,
|
|
4254
|
+
onAbort,
|
|
4255
|
+
onProgress: options.onProgress
|
|
4256
|
+
});
|
|
4257
|
+
if (signal && onAbort) {
|
|
4258
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4259
|
+
if (signal.aborted) {
|
|
4260
|
+
onAbort();
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
this.write({ ...message, id });
|
|
4265
|
+
});
|
|
4266
|
+
}
|
|
4267
|
+
async ensureConnected(spawnIfMissing) {
|
|
4268
|
+
if (this.socket && !this.socket.destroyed && this.info) return;
|
|
4269
|
+
if (this.connecting) return this.connecting;
|
|
4270
|
+
this.transition("connecting");
|
|
4271
|
+
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
4272
|
+
this.transition("error", { error });
|
|
4273
|
+
throw error;
|
|
4274
|
+
}).finally(() => {
|
|
4275
|
+
this.connecting = null;
|
|
4276
|
+
});
|
|
4277
|
+
return this.connecting;
|
|
4278
|
+
}
|
|
4279
|
+
async connectWithElection(spawnIfMissing) {
|
|
4280
|
+
const deadline = Date.now() + (spawnIfMissing ? SERVER_START_TIMEOUT_MS : CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4281
|
+
let spawned = false;
|
|
4282
|
+
let staleAttempts = 0;
|
|
4283
|
+
let lastError = new Error("codebase-index server unavailable");
|
|
4284
|
+
while (Date.now() < deadline) {
|
|
4285
|
+
try {
|
|
4286
|
+
await this.connectOnce();
|
|
4287
|
+
return;
|
|
4288
|
+
} catch (error) {
|
|
4289
|
+
lastError = error;
|
|
4290
|
+
if (error instanceof StaleProjectIndexServerError) {
|
|
4291
|
+
staleAttempts++;
|
|
4292
|
+
if (!spawnIfMissing) break;
|
|
4293
|
+
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
4294
|
+
spawned = false;
|
|
4295
|
+
await delay(100);
|
|
4296
|
+
continue;
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
if (!spawnIfMissing) break;
|
|
4300
|
+
if (!spawned) {
|
|
4301
|
+
this.spawnDetachedServer();
|
|
4302
|
+
spawned = true;
|
|
4303
|
+
}
|
|
4304
|
+
await delay(75);
|
|
4305
|
+
}
|
|
4306
|
+
throw lastError;
|
|
4307
|
+
}
|
|
4308
|
+
connectOnce() {
|
|
4309
|
+
this.socket?.destroy();
|
|
4310
|
+
this.socket = null;
|
|
4311
|
+
this.info = null;
|
|
4312
|
+
this.activity = null;
|
|
4313
|
+
this.health = null;
|
|
4314
|
+
this.buffer = "";
|
|
4315
|
+
return new Promise((resolve4, reject) => {
|
|
4316
|
+
const socket = net.createConnection(this.endpoint);
|
|
4317
|
+
this.socket = socket;
|
|
4318
|
+
socket.setEncoding("utf8");
|
|
4319
|
+
const timer = setTimeout(() => {
|
|
4320
|
+
reject(new Error("codebase-index server handshake timed out"));
|
|
4321
|
+
socket.destroy();
|
|
4322
|
+
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4323
|
+
timer.unref?.();
|
|
4324
|
+
const finishResolve = () => {
|
|
4325
|
+
clearTimeout(timer);
|
|
4326
|
+
this.connectResolve = null;
|
|
4327
|
+
this.connectReject = null;
|
|
4328
|
+
resolve4();
|
|
4329
|
+
};
|
|
4330
|
+
const finishReject = (error) => {
|
|
4331
|
+
clearTimeout(timer);
|
|
4332
|
+
this.connectResolve = null;
|
|
4333
|
+
this.connectReject = null;
|
|
4334
|
+
reject(error);
|
|
4335
|
+
};
|
|
4336
|
+
this.connectResolve = finishResolve;
|
|
4337
|
+
this.connectReject = finishReject;
|
|
4338
|
+
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
4339
|
+
socket.on("error", (error) => {
|
|
4340
|
+
if (!this.info) finishReject(error);
|
|
4341
|
+
});
|
|
4342
|
+
socket.on("close", () => this.onClose(socket));
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4345
|
+
onData(socket, chunk) {
|
|
4346
|
+
if (socket !== this.socket) return;
|
|
4347
|
+
this.buffer += chunk;
|
|
4348
|
+
while (true) {
|
|
4349
|
+
const newline = this.buffer.indexOf("\n");
|
|
4350
|
+
if (newline < 0) {
|
|
4351
|
+
if (this.buffer.length > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
4352
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
4353
|
+
}
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
4356
|
+
if (newline > PROJECT_INDEX_SERVER_MAX_FRAME_CHARS) {
|
|
4357
|
+
socket.destroy(new Error("codebase-index server response exceeds the IPC limit"));
|
|
4358
|
+
return;
|
|
4359
|
+
}
|
|
4360
|
+
const line = this.buffer.slice(0, newline);
|
|
4361
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
4362
|
+
if (!line) continue;
|
|
4363
|
+
let message;
|
|
4364
|
+
try {
|
|
4365
|
+
message = JSON.parse(line);
|
|
4366
|
+
} catch {
|
|
4367
|
+
socket.destroy(new Error("invalid codebase-index server response"));
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4370
|
+
this.onMessage(message);
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
onMessage(message) {
|
|
4374
|
+
if (message.type === "hello") {
|
|
4375
|
+
if (message.protocolVersion !== PROJECT_INDEX_SERVER_PROTOCOL_VERSION) {
|
|
4376
|
+
this.rejectStaleServer(
|
|
4377
|
+
message,
|
|
4378
|
+
`codebase-index protocol mismatch: client=${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}, server=${message.protocolVersion}`
|
|
4379
|
+
);
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
const expectedBuildId = projectIndexServerExpectedBuildId();
|
|
4383
|
+
if (expectedBuildId && message.buildId !== expectedBuildId) {
|
|
4384
|
+
this.rejectStaleServer(
|
|
4385
|
+
message,
|
|
4386
|
+
`codebase-index build mismatch: client=${expectedBuildId}, server=${message.buildId ?? "legacy"}`
|
|
4387
|
+
);
|
|
4388
|
+
return;
|
|
4389
|
+
}
|
|
4390
|
+
this.info = message;
|
|
4391
|
+
this.markResponsive();
|
|
4392
|
+
this.transition("connected", { pid: message.pid });
|
|
4393
|
+
ensureHeartbeatLoop();
|
|
4394
|
+
this.connectResolve?.();
|
|
4395
|
+
return;
|
|
4396
|
+
}
|
|
4397
|
+
if (message.type === "index-state") {
|
|
4398
|
+
this.activity = message.state;
|
|
4399
|
+
this.markResponsive();
|
|
4400
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4403
|
+
const entry = this.pending.get(message.id);
|
|
4404
|
+
if (!entry) return;
|
|
4405
|
+
this.markResponsive();
|
|
4406
|
+
const status = connectionStates.get(this.endpoint)?.status;
|
|
4407
|
+
if (status === "degraded" || status === "unresponsive") {
|
|
4408
|
+
this.transition("connected", { pid: this.info?.pid });
|
|
4409
|
+
}
|
|
4410
|
+
if (message.type === "progress") {
|
|
4411
|
+
entry.onProgress?.(message.current, message.total);
|
|
4412
|
+
return;
|
|
4413
|
+
}
|
|
4414
|
+
this.pending.delete(message.id);
|
|
4415
|
+
this.cleanupPending(entry);
|
|
4416
|
+
if (message.ok) entry.resolve(message.result);
|
|
4417
|
+
else entry.reject(remoteError(message.error, message.errorName));
|
|
4418
|
+
}
|
|
4419
|
+
onClose(socket) {
|
|
4420
|
+
if (socket !== this.socket) return;
|
|
4421
|
+
const wasConnected = this.info !== null;
|
|
4815
4422
|
this.socket = null;
|
|
4816
4423
|
this.info = null;
|
|
4817
4424
|
this.activity = null;
|
|
4818
4425
|
this.health = null;
|
|
4819
|
-
|
|
4426
|
+
const error = new Error("codebase-index server connection closed");
|
|
4427
|
+
this.connectReject?.(error);
|
|
4820
4428
|
this.connectResolve = null;
|
|
4821
4429
|
this.connectReject = null;
|
|
4822
|
-
|
|
4823
|
-
this.
|
|
4824
|
-
this.transition("offline");
|
|
4430
|
+
this.rejectPending(error);
|
|
4431
|
+
if (wasConnected) this.transition("error", { error });
|
|
4825
4432
|
maybeStopHeartbeatLoop();
|
|
4826
4433
|
}
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
if (
|
|
4830
|
-
|
|
4434
|
+
cleanupPending(entry) {
|
|
4435
|
+
clearTimeout(entry.timer);
|
|
4436
|
+
if (entry.signal && entry.onAbort) {
|
|
4437
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
4831
4438
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4439
|
+
}
|
|
4440
|
+
rejectPending(error) {
|
|
4441
|
+
const entries = [...this.pending.values()];
|
|
4442
|
+
this.pending.clear();
|
|
4443
|
+
for (const entry of entries) {
|
|
4444
|
+
this.cleanupPending(entry);
|
|
4445
|
+
entry.reject(error);
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
write(message) {
|
|
4449
|
+
const socket = this.socket;
|
|
4450
|
+
if (socket && !socket.destroyed) socket.write(encodeProjectServerMessage(message));
|
|
4451
|
+
}
|
|
4452
|
+
rejectStaleServer(message, reason) {
|
|
4453
|
+
const socket = this.socket;
|
|
4454
|
+
if (socket && !socket.destroyed) {
|
|
4455
|
+
socket.write(
|
|
4456
|
+
encodeProjectServerMessage({
|
|
4457
|
+
type: "shutdown",
|
|
4458
|
+
id: 0,
|
|
4459
|
+
reason: "stale-build-replacement"
|
|
4460
|
+
})
|
|
4461
|
+
);
|
|
4462
|
+
const timer = setTimeout(() => socket.destroy(), 25);
|
|
4845
4463
|
timer.unref?.();
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
resolve: resolve4,
|
|
4857
|
-
reject,
|
|
4858
|
-
timer,
|
|
4859
|
-
signal,
|
|
4860
|
-
onAbort,
|
|
4861
|
-
onProgress: options.onProgress
|
|
4862
|
-
});
|
|
4863
|
-
if (signal && onAbort) {
|
|
4864
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
4865
|
-
if (signal.aborted) {
|
|
4866
|
-
onAbort();
|
|
4867
|
-
return;
|
|
4868
|
-
}
|
|
4464
|
+
}
|
|
4465
|
+
this.connectReject?.(new StaleProjectIndexServerError(reason, message.pid));
|
|
4466
|
+
}
|
|
4467
|
+
spawnDetachedServer() {
|
|
4468
|
+
const url = resolveProjectServerUrl();
|
|
4469
|
+
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
4470
|
+
if (process.platform !== "win32") {
|
|
4471
|
+
try {
|
|
4472
|
+
fs4.rmSync(this.endpoint, { force: true });
|
|
4473
|
+
} catch {
|
|
4869
4474
|
}
|
|
4870
|
-
|
|
4475
|
+
}
|
|
4476
|
+
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
4477
|
+
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
4478
|
+
const child = spawn(process.execPath, args, {
|
|
4479
|
+
detached: true,
|
|
4480
|
+
stdio: "ignore",
|
|
4481
|
+
windowsHide: true,
|
|
4482
|
+
env: process.env
|
|
4871
4483
|
});
|
|
4484
|
+
child.unref();
|
|
4872
4485
|
}
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
this.transition("connecting");
|
|
4877
|
-
this.connecting = this.connectWithElection(spawnIfMissing).catch((error) => {
|
|
4878
|
-
this.transition("error", { error });
|
|
4879
|
-
throw error;
|
|
4880
|
-
}).finally(() => {
|
|
4881
|
-
this.connecting = null;
|
|
4882
|
-
});
|
|
4883
|
-
return this.connecting;
|
|
4486
|
+
forceKillKnownServer() {
|
|
4487
|
+
const pid = this.info?.pid;
|
|
4488
|
+
return pid ? this.forceKillServer(pid) : false;
|
|
4884
4489
|
}
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
while (Date.now() < deadline) {
|
|
4490
|
+
forceKillServer(pid) {
|
|
4491
|
+
if (pid === process.pid) return false;
|
|
4492
|
+
try {
|
|
4493
|
+
process.kill(pid);
|
|
4494
|
+
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
4891
4495
|
try {
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
} catch
|
|
4895
|
-
lastError = error;
|
|
4896
|
-
if (error instanceof StaleProjectIndexServerError) {
|
|
4897
|
-
staleAttempts++;
|
|
4898
|
-
if (!spawnIfMissing) break;
|
|
4899
|
-
if (staleAttempts >= 3) this.forceKillServer(error.pid);
|
|
4900
|
-
spawned = false;
|
|
4901
|
-
await delay(100);
|
|
4902
|
-
continue;
|
|
4903
|
-
}
|
|
4904
|
-
}
|
|
4905
|
-
if (!spawnIfMissing) break;
|
|
4906
|
-
if (!spawned) {
|
|
4907
|
-
this.spawnDetachedServer();
|
|
4908
|
-
spawned = true;
|
|
4496
|
+
const metadata = JSON.parse(fs4.readFileSync(metadataPath, "utf8"));
|
|
4497
|
+
if (metadata.pid === pid) fs4.rmSync(metadataPath, { force: true });
|
|
4498
|
+
} catch {
|
|
4909
4499
|
}
|
|
4910
|
-
|
|
4500
|
+
return true;
|
|
4501
|
+
} catch {
|
|
4502
|
+
return false;
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
};
|
|
4506
|
+
var connections = /* @__PURE__ */ new Map();
|
|
4507
|
+
var MAX_CACHED_CONNECTIONS = 8;
|
|
4508
|
+
var heartbeatTimer;
|
|
4509
|
+
function forgetConnection(endpoint, connection) {
|
|
4510
|
+
if (connections.get(endpoint) === connection) connections.delete(endpoint);
|
|
4511
|
+
connection.close();
|
|
4512
|
+
connectionStates.delete(endpoint);
|
|
4513
|
+
if (latestConnectionState.endpoint !== endpoint) return;
|
|
4514
|
+
latestConnectionState = [...connectionStates.values()].at(-1) ?? {
|
|
4515
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
4516
|
+
connected: false
|
|
4517
|
+
};
|
|
4518
|
+
}
|
|
4519
|
+
function trimConnectionCache(protectedConnection) {
|
|
4520
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) return;
|
|
4521
|
+
for (const [endpoint, connection] of connections) {
|
|
4522
|
+
if (connections.size <= MAX_CACHED_CONNECTIONS) break;
|
|
4523
|
+
if (connection === protectedConnection || !connection.isEvictable()) continue;
|
|
4524
|
+
forgetConnection(endpoint, connection);
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
function ensureHeartbeatLoop() {
|
|
4528
|
+
if (heartbeatTimer) return;
|
|
4529
|
+
heartbeatTimer = setInterval(() => {
|
|
4530
|
+
for (const connection of connections.values()) {
|
|
4531
|
+
if (connection.isConnected()) void connection.checkHealth(false).catch(() => {
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
}, SERVER_HEARTBEAT_INTERVAL_MS);
|
|
4535
|
+
heartbeatTimer.unref?.();
|
|
4536
|
+
}
|
|
4537
|
+
function maybeStopHeartbeatLoop() {
|
|
4538
|
+
if (!heartbeatTimer) return;
|
|
4539
|
+
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
4540
|
+
clearInterval(heartbeatTimer);
|
|
4541
|
+
heartbeatTimer = void 0;
|
|
4542
|
+
}
|
|
4543
|
+
function connectionFor(projectRoot, indexDir) {
|
|
4544
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4545
|
+
let connection = connections.get(endpoint);
|
|
4546
|
+
if (!connection) {
|
|
4547
|
+
connection = new ProjectServerConnection(projectRoot, indexDir, endpoint);
|
|
4548
|
+
connections.set(endpoint, connection);
|
|
4549
|
+
} else {
|
|
4550
|
+
connections.delete(endpoint);
|
|
4551
|
+
connections.set(endpoint, connection);
|
|
4552
|
+
}
|
|
4553
|
+
trimConnectionCache(connection);
|
|
4554
|
+
return connection;
|
|
4555
|
+
}
|
|
4556
|
+
function callProjectIndexServer(op, args, options) {
|
|
4557
|
+
return connectionFor(args.projectRoot, args.indexDir).call(op, args, options);
|
|
4558
|
+
}
|
|
4559
|
+
function ensureProjectIndexServer(options) {
|
|
4560
|
+
return connectionFor(options.projectRoot, options.indexDir).configure(
|
|
4561
|
+
options.watchExternal,
|
|
4562
|
+
options.debounceMs
|
|
4563
|
+
);
|
|
4564
|
+
}
|
|
4565
|
+
function checkProjectIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
4566
|
+
return connectionFor(projectRoot, indexDir).checkHealth(
|
|
4567
|
+
false,
|
|
4568
|
+
options.timeoutMs ?? SERVER_HEALTH_TIMEOUT_MS
|
|
4569
|
+
);
|
|
4570
|
+
}
|
|
4571
|
+
async function shutdownProjectIndexServer(projectRoot, indexDir, reason) {
|
|
4572
|
+
const endpoint = projectIndexServerEndpoint(projectRoot, indexDir);
|
|
4573
|
+
const connection = connectionFor(projectRoot, indexDir);
|
|
4574
|
+
try {
|
|
4575
|
+
return await connection.shutdownRemote(reason);
|
|
4576
|
+
} finally {
|
|
4577
|
+
connection.close();
|
|
4578
|
+
connections.delete(endpoint);
|
|
4579
|
+
connectionStates.delete(endpoint);
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
function closeProjectIndexServerClients() {
|
|
4583
|
+
for (const connection of connections.values()) connection.close();
|
|
4584
|
+
connections.clear();
|
|
4585
|
+
connectionStates.clear();
|
|
4586
|
+
latestConnectionState = {
|
|
4587
|
+
status: isProjectIndexServerAvailable() ? "offline" : "unavailable",
|
|
4588
|
+
connected: false
|
|
4589
|
+
};
|
|
4590
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
4591
|
+
heartbeatTimer = void 0;
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
// src/codebase-index/background-indexer.ts
|
|
4595
|
+
import * as fs11 from "node:fs";
|
|
4596
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
4597
|
+
import { Worker } from "node:worker_threads";
|
|
4598
|
+
|
|
4599
|
+
// src/codebase-index/indexer.ts
|
|
4600
|
+
import { expectDefined as expectDefined5 } from "@wrongstack/core/utils";
|
|
4601
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4602
|
+
import * as fs10 from "node:fs/promises";
|
|
4603
|
+
import { availableParallelism } from "node:os";
|
|
4604
|
+
import * as path12 from "node:path";
|
|
4605
|
+
import {
|
|
4606
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
4607
|
+
indexParallelBatchSize,
|
|
4608
|
+
isFrugalPerf
|
|
4609
|
+
} from "@wrongstack/core/utils";
|
|
4610
|
+
|
|
4611
|
+
// src/codebase-index/gitignore.ts
|
|
4612
|
+
import * as fs5 from "node:fs/promises";
|
|
4613
|
+
import * as path5 from "node:path";
|
|
4614
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
4615
|
+
function globBody(glob) {
|
|
4616
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
4617
|
+
}
|
|
4618
|
+
function compileGitignore(lines) {
|
|
4619
|
+
const rules = [];
|
|
4620
|
+
for (const raw of lines) {
|
|
4621
|
+
let line = raw.replace(/\r$/, "");
|
|
4622
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
4623
|
+
line = line.trim();
|
|
4624
|
+
let negated = false;
|
|
4625
|
+
if (line.startsWith("!")) {
|
|
4626
|
+
negated = true;
|
|
4627
|
+
line = line.slice(1);
|
|
4911
4628
|
}
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
socket.destroy();
|
|
4928
|
-
}, CONNECT_ATTEMPT_TIMEOUT_MS);
|
|
4929
|
-
timer.unref?.();
|
|
4930
|
-
const finishResolve = () => {
|
|
4931
|
-
clearTimeout(timer);
|
|
4932
|
-
this.connectResolve = null;
|
|
4933
|
-
this.connectReject = null;
|
|
4934
|
-
resolve4();
|
|
4935
|
-
};
|
|
4936
|
-
const finishReject = (error) => {
|
|
4937
|
-
clearTimeout(timer);
|
|
4938
|
-
this.connectResolve = null;
|
|
4939
|
-
this.connectReject = null;
|
|
4940
|
-
reject(error);
|
|
4941
|
-
};
|
|
4942
|
-
this.connectResolve = finishResolve;
|
|
4943
|
-
this.connectReject = finishReject;
|
|
4944
|
-
socket.on("data", (chunk) => this.onData(socket, chunk));
|
|
4945
|
-
socket.on("error", (error) => {
|
|
4946
|
-
if (!this.info) finishReject(error);
|
|
4947
|
-
});
|
|
4948
|
-
socket.on("close", () => this.onClose(socket));
|
|
4629
|
+
let dirOnly = false;
|
|
4630
|
+
if (line.endsWith("/")) {
|
|
4631
|
+
dirOnly = true;
|
|
4632
|
+
line = line.slice(0, -1);
|
|
4633
|
+
}
|
|
4634
|
+
if (!line) continue;
|
|
4635
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
4636
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
4637
|
+
const body = globBody(line);
|
|
4638
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
4639
|
+
rules.push({
|
|
4640
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
4641
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
4642
|
+
negated,
|
|
4643
|
+
dirOnly
|
|
4949
4644
|
});
|
|
4950
4645
|
}
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
const
|
|
4956
|
-
if (
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4646
|
+
return (relPath, isDir) => {
|
|
4647
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
4648
|
+
let ignored = false;
|
|
4649
|
+
for (const r of rules) {
|
|
4650
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
4651
|
+
if (re.test(p)) ignored = !r.negated;
|
|
4652
|
+
}
|
|
4653
|
+
return ignored;
|
|
4654
|
+
};
|
|
4655
|
+
}
|
|
4656
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
4657
|
+
let lines = [];
|
|
4658
|
+
try {
|
|
4659
|
+
const raw = await fs5.readFile(path5.join(projectRoot, ".gitignore"), "utf8");
|
|
4660
|
+
lines = raw.split("\n");
|
|
4661
|
+
} catch {
|
|
4662
|
+
}
|
|
4663
|
+
return compileGitignore(lines);
|
|
4664
|
+
}
|
|
4665
|
+
|
|
4666
|
+
// src/codebase-index/indexer.ts
|
|
4667
|
+
init_languages();
|
|
4668
|
+
|
|
4669
|
+
// src/codebase-index/parser-dispatch.ts
|
|
4670
|
+
async function parseFileContent(file, content, lang) {
|
|
4671
|
+
switch (lang) {
|
|
4672
|
+
case "ts":
|
|
4673
|
+
case "tsx":
|
|
4674
|
+
case "js":
|
|
4675
|
+
case "jsx": {
|
|
4676
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
4677
|
+
return parseSymbols8({ file, content, lang });
|
|
4678
|
+
}
|
|
4679
|
+
case "go": {
|
|
4680
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
4681
|
+
return parseSymbols8({ file, content, lang: "go" });
|
|
4682
|
+
}
|
|
4683
|
+
case "py": {
|
|
4684
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
4685
|
+
return parseSymbols8({ file, content, lang: "py" });
|
|
4686
|
+
}
|
|
4687
|
+
case "rs": {
|
|
4688
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
4689
|
+
return parseSymbols8({ file, content, lang: "rs" });
|
|
4690
|
+
}
|
|
4691
|
+
case "json": {
|
|
4692
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
4693
|
+
return parseSymbols8({ file, content, lang: "json" });
|
|
4694
|
+
}
|
|
4695
|
+
case "yaml": {
|
|
4696
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
4697
|
+
return parseSymbols8({ file, content, lang: "yaml" });
|
|
4698
|
+
}
|
|
4699
|
+
default: {
|
|
4700
|
+
const { parseSymbols: parseSymbols8 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
4701
|
+
return parseSymbols8({ file, content, lang });
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
|
|
4706
|
+
// src/codebase-index/indexer.ts
|
|
4707
|
+
var YIELD_EVERY_N = 50;
|
|
4708
|
+
function resolveParallelBatch() {
|
|
4709
|
+
return indexParallelBatchSize(availableParallelism());
|
|
4710
|
+
}
|
|
4711
|
+
function yieldEventLoop() {
|
|
4712
|
+
return new Promise((resolve4) => setImmediate(resolve4));
|
|
4713
|
+
}
|
|
4714
|
+
function throwIfAborted(signal) {
|
|
4715
|
+
if (!signal?.aborted) return;
|
|
4716
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
4717
|
+
throw new Error(typeof signal.reason === "string" ? signal.reason : "Indexing cancelled");
|
|
4718
|
+
}
|
|
4719
|
+
function isAbortError(err) {
|
|
4720
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
4721
|
+
}
|
|
4722
|
+
var DEFAULT_IGNORE = DEFAULT_WALK_IGNORE_DIRS;
|
|
4723
|
+
var DEFAULT_IGNORE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "pnpm-lock.yml"]);
|
|
4724
|
+
var INDEXABLE_EXTENSION_SET = new Set(INDEXABLE_EXTENSIONS);
|
|
4725
|
+
var MAX_INDEX_FILE_BYTES = 5 * 1024 * 1024;
|
|
4726
|
+
var MAX_GIT_FILE_LIST_BYTES = 64 * 1024 * 1024;
|
|
4727
|
+
function isWithinProject(projectRoot, file) {
|
|
4728
|
+
const rel = path12.relative(projectRoot, file);
|
|
4729
|
+
return rel !== "" && !rel.startsWith(`..${path12.sep}`) && rel !== ".." && !path12.isAbsolute(rel);
|
|
4730
|
+
}
|
|
4731
|
+
function isMissingPathError(err) {
|
|
4732
|
+
const code = err?.code;
|
|
4733
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
4734
|
+
}
|
|
4735
|
+
function normalizeComparablePath(value) {
|
|
4736
|
+
const resolved = path12.resolve(value);
|
|
4737
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
4738
|
+
}
|
|
4739
|
+
function gitOutput(projectRoot, args) {
|
|
4740
|
+
return new Promise((resolve4, reject) => {
|
|
4741
|
+
execFile2(
|
|
4742
|
+
"git",
|
|
4743
|
+
["-C", projectRoot, ...args],
|
|
4744
|
+
{
|
|
4745
|
+
encoding: "buffer",
|
|
4746
|
+
maxBuffer: MAX_GIT_FILE_LIST_BYTES,
|
|
4747
|
+
windowsHide: true
|
|
4748
|
+
},
|
|
4749
|
+
(error, stdout) => {
|
|
4750
|
+
if (error) reject(error);
|
|
4751
|
+
else resolve4(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
|
|
4961
4752
|
}
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4753
|
+
);
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
async function findGitSourceFiles(projectRoot, ignore, signal) {
|
|
4757
|
+
try {
|
|
4758
|
+
throwIfAborted(signal);
|
|
4759
|
+
const topLevel = (await gitOutput(projectRoot, ["rev-parse", "--show-toplevel"])).toString("utf8").trim();
|
|
4760
|
+
if (normalizeComparablePath(topLevel) !== normalizeComparablePath(projectRoot)) return null;
|
|
4761
|
+
throwIfAborted(signal);
|
|
4762
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4763
|
+
const [output, statusOutput] = await Promise.all([
|
|
4764
|
+
gitOutput(projectRoot, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"]),
|
|
4765
|
+
gitOutput(projectRoot, [
|
|
4766
|
+
"status",
|
|
4767
|
+
"--porcelain=v1",
|
|
4768
|
+
"-z",
|
|
4769
|
+
"--untracked-files=all",
|
|
4770
|
+
"--ignored=no"
|
|
4771
|
+
])
|
|
4772
|
+
]);
|
|
4773
|
+
throwIfAborted(signal);
|
|
4774
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
4775
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
4776
|
+
const statusRecords = statusOutput.toString("utf8").split("\0");
|
|
4777
|
+
for (let i = 0; i < statusRecords.length; i++) {
|
|
4778
|
+
const record = statusRecords[i];
|
|
4779
|
+
if (!record) continue;
|
|
4780
|
+
const status = record.slice(0, 2);
|
|
4781
|
+
const changedPath = path12.resolve(projectRoot, record.slice(3));
|
|
4782
|
+
dirty.add(changedPath);
|
|
4783
|
+
if (status.includes("D")) deleted.add(changedPath);
|
|
4784
|
+
if (status.includes("R") || status.includes("C")) {
|
|
4785
|
+
const source = statusRecords[++i];
|
|
4786
|
+
if (source) dirty.add(path12.resolve(projectRoot, source));
|
|
4965
4787
|
}
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
socket.destroy(new Error("invalid codebase-index server response"));
|
|
4974
|
-
return;
|
|
4788
|
+
}
|
|
4789
|
+
const files = [];
|
|
4790
|
+
for (const relative2 of output.toString("utf8").split("\0")) {
|
|
4791
|
+
if (!relative2) continue;
|
|
4792
|
+
const portable = relative2.replace(/\\/g, "/");
|
|
4793
|
+
if (portable.split("/").some((segment) => ignoreSet.has(segment)) || DEFAULT_IGNORE_FILES.has(path12.posix.basename(portable))) {
|
|
4794
|
+
continue;
|
|
4975
4795
|
}
|
|
4976
|
-
|
|
4796
|
+
const full = path12.resolve(projectRoot, relative2);
|
|
4797
|
+
if (deleted.has(full)) continue;
|
|
4798
|
+
const ext = path12.extname(relative2).toLowerCase();
|
|
4799
|
+
if (INDEXABLE_EXTENSION_SET.has(ext) || detectLang(full) !== null) files.push(full);
|
|
4977
4800
|
}
|
|
4801
|
+
return {
|
|
4802
|
+
files,
|
|
4803
|
+
trustedUnchanged: new Set(files.filter((file) => !dirty.has(file)))
|
|
4804
|
+
};
|
|
4805
|
+
} catch {
|
|
4806
|
+
return null;
|
|
4978
4807
|
}
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
return;
|
|
4808
|
+
}
|
|
4809
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
4810
|
+
const gitFiles = await findGitSourceFiles(projectRoot, ignore, signal);
|
|
4811
|
+
if (gitFiles) {
|
|
4812
|
+
return {
|
|
4813
|
+
files: gitFiles.files,
|
|
4814
|
+
complete: true,
|
|
4815
|
+
errors: [],
|
|
4816
|
+
trustedUnchanged: gitFiles.trustedUnchanged
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4819
|
+
const results = [];
|
|
4820
|
+
const errors = [];
|
|
4821
|
+
let complete = true;
|
|
4822
|
+
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
4823
|
+
const indexableExts = new Set(INDEXABLE_EXTENSIONS);
|
|
4824
|
+
let dirCount = 0;
|
|
4825
|
+
const walk = async (dir) => {
|
|
4826
|
+
throwIfAborted(signal);
|
|
4827
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
4828
|
+
await yieldEventLoop();
|
|
4829
|
+
throwIfAborted(signal);
|
|
5002
4830
|
}
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
4831
|
+
let entries;
|
|
4832
|
+
try {
|
|
4833
|
+
entries = await fs10.readdir(dir, { withFileTypes: true });
|
|
4834
|
+
} catch (err) {
|
|
4835
|
+
complete = false;
|
|
4836
|
+
errors.push(`scan error: ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5007
4837
|
return;
|
|
5008
4838
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
4839
|
+
dirCount++;
|
|
4840
|
+
for (const e of entries) {
|
|
4841
|
+
if (ignoreSet.has(e.name)) continue;
|
|
4842
|
+
const full = path12.join(dir, e.name);
|
|
4843
|
+
const rel = path12.relative(projectRoot, full).replace(/\\/g, "/");
|
|
4844
|
+
if (e.isDirectory()) {
|
|
4845
|
+
if (isGitIgnored(rel, true)) continue;
|
|
4846
|
+
await walk(full);
|
|
4847
|
+
} else if (e.isFile()) {
|
|
4848
|
+
if (DEFAULT_IGNORE_FILES.has(e.name) || isGitIgnored(rel, false)) continue;
|
|
4849
|
+
const ext = path12.extname(e.name).toLowerCase();
|
|
4850
|
+
if (indexableExts.has(ext) || detectLang(full) !== null) {
|
|
4851
|
+
results.push(full);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
5015
4854
|
}
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
4855
|
+
};
|
|
4856
|
+
await walk(projectRoot);
|
|
4857
|
+
return { files: results, complete, errors };
|
|
4858
|
+
}
|
|
4859
|
+
function assignRefsToSymbols2(refs, symbols) {
|
|
4860
|
+
if (refs.length === 0 || symbols.length === 0) return [];
|
|
4861
|
+
const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
|
|
4862
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4863
|
+
const assigned = [];
|
|
4864
|
+
for (const ref of refs) {
|
|
4865
|
+
let owner;
|
|
4866
|
+
for (const symbol of ordered) {
|
|
4867
|
+
if (symbol.line > ref.line) break;
|
|
4868
|
+
owner = symbol;
|
|
5019
4869
|
}
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
if (socket !== this.socket) return;
|
|
5027
|
-
const wasConnected = this.info !== null;
|
|
5028
|
-
this.socket = null;
|
|
5029
|
-
this.info = null;
|
|
5030
|
-
this.activity = null;
|
|
5031
|
-
this.health = null;
|
|
5032
|
-
const error = new Error("codebase-index server connection closed");
|
|
5033
|
-
this.connectReject?.(error);
|
|
5034
|
-
this.connectResolve = null;
|
|
5035
|
-
this.connectReject = null;
|
|
5036
|
-
this.rejectPending(error);
|
|
5037
|
-
if (wasConnected) this.transition("error", { error });
|
|
5038
|
-
maybeStopHeartbeatLoop();
|
|
4870
|
+
if (!owner && ref.callType === "import") owner = ordered[0];
|
|
4871
|
+
if (!owner || owner.id <= 0) continue;
|
|
4872
|
+
const key = `${owner.id}:${ref.toName}:${ref.callType}`;
|
|
4873
|
+
if (seen.has(key)) continue;
|
|
4874
|
+
seen.add(key);
|
|
4875
|
+
assigned.push({ ...ref, fromId: owner.id });
|
|
5039
4876
|
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
4877
|
+
return assigned;
|
|
4878
|
+
}
|
|
4879
|
+
async function runIndexer(_ctx, opts) {
|
|
4880
|
+
const store = new IndexStore(opts.projectRoot, { indexDir: opts.indexDir });
|
|
4881
|
+
try {
|
|
4882
|
+
return await runIndexerWithStore(store, opts);
|
|
4883
|
+
} finally {
|
|
4884
|
+
try {
|
|
4885
|
+
store.close();
|
|
4886
|
+
} catch {
|
|
5044
4887
|
}
|
|
5045
4888
|
}
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
4889
|
+
}
|
|
4890
|
+
async function runIndexerWithStore(store, opts) {
|
|
4891
|
+
const { projectRoot, langs, ignore = [], signal } = opts;
|
|
4892
|
+
const relationGraphVersion = "2";
|
|
4893
|
+
const refResolutionVersion = "2";
|
|
4894
|
+
const force = (opts.force ?? false) || store.getMetadata("relation_graph_version") !== relationGraphVersion;
|
|
4895
|
+
const needsFullRefResolution = force || store.getMetadata("ref_resolution_version") !== refResolutionVersion;
|
|
4896
|
+
const startMs = Date.now();
|
|
4897
|
+
const errors = [];
|
|
4898
|
+
const langStats = {};
|
|
4899
|
+
let filesIndexed = 0;
|
|
4900
|
+
let symbolsIndexed = 0;
|
|
4901
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
4902
|
+
let files;
|
|
4903
|
+
let discoveredFiles = null;
|
|
4904
|
+
let discoveryComplete = true;
|
|
4905
|
+
let trustedUnchanged;
|
|
4906
|
+
if (opts.files && opts.files.length > 0) {
|
|
4907
|
+
files = opts.files.map((f) => path12.resolve(projectRoot, f)).filter((f) => {
|
|
4908
|
+
if (!isWithinProject(projectRoot, f)) return false;
|
|
4909
|
+
const rel = path12.relative(projectRoot, f).replace(/\\/g, "/");
|
|
4910
|
+
return !rel.split("/").some((seg) => DEFAULT_IGNORE.includes(seg)) && !DEFAULT_IGNORE_FILES.has(path12.basename(f)) && !isGitIgnored(rel, false);
|
|
4911
|
+
});
|
|
4912
|
+
} else {
|
|
4913
|
+
const discovery = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
4914
|
+
files = discovery.files;
|
|
4915
|
+
errors.push(...discovery.errors);
|
|
4916
|
+
discoveryComplete = discovery.complete;
|
|
4917
|
+
discoveredFiles = new Set(files);
|
|
4918
|
+
trustedUnchanged = discovery.trustedUnchanged;
|
|
5053
4919
|
}
|
|
5054
|
-
|
|
5055
|
-
const
|
|
5056
|
-
|
|
4920
|
+
if (langs && langs.length > 0) {
|
|
4921
|
+
const langSet = new Set(langs);
|
|
4922
|
+
files = files.filter((f) => {
|
|
4923
|
+
const lang = detectLang(f);
|
|
4924
|
+
return lang ? langSet.has(lang) : false;
|
|
4925
|
+
});
|
|
5057
4926
|
}
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
);
|
|
5068
|
-
|
|
5069
|
-
|
|
4927
|
+
if (force) store.clearAll();
|
|
4928
|
+
const existingMeta = /* @__PURE__ */ new Map();
|
|
4929
|
+
if (!force) {
|
|
4930
|
+
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
4931
|
+
}
|
|
4932
|
+
const totalFilesForProgress = files.length;
|
|
4933
|
+
let filesPreSkipped = 0;
|
|
4934
|
+
if (!force && trustedUnchanged) {
|
|
4935
|
+
files = files.filter((file) => {
|
|
4936
|
+
const meta = existingMeta.get(file);
|
|
4937
|
+
if (!meta || !trustedUnchanged.has(file)) return true;
|
|
4938
|
+
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
4939
|
+
symbolsIndexed += meta.symbolCount;
|
|
4940
|
+
filesIndexed++;
|
|
4941
|
+
filesPreSkipped++;
|
|
4942
|
+
return false;
|
|
4943
|
+
});
|
|
4944
|
+
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
4945
|
+
}
|
|
4946
|
+
const parallelBatch = resolveParallelBatch();
|
|
4947
|
+
let filesSinceLastYield = 0;
|
|
4948
|
+
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
4949
|
+
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
4950
|
+
const batchFiles = files.slice(batchStart, batchEnd);
|
|
4951
|
+
opts.onProgress?.(filesPreSkipped + batchEnd, totalFilesForProgress);
|
|
4952
|
+
filesSinceLastYield += batchFiles.length;
|
|
4953
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
4954
|
+
filesSinceLastYield = 0;
|
|
4955
|
+
await yieldEventLoop();
|
|
4956
|
+
if (isFrugalPerf()) {
|
|
4957
|
+
await new Promise((r) => setTimeout(r, 8));
|
|
4958
|
+
}
|
|
4959
|
+
throwIfAborted(signal);
|
|
4960
|
+
}
|
|
4961
|
+
const statOpts = signal ? { signal } : {};
|
|
4962
|
+
const statReadParse = await Promise.allSettled(
|
|
4963
|
+
batchFiles.map(
|
|
4964
|
+
async (file) => {
|
|
4965
|
+
let stat2;
|
|
4966
|
+
try {
|
|
4967
|
+
stat2 = await fs10.stat(file, statOpts);
|
|
4968
|
+
} catch (e) {
|
|
4969
|
+
if (isAbortError(e)) throw e;
|
|
4970
|
+
return {
|
|
4971
|
+
file,
|
|
4972
|
+
stat: null,
|
|
4973
|
+
lang: "",
|
|
4974
|
+
parsed: null,
|
|
4975
|
+
error: `stat error: ${e instanceof Error ? e.message : String(e)}`,
|
|
4976
|
+
missing: isMissingPathError(e)
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4979
|
+
if (!stat2.isFile()) return { file, stat: stat2, lang: "", parsed: null };
|
|
4980
|
+
const lang = detectLang(file);
|
|
4981
|
+
if (!lang) return { file, stat: stat2, lang: "", parsed: null };
|
|
4982
|
+
if (stat2.size > MAX_INDEX_FILE_BYTES) {
|
|
4983
|
+
return {
|
|
4984
|
+
file,
|
|
4985
|
+
stat: stat2,
|
|
4986
|
+
lang,
|
|
4987
|
+
parsed: null,
|
|
4988
|
+
error: `file too large (${stat2.size} bytes; max ${MAX_INDEX_FILE_BYTES})`
|
|
4989
|
+
};
|
|
4990
|
+
}
|
|
4991
|
+
const meta = existingMeta.get(file);
|
|
4992
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
|
|
4993
|
+
return { file, stat: stat2, lang, parsed: null, skippedMeta: meta };
|
|
4994
|
+
}
|
|
4995
|
+
let content;
|
|
4996
|
+
try {
|
|
4997
|
+
content = await fs10.readFile(file, { encoding: "utf8", signal });
|
|
4998
|
+
} catch (e) {
|
|
4999
|
+
if (isAbortError(e)) throw e;
|
|
5000
|
+
return {
|
|
5001
|
+
file,
|
|
5002
|
+
stat: stat2,
|
|
5003
|
+
lang,
|
|
5004
|
+
parsed: null,
|
|
5005
|
+
error: `read error: ${e instanceof Error ? e.message : String(e)}`
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
let parsed;
|
|
5009
|
+
try {
|
|
5010
|
+
parsed = await parseFileContent(file, content, lang);
|
|
5011
|
+
} catch (e) {
|
|
5012
|
+
return {
|
|
5013
|
+
file,
|
|
5014
|
+
stat: stat2,
|
|
5015
|
+
lang,
|
|
5016
|
+
parsed: null,
|
|
5017
|
+
error: `parse error: ${e instanceof Error ? e.message : String(e)}`
|
|
5018
|
+
};
|
|
5019
|
+
}
|
|
5020
|
+
return { file, stat: stat2, lang, parsed, content };
|
|
5021
|
+
}
|
|
5022
|
+
)
|
|
5023
|
+
);
|
|
5024
|
+
const batchEntries = [];
|
|
5025
|
+
const deleteForFiles = [];
|
|
5026
|
+
for (let fi = 0; fi < statReadParse.length; fi++) {
|
|
5027
|
+
const settled = statReadParse[fi];
|
|
5028
|
+
const file = expectDefined5(batchFiles[fi]);
|
|
5029
|
+
if (settled.status === "rejected") {
|
|
5030
|
+
const err = settled.reason;
|
|
5031
|
+
if (err instanceof Error && isAbortError(err)) throw err;
|
|
5032
|
+
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5033
|
+
continue;
|
|
5034
|
+
}
|
|
5035
|
+
const result = settled.value;
|
|
5036
|
+
if (result.error) {
|
|
5037
|
+
if (result.missing) store.deleteFile(file);
|
|
5038
|
+
errors.push(`${file}: ${result.error}`);
|
|
5039
|
+
continue;
|
|
5040
|
+
}
|
|
5041
|
+
const { stat: stat2, lang, parsed } = result;
|
|
5042
|
+
if (result.skippedMeta) {
|
|
5043
|
+
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
5044
|
+
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
5045
|
+
filesIndexed++;
|
|
5046
|
+
continue;
|
|
5047
|
+
}
|
|
5048
|
+
if (!lang || !parsed) {
|
|
5049
|
+
if (lang) {
|
|
5050
|
+
store.upsertFile({
|
|
5051
|
+
file,
|
|
5052
|
+
lang,
|
|
5053
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5054
|
+
symbolCount: 0,
|
|
5055
|
+
lastIndexed: Date.now()
|
|
5056
|
+
});
|
|
5057
|
+
filesIndexed++;
|
|
5058
|
+
}
|
|
5059
|
+
continue;
|
|
5060
|
+
}
|
|
5061
|
+
if (parsed.symbols.length === 0) {
|
|
5062
|
+
store.replaceEmptyFile({
|
|
5063
|
+
file,
|
|
5064
|
+
lang,
|
|
5065
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5066
|
+
symbolCount: 0,
|
|
5067
|
+
lastIndexed: Date.now()
|
|
5068
|
+
});
|
|
5069
|
+
filesIndexed++;
|
|
5070
|
+
continue;
|
|
5071
|
+
}
|
|
5072
|
+
batchEntries.push({
|
|
5073
|
+
file,
|
|
5074
|
+
lang,
|
|
5075
|
+
symbols: parsed.symbols,
|
|
5076
|
+
refs: parsed.refs ?? [],
|
|
5077
|
+
mtimeMs: Math.floor(stat2.mtimeMs),
|
|
5078
|
+
symbolCount: parsed.symbols.length
|
|
5079
|
+
});
|
|
5080
|
+
deleteForFiles.push(file);
|
|
5070
5081
|
}
|
|
5071
|
-
|
|
5072
|
-
}
|
|
5073
|
-
spawnDetachedServer() {
|
|
5074
|
-
const url = resolveProjectServerUrl();
|
|
5075
|
-
if (!url) throw new Error("built codebase-index project server is unavailable");
|
|
5076
|
-
if (process.platform !== "win32") {
|
|
5082
|
+
if (batchEntries.length > 0) {
|
|
5077
5083
|
try {
|
|
5078
|
-
|
|
5079
|
-
|
|
5084
|
+
store.commitBatch(batchEntries, { deleteForFiles });
|
|
5085
|
+
for (const entry of batchEntries) {
|
|
5086
|
+
const count = entry.symbols.length;
|
|
5087
|
+
symbolsIndexed += count;
|
|
5088
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
5089
|
+
filesIndexed++;
|
|
5090
|
+
}
|
|
5091
|
+
} catch (err) {
|
|
5092
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5093
|
+
errors.push(`commitBatch failed: ${message} \u2014 falling back to per-file writes`);
|
|
5094
|
+
for (const entry of batchEntries) {
|
|
5095
|
+
try {
|
|
5096
|
+
store.deleteRefsForFile(entry.file);
|
|
5097
|
+
store.deleteSymbolsForFile(entry.file);
|
|
5098
|
+
const symbolsWithIds = store.insertSymbols(entry.symbols);
|
|
5099
|
+
symbolsIndexed += symbolsWithIds.length;
|
|
5100
|
+
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
5101
|
+
filesIndexed++;
|
|
5102
|
+
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
5103
|
+
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
5104
|
+
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
5105
|
+
}
|
|
5106
|
+
store.resolveRefsForNames([
|
|
5107
|
+
...entry.symbols.map((symbol) => symbol.name),
|
|
5108
|
+
...entry.refs.map((ref) => ref.toName)
|
|
5109
|
+
]);
|
|
5110
|
+
store.upsertFile({
|
|
5111
|
+
file: entry.file,
|
|
5112
|
+
lang: entry.lang,
|
|
5113
|
+
mtimeMs: entry.mtimeMs,
|
|
5114
|
+
symbolCount: entry.symbolCount,
|
|
5115
|
+
lastIndexed: Date.now()
|
|
5116
|
+
});
|
|
5117
|
+
} catch (innerErr) {
|
|
5118
|
+
errors.push(
|
|
5119
|
+
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
5120
|
+
);
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5080
5123
|
}
|
|
5081
5124
|
}
|
|
5082
|
-
const args = [fileURLToPath2(url), "--project-root", this.projectRoot];
|
|
5083
|
-
if (this.indexDir) args.push("--index-dir", this.indexDir);
|
|
5084
|
-
const child = spawn4(process.execPath, args, {
|
|
5085
|
-
detached: true,
|
|
5086
|
-
stdio: "ignore",
|
|
5087
|
-
windowsHide: true,
|
|
5088
|
-
env: process.env
|
|
5089
|
-
});
|
|
5090
|
-
child.unref();
|
|
5091
|
-
}
|
|
5092
|
-
forceKillKnownServer() {
|
|
5093
|
-
const pid = this.info?.pid;
|
|
5094
|
-
return pid ? this.forceKillServer(pid) : false;
|
|
5095
5125
|
}
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
const metadataPath = projectIndexServerMetadataPath(this.projectRoot, this.indexDir);
|
|
5101
|
-
try {
|
|
5102
|
-
const metadata = JSON.parse(fs10.readFileSync(metadataPath, "utf8"));
|
|
5103
|
-
if (metadata.pid === pid) fs10.rmSync(metadataPath, { force: true });
|
|
5104
|
-
} catch {
|
|
5126
|
+
if (discoveredFiles && discoveryComplete) {
|
|
5127
|
+
for (const [file_] of existingMeta) {
|
|
5128
|
+
if (!discoveredFiles.has(file_)) {
|
|
5129
|
+
store.deleteFile(file_);
|
|
5105
5130
|
}
|
|
5106
|
-
return true;
|
|
5107
|
-
} catch {
|
|
5108
|
-
return false;
|
|
5109
5131
|
}
|
|
5110
5132
|
}
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
if (!heartbeatTimer) return;
|
|
5126
|
-
if ([...connections.values()].some((connection) => connection.isConnected())) return;
|
|
5127
|
-
clearInterval(heartbeatTimer);
|
|
5128
|
-
heartbeatTimer = void 0;
|
|
5133
|
+
if (needsFullRefResolution) store.resolveRefs();
|
|
5134
|
+
store.setMetadata("ref_resolution_version", refResolutionVersion);
|
|
5135
|
+
store.setMetadata("relation_graph_version", relationGraphVersion);
|
|
5136
|
+
if (!opts.files || filesIndexed >= 50) store.optimize();
|
|
5137
|
+
store.setLastIndexed(Date.now());
|
|
5138
|
+
if (!opts.files) store.compactIfNeeded();
|
|
5139
|
+
const durationMs = Date.now() - startMs;
|
|
5140
|
+
return {
|
|
5141
|
+
filesIndexed,
|
|
5142
|
+
symbolsIndexed,
|
|
5143
|
+
langStats,
|
|
5144
|
+
durationMs,
|
|
5145
|
+
errors
|
|
5146
|
+
};
|
|
5129
5147
|
}
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5148
|
+
|
|
5149
|
+
// src/codebase-index/index-service.ts
|
|
5150
|
+
async function indexService(args, hooks = {}) {
|
|
5151
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5152
|
+
try {
|
|
5153
|
+
return await runIndexerWithStore(store, {
|
|
5154
|
+
projectRoot: args.projectRoot,
|
|
5155
|
+
indexDir: args.indexDir,
|
|
5156
|
+
files: args.files,
|
|
5157
|
+
force: args.force,
|
|
5158
|
+
langs: args.langs,
|
|
5159
|
+
ignore: args.ignore,
|
|
5160
|
+
signal: hooks.signal,
|
|
5161
|
+
onProgress: hooks.onProgress
|
|
5162
|
+
});
|
|
5163
|
+
} finally {
|
|
5164
|
+
indexStorePool.release(store);
|
|
5136
5165
|
}
|
|
5137
|
-
return connection;
|
|
5138
5166
|
}
|
|
5139
|
-
function
|
|
5140
|
-
|
|
5167
|
+
function searchService(args) {
|
|
5168
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5169
|
+
try {
|
|
5170
|
+
return store.searchRanked(
|
|
5171
|
+
args.query,
|
|
5172
|
+
{
|
|
5173
|
+
kind: args.kind,
|
|
5174
|
+
lang: args.lang,
|
|
5175
|
+
file: args.file,
|
|
5176
|
+
lspKind: args.lspKind
|
|
5177
|
+
},
|
|
5178
|
+
args.limit
|
|
5179
|
+
);
|
|
5180
|
+
} finally {
|
|
5181
|
+
indexStorePool.release(store);
|
|
5182
|
+
}
|
|
5141
5183
|
}
|
|
5142
|
-
function
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5184
|
+
function statsService(args) {
|
|
5185
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5186
|
+
try {
|
|
5187
|
+
return store.getStats();
|
|
5188
|
+
} finally {
|
|
5189
|
+
indexStorePool.release(store);
|
|
5190
|
+
}
|
|
5147
5191
|
}
|
|
5148
|
-
function
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5192
|
+
function packageGraphService(args) {
|
|
5193
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5194
|
+
try {
|
|
5195
|
+
return store.getPackageGraph();
|
|
5196
|
+
} finally {
|
|
5197
|
+
indexStorePool.release(store);
|
|
5198
|
+
}
|
|
5153
5199
|
}
|
|
5154
|
-
|
|
5155
|
-
const
|
|
5156
|
-
const connection = connectionFor(projectRoot, indexDir);
|
|
5200
|
+
function fileGraphService(args) {
|
|
5201
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5157
5202
|
try {
|
|
5158
|
-
return
|
|
5203
|
+
return store.getFileGraph(args.packageFilter);
|
|
5159
5204
|
} finally {
|
|
5160
|
-
|
|
5161
|
-
connections.delete(endpoint);
|
|
5162
|
-
connectionStates.delete(endpoint);
|
|
5205
|
+
indexStorePool.release(store);
|
|
5163
5206
|
}
|
|
5164
5207
|
}
|
|
5165
|
-
function
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
};
|
|
5173
|
-
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
5174
|
-
heartbeatTimer = void 0;
|
|
5208
|
+
function symbolGraphService(args) {
|
|
5209
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
5210
|
+
try {
|
|
5211
|
+
return store.getSymbolGraph(args.fileFilter);
|
|
5212
|
+
} finally {
|
|
5213
|
+
indexStorePool.release(store);
|
|
5214
|
+
}
|
|
5175
5215
|
}
|
|
5176
5216
|
|
|
5177
5217
|
// src/codebase-index/background-indexer.ts
|
|
5218
|
+
init_languages();
|
|
5178
5219
|
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 24e4;
|
|
5179
5220
|
var DEFAULT_INCREMENTAL_TIMEOUT_MS = 6e4;
|
|
5180
5221
|
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
@@ -5307,8 +5348,17 @@ async function shutdownCodebaseIndexHost() {
|
|
|
5307
5348
|
}
|
|
5308
5349
|
}
|
|
5309
5350
|
}
|
|
5351
|
+
var warnedInvalidEndpoints = /* @__PURE__ */ new Set();
|
|
5352
|
+
function warnEndpointInvalidOnce(availability) {
|
|
5353
|
+
if (warnedInvalidEndpoints.has(availability.endpoint)) return;
|
|
5354
|
+
warnedInvalidEndpoints.add(availability.endpoint);
|
|
5355
|
+
process.stderr.write(
|
|
5356
|
+
`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.
|
|
5357
|
+
`
|
|
5358
|
+
);
|
|
5359
|
+
}
|
|
5310
5360
|
function callIndexOp(op, args, opts) {
|
|
5311
|
-
const availability = resolveProjectIndexDaemonAvailability();
|
|
5361
|
+
const availability = resolveProjectIndexDaemonAvailability(args.projectRoot, args.indexDir);
|
|
5312
5362
|
if (availability.kind === "available") {
|
|
5313
5363
|
return callProjectIndexServer(op, args, opts);
|
|
5314
5364
|
}
|
|
@@ -5319,6 +5369,14 @@ function callIndexOp(op, args, opts) {
|
|
|
5319
5369
|
)
|
|
5320
5370
|
);
|
|
5321
5371
|
}
|
|
5372
|
+
if (availability.kind === "endpoint-invalid") {
|
|
5373
|
+
warnEndpointInvalidOnce(availability);
|
|
5374
|
+
return Promise.reject(
|
|
5375
|
+
new Error(
|
|
5376
|
+
`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.`
|
|
5377
|
+
)
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5322
5380
|
const w = ensureWorker();
|
|
5323
5381
|
if (!w) return callInline(op, args, opts);
|
|
5324
5382
|
if (opts.signal?.aborted) {
|
|
@@ -5592,7 +5650,11 @@ function checkCodebaseIndexServerHealth(projectRoot, indexDir, options = {}) {
|
|
|
5592
5650
|
return checkProjectIndexServerHealth(projectRoot, indexDir, options);
|
|
5593
5651
|
}
|
|
5594
5652
|
function ensureCodebaseIndexServer(options) {
|
|
5595
|
-
|
|
5653
|
+
const availability = resolveProjectIndexDaemonAvailability(options.projectRoot, options.indexDir);
|
|
5654
|
+
if (availability.kind !== "available") {
|
|
5655
|
+
if (availability.kind === "endpoint-invalid") warnEndpointInvalidOnce(availability);
|
|
5656
|
+
return Promise.resolve();
|
|
5657
|
+
}
|
|
5596
5658
|
return ensureProjectIndexServer({
|
|
5597
5659
|
projectRoot: options.projectRoot,
|
|
5598
5660
|
indexDir: options.indexDir,
|
|
@@ -6400,6 +6462,7 @@ export {
|
|
|
6400
6462
|
packageGraphService2 as packageGraphService,
|
|
6401
6463
|
resetIndexCircuitBreaker,
|
|
6402
6464
|
resolveIndexDir,
|
|
6465
|
+
resolveProjectIndexDaemonAvailability,
|
|
6403
6466
|
runDeadCodeScan,
|
|
6404
6467
|
runIndexer,
|
|
6405
6468
|
runStartupIndex,
|