@vibgrate/cli 2026.722.2 → 2026.727.1
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/DOCS.md +418 -196
- package/README.md +34 -7
- package/dist/baseline-K7YBFKZI.js +7 -0
- package/dist/{baseline-BD3A7EXD.js.map → baseline-K7YBFKZI.js.map} +1 -1
- package/dist/chunk-3BVSDMHV.js +2496 -0
- package/dist/chunk-3BVSDMHV.js.map +1 -0
- package/dist/{chunk-PY3DNX5H.js → chunk-3Q7QBWFW.js} +13 -2
- package/dist/chunk-3Q7QBWFW.js.map +1 -0
- package/dist/chunk-4CQ3MGNN.js +136 -0
- package/dist/chunk-4CQ3MGNN.js.map +1 -0
- package/dist/chunk-5I4VSBWQ.js +764 -0
- package/dist/chunk-5I4VSBWQ.js.map +1 -0
- package/dist/chunk-5Q54GEHM.js +29 -0
- package/dist/chunk-5Q54GEHM.js.map +1 -0
- package/dist/{chunk-2O5YZVZV.js → chunk-65MEOA4E.js} +21 -18
- package/dist/chunk-65MEOA4E.js.map +1 -0
- package/dist/{chunk-WNIIKCNF.js → chunk-6IOQJGFT.js} +29 -238
- package/dist/chunk-6IOQJGFT.js.map +1 -0
- package/dist/{chunk-CS37OBE3.js → chunk-7JHPQ6WF.js} +791 -783
- package/dist/chunk-7JHPQ6WF.js.map +1 -0
- package/dist/chunk-BPF3YX2U.js +64 -0
- package/dist/chunk-BPF3YX2U.js.map +1 -0
- package/dist/chunk-BWPBB45M.js +88 -0
- package/dist/chunk-BWPBB45M.js.map +1 -0
- package/dist/chunk-C4CKTAZP.js +1695 -0
- package/dist/chunk-C4CKTAZP.js.map +1 -0
- package/dist/chunk-EDAAG7BL.js +612 -0
- package/dist/chunk-EDAAG7BL.js.map +1 -0
- package/dist/chunk-EDBV33B7.js +57 -0
- package/dist/chunk-EDBV33B7.js.map +1 -0
- package/dist/chunk-HABUT7HW.js +233 -0
- package/dist/chunk-HABUT7HW.js.map +1 -0
- package/dist/{chunk-NONYJLOJ.js → chunk-HYZVHJ3Q.js} +3 -3
- package/dist/{chunk-NONYJLOJ.js.map → chunk-HYZVHJ3Q.js.map} +1 -1
- package/dist/chunk-IS7VTH2Q.js +627 -0
- package/dist/chunk-IS7VTH2Q.js.map +1 -0
- package/dist/chunk-IZDH67DX.js +289 -0
- package/dist/chunk-IZDH67DX.js.map +1 -0
- package/dist/chunk-J7LKEB2R.js +51 -0
- package/dist/chunk-J7LKEB2R.js.map +1 -0
- package/dist/chunk-JIBOJTGS.js +313 -0
- package/dist/chunk-JIBOJTGS.js.map +1 -0
- package/dist/chunk-JZYGEZTU.js +82 -0
- package/dist/chunk-JZYGEZTU.js.map +1 -0
- package/dist/chunk-K7REHDQS.js +776 -0
- package/dist/chunk-K7REHDQS.js.map +1 -0
- package/dist/chunk-KHJO4D5N.js +108 -0
- package/dist/chunk-KHJO4D5N.js.map +1 -0
- package/dist/chunk-NDMDSPFX.js +69 -0
- package/dist/chunk-NDMDSPFX.js.map +1 -0
- package/dist/chunk-OOIG3RUM.js +276 -0
- package/dist/chunk-OOIG3RUM.js.map +1 -0
- package/dist/{chunk-6CXTPC74.js → chunk-OXQNQW5J.js} +27 -6
- package/dist/chunk-OXQNQW5J.js.map +1 -0
- package/dist/chunk-PMYWZRRS.js +62 -0
- package/dist/chunk-PMYWZRRS.js.map +1 -0
- package/dist/chunk-PXHNTC6D.js +382 -0
- package/dist/chunk-PXHNTC6D.js.map +1 -0
- package/dist/{chunk-3MXNNBLI.js → chunk-S6CYDZEW.js} +1395 -1975
- package/dist/chunk-S6CYDZEW.js.map +1 -0
- package/dist/chunk-SZLF66FJ.js +843 -0
- package/dist/chunk-SZLF66FJ.js.map +1 -0
- package/dist/chunk-VXGV6NTD.js +476 -0
- package/dist/chunk-VXGV6NTD.js.map +1 -0
- package/dist/{chunk-5BDHQ7DD.js → chunk-WLETRGAC.js} +16 -9
- package/dist/chunk-WLETRGAC.js.map +1 -0
- package/dist/{chunk-I7GY7MVN.js → chunk-YDAT7LD2.js} +51 -11
- package/dist/chunk-YDAT7LD2.js.map +1 -0
- package/dist/cli.js +3412 -513
- package/dist/cli.js.map +1 -1
- package/dist/execution-env-3BN7Z737.js +3 -0
- package/dist/execution-env-3BN7Z737.js.map +1 -0
- package/dist/federation-EPZDUWCK.js +6 -0
- package/dist/federation-EPZDUWCK.js.map +1 -0
- package/dist/git-ref-HPRLUFFW.js +3 -0
- package/dist/git-ref-HPRLUFFW.js.map +1 -0
- package/dist/graph-backend-ZMSYOZMW.js +10 -0
- package/dist/graph-backend-ZMSYOZMW.js.map +1 -0
- package/dist/index.d.ts +432 -126
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/{interactive-A4DISSIW.js → interactive-ICL7D7LO.js} +185 -226
- package/dist/interactive-ICL7D7LO.js.map +1 -0
- package/dist/llm-host-2RAYQSDQ.js +8 -0
- package/dist/llm-host-2RAYQSDQ.js.map +1 -0
- package/dist/load-OHVKDX4R.js +7 -0
- package/dist/load-OHVKDX4R.js.map +1 -0
- package/dist/local-runtime-OJFLX2HI.js +4 -0
- package/dist/local-runtime-OJFLX2HI.js.map +1 -0
- package/dist/model-execution-profile-MQ2QCP24.js +3 -0
- package/dist/model-execution-profile-MQ2QCP24.js.map +1 -0
- package/dist/model-orchestrator-FFTZI3U2.js +8 -0
- package/dist/model-orchestrator-FFTZI3U2.js.map +1 -0
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +1 -1
- package/dist/paths-MCH75O3G.js +5 -0
- package/dist/paths-MCH75O3G.js.map +1 -0
- package/dist/resolve-gguf-4UPIGPEE.js +7 -0
- package/dist/resolve-gguf-4UPIGPEE.js.map +1 -0
- package/dist/runtime-session-IDU5BDUJ.js +17 -0
- package/dist/runtime-session-IDU5BDUJ.js.map +1 -0
- package/dist/session-L2OSDRPI.js +10 -0
- package/dist/{session-POJUDFPH.js.map → session-L2OSDRPI.js.map} +1 -1
- package/dist/{stream-json-Q2JGWPHB.js → stream-json-F74W5FCK.js} +16 -6
- package/dist/stream-json-F74W5FCK.js.map +1 -0
- package/dist/{types-Jl6RJ175.d.ts → types-ZMg1mSuu.d.ts} +3 -1
- package/dist/vgd-EGBQD7BW.js +16 -0
- package/dist/vgd-EGBQD7BW.js.map +1 -0
- package/package.json +9 -6
- package/dist/baseline-BD3A7EXD.js +0 -7
- package/dist/chunk-2O5YZVZV.js.map +0 -1
- package/dist/chunk-3MXNNBLI.js.map +0 -1
- package/dist/chunk-5BDHQ7DD.js.map +0 -1
- package/dist/chunk-6CXTPC74.js.map +0 -1
- package/dist/chunk-CS37OBE3.js.map +0 -1
- package/dist/chunk-GGJZA3Q6.js +0 -961
- package/dist/chunk-GGJZA3Q6.js.map +0 -1
- package/dist/chunk-I7GY7MVN.js.map +0 -1
- package/dist/chunk-JBXNQCGE.js +0 -484
- package/dist/chunk-JBXNQCGE.js.map +0 -1
- package/dist/chunk-PY3DNX5H.js.map +0 -1
- package/dist/chunk-WNIIKCNF.js.map +0 -1
- package/dist/interactive-A4DISSIW.js.map +0 -1
- package/dist/session-POJUDFPH.js +0 -6
- package/dist/stream-json-Q2JGWPHB.js.map +0 -1
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady, cacheDir, loadCache,
|
|
1
|
+
import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-WLETRGAC.js';
|
|
2
|
+
import { resolveVersion, resolveLib, loadCatalog, driftFor, localPackageDocs, readDoc, inventory, localApiSurface, edgeId, buildModuleResolver, resolve, applyStaticTestLinkage, nodeId } from './chunk-C4CKTAZP.js';
|
|
3
|
+
import { resolveOne, findNodes } from './chunk-J7LKEB2R.js';
|
|
4
|
+
import { discoverModels } from './chunk-OOIG3RUM.js';
|
|
5
|
+
import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady, cacheDir, loadCache, acquireLock, releaseLock } from './chunk-6IOQJGFT.js';
|
|
6
|
+
import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-3Q7QBWFW.js';
|
|
7
|
+
import { redactSecrets } from './chunk-RXP66R2E.js';
|
|
8
|
+
import { loadGraph, ensureVibgrateGitignore, vibgrateDir, writeArtifacts } from './chunk-HABUT7HW.js';
|
|
9
|
+
import { serializeGraph, stableStringify } from './chunk-5Q54GEHM.js';
|
|
6
10
|
import { shortId, canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
|
|
7
|
-
import
|
|
11
|
+
import { writeTextFile, pathExists, readJsonFile } from './chunk-CRAMOEBE.js';
|
|
12
|
+
import * as fs15 from 'fs';
|
|
8
13
|
import { spawnSync, execFileSync } from 'child_process';
|
|
9
|
-
import * as
|
|
14
|
+
import * as path17 from 'path';
|
|
10
15
|
import ignore from 'ignore';
|
|
11
16
|
import * as v8 from 'v8';
|
|
12
17
|
import Graph from 'graphology';
|
|
@@ -24,8 +29,8 @@ import { Command } from 'commander';
|
|
|
24
29
|
import chalk from 'chalk';
|
|
25
30
|
import * as zlib from 'zlib';
|
|
26
31
|
import { promisify } from 'util';
|
|
27
|
-
import { encode, decode } from 'gpt-tokenizer/encoding/cl100k_base';
|
|
28
32
|
import { Worker } from 'worker_threads';
|
|
33
|
+
import { encode, decode } from 'gpt-tokenizer/encoding/cl100k_base';
|
|
29
34
|
|
|
30
35
|
var NPX_INVOCATION = "npx @vibgrate/cli";
|
|
31
36
|
function whichOnPath(cmd) {
|
|
@@ -41,9 +46,9 @@ function whichOnPath(cmd) {
|
|
|
41
46
|
}
|
|
42
47
|
function isOwnBinary(binPath) {
|
|
43
48
|
try {
|
|
44
|
-
const real =
|
|
49
|
+
const real = fs15.realpathSync(binPath);
|
|
45
50
|
if (/[\\/]@vibgrate[\\/]cli[\\/]/.test(real)) return true;
|
|
46
|
-
const head =
|
|
51
|
+
const head = fs15.readFileSync(real, { encoding: "utf8" }).slice(0, 2048);
|
|
47
52
|
return head.includes("@vibgrate/cli") || head.includes("vibgrate");
|
|
48
53
|
} catch {
|
|
49
54
|
return false;
|
|
@@ -53,7 +58,7 @@ function isEphemeralNpxBinary(binPath) {
|
|
|
53
58
|
const NPX_SEGMENT = /[\\/]_npx[\\/]/;
|
|
54
59
|
if (NPX_SEGMENT.test(binPath)) return true;
|
|
55
60
|
try {
|
|
56
|
-
return NPX_SEGMENT.test(
|
|
61
|
+
return NPX_SEGMENT.test(fs15.realpathSync(binPath));
|
|
57
62
|
} catch {
|
|
58
63
|
return false;
|
|
59
64
|
}
|
|
@@ -88,6 +93,14 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
88
93
|
".wrangler",
|
|
89
94
|
".turbo",
|
|
90
95
|
".cache",
|
|
96
|
+
// Migration / tool backup trees (NativeScript, codemods, agent worktrees)
|
|
97
|
+
".migration_backup",
|
|
98
|
+
".update_backup",
|
|
99
|
+
".backup",
|
|
100
|
+
".bak",
|
|
101
|
+
".history",
|
|
102
|
+
".snapshots",
|
|
103
|
+
".snapshots_bak",
|
|
91
104
|
// JavaScript / Node — installed dependency trees
|
|
92
105
|
"node_modules",
|
|
93
106
|
"bower_components",
|
|
@@ -112,6 +125,9 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
112
125
|
"build",
|
|
113
126
|
"out",
|
|
114
127
|
"coverage",
|
|
128
|
+
// Native mobile platform build trees (NativeScript / Cordova / Capacitor)
|
|
129
|
+
"platforms",
|
|
130
|
+
"App_Resources",
|
|
115
131
|
// Vendored third-party source trees
|
|
116
132
|
"vendor",
|
|
117
133
|
"Pods",
|
|
@@ -219,19 +235,19 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
|
219
235
|
"flake.lock"
|
|
220
236
|
]);
|
|
221
237
|
function toPosix(p) {
|
|
222
|
-
return p.split(
|
|
238
|
+
return p.split(path17.sep).join("/");
|
|
223
239
|
}
|
|
224
240
|
function buildRootIgnore(root, exclude) {
|
|
225
241
|
const ig = ignore();
|
|
226
|
-
const gitignorePath =
|
|
227
|
-
if (
|
|
228
|
-
ig.add(
|
|
242
|
+
const gitignorePath = path17.join(root, ".gitignore");
|
|
243
|
+
if (fs15.existsSync(gitignorePath)) {
|
|
244
|
+
ig.add(fs15.readFileSync(gitignorePath, "utf8"));
|
|
229
245
|
}
|
|
230
246
|
if (exclude.length) ig.add(exclude);
|
|
231
247
|
return ig;
|
|
232
248
|
}
|
|
233
249
|
function discover(options) {
|
|
234
|
-
const root =
|
|
250
|
+
const root = path17.resolve(options.root);
|
|
235
251
|
const onlyLangs = (options.only ?? []).filter(Boolean);
|
|
236
252
|
const allowLang = (lang) => onlyLangs.length === 0 || onlyLangs.includes(lang.id);
|
|
237
253
|
for (const id of onlyLangs) {
|
|
@@ -240,39 +256,39 @@ function discover(options) {
|
|
|
240
256
|
}
|
|
241
257
|
}
|
|
242
258
|
const rootIg = buildRootIgnore(root, options.exclude ?? []);
|
|
243
|
-
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) =>
|
|
259
|
+
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path17.resolve(root, p)) : [root]).filter((p) => fs15.existsSync(p));
|
|
244
260
|
const found = /* @__PURE__ */ new Map();
|
|
245
261
|
const considerFile = (abs) => {
|
|
246
|
-
const rel2 = toPosix(
|
|
262
|
+
const rel2 = toPosix(path17.relative(root, abs));
|
|
247
263
|
if (rel2.startsWith("..")) return;
|
|
248
264
|
if (rel2 === "" || rootIg.ignores(rel2)) return;
|
|
249
|
-
if (SKIP_FILES.has(
|
|
250
|
-
const lang = langForExtension(
|
|
265
|
+
if (SKIP_FILES.has(path17.basename(abs).toLowerCase())) return;
|
|
266
|
+
const lang = langForExtension(path17.extname(abs));
|
|
251
267
|
if (!lang || !allowLang(lang)) return;
|
|
252
268
|
found.set(rel2, { rel: rel2, abs, lang });
|
|
253
269
|
};
|
|
254
|
-
const
|
|
270
|
+
const walk = (dir) => {
|
|
255
271
|
let entries;
|
|
256
272
|
try {
|
|
257
|
-
entries =
|
|
273
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
258
274
|
} catch {
|
|
259
275
|
return;
|
|
260
276
|
}
|
|
261
277
|
for (const entry of entries) {
|
|
262
|
-
const abs =
|
|
263
|
-
const rel2 = toPosix(
|
|
278
|
+
const abs = path17.join(dir, entry.name);
|
|
279
|
+
const rel2 = toPosix(path17.relative(root, abs));
|
|
264
280
|
if (entry.isDirectory()) {
|
|
265
281
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
266
282
|
if (rel2 && rootIg.ignores(`${rel2}/`)) continue;
|
|
267
|
-
|
|
283
|
+
walk(abs);
|
|
268
284
|
} else if (entry.isFile()) {
|
|
269
285
|
considerFile(abs);
|
|
270
286
|
}
|
|
271
287
|
}
|
|
272
288
|
};
|
|
273
289
|
for (const scope of scopeAbs) {
|
|
274
|
-
const stat =
|
|
275
|
-
if (stat.isDirectory())
|
|
290
|
+
const stat = fs15.statSync(scope);
|
|
291
|
+
if (stat.isDirectory()) walk(scope);
|
|
276
292
|
else if (stat.isFile()) considerFile(scope);
|
|
277
293
|
}
|
|
278
294
|
return [...found.values()].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
@@ -336,458 +352,6 @@ function formatBytes(n) {
|
|
|
336
352
|
if (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
337
353
|
return `${n} B`;
|
|
338
354
|
}
|
|
339
|
-
var JS_TS_EXT = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte", ".astro"];
|
|
340
|
-
var OTHER_EXT = [
|
|
341
|
-
".py",
|
|
342
|
-
".pyi",
|
|
343
|
-
".go",
|
|
344
|
-
".rs",
|
|
345
|
-
".rb",
|
|
346
|
-
".java",
|
|
347
|
-
".cs",
|
|
348
|
-
".php",
|
|
349
|
-
".kt",
|
|
350
|
-
".kts",
|
|
351
|
-
".swift",
|
|
352
|
-
".scala",
|
|
353
|
-
".sc",
|
|
354
|
-
".dart",
|
|
355
|
-
".lua",
|
|
356
|
-
".ex",
|
|
357
|
-
".exs",
|
|
358
|
-
".sh",
|
|
359
|
-
".bash",
|
|
360
|
-
".zig",
|
|
361
|
-
".c",
|
|
362
|
-
".cpp",
|
|
363
|
-
".cc",
|
|
364
|
-
".cxx",
|
|
365
|
-
".hpp",
|
|
366
|
-
".hh",
|
|
367
|
-
".hxx",
|
|
368
|
-
".h",
|
|
369
|
-
".m",
|
|
370
|
-
".mm",
|
|
371
|
-
".ml",
|
|
372
|
-
".mli",
|
|
373
|
-
".res",
|
|
374
|
-
".sol"
|
|
375
|
-
];
|
|
376
|
-
var EXT_GROUPS = {
|
|
377
|
-
".py": [".py", ".pyi"],
|
|
378
|
-
".pyi": [".py", ".pyi"],
|
|
379
|
-
".java": [".java"],
|
|
380
|
-
".cs": [".cs"],
|
|
381
|
-
".go": [".go"],
|
|
382
|
-
".rs": [".rs"],
|
|
383
|
-
".rb": [".rb"]
|
|
384
|
-
};
|
|
385
|
-
for (const e of JS_TS_EXT) EXT_GROUPS[e] = JS_TS_EXT;
|
|
386
|
-
var CPP_EXT = [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx", ".h"];
|
|
387
|
-
Object.assign(EXT_GROUPS, {
|
|
388
|
-
".php": [".php"],
|
|
389
|
-
".kt": [".kt", ".kts"],
|
|
390
|
-
".kts": [".kt", ".kts"],
|
|
391
|
-
".swift": [".swift"],
|
|
392
|
-
".scala": [".scala", ".sc"],
|
|
393
|
-
".sc": [".scala", ".sc"],
|
|
394
|
-
".dart": [".dart"],
|
|
395
|
-
".lua": [".lua"],
|
|
396
|
-
".ex": [".ex", ".exs"],
|
|
397
|
-
".exs": [".ex", ".exs"],
|
|
398
|
-
".sh": [".sh", ".bash"],
|
|
399
|
-
".bash": [".sh", ".bash"],
|
|
400
|
-
".zig": [".zig"],
|
|
401
|
-
".c": [".c", ".h"],
|
|
402
|
-
".cpp": CPP_EXT,
|
|
403
|
-
".cc": CPP_EXT,
|
|
404
|
-
".cxx": CPP_EXT,
|
|
405
|
-
".hpp": CPP_EXT,
|
|
406
|
-
".hh": CPP_EXT,
|
|
407
|
-
".hxx": CPP_EXT,
|
|
408
|
-
".h": [...CPP_EXT, ".c"],
|
|
409
|
-
// ObjC #imports headers; ObjC++ additionally reaches C++ headers.
|
|
410
|
-
".m": [".h", ".m", ".mm"],
|
|
411
|
-
".mm": [".h", ".m", ".mm", ...CPP_EXT],
|
|
412
|
-
".ml": [".ml", ".mli"],
|
|
413
|
-
".mli": [".ml", ".mli"],
|
|
414
|
-
".res": [".res"],
|
|
415
|
-
".sol": [".sol"],
|
|
416
|
-
// Template containers: ERB fragments are Ruby; HTML/EJS script is JS.
|
|
417
|
-
".erb": [".rb", ".erb"],
|
|
418
|
-
".html": JS_TS_EXT,
|
|
419
|
-
".htm": JS_TS_EXT,
|
|
420
|
-
".ejs": JS_TS_EXT
|
|
421
|
-
});
|
|
422
|
-
function buildModuleResolver(root, relSet) {
|
|
423
|
-
const probe = makeProbe(relSet);
|
|
424
|
-
const suffixIndex = buildSuffixIndex(relSet);
|
|
425
|
-
const tsPaths = loadTsconfigPaths(root);
|
|
426
|
-
const workspaces = loadWorkspacePackages(root);
|
|
427
|
-
function resolveAlias(source) {
|
|
428
|
-
if (!tsPaths) return null;
|
|
429
|
-
for (const [pattern, targets] of tsPaths.paths) {
|
|
430
|
-
const m = matchPattern(pattern, source);
|
|
431
|
-
if (m === null) continue;
|
|
432
|
-
for (const target of targets) {
|
|
433
|
-
const sub = target.replace("*", m);
|
|
434
|
-
const rel2 = posixJoin(tsPaths.baseUrlRel, sub);
|
|
435
|
-
const hit = probe(rel2);
|
|
436
|
-
if (hit) return hit;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
function resolveWorkspace(source) {
|
|
442
|
-
if (!workspaces.size) return null;
|
|
443
|
-
let best = null;
|
|
444
|
-
for (const [name, dirRel] of workspaces) {
|
|
445
|
-
if (source === name || source.startsWith(`${name}/`)) {
|
|
446
|
-
const rest = source === name ? "" : source.slice(name.length + 1);
|
|
447
|
-
if (!best || name.length > best.dirRel.length) best = { dirRel, rest };
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
if (!best) return null;
|
|
451
|
-
const base = best.rest ? posixJoin(best.dirRel, best.rest) : best.dirRel;
|
|
452
|
-
return probe(base) ?? probe(posixJoin(base, "src/index")) ?? probe(posixJoin(base, "index"));
|
|
453
|
-
}
|
|
454
|
-
return {
|
|
455
|
-
resolve(fromRel, source) {
|
|
456
|
-
if (source.includes("\\")) source = source.split("\\").join(".");
|
|
457
|
-
if (source.startsWith("./") || source.startsWith("../")) {
|
|
458
|
-
return probe(posixJoin(path2.posix.dirname(fromRel), source));
|
|
459
|
-
}
|
|
460
|
-
if (/\.[A-Za-z0-9]+$/.test(source) && !source.startsWith("<")) {
|
|
461
|
-
const hit = probe(posixJoin(path2.posix.dirname(fromRel), source));
|
|
462
|
-
if (hit) return hit;
|
|
463
|
-
}
|
|
464
|
-
if (source.startsWith(".")) {
|
|
465
|
-
return resolvePyRelative(probe, fromRel, source);
|
|
466
|
-
}
|
|
467
|
-
return resolveAlias(source) ?? resolveWorkspace(source) ?? resolveSuffix(suffixIndex, fromRel, source) ?? resolveDotted(probe, source);
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function buildSuffixIndex(relSet) {
|
|
472
|
-
const byLast = /* @__PURE__ */ new Map();
|
|
473
|
-
for (const rel2 of relSet) {
|
|
474
|
-
const dot = rel2.lastIndexOf(".");
|
|
475
|
-
if (dot < 0) continue;
|
|
476
|
-
const ext = rel2.slice(dot);
|
|
477
|
-
if (!EXT_GROUPS[ext]) continue;
|
|
478
|
-
let key = rel2.slice(0, dot);
|
|
479
|
-
const base = key.slice(key.lastIndexOf("/") + 1);
|
|
480
|
-
if (base === "__init__" || base === "index") {
|
|
481
|
-
key = key.slice(0, Math.max(0, key.length - base.length - 1));
|
|
482
|
-
}
|
|
483
|
-
if (!key) continue;
|
|
484
|
-
const last = key.slice(key.lastIndexOf("/") + 1);
|
|
485
|
-
const list = byLast.get(last);
|
|
486
|
-
if (list) list.push({ rel: rel2, key, ext });
|
|
487
|
-
else byLast.set(last, [{ rel: rel2, key, ext }]);
|
|
488
|
-
}
|
|
489
|
-
return byLast;
|
|
490
|
-
}
|
|
491
|
-
function resolveSuffix(index, fromRel, source) {
|
|
492
|
-
if (!/^[A-Za-z_][\w.]*$/.test(source) || !source.includes(".")) return null;
|
|
493
|
-
const fromDot = fromRel.lastIndexOf(".");
|
|
494
|
-
const fromExt = fromDot >= 0 ? fromRel.slice(fromDot) : "";
|
|
495
|
-
const allowed = EXT_GROUPS[fromExt];
|
|
496
|
-
if (!allowed) return null;
|
|
497
|
-
const q = source.replace(/\./g, "/");
|
|
498
|
-
const last = q.slice(q.lastIndexOf("/") + 1);
|
|
499
|
-
const cands = index.get(last);
|
|
500
|
-
if (!cands) return null;
|
|
501
|
-
const matches = cands.filter(
|
|
502
|
-
(c) => allowed.includes(c.ext) && (c.key === q || c.key.endsWith("/" + q))
|
|
503
|
-
);
|
|
504
|
-
if (matches.length === 0) return null;
|
|
505
|
-
if (matches.length === 1) return matches[0].rel;
|
|
506
|
-
return pickClosest(matches, fromRel).rel;
|
|
507
|
-
}
|
|
508
|
-
function pickClosest(matches, fromRel) {
|
|
509
|
-
const fromSegs = fromRel.split("/");
|
|
510
|
-
const score = (rel2) => {
|
|
511
|
-
const segs = rel2.split("/");
|
|
512
|
-
let i = 0;
|
|
513
|
-
while (i < segs.length && i < fromSegs.length && segs[i] === fromSegs[i]) i++;
|
|
514
|
-
return i;
|
|
515
|
-
};
|
|
516
|
-
return [...matches].sort((a, b) => {
|
|
517
|
-
const d = score(b.rel) - score(a.rel);
|
|
518
|
-
if (d) return d;
|
|
519
|
-
if (a.rel.length !== b.rel.length) return a.rel.length - b.rel.length;
|
|
520
|
-
return a.rel < b.rel ? -1 : 1;
|
|
521
|
-
})[0];
|
|
522
|
-
}
|
|
523
|
-
function resolveDotted(probe, source) {
|
|
524
|
-
if (!/^[A-Za-z_][\w.]*$/.test(source) || !source.includes(".")) return null;
|
|
525
|
-
const slashed = source.replace(/\./g, "/");
|
|
526
|
-
return probe(slashed) ?? probe(posixJoin("src", slashed));
|
|
527
|
-
}
|
|
528
|
-
function resolvePyRelative(probe, fromRel, source) {
|
|
529
|
-
let dots = 0;
|
|
530
|
-
while (source[dots] === ".") dots++;
|
|
531
|
-
const rest = source.slice(dots);
|
|
532
|
-
let dir = path2.posix.dirname(fromRel);
|
|
533
|
-
for (let i = 1; i < dots; i++) dir = path2.posix.dirname(dir);
|
|
534
|
-
const sub = rest.replace(/\./g, "/");
|
|
535
|
-
return probe(sub ? posixJoin(dir, sub) : dir);
|
|
536
|
-
}
|
|
537
|
-
function relativeResolver(relSet) {
|
|
538
|
-
const probe = makeProbe(relSet);
|
|
539
|
-
return {
|
|
540
|
-
resolve(fromRel, source) {
|
|
541
|
-
if (source.startsWith(".")) return probe(posixJoin(path2.posix.dirname(fromRel), source));
|
|
542
|
-
return resolveDotted(probe, source);
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
function makeProbe(relSet) {
|
|
547
|
-
return (base) => {
|
|
548
|
-
const norm = normalizeRel(base);
|
|
549
|
-
if (!norm) return null;
|
|
550
|
-
if (relSet.has(norm)) return norm;
|
|
551
|
-
for (const ext of JS_TS_EXT) if (relSet.has(norm + ext)) return norm + ext;
|
|
552
|
-
for (const ext of JS_TS_EXT) if (relSet.has(`${norm}/index${ext}`)) return `${norm}/index${ext}`;
|
|
553
|
-
for (const ext of OTHER_EXT) if (relSet.has(norm + ext)) return norm + ext;
|
|
554
|
-
if (relSet.has(`${norm}/__init__.py`)) return `${norm}/__init__.py`;
|
|
555
|
-
return null;
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
function normalizeRel(p) {
|
|
559
|
-
const norm = path2.posix.normalize(p);
|
|
560
|
-
const stripped = norm.startsWith("./") ? norm.slice(2) : norm;
|
|
561
|
-
return stripped.startsWith("..") ? "" : stripped;
|
|
562
|
-
}
|
|
563
|
-
function posixJoin(a, b) {
|
|
564
|
-
return path2.posix.normalize(path2.posix.join(a, b));
|
|
565
|
-
}
|
|
566
|
-
function matchPattern(pattern, source) {
|
|
567
|
-
const star = pattern.indexOf("*");
|
|
568
|
-
if (star === -1) return source === pattern ? "" : null;
|
|
569
|
-
const prefix = pattern.slice(0, star);
|
|
570
|
-
const suffix = pattern.slice(star + 1);
|
|
571
|
-
if (!source.startsWith(prefix) || !source.endsWith(suffix)) return null;
|
|
572
|
-
if (source.length < prefix.length + suffix.length) return null;
|
|
573
|
-
return source.slice(prefix.length, source.length - suffix.length);
|
|
574
|
-
}
|
|
575
|
-
function loadTsconfigPaths(root) {
|
|
576
|
-
for (const name of ["tsconfig.base.json", "tsconfig.json"]) {
|
|
577
|
-
const file = path2.join(root, name);
|
|
578
|
-
if (fs18.existsSync(file)) {
|
|
579
|
-
const merged = readTsconfigChain(root, file, /* @__PURE__ */ new Set());
|
|
580
|
-
if (merged && merged.paths.length) return merged;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
function readTsconfigChain(root, file, seen) {
|
|
586
|
-
if (seen.has(file) || seen.size > 6) return null;
|
|
587
|
-
seen.add(file);
|
|
588
|
-
let cfg;
|
|
589
|
-
try {
|
|
590
|
-
cfg = parseJsonc(fs18.readFileSync(file, "utf8"));
|
|
591
|
-
} catch {
|
|
592
|
-
return null;
|
|
593
|
-
}
|
|
594
|
-
const dir = path2.dirname(file);
|
|
595
|
-
let base = { baseUrlRel: "", paths: [] };
|
|
596
|
-
if (cfg.extends && cfg.extends.startsWith(".")) {
|
|
597
|
-
const extPath = path2.resolve(dir, cfg.extends.endsWith(".json") ? cfg.extends : `${cfg.extends}.json`);
|
|
598
|
-
if (fs18.existsSync(extPath)) {
|
|
599
|
-
const inherited = readTsconfigChain(root, extPath, seen);
|
|
600
|
-
if (inherited) base = inherited;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
const co = cfg.compilerOptions ?? {};
|
|
604
|
-
const baseUrlRel = co.baseUrl !== void 0 ? path2.relative(root, path2.resolve(dir, co.baseUrl)).split(path2.sep).join("/") : base.baseUrlRel;
|
|
605
|
-
const paths = [...base.paths];
|
|
606
|
-
if (co.paths) for (const [k, v] of Object.entries(co.paths)) paths.push([k, v]);
|
|
607
|
-
return { baseUrlRel, paths };
|
|
608
|
-
}
|
|
609
|
-
function loadWorkspacePackages(root) {
|
|
610
|
-
const map = /* @__PURE__ */ new Map();
|
|
611
|
-
const globs = [];
|
|
612
|
-
const rootPkg = path2.join(root, "package.json");
|
|
613
|
-
if (fs18.existsSync(rootPkg)) {
|
|
614
|
-
try {
|
|
615
|
-
const pkg = JSON.parse(fs18.readFileSync(rootPkg, "utf8"));
|
|
616
|
-
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
617
|
-
if (ws) globs.push(...ws);
|
|
618
|
-
} catch {
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
const pnpmWs = path2.join(root, "pnpm-workspace.yaml");
|
|
622
|
-
if (fs18.existsSync(pnpmWs)) {
|
|
623
|
-
for (const line of fs18.readFileSync(pnpmWs, "utf8").split("\n")) {
|
|
624
|
-
const m = /^\s*-\s*['"]?([^'"\n]+)['"]?\s*$/.exec(line);
|
|
625
|
-
if (m) globs.push(m[1].trim());
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
for (const dir of expandWorkspaceGlobs(root, globs)) {
|
|
629
|
-
const pj = path2.join(dir, "package.json");
|
|
630
|
-
try {
|
|
631
|
-
const name = JSON.parse(fs18.readFileSync(pj, "utf8")).name;
|
|
632
|
-
if (name) map.set(name, path2.relative(root, dir).split(path2.sep).join("/"));
|
|
633
|
-
} catch {
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return map;
|
|
637
|
-
}
|
|
638
|
-
function expandWorkspaceGlobs(root, globs) {
|
|
639
|
-
const out = /* @__PURE__ */ new Set();
|
|
640
|
-
for (const glob of globs) {
|
|
641
|
-
const clean = glob.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
|
|
642
|
-
const baseDir = path2.join(root, clean);
|
|
643
|
-
const recurse = glob.endsWith("**");
|
|
644
|
-
if (glob === clean) {
|
|
645
|
-
if (fs18.existsSync(path2.join(baseDir, "package.json"))) out.add(baseDir);
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
collectPackageDirs(baseDir, recurse ? 4 : 1, out);
|
|
649
|
-
}
|
|
650
|
-
return [...out];
|
|
651
|
-
}
|
|
652
|
-
function collectPackageDirs(dir, depth, out) {
|
|
653
|
-
if (depth < 0) return;
|
|
654
|
-
let entries;
|
|
655
|
-
try {
|
|
656
|
-
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
657
|
-
} catch {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
for (const e of entries) {
|
|
661
|
-
if (!e.isDirectory() || e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
662
|
-
const child = path2.join(dir, e.name);
|
|
663
|
-
if (fs18.existsSync(path2.join(child, "package.json"))) out.add(child);
|
|
664
|
-
else collectPackageDirs(child, depth - 1, out);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
function parseJsonc(text) {
|
|
668
|
-
return JSON.parse(stripJsonc(text));
|
|
669
|
-
}
|
|
670
|
-
function stripJsonc(text) {
|
|
671
|
-
let out = "";
|
|
672
|
-
let inStr = false;
|
|
673
|
-
let strCh = "";
|
|
674
|
-
for (let i = 0; i < text.length; i++) {
|
|
675
|
-
const ch = text[i];
|
|
676
|
-
const next = text[i + 1];
|
|
677
|
-
if (inStr) {
|
|
678
|
-
out += ch;
|
|
679
|
-
if (ch === "\\") {
|
|
680
|
-
out += next ?? "";
|
|
681
|
-
i++;
|
|
682
|
-
} else if (ch === strCh) {
|
|
683
|
-
inStr = false;
|
|
684
|
-
}
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
if (ch === '"' || ch === "'") {
|
|
688
|
-
inStr = true;
|
|
689
|
-
strCh = ch;
|
|
690
|
-
out += ch;
|
|
691
|
-
continue;
|
|
692
|
-
}
|
|
693
|
-
if (ch === "/" && next === "/") {
|
|
694
|
-
while (i < text.length && text[i] !== "\n") i++;
|
|
695
|
-
out += "\n";
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
if (ch === "/" && next === "*") {
|
|
699
|
-
i += 2;
|
|
700
|
-
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
|
701
|
-
i++;
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
out += ch;
|
|
705
|
-
}
|
|
706
|
-
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/engine/ids.ts
|
|
710
|
-
function nodeId(parts) {
|
|
711
|
-
return shortId(
|
|
712
|
-
canonicalize({
|
|
713
|
-
t: "node",
|
|
714
|
-
kind: parts.kind,
|
|
715
|
-
qn: parts.qualifiedName,
|
|
716
|
-
file: parts.file,
|
|
717
|
-
sig: parts.signature ?? ""
|
|
718
|
-
})
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
function edgeId(kind, src, dst) {
|
|
722
|
-
return shortId(canonicalize({ t: "edge", kind, src, dst }));
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// src/engine/tests.ts
|
|
726
|
-
function isTestFile(rel2) {
|
|
727
|
-
const lower = rel2.toLowerCase();
|
|
728
|
-
const base = rel2.split("/").pop() ?? rel2;
|
|
729
|
-
const lbase = base.toLowerCase();
|
|
730
|
-
if (/(^|\/)(__tests__|__test__)\//.test(lower)) return true;
|
|
731
|
-
if (/(^|\/)(tests?|spec|specs)\//.test(lower)) return true;
|
|
732
|
-
if (/(^|\/)src\/test\//.test(lower)) return true;
|
|
733
|
-
if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(lbase)) return true;
|
|
734
|
-
if (/^test_.*\.py$/.test(lbase) || /_test\.py$/.test(lbase)) return true;
|
|
735
|
-
if (/_test\.go$/.test(lbase)) return true;
|
|
736
|
-
if (/[A-Za-z0-9](Test|Tests|IT)\.java$/.test(base)) return true;
|
|
737
|
-
if (/_spec\.rb$/.test(lbase) || /_test\.rb$/.test(lbase)) return true;
|
|
738
|
-
if (/[A-Za-z0-9](Test|Tests)\.cs$/.test(base)) return true;
|
|
739
|
-
if (/[A-Za-z0-9](Test|Tests)\.swift$/.test(base)) return true;
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
function applyStaticTestLinkage(nodes, edges) {
|
|
743
|
-
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
744
|
-
const testFiles = /* @__PURE__ */ new Set();
|
|
745
|
-
for (const n of nodes) if (n.kind === "file" && isTestFile(n.file)) testFiles.add(n.file);
|
|
746
|
-
const fileNodeIdByRel = /* @__PURE__ */ new Map();
|
|
747
|
-
for (const n of nodes) if (n.kind === "file") fileNodeIdByRel.set(n.file, n.id);
|
|
748
|
-
const covered = /* @__PURE__ */ new Set();
|
|
749
|
-
const newEdges = /* @__PURE__ */ new Map();
|
|
750
|
-
for (const e of edges) {
|
|
751
|
-
if (e.kind !== "call") continue;
|
|
752
|
-
const src = byId.get(e.src);
|
|
753
|
-
const dst = byId.get(e.dst);
|
|
754
|
-
if (!src || !dst) continue;
|
|
755
|
-
if (testFiles.has(src.file) && !testFiles.has(dst.file) && dst.kind !== "external") {
|
|
756
|
-
const testFileId = fileNodeIdByRel.get(src.file);
|
|
757
|
-
if (!testFileId) continue;
|
|
758
|
-
const id = edgeId("test", testFileId, dst.id);
|
|
759
|
-
if (!newEdges.has(id)) {
|
|
760
|
-
newEdges.set(id, {
|
|
761
|
-
id,
|
|
762
|
-
kind: "test",
|
|
763
|
-
src: testFileId,
|
|
764
|
-
dst: dst.id,
|
|
765
|
-
resolution: e.resolution,
|
|
766
|
-
confidence: e.confidence
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
covered.add(dst.id);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
const outNodes = nodes.map((n) => ({ ...n, tested: testedFlag(n, covered.has(n.id), testFiles) }));
|
|
773
|
-
const outEdges = [...edges, ...newEdges.values()].sort(
|
|
774
|
-
(a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
|
|
775
|
-
);
|
|
776
|
-
return {
|
|
777
|
-
nodes: outNodes,
|
|
778
|
-
edges: outEdges,
|
|
779
|
-
testFiles: [...testFiles].sort(),
|
|
780
|
-
testEdgeCount: newEdges.size
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
function testedFlag(node, covered, testFiles) {
|
|
784
|
-
if (node.kind === "external" || node.kind === "file" || node.kind === "package" || node.kind === "module") {
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
if (testFiles.has(node.file)) return null;
|
|
788
|
-
if (node.kind === "function" || node.kind === "method") return covered;
|
|
789
|
-
return covered ? true : node.tested ?? null;
|
|
790
|
-
}
|
|
791
355
|
|
|
792
356
|
// src/engine/scip.ts
|
|
793
357
|
var ROLE_DEFINITION = 1;
|
|
@@ -1159,14 +723,14 @@ var DEFAULT_PATHS = [
|
|
|
1159
723
|
];
|
|
1160
724
|
function loadCoverage(root, explicit) {
|
|
1161
725
|
const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
|
|
1162
|
-
(p) =>
|
|
726
|
+
(p) => path17.resolve(root, p)
|
|
1163
727
|
);
|
|
1164
|
-
const found = candidates.filter((p) =>
|
|
728
|
+
const found = candidates.filter((p) => fs15.existsSync(p));
|
|
1165
729
|
if (found.length === 0) return null;
|
|
1166
730
|
const map = /* @__PURE__ */ new Map();
|
|
1167
731
|
for (const file of found) {
|
|
1168
732
|
try {
|
|
1169
|
-
const text =
|
|
733
|
+
const text = fs15.readFileSync(file, "utf8");
|
|
1170
734
|
if (file.endsWith(".json")) mergeIstanbul(map, text, root);
|
|
1171
735
|
else mergeLcov(map, text, root);
|
|
1172
736
|
} catch {
|
|
@@ -1175,8 +739,8 @@ function loadCoverage(root, explicit) {
|
|
|
1175
739
|
return map.size ? map : null;
|
|
1176
740
|
}
|
|
1177
741
|
function rel(root, p) {
|
|
1178
|
-
const abs =
|
|
1179
|
-
return
|
|
742
|
+
const abs = path17.isAbsolute(p) ? p : path17.resolve(root, p);
|
|
743
|
+
return path17.relative(root, abs).split(path17.sep).join("/");
|
|
1180
744
|
}
|
|
1181
745
|
function bump(map, file, line, hits) {
|
|
1182
746
|
let lh = map.get(file);
|
|
@@ -1486,7 +1050,7 @@ async function parseInline(files, options) {
|
|
|
1486
1050
|
onProgress?.(0, files.length);
|
|
1487
1051
|
for (const file of files) {
|
|
1488
1052
|
try {
|
|
1489
|
-
const source =
|
|
1053
|
+
const source = fs15.readFileSync(file.abs, "utf8");
|
|
1490
1054
|
out.push(await parseSource(file.rel, file.lang.id, source));
|
|
1491
1055
|
} catch (err) {
|
|
1492
1056
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
@@ -1541,9 +1105,9 @@ function isWorkerOom(err) {
|
|
|
1541
1105
|
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1542
1106
|
}
|
|
1543
1107
|
function resolveWorkerFile() {
|
|
1544
|
-
const here =
|
|
1545
|
-
const candidate =
|
|
1546
|
-
return
|
|
1108
|
+
const here = path17.dirname(fileURLToPath(import.meta.url));
|
|
1109
|
+
const candidate = path17.join(here, "parse-worker.js");
|
|
1110
|
+
return fs15.existsSync(candidate) ? candidate : null;
|
|
1547
1111
|
}
|
|
1548
1112
|
function chunk(items, buckets) {
|
|
1549
1113
|
const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
|
|
@@ -1569,335 +1133,6 @@ function emptyParse(file, warning) {
|
|
|
1569
1133
|
warnings: [warning]
|
|
1570
1134
|
};
|
|
1571
1135
|
}
|
|
1572
|
-
function resolve4(parses, resolver) {
|
|
1573
|
-
const baseNodes = /* @__PURE__ */ new Map();
|
|
1574
|
-
const relSet = new Set(parses.map((p) => p.rel));
|
|
1575
|
-
const mod = resolver ?? relativeResolver(relSet);
|
|
1576
|
-
const defsByName = /* @__PURE__ */ new Map();
|
|
1577
|
-
const defsByFile = /* @__PURE__ */ new Map();
|
|
1578
|
-
const fileNodeIdByRel = /* @__PURE__ */ new Map();
|
|
1579
|
-
for (const p of parses) {
|
|
1580
|
-
const fileId = nodeId({ kind: "file", qualifiedName: p.rel, file: p.rel });
|
|
1581
|
-
fileNodeIdByRel.set(p.rel, fileId);
|
|
1582
|
-
addNode(baseNodes, {
|
|
1583
|
-
id: fileId,
|
|
1584
|
-
kind: "file",
|
|
1585
|
-
name: path2.posix.basename(p.rel),
|
|
1586
|
-
qualifiedName: p.rel,
|
|
1587
|
-
file: p.rel,
|
|
1588
|
-
span: { start: 1, end: 1 },
|
|
1589
|
-
lang: p.lang
|
|
1590
|
-
});
|
|
1591
|
-
for (const d of p.defs) {
|
|
1592
|
-
const id = nodeId({
|
|
1593
|
-
kind: d.kind,
|
|
1594
|
-
qualifiedName: d.qualifiedName,
|
|
1595
|
-
file: p.rel,
|
|
1596
|
-
signature: d.signature
|
|
1597
|
-
});
|
|
1598
|
-
addNode(baseNodes, {
|
|
1599
|
-
id,
|
|
1600
|
-
kind: d.kind,
|
|
1601
|
-
name: d.name,
|
|
1602
|
-
qualifiedName: d.qualifiedName,
|
|
1603
|
-
file: p.rel,
|
|
1604
|
-
span: { start: d.startLine, end: d.endLine },
|
|
1605
|
-
lang: p.lang,
|
|
1606
|
-
signature: d.signature,
|
|
1607
|
-
doc: d.doc
|
|
1608
|
-
});
|
|
1609
|
-
const ref = {
|
|
1610
|
-
id,
|
|
1611
|
-
name: d.name,
|
|
1612
|
-
qualifiedName: d.qualifiedName,
|
|
1613
|
-
rel: p.rel,
|
|
1614
|
-
lang: p.lang,
|
|
1615
|
-
kind: d.kind,
|
|
1616
|
-
startByte: d.startByte,
|
|
1617
|
-
endByte: d.endByte
|
|
1618
|
-
};
|
|
1619
|
-
push(defsByName, d.name, ref);
|
|
1620
|
-
push(defsByFile, p.rel, ref);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
const filesByNamespace = /* @__PURE__ */ new Map();
|
|
1624
|
-
for (const p of parses) for (const ns of p.namespaces ?? []) addToSet(filesByNamespace, ns, p.rel);
|
|
1625
|
-
const relsByLang = /* @__PURE__ */ new Map();
|
|
1626
|
-
for (const p of parses) push(relsByLang, p.lang, p.rel);
|
|
1627
|
-
const nsReachableByRel = /* @__PURE__ */ new Map();
|
|
1628
|
-
const moduleReachableByRel = /* @__PURE__ */ new Map();
|
|
1629
|
-
for (const p of parses) {
|
|
1630
|
-
if (MODULE_SCOPED_LANGS.has(p.lang)) {
|
|
1631
|
-
const mod2 = /* @__PURE__ */ new Set();
|
|
1632
|
-
for (const rel2 of relsByLang.get(p.lang) ?? []) if (rel2 !== p.rel) mod2.add(rel2);
|
|
1633
|
-
moduleReachableByRel.set(p.rel, mod2);
|
|
1634
|
-
}
|
|
1635
|
-
const reach = /* @__PURE__ */ new Set();
|
|
1636
|
-
const namespaces = new Set(p.namespaces ?? []);
|
|
1637
|
-
for (const imp of p.imports) {
|
|
1638
|
-
namespaces.add(imp.source);
|
|
1639
|
-
if (IMPORT_NAMES_A_TYPE.has(p.lang)) {
|
|
1640
|
-
const sep9 = Math.max(imp.source.lastIndexOf("."), imp.source.lastIndexOf("\\"));
|
|
1641
|
-
if (sep9 > 0) namespaces.add(imp.source.slice(0, sep9));
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
for (const ns of namespaces) for (const rel2 of filesByNamespace.get(ns) ?? []) if (rel2 !== p.rel) reach.add(rel2);
|
|
1645
|
-
nsReachableByRel.set(p.rel, reach);
|
|
1646
|
-
}
|
|
1647
|
-
const superTypesByType = /* @__PURE__ */ new Map();
|
|
1648
|
-
for (const p of parses) {
|
|
1649
|
-
const localDefs = defsByFile.get(p.rel) ?? [];
|
|
1650
|
-
for (const h of p.heritage) {
|
|
1651
|
-
const encl = enclosingDefRef(localDefs, h.byte);
|
|
1652
|
-
if (encl && (encl.kind === "class" || encl.kind === "interface")) addToSet(superTypesByType, encl.name, h.superName);
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
const edges = new EdgeSet();
|
|
1656
|
-
const unresolved2 = /* @__PURE__ */ new Map();
|
|
1657
|
-
const stats = {
|
|
1658
|
-
callsResolved: 0,
|
|
1659
|
-
callsUnresolved: 0,
|
|
1660
|
-
importsResolvedToFile: 0,
|
|
1661
|
-
importsExternal: 0,
|
|
1662
|
-
resolvers: ["heuristic"]
|
|
1663
|
-
};
|
|
1664
|
-
for (const p of parses) {
|
|
1665
|
-
const fileId = fileNodeIdByRel.get(p.rel);
|
|
1666
|
-
const localByQn = /* @__PURE__ */ new Map();
|
|
1667
|
-
for (const ref of defsByFile.get(p.rel) ?? []) localByQn.set(ref.qualifiedName, ref.id);
|
|
1668
|
-
for (const d of p.defs) {
|
|
1669
|
-
const id = localByQn.get(d.qualifiedName);
|
|
1670
|
-
const parentQn = parentQualifiedName(d.qualifiedName);
|
|
1671
|
-
const parentId = parentQn ? localByQn.get(parentQn) : void 0;
|
|
1672
|
-
edges.add("contains", parentId ?? fileId, id, "heuristic", 1);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
const importedFilesByRel = /* @__PURE__ */ new Map();
|
|
1676
|
-
for (const p of parses) {
|
|
1677
|
-
const fileId = fileNodeIdByRel.get(p.rel);
|
|
1678
|
-
const importedRels = /* @__PURE__ */ new Set();
|
|
1679
|
-
for (const imp of p.imports) {
|
|
1680
|
-
const targetRel = mod.resolve(p.rel, imp.source);
|
|
1681
|
-
if (targetRel && targetRel !== p.rel) {
|
|
1682
|
-
importedRels.add(targetRel);
|
|
1683
|
-
edges.add("import", fileId, fileNodeIdByRel.get(targetRel), "heuristic", 0.9);
|
|
1684
|
-
stats.importsResolvedToFile++;
|
|
1685
|
-
} else {
|
|
1686
|
-
const extId = ensureExternalNode(baseNodes, imp.source);
|
|
1687
|
-
edges.add("import", fileId, extId, "heuristic", 1);
|
|
1688
|
-
stats.importsExternal++;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
importedFilesByRel.set(p.rel, importedRels);
|
|
1692
|
-
}
|
|
1693
|
-
for (const p of parses) {
|
|
1694
|
-
const fileId = fileNodeIdByRel.get(p.rel);
|
|
1695
|
-
const localDefs = defsByFile.get(p.rel) ?? [];
|
|
1696
|
-
const imported = importedFilesByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1697
|
-
const testCaller = isTestFile(p.rel);
|
|
1698
|
-
const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1699
|
-
const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1700
|
-
for (const call of p.calls) {
|
|
1701
|
-
const srcId = enclosingDefId(localDefs, call.byte) ?? fileId;
|
|
1702
|
-
const resolved = resolveCall(call, p.rel, p.lang, imported, defsByName, srcId, testCaller, nsReach, superTypesByType, modReach);
|
|
1703
|
-
if (resolved) {
|
|
1704
|
-
edges.add("call", srcId, resolved.id, "heuristic", resolved.confidence);
|
|
1705
|
-
stats.callsResolved++;
|
|
1706
|
-
} else {
|
|
1707
|
-
stats.callsUnresolved++;
|
|
1708
|
-
bumpUnresolved(unresolved2, srcId, call.callee, "call", p.rel);
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
for (const p of parses) {
|
|
1713
|
-
const fileId = fileNodeIdByRel.get(p.rel);
|
|
1714
|
-
const localDefs = defsByFile.get(p.rel) ?? [];
|
|
1715
|
-
const imported = importedFilesByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1716
|
-
const testCaller = isTestFile(p.rel);
|
|
1717
|
-
const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1718
|
-
const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1719
|
-
for (const ref of p.typeRefs) {
|
|
1720
|
-
const srcId = enclosingDefId(localDefs, ref.byte) ?? fileId;
|
|
1721
|
-
const target = resolveType(ref.name, p.rel, p.lang, imported, defsByName, testCaller, nsReach, modReach);
|
|
1722
|
-
if (target) edges.add("references", srcId, target.id, "heuristic", 0.7);
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
for (const p of parses) {
|
|
1726
|
-
const localDefs = defsByFile.get(p.rel) ?? [];
|
|
1727
|
-
const imported = importedFilesByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1728
|
-
const testCaller = isTestFile(p.rel);
|
|
1729
|
-
for (const h of p.heritage) {
|
|
1730
|
-
const srcId = enclosingDefId(localDefs, h.byte);
|
|
1731
|
-
if (!srcId) continue;
|
|
1732
|
-
const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1733
|
-
const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
|
|
1734
|
-
const target = resolveType(h.superName, p.rel, p.lang, imported, defsByName, testCaller, nsReach, modReach);
|
|
1735
|
-
if (target) edges.add(h.kind, srcId, target.id, "heuristic", 0.85);
|
|
1736
|
-
else bumpUnresolved(unresolved2, srcId, h.superName, h.kind, p.rel);
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
const nodes = [...baseNodes.values()].map((n) => ({
|
|
1740
|
-
...n,
|
|
1741
|
-
importance: 0,
|
|
1742
|
-
centrality: { degree: 0, pagerank: 0, betweenness: 0, eigenvector: 0 },
|
|
1743
|
-
area: 0,
|
|
1744
|
-
isHub: false,
|
|
1745
|
-
tested: null
|
|
1746
|
-
}));
|
|
1747
|
-
const unresolvedList = [...unresolved2.values()].sort(
|
|
1748
|
-
(a, b) => a.from.localeCompare(b.from) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
|
|
1749
|
-
);
|
|
1750
|
-
return { nodes, edges: edges.toArray(), unresolved: unresolvedList, stats };
|
|
1751
|
-
}
|
|
1752
|
-
function bumpUnresolved(map, from, name, kind, fromRel) {
|
|
1753
|
-
const key = `${from}\0${kind}\0${name}`;
|
|
1754
|
-
const existing = map.get(key);
|
|
1755
|
-
if (existing) existing.count++;
|
|
1756
|
-
else map.set(key, { from, name, kind, count: 1, fromRel });
|
|
1757
|
-
}
|
|
1758
|
-
function addNode(map, n) {
|
|
1759
|
-
if (!map.has(n.id)) map.set(n.id, n);
|
|
1760
|
-
}
|
|
1761
|
-
function ensureExternalNode(map, source) {
|
|
1762
|
-
const id = nodeId({ kind: "external", qualifiedName: source, file: "" });
|
|
1763
|
-
if (!map.has(id)) {
|
|
1764
|
-
map.set(id, {
|
|
1765
|
-
id,
|
|
1766
|
-
kind: "external",
|
|
1767
|
-
name: source,
|
|
1768
|
-
qualifiedName: source,
|
|
1769
|
-
file: "",
|
|
1770
|
-
span: { start: 1, end: 1 },
|
|
1771
|
-
lang: "external"
|
|
1772
|
-
});
|
|
1773
|
-
}
|
|
1774
|
-
return id;
|
|
1775
|
-
}
|
|
1776
|
-
function push(map, key, value) {
|
|
1777
|
-
const list = map.get(key);
|
|
1778
|
-
if (list) list.push(value);
|
|
1779
|
-
else map.set(key, [value]);
|
|
1780
|
-
}
|
|
1781
|
-
function parentQualifiedName(qn) {
|
|
1782
|
-
const idx = qn.lastIndexOf(".");
|
|
1783
|
-
return idx > 0 ? qn.slice(0, idx) : null;
|
|
1784
|
-
}
|
|
1785
|
-
function enclosingDefRef(defs, byte) {
|
|
1786
|
-
let best;
|
|
1787
|
-
for (const d of defs) {
|
|
1788
|
-
if (d.startByte <= byte && d.endByte >= byte) {
|
|
1789
|
-
if (!best || d.endByte - d.startByte < best.endByte - best.startByte) best = d;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
return best;
|
|
1793
|
-
}
|
|
1794
|
-
function enclosingDefId(defs, byte) {
|
|
1795
|
-
return enclosingDefRef(defs, byte)?.id;
|
|
1796
|
-
}
|
|
1797
|
-
function addToSet(map, key, value) {
|
|
1798
|
-
const s = map.get(key);
|
|
1799
|
-
if (s) s.add(value);
|
|
1800
|
-
else map.set(key, /* @__PURE__ */ new Set([value]));
|
|
1801
|
-
}
|
|
1802
|
-
function parentTypeName(qualifiedName) {
|
|
1803
|
-
const i = qualifiedName.lastIndexOf(".");
|
|
1804
|
-
return i > 0 ? qualifiedName.slice(0, i) : null;
|
|
1805
|
-
}
|
|
1806
|
-
function disambiguateInterfaceImpl(pool, superTypesByType) {
|
|
1807
|
-
const withParent = pool.map((c) => ({ c, parent: parentTypeName(c.qualifiedName) })).filter((x) => x.parent !== null);
|
|
1808
|
-
if (withParent.length < 2) return null;
|
|
1809
|
-
const parents = new Set(withParent.map((x) => x.parent));
|
|
1810
|
-
const impls = withParent.filter(
|
|
1811
|
-
(x) => [...parents].some((other) => other !== x.parent && superTypesByType.get(x.parent)?.has(other))
|
|
1812
|
-
);
|
|
1813
|
-
if (impls.length === 0) return null;
|
|
1814
|
-
const interfaceParents = /* @__PURE__ */ new Set();
|
|
1815
|
-
for (const impl of impls) for (const s of superTypesByType.get(impl.parent) ?? []) interfaceParents.add(s);
|
|
1816
|
-
const interfaces = withParent.filter((x) => interfaceParents.has(x.parent));
|
|
1817
|
-
const uniqueImplIds = new Set(impls.map((x) => x.c.id));
|
|
1818
|
-
if (uniqueImplIds.size === 1) return impls[0].c;
|
|
1819
|
-
const uniqueIfaceIds = new Set(interfaces.map((x) => x.c.id));
|
|
1820
|
-
if (uniqueIfaceIds.size === 1) return interfaces[0].c;
|
|
1821
|
-
return null;
|
|
1822
|
-
}
|
|
1823
|
-
var PACKAGE_SCOPED_LANGS = /* @__PURE__ */ new Set(["go", "java", "cs", "kotlin", "scala", "swift"]);
|
|
1824
|
-
var IMPORT_NAMES_A_TYPE = /* @__PURE__ */ new Set(["java", "kotlin", "scala", "php"]);
|
|
1825
|
-
var MODULE_SCOPED_LANGS = /* @__PURE__ */ new Set(["swift"]);
|
|
1826
|
-
function dirOf(rel2) {
|
|
1827
|
-
const i = rel2.lastIndexOf("/");
|
|
1828
|
-
return i >= 0 ? rel2.slice(0, i) : "";
|
|
1829
|
-
}
|
|
1830
|
-
function resolveCall(call, fromRel, fromLang, importedRels, defsByName, enclosingId, fromIsTestFile = false, nsReachableRels = /* @__PURE__ */ new Set(), superTypesByType = /* @__PURE__ */ new Map(), moduleReachableRels = /* @__PURE__ */ new Set()) {
|
|
1831
|
-
const candidates = defsByName.get(call.callee);
|
|
1832
|
-
if (!candidates || candidates.length === 0) return null;
|
|
1833
|
-
const callable = candidates.filter((c) => c.kind === "function" || c.kind === "method");
|
|
1834
|
-
const pool = callable.length ? callable : candidates;
|
|
1835
|
-
let sameFile = pool.filter((c) => c.rel === fromRel);
|
|
1836
|
-
if (call.qualified && enclosingId) sameFile = sameFile.filter((c) => c.id !== enclosingId);
|
|
1837
|
-
if (sameFile.length === 1) return { id: sameFile[0].id, confidence: 0.85 };
|
|
1838
|
-
if (sameFile.length > 1) {
|
|
1839
|
-
const pick = [...sameFile].sort((a, b) => a.id.localeCompare(b.id))[0];
|
|
1840
|
-
return { id: pick.id, confidence: 0.4 };
|
|
1841
|
-
}
|
|
1842
|
-
const dir = dirOf(fromRel);
|
|
1843
|
-
const notSelf = (c) => !(call.qualified && enclosingId && c.id === enclosingId);
|
|
1844
|
-
const strict = pool.filter(
|
|
1845
|
-
(c) => notSelf(c) && (importedRels.has(c.rel) || PACKAGE_SCOPED_LANGS.has(fromLang) && c.lang === fromLang && dirOf(c.rel) === dir || nsReachableRels.has(c.rel))
|
|
1846
|
-
);
|
|
1847
|
-
if (strict.length === 1) return { id: strict[0].id, confidence: 0.75 };
|
|
1848
|
-
const broad = pool.filter((c) => notSelf(c) && (moduleReachableRels.has(c.rel) || strict.includes(c)));
|
|
1849
|
-
if (broad.length > 1) {
|
|
1850
|
-
const dedup = disambiguateInterfaceImpl(broad, superTypesByType);
|
|
1851
|
-
if (dedup) return { id: dedup.id, confidence: 0.6 };
|
|
1852
|
-
}
|
|
1853
|
-
if (strict.length > 1) return null;
|
|
1854
|
-
if (fromLang === "swift" && fromIsTestFile && pool.length === 1) {
|
|
1855
|
-
return { id: pool[0].id, confidence: 0.5 };
|
|
1856
|
-
}
|
|
1857
|
-
return null;
|
|
1858
|
-
}
|
|
1859
|
-
function resolveType(name, fromRel, fromLang, importedRels, defsByName, fromIsTestFile = false, nsReachableRels = /* @__PURE__ */ new Set(), moduleReachableRels = /* @__PURE__ */ new Set()) {
|
|
1860
|
-
const candidates = (defsByName.get(name) ?? []).filter(
|
|
1861
|
-
(c) => c.kind === "class" || c.kind === "interface"
|
|
1862
|
-
);
|
|
1863
|
-
if (candidates.length === 0) return null;
|
|
1864
|
-
const sameFile = candidates.filter((c) => c.rel === fromRel);
|
|
1865
|
-
if (sameFile.length === 1) return { id: sameFile[0].id };
|
|
1866
|
-
const imported = candidates.filter((c) => importedRels.has(c.rel));
|
|
1867
|
-
if (imported.length === 1) return { id: imported[0].id };
|
|
1868
|
-
if (imported.length > 1) return null;
|
|
1869
|
-
if (PACKAGE_SCOPED_LANGS.has(fromLang)) {
|
|
1870
|
-
const dir = dirOf(fromRel);
|
|
1871
|
-
const samePkg = candidates.filter((c) => c.lang === fromLang && dirOf(c.rel) === dir);
|
|
1872
|
-
if (samePkg.length === 1) return { id: samePkg[0].id };
|
|
1873
|
-
}
|
|
1874
|
-
if (nsReachableRels.size || moduleReachableRels.size) {
|
|
1875
|
-
const reach = candidates.filter((c) => nsReachableRels.has(c.rel) || moduleReachableRels.has(c.rel));
|
|
1876
|
-
if (reach.length === 1) return { id: reach[0].id };
|
|
1877
|
-
}
|
|
1878
|
-
if (fromLang === "swift" && fromIsTestFile && candidates.length === 1) {
|
|
1879
|
-
return { id: candidates[0].id };
|
|
1880
|
-
}
|
|
1881
|
-
return null;
|
|
1882
|
-
}
|
|
1883
|
-
var EdgeSet = class {
|
|
1884
|
-
map = /* @__PURE__ */ new Map();
|
|
1885
|
-
add(kind, src, dst, resolution, confidence) {
|
|
1886
|
-
const id = edgeId(kind, src, dst);
|
|
1887
|
-
const existing = this.map.get(id);
|
|
1888
|
-
if (existing) {
|
|
1889
|
-
existing.count = (existing.count ?? 1) + 1;
|
|
1890
|
-
if (confidence > existing.confidence) existing.confidence = confidence;
|
|
1891
|
-
return;
|
|
1892
|
-
}
|
|
1893
|
-
this.map.set(id, { id, kind, src, dst, resolution, confidence, count: 1 });
|
|
1894
|
-
}
|
|
1895
|
-
toArray() {
|
|
1896
|
-
return [...this.map.values()].sort(
|
|
1897
|
-
(a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
|
|
1898
|
-
);
|
|
1899
|
-
}
|
|
1900
|
-
};
|
|
1901
1136
|
var PRECISE_KIND = "tsc";
|
|
1902
1137
|
function tsResolveEdges(root, tsFiles, nodes) {
|
|
1903
1138
|
const empty = {
|
|
@@ -2121,7 +1356,7 @@ function enclosing3(nodes, line) {
|
|
|
2121
1356
|
return best;
|
|
2122
1357
|
}
|
|
2123
1358
|
function normalize2(p) {
|
|
2124
|
-
return
|
|
1359
|
+
return path17.resolve(p).split(path17.sep).join("/");
|
|
2125
1360
|
}
|
|
2126
1361
|
|
|
2127
1362
|
// src/engine/epistemic.ts
|
|
@@ -2147,11 +1382,416 @@ function epistemicBreakdown(edges) {
|
|
|
2147
1382
|
}
|
|
2148
1383
|
return out;
|
|
2149
1384
|
}
|
|
1385
|
+
var DOC_BODY_MAX_CHARS = 8e3;
|
|
1386
|
+
var DOC_FILE_MAX_BYTES = 512 * 1024;
|
|
1387
|
+
var DOC_MAX_FILES = 600;
|
|
1388
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".rst", ".adoc"]);
|
|
1389
|
+
var ENV_EXAMPLE_NAMES = /* @__PURE__ */ new Set([
|
|
1390
|
+
"example.env",
|
|
1391
|
+
"env.example",
|
|
1392
|
+
".env.example",
|
|
1393
|
+
".env.sample",
|
|
1394
|
+
".env.template",
|
|
1395
|
+
".env.development.example",
|
|
1396
|
+
".env.local.example",
|
|
1397
|
+
"dotenv.example"
|
|
1398
|
+
]);
|
|
1399
|
+
var EXACT_BASENAME = {
|
|
1400
|
+
// Manifests / package managers
|
|
1401
|
+
"package.json": "manifest",
|
|
1402
|
+
"package.yaml": "manifest",
|
|
1403
|
+
"package.yml": "manifest",
|
|
1404
|
+
"pyproject.toml": "manifest",
|
|
1405
|
+
"setup.cfg": "manifest",
|
|
1406
|
+
"requirements.txt": "manifest",
|
|
1407
|
+
"requirements-dev.txt": "manifest",
|
|
1408
|
+
"pipfile": "manifest",
|
|
1409
|
+
"go.mod": "manifest",
|
|
1410
|
+
"go.work": "manifest",
|
|
1411
|
+
"cargo.toml": "manifest",
|
|
1412
|
+
"composer.json": "manifest",
|
|
1413
|
+
"gemfile": "manifest",
|
|
1414
|
+
"podfile": "manifest",
|
|
1415
|
+
"pubspec.yaml": "manifest",
|
|
1416
|
+
"mix.exs": "manifest",
|
|
1417
|
+
"build.gradle": "manifest",
|
|
1418
|
+
"build.gradle.kts": "manifest",
|
|
1419
|
+
"settings.gradle": "manifest",
|
|
1420
|
+
"settings.gradle.kts": "manifest",
|
|
1421
|
+
"pom.xml": "manifest",
|
|
1422
|
+
"project.clj": "manifest",
|
|
1423
|
+
"stack.yaml": "manifest",
|
|
1424
|
+
"package.swift": "manifest",
|
|
1425
|
+
// Workspace / monorepo
|
|
1426
|
+
"pnpm-workspace.yaml": "workspace",
|
|
1427
|
+
"pnpm-workspace.yml": "workspace",
|
|
1428
|
+
"lerna.json": "workspace",
|
|
1429
|
+
"nx.json": "workspace",
|
|
1430
|
+
"turbo.json": "workspace",
|
|
1431
|
+
"rush.json": "workspace",
|
|
1432
|
+
"workspace.json": "workspace",
|
|
1433
|
+
// Docker
|
|
1434
|
+
dockerfile: "docker",
|
|
1435
|
+
"dockerfile.dev": "docker",
|
|
1436
|
+
"dockerfile.prod": "docker",
|
|
1437
|
+
"dockerfile.local": "docker",
|
|
1438
|
+
containerfile: "docker",
|
|
1439
|
+
// Compose
|
|
1440
|
+
"docker-compose.yml": "compose",
|
|
1441
|
+
"docker-compose.yaml": "compose",
|
|
1442
|
+
"compose.yml": "compose",
|
|
1443
|
+
"compose.yaml": "compose",
|
|
1444
|
+
"docker-compose.dev.yml": "compose",
|
|
1445
|
+
"docker-compose.prod.yml": "compose",
|
|
1446
|
+
"docker-compose.override.yml": "compose",
|
|
1447
|
+
// Task runners
|
|
1448
|
+
makefile: "task-runner",
|
|
1449
|
+
gnumakefile: "task-runner",
|
|
1450
|
+
justfile: "task-runner",
|
|
1451
|
+
"taskfile.yml": "task-runner",
|
|
1452
|
+
"taskfile.yaml": "task-runner",
|
|
1453
|
+
rakefile: "task-runner",
|
|
1454
|
+
// Build / tool config (exact)
|
|
1455
|
+
"tsconfig.json": "build-config",
|
|
1456
|
+
"jsconfig.json": "build-config",
|
|
1457
|
+
"vite.config.ts": "build-config",
|
|
1458
|
+
"vite.config.js": "build-config",
|
|
1459
|
+
"vite.config.mjs": "build-config",
|
|
1460
|
+
"vitest.config.ts": "build-config",
|
|
1461
|
+
"vitest.config.js": "build-config",
|
|
1462
|
+
"jest.config.js": "build-config",
|
|
1463
|
+
"jest.config.ts": "build-config",
|
|
1464
|
+
"jest.config.cjs": "build-config",
|
|
1465
|
+
"webpack.config.js": "build-config",
|
|
1466
|
+
"webpack.config.ts": "build-config",
|
|
1467
|
+
"rollup.config.js": "build-config",
|
|
1468
|
+
"esbuild.config.js": "build-config",
|
|
1469
|
+
"next.config.js": "build-config",
|
|
1470
|
+
"next.config.mjs": "build-config",
|
|
1471
|
+
"next.config.ts": "build-config",
|
|
1472
|
+
"nuxt.config.ts": "build-config",
|
|
1473
|
+
"nuxt.config.js": "build-config",
|
|
1474
|
+
"astro.config.mjs": "build-config",
|
|
1475
|
+
"svelte.config.js": "build-config",
|
|
1476
|
+
"remix.config.js": "build-config",
|
|
1477
|
+
"angular.json": "build-config",
|
|
1478
|
+
"babel.config.js": "build-config",
|
|
1479
|
+
"babel.config.json": "build-config",
|
|
1480
|
+
".babelrc": "build-config",
|
|
1481
|
+
".babelrc.js": "build-config",
|
|
1482
|
+
".babelrc.json": "build-config",
|
|
1483
|
+
"eslint.config.js": "build-config",
|
|
1484
|
+
"eslint.config.mjs": "build-config",
|
|
1485
|
+
"eslint.config.cjs": "build-config",
|
|
1486
|
+
"eslint.config.ts": "build-config",
|
|
1487
|
+
".eslintrc": "build-config",
|
|
1488
|
+
".eslintrc.js": "build-config",
|
|
1489
|
+
".eslintrc.cjs": "build-config",
|
|
1490
|
+
".eslintrc.json": "build-config",
|
|
1491
|
+
".eslintrc.yml": "build-config",
|
|
1492
|
+
".prettierrc": "build-config",
|
|
1493
|
+
".prettierrc.js": "build-config",
|
|
1494
|
+
".prettierrc.json": "build-config",
|
|
1495
|
+
".prettierrc.yml": "build-config",
|
|
1496
|
+
"prettier.config.js": "build-config",
|
|
1497
|
+
"prettier.config.cjs": "build-config",
|
|
1498
|
+
"ruff.toml": "build-config",
|
|
1499
|
+
".ruff.toml": "build-config",
|
|
1500
|
+
"pytest.ini": "build-config",
|
|
1501
|
+
"tox.ini": "build-config",
|
|
1502
|
+
"mypy.ini": "build-config",
|
|
1503
|
+
".mypy.ini": "build-config",
|
|
1504
|
+
"phpunit.xml": "build-config",
|
|
1505
|
+
"phpunit.xml.dist": "build-config",
|
|
1506
|
+
// CI exact
|
|
1507
|
+
"jenkinsfile": "ci",
|
|
1508
|
+
".gitlab-ci.yml": "ci",
|
|
1509
|
+
".travis.yml": "ci",
|
|
1510
|
+
"azure-pipelines.yml": "ci",
|
|
1511
|
+
"buildkite.yml": "ci",
|
|
1512
|
+
"cloudbuild.yaml": "ci",
|
|
1513
|
+
"appveyor.yml": "ci",
|
|
1514
|
+
"circle.yml": "ci",
|
|
1515
|
+
// Deploy / infra (root-level common names)
|
|
1516
|
+
"vercel.json": "infra",
|
|
1517
|
+
"netlify.toml": "infra",
|
|
1518
|
+
"wrangler.toml": "infra",
|
|
1519
|
+
"wrangler.json": "infra",
|
|
1520
|
+
"wrangler.jsonc": "infra",
|
|
1521
|
+
"fly.toml": "infra",
|
|
1522
|
+
"railway.toml": "infra",
|
|
1523
|
+
"render.yaml": "infra",
|
|
1524
|
+
"app.yaml": "infra",
|
|
1525
|
+
"Procfile": "infra",
|
|
1526
|
+
"procfile": "infra",
|
|
1527
|
+
"heroku.yml": "infra",
|
|
1528
|
+
"serverless.yml": "infra",
|
|
1529
|
+
"serverless.yaml": "infra",
|
|
1530
|
+
"sam.yaml": "infra",
|
|
1531
|
+
"samconfig.toml": "infra",
|
|
1532
|
+
"cdk.json": "infra",
|
|
1533
|
+
"pulumi.yaml": "infra",
|
|
1534
|
+
"terraform.tf": "infra",
|
|
1535
|
+
// Agent / project meta (also covered by .md extension)
|
|
1536
|
+
"agents.md": "markdown",
|
|
1537
|
+
"claude.md": "markdown",
|
|
1538
|
+
"codeowners": "other-config",
|
|
1539
|
+
".nvmrc": "other-config",
|
|
1540
|
+
".node-version": "other-config",
|
|
1541
|
+
".python-version": "other-config",
|
|
1542
|
+
"runtime.txt": "other-config",
|
|
1543
|
+
".tool-versions": "other-config",
|
|
1544
|
+
"editorconfig": "other-config",
|
|
1545
|
+
".editorconfig": "other-config"
|
|
1546
|
+
};
|
|
1547
|
+
var BASENAME_PREFIX = [
|
|
1548
|
+
{ re: /^dockerfile(\.|$)/i, category: "docker" },
|
|
1549
|
+
{ re: /^docker-compose(\.|$)/i, category: "compose" },
|
|
1550
|
+
{ re: /^compose\.(ya?ml)$/i, category: "compose" },
|
|
1551
|
+
{ re: /^tsconfig(\..+)?\.json$/i, category: "build-config" },
|
|
1552
|
+
{ re: /^jsconfig(\..+)?\.json$/i, category: "build-config" },
|
|
1553
|
+
{ re: /^vite\.config\./i, category: "build-config" },
|
|
1554
|
+
{ re: /^vitest\.config\./i, category: "build-config" },
|
|
1555
|
+
{ re: /^jest\.config\./i, category: "build-config" },
|
|
1556
|
+
{ re: /^webpack\.config\./i, category: "build-config" },
|
|
1557
|
+
{ re: /^rollup\.config\./i, category: "build-config" },
|
|
1558
|
+
{ re: /^next\.config\./i, category: "build-config" },
|
|
1559
|
+
{ re: /^nuxt\.config\./i, category: "build-config" },
|
|
1560
|
+
{ re: /^astro\.config\./i, category: "build-config" },
|
|
1561
|
+
{ re: /^eslint\.config\./i, category: "build-config" },
|
|
1562
|
+
{ re: /^\.eslintrc/i, category: "build-config" },
|
|
1563
|
+
{ re: /^openapi\./i, category: "api-contract" },
|
|
1564
|
+
{ re: /^swagger\./i, category: "api-contract" },
|
|
1565
|
+
{ re: /^requirements(-\w+)?\.txt$/i, category: "manifest" },
|
|
1566
|
+
{ re: /^dockerfile/i, category: "docker" }
|
|
1567
|
+
];
|
|
1568
|
+
var PATH_PATTERNS = [
|
|
1569
|
+
// GitHub Actions / workflows
|
|
1570
|
+
{ re: /^\.github\/workflows\/.+\.ya?ml$/i, category: "ci" },
|
|
1571
|
+
{ re: /^\.github\/actions\/.+/i, category: "ci" },
|
|
1572
|
+
{ re: /^\.circleci\/.+\.ya?ml$/i, category: "ci" },
|
|
1573
|
+
{ re: /^\.buildkite\/.+/i, category: "ci" },
|
|
1574
|
+
// Helm / k8s (values + charts — not every template blob)
|
|
1575
|
+
{ re: /^charts\/[^/]+\/(chart\.ya?ml|values.*\.ya?ml)$/i, category: "infra" },
|
|
1576
|
+
{ re: /^deploy\/.+\.ya?ml$/i, category: "infra" },
|
|
1577
|
+
{ re: /^k8s\/.+\.ya?ml$/i, category: "infra" },
|
|
1578
|
+
{ re: /^kubernetes\/.+\.ya?ml$/i, category: "infra" },
|
|
1579
|
+
{ re: /^infra\/.+\.ya?ml$/i, category: "infra" },
|
|
1580
|
+
{ re: /^terraform\/.+\.tf$/i, category: "infra" },
|
|
1581
|
+
// API contracts by path
|
|
1582
|
+
{ re: /(^|\/)(openapi|swagger)(\.|\/)/i, category: "api-contract" },
|
|
1583
|
+
{ re: /(^|\/)api(-)?(spec|schema|contracts?)\//i, category: "api-contract" }
|
|
1584
|
+
];
|
|
1585
|
+
var API_EXTENSIONS = /* @__PURE__ */ new Set([".graphql", ".gql", ".proto", ".avsc", ".thrift"]);
|
|
1586
|
+
var IMPORTANCE = {
|
|
1587
|
+
markdown: 0.1,
|
|
1588
|
+
text: 0.06,
|
|
1589
|
+
"env-example": 0.12,
|
|
1590
|
+
manifest: 0.16,
|
|
1591
|
+
docker: 0.14,
|
|
1592
|
+
compose: 0.15,
|
|
1593
|
+
ci: 0.14,
|
|
1594
|
+
"build-config": 0.11,
|
|
1595
|
+
"api-contract": 0.13,
|
|
1596
|
+
"task-runner": 0.13,
|
|
1597
|
+
workspace: 0.14,
|
|
1598
|
+
infra: 0.12,
|
|
1599
|
+
"other-config": 0.08
|
|
1600
|
+
};
|
|
1601
|
+
function toPosix2(p) {
|
|
1602
|
+
return p.split(path17.sep).join("/");
|
|
1603
|
+
}
|
|
1604
|
+
function buildRootIgnore2(root, exclude) {
|
|
1605
|
+
const ig = ignore();
|
|
1606
|
+
const gitignorePath = path17.join(root, ".gitignore");
|
|
1607
|
+
if (fs15.existsSync(gitignorePath)) {
|
|
1608
|
+
try {
|
|
1609
|
+
ig.add(fs15.readFileSync(gitignorePath, "utf8"));
|
|
1610
|
+
} catch {
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
if (exclude.length) ig.add(exclude);
|
|
1614
|
+
return ig;
|
|
1615
|
+
}
|
|
1616
|
+
function classifyProjectContext(relOrName) {
|
|
1617
|
+
const rel2 = relOrName.replace(/\\/g, "/");
|
|
1618
|
+
const base = path17.basename(rel2);
|
|
1619
|
+
const baseLower = base.toLowerCase();
|
|
1620
|
+
if (/^\.env($|\.)/i.test(base) && !isEnvExampleName(baseLower)) return null;
|
|
1621
|
+
if (SKIP_FILES.has(baseLower)) return null;
|
|
1622
|
+
if (isEnvExampleName(baseLower)) return "env-example";
|
|
1623
|
+
const exact = EXACT_BASENAME[baseLower] ?? EXACT_BASENAME[base];
|
|
1624
|
+
if (exact) return exact;
|
|
1625
|
+
for (const { re, category } of BASENAME_PREFIX) {
|
|
1626
|
+
if (re.test(base)) return category;
|
|
1627
|
+
}
|
|
1628
|
+
for (const { re, category } of PATH_PATTERNS) {
|
|
1629
|
+
if (re.test(rel2)) return category;
|
|
1630
|
+
}
|
|
1631
|
+
const ext = path17.extname(baseLower);
|
|
1632
|
+
if (DOC_EXTENSIONS.has(ext)) return ext === ".txt" ? "text" : "markdown";
|
|
1633
|
+
if (API_EXTENSIONS.has(ext)) return "api-contract";
|
|
1634
|
+
if ((ext === ".yaml" || ext === ".yml" || ext === ".json") && /openapi|swagger|asyncapi/i.test(base)) {
|
|
1635
|
+
return "api-contract";
|
|
1636
|
+
}
|
|
1637
|
+
if (/^docker-compose\..+\.ya?ml$/i.test(base) || /^compose\..+\.ya?ml$/i.test(base)) {
|
|
1638
|
+
return "compose";
|
|
1639
|
+
}
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
function isEnvExampleName(baseLower) {
|
|
1643
|
+
if (ENV_EXAMPLE_NAMES.has(baseLower)) return true;
|
|
1644
|
+
if (/^(\.)?env\.(example|sample|template)(\.|$)/i.test(baseLower)) return true;
|
|
1645
|
+
if (/^example\.env/i.test(baseLower)) return true;
|
|
1646
|
+
return false;
|
|
1647
|
+
}
|
|
1648
|
+
function discoverDocs(options) {
|
|
1649
|
+
const root = path17.resolve(options.root);
|
|
1650
|
+
const maxFiles = options.maxFiles ?? DOC_MAX_FILES;
|
|
1651
|
+
const rootIg = buildRootIgnore2(root, options.exclude ?? []);
|
|
1652
|
+
const scopeAbs = (options.paths?.length ? options.paths.map((p) => path17.resolve(root, p)) : [root]).filter(
|
|
1653
|
+
(p) => fs15.existsSync(p)
|
|
1654
|
+
);
|
|
1655
|
+
const found = /* @__PURE__ */ new Map();
|
|
1656
|
+
const consider = (abs) => {
|
|
1657
|
+
if (found.size >= maxFiles) return;
|
|
1658
|
+
const rel2 = toPosix2(path17.relative(root, abs));
|
|
1659
|
+
if (!rel2 || rel2.startsWith("..")) return;
|
|
1660
|
+
if (rootIg.ignores(rel2)) return;
|
|
1661
|
+
const base = path17.basename(abs);
|
|
1662
|
+
if (SKIP_FILES.has(base.toLowerCase())) return;
|
|
1663
|
+
const category = classifyProjectContext(rel2);
|
|
1664
|
+
if (!category) return;
|
|
1665
|
+
try {
|
|
1666
|
+
const st = fs15.statSync(abs);
|
|
1667
|
+
if (!st.isFile() || st.size > DOC_FILE_MAX_BYTES) return;
|
|
1668
|
+
if (st.size === 0) return;
|
|
1669
|
+
} catch {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
found.set(rel2, { rel: rel2, abs, kind: category, category });
|
|
1673
|
+
};
|
|
1674
|
+
const walk = (dir, depth) => {
|
|
1675
|
+
if (found.size >= maxFiles || depth > 12) return;
|
|
1676
|
+
let entries;
|
|
1677
|
+
try {
|
|
1678
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
1679
|
+
} catch {
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1683
|
+
if (found.size >= maxFiles) return;
|
|
1684
|
+
const abs = path17.join(dir, entry.name);
|
|
1685
|
+
const rel2 = toPosix2(path17.relative(root, abs));
|
|
1686
|
+
if (entry.isDirectory()) {
|
|
1687
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1688
|
+
if (rel2 && rootIg.ignores(`${rel2}/`) && !rel2.startsWith(".github")) continue;
|
|
1689
|
+
walk(abs, depth + 1);
|
|
1690
|
+
} else if (entry.isFile()) {
|
|
1691
|
+
consider(abs);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
for (const scope of scopeAbs) {
|
|
1696
|
+
try {
|
|
1697
|
+
const st = fs15.statSync(scope);
|
|
1698
|
+
if (st.isDirectory()) walk(scope, 0);
|
|
1699
|
+
else if (st.isFile()) consider(scope);
|
|
1700
|
+
} catch {
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return [...found.values()].sort((a, b) => a.rel.localeCompare(b.rel));
|
|
1704
|
+
}
|
|
1705
|
+
function prepareDocBody(rel2, raw, category) {
|
|
1706
|
+
const scrubbed = redactSecrets(raw);
|
|
1707
|
+
if (scrubbed.length <= DOC_BODY_MAX_CHARS) return scrubbed;
|
|
1708
|
+
if (category === "manifest" && path17.basename(rel2).toLowerCase() === "package.json") {
|
|
1709
|
+
return summarizePackageJson(scrubbed);
|
|
1710
|
+
}
|
|
1711
|
+
if (category === "api-contract" && /\.(ya?ml|json)$/i.test(rel2)) {
|
|
1712
|
+
return scrubbed.slice(0, DOC_BODY_MAX_CHARS);
|
|
1713
|
+
}
|
|
1714
|
+
if (category === "ci") {
|
|
1715
|
+
return scrubbed.slice(0, DOC_BODY_MAX_CHARS);
|
|
1716
|
+
}
|
|
1717
|
+
return scrubbed.slice(0, DOC_BODY_MAX_CHARS);
|
|
1718
|
+
}
|
|
1719
|
+
function summarizePackageJson(raw) {
|
|
1720
|
+
try {
|
|
1721
|
+
const o = JSON.parse(raw);
|
|
1722
|
+
const keep = {};
|
|
1723
|
+
for (const k of [
|
|
1724
|
+
"name",
|
|
1725
|
+
"version",
|
|
1726
|
+
"description",
|
|
1727
|
+
"private",
|
|
1728
|
+
"type",
|
|
1729
|
+
"packageManager",
|
|
1730
|
+
"engines",
|
|
1731
|
+
"workspaces",
|
|
1732
|
+
"scripts",
|
|
1733
|
+
"dependencies",
|
|
1734
|
+
"devDependencies",
|
|
1735
|
+
"peerDependencies",
|
|
1736
|
+
"optionalDependencies",
|
|
1737
|
+
"main",
|
|
1738
|
+
"bin",
|
|
1739
|
+
"exports"
|
|
1740
|
+
]) {
|
|
1741
|
+
if (o[k] !== void 0) keep[k] = o[k];
|
|
1742
|
+
}
|
|
1743
|
+
const s = JSON.stringify(keep, null, 2);
|
|
1744
|
+
return s.length <= DOC_BODY_MAX_CHARS ? s : s.slice(0, DOC_BODY_MAX_CHARS);
|
|
1745
|
+
} catch {
|
|
1746
|
+
return raw.slice(0, DOC_BODY_MAX_CHARS);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
function documentNodesFromDocs(docs) {
|
|
1750
|
+
const nodes = [];
|
|
1751
|
+
for (const d of docs) {
|
|
1752
|
+
let raw = "";
|
|
1753
|
+
try {
|
|
1754
|
+
raw = fs15.readFileSync(d.abs, "utf8");
|
|
1755
|
+
} catch {
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1758
|
+
const category = d.category ?? d.kind;
|
|
1759
|
+
const scrubbed = prepareDocBody(d.rel, raw, category);
|
|
1760
|
+
if (!scrubbed.trim()) continue;
|
|
1761
|
+
const name = path17.basename(d.rel);
|
|
1762
|
+
const qualifiedName = d.rel.replace(/\\/g, "/");
|
|
1763
|
+
const id = nodeId({
|
|
1764
|
+
kind: "document",
|
|
1765
|
+
qualifiedName,
|
|
1766
|
+
file: qualifiedName,
|
|
1767
|
+
signature: category
|
|
1768
|
+
});
|
|
1769
|
+
const lineCount = Math.max(1, scrubbed.split("\n").length);
|
|
1770
|
+
const langTag = category === "markdown" ? "md" : category === "env-example" ? "env" : category === "manifest" ? "manifest" : category === "docker" || category === "compose" ? "docker" : category === "ci" ? "ci" : category === "api-contract" ? "api" : category === "build-config" ? "config" : category === "task-runner" ? "task" : category === "workspace" ? "workspace" : category === "infra" ? "infra" : "txt";
|
|
1771
|
+
nodes.push({
|
|
1772
|
+
id,
|
|
1773
|
+
kind: "document",
|
|
1774
|
+
name,
|
|
1775
|
+
qualifiedName,
|
|
1776
|
+
file: qualifiedName,
|
|
1777
|
+
span: { start: 1, end: lineCount },
|
|
1778
|
+
lang: langTag,
|
|
1779
|
+
signature: `document:${category}`,
|
|
1780
|
+
doc: scrubbed,
|
|
1781
|
+
importance: IMPORTANCE[category] ?? 0.05,
|
|
1782
|
+
centrality: { degree: 0, pagerank: 0, betweenness: 0, eigenvector: 0 },
|
|
1783
|
+
area: -1,
|
|
1784
|
+
isHub: false,
|
|
1785
|
+
tested: null
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
return nodes.sort((a, b) => a.file.localeCompare(b.file) || a.id.localeCompare(b.id));
|
|
1789
|
+
}
|
|
2150
1790
|
|
|
2151
1791
|
// src/engine/build.ts
|
|
2152
1792
|
async function buildGraph(options) {
|
|
2153
1793
|
const start = nowMs();
|
|
2154
|
-
const root =
|
|
1794
|
+
const root = path17.resolve(options.root);
|
|
2155
1795
|
const files = discover({
|
|
2156
1796
|
root,
|
|
2157
1797
|
only: options.only,
|
|
@@ -2179,7 +1819,7 @@ async function buildGraph(options) {
|
|
|
2179
1819
|
for (const file of files) {
|
|
2180
1820
|
let stat;
|
|
2181
1821
|
try {
|
|
2182
|
-
stat =
|
|
1822
|
+
stat = fs15.statSync(file.abs);
|
|
2183
1823
|
} catch {
|
|
2184
1824
|
continue;
|
|
2185
1825
|
}
|
|
@@ -2197,7 +1837,7 @@ async function buildGraph(options) {
|
|
|
2197
1837
|
}
|
|
2198
1838
|
let hash = "";
|
|
2199
1839
|
try {
|
|
2200
|
-
hash = hashBytes(
|
|
1840
|
+
hash = hashBytes(fs15.readFileSync(file.abs));
|
|
2201
1841
|
} catch {
|
|
2202
1842
|
continue;
|
|
2203
1843
|
}
|
|
@@ -2224,7 +1864,7 @@ async function buildGraph(options) {
|
|
|
2224
1864
|
);
|
|
2225
1865
|
const warnings = [...buildWarnings, ...parses.flatMap((p) => p.warnings ?? [])];
|
|
2226
1866
|
const moduleResolver = buildModuleResolver(root, new Set(parses.map((p) => p.rel)));
|
|
2227
|
-
const resolved =
|
|
1867
|
+
const resolved = resolve(parses, moduleResolver);
|
|
2228
1868
|
checkMemoryBudget("resolve", limits.memoryBudgetMb);
|
|
2229
1869
|
const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
|
|
2230
1870
|
let edges = resolved.edges;
|
|
@@ -2261,9 +1901,25 @@ async function buildGraph(options) {
|
|
|
2261
1901
|
let nodes = linked.nodes;
|
|
2262
1902
|
const coverage = options.noCoverage ? null : loadCoverage(root, options.coverage);
|
|
2263
1903
|
if (coverage) nodes = applyCoverage(nodes, coverage);
|
|
1904
|
+
const docs = discoverDocs({
|
|
1905
|
+
root,
|
|
1906
|
+
exclude: options.exclude,
|
|
1907
|
+
paths: options.paths
|
|
1908
|
+
});
|
|
1909
|
+
for (const d of docs) {
|
|
1910
|
+
try {
|
|
1911
|
+
const st = fs15.statSync(d.abs);
|
|
1912
|
+
const hash = hashBytes(fs15.readFileSync(d.abs));
|
|
1913
|
+
hashes.set(d.rel, hash);
|
|
1914
|
+
fileStats.push({ rel: d.rel, size: st.size, mtimeMs: st.mtimeMs, hash });
|
|
1915
|
+
} catch {
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const docNodes = documentNodesFromDocs(docs);
|
|
1919
|
+
if (docNodes.length) nodes = [...nodes, ...docNodes];
|
|
2264
1920
|
const analysis = analyze(nodes, linked.edges, { cluster: options.cluster });
|
|
2265
1921
|
checkMemoryBudget("analysis", limits.memoryBudgetMb);
|
|
2266
|
-
const languages = [
|
|
1922
|
+
const languages = [.../* @__PURE__ */ new Set([...parses.map((p) => p.lang), ...docNodes.map((n) => n.lang)])].sort();
|
|
2267
1923
|
const edgeKinds = [...new Set(analysis.edges.map((e) => e.kind))].sort();
|
|
2268
1924
|
const corpusHash = computeCorpusHash(parses, hashes);
|
|
2269
1925
|
const nodeKindById = new Map(analysis.nodes.map((n) => [n.id, n.kind]));
|
|
@@ -2289,7 +1945,7 @@ async function buildGraph(options) {
|
|
|
2289
1945
|
toolchain
|
|
2290
1946
|
},
|
|
2291
1947
|
meta: {
|
|
2292
|
-
root:
|
|
1948
|
+
root: path17.basename(root) === "" ? "." : ".",
|
|
2293
1949
|
languages,
|
|
2294
1950
|
counts: {
|
|
2295
1951
|
nodes: analysis.nodes.length,
|
|
@@ -2335,14 +1991,14 @@ function toRepoRel(p) {
|
|
|
2335
1991
|
function loadScipIndex(root, explicit) {
|
|
2336
1992
|
const candidates = [
|
|
2337
1993
|
explicit,
|
|
2338
|
-
|
|
2339
|
-
|
|
1994
|
+
path17.join(root, "index.scip"),
|
|
1995
|
+
path17.join(root, ".vibgrate", "index.scip")
|
|
2340
1996
|
].filter((p) => Boolean(p));
|
|
2341
1997
|
for (const file of candidates) {
|
|
2342
|
-
const abs =
|
|
2343
|
-
if (!
|
|
1998
|
+
const abs = path17.isAbsolute(file) ? file : path17.resolve(root, file);
|
|
1999
|
+
if (!fs15.existsSync(abs)) continue;
|
|
2344
2000
|
try {
|
|
2345
|
-
const index = decodeScipIndex(new Uint8Array(
|
|
2001
|
+
const index = decodeScipIndex(new Uint8Array(fs15.readFileSync(abs)));
|
|
2346
2002
|
if (index.documents.length) {
|
|
2347
2003
|
return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
|
|
2348
2004
|
}
|
|
@@ -2384,188 +2040,17 @@ function computeToolchain(grammars, resolvers) {
|
|
|
2384
2040
|
};
|
|
2385
2041
|
}
|
|
2386
2042
|
function computeCorpusHash(parses, hashes) {
|
|
2387
|
-
const
|
|
2043
|
+
const fromParses = parses.map((p) => [p.rel, hashes.get(p.rel) ?? p.hash]);
|
|
2044
|
+
const fromHashes = [...hashes.entries()].map(([rel2, hash]) => [rel2, hash]);
|
|
2045
|
+
const byRel = /* @__PURE__ */ new Map();
|
|
2046
|
+
for (const [rel2, hash] of [...fromParses, ...fromHashes]) byRel.set(rel2, hash);
|
|
2047
|
+
const list = [...byRel.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
2388
2048
|
return hashString(canonicalize(list));
|
|
2389
2049
|
}
|
|
2390
2050
|
function nowMs() {
|
|
2391
2051
|
return Number(process.hrtime.bigint() / 1000n) / 1e3;
|
|
2392
2052
|
}
|
|
2393
2053
|
|
|
2394
|
-
// src/engine/report.ts
|
|
2395
|
-
function renderReport(graph) {
|
|
2396
|
-
const { meta, provenance, nodes, edges } = graph;
|
|
2397
|
-
const lines = [];
|
|
2398
|
-
lines.push("# Code Graph Report");
|
|
2399
|
-
lines.push("");
|
|
2400
|
-
lines.push(`Generated by \`vg\` ${provenance.version} \xB7 schema \`${graph.schemaVersion}\``);
|
|
2401
|
-
lines.push("");
|
|
2402
|
-
lines.push("## Summary");
|
|
2403
|
-
lines.push("");
|
|
2404
|
-
lines.push(`- **Nodes:** ${meta.counts.nodes.toLocaleString("en-US")}`);
|
|
2405
|
-
lines.push(`- **Edges:** ${meta.counts.edges.toLocaleString("en-US")}`);
|
|
2406
|
-
lines.push(`- **Areas:** ${meta.counts.areas.toLocaleString("en-US")}`);
|
|
2407
|
-
lines.push(`- **Languages:** ${meta.languages.join(", ") || "\u2014"}`);
|
|
2408
|
-
lines.push(`- **Clustering:** ${meta.cluster}`);
|
|
2409
|
-
lines.push(`- **Resolvers:** ${provenance.resolver.join(", ")}`);
|
|
2410
|
-
lines.push(`- **Edge kinds:** ${meta.edgeKinds.join(", ") || "\u2014"}`);
|
|
2411
|
-
lines.push("");
|
|
2412
|
-
const fileNodes = nodes.filter((n) => n.kind === "file");
|
|
2413
|
-
const defsByFile = /* @__PURE__ */ new Map();
|
|
2414
|
-
for (const n of nodes) {
|
|
2415
|
-
if (n.kind === "file" || n.kind === "external") continue;
|
|
2416
|
-
defsByFile.set(n.file, (defsByFile.get(n.file) ?? 0) + 1);
|
|
2417
|
-
}
|
|
2418
|
-
const topFiles = [...defsByFile.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 15);
|
|
2419
|
-
if (topFiles.length) {
|
|
2420
|
-
lines.push("## Largest files (by definitions)");
|
|
2421
|
-
lines.push("");
|
|
2422
|
-
lines.push("| File | Definitions |");
|
|
2423
|
-
lines.push("| --- | ---: |");
|
|
2424
|
-
for (const [file, count] of topFiles) lines.push(`| \`${file}\` | ${count} |`);
|
|
2425
|
-
lines.push("");
|
|
2426
|
-
}
|
|
2427
|
-
const topNodes = nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 15);
|
|
2428
|
-
if (topNodes.length) {
|
|
2429
|
-
lines.push("## Most-connected definitions");
|
|
2430
|
-
lines.push("");
|
|
2431
|
-
lines.push("| Symbol | Kind | File | Importance |");
|
|
2432
|
-
lines.push("| --- | --- | --- | ---: |");
|
|
2433
|
-
for (const n of topNodes) {
|
|
2434
|
-
lines.push(
|
|
2435
|
-
`| \`${n.qualifiedName}\` | ${n.kind} | \`${n.file}\`:${n.span.start} | ${n.importance.toFixed(3)} |`
|
|
2436
|
-
);
|
|
2437
|
-
}
|
|
2438
|
-
lines.push("");
|
|
2439
|
-
}
|
|
2440
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
2441
|
-
for (const e of edges) byKind.set(e.kind, (byKind.get(e.kind) ?? 0) + 1);
|
|
2442
|
-
if (byKind.size) {
|
|
2443
|
-
lines.push("## Edges by kind");
|
|
2444
|
-
lines.push("");
|
|
2445
|
-
lines.push("| Kind | Count |");
|
|
2446
|
-
lines.push("| --- | ---: |");
|
|
2447
|
-
for (const [kind, count] of [...byKind.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
2448
|
-
lines.push(`| ${kind} | ${count} |`);
|
|
2449
|
-
}
|
|
2450
|
-
lines.push("");
|
|
2451
|
-
}
|
|
2452
|
-
lines.push("---");
|
|
2453
|
-
lines.push("");
|
|
2454
|
-
lines.push(`_Files: ${fileNodes.length} \xB7 Corpus: \`${provenance.corpusHash.slice(0, 16)}\u2026\`_`);
|
|
2455
|
-
lines.push("");
|
|
2456
|
-
return lines.join("\n");
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
|
-
// src/engine/html.ts
|
|
2460
|
-
function renderHtml(graph) {
|
|
2461
|
-
const top = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 50);
|
|
2462
|
-
const rows = top.map(
|
|
2463
|
-
(n) => `<tr><td><code>${esc(n.qualifiedName)}</code></td><td>${esc(n.kind)}</td><td><code>${esc(n.file)}</code>:${n.span.start}</td><td>${n.importance.toFixed(3)}</td></tr>`
|
|
2464
|
-
).join("\n");
|
|
2465
|
-
const embedded = stableStringify(graph, 0);
|
|
2466
|
-
return `<!doctype html>
|
|
2467
|
-
<html lang="en">
|
|
2468
|
-
<head>
|
|
2469
|
-
<meta charset="utf-8">
|
|
2470
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2471
|
-
<title>vg \xB7 code graph</title>
|
|
2472
|
-
<style>
|
|
2473
|
-
:root { color-scheme: light dark; }
|
|
2474
|
-
body { font: 14px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; }
|
|
2475
|
-
h1 { font-size: 1.4rem; } h2 { font-size: 1.1rem; margin-top: 2rem; }
|
|
2476
|
-
table { border-collapse: collapse; width: 100%; }
|
|
2477
|
-
th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #8884; }
|
|
2478
|
-
td:last-child, th:last-child { text-align: right; }
|
|
2479
|
-
.meta { color: #888; } code { font-size: 0.9em; }
|
|
2480
|
-
</style>
|
|
2481
|
-
</head>
|
|
2482
|
-
<body>
|
|
2483
|
-
<h1>vg \xB7 code graph</h1>
|
|
2484
|
-
<p class="meta">${graph.meta.counts.nodes} nodes \xB7 ${graph.meta.counts.edges} edges \xB7
|
|
2485
|
-
${graph.meta.counts.areas} areas \xB7 ${esc(graph.meta.languages.join(", ") || "\u2014")} \xB7
|
|
2486
|
-
clustering: ${esc(graph.meta.cluster)} \xB7 vg ${esc(graph.provenance.version)}</p>
|
|
2487
|
-
<h2>Most-connected definitions</h2>
|
|
2488
|
-
<table>
|
|
2489
|
-
<thead><tr><th>Symbol</th><th>Kind</th><th>Location</th><th>Importance</th></tr></thead>
|
|
2490
|
-
<tbody>
|
|
2491
|
-
${rows}
|
|
2492
|
-
</tbody>
|
|
2493
|
-
</table>
|
|
2494
|
-
<p class="meta">Interactive WebGL view arrives in Phase 1. The full graph is embedded below for tooling.</p>
|
|
2495
|
-
<script type="application/json" id="vg-graph">
|
|
2496
|
-
${embedded}
|
|
2497
|
-
</script>
|
|
2498
|
-
</body>
|
|
2499
|
-
</html>
|
|
2500
|
-
`;
|
|
2501
|
-
}
|
|
2502
|
-
function esc(s) {
|
|
2503
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2504
|
-
}
|
|
2505
|
-
function vibgrateDir(root) {
|
|
2506
|
-
return path2.join(root, ".vibgrate");
|
|
2507
|
-
}
|
|
2508
|
-
var DEFAULT_GITIGNORE = [
|
|
2509
|
-
"# Created once by vg \u2014 local graph artifacts stay out of git by default.",
|
|
2510
|
-
"# Run `vg share` to commit the map for your team (it rewrites this file).",
|
|
2511
|
-
"# vg never touches an existing .vibgrate/.gitignore: edit it (or leave it",
|
|
2512
|
-
"# empty) to manage these ignores yourself.",
|
|
2513
|
-
".gitignore",
|
|
2514
|
-
"cache/",
|
|
2515
|
-
"graph.json",
|
|
2516
|
-
"graph.html",
|
|
2517
|
-
"GRAPH_REPORT.md",
|
|
2518
|
-
"facts.jsonl",
|
|
2519
|
-
"mcp-navigation.json"
|
|
2520
|
-
];
|
|
2521
|
-
function ensureVibgrateGitignore(root) {
|
|
2522
|
-
const file = path2.join(vibgrateDir(root), ".gitignore");
|
|
2523
|
-
if (fs18.existsSync(file)) return;
|
|
2524
|
-
fs18.mkdirSync(vibgrateDir(root), { recursive: true });
|
|
2525
|
-
fs18.writeFileSync(file, `${DEFAULT_GITIGNORE.join("\n")}
|
|
2526
|
-
`);
|
|
2527
|
-
}
|
|
2528
|
-
function defaultGraphPath(root) {
|
|
2529
|
-
return path2.join(vibgrateDir(root), "graph.json");
|
|
2530
|
-
}
|
|
2531
|
-
function writeArtifacts(graph, options) {
|
|
2532
|
-
const dir = vibgrateDir(options.root);
|
|
2533
|
-
fs18.mkdirSync(dir, { recursive: true });
|
|
2534
|
-
ensureVibgrateGitignore(options.root);
|
|
2535
|
-
const graphPath = options.graphPath ?? defaultGraphPath(options.root);
|
|
2536
|
-
fs18.mkdirSync(path2.dirname(graphPath), { recursive: true });
|
|
2537
|
-
const tmp = `${graphPath}.${process.pid}.tmp`;
|
|
2538
|
-
try {
|
|
2539
|
-
fs18.writeFileSync(tmp, serializeGraph(graph));
|
|
2540
|
-
fs18.renameSync(tmp, graphPath);
|
|
2541
|
-
} catch (err) {
|
|
2542
|
-
fs18.rmSync(tmp, { force: true });
|
|
2543
|
-
throw err;
|
|
2544
|
-
}
|
|
2545
|
-
const written = { graphPath };
|
|
2546
|
-
if (options.report !== false) {
|
|
2547
|
-
const reportPath = path2.join(dir, "GRAPH_REPORT.md");
|
|
2548
|
-
fs18.writeFileSync(reportPath, renderReport(graph));
|
|
2549
|
-
written.reportPath = reportPath;
|
|
2550
|
-
}
|
|
2551
|
-
if (options.html !== false) {
|
|
2552
|
-
const htmlPath = path2.join(dir, "graph.html");
|
|
2553
|
-
fs18.writeFileSync(htmlPath, renderHtml(graph));
|
|
2554
|
-
written.htmlPath = htmlPath;
|
|
2555
|
-
}
|
|
2556
|
-
if (graph.facts && graph.facts.length) {
|
|
2557
|
-
const factsPath = path2.join(dir, "facts.jsonl");
|
|
2558
|
-
fs18.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
|
|
2559
|
-
written.factsPath = factsPath;
|
|
2560
|
-
}
|
|
2561
|
-
return written;
|
|
2562
|
-
}
|
|
2563
|
-
function loadGraph(root, graphPath) {
|
|
2564
|
-
const file = graphPath ?? defaultGraphPath(root);
|
|
2565
|
-
if (!fs18.existsSync(file)) return null;
|
|
2566
|
-
return parseGraph(fs18.readFileSync(file, "utf8"));
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
2054
|
// src/engine/verify.ts
|
|
2570
2055
|
var PINNED = "1970-01-01T00:00:00.000Z";
|
|
2571
2056
|
async function verifyDeterminism(opts) {
|
|
@@ -2631,21 +2116,21 @@ function usageError(message) {
|
|
|
2631
2116
|
}
|
|
2632
2117
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2633
2118
|
function snapshotPath(root) {
|
|
2634
|
-
return
|
|
2119
|
+
return path17.join(cacheDir(root), "freshness.json");
|
|
2635
2120
|
}
|
|
2636
2121
|
function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
|
|
2637
2122
|
const files = {};
|
|
2638
2123
|
for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
|
|
2639
2124
|
const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
|
|
2640
2125
|
try {
|
|
2641
|
-
|
|
2642
|
-
|
|
2126
|
+
fs15.mkdirSync(cacheDir(root), { recursive: true });
|
|
2127
|
+
fs15.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2643
2128
|
} catch {
|
|
2644
2129
|
}
|
|
2645
2130
|
}
|
|
2646
2131
|
function loadSnapshot(root) {
|
|
2647
2132
|
try {
|
|
2648
|
-
const loaded = JSON.parse(
|
|
2133
|
+
const loaded = JSON.parse(fs15.readFileSync(snapshotPath(root), "utf8"));
|
|
2649
2134
|
if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
|
|
2650
2135
|
return { ...loaded, scope: loaded.scope ?? {} };
|
|
2651
2136
|
}
|
|
@@ -2681,7 +2166,7 @@ function probeFreshness(root) {
|
|
|
2681
2166
|
}
|
|
2682
2167
|
let stat;
|
|
2683
2168
|
try {
|
|
2684
|
-
stat =
|
|
2169
|
+
stat = fs15.statSync(file.abs);
|
|
2685
2170
|
} catch {
|
|
2686
2171
|
drift.removed.push(file.rel);
|
|
2687
2172
|
continue;
|
|
@@ -2689,7 +2174,7 @@ function probeFreshness(root) {
|
|
|
2689
2174
|
if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
|
|
2690
2175
|
let hash = "";
|
|
2691
2176
|
try {
|
|
2692
|
-
hash = hashBytes(
|
|
2177
|
+
hash = hashBytes(fs15.readFileSync(file.abs));
|
|
2693
2178
|
} catch {
|
|
2694
2179
|
drift.removed.push(file.rel);
|
|
2695
2180
|
continue;
|
|
@@ -2706,7 +2191,7 @@ function probeFreshness(root) {
|
|
|
2706
2191
|
}
|
|
2707
2192
|
if (absorbed && !hasDrift(drift)) {
|
|
2708
2193
|
try {
|
|
2709
|
-
|
|
2194
|
+
fs15.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2710
2195
|
} catch {
|
|
2711
2196
|
}
|
|
2712
2197
|
}
|
|
@@ -2787,10 +2272,10 @@ function testsToRun(graph, rootId, depth = 4) {
|
|
|
2787
2272
|
};
|
|
2788
2273
|
}
|
|
2789
2274
|
function detectRunner(root, lang) {
|
|
2790
|
-
const pkgPath =
|
|
2791
|
-
if (
|
|
2275
|
+
const pkgPath = path17.join(root, "package.json");
|
|
2276
|
+
if (fs15.existsSync(pkgPath)) {
|
|
2792
2277
|
try {
|
|
2793
|
-
const pkg = JSON.parse(
|
|
2278
|
+
const pkg = JSON.parse(fs15.readFileSync(pkgPath, "utf8"));
|
|
2794
2279
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2795
2280
|
if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
|
|
2796
2281
|
if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
|
|
@@ -2798,10 +2283,10 @@ function detectRunner(root, lang) {
|
|
|
2798
2283
|
} catch {
|
|
2799
2284
|
}
|
|
2800
2285
|
}
|
|
2801
|
-
if (lang === "py" ||
|
|
2286
|
+
if (lang === "py" || fs15.existsSync(path17.join(root, "pyproject.toml")) || fs15.existsSync(path17.join(root, "pytest.ini"))) {
|
|
2802
2287
|
return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
|
|
2803
2288
|
}
|
|
2804
|
-
if (lang === "go" ||
|
|
2289
|
+
if (lang === "go" || fs15.existsSync(path17.join(root, "go.mod"))) {
|
|
2805
2290
|
return { name: "go test", command: () => "go test ./..." };
|
|
2806
2291
|
}
|
|
2807
2292
|
if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
|
|
@@ -2812,82 +2297,16 @@ function detectRunner(root, lang) {
|
|
|
2812
2297
|
}
|
|
2813
2298
|
function hasFile(root, re) {
|
|
2814
2299
|
try {
|
|
2815
|
-
return
|
|
2300
|
+
return fs15.readdirSync(root).some((f) => re.test(f));
|
|
2816
2301
|
} catch {
|
|
2817
2302
|
return false;
|
|
2818
2303
|
}
|
|
2819
2304
|
}
|
|
2820
|
-
function discoverModels(home = os.homedir()) {
|
|
2821
|
-
return [...ollama(home), ...lmStudio(home), ...looseGguf(home)].sort(
|
|
2822
|
-
(a, b) => a.runtime.localeCompare(b.runtime) || a.name.localeCompare(b.name)
|
|
2823
|
-
);
|
|
2824
|
-
}
|
|
2825
|
-
function ollama(home) {
|
|
2826
|
-
const base = path2.join(home, ".ollama", "models", "manifests");
|
|
2827
|
-
const out = [];
|
|
2828
|
-
walk(base, 4, (file) => {
|
|
2829
|
-
const rel2 = path2.relative(base, file).split(path2.sep);
|
|
2830
|
-
if (rel2.length >= 2) {
|
|
2831
|
-
const tag = rel2[rel2.length - 1];
|
|
2832
|
-
const model = rel2[rel2.length - 2];
|
|
2833
|
-
out.push({ runtime: "ollama", name: `${model}:${tag}`, path: file });
|
|
2834
|
-
}
|
|
2835
|
-
});
|
|
2836
|
-
return dedupe(out);
|
|
2837
|
-
}
|
|
2838
|
-
function lmStudio(home) {
|
|
2839
|
-
const bases = [path2.join(home, ".lmstudio", "models"), path2.join(home, ".cache", "lm-studio", "models")];
|
|
2840
|
-
const out = [];
|
|
2841
|
-
for (const base of bases) {
|
|
2842
|
-
walk(base, 5, (file) => {
|
|
2843
|
-
if (!file.endsWith(".gguf")) return;
|
|
2844
|
-
const rel2 = path2.relative(base, file).replace(/\\/g, "/");
|
|
2845
|
-
out.push({ runtime: "lm-studio", name: rel2, path: file });
|
|
2846
|
-
});
|
|
2847
|
-
}
|
|
2848
|
-
return dedupe(out);
|
|
2849
|
-
}
|
|
2850
|
-
function looseGguf(home) {
|
|
2851
|
-
const bases = [path2.join(home, "models"), path2.join(home, ".cache", "huggingface")];
|
|
2852
|
-
const out = [];
|
|
2853
|
-
for (const base of bases) {
|
|
2854
|
-
walk(base, 4, (file) => {
|
|
2855
|
-
if (!file.endsWith(".gguf")) return;
|
|
2856
|
-
out.push({ runtime: "gguf", name: path2.basename(file), path: file });
|
|
2857
|
-
});
|
|
2858
|
-
}
|
|
2859
|
-
return dedupe(out);
|
|
2860
|
-
}
|
|
2861
|
-
function walk(dir, depth, onFile) {
|
|
2862
|
-
if (depth < 0) return;
|
|
2863
|
-
let entries;
|
|
2864
|
-
try {
|
|
2865
|
-
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
2866
|
-
} catch {
|
|
2867
|
-
return;
|
|
2868
|
-
}
|
|
2869
|
-
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2870
|
-
const abs = path2.join(dir, e.name);
|
|
2871
|
-
if (e.isDirectory()) walk(abs, depth - 1, onFile);
|
|
2872
|
-
else if (e.isFile()) onFile(abs);
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
function dedupe(models) {
|
|
2876
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2877
|
-
const out = [];
|
|
2878
|
-
for (const m of models) {
|
|
2879
|
-
const key = `${m.runtime}:${m.name}`;
|
|
2880
|
-
if (seen.has(key)) continue;
|
|
2881
|
-
seen.add(key);
|
|
2882
|
-
out.push(m);
|
|
2883
|
-
}
|
|
2884
|
-
return out;
|
|
2885
|
-
}
|
|
2886
2305
|
function findGitRoot(startDir = process.cwd()) {
|
|
2887
|
-
let dir =
|
|
2306
|
+
let dir = path17.resolve(startDir);
|
|
2888
2307
|
for (; ; ) {
|
|
2889
|
-
if (
|
|
2890
|
-
const parent =
|
|
2308
|
+
if (fs15.existsSync(path17.join(dir, ".git"))) return dir;
|
|
2309
|
+
const parent = path17.dirname(dir);
|
|
2891
2310
|
if (parent === dir) return null;
|
|
2892
2311
|
dir = parent;
|
|
2893
2312
|
}
|
|
@@ -2898,19 +2317,19 @@ function normalizeEntry(line) {
|
|
|
2898
2317
|
function ensureGitignored(entry, startDir = process.cwd()) {
|
|
2899
2318
|
const root = findGitRoot(startDir);
|
|
2900
2319
|
if (!root) return { status: "not-a-repo", entry };
|
|
2901
|
-
const gitignorePath =
|
|
2320
|
+
const gitignorePath = path17.join(root, ".gitignore");
|
|
2902
2321
|
const target = normalizeEntry(entry);
|
|
2903
2322
|
let existing = "";
|
|
2904
2323
|
let fileExisted = true;
|
|
2905
2324
|
try {
|
|
2906
|
-
existing =
|
|
2325
|
+
existing = fs15.readFileSync(gitignorePath, "utf8");
|
|
2907
2326
|
} catch {
|
|
2908
2327
|
fileExisted = false;
|
|
2909
2328
|
}
|
|
2910
2329
|
const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
|
|
2911
2330
|
if (alreadyPresent) return { status: "present", entry, gitignorePath };
|
|
2912
2331
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
2913
|
-
|
|
2332
|
+
fs15.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
|
|
2914
2333
|
`, "utf8");
|
|
2915
2334
|
return { status: fileExisted ? "added" : "created", entry, gitignorePath };
|
|
2916
2335
|
}
|
|
@@ -3064,14 +2483,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
3064
2483
|
console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
3065
2484
|
}
|
|
3066
2485
|
if (opts.write) {
|
|
3067
|
-
const writePath =
|
|
2486
|
+
const writePath = path17.resolve(opts.write);
|
|
3068
2487
|
await writeTextFile(writePath, dsn + "\n");
|
|
3069
2488
|
console.log("");
|
|
3070
2489
|
console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
3071
|
-
const root = findGitRoot(
|
|
3072
|
-
const rel2 = root ?
|
|
2490
|
+
const root = findGitRoot(path17.dirname(writePath));
|
|
2491
|
+
const rel2 = root ? path17.relative(root, writePath).split(path17.sep).join("/") : "";
|
|
3073
2492
|
if (root && rel2 && !rel2.startsWith("..")) {
|
|
3074
|
-
const res = ensureGitignored(rel2,
|
|
2493
|
+
const res = ensureGitignored(rel2, path17.dirname(writePath));
|
|
3075
2494
|
if (res.status === "created") {
|
|
3076
2495
|
console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
|
|
3077
2496
|
} else if (res.status === "added") {
|
|
@@ -3089,30 +2508,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
3089
2508
|
var STORE_DIRNAME = ".vibgrate";
|
|
3090
2509
|
var STORE_FILENAME = "credentials.json";
|
|
3091
2510
|
function homeCredentialsPath() {
|
|
3092
|
-
return
|
|
2511
|
+
return path17.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
|
|
3093
2512
|
}
|
|
3094
2513
|
function projectCredentialsPath(cwd = process.cwd()) {
|
|
3095
2514
|
const root = findGitRoot(cwd) ?? cwd;
|
|
3096
|
-
return
|
|
2515
|
+
return path17.join(root, STORE_DIRNAME, STORE_FILENAME);
|
|
3097
2516
|
}
|
|
3098
2517
|
function credentialsPath(opts = {}) {
|
|
3099
2518
|
const override = process.env.VIBGRATE_CREDENTIALS?.trim();
|
|
3100
|
-
if (override) return
|
|
2519
|
+
if (override) return path17.resolve(override);
|
|
3101
2520
|
const local = projectCredentialsPath(opts.cwd);
|
|
3102
2521
|
if (opts.local) return local;
|
|
3103
|
-
if (
|
|
2522
|
+
if (fs15.existsSync(local)) return local;
|
|
3104
2523
|
return homeCredentialsPath();
|
|
3105
2524
|
}
|
|
3106
2525
|
function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
|
|
3107
|
-
const rel2 =
|
|
3108
|
-
if (rel2 && !rel2.startsWith("..") && !
|
|
3109
|
-
return rel2.split(
|
|
2526
|
+
const rel2 = path17.relative(repoRoot, credsFile);
|
|
2527
|
+
if (rel2 && !rel2.startsWith("..") && !path17.isAbsolute(rel2)) {
|
|
2528
|
+
return rel2.split(path17.sep).join("/");
|
|
3110
2529
|
}
|
|
3111
2530
|
return `${STORE_DIRNAME}/${STORE_FILENAME}`;
|
|
3112
2531
|
}
|
|
3113
2532
|
function readStoredCredentials(opts = {}) {
|
|
3114
2533
|
try {
|
|
3115
|
-
const raw =
|
|
2534
|
+
const raw = fs15.readFileSync(credentialsPath(opts), "utf8");
|
|
3116
2535
|
const parsed = JSON.parse(raw);
|
|
3117
2536
|
return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
|
|
3118
2537
|
} catch {
|
|
@@ -3121,16 +2540,16 @@ function readStoredCredentials(opts = {}) {
|
|
|
3121
2540
|
}
|
|
3122
2541
|
function writeStoredCredentials(creds, opts = {}) {
|
|
3123
2542
|
const file = credentialsPath(opts);
|
|
3124
|
-
|
|
3125
|
-
|
|
2543
|
+
fs15.mkdirSync(path17.dirname(file), { recursive: true });
|
|
2544
|
+
fs15.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
3126
2545
|
try {
|
|
3127
|
-
|
|
2546
|
+
fs15.chmodSync(file, 384);
|
|
3128
2547
|
} catch {
|
|
3129
2548
|
}
|
|
3130
2549
|
}
|
|
3131
2550
|
function clearStoredCredentials(opts = {}) {
|
|
3132
2551
|
try {
|
|
3133
|
-
|
|
2552
|
+
fs15.rmSync(credentialsPath(opts));
|
|
3134
2553
|
return true;
|
|
3135
2554
|
} catch {
|
|
3136
2555
|
return false;
|
|
@@ -3501,7 +2920,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
3501
2920
|
if (opts.strict) process.exit(1);
|
|
3502
2921
|
return;
|
|
3503
2922
|
}
|
|
3504
|
-
const filePath =
|
|
2923
|
+
const filePath = path17.resolve(opts.file);
|
|
3505
2924
|
if (!await pathExists(filePath)) {
|
|
3506
2925
|
console.error(chalk.red(`Scan artifact not found: ${filePath}`));
|
|
3507
2926
|
console.error(chalk.dim('Run "vibgrate scan" first.'));
|
|
@@ -3562,935 +2981,935 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
3562
2981
|
if (opts.strict) process.exit(1);
|
|
3563
2982
|
}
|
|
3564
2983
|
});
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
2984
|
+
var MAX_FILE_BYTES = 1e6;
|
|
2985
|
+
var PREVIEW_CHARS = 120;
|
|
2986
|
+
var NUL = "\0";
|
|
2987
|
+
var PARALLEL_MIN_BYTES = 64e6;
|
|
2988
|
+
var PARALLEL_MIN_FILES = 2e3;
|
|
2989
|
+
var ROW_CAP = 1e4;
|
|
2990
|
+
async function scanCandidates(root, files, needle, opts) {
|
|
2991
|
+
const needleLower = needle.toLowerCase();
|
|
2992
|
+
if (!needleLower || files.length === 0) return { hits: [], total: 0, truncated: false };
|
|
2993
|
+
if (files.length < PARALLEL_MIN_FILES && !process.env.VG_PARALLEL_MIN_BYTES) {
|
|
2994
|
+
return opts.collectAll ? scanInline(root, files, needleLower, ROW_CAP, false, void 0) : scanInline(root, files, needleLower, opts.budget, true, void 0);
|
|
2995
|
+
}
|
|
2996
|
+
const { over, sizes } = measure(root, files);
|
|
2997
|
+
if (!over) {
|
|
2998
|
+
return opts.collectAll ? scanInline(root, files, needleLower, ROW_CAP, false, sizes) : scanInline(root, files, needleLower, opts.budget, true, sizes);
|
|
2999
|
+
}
|
|
3000
|
+
const out = await scanParallel(root, files, needleLower);
|
|
3001
|
+
if (!opts.collectAll && out.hits.length > opts.budget) {
|
|
3002
|
+
out.hits.length = opts.budget;
|
|
3003
|
+
out.truncated = true;
|
|
3571
3004
|
}
|
|
3005
|
+
return out;
|
|
3572
3006
|
}
|
|
3573
|
-
function
|
|
3574
|
-
|
|
3007
|
+
function parallelMinBytes() {
|
|
3008
|
+
const v = Number(process.env.VG_PARALLEL_MIN_BYTES);
|
|
3009
|
+
return Number.isFinite(v) && v >= 0 ? v : PARALLEL_MIN_BYTES;
|
|
3575
3010
|
}
|
|
3576
|
-
function
|
|
3577
|
-
const
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
};
|
|
3011
|
+
function measure(root, files) {
|
|
3012
|
+
const threshold = parallelMinBytes();
|
|
3013
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
3014
|
+
let total = 0;
|
|
3015
|
+
for (const rel2 of files) {
|
|
3016
|
+
let size;
|
|
3017
|
+
try {
|
|
3018
|
+
size = fs15.statSync(path17.join(root, rel2)).size;
|
|
3019
|
+
} catch {
|
|
3020
|
+
continue;
|
|
3587
3021
|
}
|
|
3022
|
+
sizes.set(rel2, size);
|
|
3023
|
+
if (size <= MAX_FILE_BYTES) total += size;
|
|
3024
|
+
if (total >= threshold) return { over: true, sizes };
|
|
3588
3025
|
}
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3026
|
+
return { over: false, sizes };
|
|
3027
|
+
}
|
|
3028
|
+
function matchLines(text, needleLower) {
|
|
3029
|
+
const low = text.toLowerCase();
|
|
3030
|
+
if (!low.includes(needleLower)) return [];
|
|
3031
|
+
const lowLines = low.split("\n");
|
|
3032
|
+
const rawLines = text.split("\n");
|
|
3033
|
+
const hits = [];
|
|
3034
|
+
for (let i = 0; i < lowLines.length; i++) {
|
|
3035
|
+
if (lowLines[i].includes(needleLower)) hits.push({ line: i + 1, preview: rawLines[i].trim().slice(0, PREVIEW_CHARS) });
|
|
3593
3036
|
}
|
|
3594
|
-
return
|
|
3037
|
+
return hits;
|
|
3595
3038
|
}
|
|
3596
|
-
function
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3039
|
+
function readCandidate(abs, knownSize) {
|
|
3040
|
+
try {
|
|
3041
|
+
const size = knownSize ?? fs15.statSync(abs).size;
|
|
3042
|
+
if (size > MAX_FILE_BYTES) return null;
|
|
3043
|
+
const text = fs15.readFileSync(abs, "utf8");
|
|
3044
|
+
return text.includes(NUL) ? null : text;
|
|
3045
|
+
} catch {
|
|
3046
|
+
return null;
|
|
3047
|
+
}
|
|
3600
3048
|
}
|
|
3601
|
-
function
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
const
|
|
3606
|
-
|
|
3049
|
+
function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
|
|
3050
|
+
const hits = [];
|
|
3051
|
+
let total = 0;
|
|
3052
|
+
let truncated = false;
|
|
3053
|
+
for (const rel2 of files) {
|
|
3054
|
+
const text = readCandidate(path17.join(root, rel2), sizes?.get(rel2));
|
|
3055
|
+
if (text === null) continue;
|
|
3056
|
+
for (const h of matchLines(text, needleLower)) {
|
|
3057
|
+
total++;
|
|
3058
|
+
if (hits.length < cap) hits.push({ file: rel2, line: h.line, preview: h.preview });
|
|
3059
|
+
else {
|
|
3060
|
+
truncated = true;
|
|
3061
|
+
if (stopAtCap) return { hits, total, truncated };
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
return { hits, total, truncated };
|
|
3607
3066
|
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3067
|
+
async function scanParallel(root, files, needleLower) {
|
|
3068
|
+
const n = Math.min(files.length, Math.max(2, Math.min(8, os.cpus().length - 1)));
|
|
3069
|
+
const shards = Array.from({ length: n }, () => []);
|
|
3070
|
+
files.forEach((f, i) => shards[i % n].push(f));
|
|
3071
|
+
let results;
|
|
3072
|
+
try {
|
|
3073
|
+
results = await Promise.all(shards.map((shard) => runWorker(root, shard, needleLower)));
|
|
3074
|
+
} catch {
|
|
3075
|
+
return scanInline(root, files, needleLower, ROW_CAP, false, void 0);
|
|
3076
|
+
}
|
|
3077
|
+
const hits = [];
|
|
3078
|
+
let total = 0;
|
|
3079
|
+
let truncated = false;
|
|
3080
|
+
for (const r of results) {
|
|
3081
|
+
total += r.total;
|
|
3082
|
+
truncated = truncated || r.truncated;
|
|
3083
|
+
for (const h of r.hits) hits.push(h);
|
|
3084
|
+
}
|
|
3085
|
+
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
3086
|
+
if (hits.length > ROW_CAP) {
|
|
3087
|
+
hits.length = ROW_CAP;
|
|
3088
|
+
truncated = true;
|
|
3089
|
+
}
|
|
3090
|
+
return { hits, total, truncated };
|
|
3624
3091
|
}
|
|
3625
|
-
|
|
3626
|
-
const
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
const
|
|
3630
|
-
|
|
3631
|
-
const
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3092
|
+
var WORKER_SOURCE = [
|
|
3093
|
+
"const { parentPort, workerData } = require('worker_threads');",
|
|
3094
|
+
"const fs = require('fs');",
|
|
3095
|
+
"const path = require('path');",
|
|
3096
|
+
`const MAX = ${MAX_FILE_BYTES};`,
|
|
3097
|
+
`const PREVIEW = ${PREVIEW_CHARS};`,
|
|
3098
|
+
`const CAP = ${ROW_CAP};`,
|
|
3099
|
+
"const NUL = String.fromCharCode(0);",
|
|
3100
|
+
"const NL = String.fromCharCode(10);",
|
|
3101
|
+
"const { root, files, needleLower } = workerData;",
|
|
3102
|
+
"const hits = []; let total = 0, truncated = false;",
|
|
3103
|
+
"for (const rel of files) {",
|
|
3104
|
+
" const abs = path.join(root, rel);",
|
|
3105
|
+
" let text;",
|
|
3106
|
+
' try { if (fs.statSync(abs).size > MAX) continue; text = fs.readFileSync(abs, "utf8"); } catch { continue; }',
|
|
3107
|
+
" if (text.includes(NUL)) continue;",
|
|
3108
|
+
" const low = text.toLowerCase();",
|
|
3109
|
+
" if (!low.includes(needleLower)) continue;",
|
|
3110
|
+
" const lowLines = low.split(NL); const rawLines = text.split(NL);",
|
|
3111
|
+
" for (let i = 0; i < lowLines.length; i++) {",
|
|
3112
|
+
" if (!lowLines[i].includes(needleLower)) continue;",
|
|
3113
|
+
" total++;",
|
|
3114
|
+
" if (hits.length < CAP) hits.push({ file: rel, line: i + 1, preview: rawLines[i].trim().slice(0, PREVIEW) });",
|
|
3115
|
+
" else truncated = true;",
|
|
3116
|
+
" }",
|
|
3117
|
+
"}",
|
|
3118
|
+
"parentPort.postMessage({ hits, total, truncated });"
|
|
3119
|
+
].join("\n");
|
|
3120
|
+
function runWorker(root, files, needleLower) {
|
|
3121
|
+
return new Promise((resolve11, reject) => {
|
|
3122
|
+
const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
|
|
3123
|
+
let settled = false;
|
|
3124
|
+
w.once("message", (m) => {
|
|
3125
|
+
settled = true;
|
|
3126
|
+
resolve11(m);
|
|
3127
|
+
});
|
|
3128
|
+
w.once("error", reject);
|
|
3129
|
+
w.once("exit", (code) => {
|
|
3130
|
+
if (!settled && code !== 0) reject(new Error(`literal-scan worker exited with code ${code}`));
|
|
3131
|
+
});
|
|
3641
3132
|
});
|
|
3642
|
-
return { ...inv, records, attribution: "git" };
|
|
3643
|
-
}
|
|
3644
|
-
function countTokens(text) {
|
|
3645
|
-
return encode(text).length;
|
|
3646
|
-
}
|
|
3647
|
-
function truncateToTokens(text, budget, marker = "\n\u2026(truncated)") {
|
|
3648
|
-
if (!Number.isFinite(budget) || budget <= 0) return { text, truncated: false, tokens: countTokens(text) };
|
|
3649
|
-
const tokens = encode(text);
|
|
3650
|
-
if (tokens.length <= budget) return { text, truncated: false, tokens: tokens.length };
|
|
3651
|
-
const markerTokens = encode(marker).length;
|
|
3652
|
-
const keep = Math.max(0, budget - markerTokens);
|
|
3653
|
-
let slice = decode(tokens.slice(0, keep));
|
|
3654
|
-
const lastNl = slice.lastIndexOf("\n");
|
|
3655
|
-
if (lastNl > 0) slice = slice.slice(0, lastNl);
|
|
3656
|
-
const out = slice + marker;
|
|
3657
|
-
return { text: out, truncated: true, tokens: countTokens(out) };
|
|
3658
3133
|
}
|
|
3659
3134
|
|
|
3660
|
-
// src/engine/
|
|
3661
|
-
var
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
//
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3135
|
+
// src/engine/search.ts
|
|
3136
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
3137
|
+
".git",
|
|
3138
|
+
".vibgrate",
|
|
3139
|
+
"node_modules",
|
|
3140
|
+
"dist",
|
|
3141
|
+
"build",
|
|
3142
|
+
"out",
|
|
3143
|
+
"target",
|
|
3144
|
+
"vendor",
|
|
3145
|
+
"__pycache__",
|
|
3146
|
+
// .NET intermediate output and coverage reports: purely generated trees that a
|
|
3147
|
+
// C#/coverage-heavy repo pays for on every literal sweep. `bin` is deliberately
|
|
3148
|
+
// NOT here — plenty of repos keep real scripts in bin/ (rails, node CLIs), and
|
|
3149
|
+
// completeness beats speed; the binary-extension skip below keeps a .NET bin/
|
|
3150
|
+
// cheap anyway.
|
|
3151
|
+
"obj",
|
|
3152
|
+
"coverage"
|
|
3153
|
+
]);
|
|
3154
|
+
var MAX_FILES_SCANNED = 2e4;
|
|
3155
|
+
var BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
3156
|
+
".dll",
|
|
3157
|
+
".pdb",
|
|
3158
|
+
".exe",
|
|
3159
|
+
".so",
|
|
3160
|
+
".dylib",
|
|
3161
|
+
".a",
|
|
3162
|
+
".lib",
|
|
3163
|
+
".o",
|
|
3164
|
+
".class",
|
|
3165
|
+
".jar",
|
|
3166
|
+
".wasm",
|
|
3167
|
+
".node",
|
|
3168
|
+
".png",
|
|
3169
|
+
".jpg",
|
|
3170
|
+
".jpeg",
|
|
3171
|
+
".gif",
|
|
3172
|
+
".webp",
|
|
3173
|
+
".ico",
|
|
3174
|
+
".icns",
|
|
3175
|
+
".bmp",
|
|
3176
|
+
".zip",
|
|
3177
|
+
".gz",
|
|
3178
|
+
".tgz",
|
|
3179
|
+
".bz2",
|
|
3180
|
+
".xz",
|
|
3181
|
+
".7z",
|
|
3182
|
+
".rar",
|
|
3183
|
+
".woff",
|
|
3184
|
+
".woff2",
|
|
3185
|
+
".ttf",
|
|
3186
|
+
".otf",
|
|
3187
|
+
".eot",
|
|
3188
|
+
".mp3",
|
|
3189
|
+
".mp4",
|
|
3190
|
+
".mov",
|
|
3191
|
+
".avi",
|
|
3192
|
+
".pdf"
|
|
3193
|
+
]);
|
|
3194
|
+
function hasBinaryExt(rel2) {
|
|
3195
|
+
return BINARY_EXTS.has(path17.extname(rel2).toLowerCase());
|
|
3696
3196
|
}
|
|
3697
|
-
function
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
flush();
|
|
3710
|
-
continue;
|
|
3711
|
-
}
|
|
3712
|
-
cur.push(line);
|
|
3197
|
+
function toPosix3(rel2) {
|
|
3198
|
+
return path17.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
|
|
3199
|
+
}
|
|
3200
|
+
async function searchSymbols(graph, root, query, limit) {
|
|
3201
|
+
let q = query.trim();
|
|
3202
|
+
if (!q) return { matches: [], moreAvailable: false, hint: "query is required" };
|
|
3203
|
+
const quoted = /^(["'`])(.+)\1$/.exec(q);
|
|
3204
|
+
if (quoted && quoted[2].trim()) q = quoted[2].trim();
|
|
3205
|
+
const isPhrase = Boolean(quoted) || /\s/.test(q) || /[/\\]/.test(q) && !q.includes(":") && !/[*?]/.test(q);
|
|
3206
|
+
let nodes = findNodes(graph, q).filter((n) => n.kind !== "file");
|
|
3207
|
+
if (nodes.length === 0) {
|
|
3208
|
+
nodes = fileSymbolNodes(graph, q);
|
|
3713
3209
|
}
|
|
3714
|
-
|
|
3715
|
-
|
|
3210
|
+
if (nodes.length === 0 && isPhrase) {
|
|
3211
|
+
nodes = reconstructedIdentifierNodes(graph, q);
|
|
3212
|
+
}
|
|
3213
|
+
if (nodes.length === 0) {
|
|
3214
|
+
const tokens = queryTokens(q);
|
|
3215
|
+
if (tokens.length >= 2) nodes = multiTokenNodes(graph, tokens);
|
|
3216
|
+
}
|
|
3217
|
+
const symbolCap = isPhrase ? Math.max(1, Math.floor(limit / 3)) : limit;
|
|
3218
|
+
const symbolHits = nodes.slice(0, symbolCap).map((n, i) => ({
|
|
3219
|
+
kind: n.kind,
|
|
3220
|
+
name: n.qualifiedName,
|
|
3221
|
+
file: n.file,
|
|
3222
|
+
line: n.span.start,
|
|
3223
|
+
score: Math.round((1 - i / Math.max(nodes.length, 1)) * 100) / 100
|
|
3224
|
+
}));
|
|
3225
|
+
const spare = limit - symbolHits.length;
|
|
3226
|
+
const textHits = [];
|
|
3227
|
+
let truncatedScan = false;
|
|
3228
|
+
let totalTextMatches;
|
|
3229
|
+
const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q);
|
|
3230
|
+
if (spare > 0 && !skipScan) {
|
|
3231
|
+
const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
|
|
3232
|
+
const scan = await scanFiles(root, q, spare, seen, textHits, isPhrase);
|
|
3233
|
+
truncatedScan = scan.truncated;
|
|
3234
|
+
if (isPhrase) totalTextMatches = scan.total;
|
|
3235
|
+
}
|
|
3236
|
+
const matches = [...symbolHits, ...textHits];
|
|
3237
|
+
if (matches.length === 0) {
|
|
3238
|
+
return {
|
|
3239
|
+
matches,
|
|
3240
|
+
moreAvailable: false,
|
|
3241
|
+
hint: "no symbol or text match \u2014 for meaning-level questions (symptoms, relationships, what-breaks-if) use query_graph"
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
const literalTruncated = totalTextMatches !== void 0 && totalTextMatches > textHits.length;
|
|
3245
|
+
const moreAvailable = isPhrase ? literalTruncated || truncatedScan : nodes.length > symbolHits.length || truncatedScan;
|
|
3246
|
+
const result = { matches, moreAvailable };
|
|
3247
|
+
if (totalTextMatches !== void 0) result.totalTextMatches = totalTextMatches;
|
|
3248
|
+
if (literalTruncated) {
|
|
3249
|
+
result.hint = `showing ${textHits.length} of ${totalTextMatches}${truncatedScan ? "+" : ""} literal matches \u2014 raise limit or narrow the query to get them all`;
|
|
3250
|
+
}
|
|
3251
|
+
return result;
|
|
3716
3252
|
}
|
|
3717
|
-
function
|
|
3718
|
-
|
|
3253
|
+
function fileSymbolNodes(graph, q) {
|
|
3254
|
+
if (!/[/\\]/.test(q) && !/\.\w+$/.test(q)) return [];
|
|
3255
|
+
const norm = q.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
3256
|
+
const inFile = graph.nodes.filter(
|
|
3257
|
+
(n) => n.kind !== "file" && (n.file === norm || n.file.endsWith(`/${norm}`))
|
|
3258
|
+
);
|
|
3259
|
+
return [...inFile].sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName));
|
|
3719
3260
|
}
|
|
3720
|
-
function
|
|
3721
|
-
if (
|
|
3722
|
-
|
|
3261
|
+
function hasExactSymbolMatch(nodes, q) {
|
|
3262
|
+
if (nodes.length === 0) return false;
|
|
3263
|
+
if (/^(.*):(\d+)$/.test(q)) return true;
|
|
3264
|
+
const lower = q.toLowerCase();
|
|
3265
|
+
return nodes.some((n) => n.id === q || n.qualifiedName.toLowerCase() === lower || n.name.toLowerCase() === lower);
|
|
3723
3266
|
}
|
|
3724
|
-
function
|
|
3725
|
-
|
|
3726
|
-
const out = /* @__PURE__ */ new Set();
|
|
3727
|
-
const re = /\b(?:function|class|interface|type|const|enum|namespace)\s+([A-Za-z_$][\w$]*)/g;
|
|
3728
|
-
let m;
|
|
3729
|
-
while (m = re.exec(api)) out.add(m[1]);
|
|
3730
|
-
return [...out];
|
|
3267
|
+
function queryTokens(q) {
|
|
3268
|
+
return [...new Set(q.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2))];
|
|
3731
3269
|
}
|
|
3732
|
-
function
|
|
3733
|
-
const
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
if (
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
if (codeBlocks > 0) score += w.hasCode + Math.min(codeBlocks, 5) * w.codeDensity;
|
|
3740
|
-
for (const t of queryTerms) if (text.includes(t)) score += w.queryOverlap;
|
|
3741
|
-
for (const sym of symbols) if (sym.length > 2 && text.includes(sym.toLowerCase())) score += w.symbolMatch;
|
|
3742
|
-
score += idx * w.position;
|
|
3743
|
-
const links = (s.body.match(/\]\(|https?:\/\//g) ?? []).length;
|
|
3744
|
-
const lineCount = Math.max(1, s.body.split("\n").filter((l) => l.trim()).length);
|
|
3745
|
-
if (links / lineCount > 0.5) score += w.linkDensity;
|
|
3746
|
-
return score;
|
|
3270
|
+
function reconstructedIdentifierNodes(graph, q) {
|
|
3271
|
+
const words = q.trim().split(/\s+/).filter(Boolean);
|
|
3272
|
+
if (words.length < 2) return [];
|
|
3273
|
+
const concat = findNodes(graph, words.join(""));
|
|
3274
|
+
if (concat.length) return concat.filter((n) => n.kind !== "file");
|
|
3275
|
+
const snake = findNodes(graph, words.join("_"));
|
|
3276
|
+
return snake.filter((n) => n.kind !== "file");
|
|
3747
3277
|
}
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
const budget = input.budget;
|
|
3762
|
-
const queryTerms = tokenizeQuery(input.query);
|
|
3763
|
-
const symbols = symbolsFromApi(input.apiSurface);
|
|
3764
|
-
const render = (s) => {
|
|
3765
|
-
const body = stripNoise(s.body);
|
|
3766
|
-
if (!body) return "";
|
|
3767
|
-
return `${s.heading ? `## ${s.heading}
|
|
3768
|
-
` : ""}${body}`.trim();
|
|
3769
|
-
};
|
|
3770
|
-
const sections = splitSections(readme).filter((s) => !PREAMBLE_RE.test(s.heading));
|
|
3771
|
-
const full = [api, ...sections.map(render)].filter(Boolean).join("\n\n").trim();
|
|
3772
|
-
if (countTokens(full) <= budget) {
|
|
3773
|
-
return { text: full, tokens: countTokens(full), truncated: false };
|
|
3774
|
-
}
|
|
3775
|
-
const units = [];
|
|
3776
|
-
for (const s of sections) {
|
|
3777
|
-
if (countTokens(s.body) > budget) {
|
|
3778
|
-
chunkBody(s.body).forEach((p, i) => units.push({ heading: i === 0 ? s.heading : "", level: s.level, body: p }));
|
|
3779
|
-
} else {
|
|
3780
|
-
units.push(s);
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
const ranked = units.map((s, idx) => ({ s, idx, score: scoreSection(s, idx, queryTerms, symbols, w) })).sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
3784
|
-
const out = [];
|
|
3785
|
-
let used = 0;
|
|
3786
|
-
const add3 = (chunk2) => {
|
|
3787
|
-
const t = countTokens(chunk2);
|
|
3788
|
-
if (used + t <= budget) {
|
|
3789
|
-
out.push(chunk2);
|
|
3790
|
-
used += t;
|
|
3791
|
-
return true;
|
|
3792
|
-
}
|
|
3793
|
-
return false;
|
|
3794
|
-
};
|
|
3795
|
-
if (api && !add3(api) && used < budget) {
|
|
3796
|
-
out.push(truncateToTokens(api, budget - used).text);
|
|
3797
|
-
used = budget;
|
|
3798
|
-
}
|
|
3799
|
-
const picked = [];
|
|
3800
|
-
for (const { s, idx } of ranked) {
|
|
3801
|
-
const chunk2 = render(s);
|
|
3802
|
-
if (!chunk2) continue;
|
|
3803
|
-
if (used + countTokens(`
|
|
3804
|
-
${chunk2}`) <= budget) {
|
|
3805
|
-
picked.push(idx);
|
|
3806
|
-
used += countTokens(`
|
|
3807
|
-
${chunk2}`);
|
|
3278
|
+
function multiTokenNodes(graph, tokens) {
|
|
3279
|
+
const scored = [];
|
|
3280
|
+
for (const n of graph.nodes) {
|
|
3281
|
+
if (n.kind === "file") continue;
|
|
3282
|
+
const name = n.name.toLowerCase();
|
|
3283
|
+
const qn = n.qualifiedName.toLowerCase();
|
|
3284
|
+
let hits = 0;
|
|
3285
|
+
let exact = false;
|
|
3286
|
+
for (const t of tokens) {
|
|
3287
|
+
if (name === t || qn === t) {
|
|
3288
|
+
hits++;
|
|
3289
|
+
exact = true;
|
|
3290
|
+
} else if (qn.includes(t)) hits++;
|
|
3808
3291
|
}
|
|
3292
|
+
if (exact || hits >= 2) scored.push({ node: n, hits, exact });
|
|
3809
3293
|
}
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3294
|
+
return scored.sort((a, b) => Number(b.exact) - Number(a.exact) || b.hits - a.hits || b.node.importance - a.node.importance || a.node.qualifiedName.localeCompare(b.node.qualifiedName)).map((e) => e.node);
|
|
3295
|
+
}
|
|
3296
|
+
async function scanFiles(root, needle, budget, seen, out, countAll) {
|
|
3297
|
+
const listing = listCandidateFiles(root, needle);
|
|
3298
|
+
const outcome = await scanCandidates(root, listing.files, needle, { collectAll: countAll, budget });
|
|
3299
|
+
let overlap = 0;
|
|
3300
|
+
for (const h of outcome.hits) {
|
|
3301
|
+
if (seen.has(`${h.file}:${h.line}`)) {
|
|
3302
|
+
overlap++;
|
|
3303
|
+
continue;
|
|
3817
3304
|
}
|
|
3305
|
+
seen.add(`${h.file}:${h.line}`);
|
|
3306
|
+
if (out.length < budget) out.push({ kind: "text", file: h.file, line: h.line, preview: h.preview });
|
|
3818
3307
|
}
|
|
3819
|
-
|
|
3820
|
-
return { text, tokens: countTokens(text), truncated: true };
|
|
3308
|
+
return { total: outcome.total - overlap, truncated: listing.truncated || outcome.truncated };
|
|
3821
3309
|
}
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
const
|
|
3833
|
-
if (
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
if (bareName && bareName.length > 1) {
|
|
3837
|
-
if (lc.includes(bareName)) score += 1;
|
|
3838
|
-
else reasons.push("library name absent");
|
|
3310
|
+
function listCandidateFiles(root, needle) {
|
|
3311
|
+
return ripgrepCandidates(root, needle) ?? walkCandidatesCached(root);
|
|
3312
|
+
}
|
|
3313
|
+
var LISTING_TTL_MS = 2e3;
|
|
3314
|
+
var listingCache = /* @__PURE__ */ new Map();
|
|
3315
|
+
function listingTtlMs() {
|
|
3316
|
+
const v = Number(process.env.VG_LISTING_TTL_MS);
|
|
3317
|
+
return Number.isFinite(v) && v >= 0 ? v : LISTING_TTL_MS;
|
|
3318
|
+
}
|
|
3319
|
+
function walkCandidatesCached(root) {
|
|
3320
|
+
const ttl = listingTtlMs();
|
|
3321
|
+
if (ttl > 0) {
|
|
3322
|
+
const hit = listingCache.get(root);
|
|
3323
|
+
if (hit && Date.now() - hit.at < ttl) return hit.listing;
|
|
3839
3324
|
}
|
|
3840
|
-
const
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3325
|
+
const listing = walkCandidates(root);
|
|
3326
|
+
if (ttl > 0) listingCache.set(root, { listing, at: Date.now() });
|
|
3327
|
+
return listing;
|
|
3328
|
+
}
|
|
3329
|
+
function walkCandidates(root) {
|
|
3330
|
+
const files = [];
|
|
3331
|
+
let truncated = false;
|
|
3332
|
+
const stack = [root];
|
|
3333
|
+
while (stack.length) {
|
|
3334
|
+
const dir = stack.pop();
|
|
3335
|
+
let entries;
|
|
3336
|
+
try {
|
|
3337
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
3338
|
+
} catch {
|
|
3339
|
+
continue;
|
|
3340
|
+
}
|
|
3341
|
+
for (const e of entries) {
|
|
3342
|
+
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
3343
|
+
const abs = path17.join(dir, e.name);
|
|
3344
|
+
if (e.isDirectory()) {
|
|
3345
|
+
stack.push(abs);
|
|
3346
|
+
continue;
|
|
3347
|
+
}
|
|
3348
|
+
if (hasBinaryExt(e.name)) continue;
|
|
3349
|
+
if (files.length >= MAX_FILES_SCANNED) {
|
|
3350
|
+
truncated = true;
|
|
3351
|
+
continue;
|
|
3352
|
+
}
|
|
3353
|
+
files.push(toPosix3(path17.relative(root, abs)));
|
|
3849
3354
|
}
|
|
3850
3355
|
}
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3356
|
+
files.sort();
|
|
3357
|
+
return { files, truncated };
|
|
3358
|
+
}
|
|
3359
|
+
var rgAvailable;
|
|
3360
|
+
function hasRipgrep() {
|
|
3361
|
+
if (process.env.VG_DISABLE_RIPGREP) return false;
|
|
3362
|
+
if (rgAvailable === void 0) {
|
|
3363
|
+
try {
|
|
3364
|
+
rgAvailable = spawnSync("rg", ["--version"], { stdio: "ignore", timeout: 3e3 }).status === 0;
|
|
3365
|
+
} catch {
|
|
3366
|
+
rgAvailable = false;
|
|
3367
|
+
}
|
|
3854
3368
|
}
|
|
3855
|
-
|
|
3856
|
-
return { score, sufficient, reasons };
|
|
3369
|
+
return rgAvailable;
|
|
3857
3370
|
}
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
const
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
}
|
|
3869
|
-
return
|
|
3371
|
+
function ripgrepCandidates(root, needle) {
|
|
3372
|
+
if (!hasRipgrep()) return null;
|
|
3373
|
+
const globs = [...IGNORE_DIRS].flatMap((d) => ["--glob", `!**/${d}/**`, "--glob", `!${d}/**`]);
|
|
3374
|
+
const res = spawnSync(
|
|
3375
|
+
"rg",
|
|
3376
|
+
["--files-with-matches", "--fixed-strings", "--ignore-case", "--no-ignore", "--no-messages", ...globs, "--", needle, "."],
|
|
3377
|
+
{ cwd: root, encoding: "utf8", timeout: 15e3, maxBuffer: 32 * 1024 * 1024 }
|
|
3378
|
+
);
|
|
3379
|
+
if (res.error || res.status !== 0 && res.status !== 1 || res.signal) return null;
|
|
3380
|
+
const files = parseRgFileList(res.stdout);
|
|
3381
|
+
if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
|
|
3382
|
+
return { files, truncated: false };
|
|
3870
3383
|
}
|
|
3871
|
-
|
|
3872
|
-
const
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3384
|
+
function parseRgFileList(stdout, sep8 = path17.sep) {
|
|
3385
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3386
|
+
for (const raw of stdout.split("\n")) {
|
|
3387
|
+
if (!raw) continue;
|
|
3388
|
+
const posix = sep8 === "\\" ? raw.replaceAll("\\", "/") : raw;
|
|
3389
|
+
const rel2 = posix.startsWith("./") ? posix.slice(2) : posix;
|
|
3390
|
+
if (!rel2) continue;
|
|
3391
|
+
const segs = rel2.split("/");
|
|
3392
|
+
if (segs.some((s) => s.startsWith(".") || IGNORE_DIRS.has(s))) continue;
|
|
3393
|
+
if (hasBinaryExt(rel2)) continue;
|
|
3394
|
+
seen.add(rel2);
|
|
3881
3395
|
}
|
|
3396
|
+
return [...seen].sort();
|
|
3397
|
+
}
|
|
3398
|
+
function readScanArtifact(root) {
|
|
3882
3399
|
try {
|
|
3883
|
-
const
|
|
3884
|
-
|
|
3885
|
-
headers,
|
|
3886
|
-
body: JSON.stringify({
|
|
3887
|
-
name: req.name,
|
|
3888
|
-
targetId: req.targetId,
|
|
3889
|
-
query: req.query,
|
|
3890
|
-
verbosity: req.verbosity,
|
|
3891
|
-
max_tokens: req.maxTokens
|
|
3892
|
-
}),
|
|
3893
|
-
signal: ctrl.signal
|
|
3894
|
-
});
|
|
3895
|
-
if (!res.ok) return null;
|
|
3896
|
-
const data = await res.json();
|
|
3897
|
-
const content = typeof data.content === "string" ? data.content : typeof data.docs === "string" ? data.docs : "";
|
|
3898
|
-
if (!content.trim()) return null;
|
|
3899
|
-
return {
|
|
3900
|
-
content,
|
|
3901
|
-
version: typeof data.version === "string" ? data.version : null,
|
|
3902
|
-
source: "hosted",
|
|
3903
|
-
metadata: data.metadata ?? void 0
|
|
3904
|
-
};
|
|
3400
|
+
const file = path17.join(root, ".vibgrate", "scan_result.json");
|
|
3401
|
+
return JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
3905
3402
|
} catch {
|
|
3906
3403
|
return null;
|
|
3907
|
-
} finally {
|
|
3908
|
-
clearTimeout(timer);
|
|
3909
3404
|
}
|
|
3910
3405
|
}
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
const
|
|
3916
|
-
const
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
compliance: req.compliance,
|
|
3932
|
-
language: req.language,
|
|
3933
|
-
sourceUrl: req.sourceUrl
|
|
3934
|
-
}),
|
|
3935
|
-
signal: ctrl.signal
|
|
3936
|
-
});
|
|
3937
|
-
const data = await res.json().catch(() => ({}));
|
|
3938
|
-
if (!res.ok) return { ok: false, status: res.status, error: typeof data.error === "string" ? data.error : void 0, message: typeof data.message === "string" ? data.message : void 0 };
|
|
3939
|
-
return {
|
|
3940
|
-
ok: true,
|
|
3941
|
-
targetId: String(data.targetId ?? ""),
|
|
3942
|
-
ecosystem: String(data.ecosystem ?? ""),
|
|
3943
|
-
entities: Number(data.entities) || 0,
|
|
3944
|
-
indexedTokens: Number(data.indexedTokens) || 0
|
|
3945
|
-
};
|
|
3946
|
-
} catch (err) {
|
|
3947
|
-
return { ok: false, status: 0, message: err instanceof Error ? err.message : "request failed" };
|
|
3948
|
-
} finally {
|
|
3949
|
-
clearTimeout(timer);
|
|
3406
|
+
function loadVulnerabilities(root) {
|
|
3407
|
+
return readScanArtifact(root)?.extended?.vulnerabilities ?? null;
|
|
3408
|
+
}
|
|
3409
|
+
function resolvePackageTarget(root, pkg) {
|
|
3410
|
+
const artifact = readScanArtifact(root);
|
|
3411
|
+
for (const project of artifact?.projects ?? []) {
|
|
3412
|
+
const dep = project.dependencies?.find((d) => d.package === pkg);
|
|
3413
|
+
if (dep) {
|
|
3414
|
+
return {
|
|
3415
|
+
ecosystem: projectTypeToVulnEcosystem(project.type) ?? "unknown",
|
|
3416
|
+
currentVersion: dep.resolvedVersion,
|
|
3417
|
+
latestVersion: dep.latestStable,
|
|
3418
|
+
majorsBehind: dep.majorsBehind
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
const rec = inventory(root).records.find((r) => r.name === pkg);
|
|
3423
|
+
if (rec) {
|
|
3424
|
+
const ecosystem = rec.ecosystem === "npm" ? "npm" : rec.ecosystem === "pypi" ? "pypi" : "unknown";
|
|
3425
|
+
return { ecosystem, currentVersion: rec.installed ?? null, latestVersion: rec.latest ?? null, majorsBehind: null };
|
|
3950
3426
|
}
|
|
3427
|
+
return { ecosystem: "unknown", currentVersion: null, latestVersion: null, majorsBehind: null };
|
|
3428
|
+
}
|
|
3429
|
+
function openFixableAdvisories(root, pkg) {
|
|
3430
|
+
const vulns = loadVulnerabilities(root);
|
|
3431
|
+
const match = vulns?.packages.find((p) => p.package === pkg);
|
|
3432
|
+
return (match?.advisories ?? []).filter((a) => a.fixedVersions.length).map((a) => a.id);
|
|
3433
|
+
}
|
|
3434
|
+
function filterBySeverity(result, minSeverity) {
|
|
3435
|
+
if (!minSeverity || minSeverity === "unknown") return result;
|
|
3436
|
+
const floor = severityRank(minSeverity);
|
|
3437
|
+
const packages = result.packages.map((p) => ({ ...p, advisories: p.advisories.filter((a) => severityRank(a.severity) >= floor) })).filter((p) => p.advisories.length > 0);
|
|
3438
|
+
const totalAdvisories = packages.reduce((n, p) => n + p.advisories.length, 0);
|
|
3439
|
+
return { ...result, packages, totalAdvisories };
|
|
3951
3440
|
}
|
|
3952
3441
|
|
|
3953
|
-
// src/
|
|
3954
|
-
var
|
|
3955
|
-
var
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3442
|
+
// src/mcp/attribution.ts
|
|
3443
|
+
var MCP_MAX_COMMITS = 300;
|
|
3444
|
+
var ECOSYSTEM_MAP = {
|
|
3445
|
+
npm: "npm",
|
|
3446
|
+
pypi: "pypi",
|
|
3447
|
+
go: "go",
|
|
3448
|
+
rust: "cargo",
|
|
3449
|
+
ruby: "rubygems",
|
|
3450
|
+
php: "composer",
|
|
3451
|
+
dotnet: "nuget",
|
|
3452
|
+
dart: "pub"
|
|
3453
|
+
// swift (no advisory ecosystem) and java (no resolved lockfile) have no timeline.
|
|
3454
|
+
};
|
|
3455
|
+
function brief(commit) {
|
|
3456
|
+
return { sha: commit.shortSha, author: commit.authorName, date: commit.date, subject: commit.subject };
|
|
3959
3457
|
}
|
|
3960
|
-
function
|
|
3961
|
-
const
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3458
|
+
async function attributedInventory(root, opts = {}) {
|
|
3459
|
+
const inv = inventory(root);
|
|
3460
|
+
if (!opts.attribute) return { ...inv, attribution: "off" };
|
|
3461
|
+
if (!await gitHistoryAvailable(root)) return { ...inv, attribution: "unavailable" };
|
|
3462
|
+
const timelines = await buildVersionTimelines(root, { maxCommits: MCP_MAX_COMMITS });
|
|
3463
|
+
if (!timelines) return { ...inv, attribution: "unavailable" };
|
|
3464
|
+
const records = inv.records.map((r) => {
|
|
3465
|
+
const ecosystem = ECOSYSTEM_MAP[r.ecosystem];
|
|
3466
|
+
if (!ecosystem) return r;
|
|
3467
|
+
const pt = findPackageTimeline(timelines, ecosystem, r.name);
|
|
3468
|
+
if (!pt || !pt.changes.length) return r;
|
|
3469
|
+
return {
|
|
3470
|
+
...r,
|
|
3471
|
+
addedBy: brief(pt.changes[0].commit),
|
|
3472
|
+
currentVersionBy: brief(pt.changes[pt.changes.length - 1].commit)
|
|
3473
|
+
};
|
|
3474
|
+
});
|
|
3475
|
+
return { ...inv, records, attribution: "git" };
|
|
3971
3476
|
}
|
|
3972
|
-
function
|
|
3973
|
-
|
|
3974
|
-
const parsed = JSON.parse(fs18.readFileSync(cachePath(root), "utf8"));
|
|
3975
|
-
if (parsed && parsed.version === CACHE_VERSION && parsed.entries && typeof parsed.entries === "object") return parsed;
|
|
3976
|
-
} catch {
|
|
3977
|
-
}
|
|
3978
|
-
return { version: CACHE_VERSION, entries: {} };
|
|
3477
|
+
function countTokens(text) {
|
|
3478
|
+
return encode(text).length;
|
|
3979
3479
|
}
|
|
3980
|
-
function
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
}
|
|
3480
|
+
function truncateToTokens(text, budget, marker = "\n\u2026(truncated)") {
|
|
3481
|
+
if (!Number.isFinite(budget) || budget <= 0) return { text, truncated: false, tokens: countTokens(text) };
|
|
3482
|
+
const tokens = encode(text);
|
|
3483
|
+
if (tokens.length <= budget) return { text, truncated: false, tokens: tokens.length };
|
|
3484
|
+
const markerTokens = encode(marker).length;
|
|
3485
|
+
const keep = Math.max(0, budget - markerTokens);
|
|
3486
|
+
let slice = decode(tokens.slice(0, keep));
|
|
3487
|
+
const lastNl = slice.lastIndexOf("\n");
|
|
3488
|
+
if (lastNl > 0) slice = slice.slice(0, lastNl);
|
|
3489
|
+
const out = slice + marker;
|
|
3490
|
+
return { text: out, truncated: true, tokens: countTokens(out) };
|
|
3992
3491
|
}
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
3492
|
+
|
|
3493
|
+
// src/engine/selection-weights.ts
|
|
3494
|
+
var DEFAULT_SELECTION_WEIGHTS = {
|
|
3495
|
+
headingUsage: 4,
|
|
3496
|
+
headingPreamble: -6,
|
|
3497
|
+
hasCode: 3,
|
|
3498
|
+
codeDensity: 0.5,
|
|
3499
|
+
queryOverlap: 5,
|
|
3500
|
+
symbolMatch: 2,
|
|
3501
|
+
position: -0.1,
|
|
3502
|
+
linkDensity: -3
|
|
3503
|
+
};
|
|
3504
|
+
|
|
3505
|
+
// src/engine/select.ts
|
|
3506
|
+
var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "to", "of", "in", "for", "and", "or", "is", "how", "do", "i", "use", "using", "with", "on", "my"]);
|
|
3507
|
+
var USAGE_RE = /\b(usage|example|examples|quick ?start|getting started|api|guide|how to|reference|tutorial|basic)\b/i;
|
|
3508
|
+
var PREAMBLE_RE = /\b(install|installation|license|licence|contributing|contributors?|sponsors?|table of contents|toc|changelog|funding|code of conduct|security|acknowledge)\b/i;
|
|
3509
|
+
function splitSections(md) {
|
|
3510
|
+
const out = [];
|
|
3511
|
+
let cur = { heading: "", level: 0};
|
|
3512
|
+
let buf = [];
|
|
3513
|
+
const flush = () => {
|
|
3514
|
+
const body = buf.join("\n").trim();
|
|
3515
|
+
if (cur.heading || body) out.push({ heading: cur.heading, level: cur.level, body });
|
|
3516
|
+
buf = [];
|
|
3517
|
+
};
|
|
3518
|
+
for (const line of md.split("\n")) {
|
|
3519
|
+
const h = /^(#{1,6})\s+(.*\S)\s*$/.exec(line);
|
|
3520
|
+
if (h) {
|
|
3521
|
+
flush();
|
|
3522
|
+
cur = { heading: h[2].trim(), level: h[1].length, body: "" };
|
|
3523
|
+
} else {
|
|
3524
|
+
buf.push(line);
|
|
4002
3525
|
}
|
|
4003
3526
|
}
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
file.entries[key] = { at: now(), result };
|
|
4007
|
-
saveFile(root, file);
|
|
4008
|
-
}
|
|
4009
|
-
return result;
|
|
4010
|
-
}
|
|
4011
|
-
|
|
4012
|
-
// src/mcp/response.ts
|
|
4013
|
-
var MAX_RESULT_TOKENS = 25e3;
|
|
4014
|
-
function renderToolResult(result, maxTokens = MAX_RESULT_TOKENS) {
|
|
4015
|
-
return toolResult(clampToBudget(compactResult(result), maxTokens));
|
|
3527
|
+
flush();
|
|
3528
|
+
return out;
|
|
4016
3529
|
}
|
|
4017
|
-
function
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
3530
|
+
function chunkBody(body) {
|
|
3531
|
+
const chunks = [];
|
|
3532
|
+
let cur = [];
|
|
3533
|
+
let inFence = false;
|
|
3534
|
+
const flush = () => {
|
|
3535
|
+
const c = cur.join("\n").trim();
|
|
3536
|
+
if (c) chunks.push(c);
|
|
3537
|
+
cur = [];
|
|
4021
3538
|
};
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
if (Array.isArray(v)) return v.map(prune);
|
|
4028
|
-
if (v && typeof v === "object") {
|
|
4029
|
-
const out = {};
|
|
4030
|
-
for (const [k, raw] of Object.entries(v)) {
|
|
4031
|
-
const pv = prune(raw);
|
|
4032
|
-
if (!isEmpty(pv)) out[k] = pv;
|
|
3539
|
+
for (const line of body.split("\n")) {
|
|
3540
|
+
if (/^\s*```/.test(line)) inFence = !inFence;
|
|
3541
|
+
if (!inFence && line.trim() === "") {
|
|
3542
|
+
flush();
|
|
3543
|
+
continue;
|
|
4033
3544
|
}
|
|
4034
|
-
|
|
3545
|
+
cur.push(line);
|
|
4035
3546
|
}
|
|
4036
|
-
|
|
3547
|
+
flush();
|
|
3548
|
+
return chunks;
|
|
4037
3549
|
}
|
|
4038
|
-
function
|
|
4039
|
-
|
|
4040
|
-
if (Array.isArray(v)) return v.length === 0;
|
|
4041
|
-
if (typeof v === "object") return Object.keys(v).length === 0;
|
|
4042
|
-
return false;
|
|
3550
|
+
function stripNoise(body) {
|
|
3551
|
+
return body.split("\n").filter((l) => !/^\s*(?:!\[[^\]]*\]\([^)]*\)\s*)+$/.test(l)).join("\n").trim();
|
|
4043
3552
|
}
|
|
4044
|
-
function
|
|
4045
|
-
if (!
|
|
4046
|
-
|
|
4047
|
-
const obj2 = { ...value };
|
|
4048
|
-
const truncated = {};
|
|
4049
|
-
for (let guard = 0; guard < 64 && tokensOf(obj2) > maxTokens; guard++) {
|
|
4050
|
-
const arrays = Object.entries(obj2).filter(
|
|
4051
|
-
(e) => Array.isArray(e[1]) && e[1].length > 0
|
|
4052
|
-
);
|
|
4053
|
-
if (arrays.length === 0) break;
|
|
4054
|
-
arrays.sort((a, b) => tokensOf(b[1]) - tokensOf(a[1]));
|
|
4055
|
-
const [field, arr] = arrays[0];
|
|
4056
|
-
const total = truncated[field]?.total ?? arr.length;
|
|
4057
|
-
const shown = Math.max(1, Math.floor(arr.length / 2));
|
|
4058
|
-
obj2[field] = arr.slice(0, shown);
|
|
4059
|
-
truncated[field] = { shown, total };
|
|
4060
|
-
}
|
|
4061
|
-
if (Object.keys(truncated).length) obj2._truncated = truncated;
|
|
4062
|
-
return obj2;
|
|
3553
|
+
function tokenizeQuery(q) {
|
|
3554
|
+
if (!q) return [];
|
|
3555
|
+
return [...new Set(q.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 1 && !STOPWORDS.has(w)))];
|
|
4063
3556
|
}
|
|
4064
|
-
function
|
|
4065
|
-
|
|
3557
|
+
function symbolsFromApi(api) {
|
|
3558
|
+
if (!api) return [];
|
|
3559
|
+
const out = /* @__PURE__ */ new Set();
|
|
3560
|
+
const re = /\b(?:function|class|interface|type|const|enum|namespace)\s+([A-Za-z_$][\w$]*)/g;
|
|
3561
|
+
let m;
|
|
3562
|
+
while (m = re.exec(api)) out.add(m[1]);
|
|
3563
|
+
return [...out];
|
|
4066
3564
|
}
|
|
4067
|
-
function
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
3565
|
+
function scoreSection(s, idx, queryTerms, symbols, w) {
|
|
3566
|
+
const text = `${s.heading}
|
|
3567
|
+
${s.body}`.toLowerCase();
|
|
3568
|
+
let score = 0;
|
|
3569
|
+
if (USAGE_RE.test(s.heading)) score += w.headingUsage;
|
|
3570
|
+
if (PREAMBLE_RE.test(s.heading)) score += w.headingPreamble;
|
|
3571
|
+
const codeBlocks = Math.floor((s.body.match(/```/g) ?? []).length / 2);
|
|
3572
|
+
if (codeBlocks > 0) score += w.hasCode + Math.min(codeBlocks, 5) * w.codeDensity;
|
|
3573
|
+
for (const t of queryTerms) if (text.includes(t)) score += w.queryOverlap;
|
|
3574
|
+
for (const sym of symbols) if (sym.length > 2 && text.includes(sym.toLowerCase())) score += w.symbolMatch;
|
|
3575
|
+
score += idx * w.position;
|
|
3576
|
+
const links = (s.body.match(/\]\(|https?:\/\//g) ?? []).length;
|
|
3577
|
+
const lineCount = Math.max(1, s.body.split("\n").filter((l) => l.trim()).length);
|
|
3578
|
+
if (links / lineCount > 0.5) score += w.linkDensity;
|
|
3579
|
+
return score;
|
|
4072
3580
|
}
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
3581
|
+
var apiBlock = (api) => api ? `## API (types)
|
|
3582
|
+
|
|
3583
|
+
\`\`\`ts
|
|
3584
|
+
${api}
|
|
3585
|
+
\`\`\`` : "";
|
|
3586
|
+
function selectForBudget(input) {
|
|
3587
|
+
const w = input.weights ?? DEFAULT_SELECTION_WEIGHTS;
|
|
3588
|
+
const api = apiBlock(input.apiSurface);
|
|
3589
|
+
const readme = input.readme.trim();
|
|
3590
|
+
if (!input.budget || !Number.isFinite(input.budget)) {
|
|
3591
|
+
const full2 = [readme, api].filter(Boolean).join("\n\n");
|
|
3592
|
+
return { text: full2, tokens: countTokens(full2), truncated: false };
|
|
4076
3593
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
const
|
|
4088
|
-
if (
|
|
4089
|
-
|
|
4090
|
-
return opts.collectAll ? scanInline(root, files, needleLower, ROW_CAP, false, void 0) : scanInline(root, files, needleLower, opts.budget, true, void 0);
|
|
3594
|
+
const budget = input.budget;
|
|
3595
|
+
const queryTerms = tokenizeQuery(input.query);
|
|
3596
|
+
const symbols = symbolsFromApi(input.apiSurface);
|
|
3597
|
+
const render = (s) => {
|
|
3598
|
+
const body = stripNoise(s.body);
|
|
3599
|
+
if (!body) return "";
|
|
3600
|
+
return `${s.heading ? `## ${s.heading}
|
|
3601
|
+
` : ""}${body}`.trim();
|
|
3602
|
+
};
|
|
3603
|
+
const sections = splitSections(readme).filter((s) => !PREAMBLE_RE.test(s.heading));
|
|
3604
|
+
const full = [api, ...sections.map(render)].filter(Boolean).join("\n\n").trim();
|
|
3605
|
+
if (countTokens(full) <= budget) {
|
|
3606
|
+
return { text: full, tokens: countTokens(full), truncated: false };
|
|
4091
3607
|
}
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
3608
|
+
const units = [];
|
|
3609
|
+
for (const s of sections) {
|
|
3610
|
+
if (countTokens(s.body) > budget) {
|
|
3611
|
+
chunkBody(s.body).forEach((p, i) => units.push({ heading: i === 0 ? s.heading : "", level: s.level, body: p }));
|
|
3612
|
+
} else {
|
|
3613
|
+
units.push(s);
|
|
3614
|
+
}
|
|
4095
3615
|
}
|
|
4096
|
-
const
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
3616
|
+
const ranked = units.map((s, idx) => ({ s, idx, score: scoreSection(s, idx, queryTerms, symbols, w) })).sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
3617
|
+
const out = [];
|
|
3618
|
+
let used = 0;
|
|
3619
|
+
const add3 = (chunk2) => {
|
|
3620
|
+
const t = countTokens(chunk2);
|
|
3621
|
+
if (used + t <= budget) {
|
|
3622
|
+
out.push(chunk2);
|
|
3623
|
+
used += t;
|
|
3624
|
+
return true;
|
|
3625
|
+
}
|
|
3626
|
+
return false;
|
|
3627
|
+
};
|
|
3628
|
+
if (api && !add3(api) && used < budget) {
|
|
3629
|
+
out.push(truncateToTokens(api, budget - used).text);
|
|
3630
|
+
used = budget;
|
|
4100
3631
|
}
|
|
4101
|
-
|
|
4102
|
-
}
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
let total = 0;
|
|
4111
|
-
for (const rel2 of files) {
|
|
4112
|
-
let size;
|
|
4113
|
-
try {
|
|
4114
|
-
size = fs18.statSync(path2.join(root, rel2)).size;
|
|
4115
|
-
} catch {
|
|
4116
|
-
continue;
|
|
3632
|
+
const picked = [];
|
|
3633
|
+
for (const { s, idx } of ranked) {
|
|
3634
|
+
const chunk2 = render(s);
|
|
3635
|
+
if (!chunk2) continue;
|
|
3636
|
+
if (used + countTokens(`
|
|
3637
|
+
${chunk2}`) <= budget) {
|
|
3638
|
+
picked.push(idx);
|
|
3639
|
+
used += countTokens(`
|
|
3640
|
+
${chunk2}`);
|
|
4117
3641
|
}
|
|
4118
|
-
sizes.set(rel2, size);
|
|
4119
|
-
if (size <= MAX_FILE_BYTES) total += size;
|
|
4120
|
-
if (total >= threshold) return { over: true, sizes };
|
|
4121
3642
|
}
|
|
4122
|
-
|
|
3643
|
+
picked.sort((a, b) => a - b);
|
|
3644
|
+
for (const idx of picked) out.push(render(units[idx]));
|
|
3645
|
+
if (used < budget - 20) {
|
|
3646
|
+
const next = ranked.find((r) => !picked.includes(r.idx) && render(r.s));
|
|
3647
|
+
if (next) {
|
|
3648
|
+
const head = truncateToTokens(render(next.s), budget - used).text;
|
|
3649
|
+
if (head.trim()) out.push(head);
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
const text = out.join("\n\n").trim();
|
|
3653
|
+
return { text, tokens: countTokens(text), truncated: true };
|
|
4123
3654
|
}
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
const
|
|
4128
|
-
const
|
|
4129
|
-
const
|
|
4130
|
-
|
|
4131
|
-
|
|
3655
|
+
|
|
3656
|
+
// src/engine/quality.ts
|
|
3657
|
+
function assessDocQuality(content, opts = {}) {
|
|
3658
|
+
const minTokens = opts.minTokens ?? 60;
|
|
3659
|
+
const lc = content.toLowerCase();
|
|
3660
|
+
const reasons = [];
|
|
3661
|
+
let score = 0;
|
|
3662
|
+
const hasCode = /```/.test(content) || /(^|\n)( {4}|\t)\S/.test(content);
|
|
3663
|
+
if (hasCode) score += 2;
|
|
3664
|
+
else reasons.push("no code example");
|
|
3665
|
+
const tokens = countTokens(content);
|
|
3666
|
+
if (tokens >= minTokens) score += 1;
|
|
3667
|
+
else reasons.push(`thin (${tokens} tokens)`);
|
|
3668
|
+
const bareName = opts.name?.toLowerCase().replace(/^@[^/]+\//, "");
|
|
3669
|
+
if (bareName && bareName.length > 1) {
|
|
3670
|
+
if (lc.includes(bareName)) score += 1;
|
|
3671
|
+
else reasons.push("library name absent");
|
|
3672
|
+
}
|
|
3673
|
+
const qt = tokenizeQuery(opts.query);
|
|
3674
|
+
let onTopic = qt.length === 0;
|
|
3675
|
+
if (qt.length) {
|
|
3676
|
+
const hits = qt.filter((t) => lc.includes(t)).length;
|
|
3677
|
+
if (hits) {
|
|
3678
|
+
score += Math.min(hits, 2);
|
|
3679
|
+
onTopic = true;
|
|
3680
|
+
} else {
|
|
3681
|
+
reasons.push("query terms absent");
|
|
3682
|
+
}
|
|
4132
3683
|
}
|
|
4133
|
-
|
|
3684
|
+
if (opts.symbols?.length) {
|
|
3685
|
+
if (opts.symbols.some((s) => s.length > 2 && lc.includes(s.toLowerCase()))) score += 1;
|
|
3686
|
+
else reasons.push("no API symbols present");
|
|
3687
|
+
}
|
|
3688
|
+
const sufficient = hasCode && tokens >= minTokens && onTopic;
|
|
3689
|
+
return { score, sufficient, reasons };
|
|
4134
3690
|
}
|
|
4135
|
-
|
|
3691
|
+
|
|
3692
|
+
// src/engine/hosted.ts
|
|
3693
|
+
function hostedBase(opts = {}) {
|
|
3694
|
+
const explicit = opts.base || process.env.VIBGRATE_LIB_HOST;
|
|
3695
|
+
if (explicit) return explicit.replace(/\/+$/, "");
|
|
3696
|
+
let host;
|
|
4136
3697
|
try {
|
|
4137
|
-
|
|
4138
|
-
if (size > MAX_FILE_BYTES) return null;
|
|
4139
|
-
const text = fs18.readFileSync(abs, "utf8");
|
|
4140
|
-
return text.includes(NUL) ? null : text;
|
|
3698
|
+
host = resolveIngestHost(opts.region, opts.ingest);
|
|
4141
3699
|
} catch {
|
|
4142
|
-
|
|
3700
|
+
host = "us.ingest.vibgrate.com";
|
|
4143
3701
|
}
|
|
3702
|
+
return `https://${host}`;
|
|
4144
3703
|
}
|
|
4145
|
-
function
|
|
4146
|
-
const
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
else {
|
|
4156
|
-
truncated = true;
|
|
4157
|
-
if (stopAtCap) return { hits, total, truncated };
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
3704
|
+
async function fetchHostedDocs(req, opts = {}) {
|
|
3705
|
+
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
3706
|
+
if (!f) return null;
|
|
3707
|
+
const url = `${hostedBase(opts)}/v1/lib/docs`;
|
|
3708
|
+
const ctrl = new AbortController();
|
|
3709
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 4e3);
|
|
3710
|
+
const headers = { "content-type": "application/json" };
|
|
3711
|
+
if (opts.auth) {
|
|
3712
|
+
headers["Authorization"] = `VibgrateDSN ${opts.auth.keyId}:${opts.auth.secret}`;
|
|
3713
|
+
headers["X-Vibgrate-Timestamp"] = String(Date.now());
|
|
4160
3714
|
}
|
|
4161
|
-
return { hits, total, truncated };
|
|
4162
|
-
}
|
|
4163
|
-
async function scanParallel(root, files, needleLower) {
|
|
4164
|
-
const n = Math.min(files.length, Math.max(2, Math.min(8, os.cpus().length - 1)));
|
|
4165
|
-
const shards = Array.from({ length: n }, () => []);
|
|
4166
|
-
files.forEach((f, i) => shards[i % n].push(f));
|
|
4167
|
-
let results;
|
|
4168
3715
|
try {
|
|
4169
|
-
|
|
3716
|
+
const res = await f(url, {
|
|
3717
|
+
method: "POST",
|
|
3718
|
+
headers,
|
|
3719
|
+
body: JSON.stringify({
|
|
3720
|
+
name: req.name,
|
|
3721
|
+
targetId: req.targetId,
|
|
3722
|
+
query: req.query,
|
|
3723
|
+
verbosity: req.verbosity,
|
|
3724
|
+
max_tokens: req.maxTokens
|
|
3725
|
+
}),
|
|
3726
|
+
signal: ctrl.signal
|
|
3727
|
+
});
|
|
3728
|
+
if (!res.ok) return null;
|
|
3729
|
+
const data = await res.json();
|
|
3730
|
+
const content = typeof data.content === "string" ? data.content : typeof data.docs === "string" ? data.docs : "";
|
|
3731
|
+
if (!content.trim()) return null;
|
|
3732
|
+
return {
|
|
3733
|
+
content,
|
|
3734
|
+
version: typeof data.version === "string" ? data.version : null,
|
|
3735
|
+
source: "hosted",
|
|
3736
|
+
metadata: data.metadata ?? void 0
|
|
3737
|
+
};
|
|
4170
3738
|
} catch {
|
|
4171
|
-
return
|
|
4172
|
-
}
|
|
4173
|
-
|
|
4174
|
-
let total = 0;
|
|
4175
|
-
let truncated = false;
|
|
4176
|
-
for (const r of results) {
|
|
4177
|
-
total += r.total;
|
|
4178
|
-
truncated = truncated || r.truncated;
|
|
4179
|
-
for (const h of r.hits) hits.push(h);
|
|
4180
|
-
}
|
|
4181
|
-
hits.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line);
|
|
4182
|
-
if (hits.length > ROW_CAP) {
|
|
4183
|
-
hits.length = ROW_CAP;
|
|
4184
|
-
truncated = true;
|
|
3739
|
+
return null;
|
|
3740
|
+
} finally {
|
|
3741
|
+
clearTimeout(timer);
|
|
4185
3742
|
}
|
|
4186
|
-
return { hits, total, truncated };
|
|
4187
|
-
}
|
|
4188
|
-
var WORKER_SOURCE = [
|
|
4189
|
-
"const { parentPort, workerData } = require('worker_threads');",
|
|
4190
|
-
"const fs = require('fs');",
|
|
4191
|
-
"const path = require('path');",
|
|
4192
|
-
`const MAX = ${MAX_FILE_BYTES};`,
|
|
4193
|
-
`const PREVIEW = ${PREVIEW_CHARS};`,
|
|
4194
|
-
`const CAP = ${ROW_CAP};`,
|
|
4195
|
-
"const NUL = String.fromCharCode(0);",
|
|
4196
|
-
"const NL = String.fromCharCode(10);",
|
|
4197
|
-
"const { root, files, needleLower } = workerData;",
|
|
4198
|
-
"const hits = []; let total = 0, truncated = false;",
|
|
4199
|
-
"for (const rel of files) {",
|
|
4200
|
-
" const abs = path.join(root, rel);",
|
|
4201
|
-
" let text;",
|
|
4202
|
-
' try { if (fs.statSync(abs).size > MAX) continue; text = fs.readFileSync(abs, "utf8"); } catch { continue; }',
|
|
4203
|
-
" if (text.includes(NUL)) continue;",
|
|
4204
|
-
" const low = text.toLowerCase();",
|
|
4205
|
-
" if (!low.includes(needleLower)) continue;",
|
|
4206
|
-
" const lowLines = low.split(NL); const rawLines = text.split(NL);",
|
|
4207
|
-
" for (let i = 0; i < lowLines.length; i++) {",
|
|
4208
|
-
" if (!lowLines[i].includes(needleLower)) continue;",
|
|
4209
|
-
" total++;",
|
|
4210
|
-
" if (hits.length < CAP) hits.push({ file: rel, line: i + 1, preview: rawLines[i].trim().slice(0, PREVIEW) });",
|
|
4211
|
-
" else truncated = true;",
|
|
4212
|
-
" }",
|
|
4213
|
-
"}",
|
|
4214
|
-
"parentPort.postMessage({ hits, total, truncated });"
|
|
4215
|
-
].join("\n");
|
|
4216
|
-
function runWorker(root, files, needleLower) {
|
|
4217
|
-
return new Promise((resolve11, reject) => {
|
|
4218
|
-
const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
|
|
4219
|
-
let settled = false;
|
|
4220
|
-
w.once("message", (m) => {
|
|
4221
|
-
settled = true;
|
|
4222
|
-
resolve11(m);
|
|
4223
|
-
});
|
|
4224
|
-
w.once("error", reject);
|
|
4225
|
-
w.once("exit", (code) => {
|
|
4226
|
-
if (!settled && code !== 0) reject(new Error(`literal-scan worker exited with code ${code}`));
|
|
4227
|
-
});
|
|
4228
|
-
});
|
|
4229
|
-
}
|
|
4230
|
-
|
|
4231
|
-
// src/engine/search.ts
|
|
4232
|
-
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
4233
|
-
".git",
|
|
4234
|
-
".vibgrate",
|
|
4235
|
-
"node_modules",
|
|
4236
|
-
"dist",
|
|
4237
|
-
"build",
|
|
4238
|
-
"out",
|
|
4239
|
-
"target",
|
|
4240
|
-
"vendor",
|
|
4241
|
-
"__pycache__",
|
|
4242
|
-
// .NET intermediate output and coverage reports: purely generated trees that a
|
|
4243
|
-
// C#/coverage-heavy repo pays for on every literal sweep. `bin` is deliberately
|
|
4244
|
-
// NOT here — plenty of repos keep real scripts in bin/ (rails, node CLIs), and
|
|
4245
|
-
// completeness beats speed; the binary-extension skip below keeps a .NET bin/
|
|
4246
|
-
// cheap anyway.
|
|
4247
|
-
"obj",
|
|
4248
|
-
"coverage"
|
|
4249
|
-
]);
|
|
4250
|
-
var MAX_FILES_SCANNED = 2e4;
|
|
4251
|
-
var BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
4252
|
-
".dll",
|
|
4253
|
-
".pdb",
|
|
4254
|
-
".exe",
|
|
4255
|
-
".so",
|
|
4256
|
-
".dylib",
|
|
4257
|
-
".a",
|
|
4258
|
-
".lib",
|
|
4259
|
-
".o",
|
|
4260
|
-
".class",
|
|
4261
|
-
".jar",
|
|
4262
|
-
".wasm",
|
|
4263
|
-
".node",
|
|
4264
|
-
".png",
|
|
4265
|
-
".jpg",
|
|
4266
|
-
".jpeg",
|
|
4267
|
-
".gif",
|
|
4268
|
-
".webp",
|
|
4269
|
-
".ico",
|
|
4270
|
-
".icns",
|
|
4271
|
-
".bmp",
|
|
4272
|
-
".zip",
|
|
4273
|
-
".gz",
|
|
4274
|
-
".tgz",
|
|
4275
|
-
".bz2",
|
|
4276
|
-
".xz",
|
|
4277
|
-
".7z",
|
|
4278
|
-
".rar",
|
|
4279
|
-
".woff",
|
|
4280
|
-
".woff2",
|
|
4281
|
-
".ttf",
|
|
4282
|
-
".otf",
|
|
4283
|
-
".eot",
|
|
4284
|
-
".mp3",
|
|
4285
|
-
".mp4",
|
|
4286
|
-
".mov",
|
|
4287
|
-
".avi",
|
|
4288
|
-
".pdf"
|
|
4289
|
-
]);
|
|
4290
|
-
function hasBinaryExt(rel2) {
|
|
4291
|
-
return BINARY_EXTS.has(path2.extname(rel2).toLowerCase());
|
|
4292
3743
|
}
|
|
4293
|
-
function
|
|
4294
|
-
|
|
4295
|
-
}
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
const
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
const spare = limit - symbolHits.length;
|
|
4322
|
-
const textHits = [];
|
|
4323
|
-
let truncatedScan = false;
|
|
4324
|
-
let totalTextMatches;
|
|
4325
|
-
const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q);
|
|
4326
|
-
if (spare > 0 && !skipScan) {
|
|
4327
|
-
const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
|
|
4328
|
-
const scan = await scanFiles(root, q, spare, seen, textHits, isPhrase);
|
|
4329
|
-
truncatedScan = scan.truncated;
|
|
4330
|
-
if (isPhrase) totalTextMatches = scan.total;
|
|
4331
|
-
}
|
|
4332
|
-
const matches = [...symbolHits, ...textHits];
|
|
4333
|
-
if (matches.length === 0) {
|
|
3744
|
+
async function publishPrivateLibrary(req, opts = {}) {
|
|
3745
|
+
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
3746
|
+
if (!f) return { ok: false, status: 0, message: "no network available" };
|
|
3747
|
+
if (!opts.auth) return { ok: false, status: 401, message: "a DSN is required to publish a private library (run `vibgrate login` or set VIBGRATE_DSN)" };
|
|
3748
|
+
const url = `${hostedBase(opts)}/v1/lib/private`;
|
|
3749
|
+
const ctrl = new AbortController();
|
|
3750
|
+
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
3751
|
+
try {
|
|
3752
|
+
const res = await f(url, {
|
|
3753
|
+
method: "POST",
|
|
3754
|
+
headers: {
|
|
3755
|
+
"content-type": "application/json",
|
|
3756
|
+
Authorization: `VibgrateDSN ${opts.auth.keyId}:${opts.auth.secret}`,
|
|
3757
|
+
"X-Vibgrate-Timestamp": String(Date.now())
|
|
3758
|
+
},
|
|
3759
|
+
body: JSON.stringify({
|
|
3760
|
+
name: req.name,
|
|
3761
|
+
version: req.version,
|
|
3762
|
+
readme: req.readme,
|
|
3763
|
+
dts: req.dts,
|
|
3764
|
+
compliance: req.compliance,
|
|
3765
|
+
language: req.language,
|
|
3766
|
+
sourceUrl: req.sourceUrl
|
|
3767
|
+
}),
|
|
3768
|
+
signal: ctrl.signal
|
|
3769
|
+
});
|
|
3770
|
+
const data = await res.json().catch(() => ({}));
|
|
3771
|
+
if (!res.ok) return { ok: false, status: res.status, error: typeof data.error === "string" ? data.error : void 0, message: typeof data.message === "string" ? data.message : void 0 };
|
|
4334
3772
|
return {
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
3773
|
+
ok: true,
|
|
3774
|
+
targetId: String(data.targetId ?? ""),
|
|
3775
|
+
ecosystem: String(data.ecosystem ?? ""),
|
|
3776
|
+
entities: Number(data.entities) || 0,
|
|
3777
|
+
indexedTokens: Number(data.indexedTokens) || 0
|
|
4338
3778
|
};
|
|
3779
|
+
} catch (err) {
|
|
3780
|
+
return { ok: false, status: 0, message: err instanceof Error ? err.message : "request failed" };
|
|
3781
|
+
} finally {
|
|
3782
|
+
clearTimeout(timer);
|
|
4339
3783
|
}
|
|
4340
|
-
const literalTruncated = totalTextMatches !== void 0 && totalTextMatches > textHits.length;
|
|
4341
|
-
const moreAvailable = isPhrase ? literalTruncated || truncatedScan : nodes.length > symbolHits.length || truncatedScan;
|
|
4342
|
-
const result = { matches, moreAvailable };
|
|
4343
|
-
if (totalTextMatches !== void 0) result.totalTextMatches = totalTextMatches;
|
|
4344
|
-
if (literalTruncated) {
|
|
4345
|
-
result.hint = `showing ${textHits.length} of ${totalTextMatches}${truncatedScan ? "+" : ""} literal matches \u2014 raise limit or narrow the query to get them all`;
|
|
4346
|
-
}
|
|
4347
|
-
return result;
|
|
4348
|
-
}
|
|
4349
|
-
function fileSymbolNodes(graph, q) {
|
|
4350
|
-
if (!/[/\\]/.test(q) && !/\.\w+$/.test(q)) return [];
|
|
4351
|
-
const norm = q.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
4352
|
-
const inFile = graph.nodes.filter(
|
|
4353
|
-
(n) => n.kind !== "file" && (n.file === norm || n.file.endsWith(`/${norm}`))
|
|
4354
|
-
);
|
|
4355
|
-
return [...inFile].sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName));
|
|
4356
3784
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
3785
|
+
|
|
3786
|
+
// src/engine/hosted-cache.ts
|
|
3787
|
+
var CACHE_VERSION = "vg-hosted-docs/1";
|
|
3788
|
+
var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3789
|
+
var MAX_ENTRIES = 64;
|
|
3790
|
+
function cachePath(root) {
|
|
3791
|
+
return path17.join(cacheDir(root), "hosted-docs.json");
|
|
4362
3792
|
}
|
|
4363
|
-
function
|
|
4364
|
-
|
|
3793
|
+
function hostedDocsCacheKey(req, opts = {}) {
|
|
3794
|
+
const material = JSON.stringify([
|
|
3795
|
+
req.name ?? "",
|
|
3796
|
+
req.targetId ?? "",
|
|
3797
|
+
req.query ?? "",
|
|
3798
|
+
req.verbosity ?? "",
|
|
3799
|
+
req.maxTokens ?? 0,
|
|
3800
|
+
hostedBase(opts),
|
|
3801
|
+
opts.auth?.keyId ?? "anon"
|
|
3802
|
+
]);
|
|
3803
|
+
return createHash("sha256").update(material).digest("hex");
|
|
4365
3804
|
}
|
|
4366
|
-
function
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
return
|
|
3805
|
+
function loadFile(root) {
|
|
3806
|
+
try {
|
|
3807
|
+
const parsed = JSON.parse(fs15.readFileSync(cachePath(root), "utf8"));
|
|
3808
|
+
if (parsed && parsed.version === CACHE_VERSION && parsed.entries && typeof parsed.entries === "object") return parsed;
|
|
3809
|
+
} catch {
|
|
3810
|
+
}
|
|
3811
|
+
return { version: CACHE_VERSION, entries: {} };
|
|
4373
3812
|
}
|
|
4374
|
-
function
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
if (
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
let exact = false;
|
|
4382
|
-
for (const t of tokens) {
|
|
4383
|
-
if (name === t || qn === t) {
|
|
4384
|
-
hits++;
|
|
4385
|
-
exact = true;
|
|
4386
|
-
} else if (qn.includes(t)) hits++;
|
|
3813
|
+
function saveFile(root, file) {
|
|
3814
|
+
try {
|
|
3815
|
+
const keys = Object.keys(file.entries);
|
|
3816
|
+
if (keys.length > MAX_ENTRIES) {
|
|
3817
|
+
for (const k of keys.sort((a, b) => file.entries[a].at - file.entries[b].at).slice(0, keys.length - MAX_ENTRIES)) {
|
|
3818
|
+
delete file.entries[k];
|
|
3819
|
+
}
|
|
4387
3820
|
}
|
|
4388
|
-
|
|
3821
|
+
fs15.mkdirSync(cacheDir(root), { recursive: true });
|
|
3822
|
+
fs15.writeFileSync(cachePath(root), JSON.stringify(file));
|
|
3823
|
+
} catch {
|
|
4389
3824
|
}
|
|
4390
|
-
return scored.sort((a, b) => Number(b.exact) - Number(a.exact) || b.hits - a.hits || b.node.importance - a.node.importance || a.node.qualifiedName.localeCompare(b.node.qualifiedName)).map((e) => e.node);
|
|
4391
3825
|
}
|
|
4392
|
-
async function
|
|
4393
|
-
const
|
|
4394
|
-
const
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
3826
|
+
async function fetchHostedDocsCached(root, req, opts = {}) {
|
|
3827
|
+
const now = opts.now ?? Date.now;
|
|
3828
|
+
const ttl = opts.ttlMs ?? HOSTED_DOCS_TTL_MS;
|
|
3829
|
+
const key = hostedDocsCacheKey(req, opts);
|
|
3830
|
+
const file = loadFile(root);
|
|
3831
|
+
if (!opts.bypass) {
|
|
3832
|
+
const hit = file.entries[key];
|
|
3833
|
+
if (hit && now() - hit.at < ttl && hit.result?.content) {
|
|
3834
|
+
return { ...hit.result, metadata: { ...hit.result.metadata ?? {}, cached: true } };
|
|
4400
3835
|
}
|
|
4401
|
-
seen.add(`${h.file}:${h.line}`);
|
|
4402
|
-
if (out.length < budget) out.push({ kind: "text", file: h.file, line: h.line, preview: h.preview });
|
|
4403
3836
|
}
|
|
4404
|
-
|
|
3837
|
+
const result = await fetchHostedDocs(req, opts);
|
|
3838
|
+
if (result) {
|
|
3839
|
+
file.entries[key] = { at: now(), result };
|
|
3840
|
+
saveFile(root, file);
|
|
3841
|
+
}
|
|
3842
|
+
return result;
|
|
4405
3843
|
}
|
|
4406
|
-
|
|
4407
|
-
|
|
3844
|
+
|
|
3845
|
+
// src/mcp/response.ts
|
|
3846
|
+
var MAX_RESULT_TOKENS = 25e3;
|
|
3847
|
+
function renderToolResult(result, maxTokens = MAX_RESULT_TOKENS) {
|
|
3848
|
+
return toolResult(clampToBudget(compactResult(result), maxTokens));
|
|
4408
3849
|
}
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
3850
|
+
function toolResult(result) {
|
|
3851
|
+
return {
|
|
3852
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
3853
|
+
structuredContent: asStructured(result)
|
|
3854
|
+
};
|
|
4414
3855
|
}
|
|
4415
|
-
function
|
|
4416
|
-
|
|
4417
|
-
if (ttl > 0) {
|
|
4418
|
-
const hit = listingCache.get(root);
|
|
4419
|
-
if (hit && Date.now() - hit.at < ttl) return hit.listing;
|
|
4420
|
-
}
|
|
4421
|
-
const listing = walkCandidates(root);
|
|
4422
|
-
if (ttl > 0) listingCache.set(root, { listing, at: Date.now() });
|
|
4423
|
-
return listing;
|
|
3856
|
+
function compactResult(value) {
|
|
3857
|
+
return prune(value);
|
|
4424
3858
|
}
|
|
4425
|
-
function
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
try {
|
|
4433
|
-
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4434
|
-
} catch {
|
|
4435
|
-
continue;
|
|
4436
|
-
}
|
|
4437
|
-
for (const e of entries) {
|
|
4438
|
-
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
4439
|
-
const abs = path2.join(dir, e.name);
|
|
4440
|
-
if (e.isDirectory()) {
|
|
4441
|
-
stack.push(abs);
|
|
4442
|
-
continue;
|
|
4443
|
-
}
|
|
4444
|
-
if (hasBinaryExt(e.name)) continue;
|
|
4445
|
-
if (files.length >= MAX_FILES_SCANNED) {
|
|
4446
|
-
truncated = true;
|
|
4447
|
-
continue;
|
|
4448
|
-
}
|
|
4449
|
-
files.push(toPosix2(path2.relative(root, abs)));
|
|
3859
|
+
function prune(v) {
|
|
3860
|
+
if (Array.isArray(v)) return v.map(prune);
|
|
3861
|
+
if (v && typeof v === "object") {
|
|
3862
|
+
const out = {};
|
|
3863
|
+
for (const [k, raw] of Object.entries(v)) {
|
|
3864
|
+
const pv = prune(raw);
|
|
3865
|
+
if (!isEmpty(pv)) out[k] = pv;
|
|
4450
3866
|
}
|
|
3867
|
+
return out;
|
|
4451
3868
|
}
|
|
4452
|
-
|
|
4453
|
-
return { files, truncated };
|
|
3869
|
+
return v;
|
|
4454
3870
|
}
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
if (
|
|
4458
|
-
if (
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
3871
|
+
function isEmpty(v) {
|
|
3872
|
+
if (v === null || v === void 0 || v === "") return true;
|
|
3873
|
+
if (Array.isArray(v)) return v.length === 0;
|
|
3874
|
+
if (typeof v === "object") return Object.keys(v).length === 0;
|
|
3875
|
+
return false;
|
|
3876
|
+
}
|
|
3877
|
+
function clampToBudget(value, maxTokens = MAX_RESULT_TOKENS) {
|
|
3878
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
3879
|
+
if (tokensOf(value) <= maxTokens) return value;
|
|
3880
|
+
const obj2 = { ...value };
|
|
3881
|
+
const truncated = {};
|
|
3882
|
+
for (let guard = 0; guard < 64 && tokensOf(obj2) > maxTokens; guard++) {
|
|
3883
|
+
const arrays = Object.entries(obj2).filter(
|
|
3884
|
+
(e) => Array.isArray(e[1]) && e[1].length > 0
|
|
3885
|
+
);
|
|
3886
|
+
if (arrays.length === 0) break;
|
|
3887
|
+
arrays.sort((a, b) => tokensOf(b[1]) - tokensOf(a[1]));
|
|
3888
|
+
const [field, arr] = arrays[0];
|
|
3889
|
+
const total = truncated[field]?.total ?? arr.length;
|
|
3890
|
+
const shown = Math.max(1, Math.floor(arr.length / 2));
|
|
3891
|
+
obj2[field] = arr.slice(0, shown);
|
|
3892
|
+
truncated[field] = { shown, total };
|
|
4464
3893
|
}
|
|
4465
|
-
|
|
3894
|
+
if (Object.keys(truncated).length) obj2._truncated = truncated;
|
|
3895
|
+
return obj2;
|
|
4466
3896
|
}
|
|
4467
|
-
function
|
|
4468
|
-
|
|
4469
|
-
const globs = [...IGNORE_DIRS].flatMap((d) => ["--glob", `!**/${d}/**`, "--glob", `!${d}/**`]);
|
|
4470
|
-
const res = spawnSync(
|
|
4471
|
-
"rg",
|
|
4472
|
-
["--files-with-matches", "--fixed-strings", "--ignore-case", "--no-ignore", "--no-messages", ...globs, "--", needle, "."],
|
|
4473
|
-
{ cwd: root, encoding: "utf8", timeout: 15e3, maxBuffer: 32 * 1024 * 1024 }
|
|
4474
|
-
);
|
|
4475
|
-
if (res.error || res.status !== 0 && res.status !== 1 || res.signal) return null;
|
|
4476
|
-
const files = parseRgFileList(res.stdout);
|
|
4477
|
-
if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
|
|
4478
|
-
return { files, truncated: false };
|
|
3897
|
+
function tokensOf(v) {
|
|
3898
|
+
return countTokens(JSON.stringify(v));
|
|
4479
3899
|
}
|
|
4480
|
-
function
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
if (!raw) continue;
|
|
4484
|
-
const posix3 = sep9 === "\\" ? raw.replaceAll("\\", "/") : raw;
|
|
4485
|
-
const rel2 = posix3.startsWith("./") ? posix3.slice(2) : posix3;
|
|
4486
|
-
if (!rel2) continue;
|
|
4487
|
-
const segs = rel2.split("/");
|
|
4488
|
-
if (segs.some((s) => s.startsWith(".") || IGNORE_DIRS.has(s))) continue;
|
|
4489
|
-
if (hasBinaryExt(rel2)) continue;
|
|
4490
|
-
seen.add(rel2);
|
|
3900
|
+
function asStructured(result) {
|
|
3901
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
3902
|
+
return result;
|
|
4491
3903
|
}
|
|
4492
|
-
return
|
|
3904
|
+
return { result };
|
|
3905
|
+
}
|
|
3906
|
+
function boundList(items, max) {
|
|
3907
|
+
if (!Number.isFinite(max) || max < 0 || items.length <= max) {
|
|
3908
|
+
return { items, total: items.length };
|
|
3909
|
+
}
|
|
3910
|
+
return { items: items.slice(0, max), total: items.length };
|
|
4493
3911
|
}
|
|
3912
|
+
var NODE_EDGE_CAP = 50;
|
|
4494
3913
|
|
|
4495
3914
|
// src/mcp/tools.ts
|
|
4496
3915
|
var warmEmbedder = null;
|
|
@@ -5375,12 +4794,12 @@ function detectAssistants(root, opts = {}) {
|
|
|
5375
4794
|
const which = opts.which ?? whichOnPath;
|
|
5376
4795
|
const found = [];
|
|
5377
4796
|
for (const assistant of ASSISTANTS) {
|
|
5378
|
-
const repoHit = (assistant.markers ?? []).find((m) =>
|
|
4797
|
+
const repoHit = (assistant.markers ?? []).find((m) => fs15.existsSync(path17.join(root, m)));
|
|
5379
4798
|
if (repoHit) {
|
|
5380
4799
|
found.push({ assistant, via: "repo", marker: repoHit });
|
|
5381
4800
|
continue;
|
|
5382
4801
|
}
|
|
5383
|
-
const homeHit = (assistant.homeMarkers ?? []).find((m) =>
|
|
4802
|
+
const homeHit = (assistant.homeMarkers ?? []).find((m) => fs15.existsSync(path17.join(home, m)));
|
|
5384
4803
|
if (homeHit) {
|
|
5385
4804
|
found.push({ assistant, via: "home", marker: homeHit });
|
|
5386
4805
|
continue;
|
|
@@ -5412,19 +4831,19 @@ function installAssistant(a, opts) {
|
|
|
5412
4831
|
const skipped = [];
|
|
5413
4832
|
let note;
|
|
5414
4833
|
if (a.skill) {
|
|
5415
|
-
writeFileEnsured(
|
|
4834
|
+
writeFileEnsured(path17.join(opts.root, a.skill), skillMarkdown(a.id));
|
|
5416
4835
|
wrote.push(a.skill);
|
|
5417
4836
|
}
|
|
5418
4837
|
if (a.mcp) {
|
|
5419
4838
|
const launch = opts.launch ?? detectServeLaunch();
|
|
5420
|
-
upsertMcp(
|
|
4839
|
+
upsertMcp(path17.join(opts.root, a.mcp.file), a.mcp, launch);
|
|
5421
4840
|
wrote.push(a.mcp.file);
|
|
5422
4841
|
note = launch.note;
|
|
5423
4842
|
} else {
|
|
5424
4843
|
skipped.push("mcp (host-specific setup)");
|
|
5425
4844
|
}
|
|
5426
4845
|
if (a.nudge && opts.hook !== false) {
|
|
5427
|
-
writeNudge(
|
|
4846
|
+
writeNudge(path17.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
|
|
5428
4847
|
wrote.push(a.nudge.file);
|
|
5429
4848
|
} else if (a.nudge) {
|
|
5430
4849
|
skipped.push("nudge (--no-hook)");
|
|
@@ -5434,41 +4853,41 @@ function installAssistant(a, opts) {
|
|
|
5434
4853
|
function uninstallAssistant(a, root, purge) {
|
|
5435
4854
|
const removed = [];
|
|
5436
4855
|
if (a.mcp) {
|
|
5437
|
-
const file =
|
|
4856
|
+
const file = path17.join(root, a.mcp.file);
|
|
5438
4857
|
if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
|
|
5439
4858
|
}
|
|
5440
4859
|
if (a.nudge) {
|
|
5441
|
-
const file =
|
|
4860
|
+
const file = path17.join(root, a.nudge.file);
|
|
5442
4861
|
if (a.nudge.kind === "block") {
|
|
5443
4862
|
if (removeBlock(file)) removed.push(a.nudge.file);
|
|
5444
|
-
} else if (
|
|
5445
|
-
|
|
4863
|
+
} else if (fs15.existsSync(file)) {
|
|
4864
|
+
fs15.rmSync(file);
|
|
5446
4865
|
removed.push(a.nudge.file);
|
|
5447
4866
|
}
|
|
5448
4867
|
}
|
|
5449
4868
|
if (purge && a.skill) {
|
|
5450
|
-
const file =
|
|
5451
|
-
if (
|
|
5452
|
-
|
|
4869
|
+
const file = path17.join(root, a.skill);
|
|
4870
|
+
if (fs15.existsSync(file)) {
|
|
4871
|
+
fs15.rmSync(file);
|
|
5453
4872
|
removed.push(a.skill);
|
|
5454
4873
|
}
|
|
5455
4874
|
}
|
|
5456
4875
|
return removed;
|
|
5457
4876
|
}
|
|
5458
4877
|
function writeFileEnsured(file, content) {
|
|
5459
|
-
|
|
5460
|
-
|
|
4878
|
+
fs15.mkdirSync(path17.dirname(file), { recursive: true });
|
|
4879
|
+
fs15.writeFileSync(file, content);
|
|
5461
4880
|
}
|
|
5462
4881
|
function writeNavigationConfig(root) {
|
|
5463
4882
|
ensureVibgrateGitignore(root);
|
|
5464
|
-
const rel2 =
|
|
4883
|
+
const rel2 = path17.join(".vibgrate", "mcp-navigation.json");
|
|
5465
4884
|
const doc = {
|
|
5466
4885
|
_readme: "Deferred-loading config for agents embedding `vg serve` via the Claude API (defer_loading + tool-search). The hot navigation core stays in context; the rest load on demand \u2014 ~350-450 schema tokens/step vs ~1,881 for the full set. Hosts without defer_loading ignore this and serve the whole (already optimized) tool set. See docs/graph/VG-NAVIGATION-PROFILE.md.",
|
|
5467
4886
|
hot_core: [...HOT_TOOLS],
|
|
5468
4887
|
deferred: deferredToolNames(),
|
|
5469
4888
|
toolset: navigationToolsetConfig()
|
|
5470
4889
|
};
|
|
5471
|
-
writeFileEnsured(
|
|
4890
|
+
writeFileEnsured(path17.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
5472
4891
|
`);
|
|
5473
4892
|
return rel2;
|
|
5474
4893
|
}
|
|
@@ -5483,7 +4902,7 @@ function upsertMcp(file, target, launch) {
|
|
|
5483
4902
|
`);
|
|
5484
4903
|
}
|
|
5485
4904
|
function removeMcp(file, target) {
|
|
5486
|
-
if (!
|
|
4905
|
+
if (!fs15.existsSync(file)) return false;
|
|
5487
4906
|
const config = readJson(file);
|
|
5488
4907
|
const bag = config[target.key];
|
|
5489
4908
|
if (!bag || !(bag.vg !== void 0)) return false;
|
|
@@ -5502,8 +4921,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5502
4921
|
};
|
|
5503
4922
|
for (const a of ASSISTANTS) {
|
|
5504
4923
|
if (a.skill) {
|
|
5505
|
-
const file =
|
|
5506
|
-
if (
|
|
4924
|
+
const file = path17.join(root, a.skill);
|
|
4925
|
+
if (fs15.existsSync(file)) {
|
|
5507
4926
|
const text = readTextSafe(file);
|
|
5508
4927
|
const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
|
|
5509
4928
|
if (from !== null) {
|
|
@@ -5513,8 +4932,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5513
4932
|
}
|
|
5514
4933
|
}
|
|
5515
4934
|
if (a.nudge) {
|
|
5516
|
-
const file =
|
|
5517
|
-
if (!
|
|
4935
|
+
const file = path17.join(root, a.nudge.file);
|
|
4936
|
+
if (!fs15.existsSync(file)) continue;
|
|
5518
4937
|
const text = readTextSafe(file);
|
|
5519
4938
|
if (text === null) continue;
|
|
5520
4939
|
if (a.nudge.kind === "block") {
|
|
@@ -5538,7 +4957,7 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5538
4957
|
}
|
|
5539
4958
|
function readTextSafe(file) {
|
|
5540
4959
|
try {
|
|
5541
|
-
return
|
|
4960
|
+
return fs15.readFileSync(file, "utf8");
|
|
5542
4961
|
} catch {
|
|
5543
4962
|
return null;
|
|
5544
4963
|
}
|
|
@@ -5557,8 +4976,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
|
|
|
5557
4976
|
upsertBlock(file, nudgeMarkdown(smallRepo, client));
|
|
5558
4977
|
}
|
|
5559
4978
|
function upsertBlock(file, block) {
|
|
5560
|
-
|
|
5561
|
-
let existing =
|
|
4979
|
+
fs15.mkdirSync(path17.dirname(file), { recursive: true });
|
|
4980
|
+
let existing = fs15.existsSync(file) ? fs15.readFileSync(file, "utf8") : "";
|
|
5562
4981
|
const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
|
|
5563
4982
|
if (re.test(existing)) existing = existing.replace(re, block);
|
|
5564
4983
|
else existing = existing.length ? `${existing.replace(/\s*$/, "")}
|
|
@@ -5566,20 +4985,20 @@ function upsertBlock(file, block) {
|
|
|
5566
4985
|
${block}
|
|
5567
4986
|
` : `${block}
|
|
5568
4987
|
`;
|
|
5569
|
-
|
|
4988
|
+
fs15.writeFileSync(file, existing);
|
|
5570
4989
|
}
|
|
5571
4990
|
function removeBlock(file) {
|
|
5572
|
-
if (!
|
|
5573
|
-
const existing =
|
|
4991
|
+
if (!fs15.existsSync(file)) return false;
|
|
4992
|
+
const existing = fs15.readFileSync(file, "utf8");
|
|
5574
4993
|
const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
|
|
5575
4994
|
if (!re.test(existing)) return false;
|
|
5576
|
-
|
|
4995
|
+
fs15.writeFileSync(file, existing.replace(re, "\n"));
|
|
5577
4996
|
return true;
|
|
5578
4997
|
}
|
|
5579
4998
|
function readJson(file) {
|
|
5580
|
-
if (!
|
|
4999
|
+
if (!fs15.existsSync(file)) return {};
|
|
5581
5000
|
try {
|
|
5582
|
-
const parsed = JSON.parse(
|
|
5001
|
+
const parsed = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
5583
5002
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5584
5003
|
return parsed;
|
|
5585
5004
|
}
|
|
@@ -5599,7 +5018,7 @@ function escapeRe(s) {
|
|
|
5599
5018
|
}
|
|
5600
5019
|
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
5601
5020
|
function refreshLockPath(root) {
|
|
5602
|
-
return
|
|
5021
|
+
return path17.join(cacheDir(root), "refresh.lock");
|
|
5603
5022
|
}
|
|
5604
5023
|
async function refreshIfStale(root, opts = {}) {
|
|
5605
5024
|
const probe = probeFreshness(root);
|
|
@@ -5630,8 +5049,9 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5630
5049
|
const dir = vibgrateDir(root);
|
|
5631
5050
|
writeArtifacts(result.graph, {
|
|
5632
5051
|
root,
|
|
5633
|
-
|
|
5634
|
-
|
|
5052
|
+
graphPath: opts.graphPath,
|
|
5053
|
+
report: fs15.existsSync(path17.join(dir, "GRAPH_REPORT.md")),
|
|
5054
|
+
html: fs15.existsSync(path17.join(dir, "graph.html"))
|
|
5635
5055
|
});
|
|
5636
5056
|
}
|
|
5637
5057
|
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
@@ -5651,6 +5071,6 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5651
5071
|
}
|
|
5652
5072
|
}
|
|
5653
5073
|
|
|
5654
|
-
export { ASSISTANTS, CliError, ExitCode, FREE_PACK, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, SMALL_REPO_FILES, TOOLS, UsageError, analyze, applyCoverage,
|
|
5655
|
-
//# sourceMappingURL=chunk-
|
|
5656
|
-
//# sourceMappingURL=chunk-
|
|
5074
|
+
export { ASSISTANTS, CliError, ExitCode, FREE_PACK, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, SMALL_REPO_FILES, TOOLS, UsageError, analyze, applyCoverage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, clearStoredCredentials, countTokens, coveringTests, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, detectAssistants, detectRunner, detectServeLaunch, discover, driftCount, dsnCommand, ensureGitignored, epistemicBreakdown, fetchHostedDocsCached, findGitRoot, gitignoreEntryForCredentials, groundGraph, hasDrift, homeCredentialsPath, hostedBase, ingestHostForRegionId, installAssistant, loadCoverage, loadSnapshot, parseDsn, probeFreshness, projectCredentialsPath, publishPrivateLibrary, pushCommand, readScanArtifact, readStoredCredentials, refreshIfStale, refreshInstalledInstructions, renderToolResult, resolveCliInvocation, resolveDsn, resolveIngestHost, resolveLimits, scipEdges, searchSymbols, selectForBudget, shortestPath, symbolsFromApi, testsToRun, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, warmEmbedderInBackground, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
5075
|
+
//# sourceMappingURL=chunk-S6CYDZEW.js.map
|
|
5076
|
+
//# sourceMappingURL=chunk-S6CYDZEW.js.map
|