@wrongstack/tools 0.296.3 → 0.296.4

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