@vibgrate/cli 2026.703.7 → 2026.704.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.
@@ -1,4 +1,4 @@
1
- import { runCoreScan, writeJsonFile } from './chunk-QWS7M7DG.js';
1
+ import { runCoreScan, writeJsonFile } from './chunk-RJHYTD62.js';
2
2
  import * as path from 'path';
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
@@ -27,5 +27,5 @@ var baselineCommand = new Command("baseline").description("Create a drift baseli
27
27
  });
28
28
 
29
29
  export { baselineCommand, loadAdvancedScanHook, runBaseline };
30
- //# sourceMappingURL=chunk-ZNVLRKWK.js.map
31
- //# sourceMappingURL=chunk-ZNVLRKWK.js.map
30
+ //# sourceMappingURL=chunk-W4ANUENJ.js.map
31
+ //# sourceMappingURL=chunk-W4ANUENJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reporting/advanced-hook.ts","../src/reporting/commands/baseline.ts"],"names":[],"mappings":";;;;;;AAUA,eAAsB,oBAAA,GAA8D;AAClF,EAAA,OAAO,MAAA;AACT;;;ACLA,eAAsB,YAAY,OAAA,EAAgC;AAChE,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,sBAAsB,CAAC,CAAA;AAE7C,EAAA,MAAM,QAAA,GAAW,MAAM,oBAAA,EAAqB;AAC5C,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,OAAA,EAAS;AAAA,IAC1C,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa;AAAA,KACZ,QAAQ,CAAA;AAEX,EAAA,MAAM,YAAA,GAAoB,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,eAAe,CAAA;AACpE,EAAA,MAAM,aAAA,CAAc,cAAc,QAAQ,CAAA;AAC1C,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,QAAG,CAAA,GAAI,sBAAsB,KAAA,CAAM,IAAA,CAAK,yBAAyB,CAAC,CAAA,CAAE,CAAA;AAC5F,EAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,GAAA,CAAI,CAAA,kBAAA,EAAqB,SAAS,KAAA,CAAM,KAAK,MAAM,CAAC,CAAA;AACxE;AAEO,IAAM,eAAA,GAAkB,IAAI,OAAA,CAAQ,UAAU,EAClD,WAAA,CAAY,kCAAkC,CAAA,CAC9C,QAAA,CAAS,UAAU,kBAAA,EAAoB,GAAG,CAAA,CAC1C,MAAA,CAAO,OAAO,UAAA,KAAuB;AACpC,EAAA,MAAM,OAAA,GAAe,aAAQ,UAAU,CAAA;AACvC,EAAA,MAAM,YAAY,OAAO,CAAA;AAC3B,CAAC","file":"chunk-ZNVLRKWK.js","sourcesContent":["import type { AdvancedScanHook } from '../core-open/index.js';\n\n/**\n * Open build: there is no proprietary advanced-analysis hook.\n *\n * In the public, Apache-2.0 distribution every reporting command runs purely on\n * the open base engine (`runCoreScan` from `@vibgrate/core-open`). This shim\n * keeps the call sites in `scan`/`baseline` unchanged while always resolving to\n * `undefined`, so the scan falls back to the open base engine.\n */\nexport async function loadAdvancedScanHook(): Promise<AdvancedScanHook | undefined> {\n return undefined;\n}\n","import * as path from 'node:path';\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { runCoreScan } from '../../core-open/index.js';\nimport { writeJsonFile } from '../utils/fs.js';\nimport { loadAdvancedScanHook } from '../advanced-hook.js';\n\nexport async function runBaseline(rootDir: string): Promise<void> {\n console.log(chalk.dim('Creating baseline...'));\n\n const advanced = await loadAdvancedScanHook();\n const artifact = await runCoreScan(rootDir, {\n format: 'text',\n concurrency: 8,\n }, advanced);\n\n const baselinePath = path.join(rootDir, '.vibgrate', 'baseline.json');\n await writeJsonFile(baselinePath, artifact);\n console.log(chalk.green('✔') + ` Baseline saved to ${chalk.bold('.vibgrate/baseline.json')}`);\n console.log(chalk.dim(` Baseline score: ${artifact.drift.score}/100`));\n}\n\nexport const baselineCommand = new Command('baseline')\n .description('Create a drift baseline snapshot')\n .argument('[path]', 'Path to baseline', '.')\n .action(async (targetPath: string) => {\n const rootDir = path.resolve(targetPath);\n await runBaseline(rootDir);\n });\n"]}
1
+ {"version":3,"sources":["../src/reporting/advanced-hook.ts","../src/reporting/commands/baseline.ts"],"names":[],"mappings":";;;;;;AAUA,eAAsB,oBAAA,GAA8D;AAClF,EAAA,OAAO,MAAA;AACT;;;ACLA,eAAsB,YAAY,OAAA,EAAgC;AAChE,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,sBAAsB,CAAC,CAAA;AAE7C,EAAA,MAAM,QAAA,GAAW,MAAM,oBAAA,EAAqB;AAC5C,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,OAAA,EAAS;AAAA,IAC1C,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa;AAAA,KACZ,QAAQ,CAAA;AAEX,EAAA,MAAM,YAAA,GAAoB,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,eAAe,CAAA;AACpE,EAAA,MAAM,aAAA,CAAc,cAAc,QAAQ,CAAA;AAC1C,EAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,CAAM,QAAG,CAAA,GAAI,sBAAsB,KAAA,CAAM,IAAA,CAAK,yBAAyB,CAAC,CAAA,CAAE,CAAA;AAC5F,EAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,GAAA,CAAI,CAAA,kBAAA,EAAqB,SAAS,KAAA,CAAM,KAAK,MAAM,CAAC,CAAA;AACxE;AAEO,IAAM,eAAA,GAAkB,IAAI,OAAA,CAAQ,UAAU,EAClD,WAAA,CAAY,kCAAkC,CAAA,CAC9C,QAAA,CAAS,UAAU,kBAAA,EAAoB,GAAG,CAAA,CAC1C,MAAA,CAAO,OAAO,UAAA,KAAuB;AACpC,EAAA,MAAM,OAAA,GAAe,aAAQ,UAAU,CAAA;AACvC,EAAA,MAAM,YAAY,OAAO,CAAA;AAC3B,CAAC","file":"chunk-W4ANUENJ.js","sourcesContent":["import type { AdvancedScanHook } from '../core-open/index.js';\n\n/**\n * Open build: there is no proprietary advanced-analysis hook.\n *\n * In the public, Apache-2.0 distribution every reporting command runs purely on\n * the open base engine (`runCoreScan` from `@vibgrate/core-open`). This shim\n * keeps the call sites in `scan`/`baseline` unchanged while always resolving to\n * `undefined`, so the scan falls back to the open base engine.\n */\nexport async function loadAdvancedScanHook(): Promise<AdvancedScanHook | undefined> {\n return undefined;\n}\n","import * as path from 'node:path';\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { runCoreScan } from '../../core-open/index.js';\nimport { writeJsonFile } from '../utils/fs.js';\nimport { loadAdvancedScanHook } from '../advanced-hook.js';\n\nexport async function runBaseline(rootDir: string): Promise<void> {\n console.log(chalk.dim('Creating baseline...'));\n\n const advanced = await loadAdvancedScanHook();\n const artifact = await runCoreScan(rootDir, {\n format: 'text',\n concurrency: 8,\n }, advanced);\n\n const baselinePath = path.join(rootDir, '.vibgrate', 'baseline.json');\n await writeJsonFile(baselinePath, artifact);\n console.log(chalk.green('✔') + ` Baseline saved to ${chalk.bold('.vibgrate/baseline.json')}`);\n console.log(chalk.dim(` Baseline score: ${artifact.drift.score}/100`));\n}\n\nexport const baselineCommand = new Command('baseline')\n .description('Create a drift baseline snapshot')\n .argument('[path]', 'Path to baseline', '.')\n .action(async (targetPath: string) => {\n const rootDir = path.resolve(targetPath);\n await runBaseline(rootDir);\n });\n"]}
@@ -1,9 +1,10 @@
1
- import { langById, shortId, canonicalize, grammarSetVersion, hashBytes, hashString, langForExtension, setGrammarsOverride, parseSource } from './chunk-X5YT263H.js';
2
- import { writeTextFile, pathExists, readJsonFile, VERSION, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem } from './chunk-QWS7M7DG.js';
1
+ import { langById, shortId, canonicalize, grammarSetVersion, hashString, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-X5YT263H.js';
2
+ import { writeTextFile, pathExists, readJsonFile, VERSION, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem } from './chunk-RJHYTD62.js';
3
3
  import * as fs18 from 'fs';
4
4
  import { execFileSync } from 'child_process';
5
5
  import * as path18 from 'path';
6
6
  import ignore from 'ignore';
7
+ import * as v8 from 'v8';
7
8
  import Graph from 'graphology';
8
9
  import pagerank from 'graphology-metrics/centrality/pagerank.js';
9
10
  import betweenness from 'graphology-metrics/centrality/betweenness.js';
@@ -45,47 +46,174 @@ function isOwnBinary(binPath) {
45
46
  return false;
46
47
  }
47
48
  }
49
+ function isEphemeralNpxBinary(binPath) {
50
+ const NPX_SEGMENT = /[\\/]_npx[\\/]/;
51
+ if (NPX_SEGMENT.test(binPath)) return true;
52
+ try {
53
+ return NPX_SEGMENT.test(fs18.realpathSync(binPath));
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+ function isInstalledOwnBinary(binPath) {
59
+ return isOwnBinary(binPath) && !isEphemeralNpxBinary(binPath);
60
+ }
48
61
  var cached;
49
62
  function resolveCliInvocation(which) {
50
63
  if (!which && cached !== void 0) return cached;
51
64
  const lookup = which ?? whichOnPath;
52
65
  const vg = lookup("vg");
53
66
  let result;
54
- if (vg && isOwnBinary(vg)) {
67
+ if (vg && isInstalledOwnBinary(vg)) {
55
68
  result = "vg";
56
69
  } else {
57
70
  const vibgrate = lookup("vibgrate");
58
- result = vibgrate && isOwnBinary(vibgrate) ? "vibgrate" : NPX_INVOCATION;
71
+ result = vibgrate && isInstalledOwnBinary(vibgrate) ? "vibgrate" : NPX_INVOCATION;
59
72
  }
60
73
  if (!which) cached = result;
61
74
  return result;
62
75
  }
63
76
  var SKIP_DIRS = /* @__PURE__ */ new Set([
77
+ // Version control, IDE & tool metadata
64
78
  ".git",
65
- "node_modules",
79
+ ".svn",
80
+ ".hg",
81
+ ".idea",
82
+ ".vscode",
83
+ ".vs",
66
84
  ".vibgrate",
85
+ ".wrangler",
86
+ ".turbo",
87
+ ".cache",
88
+ // JavaScript / Node — installed dependency trees
89
+ "node_modules",
90
+ "bower_components",
91
+ "jspm_packages",
92
+ "web_modules",
93
+ ".pnpm-store",
94
+ ".yarn",
95
+ // JS/TS meta-framework & bundler build output
96
+ ".next",
97
+ ".nuxt",
98
+ ".output",
99
+ ".svelte-kit",
100
+ ".astro",
101
+ ".vercel",
102
+ ".netlify",
103
+ ".angular",
104
+ ".parcel-cache",
105
+ ".docusaurus",
106
+ "storybook-static",
107
+ // Generic build / dist / coverage output
67
108
  "dist",
68
109
  "build",
69
110
  "out",
70
111
  "coverage",
71
- ".next",
72
- ".nuxt",
73
- ".svelte-kit",
74
- ".turbo",
75
- ".cache",
112
+ // Vendored third-party source trees
76
113
  "vendor",
77
- "target",
78
- // rust / java
79
- "bin",
80
- "obj",
81
- // .NET
82
- "__pycache__",
114
+ "Pods",
115
+ "Carthage",
116
+ // Python — virtualenvs & tool caches
83
117
  ".venv",
84
118
  "venv",
119
+ "virtualenv",
85
120
  ".tox",
121
+ ".nox",
122
+ "__pycache__",
123
+ ".mypy_cache",
124
+ ".pytest_cache",
125
+ ".ruff_cache",
126
+ ".eggs",
127
+ ".ipynb_checkpoints",
128
+ // Ruby
129
+ ".bundle",
130
+ // Rust / Maven / sbt / Clojure — all build into target/
131
+ "target",
132
+ // JVM (Gradle / Maven) & .NET build output
86
133
  ".gradle",
87
- ".idea",
88
- ".vscode"
134
+ "bin",
135
+ "obj",
136
+ "TestResults",
137
+ // Swift / Xcode
138
+ ".build",
139
+ ".swiftpm",
140
+ "DerivedData",
141
+ // Dart / Flutter
142
+ ".dart_tool",
143
+ // Elixir / Erlang
144
+ "_build",
145
+ "deps",
146
+ // Haskell (Cabal / Stack)
147
+ "dist-newstyle",
148
+ ".stack-work",
149
+ // Infrastructure-as-code build/state
150
+ ".terraform",
151
+ ".serverless",
152
+ "cdk.out",
153
+ ".aws-sam"
154
+ ]);
155
+ var SKIP_FILES = /* @__PURE__ */ new Set([
156
+ // JavaScript / Node
157
+ "package-lock.json",
158
+ "npm-shrinkwrap.json",
159
+ "yarn.lock",
160
+ "pnpm-lock.yaml",
161
+ "bun.lockb",
162
+ "bun.lock",
163
+ "deno.lock",
164
+ ".pnp.cjs",
165
+ ".pnp.data.json",
166
+ ".pnp.loader.mjs",
167
+ // Python
168
+ "poetry.lock",
169
+ "pipfile.lock",
170
+ "pdm.lock",
171
+ "uv.lock",
172
+ "pylock.toml",
173
+ "conda-lock.yml",
174
+ // Ruby
175
+ "gemfile.lock",
176
+ // PHP
177
+ "composer.lock",
178
+ // Rust
179
+ "cargo.lock",
180
+ // Go
181
+ "go.sum",
182
+ "go.work.sum",
183
+ "gopkg.lock",
184
+ "glide.lock",
185
+ // .NET
186
+ "packages.lock.json",
187
+ // Java / JVM (Gradle)
188
+ "gradle.lockfile",
189
+ // Swift / Xcode
190
+ "package.resolved",
191
+ // Dart / Flutter
192
+ "pubspec.lock",
193
+ // Elixir
194
+ "mix.lock",
195
+ // Haskell (Cabal / Stack)
196
+ "cabal.project.freeze",
197
+ "stack.yaml.lock",
198
+ // Julia
199
+ "manifest.toml",
200
+ // R
201
+ "renv.lock",
202
+ // Perl (Carton)
203
+ "cpanfile.snapshot",
204
+ // CocoaPods / Carthage
205
+ "podfile.lock",
206
+ "cartfile.resolved",
207
+ // Terraform / IaC
208
+ ".terraform.lock.hcl",
209
+ // Helm
210
+ "chart.lock",
211
+ // C / C++ (Conan)
212
+ "conan.lock",
213
+ // Bazel
214
+ "module.bazel.lock",
215
+ // Nix
216
+ "flake.lock"
89
217
  ]);
90
218
  function toPosix(p) {
91
219
  return p.split(path18.sep).join("/");
@@ -115,6 +243,7 @@ function discover(options) {
115
243
  const rel2 = toPosix(path18.relative(root, abs));
116
244
  if (rel2.startsWith("..")) return;
117
245
  if (rel2 === "" || rootIg.ignores(rel2)) return;
246
+ if (SKIP_FILES.has(path18.basename(abs).toLowerCase())) return;
118
247
  const lang = langForExtension(path18.extname(abs));
119
248
  if (!lang || !allowLang(lang)) return;
120
249
  found.set(rel2, { rel: rel2, abs, lang });
@@ -152,6 +281,58 @@ var UsageError = class extends Error {
152
281
  this.name = "UsageError";
153
282
  }
154
283
  };
284
+ var MIB = 1024 * 1024;
285
+ var DEFAULT_MAX_FILE_BYTES = 2 * MIB;
286
+ var DEFAULT_MAX_FILES = 1e5;
287
+ var DEFAULT_TSC_MAX_FILES = 1e4;
288
+ var HEAP_GUARD_FRACTION = 0.9;
289
+ var ResourceLimitError = class extends Error {
290
+ isResourceLimitError = true;
291
+ constructor(message) {
292
+ super(message);
293
+ this.name = "ResourceLimitError";
294
+ }
295
+ };
296
+ function envInt(name, fallback) {
297
+ const raw = process.env[name];
298
+ if (raw === void 0 || raw.trim() === "") return fallback;
299
+ const n = Number(raw);
300
+ return Number.isInteger(n) && n >= 0 ? n : fallback;
301
+ }
302
+ function envIntOptional(name) {
303
+ const n = envInt(name, 0);
304
+ return n > 0 ? n : void 0;
305
+ }
306
+ function defaultMemoryBudgetMb() {
307
+ return Math.floor(v8.getHeapStatistics().heap_size_limit / MIB * HEAP_GUARD_FRACTION);
308
+ }
309
+ function resolveLimits(overrides = {}) {
310
+ return {
311
+ maxFileBytes: overrides.maxFileBytes ?? envInt("VG_MAX_FILE_BYTES", DEFAULT_MAX_FILE_BYTES),
312
+ maxFiles: overrides.maxFiles ?? envInt("VG_MAX_FILES", DEFAULT_MAX_FILES),
313
+ tscMaxFiles: overrides.tscMaxFiles ?? envInt("VG_TSC_MAX_FILES", DEFAULT_TSC_MAX_FILES),
314
+ memoryBudgetMb: overrides.memoryBudgetMb ?? envInt("VG_MEMORY_BUDGET_MB", defaultMemoryBudgetMb())
315
+ };
316
+ }
317
+ function envJobs() {
318
+ return envIntOptional("VG_JOBS");
319
+ }
320
+ function envWorkerHeapMb() {
321
+ return envIntOptional("VG_WORKER_HEAP_MB");
322
+ }
323
+ function checkMemoryBudget(phase, budgetMb) {
324
+ if (budgetMb <= 0) return;
325
+ const usedMb = process.memoryUsage().heapUsed / MIB;
326
+ if (usedMb <= budgetMb) return;
327
+ throw new ResourceLimitError(
328
+ `graph build stopped during ${phase}: heap use ${Math.round(usedMb)} MiB exceeds the ${budgetMb} MiB budget. Narrow the build (scope to sub-paths, add --exclude globs, or --only <langs>), raise the Node heap (NODE_OPTIONS=--max-old-space-size=8192), or set VG_MEMORY_BUDGET_MB (0 disables this safeguard).`
329
+ );
330
+ }
331
+ function formatBytes(n) {
332
+ if (n >= MIB) return `${(n / MIB).toFixed(1)} MiB`;
333
+ if (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;
334
+ return `${n} B`;
335
+ }
155
336
  var JS_TS_EXT = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
156
337
  var OTHER_EXT = [
157
338
  ".py",
@@ -1293,18 +1474,18 @@ var DEFAULT_INLINE_THRESHOLD = 24;
1293
1474
  async function parseFiles(files, options = {}) {
1294
1475
  const threshold = options.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;
1295
1476
  const cores = Math.max(1, os.cpus()?.length ?? 1);
1296
- const jobs = Math.max(1, options.jobs ?? (Math.min(cores - 1, files.length) || 1));
1477
+ const jobs = Math.max(1, options.jobs ?? envJobs() ?? (Math.min(cores - 1, files.length) || 1));
1297
1478
  const workerFile = resolveWorkerFile();
1298
1479
  const useInline = options.inline === true || jobs <= 1 || files.length < threshold || workerFile === null;
1299
1480
  if (useInline) {
1300
1481
  if (options.grammarsDir) setGrammarsOverride(options.grammarsDir);
1301
- return sortByRel(await parseInline(files, options.onProgress));
1482
+ return sortByRel(await parseInline(files, options));
1302
1483
  }
1303
- return sortByRel(
1304
- await parsePooled(files, jobs, workerFile, options.onProgress, options.grammarsDir)
1305
- );
1484
+ return sortByRel(await parsePooled(files, jobs, workerFile, options));
1306
1485
  }
1307
- async function parseInline(files, onProgress) {
1486
+ var MEM_CHECK_EVERY = 64;
1487
+ async function parseInline(files, options) {
1488
+ const { onProgress, memoryBudgetMb = 0 } = options;
1308
1489
  const out = [];
1309
1490
  onProgress?.(0, files.length);
1310
1491
  for (const file of files) {
@@ -1315,12 +1496,20 @@ async function parseInline(files, onProgress) {
1315
1496
  out.push(emptyParse(file, `parse failed: ${err.message}`));
1316
1497
  }
1317
1498
  onProgress?.(out.length, files.length);
1499
+ if (out.length % MEM_CHECK_EVERY === 0) checkMemoryBudget("parse", memoryBudgetMb);
1318
1500
  }
1319
1501
  return out;
1320
1502
  }
1321
- async function parsePooled(files, jobs, workerFile, onProgress, grammarsDir) {
1503
+ async function parsePooled(files, jobs, workerFile, options) {
1504
+ const { onProgress, grammarsDir, memoryBudgetMb = 0 } = options;
1322
1505
  const { default: Tinypool } = await import('tinypool');
1323
- const pool = new Tinypool({ filename: workerFile, maxThreads: jobs, minThreads: 1 });
1506
+ const workerHeapMb = envWorkerHeapMb();
1507
+ const pool = new Tinypool({
1508
+ filename: workerFile,
1509
+ maxThreads: jobs,
1510
+ minThreads: 1,
1511
+ ...workerHeapMb ? { resourceLimits: { maxOldGenerationSizeMb: workerHeapMb } } : {}
1512
+ });
1324
1513
  try {
1325
1514
  const total = files.length;
1326
1515
  const buckets = chunk(
@@ -1334,15 +1523,27 @@ async function parsePooled(files, jobs, workerFile, onProgress, grammarsDir) {
1334
1523
  (b) => pool.run({ tasks: b, grammarsDir }).then((r) => {
1335
1524
  done += b.length;
1336
1525
  onProgress?.(done, total);
1526
+ checkMemoryBudget("parse", memoryBudgetMb);
1337
1527
  return r;
1338
1528
  })
1339
1529
  )
1340
1530
  );
1341
1531
  return results.flat();
1532
+ } catch (err) {
1533
+ if (isWorkerOom(err)) {
1534
+ throw new ResourceLimitError(
1535
+ `graph build stopped: a parse worker exceeded its ${workerHeapMb ?? "?"} MiB heap cap (VG_WORKER_HEAP_MB). Raise the cap, exclude the offending files (--exclude), or run single-threaded with --jobs 1.`
1536
+ );
1537
+ }
1538
+ throw err;
1342
1539
  } finally {
1343
1540
  await pool.destroy();
1344
1541
  }
1345
1542
  }
1543
+ function isWorkerOom(err) {
1544
+ const e = err;
1545
+ return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
1546
+ }
1346
1547
  function resolveWorkerFile() {
1347
1548
  const here = path18.dirname(fileURLToPath(import.meta.url));
1348
1549
  const candidate = path18.join(here, "parse-worker.js");
@@ -1845,6 +2046,13 @@ async function buildGraph(options) {
1845
2046
  exclude: options.exclude,
1846
2047
  paths: options.paths
1847
2048
  });
2049
+ const limits = resolveLimits(options.limits);
2050
+ if (limits.maxFiles > 0 && files.length > limits.maxFiles) {
2051
+ throw new ResourceLimitError(
2052
+ `graph build stopped: ${files.length.toLocaleString()} files exceed the ${limits.maxFiles.toLocaleString()}-file limit. Scope the build (pass sub-paths, add --exclude globs, or --only <langs>), or set VG_MAX_FILES to raise the limit (0 disables it).`
2053
+ );
2054
+ }
2055
+ checkMemoryBudget("discovery", limits.memoryBudgetMb);
1848
2056
  const grammars = grammarSetVersion();
1849
2057
  const cache = loadCache(root, {
1850
2058
  toolVersion: VERSION,
@@ -1855,11 +2063,28 @@ async function buildGraph(options) {
1855
2063
  const fileStats = [];
1856
2064
  const toParse = [];
1857
2065
  const reused = [];
2066
+ const buildWarnings = [];
1858
2067
  for (const file of files) {
1859
- let hash = "";
1860
2068
  let stat;
1861
2069
  try {
1862
2070
  stat = fs18.statSync(file.abs);
2071
+ } catch {
2072
+ continue;
2073
+ }
2074
+ if (limits.maxFileBytes > 0 && stat.size > limits.maxFileBytes) {
2075
+ fileStats.push({
2076
+ rel: file.rel,
2077
+ size: stat.size,
2078
+ mtimeMs: stat.mtimeMs,
2079
+ hash: hashString(`vg:oversize:${stat.size}`)
2080
+ });
2081
+ buildWarnings.push(
2082
+ `${file.rel}: skipped \u2014 ${formatBytes(stat.size)} exceeds the ${formatBytes(limits.maxFileBytes)} per-file limit (set VG_MAX_FILE_BYTES to raise it, 0 to disable)`
2083
+ );
2084
+ continue;
2085
+ }
2086
+ let hash = "";
2087
+ try {
1863
2088
  hash = hashBytes(fs18.readFileSync(file.abs));
1864
2089
  } catch {
1865
2090
  continue;
@@ -1874,8 +2099,10 @@ async function buildGraph(options) {
1874
2099
  jobs: options.jobs,
1875
2100
  inline: options.inline,
1876
2101
  onProgress: options.onParseProgress,
1877
- grammarsDir: options.grammarsDir
2102
+ grammarsDir: options.grammarsDir,
2103
+ memoryBudgetMb: limits.memoryBudgetMb
1878
2104
  });
2105
+ checkMemoryBudget("parse", limits.memoryBudgetMb);
1879
2106
  for (const p of parsedNew) cache.set(p.rel, p);
1880
2107
  const currentRels = new Set(files.map((f) => f.rel));
1881
2108
  cache.prune(currentRels);
@@ -1883,14 +2110,21 @@ async function buildGraph(options) {
1883
2110
  const parses = [...reused, ...parsedNew].sort(
1884
2111
  (a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0
1885
2112
  );
1886
- const warnings = parses.flatMap((p) => p.warnings ?? []);
2113
+ const warnings = [...buildWarnings, ...parses.flatMap((p) => p.warnings ?? [])];
1887
2114
  const moduleResolver = buildModuleResolver(root, new Set(parses.map((p) => p.rel)));
1888
2115
  const resolved = resolve4(parses, moduleResolver);
2116
+ checkMemoryBudget("resolve", limits.memoryBudgetMb);
1889
2117
  const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
1890
2118
  let edges = resolved.edges;
1891
2119
  const resolvers = [...resolved.stats.resolvers];
1892
2120
  let tscStats;
1893
- const tsFiles = options.noTsc ? [] : files.filter((f) => f.lang.id === "ts" || f.lang.id === "tsx" || f.lang.id === "js").map((f) => ({ rel: f.rel, abs: f.abs }));
2121
+ let tsFiles = options.noTsc ? [] : files.filter((f) => (f.lang.id === "ts" || f.lang.id === "tsx" || f.lang.id === "js") && hashes.has(f.rel)).map((f) => ({ rel: f.rel, abs: f.abs }));
2122
+ if (limits.tscMaxFiles > 0 && tsFiles.length > limits.tscMaxFiles) {
2123
+ warnings.push(
2124
+ `typescript resolver skipped \u2014 ${tsFiles.length.toLocaleString()} TS/JS files exceed the ${limits.tscMaxFiles.toLocaleString()}-file limit; calls use the heuristic resolver (set VG_TSC_MAX_FILES to raise it, 0 to disable)`
2125
+ );
2126
+ tsFiles = [];
2127
+ }
1894
2128
  if (tsFiles.length) {
1895
2129
  const res = tsResolveEdges(root, tsFiles, resolved.nodes);
1896
2130
  if (res.stats.files > 0) {
@@ -1898,6 +2132,7 @@ async function buildGraph(options) {
1898
2132
  if (!resolvers.includes("tsc")) resolvers.unshift("tsc");
1899
2133
  tscStats = res.stats;
1900
2134
  }
2135
+ checkMemoryBudget("typescript resolution", limits.memoryBudgetMb);
1901
2136
  }
1902
2137
  const scip = options.noScip ? null : loadScipIndex(root, options.scip);
1903
2138
  let scipStats;
@@ -1912,6 +2147,7 @@ async function buildGraph(options) {
1912
2147
  const coverage = options.noCoverage ? null : loadCoverage(root, options.coverage);
1913
2148
  if (coverage) nodes = applyCoverage(nodes, coverage);
1914
2149
  const analysis = analyze(nodes, linked.edges, { cluster: options.cluster });
2150
+ checkMemoryBudget("analysis", limits.memoryBudgetMb);
1915
2151
  const languages = [...new Set(parses.map((p) => p.lang))].sort();
1916
2152
  const edgeKinds = [...new Set(analysis.edges.map((e) => e.kind))].sort();
1917
2153
  const corpusHash = computeCorpusHash(parses, hashes);
@@ -6007,9 +6243,9 @@ function assistantById(id) {
6007
6243
  }
6008
6244
  function detectServeLaunch(which = whichOnPath) {
6009
6245
  const vg = which("vg");
6010
- if (vg && isOwnBinary(vg)) return { command: "vg", args: ["serve"] };
6246
+ if (vg && isInstalledOwnBinary(vg)) return { command: "vg", args: ["serve"] };
6011
6247
  const vibgrate = which("vibgrate");
6012
- if (vibgrate && isOwnBinary(vibgrate)) {
6248
+ if (vibgrate && isInstalledOwnBinary(vibgrate)) {
6013
6249
  return {
6014
6250
  command: "vibgrate",
6015
6251
  args: ["serve"],
@@ -6288,6 +6524,6 @@ function spdx(ctx) {
6288
6524
  return JSON.stringify(doc, null, 2) + "\n";
6289
6525
  }
6290
6526
 
6291
- export { ASSISTANTS, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, SCHEMA_VERSION, SKIP_DIRS, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearStoredCredentials, cosine, countPending, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeSnapshot, writeStoredCredentials };
6292
- //# sourceMappingURL=chunk-WGXDYBAN.js.map
6293
- //# sourceMappingURL=chunk-WGXDYBAN.js.map
6527
+ export { ASSISTANTS, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearStoredCredentials, cosine, countPending, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeSnapshot, writeStoredCredentials };
6528
+ //# sourceMappingURL=chunk-YODVLV37.js.map
6529
+ //# sourceMappingURL=chunk-YODVLV37.js.map