@wrongstack/tools 0.236.0 → 0.250.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +1 -0
- package/dist/audit.js.map +1 -1
- package/dist/background-indexer-DwJsyAB0.d.ts +373 -0
- package/dist/bash.js +5 -0
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +865 -327
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.d.ts +53 -2
- package/dist/codebase-index/index.js +854 -364
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/worker.d.ts +2 -0
- package/dist/codebase-index/worker.js +2321 -0
- package/dist/codebase-index/worker.js.map +1 -0
- package/dist/diff.js +2 -1
- package/dist/diff.js.map +1 -1
- package/dist/exec.js +1 -0
- package/dist/exec.js.map +1 -1
- package/dist/format.js +1 -0
- package/dist/format.js.map +1 -1
- package/dist/git.js +2 -1
- package/dist/git.js.map +1 -1
- package/dist/grep.js +2 -2
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +886 -386
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -0
- package/dist/install.js.map +1 -1
- package/dist/lint.js +1 -0
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +1 -1
- package/dist/logs.js.map +1 -1
- package/dist/outdated.js +1 -1
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +865 -327
- package/dist/pack.js.map +1 -1
- package/dist/patch.js +1 -1
- package/dist/patch.js.map +1 -1
- package/dist/replace.js +3 -2
- package/dist/replace.js.map +1 -1
- package/dist/test.js +1 -0
- package/dist/test.js.map +1 -1
- package/dist/typecheck.js +1 -0
- package/dist/typecheck.js.map +1 -1
- package/package.json +2 -2
- package/dist/background-indexer-CtbgPExj.d.ts +0 -228
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs4 from 'node:fs/promises';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { resolve, sep, dirname, join } from 'node:path';
|
|
4
4
|
import * as Core from '@wrongstack/core';
|
|
5
|
-
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, emptyTaskFile, formatTaskList, formatPlan, recordPackageAction, detectPackageEcosystem, mutateTasks, emptyPlan, computeTaskItemProgress, resolveWstackPaths } from '@wrongstack/core';
|
|
5
|
+
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, emptyTaskFile, formatTaskList, formatPlan, recordPackageAction, detectPackageEcosystem, mutateTasks, emptyPlan, computeTaskItemProgress, resolveWstackPaths, truncate } from '@wrongstack/core';
|
|
6
6
|
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
7
7
|
import * as os from 'node:os';
|
|
8
8
|
import * as fs7 from 'node:fs';
|
|
@@ -11,6 +11,8 @@ import * as dns from 'node:dns/promises';
|
|
|
11
11
|
import * as net from 'node:net';
|
|
12
12
|
import { Agent } from 'undici';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { Worker } from 'node:worker_threads';
|
|
14
16
|
import * as ts from 'typescript';
|
|
15
17
|
import { randomUUID } from 'node:crypto';
|
|
16
18
|
|
|
@@ -599,7 +601,7 @@ async function globFiles(pattern, base, extraGlob) {
|
|
|
599
601
|
function checkRg() {
|
|
600
602
|
return new Promise((resolve7) => {
|
|
601
603
|
try {
|
|
602
|
-
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore" });
|
|
604
|
+
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", windowsHide: true });
|
|
603
605
|
p.on("error", () => resolve7(false));
|
|
604
606
|
p.on("close", (code) => resolve7(code === 0));
|
|
605
607
|
} catch {
|
|
@@ -612,7 +614,8 @@ function spawnRgFind(pattern, base) {
|
|
|
612
614
|
const child = spawn("rg", args, {
|
|
613
615
|
signal: AbortSignal.timeout(3e4),
|
|
614
616
|
env: buildChildEnv(),
|
|
615
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
617
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
618
|
+
windowsHide: true
|
|
616
619
|
});
|
|
617
620
|
let buf = "";
|
|
618
621
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -828,7 +831,7 @@ var grepTool = {
|
|
|
828
831
|
async function detectRg(signal) {
|
|
829
832
|
return new Promise((resolve7) => {
|
|
830
833
|
try {
|
|
831
|
-
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal });
|
|
834
|
+
const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal, windowsHide: true });
|
|
832
835
|
p.on("error", () => resolve7(false));
|
|
833
836
|
p.on("close", (code) => resolve7(code === 0));
|
|
834
837
|
} catch {
|
|
@@ -858,7 +861,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
858
861
|
const FLUSH_AT = 16;
|
|
859
862
|
const MAX_BUF_BYTES = 1e6;
|
|
860
863
|
let bufOverflow = false;
|
|
861
|
-
const child = spawn("rg", args, { signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"] });
|
|
864
|
+
const child = spawn("rg", args, { signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
862
865
|
const queue = [];
|
|
863
866
|
let waiter;
|
|
864
867
|
const wake = () => {
|
|
@@ -1523,6 +1526,10 @@ var bashTool = {
|
|
|
1523
1526
|
env,
|
|
1524
1527
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1525
1528
|
detached: true,
|
|
1529
|
+
// Detached console children on Windows allocate their own VISIBLE
|
|
1530
|
+
// console window (one per background command — test suites flash
|
|
1531
|
+
// dozens). CREATE_NO_WINDOW suppresses it; no-op elsewhere.
|
|
1532
|
+
windowsHide: true,
|
|
1526
1533
|
signal: opts.signal
|
|
1527
1534
|
});
|
|
1528
1535
|
const pid2 = child2.pid;
|
|
@@ -1575,6 +1582,7 @@ var bashTool = {
|
|
|
1575
1582
|
env,
|
|
1576
1583
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1577
1584
|
detached,
|
|
1585
|
+
windowsHide: true,
|
|
1578
1586
|
...isWin ? {} : { signal: opts.signal }
|
|
1579
1587
|
});
|
|
1580
1588
|
const pid = child.pid;
|
|
@@ -1589,7 +1597,7 @@ var bashTool = {
|
|
|
1589
1597
|
});
|
|
1590
1598
|
}
|
|
1591
1599
|
let buf = "";
|
|
1592
|
-
let
|
|
1600
|
+
let pending2 = "";
|
|
1593
1601
|
let timedOut = false;
|
|
1594
1602
|
const timers = [];
|
|
1595
1603
|
function killWithTimeout(child2, timeoutMs2) {
|
|
@@ -1671,9 +1679,9 @@ var bashTool = {
|
|
|
1671
1679
|
});
|
|
1672
1680
|
let lastFlush = Date.now();
|
|
1673
1681
|
const flush = () => {
|
|
1674
|
-
if (
|
|
1675
|
-
const text =
|
|
1676
|
-
|
|
1682
|
+
if (pending2.length === 0) return null;
|
|
1683
|
+
const text = pending2;
|
|
1684
|
+
pending2 = "";
|
|
1677
1685
|
lastFlush = Date.now();
|
|
1678
1686
|
return text;
|
|
1679
1687
|
};
|
|
@@ -1697,7 +1705,7 @@ var bashTool = {
|
|
|
1697
1705
|
if (buf.length < MAX_OUTPUT) {
|
|
1698
1706
|
buf += text.slice(0, MAX_OUTPUT - buf.length);
|
|
1699
1707
|
}
|
|
1700
|
-
|
|
1708
|
+
pending2 += text;
|
|
1701
1709
|
push({ kind: "data", text });
|
|
1702
1710
|
pauseIfFlooded();
|
|
1703
1711
|
};
|
|
@@ -1735,7 +1743,7 @@ var bashTool = {
|
|
|
1735
1743
|
return;
|
|
1736
1744
|
}
|
|
1737
1745
|
const now = Date.now();
|
|
1738
|
-
if (
|
|
1746
|
+
if (pending2.length >= STREAM_FLUSH_BYTES || now - lastFlush >= STREAM_FLUSH_INTERVAL_MS) {
|
|
1739
1747
|
const text = flush();
|
|
1740
1748
|
if (text) yield { type: "partial_output", text };
|
|
1741
1749
|
}
|
|
@@ -1989,6 +1997,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1989
1997
|
cwd,
|
|
1990
1998
|
env: buildChildEnv(sessionId),
|
|
1991
1999
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2000
|
+
windowsHide: true,
|
|
1992
2001
|
...isWin ? {} : { signal },
|
|
1993
2002
|
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
1994
2003
|
});
|
|
@@ -3151,7 +3160,8 @@ function runGit(args, cwd, signal) {
|
|
|
3151
3160
|
cwd,
|
|
3152
3161
|
signal,
|
|
3153
3162
|
env: buildChildEnv(),
|
|
3154
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3163
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3164
|
+
windowsHide: true
|
|
3155
3165
|
});
|
|
3156
3166
|
child.stdout?.on("data", (chunk) => {
|
|
3157
3167
|
if (stdout.length < MAX_OUTPUT3) {
|
|
@@ -3274,7 +3284,7 @@ function runPatch(args, cwd, signal) {
|
|
|
3274
3284
|
let stdout = "";
|
|
3275
3285
|
let stderr = "";
|
|
3276
3286
|
const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
|
|
3277
|
-
const child = spawn("patch", args, { cwd, signal, env, stdio: ["pipe", "pipe", "pipe"] });
|
|
3287
|
+
const child = spawn("patch", args, { cwd, signal, env, stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
3278
3288
|
child.stdout?.on("data", (c) => {
|
|
3279
3289
|
stdout += c.toString();
|
|
3280
3290
|
});
|
|
@@ -3513,7 +3523,8 @@ function runGit2(args, cwd, signal) {
|
|
|
3513
3523
|
cwd,
|
|
3514
3524
|
signal,
|
|
3515
3525
|
env: buildChildEnv(),
|
|
3516
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3526
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3527
|
+
windowsHide: true
|
|
3517
3528
|
});
|
|
3518
3529
|
child.stdout?.on("data", (c) => {
|
|
3519
3530
|
stdout += c.toString();
|
|
@@ -3741,7 +3752,7 @@ async function* spawnStream(opts) {
|
|
|
3741
3752
|
const maxQueue = opts.maxQueueSize ?? 500;
|
|
3742
3753
|
let stdout = "";
|
|
3743
3754
|
let stderr = "";
|
|
3744
|
-
let
|
|
3755
|
+
let pending2 = "";
|
|
3745
3756
|
let error;
|
|
3746
3757
|
const cmd = resolveWin32Command(opts.cmd);
|
|
3747
3758
|
const needsShell = process.platform === "win32" && (cmd.endsWith(".cmd") || cmd.endsWith(".bat"));
|
|
@@ -3750,6 +3761,7 @@ async function* spawnStream(opts) {
|
|
|
3750
3761
|
signal: opts.signal,
|
|
3751
3762
|
env: buildChildEnv(),
|
|
3752
3763
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3764
|
+
windowsHide: true,
|
|
3753
3765
|
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
3754
3766
|
});
|
|
3755
3767
|
const queue = [];
|
|
@@ -3819,14 +3831,14 @@ async function* spawnStream(opts) {
|
|
|
3819
3831
|
exitCode = 1;
|
|
3820
3832
|
continue;
|
|
3821
3833
|
}
|
|
3822
|
-
|
|
3823
|
-
if (
|
|
3824
|
-
yield { type: "partial_output", text:
|
|
3825
|
-
|
|
3834
|
+
pending2 += chunk.data;
|
|
3835
|
+
if (pending2.length >= flushAt) {
|
|
3836
|
+
yield { type: "partial_output", text: pending2 };
|
|
3837
|
+
pending2 = "";
|
|
3826
3838
|
}
|
|
3827
3839
|
}
|
|
3828
|
-
if (
|
|
3829
|
-
yield { type: "partial_output", text:
|
|
3840
|
+
if (pending2.length > 0) {
|
|
3841
|
+
yield { type: "partial_output", text: pending2 };
|
|
3830
3842
|
}
|
|
3831
3843
|
return {
|
|
3832
3844
|
stdout,
|
|
@@ -4539,7 +4551,7 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
4539
4551
|
const MAX = 1e5;
|
|
4540
4552
|
const resolved = resolveWin32Command(manager);
|
|
4541
4553
|
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
4542
|
-
const child = spawn(resolved, args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {} });
|
|
4554
|
+
const child = spawn(resolved, args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true, ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {} });
|
|
4543
4555
|
child.stdout?.on("data", (c) => {
|
|
4544
4556
|
if (stdout.length < MAX) stdout += c.toString();
|
|
4545
4557
|
});
|
|
@@ -4693,7 +4705,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
4693
4705
|
clearTimeout(timer);
|
|
4694
4706
|
resolve7(result);
|
|
4695
4707
|
};
|
|
4696
|
-
const child = spawn("docker", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"] });
|
|
4708
|
+
const child = spawn("docker", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
4697
4709
|
const timer = setTimeout(() => {
|
|
4698
4710
|
child.kill("SIGTERM");
|
|
4699
4711
|
finish(empty());
|
|
@@ -5754,8 +5766,91 @@ ${mode.description}`
|
|
|
5754
5766
|
};
|
|
5755
5767
|
}
|
|
5756
5768
|
|
|
5769
|
+
// src/codebase-index/circuit-breaker.ts
|
|
5770
|
+
var CircuitOpenError = class extends Error {
|
|
5771
|
+
name = "CircuitOpenError";
|
|
5772
|
+
};
|
|
5773
|
+
var IndexTimeoutError = class extends Error {
|
|
5774
|
+
name = "IndexTimeoutError";
|
|
5775
|
+
};
|
|
5776
|
+
var LockError = class extends Error {
|
|
5777
|
+
name = "LockError";
|
|
5778
|
+
};
|
|
5779
|
+
var IndexCircuitBreaker = class {
|
|
5780
|
+
failureThreshold;
|
|
5781
|
+
cooldownMs;
|
|
5782
|
+
now;
|
|
5783
|
+
state = "closed";
|
|
5784
|
+
consecutiveFailures = 0;
|
|
5785
|
+
openedAt = 0;
|
|
5786
|
+
lastFailure = null;
|
|
5787
|
+
probeInFlight = false;
|
|
5788
|
+
constructor(opts = {}) {
|
|
5789
|
+
this.failureThreshold = opts.failureThreshold ?? 3;
|
|
5790
|
+
this.cooldownMs = opts.cooldownMs ?? 6e4;
|
|
5791
|
+
this.now = opts.now ?? Date.now;
|
|
5792
|
+
}
|
|
5793
|
+
/**
|
|
5794
|
+
* True when a run may proceed. An open circuit transitions to half-open once
|
|
5795
|
+
* the cooldown has elapsed, admitting exactly one probe; further requests
|
|
5796
|
+
* are rejected until that probe settles via recordSuccess/recordFailure.
|
|
5797
|
+
*/
|
|
5798
|
+
allowRequest() {
|
|
5799
|
+
if (this.state === "closed") return true;
|
|
5800
|
+
if (this.state === "open") {
|
|
5801
|
+
if (this.now() - this.openedAt < this.cooldownMs) return false;
|
|
5802
|
+
this.state = "half-open";
|
|
5803
|
+
this.probeInFlight = true;
|
|
5804
|
+
return true;
|
|
5805
|
+
}
|
|
5806
|
+
if (this.probeInFlight) return false;
|
|
5807
|
+
this.probeInFlight = true;
|
|
5808
|
+
return true;
|
|
5809
|
+
}
|
|
5810
|
+
recordSuccess() {
|
|
5811
|
+
this.state = "closed";
|
|
5812
|
+
this.consecutiveFailures = 0;
|
|
5813
|
+
this.lastFailure = null;
|
|
5814
|
+
this.probeInFlight = false;
|
|
5815
|
+
}
|
|
5816
|
+
recordFailure(err) {
|
|
5817
|
+
if (err instanceof LockError) {
|
|
5818
|
+
this.lastFailure = `[transient/lock] ${err.message}`;
|
|
5819
|
+
this.probeInFlight = false;
|
|
5820
|
+
return;
|
|
5821
|
+
}
|
|
5822
|
+
this.lastFailure = err instanceof Error ? err.message : String(err);
|
|
5823
|
+
this.probeInFlight = false;
|
|
5824
|
+
this.consecutiveFailures++;
|
|
5825
|
+
if (this.state === "half-open" || this.consecutiveFailures >= this.failureThreshold) {
|
|
5826
|
+
this.state = "open";
|
|
5827
|
+
this.openedAt = this.now();
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
/** Force-close the circuit (manual recovery: `/codebase-reindex`). */
|
|
5831
|
+
reset() {
|
|
5832
|
+
this.state = "closed";
|
|
5833
|
+
this.consecutiveFailures = 0;
|
|
5834
|
+
this.lastFailure = null;
|
|
5835
|
+
this.probeInFlight = false;
|
|
5836
|
+
this.openedAt = 0;
|
|
5837
|
+
}
|
|
5838
|
+
snapshot() {
|
|
5839
|
+
return {
|
|
5840
|
+
state: this.state,
|
|
5841
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
5842
|
+
lastFailure: this.lastFailure,
|
|
5843
|
+
cooldownRemainingMs: this.state === "open" ? Math.max(0, this.cooldownMs - (this.now() - this.openedAt)) : 0
|
|
5844
|
+
};
|
|
5845
|
+
}
|
|
5846
|
+
};
|
|
5847
|
+
var indexCircuitBreaker = new IndexCircuitBreaker();
|
|
5848
|
+
function resetIndexCircuitBreaker() {
|
|
5849
|
+
indexCircuitBreaker.reset();
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5757
5852
|
// src/codebase-index/schema.ts
|
|
5758
|
-
var SCHEMA_VERSION =
|
|
5853
|
+
var SCHEMA_VERSION = 2;
|
|
5759
5854
|
|
|
5760
5855
|
// src/codebase-index/lsp-kind.ts
|
|
5761
5856
|
function lspKindToInternalKind(k) {
|
|
@@ -5790,6 +5885,94 @@ function lspKindToInternalKind(k) {
|
|
|
5790
5885
|
}
|
|
5791
5886
|
}
|
|
5792
5887
|
|
|
5888
|
+
// src/codebase-index/bm25.ts
|
|
5889
|
+
var K1 = 1.5;
|
|
5890
|
+
var B = 0.75;
|
|
5891
|
+
function tokenise(text) {
|
|
5892
|
+
const sanitised = text.replace(/[^\p{L}\p{N}$'_]/gu, " ").replace(/_/g, " ");
|
|
5893
|
+
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
5894
|
+
}
|
|
5895
|
+
function splitName(name) {
|
|
5896
|
+
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
5897
|
+
}
|
|
5898
|
+
function buildIndexableText(name, signature, docComment) {
|
|
5899
|
+
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
5900
|
+
}
|
|
5901
|
+
function buildBm25Index(docs) {
|
|
5902
|
+
const documents = docs.map((d) => {
|
|
5903
|
+
const tokens = tokenise(d.text);
|
|
5904
|
+
return { id: d.id, tokens, raw: d.text, len: tokens.length };
|
|
5905
|
+
});
|
|
5906
|
+
const df = {};
|
|
5907
|
+
for (const doc of documents) {
|
|
5908
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5909
|
+
for (const t of doc.tokens) {
|
|
5910
|
+
if (!seen.has(t)) {
|
|
5911
|
+
df[t] = (df[t] ?? 0) + 1;
|
|
5912
|
+
seen.add(t);
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
const N = documents.length;
|
|
5917
|
+
const totalLen = documents.reduce((sum, d) => sum + d.len, 0);
|
|
5918
|
+
const avgLen = N === 0 ? 0 : totalLen / N;
|
|
5919
|
+
return new Bm25Index(documents, df, N, avgLen);
|
|
5920
|
+
}
|
|
5921
|
+
var Bm25Index = class {
|
|
5922
|
+
constructor(documents, df, N, avgLen) {
|
|
5923
|
+
this.documents = documents;
|
|
5924
|
+
this.df = df;
|
|
5925
|
+
this.N = N;
|
|
5926
|
+
this.safeAvgLen = avgLen === 0 ? 1 : avgLen;
|
|
5927
|
+
}
|
|
5928
|
+
documents;
|
|
5929
|
+
df;
|
|
5930
|
+
N;
|
|
5931
|
+
safeAvgLen;
|
|
5932
|
+
score(query2, filter) {
|
|
5933
|
+
const qTokens = tokenise(query2);
|
|
5934
|
+
if (qTokens.length === 0) return [];
|
|
5935
|
+
const results = [];
|
|
5936
|
+
for (const doc of this.documents) {
|
|
5937
|
+
if (filter && !filter(doc.id)) continue;
|
|
5938
|
+
let docScore = 0;
|
|
5939
|
+
for (const qTerm of qTokens) {
|
|
5940
|
+
let tf = 0;
|
|
5941
|
+
for (const t of doc.tokens) {
|
|
5942
|
+
if (t === qTerm) tf++;
|
|
5943
|
+
}
|
|
5944
|
+
if (tf === 0) continue;
|
|
5945
|
+
const dfVal = this.df[qTerm] ?? 0;
|
|
5946
|
+
if (dfVal === 0) continue;
|
|
5947
|
+
const idf = Math.log((this.N - dfVal + 0.5) / (dfVal + 0.5) + 1);
|
|
5948
|
+
const lenRatio = B * (doc.len / this.safeAvgLen);
|
|
5949
|
+
const tfComponent = tf * (K1 + 1) / (tf + K1 * (1 - B + lenRatio));
|
|
5950
|
+
docScore += idf * tfComponent;
|
|
5951
|
+
}
|
|
5952
|
+
if (docScore > 0) results.push({ id: doc.id, score: docScore });
|
|
5953
|
+
}
|
|
5954
|
+
return results;
|
|
5955
|
+
}
|
|
5956
|
+
getDoc(id) {
|
|
5957
|
+
return this.documents.find((d) => d.id === id);
|
|
5958
|
+
}
|
|
5959
|
+
extractSnippet(docId, queryTokens, radius = 40) {
|
|
5960
|
+
const doc = this.getDoc(docId);
|
|
5961
|
+
if (!doc) return "";
|
|
5962
|
+
for (const tok of queryTokens) {
|
|
5963
|
+
const idx = doc.raw.toLowerCase().indexOf(tok);
|
|
5964
|
+
if (idx !== -1) {
|
|
5965
|
+
const start = Math.max(0, idx - radius);
|
|
5966
|
+
const end = Math.min(doc.raw.length, idx + tok.length + radius);
|
|
5967
|
+
const excerpt = doc.raw.slice(start, end);
|
|
5968
|
+
const ellipsis = "\u2026";
|
|
5969
|
+
return (start > 0 ? ellipsis : "") + excerpt + (end < doc.raw.length ? ellipsis : "");
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
return doc.raw.slice(0, radius * 2) + (doc.raw.length > radius * 2 ? "\u2026" : "");
|
|
5973
|
+
}
|
|
5974
|
+
};
|
|
5975
|
+
|
|
5793
5976
|
// src/codebase-index/writer.ts
|
|
5794
5977
|
var DB_FILE = "index.db";
|
|
5795
5978
|
function resolveIndexDir(projectRoot, override) {
|
|
@@ -5825,15 +6008,79 @@ function loadDatabaseSync() {
|
|
|
5825
6008
|
}
|
|
5826
6009
|
return DatabaseSyncCtor;
|
|
5827
6010
|
}
|
|
6011
|
+
var MAX_LOCK_RETRIES = 3;
|
|
6012
|
+
var LOCK_RETRY_BASE_DELAY_MS = 50;
|
|
6013
|
+
var LOCK_RETRY_MAX_DELAY_MS = 500;
|
|
6014
|
+
function isLockError(err) {
|
|
6015
|
+
if (!(err instanceof Error)) return false;
|
|
6016
|
+
const e = err;
|
|
6017
|
+
const code = e.code ?? e.sqliteCode;
|
|
6018
|
+
if (typeof code === "string" && /SQLITE_(BUSY|LOCKED)/.test(code)) return true;
|
|
6019
|
+
if (typeof code === "number" && (code === 5 || code === 6)) return true;
|
|
6020
|
+
if (/SQLITE_(BUSY|LOCKED)/.test(err.message)) return true;
|
|
6021
|
+
return false;
|
|
6022
|
+
}
|
|
6023
|
+
function sleepSync(ms) {
|
|
6024
|
+
try {
|
|
6025
|
+
const sab = new SharedArrayBuffer(4);
|
|
6026
|
+
const view = new Int32Array(sab);
|
|
6027
|
+
Atomics.wait(view, 0, 0, ms);
|
|
6028
|
+
} catch {
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
5828
6031
|
var IndexStore = class {
|
|
5829
6032
|
db;
|
|
5830
6033
|
/** Absolute path to this project's index directory. */
|
|
5831
6034
|
indexDir;
|
|
6035
|
+
/**
|
|
6036
|
+
* True when the SQLite build provides FTS5 (Node's bundled SQLite does).
|
|
6037
|
+
* When false, ranked search falls back to the LIKE + in-process BM25 path.
|
|
6038
|
+
*/
|
|
6039
|
+
ftsAvailable = false;
|
|
6040
|
+
/**
|
|
6041
|
+
* Execute a SQLite write operation with automatic retry on lock conflicts.
|
|
6042
|
+
*
|
|
6043
|
+
* When another wstack process is holding the write lock the statement first
|
|
6044
|
+
* waits up to `busy_timeout` ms, then throws SQLITE_BUSY. This wrapper catches
|
|
6045
|
+
* that error and retries (up to MAX_LOCK_RETRIES) with exponential backoff,
|
|
6046
|
+
* giving the competing writer time to finish and release the lock.
|
|
6047
|
+
*
|
|
6048
|
+
* @param fn The write operation to execute. Can return a value which is
|
|
6049
|
+
* returned to the caller on success.
|
|
6050
|
+
* @throws {@link LockError} when all retries are exhausted on a lock conflict
|
|
6051
|
+
* (non-lock errors always propagate on the first attempt).
|
|
6052
|
+
*/
|
|
6053
|
+
runWithRetry(fn) {
|
|
6054
|
+
let lastError;
|
|
6055
|
+
for (let attempt = 0; attempt <= MAX_LOCK_RETRIES; attempt++) {
|
|
6056
|
+
try {
|
|
6057
|
+
return fn();
|
|
6058
|
+
} catch (err) {
|
|
6059
|
+
lastError = err;
|
|
6060
|
+
if (!isLockError(err)) throw err;
|
|
6061
|
+
if (attempt === MAX_LOCK_RETRIES) {
|
|
6062
|
+
const msg = lastError instanceof Error ? lastError.message : String(lastError);
|
|
6063
|
+
throw new LockError(`SQLite lock conflict after ${MAX_LOCK_RETRIES} retries: ${msg}`);
|
|
6064
|
+
}
|
|
6065
|
+
const delay = Math.min(
|
|
6066
|
+
LOCK_RETRY_BASE_DELAY_MS * Math.pow(2, attempt),
|
|
6067
|
+
LOCK_RETRY_MAX_DELAY_MS
|
|
6068
|
+
);
|
|
6069
|
+
sleepSync(delay);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
throw lastError;
|
|
6073
|
+
}
|
|
5832
6074
|
constructor(projectRoot, opts = {}) {
|
|
5833
6075
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
5834
6076
|
fs7.mkdirSync(this.indexDir, { recursive: true });
|
|
5835
6077
|
const Database = loadDatabaseSync();
|
|
5836
6078
|
this.db = new Database(path.join(this.indexDir, DB_FILE));
|
|
6079
|
+
try {
|
|
6080
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
6081
|
+
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
6082
|
+
} catch {
|
|
6083
|
+
}
|
|
5837
6084
|
this.initSchema();
|
|
5838
6085
|
}
|
|
5839
6086
|
initSchema() {
|
|
@@ -5842,6 +6089,21 @@ var IndexStore = class {
|
|
|
5842
6089
|
key TEXT PRIMARY KEY,
|
|
5843
6090
|
value TEXT NOT NULL
|
|
5844
6091
|
);
|
|
6092
|
+
`);
|
|
6093
|
+
const storedRows = this.db.prepare("SELECT value FROM metadata WHERE key = ?").all("version");
|
|
6094
|
+
const storedVersion = storedRows.length ? Number(storedRows[0]?.value) : null;
|
|
6095
|
+
if (storedVersion !== null && storedVersion !== SCHEMA_VERSION) {
|
|
6096
|
+
this.db.exec(`
|
|
6097
|
+
DROP TABLE IF EXISTS symbols;
|
|
6098
|
+
DROP TABLE IF EXISTS files;
|
|
6099
|
+
DROP TABLE IF EXISTS refs;
|
|
6100
|
+
`);
|
|
6101
|
+
this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
6102
|
+
this.db.prepare("UPDATE metadata SET value = ? WHERE key = ?").run(String(SCHEMA_VERSION), "version");
|
|
6103
|
+
} else if (storedVersion === null) {
|
|
6104
|
+
this.db.prepare("INSERT INTO metadata(key, value) VALUES (?, ?)").run("version", String(SCHEMA_VERSION));
|
|
6105
|
+
}
|
|
6106
|
+
this.db.exec(`
|
|
5845
6107
|
CREATE TABLE IF NOT EXISTS files (
|
|
5846
6108
|
file TEXT PRIMARY KEY,
|
|
5847
6109
|
lang TEXT NOT NULL,
|
|
@@ -5882,53 +6144,76 @@ var IndexStore = class {
|
|
|
5882
6144
|
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)");
|
|
5883
6145
|
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)");
|
|
5884
6146
|
this.db.exec("CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)");
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
this.
|
|
6147
|
+
try {
|
|
6148
|
+
this.db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')");
|
|
6149
|
+
this.ftsAvailable = true;
|
|
6150
|
+
} catch {
|
|
6151
|
+
this.ftsAvailable = false;
|
|
5888
6152
|
}
|
|
5889
6153
|
}
|
|
5890
6154
|
// ─── Symbol CRUD ─────────────────────────────────────────────────────────────
|
|
5891
6155
|
insertSymbols(symbols, nextId) {
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
let id = nextId;
|
|
5897
|
-
for (const s of symbols) {
|
|
5898
|
-
stmt.run(
|
|
5899
|
-
id++,
|
|
5900
|
-
s.lang,
|
|
5901
|
-
s.kind,
|
|
5902
|
-
s.name,
|
|
5903
|
-
s.file,
|
|
5904
|
-
s.line,
|
|
5905
|
-
s.col,
|
|
5906
|
-
s.signature,
|
|
5907
|
-
s.docComment,
|
|
5908
|
-
s.scope,
|
|
5909
|
-
s.text,
|
|
5910
|
-
s.file
|
|
6156
|
+
return this.runWithRetry(() => {
|
|
6157
|
+
const stmt = this.db.prepare(
|
|
6158
|
+
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
6159
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
5911
6160
|
);
|
|
5912
|
-
|
|
5913
|
-
|
|
6161
|
+
const ftsStmt = this.ftsAvailable ? this.db.prepare("INSERT INTO symbols_fts(rowid, text) VALUES (?, ?)") : null;
|
|
6162
|
+
let id = nextId;
|
|
6163
|
+
for (const s of symbols) {
|
|
6164
|
+
stmt.run(
|
|
6165
|
+
id,
|
|
6166
|
+
s.lang,
|
|
6167
|
+
s.kind,
|
|
6168
|
+
s.name,
|
|
6169
|
+
s.file,
|
|
6170
|
+
s.line,
|
|
6171
|
+
s.col,
|
|
6172
|
+
s.signature,
|
|
6173
|
+
s.docComment,
|
|
6174
|
+
s.scope,
|
|
6175
|
+
s.text,
|
|
6176
|
+
s.file
|
|
6177
|
+
);
|
|
6178
|
+
ftsStmt?.run(id, buildIndexableText(s.name, s.signature, s.docComment));
|
|
6179
|
+
id++;
|
|
6180
|
+
}
|
|
6181
|
+
return id;
|
|
6182
|
+
});
|
|
5914
6183
|
}
|
|
5915
6184
|
deleteSymbolsForFile(file) {
|
|
5916
|
-
this.
|
|
6185
|
+
this.runWithRetry(() => {
|
|
6186
|
+
if (this.ftsAvailable) {
|
|
6187
|
+
this.db.prepare("DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)").run(file);
|
|
6188
|
+
}
|
|
6189
|
+
this.db.prepare("DELETE FROM symbols WHERE file_fk = ?").run(file);
|
|
6190
|
+
});
|
|
5917
6191
|
}
|
|
6192
|
+
/**
|
|
6193
|
+
* Remove every trace of a file (refs, symbols, FTS rows, file meta). Used
|
|
6194
|
+
* when a source file disappears between index runs — previously this only
|
|
6195
|
+
* dropped the `files` row, leaving its symbols orphaned but still searchable.
|
|
6196
|
+
*/
|
|
5918
6197
|
deleteFile(file) {
|
|
5919
|
-
this.
|
|
6198
|
+
this.runWithRetry(() => {
|
|
6199
|
+
this.deleteRefsForFile(file);
|
|
6200
|
+
this.deleteSymbolsForFile(file);
|
|
6201
|
+
this.db.prepare("DELETE FROM files WHERE file = ?").run(file);
|
|
6202
|
+
});
|
|
5920
6203
|
}
|
|
5921
6204
|
// ─── File metadata ──────────────────────────────────────────────────────────
|
|
5922
6205
|
upsertFile(meta) {
|
|
5923
|
-
this.
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
6206
|
+
this.runWithRetry(() => {
|
|
6207
|
+
this.db.prepare(
|
|
6208
|
+
`INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)
|
|
6209
|
+
VALUES (?, ?, ?, ?, ?)
|
|
6210
|
+
ON CONFLICT(file) DO UPDATE SET
|
|
6211
|
+
lang = excluded.lang,
|
|
6212
|
+
mtime_ms = excluded.mtime_ms,
|
|
6213
|
+
symbol_count = excluded.symbol_count,
|
|
6214
|
+
last_indexed = excluded.last_indexed`
|
|
6215
|
+
).run(meta.file, meta.lang, meta.mtimeMs, meta.symbolCount, meta.lastIndexed);
|
|
6216
|
+
});
|
|
5932
6217
|
}
|
|
5933
6218
|
getFileMeta(file) {
|
|
5934
6219
|
const rows = this.db.prepare(
|
|
@@ -5995,6 +6280,94 @@ var IndexStore = class {
|
|
|
5995
6280
|
lspKind: filter?.lspKind
|
|
5996
6281
|
}));
|
|
5997
6282
|
}
|
|
6283
|
+
/**
|
|
6284
|
+
* Ranked search — the one-stop query the codebase-search tool and plug-lsp
|
|
6285
|
+
* use. With FTS5 this is a single indexed `MATCH` ranked by SQLite's native
|
|
6286
|
+
* `bm25()` with a built-in `snippet()`; without FTS5 it falls back to the
|
|
6287
|
+
* legacy LIKE scan + in-process BM25 (identical semantics, slower).
|
|
6288
|
+
*
|
|
6289
|
+
* Tokens are matched as prefixes (`"tok"*`), mirroring the old
|
|
6290
|
+
* `LIKE '%tok%'` recall for the common symbol-search shapes ("user" finds
|
|
6291
|
+
* "users", camelCase-split text makes "complex" find "complexOperation").
|
|
6292
|
+
*/
|
|
6293
|
+
searchRanked(query2, filter, limit) {
|
|
6294
|
+
const tokens = tokenise(query2);
|
|
6295
|
+
if (tokens.length === 0 || !this.ftsAvailable) {
|
|
6296
|
+
return this.searchRankedFallback(query2, filter, limit);
|
|
6297
|
+
}
|
|
6298
|
+
let effectiveKind = filter?.kind;
|
|
6299
|
+
if (filter?.lspKind !== void 0) {
|
|
6300
|
+
const mapped = lspKindToInternalKind(filter.lspKind);
|
|
6301
|
+
if (mapped === null) return { results: [], total: 0 };
|
|
6302
|
+
effectiveKind = mapped;
|
|
6303
|
+
}
|
|
6304
|
+
const match = tokens.map((t) => `"${t.replaceAll('"', "")}"*`).join(" OR ");
|
|
6305
|
+
const conditions = ["symbols_fts MATCH ?"];
|
|
6306
|
+
const values = [match];
|
|
6307
|
+
if (effectiveKind) {
|
|
6308
|
+
conditions.push("s.kind = ?");
|
|
6309
|
+
values.push(effectiveKind);
|
|
6310
|
+
}
|
|
6311
|
+
if (filter?.lang) {
|
|
6312
|
+
conditions.push("s.lang = ?");
|
|
6313
|
+
values.push(filter.lang);
|
|
6314
|
+
}
|
|
6315
|
+
if (filter?.file) {
|
|
6316
|
+
conditions.push("s.file LIKE ?");
|
|
6317
|
+
values.push(`%${filter.file}%`);
|
|
6318
|
+
}
|
|
6319
|
+
const where = conditions.join(" AND ");
|
|
6320
|
+
const countRows = this.db.prepare(`SELECT COUNT(*) AS n FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid WHERE ${where}`).all(...values);
|
|
6321
|
+
const total = countRows[0] ? Number(countRows[0].n) : 0;
|
|
6322
|
+
if (total === 0) return { results: [], total: 0 };
|
|
6323
|
+
const rows = this.db.prepare(
|
|
6324
|
+
`SELECT s.id, s.lang, s.kind, s.name, s.file, s.line, s.col, s.signature, s.doc_comment,
|
|
6325
|
+
-bm25(symbols_fts) AS score,
|
|
6326
|
+
snippet(symbols_fts, 0, '', '', '\u2026', 12) AS snippet
|
|
6327
|
+
FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid
|
|
6328
|
+
WHERE ${where}
|
|
6329
|
+
ORDER BY bm25(symbols_fts)
|
|
6330
|
+
LIMIT ?`
|
|
6331
|
+
).all(...values, limit);
|
|
6332
|
+
return {
|
|
6333
|
+
results: rows.map((r) => ({
|
|
6334
|
+
id: r.id,
|
|
6335
|
+
lang: r.lang,
|
|
6336
|
+
kind: r.kind,
|
|
6337
|
+
name: r.name,
|
|
6338
|
+
file: r.file,
|
|
6339
|
+
line: r.line,
|
|
6340
|
+
col: r.col,
|
|
6341
|
+
signature: r.signature,
|
|
6342
|
+
docComment: r.doc_comment,
|
|
6343
|
+
// bm25() is negative-is-better; negate so callers keep "higher is
|
|
6344
|
+
// better" and clamp so a match never reports a zero score.
|
|
6345
|
+
score: Math.max(1e-4, r.score),
|
|
6346
|
+
snippet: r.snippet,
|
|
6347
|
+
lspKind: filter?.lspKind
|
|
6348
|
+
})),
|
|
6349
|
+
total
|
|
6350
|
+
};
|
|
6351
|
+
}
|
|
6352
|
+
/** Legacy ranked path: LIKE candidates + in-process BM25 + JS snippets. */
|
|
6353
|
+
searchRankedFallback(query2, filter, limit) {
|
|
6354
|
+
const candidates = this.search(query2, filter);
|
|
6355
|
+
if (candidates.length === 0) return { results: [], total: 0 };
|
|
6356
|
+
if (!query2.trim()) {
|
|
6357
|
+
return { results: candidates.slice(0, limit), total: candidates.length };
|
|
6358
|
+
}
|
|
6359
|
+
const bm25 = buildBm25Index(
|
|
6360
|
+
candidates.map((c) => ({ id: c.id, text: buildIndexableText(c.name, c.signature, c.docComment) }))
|
|
6361
|
+
);
|
|
6362
|
+
const scored = bm25.score(query2, (id) => candidates.some((c) => c.id === id));
|
|
6363
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6364
|
+
const qTokens = tokenise(query2);
|
|
6365
|
+
const results = scored.slice(0, limit).map(({ id, score }) => {
|
|
6366
|
+
const c = expectDefined(candidates.find((cand) => cand.id === id));
|
|
6367
|
+
return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
|
|
6368
|
+
});
|
|
6369
|
+
return { results, total: candidates.length };
|
|
6370
|
+
}
|
|
5998
6371
|
getAllIndexable() {
|
|
5999
6372
|
return this.db.prepare("SELECT id, text FROM symbols").all().map(
|
|
6000
6373
|
({ id, text }) => ({ id, text })
|
|
@@ -6044,14 +6417,19 @@ var IndexStore = class {
|
|
|
6044
6417
|
};
|
|
6045
6418
|
}
|
|
6046
6419
|
setLastIndexed(ts2) {
|
|
6047
|
-
this.
|
|
6048
|
-
|
|
6049
|
-
|
|
6420
|
+
this.runWithRetry(() => {
|
|
6421
|
+
this.db.prepare(
|
|
6422
|
+
"INSERT OR REPLACE INTO metadata(key, value) VALUES('last_indexed', ?)"
|
|
6423
|
+
).run(String(ts2));
|
|
6424
|
+
});
|
|
6050
6425
|
}
|
|
6051
6426
|
clearAll() {
|
|
6052
|
-
this.
|
|
6053
|
-
|
|
6054
|
-
|
|
6427
|
+
this.runWithRetry(() => {
|
|
6428
|
+
this.db.exec("DELETE FROM symbols");
|
|
6429
|
+
this.db.exec("DELETE FROM files");
|
|
6430
|
+
this.db.exec("DELETE FROM refs");
|
|
6431
|
+
if (this.ftsAvailable) this.db.exec("DELETE FROM symbols_fts");
|
|
6432
|
+
});
|
|
6055
6433
|
}
|
|
6056
6434
|
// ─── Ref CRUD ────────────────────────────────────────────────────────────────
|
|
6057
6435
|
/**
|
|
@@ -6059,46 +6437,52 @@ var IndexStore = class {
|
|
|
6059
6437
|
* Replaces any existing refs from the same source (idempotent on re-index).
|
|
6060
6438
|
*/
|
|
6061
6439
|
insertRefs(fromId, refs) {
|
|
6062
|
-
this.
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6440
|
+
this.runWithRetry(() => {
|
|
6441
|
+
this.db.prepare("DELETE FROM refs WHERE from_id = ?").run(fromId);
|
|
6442
|
+
if (refs.length === 0) return;
|
|
6443
|
+
const stmt = this.db.prepare(
|
|
6444
|
+
`INSERT INTO refs(from_id, to_name, to_id, call_type, line)
|
|
6445
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
6446
|
+
);
|
|
6447
|
+
for (const ref of refs) {
|
|
6448
|
+
stmt.run(fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);
|
|
6449
|
+
}
|
|
6450
|
+
});
|
|
6071
6451
|
}
|
|
6072
6452
|
/**
|
|
6073
6453
|
* Delete all refs whose source symbols are in a given file.
|
|
6074
6454
|
* Used when re-indexing a file to clear stale refs.
|
|
6075
6455
|
*/
|
|
6076
6456
|
deleteRefsForFile(file) {
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6457
|
+
this.runWithRetry(() => {
|
|
6458
|
+
const ids = this.db.prepare(
|
|
6459
|
+
"SELECT id FROM symbols WHERE file = ?"
|
|
6460
|
+
).all(file);
|
|
6461
|
+
if (!ids.length) return;
|
|
6462
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
6463
|
+
this.db.prepare(`DELETE FROM refs WHERE from_id IN (${placeholders})`).run(...ids.map((r) => r.id));
|
|
6464
|
+
});
|
|
6083
6465
|
}
|
|
6084
6466
|
/**
|
|
6085
6467
|
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
6086
6468
|
* Call this after all symbols have been inserted to fill in cross-references.
|
|
6087
6469
|
*/
|
|
6088
6470
|
resolveRefs() {
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6471
|
+
return this.runWithRetry(() => {
|
|
6472
|
+
const unresolved = this.db.prepare(
|
|
6473
|
+
"SELECT id, to_name FROM refs WHERE to_id IS NULL AND to_name IS NOT NULL"
|
|
6474
|
+
).all();
|
|
6475
|
+
let resolved = 0;
|
|
6476
|
+
for (const row of unresolved) {
|
|
6477
|
+
const target = this.db.prepare("SELECT id FROM symbols WHERE name = ? LIMIT 1").all(row.to_name);
|
|
6478
|
+
const first = target[0];
|
|
6479
|
+
if (first) {
|
|
6480
|
+
this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(first.id, row.id);
|
|
6481
|
+
resolved++;
|
|
6482
|
+
}
|
|
6099
6483
|
}
|
|
6100
|
-
|
|
6101
|
-
|
|
6484
|
+
return resolved;
|
|
6485
|
+
});
|
|
6102
6486
|
}
|
|
6103
6487
|
/**
|
|
6104
6488
|
* Find all references TO a given symbol (who calls / uses this symbol?).
|
|
@@ -6859,7 +7243,7 @@ function parseSymbols4(opts) {
|
|
|
6859
7243
|
}
|
|
6860
7244
|
function checkNativeParser() {
|
|
6861
7245
|
try {
|
|
6862
|
-
execFileSync("rustc", ["--version"], { stdio: "pipe" });
|
|
7246
|
+
execFileSync("rustc", ["--version"], { stdio: "pipe", windowsHide: true });
|
|
6863
7247
|
const toolsDir = path.join(process.cwd(), "tools");
|
|
6864
7248
|
try {
|
|
6865
7249
|
execFileSync(
|
|
@@ -6872,7 +7256,7 @@ function checkNativeParser() {
|
|
|
6872
7256
|
"--manifest-path",
|
|
6873
7257
|
path.join(toolsDir, "Cargo.toml")
|
|
6874
7258
|
],
|
|
6875
|
-
{ stdio: "pipe" }
|
|
7259
|
+
{ stdio: "pipe", windowsHide: true }
|
|
6876
7260
|
);
|
|
6877
7261
|
return true;
|
|
6878
7262
|
} catch {
|
|
@@ -6895,7 +7279,8 @@ function tryNativeParse(file, content) {
|
|
|
6895
7279
|
cwd: process.cwd(),
|
|
6896
7280
|
encoding: "utf8",
|
|
6897
7281
|
timeout: 15e3,
|
|
6898
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
7282
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
7283
|
+
windowsHide: true
|
|
6899
7284
|
}
|
|
6900
7285
|
);
|
|
6901
7286
|
if (result.status === 0 && result.stdout) {
|
|
@@ -7309,10 +7694,6 @@ function isScalar(value) {
|
|
|
7309
7694
|
if (/^'[^']*'$/.test(value) || /^"[^"]*"$/.test(value)) return true;
|
|
7310
7695
|
return false;
|
|
7311
7696
|
}
|
|
7312
|
-
function truncate(s, max) {
|
|
7313
|
-
if (s.length <= max) return s;
|
|
7314
|
-
return s.slice(0, max) + "...";
|
|
7315
|
-
}
|
|
7316
7697
|
function makeSymbol2(opts) {
|
|
7317
7698
|
return {
|
|
7318
7699
|
id: 0,
|
|
@@ -7379,140 +7760,20 @@ async function loadGitignoreMatcher(projectRoot) {
|
|
|
7379
7760
|
return compileGitignore(lines);
|
|
7380
7761
|
}
|
|
7381
7762
|
|
|
7382
|
-
// src/codebase-index/
|
|
7383
|
-
var
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
var _totalFiles = 0;
|
|
7387
|
-
var _lastError = null;
|
|
7388
|
-
function isIndexReady() {
|
|
7389
|
-
return _ready;
|
|
7390
|
-
}
|
|
7391
|
-
function setIndexReady() {
|
|
7392
|
-
_ready = true;
|
|
7393
|
-
}
|
|
7394
|
-
function isIndexing() {
|
|
7395
|
-
return _indexing;
|
|
7396
|
-
}
|
|
7397
|
-
function getIndexState() {
|
|
7398
|
-
return {
|
|
7399
|
-
ready: _ready,
|
|
7400
|
-
indexing: _indexing,
|
|
7401
|
-
currentFile: _currentFile,
|
|
7402
|
-
totalFiles: _totalFiles,
|
|
7403
|
-
lastError: _lastError
|
|
7404
|
-
};
|
|
7405
|
-
}
|
|
7406
|
-
var _listeners = [];
|
|
7407
|
-
function onIndexStateChange(listener) {
|
|
7408
|
-
_listeners.push(listener);
|
|
7409
|
-
return () => {
|
|
7410
|
-
_listeners = _listeners.filter((l) => l !== listener);
|
|
7411
|
-
};
|
|
7763
|
+
// src/codebase-index/indexer.ts
|
|
7764
|
+
var YIELD_EVERY_N = 50;
|
|
7765
|
+
function yieldEventLoop() {
|
|
7766
|
+
return new Promise((resolve7) => setImmediate(resolve7));
|
|
7412
7767
|
}
|
|
7413
|
-
function
|
|
7414
|
-
|
|
7415
|
-
|
|
7768
|
+
function throwIfAborted(signal) {
|
|
7769
|
+
if (!signal?.aborted) return;
|
|
7770
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
7771
|
+
throw new Error(
|
|
7772
|
+
typeof signal.reason === "string" ? signal.reason : "Indexing cancelled"
|
|
7773
|
+
);
|
|
7416
7774
|
}
|
|
7417
|
-
function
|
|
7418
|
-
|
|
7419
|
-
_totalFiles = total;
|
|
7420
|
-
emitState();
|
|
7421
|
-
}
|
|
7422
|
-
function stubCtx(projectRoot) {
|
|
7423
|
-
return {
|
|
7424
|
-
projectRoot,
|
|
7425
|
-
cwd: projectRoot,
|
|
7426
|
-
messages: [],
|
|
7427
|
-
todos: [],
|
|
7428
|
-
readFiles: /* @__PURE__ */ new Set(),
|
|
7429
|
-
fileMtimes: /* @__PURE__ */ new Map()
|
|
7430
|
-
};
|
|
7431
|
-
}
|
|
7432
|
-
var chain = Promise.resolve();
|
|
7433
|
-
function withMutex(job) {
|
|
7434
|
-
const run = chain.then(job, job);
|
|
7435
|
-
chain = run.then(
|
|
7436
|
-
() => void 0,
|
|
7437
|
-
() => void 0
|
|
7438
|
-
);
|
|
7439
|
-
return run;
|
|
7440
|
-
}
|
|
7441
|
-
var DEFAULT_DEBOUNCE_MS = 400;
|
|
7442
|
-
var debounceTimers = /* @__PURE__ */ new Map();
|
|
7443
|
-
function debounceKey(indexDir, file) {
|
|
7444
|
-
return `${indexDir ?? ""}|${file}`;
|
|
7445
|
-
}
|
|
7446
|
-
function isIndexableFile(filePath) {
|
|
7447
|
-
return detectLang(filePath) !== null;
|
|
7448
|
-
}
|
|
7449
|
-
async function runStartupIndex(opts) {
|
|
7450
|
-
_indexing = true;
|
|
7451
|
-
emitState();
|
|
7452
|
-
try {
|
|
7453
|
-
const result = await withMutex(() => {
|
|
7454
|
-
_currentFile = 0;
|
|
7455
|
-
_totalFiles = 0;
|
|
7456
|
-
_lastError = null;
|
|
7457
|
-
return runIndexer(stubCtx(opts.projectRoot), {
|
|
7458
|
-
projectRoot: opts.projectRoot,
|
|
7459
|
-
indexDir: opts.indexDir,
|
|
7460
|
-
force: opts.force,
|
|
7461
|
-
signal: opts.signal
|
|
7462
|
-
});
|
|
7463
|
-
});
|
|
7464
|
-
_ready = true;
|
|
7465
|
-
return result;
|
|
7466
|
-
} catch (err) {
|
|
7467
|
-
_lastError = err instanceof Error ? err.message : String(err);
|
|
7468
|
-
_ready = true;
|
|
7469
|
-
throw err;
|
|
7470
|
-
} finally {
|
|
7471
|
-
_indexing = false;
|
|
7472
|
-
emitState();
|
|
7473
|
-
}
|
|
7474
|
-
}
|
|
7475
|
-
function enqueueReindex(opts) {
|
|
7476
|
-
const files = opts.files.filter(isIndexableFile);
|
|
7477
|
-
if (files.length === 0) return;
|
|
7478
|
-
const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
7479
|
-
for (const file of files) {
|
|
7480
|
-
const key = debounceKey(opts.indexDir, file);
|
|
7481
|
-
const existing = debounceTimers.get(key);
|
|
7482
|
-
if (existing) clearTimeout(existing);
|
|
7483
|
-
const timer = setTimeout(() => {
|
|
7484
|
-
debounceTimers.delete(key);
|
|
7485
|
-
void withMutex(
|
|
7486
|
-
() => runIndexer(stubCtx(opts.projectRoot), {
|
|
7487
|
-
projectRoot: opts.projectRoot,
|
|
7488
|
-
files: [file],
|
|
7489
|
-
indexDir: opts.indexDir
|
|
7490
|
-
})
|
|
7491
|
-
).catch((err) => opts.onError?.(err));
|
|
7492
|
-
}, ms);
|
|
7493
|
-
timer.unref?.();
|
|
7494
|
-
debounceTimers.set(key, timer);
|
|
7495
|
-
}
|
|
7496
|
-
}
|
|
7497
|
-
function cancelPendingReindexes() {
|
|
7498
|
-
for (const t of debounceTimers.values()) clearTimeout(t);
|
|
7499
|
-
debounceTimers.clear();
|
|
7500
|
-
}
|
|
7501
|
-
|
|
7502
|
-
// src/codebase-index/indexer.ts
|
|
7503
|
-
var YIELD_EVERY_N = 50;
|
|
7504
|
-
function yieldEventLoop() {
|
|
7505
|
-
return new Promise((resolve7) => setImmediate(resolve7));
|
|
7506
|
-
}
|
|
7507
|
-
function throwIfAborted(signal) {
|
|
7508
|
-
if (!signal?.aborted) return;
|
|
7509
|
-
if (signal.reason instanceof Error) throw signal.reason;
|
|
7510
|
-
throw new Error(
|
|
7511
|
-
typeof signal.reason === "string" ? signal.reason : "Indexing cancelled"
|
|
7512
|
-
);
|
|
7513
|
-
}
|
|
7514
|
-
function isAbortError(err) {
|
|
7515
|
-
return err instanceof DOMException && err.name === "AbortError";
|
|
7775
|
+
function isAbortError(err) {
|
|
7776
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
7516
7777
|
}
|
|
7517
7778
|
var DEFAULT_IGNORE5 = [
|
|
7518
7779
|
"node_modules",
|
|
@@ -7636,7 +7897,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7636
7897
|
}
|
|
7637
7898
|
for (let fi = 0; fi < files.length; fi++) {
|
|
7638
7899
|
const file = expectDefined(files[fi]);
|
|
7639
|
-
|
|
7900
|
+
opts.onProgress?.(fi + 1, files.length);
|
|
7640
7901
|
if (fi > 0 && fi % YIELD_EVERY_N === 0) {
|
|
7641
7902
|
await yieldEventLoop();
|
|
7642
7903
|
throwIfAborted(signal);
|
|
@@ -7731,6 +7992,349 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7731
7992
|
};
|
|
7732
7993
|
}
|
|
7733
7994
|
|
|
7995
|
+
// src/codebase-index/index-service.ts
|
|
7996
|
+
function stubCtx(projectRoot) {
|
|
7997
|
+
return {
|
|
7998
|
+
projectRoot,
|
|
7999
|
+
cwd: projectRoot,
|
|
8000
|
+
messages: [],
|
|
8001
|
+
todos: [],
|
|
8002
|
+
readFiles: /* @__PURE__ */ new Set(),
|
|
8003
|
+
fileMtimes: /* @__PURE__ */ new Map()
|
|
8004
|
+
};
|
|
8005
|
+
}
|
|
8006
|
+
async function indexService(args, hooks = {}) {
|
|
8007
|
+
return runIndexer(stubCtx(args.projectRoot), {
|
|
8008
|
+
projectRoot: args.projectRoot,
|
|
8009
|
+
indexDir: args.indexDir,
|
|
8010
|
+
files: args.files,
|
|
8011
|
+
force: args.force,
|
|
8012
|
+
langs: args.langs,
|
|
8013
|
+
ignore: args.ignore,
|
|
8014
|
+
signal: hooks.signal,
|
|
8015
|
+
onProgress: hooks.onProgress
|
|
8016
|
+
});
|
|
8017
|
+
}
|
|
8018
|
+
function searchService(args) {
|
|
8019
|
+
const store = new IndexStore(args.projectRoot, { indexDir: args.indexDir });
|
|
8020
|
+
try {
|
|
8021
|
+
return store.searchRanked(
|
|
8022
|
+
args.query,
|
|
8023
|
+
{
|
|
8024
|
+
kind: args.kind,
|
|
8025
|
+
lang: args.lang,
|
|
8026
|
+
file: args.file,
|
|
8027
|
+
lspKind: args.lspKind
|
|
8028
|
+
},
|
|
8029
|
+
args.limit
|
|
8030
|
+
);
|
|
8031
|
+
} finally {
|
|
8032
|
+
store.close();
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
function statsService(args) {
|
|
8036
|
+
const store = new IndexStore(args.projectRoot, { indexDir: args.indexDir });
|
|
8037
|
+
try {
|
|
8038
|
+
return store.getStats();
|
|
8039
|
+
} finally {
|
|
8040
|
+
store.close();
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
|
|
8044
|
+
// src/codebase-index/background-indexer.ts
|
|
8045
|
+
var DEFAULT_FULL_INDEX_TIMEOUT_MS = 12e4;
|
|
8046
|
+
var DEFAULT_INCREMENTAL_TIMEOUT_MS = 3e4;
|
|
8047
|
+
var DEFAULT_QUERY_TIMEOUT_MS = 8e3;
|
|
8048
|
+
var _ready = false;
|
|
8049
|
+
var _indexing = false;
|
|
8050
|
+
var _currentFile = 0;
|
|
8051
|
+
var _totalFiles = 0;
|
|
8052
|
+
var _lastError = null;
|
|
8053
|
+
function isIndexReady() {
|
|
8054
|
+
return _ready;
|
|
8055
|
+
}
|
|
8056
|
+
function isIndexing() {
|
|
8057
|
+
return _indexing;
|
|
8058
|
+
}
|
|
8059
|
+
function getIndexState() {
|
|
8060
|
+
return {
|
|
8061
|
+
ready: _ready,
|
|
8062
|
+
indexing: _indexing,
|
|
8063
|
+
currentFile: _currentFile,
|
|
8064
|
+
totalFiles: _totalFiles,
|
|
8065
|
+
lastError: _lastError,
|
|
8066
|
+
circuit: indexCircuitBreaker.snapshot()
|
|
8067
|
+
};
|
|
8068
|
+
}
|
|
8069
|
+
var _listeners = [];
|
|
8070
|
+
function onIndexStateChange(listener) {
|
|
8071
|
+
_listeners.push(listener);
|
|
8072
|
+
return () => {
|
|
8073
|
+
_listeners = _listeners.filter((l) => l !== listener);
|
|
8074
|
+
};
|
|
8075
|
+
}
|
|
8076
|
+
function emitState() {
|
|
8077
|
+
const state = getIndexState();
|
|
8078
|
+
for (const l of _listeners) l(state);
|
|
8079
|
+
}
|
|
8080
|
+
function setIndexProgress(current, total) {
|
|
8081
|
+
_currentFile = current;
|
|
8082
|
+
_totalFiles = total;
|
|
8083
|
+
emitState();
|
|
8084
|
+
}
|
|
8085
|
+
var worker = null;
|
|
8086
|
+
var workerUnavailable = false;
|
|
8087
|
+
var nextRpcId = 1;
|
|
8088
|
+
var pending = /* @__PURE__ */ new Map();
|
|
8089
|
+
function resolveWorkerUrl() {
|
|
8090
|
+
if (process.env["WRONGSTACK_INDEX_INLINE"]) return null;
|
|
8091
|
+
for (const rel of ["./worker.js", "./codebase-index/worker.js"]) {
|
|
8092
|
+
try {
|
|
8093
|
+
const url = new URL(rel, import.meta.url);
|
|
8094
|
+
if (url.protocol === "file:" && fs7.existsSync(fileURLToPath(url))) return url;
|
|
8095
|
+
} catch {
|
|
8096
|
+
}
|
|
8097
|
+
}
|
|
8098
|
+
return null;
|
|
8099
|
+
}
|
|
8100
|
+
function failAllPending(err) {
|
|
8101
|
+
const entries = [...pending.values()];
|
|
8102
|
+
pending.clear();
|
|
8103
|
+
for (const p of entries) p.reject(err);
|
|
8104
|
+
}
|
|
8105
|
+
function ensureWorker() {
|
|
8106
|
+
if (worker) return worker;
|
|
8107
|
+
if (workerUnavailable) return null;
|
|
8108
|
+
const url = resolveWorkerUrl();
|
|
8109
|
+
if (!url) {
|
|
8110
|
+
workerUnavailable = true;
|
|
8111
|
+
return null;
|
|
8112
|
+
}
|
|
8113
|
+
try {
|
|
8114
|
+
const w = new Worker(url, { name: "wstack-codebase-index" });
|
|
8115
|
+
w.unref();
|
|
8116
|
+
w.on("message", (msg) => {
|
|
8117
|
+
if (msg.type === "progress") {
|
|
8118
|
+
pending.get(msg.id)?.onProgress?.(msg.current, msg.total);
|
|
8119
|
+
return;
|
|
8120
|
+
}
|
|
8121
|
+
const entry = pending.get(msg.id);
|
|
8122
|
+
if (!entry) return;
|
|
8123
|
+
pending.delete(msg.id);
|
|
8124
|
+
if (msg.ok) entry.resolve(msg.result);
|
|
8125
|
+
else entry.reject(new Error(msg.error));
|
|
8126
|
+
});
|
|
8127
|
+
w.on("error", (err) => {
|
|
8128
|
+
worker = null;
|
|
8129
|
+
failAllPending(err);
|
|
8130
|
+
});
|
|
8131
|
+
w.on("exit", () => {
|
|
8132
|
+
if (worker === w) worker = null;
|
|
8133
|
+
failAllPending(new Error("codebase-index worker exited"));
|
|
8134
|
+
});
|
|
8135
|
+
worker = w;
|
|
8136
|
+
return w;
|
|
8137
|
+
} catch {
|
|
8138
|
+
workerUnavailable = true;
|
|
8139
|
+
return null;
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8142
|
+
function terminateWorker(reason) {
|
|
8143
|
+
const w = worker;
|
|
8144
|
+
worker = null;
|
|
8145
|
+
failAllPending(reason);
|
|
8146
|
+
if (w) void w.terminate().catch(() => {
|
|
8147
|
+
});
|
|
8148
|
+
}
|
|
8149
|
+
function shutdownCodebaseIndexHost() {
|
|
8150
|
+
cancelPendingReindexes();
|
|
8151
|
+
terminateWorker(new Error("codebase-index host shut down"));
|
|
8152
|
+
workerUnavailable = false;
|
|
8153
|
+
}
|
|
8154
|
+
function callIndexOp(op, args, opts) {
|
|
8155
|
+
const w = ensureWorker();
|
|
8156
|
+
if (!w) return callInline(op, args, opts);
|
|
8157
|
+
return new Promise((resolve7, reject) => {
|
|
8158
|
+
const id = nextRpcId++;
|
|
8159
|
+
const timer = setTimeout(() => {
|
|
8160
|
+
pending.delete(id);
|
|
8161
|
+
const err = new IndexTimeoutError(
|
|
8162
|
+
`Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`
|
|
8163
|
+
);
|
|
8164
|
+
terminateWorker(err);
|
|
8165
|
+
reject(err);
|
|
8166
|
+
}, opts.timeoutMs);
|
|
8167
|
+
timer.unref?.();
|
|
8168
|
+
const onAbort = () => {
|
|
8169
|
+
w.postMessage({ type: "cancel", id });
|
|
8170
|
+
};
|
|
8171
|
+
if (opts.signal?.aborted) onAbort();
|
|
8172
|
+
else opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
8173
|
+
const cleanup = () => {
|
|
8174
|
+
clearTimeout(timer);
|
|
8175
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
8176
|
+
};
|
|
8177
|
+
pending.set(id, {
|
|
8178
|
+
resolve: (v) => {
|
|
8179
|
+
cleanup();
|
|
8180
|
+
resolve7(v);
|
|
8181
|
+
},
|
|
8182
|
+
reject: (e) => {
|
|
8183
|
+
cleanup();
|
|
8184
|
+
reject(e);
|
|
8185
|
+
},
|
|
8186
|
+
onProgress: opts.onProgress
|
|
8187
|
+
});
|
|
8188
|
+
w.postMessage({ type: "request", id, op, args });
|
|
8189
|
+
});
|
|
8190
|
+
}
|
|
8191
|
+
async function callInline(op, args, opts) {
|
|
8192
|
+
const ac = new AbortController();
|
|
8193
|
+
const onOuterAbort = () => ac.abort(opts.signal?.reason ?? new Error("Indexing cancelled"));
|
|
8194
|
+
if (opts.signal?.aborted) onOuterAbort();
|
|
8195
|
+
else opts.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
8196
|
+
let timer;
|
|
8197
|
+
const watchdog = new Promise((_, reject) => {
|
|
8198
|
+
timer = setTimeout(() => {
|
|
8199
|
+
const err = new IndexTimeoutError(
|
|
8200
|
+
`Index ${op} exceeded its ${opts.timeoutMs}ms watchdog timeout`
|
|
8201
|
+
);
|
|
8202
|
+
ac.abort(err);
|
|
8203
|
+
reject(err);
|
|
8204
|
+
}, opts.timeoutMs);
|
|
8205
|
+
timer.unref?.();
|
|
8206
|
+
});
|
|
8207
|
+
const job = async () => {
|
|
8208
|
+
switch (op) {
|
|
8209
|
+
case "index":
|
|
8210
|
+
return await indexService(args, {
|
|
8211
|
+
signal: ac.signal,
|
|
8212
|
+
onProgress: opts.onProgress
|
|
8213
|
+
});
|
|
8214
|
+
case "search":
|
|
8215
|
+
return searchService(args);
|
|
8216
|
+
case "stats":
|
|
8217
|
+
return statsService(args);
|
|
8218
|
+
default:
|
|
8219
|
+
throw new Error(`unknown index op: ${String(op)}`);
|
|
8220
|
+
}
|
|
8221
|
+
};
|
|
8222
|
+
try {
|
|
8223
|
+
return await Promise.race([job(), watchdog]);
|
|
8224
|
+
} finally {
|
|
8225
|
+
if (timer) clearTimeout(timer);
|
|
8226
|
+
opts.signal?.removeEventListener("abort", onOuterAbort);
|
|
8227
|
+
}
|
|
8228
|
+
}
|
|
8229
|
+
var chain = Promise.resolve();
|
|
8230
|
+
function withMutex(job) {
|
|
8231
|
+
const run = chain.then(job, job);
|
|
8232
|
+
chain = run.then(
|
|
8233
|
+
() => void 0,
|
|
8234
|
+
() => void 0
|
|
8235
|
+
);
|
|
8236
|
+
return run;
|
|
8237
|
+
}
|
|
8238
|
+
function circuitOpenError() {
|
|
8239
|
+
const c = indexCircuitBreaker.snapshot();
|
|
8240
|
+
return new CircuitOpenError(
|
|
8241
|
+
"Codebase indexing is temporarily paused after repeated failures" + (c.lastFailure ? ` (last: ${c.lastFailure})` : "") + (c.cooldownRemainingMs > 0 ? `; auto-retry in ${Math.ceil(c.cooldownRemainingMs / 1e3)}s` : "") + ". Use /codebase-reindex to retry now."
|
|
8242
|
+
);
|
|
8243
|
+
}
|
|
8244
|
+
var DEFAULT_DEBOUNCE_MS = 400;
|
|
8245
|
+
var debounceTimers = /* @__PURE__ */ new Map();
|
|
8246
|
+
function debounceKey(indexDir, file) {
|
|
8247
|
+
return `${indexDir ?? ""}|${file}`;
|
|
8248
|
+
}
|
|
8249
|
+
function isIndexableFile(filePath) {
|
|
8250
|
+
return detectLang(filePath) !== null;
|
|
8251
|
+
}
|
|
8252
|
+
async function runStartupIndex(opts) {
|
|
8253
|
+
if (!indexCircuitBreaker.allowRequest()) throw circuitOpenError();
|
|
8254
|
+
_indexing = true;
|
|
8255
|
+
emitState();
|
|
8256
|
+
try {
|
|
8257
|
+
const result = await withMutex(() => {
|
|
8258
|
+
_currentFile = 0;
|
|
8259
|
+
_totalFiles = 0;
|
|
8260
|
+
_lastError = null;
|
|
8261
|
+
return callIndexOp(
|
|
8262
|
+
"index",
|
|
8263
|
+
{
|
|
8264
|
+
projectRoot: opts.projectRoot,
|
|
8265
|
+
indexDir: opts.indexDir,
|
|
8266
|
+
force: opts.force,
|
|
8267
|
+
langs: opts.langs
|
|
8268
|
+
},
|
|
8269
|
+
{
|
|
8270
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_FULL_INDEX_TIMEOUT_MS,
|
|
8271
|
+
signal: opts.signal,
|
|
8272
|
+
onProgress: setIndexProgress
|
|
8273
|
+
}
|
|
8274
|
+
);
|
|
8275
|
+
});
|
|
8276
|
+
_ready = true;
|
|
8277
|
+
indexCircuitBreaker.recordSuccess();
|
|
8278
|
+
return result;
|
|
8279
|
+
} catch (err) {
|
|
8280
|
+
_lastError = err instanceof Error ? err.message : String(err);
|
|
8281
|
+
_ready = true;
|
|
8282
|
+
if (!opts.signal?.aborted) indexCircuitBreaker.recordFailure(err);
|
|
8283
|
+
throw err;
|
|
8284
|
+
} finally {
|
|
8285
|
+
_indexing = false;
|
|
8286
|
+
emitState();
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
function enqueueReindex(opts) {
|
|
8290
|
+
const files = opts.files.filter(isIndexableFile);
|
|
8291
|
+
if (files.length === 0) return;
|
|
8292
|
+
const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
8293
|
+
for (const file of files) {
|
|
8294
|
+
const key = debounceKey(opts.indexDir, file);
|
|
8295
|
+
const existing = debounceTimers.get(key);
|
|
8296
|
+
if (existing) clearTimeout(existing);
|
|
8297
|
+
const timer = setTimeout(() => {
|
|
8298
|
+
debounceTimers.delete(key);
|
|
8299
|
+
if (!indexCircuitBreaker.allowRequest()) {
|
|
8300
|
+
opts.onError?.(circuitOpenError());
|
|
8301
|
+
return;
|
|
8302
|
+
}
|
|
8303
|
+
void withMutex(
|
|
8304
|
+
() => callIndexOp(
|
|
8305
|
+
"index",
|
|
8306
|
+
{ projectRoot: opts.projectRoot, files: [file], indexDir: opts.indexDir },
|
|
8307
|
+
{ timeoutMs: opts.timeoutMs ?? DEFAULT_INCREMENTAL_TIMEOUT_MS }
|
|
8308
|
+
)
|
|
8309
|
+
).then(
|
|
8310
|
+
() => indexCircuitBreaker.recordSuccess(),
|
|
8311
|
+
(err) => {
|
|
8312
|
+
indexCircuitBreaker.recordFailure(err);
|
|
8313
|
+
opts.onError?.(err);
|
|
8314
|
+
}
|
|
8315
|
+
);
|
|
8316
|
+
}, ms);
|
|
8317
|
+
timer.unref?.();
|
|
8318
|
+
debounceTimers.set(key, timer);
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
function cancelPendingReindexes() {
|
|
8322
|
+
for (const t of debounceTimers.values()) clearTimeout(t);
|
|
8323
|
+
debounceTimers.clear();
|
|
8324
|
+
}
|
|
8325
|
+
async function searchCodebaseIndex(args, opts = {}) {
|
|
8326
|
+
return callIndexOp("search", args, {
|
|
8327
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,
|
|
8328
|
+
signal: opts.signal
|
|
8329
|
+
});
|
|
8330
|
+
}
|
|
8331
|
+
async function codebaseIndexStats(args, opts = {}) {
|
|
8332
|
+
return callIndexOp("stats", args, {
|
|
8333
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,
|
|
8334
|
+
signal: opts.signal
|
|
8335
|
+
});
|
|
8336
|
+
}
|
|
8337
|
+
|
|
7734
8338
|
// src/codebase-index/codebase-index-tool.ts
|
|
7735
8339
|
var codebaseIndexTool = {
|
|
7736
8340
|
name: "codebase-index",
|
|
@@ -7766,103 +8370,24 @@ var codebaseIndexTool = {
|
|
|
7766
8370
|
note: "A full index is already in progress. Retry codebase-index after it completes (check codebase-stats)."
|
|
7767
8371
|
};
|
|
7768
8372
|
}
|
|
7769
|
-
const
|
|
8373
|
+
const circuit = indexCircuitBreaker.snapshot();
|
|
8374
|
+
if (circuit.state === "open" && circuit.cooldownRemainingMs > 0) {
|
|
8375
|
+
return {
|
|
8376
|
+
filesIndexed: 0,
|
|
8377
|
+
symbolsIndexed: 0,
|
|
8378
|
+
langStats: {},
|
|
8379
|
+
durationMs: 0,
|
|
8380
|
+
errors: [],
|
|
8381
|
+
note: `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}). Auto-retry possible in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s; the user can run /codebase-reindex to retry immediately.`
|
|
8382
|
+
};
|
|
8383
|
+
}
|
|
8384
|
+
return await runStartupIndex({
|
|
7770
8385
|
projectRoot: ctx.projectRoot,
|
|
7771
8386
|
force: input.force ?? false,
|
|
7772
8387
|
langs: input.langs,
|
|
7773
8388
|
indexDir: codebaseIndexDirOverride(ctx),
|
|
7774
8389
|
signal: execOpts?.signal
|
|
7775
8390
|
});
|
|
7776
|
-
setIndexReady();
|
|
7777
|
-
return result;
|
|
7778
|
-
}
|
|
7779
|
-
};
|
|
7780
|
-
|
|
7781
|
-
// src/codebase-index/bm25.ts
|
|
7782
|
-
var K1 = 1.5;
|
|
7783
|
-
var B = 0.75;
|
|
7784
|
-
function tokenise(text) {
|
|
7785
|
-
const sanitised = text.replace(/[^\p{L}\p{N}$'_]/gu, " ").replace(/_/g, " ");
|
|
7786
|
-
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
7787
|
-
}
|
|
7788
|
-
function splitName(name) {
|
|
7789
|
-
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
7790
|
-
}
|
|
7791
|
-
function buildIndexableText(name, signature, docComment) {
|
|
7792
|
-
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
7793
|
-
}
|
|
7794
|
-
function buildBm25Index(docs) {
|
|
7795
|
-
const documents = docs.map((d) => {
|
|
7796
|
-
const tokens = tokenise(d.text);
|
|
7797
|
-
return { id: d.id, tokens, raw: d.text, len: tokens.length };
|
|
7798
|
-
});
|
|
7799
|
-
const df = {};
|
|
7800
|
-
for (const doc of documents) {
|
|
7801
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7802
|
-
for (const t of doc.tokens) {
|
|
7803
|
-
if (!seen.has(t)) {
|
|
7804
|
-
df[t] = (df[t] ?? 0) + 1;
|
|
7805
|
-
seen.add(t);
|
|
7806
|
-
}
|
|
7807
|
-
}
|
|
7808
|
-
}
|
|
7809
|
-
const N = documents.length;
|
|
7810
|
-
const totalLen = documents.reduce((sum, d) => sum + d.len, 0);
|
|
7811
|
-
const avgLen = N === 0 ? 0 : totalLen / N;
|
|
7812
|
-
return new Bm25Index(documents, df, N, avgLen);
|
|
7813
|
-
}
|
|
7814
|
-
var Bm25Index = class {
|
|
7815
|
-
constructor(documents, df, N, avgLen) {
|
|
7816
|
-
this.documents = documents;
|
|
7817
|
-
this.df = df;
|
|
7818
|
-
this.N = N;
|
|
7819
|
-
this.safeAvgLen = avgLen === 0 ? 1 : avgLen;
|
|
7820
|
-
}
|
|
7821
|
-
documents;
|
|
7822
|
-
df;
|
|
7823
|
-
N;
|
|
7824
|
-
safeAvgLen;
|
|
7825
|
-
score(query2, filter) {
|
|
7826
|
-
const qTokens = tokenise(query2);
|
|
7827
|
-
if (qTokens.length === 0) return [];
|
|
7828
|
-
const results = [];
|
|
7829
|
-
for (const doc of this.documents) {
|
|
7830
|
-
if (filter && !filter(doc.id)) continue;
|
|
7831
|
-
let docScore = 0;
|
|
7832
|
-
for (const qTerm of qTokens) {
|
|
7833
|
-
let tf = 0;
|
|
7834
|
-
for (const t of doc.tokens) {
|
|
7835
|
-
if (t === qTerm) tf++;
|
|
7836
|
-
}
|
|
7837
|
-
if (tf === 0) continue;
|
|
7838
|
-
const dfVal = this.df[qTerm] ?? 0;
|
|
7839
|
-
if (dfVal === 0) continue;
|
|
7840
|
-
const idf = Math.log((this.N - dfVal + 0.5) / (dfVal + 0.5) + 1);
|
|
7841
|
-
const lenRatio = B * (doc.len / this.safeAvgLen);
|
|
7842
|
-
const tfComponent = tf * (K1 + 1) / (tf + K1 * (1 - B + lenRatio));
|
|
7843
|
-
docScore += idf * tfComponent;
|
|
7844
|
-
}
|
|
7845
|
-
if (docScore > 0) results.push({ id: doc.id, score: docScore });
|
|
7846
|
-
}
|
|
7847
|
-
return results;
|
|
7848
|
-
}
|
|
7849
|
-
getDoc(id) {
|
|
7850
|
-
return this.documents.find((d) => d.id === id);
|
|
7851
|
-
}
|
|
7852
|
-
extractSnippet(docId, queryTokens, radius = 40) {
|
|
7853
|
-
const doc = this.getDoc(docId);
|
|
7854
|
-
if (!doc) return "";
|
|
7855
|
-
for (const tok of queryTokens) {
|
|
7856
|
-
const idx = doc.raw.toLowerCase().indexOf(tok);
|
|
7857
|
-
if (idx !== -1) {
|
|
7858
|
-
const start = Math.max(0, idx - radius);
|
|
7859
|
-
const end = Math.min(doc.raw.length, idx + tok.length + radius);
|
|
7860
|
-
const excerpt = doc.raw.slice(start, end);
|
|
7861
|
-
const ellipsis = "\u2026";
|
|
7862
|
-
return (start > 0 ? ellipsis : "") + excerpt + (end < doc.raw.length ? ellipsis : "");
|
|
7863
|
-
}
|
|
7864
|
-
}
|
|
7865
|
-
return doc.raw.slice(0, radius * 2) + (doc.raw.length > radius * 2 ? "\u2026" : "");
|
|
7866
8391
|
}
|
|
7867
8392
|
};
|
|
7868
8393
|
|
|
@@ -7908,7 +8433,7 @@ var codebaseSearchTool = {
|
|
|
7908
8433
|
},
|
|
7909
8434
|
required: ["query"]
|
|
7910
8435
|
},
|
|
7911
|
-
async execute(input, ctx) {
|
|
8436
|
+
async execute(input, ctx, execOpts) {
|
|
7912
8437
|
const state = getIndexState();
|
|
7913
8438
|
if (!state.ready) {
|
|
7914
8439
|
return {
|
|
@@ -7927,51 +8452,30 @@ var codebaseSearchTool = {
|
|
|
7927
8452
|
};
|
|
7928
8453
|
}
|
|
7929
8454
|
if (state.lastError) {
|
|
8455
|
+
const circuit = state.circuit;
|
|
8456
|
+
const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s); the user can run /codebase-reindex to retry now.` : "Try /codebase-reindex.";
|
|
7930
8457
|
return {
|
|
7931
8458
|
results: [],
|
|
7932
8459
|
total: 0,
|
|
7933
8460
|
query: input.query,
|
|
7934
|
-
indexStatus: `Index build failed: ${state.lastError}.
|
|
8461
|
+
indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
|
|
7935
8462
|
};
|
|
7936
8463
|
}
|
|
7937
|
-
const
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
8464
|
+
const limit = Math.min(input.limit ?? 20, 100);
|
|
8465
|
+
const { results, total } = await searchCodebaseIndex(
|
|
8466
|
+
{
|
|
8467
|
+
projectRoot: ctx.projectRoot,
|
|
8468
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
8469
|
+
query: input.query,
|
|
7941
8470
|
kind: input.kind,
|
|
7942
8471
|
lang: input.lang,
|
|
7943
8472
|
file: input.file,
|
|
7944
|
-
lspKind: input.lspKind
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
id: c.id,
|
|
7951
|
-
text: buildIndexableText(c.name, c.signature, c.docComment)
|
|
7952
|
-
}));
|
|
7953
|
-
const bm25 = buildBm25Index(indexable);
|
|
7954
|
-
const scored = bm25.score(input.query, (id) => candidates.some((c) => c.id === id));
|
|
7955
|
-
scored.sort((a, b) => b.score - a.score);
|
|
7956
|
-
const top = scored.slice(0, limit);
|
|
7957
|
-
const qTokens = tokenise(input.query);
|
|
7958
|
-
const results = top.map(({ id, score }) => {
|
|
7959
|
-
const c = expectDefined(candidates.find((c2) => c2.id === id));
|
|
7960
|
-
const snippet = bm25.extractSnippet(id, qTokens);
|
|
7961
|
-
return {
|
|
7962
|
-
...c,
|
|
7963
|
-
score,
|
|
7964
|
-
snippet
|
|
7965
|
-
};
|
|
7966
|
-
});
|
|
7967
|
-
return {
|
|
7968
|
-
results,
|
|
7969
|
-
total: candidates.length,
|
|
7970
|
-
query: input.query
|
|
7971
|
-
};
|
|
7972
|
-
} finally {
|
|
7973
|
-
store.close();
|
|
7974
|
-
}
|
|
8473
|
+
lspKind: input.lspKind,
|
|
8474
|
+
limit
|
|
8475
|
+
},
|
|
8476
|
+
{ signal: execOpts?.signal }
|
|
8477
|
+
);
|
|
8478
|
+
return { results, total, query: input.query };
|
|
7975
8479
|
}
|
|
7976
8480
|
};
|
|
7977
8481
|
|
|
@@ -7990,7 +8494,7 @@ var codebaseStatsTool = {
|
|
|
7990
8494
|
properties: {},
|
|
7991
8495
|
additionalProperties: false
|
|
7992
8496
|
},
|
|
7993
|
-
async execute(_input, ctx) {
|
|
8497
|
+
async execute(_input, ctx, execOpts) {
|
|
7994
8498
|
const idxState = getIndexState();
|
|
7995
8499
|
if (!idxState.ready) {
|
|
7996
8500
|
return {
|
|
@@ -8005,34 +8509,30 @@ var codebaseStatsTool = {
|
|
|
8005
8509
|
indexStatus: idxState.indexing ? `Indexing in progress (${idxState.currentFile}/${idxState.totalFiles} files).` : "Index not yet built."
|
|
8006
8510
|
};
|
|
8007
8511
|
}
|
|
8512
|
+
const stats = await codebaseIndexStats(
|
|
8513
|
+
{ projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
|
|
8514
|
+
{ signal: execOpts?.signal }
|
|
8515
|
+
);
|
|
8008
8516
|
if (idxState.indexing) {
|
|
8009
|
-
const store2 = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
|
|
8010
|
-
try {
|
|
8011
|
-
const stats = store2.getStats();
|
|
8012
|
-
return {
|
|
8013
|
-
...stats,
|
|
8014
|
-
indexStatus: `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). Stats may be incomplete.`
|
|
8015
|
-
};
|
|
8016
|
-
} finally {
|
|
8017
|
-
store2.close();
|
|
8018
|
-
}
|
|
8019
|
-
}
|
|
8020
|
-
const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
|
|
8021
|
-
try {
|
|
8022
|
-
const stats = store.getStats();
|
|
8023
8517
|
return {
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
byLang: stats.byLang,
|
|
8027
|
-
byKind: stats.byKind,
|
|
8028
|
-
lastIndexed: stats.lastIndexed,
|
|
8029
|
-
sizeBytes: stats.sizeBytes,
|
|
8030
|
-
indexPath: stats.indexPath,
|
|
8031
|
-
version: stats.version
|
|
8518
|
+
...stats,
|
|
8519
|
+
indexStatus: `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). Stats may be incomplete.`
|
|
8032
8520
|
};
|
|
8033
|
-
} finally {
|
|
8034
|
-
store.close();
|
|
8035
8521
|
}
|
|
8522
|
+
const circuit = idxState.circuit;
|
|
8523
|
+
return {
|
|
8524
|
+
totalSymbols: stats.totalSymbols,
|
|
8525
|
+
totalFiles: stats.totalFiles,
|
|
8526
|
+
byLang: stats.byLang,
|
|
8527
|
+
byKind: stats.byKind,
|
|
8528
|
+
lastIndexed: stats.lastIndexed,
|
|
8529
|
+
sizeBytes: stats.sizeBytes,
|
|
8530
|
+
indexPath: stats.indexPath,
|
|
8531
|
+
version: stats.version,
|
|
8532
|
+
...circuit.state === "open" ? {
|
|
8533
|
+
indexStatus: `Indexing is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}); auto-retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s, or run /codebase-reindex. Stats reflect the last successful build.`
|
|
8534
|
+
} : {}
|
|
8535
|
+
};
|
|
8036
8536
|
}
|
|
8037
8537
|
};
|
|
8038
8538
|
var setWorkingDirTool = {
|
|
@@ -8430,6 +8930,6 @@ var builtinToolsPack = {
|
|
|
8430
8930
|
tools: builtinTools
|
|
8431
8931
|
};
|
|
8432
8932
|
|
|
8433
|
-
export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, runStartupIndex, scaffoldTool, searchMemoryTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
8933
|
+
export { CircuitBreaker, CircuitOpenError, IndexCircuitBreaker, IndexTimeoutError, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetIndexCircuitBreaker, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
8434
8934
|
//# sourceMappingURL=index.js.map
|
|
8435
8935
|
//# sourceMappingURL=index.js.map
|