@nudojs/cli 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +660 -182
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -16305,7 +16305,12 @@ function isRelFn(a) {
16305
16305
  if (!s || s.k !== "fn") return false;
16306
16306
  if (s.returnType === void 0) return false;
16307
16307
  if (s.paramTypes === void 0) return s.params.length === 0;
16308
- return s.paramTypes.length === s.params.length;
16308
+ if (s.paramTypes.length === s.params.length) return true;
16309
+ let required = 0;
16310
+ for (const p of s.params) {
16311
+ if (p && !p.startsWith("...") && !p.endsWith("?")) required++;
16312
+ }
16313
+ return s.paramTypes.length === required;
16309
16314
  }
16310
16315
  function litOfTerm(t) {
16311
16316
  return t.op === "lit" ? t.value : void 0;
@@ -18910,6 +18915,15 @@ function ok() {
18910
18915
  function fail(reason) {
18911
18916
  return { ok: false, reason };
18912
18917
  }
18918
+ function requiredFnArity(params) {
18919
+ if (!params) return 0;
18920
+ let n = 0;
18921
+ for (const p of params) {
18922
+ if (!p || p.startsWith("...") || p.endsWith("?")) continue;
18923
+ n++;
18924
+ }
18925
+ return n;
18926
+ }
18913
18927
  function leqAbs(src, tgt, opts = {}) {
18914
18928
  const phi2 = opts.phi ?? pTrue;
18915
18929
  return leqWithPred(src, tgt, phi2, opts.env ?? null, 0);
@@ -19062,8 +19076,12 @@ function leqShape(src, tgt, phi2, env, depth) {
19062
19076
  }
19063
19077
  if (t.k === "fn") {
19064
19078
  if (s.k !== "fn") return fail(`shape ${s.k} \u22AD fn`);
19065
- if (s.params.length !== t.params.length) {
19066
- return fail(`fn arity ${s.params.length} \u22AD ${t.params.length}`);
19079
+ const sReq = requiredFnArity(s.params);
19080
+ const tReq = requiredFnArity(t.params);
19081
+ if (sReq > tReq) {
19082
+ return fail(
19083
+ `fn arity required ${sReq} \u22AD target required ${tReq} (params ${s.params.length} / ${t.params.length})`
19084
+ );
19067
19085
  }
19068
19086
  if (t.returnType !== void 0) {
19069
19087
  const sr = s.returnType;
@@ -21319,13 +21337,39 @@ function formatShape(a) {
21319
21337
  case "tuple":
21320
21338
  return `[${s.elements.map(formatShape).join(", ")}]`;
21321
21339
  case "fn": {
21322
- if (s.paramTypes && s.paramTypes.length > 0) {
21323
- const ps = s.paramTypes.map((p) => formatShapeSlot(p));
21324
- const ret2 = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
21325
- return `(${ps.join(", ")}) => ${ret2}`;
21326
- }
21327
21340
  const ret = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
21328
- return `(${s.params.join(", ")}) => ${ret}`;
21341
+ const labels = s.params ?? [];
21342
+ const paramTypes = s.paramTypes;
21343
+ const hasMarkers = labels.some(
21344
+ (p) => p.startsWith("...") || p.endsWith("?")
21345
+ );
21346
+ const renderLabeled = () => {
21347
+ const ps = [];
21348
+ const n = paramTypes ? Math.max(paramTypes.length, labels.length) : labels.length;
21349
+ for (let i = 0; i < n; i++) {
21350
+ const label = labels[i];
21351
+ const type = paramTypes?.[i];
21352
+ const typeText = type !== void 0 ? formatShapeSlot(type) : void 0;
21353
+ if (label?.startsWith("...")) {
21354
+ ps.push(typeText ? `...${label.slice(3)}: ${typeText}` : label);
21355
+ } else if (label?.endsWith("?")) {
21356
+ ps.push(typeText ? `${label.slice(0, -1)}?: ${typeText}` : label);
21357
+ } else if (typeText !== void 0) {
21358
+ ps.push(typeText);
21359
+ } else if (label) {
21360
+ ps.push(label);
21361
+ }
21362
+ }
21363
+ return ps;
21364
+ };
21365
+ if (hasMarkers) {
21366
+ return `(${renderLabeled().join(", ")}) => ${ret}`;
21367
+ }
21368
+ if (paramTypes && paramTypes.length > 0) {
21369
+ const ps = paramTypes.map((p) => formatShapeSlot(p));
21370
+ return `(${ps.join(", ")}) => ${ret}`;
21371
+ }
21372
+ return `(${labels.join(", ")}) => ${ret}`;
21329
21373
  }
21330
21374
  case "brand":
21331
21375
  return `${s.name}`;
@@ -21453,20 +21497,17 @@ function any() {
21453
21497
  return makeBuilder(void 0, []);
21454
21498
  }
21455
21499
  function array(item) {
21456
- if (!isConstraint(item))
21457
- throw new Error("nudo: array(item) \u671F\u671B\u7EA6\u675F\u503C\uFF08number()/string()/\u2026\u6216\u5176\u7EC4\u5408\u5B50\uFF09");
21458
- return makeBuilder(void 0, [], { element: item });
21500
+ return makeBuilder(void 0, [], {
21501
+ element: asNestedConstraint(item, "array(item)")
21502
+ });
21459
21503
  }
21460
21504
  function shape(fields) {
21461
21505
  const mapped = {};
21462
21506
  for (const [k, v2] of Object.entries(fields)) {
21463
- if (!isConstraint(v2))
21464
- throw new Error(
21465
- `nudo: shape \u5B57\u6BB5 '${k}' \u671F\u671B\u7EA6\u675F\u503C\uFF08number()/string()/\u2026\u6216\u5176\u7EC4\u5408\u5B50\uFF09\uFF0C\u6536\u5230\u975E\u7EA6\u675F`
21466
- );
21507
+ const constraint = asNestedConstraint(v2, `shape \u5B57\u6BB5 '${k}'`);
21467
21508
  mapped[k] = {
21468
- constraint: v2,
21469
- ...v2.isOptional ? { optional: true } : {}
21509
+ constraint,
21510
+ ...constraint.isOptional ? { optional: true } : {}
21470
21511
  };
21471
21512
  }
21472
21513
  return makeBuilder(void 0, [], { fields: mapped });
@@ -21491,19 +21532,32 @@ function lit3(v2) {
21491
21532
  const prim2 = typeof v2 === "number" ? "number" : typeof v2 === "string" ? "string" : typeof v2 === "boolean" ? "boolean" : void 0;
21492
21533
  return makeBuilder(prim2, [eq(selfTerm(), lit(v2))]);
21493
21534
  }
21535
+ function asNestedConstraint(x, ctx) {
21536
+ if (isConstraint(x)) return toPlainConstraint(x);
21537
+ if (x === null || x === void 0 || typeof x === "number" || typeof x === "string" || typeof x === "boolean") {
21538
+ return toPlainConstraint(lit3(x));
21539
+ }
21540
+ throw new Error(
21541
+ `nudo: ${ctx} \u671F\u671B\u7EA6\u675F\u503C\uFF08number()/string()/\u2026\uFF09\u6216\u5177\u4F53\u5B57\u9762\u91CF\uFF0C\u6536\u5230\u975E\u7EA6\u675F`
21542
+ );
21543
+ }
21494
21544
  function union(...cs) {
21495
21545
  if (cs.length === 0)
21496
21546
  throw new Error("nudo union(): \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6210\u5458\u7EA6\u675F");
21497
- return makeBuilder(void 0, [], { members: cs.map(toPlainConstraint) });
21547
+ return makeBuilder(void 0, [], {
21548
+ members: cs.map((c) => asNestedConstraint(c, "union \u6210\u5458"))
21549
+ });
21498
21550
  }
21499
21551
  function fn(params, returns, opts) {
21500
21552
  const normalized = {};
21501
- for (const [k, v2] of Object.entries(params)) normalized[k] = toPlainConstraint(v2);
21553
+ for (const [k, v2] of Object.entries(params)) {
21554
+ normalized[k] = asNestedConstraint(v2, `fn \u53C2\u6570 '${k}'`);
21555
+ }
21502
21556
  return makeBuilder(void 0, [], {
21503
21557
  fn: {
21504
21558
  params: normalized,
21505
- ...returns !== void 0 ? { returns: toPlainConstraint(returns) } : {},
21506
- ...opts?.throws !== void 0 ? { throws: toPlainConstraint(opts.throws) } : {}
21559
+ ...returns !== void 0 ? { returns: asNestedConstraint(returns, "fn \u8FD4\u56DE\u503C") } : {},
21560
+ ...opts?.throws !== void 0 ? { throws: asNestedConstraint(opts.throws, "fn throws") } : {}
21507
21561
  }
21508
21562
  });
21509
21563
  }
@@ -31703,6 +31757,7 @@ __export(src_exports, {
31703
31757
  registerBClass: () => registerBClass,
31704
31758
  relationFingerprint: () => relationFingerprint,
31705
31759
  relationFn: () => relationFn,
31760
+ requiredFnArity: () => requiredFnArity,
31706
31761
  resetAbsCallBudget: () => resetAbsCallBudget,
31707
31762
  resetCheckSourceMemo: () => resetCheckSourceMemo,
31708
31763
  resetFnFpCache: () => resetFnFpCache,
@@ -60630,10 +60685,6 @@ function absLit(v2) {
60630
60685
  if (v2 === null) return absNullLit();
60631
60686
  return absUndefLit();
60632
60687
  }
60633
- function absUnion(members) {
60634
- if (members.length === 0) return absExact({ k: "never" });
60635
- return members.reduce((acc, m) => joinAbs(acc, m));
60636
- }
60637
60688
  function tryParseConstraint(expr) {
60638
60689
  const s = expr.trim();
60639
60690
  if (!CONSTRAINT_EXPR_RE.test(s)) return void 0;
@@ -60662,15 +60713,15 @@ function flattenPreds(preds) {
60662
60713
  function constraintSelfEqLit(c, selfIds) {
60663
60714
  const leaves = flattenPreds(c.preds);
60664
60715
  const eqs = leaves.filter((p2) => p2.op === "eq");
60665
- if (eqs.length !== 1) return void 0;
60716
+ if (eqs.length !== 1) return { found: false };
60666
60717
  const p = eqs[0];
60667
- if (p.op !== "eq") return void 0;
60718
+ if (p.op !== "eq") return { found: false };
60668
60719
  const isSelf = (t) => t.op === "var" && typeof t.id === "string" && selfIds.includes(t.id);
60669
60720
  const a = p.a;
60670
60721
  const b = p.b;
60671
- if (isSelf(a) && b.op === "lit") return b.value;
60672
- if (isSelf(b) && a.op === "lit") return a.value;
60673
- return void 0;
60722
+ if (isSelf(a) && b.op === "lit") return { found: true, value: b.value };
60723
+ if (isSelf(b) && a.op === "lit") return { found: true, value: a.value };
60724
+ return { found: false };
60674
60725
  }
60675
60726
  function constraintToCaseArgAbs(c) {
60676
60727
  if (c.members && c.members.length > 0) {
@@ -60679,10 +60730,32 @@ function constraintToCaseArgAbs(c) {
60679
60730
  return { shape: { k: "sum", members: parts }, conf: "path" };
60680
60731
  }
60681
60732
  const selfIds = [SELF, "__arg", "__nudo_self__"];
60682
- const lv = constraintSelfEqLit(c, selfIds);
60683
- if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
60684
- if (typeof lv === "string") return strLit(lv);
60685
- if (typeof lv === "boolean") return boolLit(lv);
60733
+ const litRes = constraintSelfEqLit(c, selfIds);
60734
+ if (litRes.found) {
60735
+ const lv = litRes.value;
60736
+ if (typeof lv === "number" && !Number.isNaN(lv)) return numLit(lv);
60737
+ if (typeof lv === "string") return strLit(lv);
60738
+ if (typeof lv === "boolean") return boolLit(lv);
60739
+ if (lv === null) return absNullLit();
60740
+ return absUndefLit();
60741
+ }
60742
+ if (c.element) {
60743
+ return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element) });
60744
+ }
60745
+ if (c.fields) {
60746
+ const slots = {};
60747
+ for (const [key, field] of Object.entries(c.fields)) {
60748
+ slots[key] = { value: constraintToCaseArgAbs(field.constraint) };
60749
+ }
60750
+ return absExact({ k: "obj", slots });
60751
+ }
60752
+ const bareScalar = !c.fields && !c.element && !c.members && !c.fn && c.int !== true && c.preds.length === 0;
60753
+ if (bareScalar) {
60754
+ if (c.prim === "number" || c.prim === "string" || c.prim === "boolean") {
60755
+ return absExact({ k: "prim", type: c.prim });
60756
+ }
60757
+ if (!c.prim) return absUnknown2();
60758
+ }
60686
60759
  return constraintToEntryAbs(c, "__arg");
60687
60760
  }
60688
60761
  function parseCaseArgExpr(expr) {
@@ -60694,49 +60767,16 @@ function parseCaseArgExpr(expr) {
60694
60767
  return absUnknown2();
60695
60768
  }
60696
60769
  }
60697
- return parseLegacyTypeExpr(expr);
60770
+ return parseLiteralOrStructure(expr);
60698
60771
  }
60699
- function parseLegacyTypeExpr(expr) {
60772
+ function parseLiteralOrStructure(expr) {
60700
60773
  const s = expr.trim();
60701
- if (s === "T.number") return num();
60702
- if (s === "T.string") return str();
60703
- if (s === "T.boolean") return bool();
60704
- if (s === "T.unknown") return absUnknown2();
60705
- if (s === "T.never") return absExact({ k: "never" });
60706
- if (s === "T.null") return absNullLit();
60707
- if (s === "T.undefined") return absUndefLit();
60708
60774
  if (s === "true") return absLit(true);
60709
60775
  if (s === "false") return absLit(false);
60710
60776
  if (s === "null") return absLit(null);
60711
60777
  if (s === "undefined") return absLit(void 0);
60712
- const literalMatch = s.match(/^T\.literal\((.+)\)$/);
60713
- if (literalMatch) {
60714
- return absLit(parsePrimitiveValue(literalMatch[1].trim()));
60715
- }
60716
- if (s.startsWith("T.union(") && s.endsWith(")")) {
60717
- const inner = s.slice("T.union(".length, -1);
60718
- const args = splitTopLevelArgs(inner);
60719
- return absUnion(args.map(parseLegacyTypeExpr));
60720
- }
60721
- if (s.startsWith("T.array(") && s.endsWith(")")) {
60722
- const inner = s.slice("T.array(".length, -1);
60723
- return absExact({ k: "arr", element: parseLegacyTypeExpr(inner) });
60724
- }
60725
- if (s.startsWith("T.tuple(") && s.endsWith(")")) {
60726
- const inner = s.slice("T.tuple(".length, -1).trim();
60727
- if (inner.startsWith("[") && inner.endsWith("]")) {
60728
- const elements = splitTopLevelArgs(inner.slice(1, -1));
60729
- return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
60730
- }
60731
- return absExact({ k: "tuple", elements: [] });
60732
- }
60733
- if (s.startsWith("T.object(") && s.endsWith(")")) {
60734
- const inner = s.slice("T.object(".length, -1).trim();
60735
- if (inner.startsWith("{") && inner.endsWith("}")) {
60736
- return parseObjectLiteral(inner.slice(1, -1).trim());
60737
- }
60738
- return absExact({ k: "obj", slots: {} });
60739
- }
60778
+ if (s === "unknown" || s === "any") return absUnknown2();
60779
+ if (s === "never") return absExact({ k: "never" });
60740
60780
  if (findTopLevelArrow(s) !== -1 || /^function\s*[\w$]*\s*\(/.test(s)) {
60741
60781
  const fnExpr = parseArrowFunctionExpr(s);
60742
60782
  if (fnExpr) {
@@ -60754,7 +60794,7 @@ function parseLegacyTypeExpr(expr) {
60754
60794
  const content = s.slice(1, -1).trim();
60755
60795
  if (!content) return absExact({ k: "tuple", elements: [] });
60756
60796
  const elements = splitTopLevelArgs(content);
60757
- return absExact({ k: "tuple", elements: elements.map(parseLegacyTypeExpr) });
60797
+ return absExact({ k: "tuple", elements: elements.map((e) => parseCaseArgExpr(e)) });
60758
60798
  }
60759
60799
  return absUnknown2();
60760
60800
  }
@@ -60767,21 +60807,10 @@ function parseObjectLiteral(content) {
60767
60807
  if (colonIdx === -1) continue;
60768
60808
  const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
60769
60809
  const val = entry.slice(colonIdx + 1).trim();
60770
- slots[key] = { value: parseLegacyTypeExpr(val) };
60810
+ slots[key] = { value: parseCaseArgExpr(val) };
60771
60811
  }
60772
60812
  return absExact({ k: "obj", slots });
60773
60813
  }
60774
- function parsePrimitiveValue(s) {
60775
- if (s === "true") return true;
60776
- if (s === "false") return false;
60777
- if (s === "null") return null;
60778
- if (s === "undefined") return void 0;
60779
- if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
60780
- if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
60781
- return s.slice(1, -1);
60782
- }
60783
- return s;
60784
- }
60785
60814
  function splitTopLevelArgs(s) {
60786
60815
  const result = [];
60787
60816
  let depth = 0;
@@ -61194,7 +61223,7 @@ function findReplaceSeparator(raw) {
61194
61223
  }
61195
61224
  if (ch === " " && depth === 0) {
61196
61225
  const rest = raw.slice(i + 1).trimStart();
61197
- if (rest.startsWith("T.") || rest.startsWith("{") || rest.startsWith("[") || rest.startsWith('"') || rest.startsWith("'") || /^-?\d/.test(rest) || rest === "true" || rest === "false" || rest === "null" || rest === "undefined") {
61226
+ if (CONSTRAINT_EXPR_RE.test(rest) || rest.startsWith("{") || rest.startsWith("[") || rest.startsWith('"') || rest.startsWith("'") || rest === "true" || rest === "false" || rest === "null" || rest === "undefined" || rest === "unknown" || rest === "any" || rest === "never" || /^-?\d+(\.\d+)?$/.test(rest) || rest.includes("=>") || /^function\s*[\w$]*\s*\(/.test(rest)) {
61198
61227
  return i;
61199
61228
  }
61200
61229
  }
@@ -61248,7 +61277,7 @@ var init_directives = __esm({
61248
61277
  PURE_REGEX = /@nudo:pure\b/g;
61249
61278
  SKIP_REGEX = /@nudo:skip(?:\s+(.+))?/g;
61250
61279
  SAMPLE_REGEX = /@nudo:sample\s+(\d+)/g;
61251
- CONSTRAINT_EXPR_RE = /^(number|string|boolean|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
61280
+ CONSTRAINT_EXPR_RE = /^(number|string|boolean|any|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/;
61252
61281
  AS_REGEX = /^\s*@nudo:as\s+(.+)/;
61253
61282
  REPLACE_REGEX = /^\s*@nudo:replace\s+(.+)/;
61254
61283
  ENV_REGEX = /^\/\s*@nudo:env\s+(.+)/;
@@ -61461,6 +61490,19 @@ function envFn(paramTypes, returnType, apply, opts) {
61461
61490
  if (opts?.conf && opts.conf !== "exact") a.conf = opts.conf;
61462
61491
  return a;
61463
61492
  }
61493
+ function envFnVariadic(restType, returnType, opts) {
61494
+ const required = opts?.required ?? [];
61495
+ const restName = opts?.restName ?? "...rest";
61496
+ const paramTypes = [...required, restType];
61497
+ const params = [
61498
+ ...required.map((_, i) => `x${i}`),
61499
+ restName
61500
+ ];
61501
+ return envFn(paramTypes, returnType, opts?.apply, {
61502
+ params,
61503
+ ...opts?.name ? { name: opts.name } : {}
61504
+ });
61505
+ }
61464
61506
  function slotsOf(entries) {
61465
61507
  const out = {};
61466
61508
  for (const [k, v2] of Object.entries(entries)) {
@@ -62235,6 +62277,106 @@ function defineEnv3() {
62235
62277
  chmodSync: envFn([prim.str(), prim.num()], undef2()),
62236
62278
  chownSync: envFn([prim.str(), prim.num(), prim.num()], undef2()),
62237
62279
  accessSync: envFn([prim.str(), prim.num()], undef2()),
62280
+ // Callback-style async on `fs` / `node:fs` (Node actual API).
62281
+ // Promise APIs live only under fs.promises / node:fs/promises.
62282
+ readFile: envFn(
62283
+ [prim.str(), prim.unknown, prim.unknown],
62284
+ undef2(),
62285
+ void 0,
62286
+ { params: ["path", "options?", "callback"] }
62287
+ ),
62288
+ writeFile: envFn(
62289
+ [prim.str(), unionOf(prim.str(), bufferBrand), prim.unknown],
62290
+ undef2(),
62291
+ void 0,
62292
+ { params: ["path", "data", "callback"] }
62293
+ ),
62294
+ mkdir: envFn(
62295
+ [prim.str(), prim.unknown, prim.unknown],
62296
+ undef2(),
62297
+ void 0,
62298
+ { params: ["path", "options?", "callback"] }
62299
+ ),
62300
+ rm: envFn(
62301
+ [prim.str(), prim.unknown, prim.unknown],
62302
+ undef2(),
62303
+ void 0,
62304
+ { params: ["path", "options?", "callback"] }
62305
+ ),
62306
+ stat: envFn(
62307
+ [prim.str(), prim.unknown, prim.unknown],
62308
+ undef2(),
62309
+ void 0,
62310
+ { params: ["path", "options?", "callback"] }
62311
+ ),
62312
+ readdir: envFn(
62313
+ [prim.str(), prim.unknown, prim.unknown],
62314
+ undef2(),
62315
+ void 0,
62316
+ { params: ["path", "options?", "callback"] }
62317
+ ),
62318
+ access: envFn(
62319
+ [prim.str(), prim.unknown, prim.unknown],
62320
+ undef2(),
62321
+ void 0,
62322
+ { params: ["path", "mode?", "callback"] }
62323
+ ),
62324
+ appendFile: envFn(
62325
+ [prim.str(), unionOf(prim.str(), bufferBrand), prim.unknown],
62326
+ undef2(),
62327
+ void 0,
62328
+ { params: ["path", "data", "callback"] }
62329
+ ),
62330
+ unlink: envFn(
62331
+ [prim.str(), prim.unknown, prim.unknown],
62332
+ undef2(),
62333
+ void 0,
62334
+ { params: ["path", "options?", "callback"] }
62335
+ ),
62336
+ rename: envFn(
62337
+ [prim.str(), prim.str(), prim.unknown],
62338
+ undef2(),
62339
+ void 0,
62340
+ { params: ["oldPath", "newPath", "callback"] }
62341
+ ),
62342
+ copyFile: envFn(
62343
+ [prim.str(), prim.str(), prim.unknown],
62344
+ undef2(),
62345
+ void 0,
62346
+ { params: ["src", "dest", "callback"] }
62347
+ ),
62348
+ realpath: envFn(
62349
+ [prim.str(), prim.unknown, prim.unknown],
62350
+ undef2(),
62351
+ void 0,
62352
+ { params: ["path", "options?", "callback"] }
62353
+ ),
62354
+ readlink: envFn(
62355
+ [prim.str(), prim.unknown, prim.unknown],
62356
+ undef2(),
62357
+ void 0,
62358
+ { params: ["path", "options?", "callback"] }
62359
+ ),
62360
+ symlink: envFn(
62361
+ [prim.str(), prim.str(), prim.unknown, prim.unknown],
62362
+ undef2(),
62363
+ void 0,
62364
+ { params: ["target", "path", "type?", "callback"] }
62365
+ ),
62366
+ chmod: envFn(
62367
+ [prim.str(), prim.num(), prim.unknown],
62368
+ undef2(),
62369
+ void 0,
62370
+ { params: ["path", "mode", "callback"] }
62371
+ ),
62372
+ open: envFn(
62373
+ [prim.str(), prim.unknown, prim.unknown],
62374
+ undef2(),
62375
+ void 0,
62376
+ { params: ["path", "flags?", "callback"] }
62377
+ )
62378
+ };
62379
+ const fsPromisesModule = {
62238
62380
  readFile: envFn(
62239
62381
  [prim.str(), prim.unknown],
62240
62382
  promiseOf(unionOf(prim.str(), bufferBrand))
@@ -62253,7 +62395,19 @@ function defineEnv3() {
62253
62395
  [prim.str(), prim.unknown],
62254
62396
  promiseOf(arrOf(prim.unknown))
62255
62397
  ),
62256
- access: envFn([prim.str(), prim.num()], promiseOf(undef2()))
62398
+ access: envFn([prim.str(), prim.num()], promiseOf(undef2())),
62399
+ appendFile: envFn(
62400
+ [prim.str(), unionOf(prim.str(), bufferBrand)],
62401
+ promiseOf(undef2())
62402
+ ),
62403
+ unlink: envFn([prim.str()], promiseOf(undef2())),
62404
+ rename: envFn([prim.str(), prim.str()], promiseOf(undef2())),
62405
+ copyFile: envFn([prim.str(), prim.str()], promiseOf(undef2())),
62406
+ realpath: envFn([prim.str()], promiseOf(prim.str())),
62407
+ readlink: envFn([prim.str()], promiseOf(prim.str())),
62408
+ symlink: envFn([prim.str(), prim.str()], promiseOf(undef2())),
62409
+ chmod: envFn([prim.str(), prim.num()], promiseOf(undef2())),
62410
+ open: envFn([prim.str(), prim.str()], promiseOf(prim.unknown))
62257
62411
  };
62258
62412
  const strImpl1Abs = (fn2) => (args) => {
62259
62413
  const a = absStr2(args[0]);
@@ -62283,10 +62437,21 @@ function defineEnv3() {
62283
62437
  return p !== void 0 ? boolLit(nodePath.isAbsolute(p)) : void 0;
62284
62438
  };
62285
62439
  const pathModule = {
62286
- join: envFn([prim.str(), prim.str()], prim.str(), pathJoinAbs),
62287
- resolve: envFn([prim.str()], prim.str(), pathResolveAbs),
62440
+ // Node variadic: declare min required arity + rest label (not n fixed slots).
62441
+ join: envFnVariadic(prim.str(), prim.str(), {
62442
+ apply: pathJoinAbs,
62443
+ required: [prim.str()],
62444
+ restName: "...paths"
62445
+ }),
62446
+ resolve: envFnVariadic(prim.str(), prim.str(), {
62447
+ apply: pathResolveAbs,
62448
+ restName: "...paths"
62449
+ }),
62288
62450
  dirname: envFn([prim.str()], prim.str(), strImpl1Abs(nodePath.dirname)),
62289
- basename: envFn([prim.str(), prim.str()], prim.str(), pathBasenameAbs),
62451
+ // ext is optional in Node — required arity 1; label+type render `ext?: string`.
62452
+ basename: envFn([prim.str(), prim.str()], prim.str(), pathBasenameAbs, {
62453
+ params: ["path", "ext?"]
62454
+ }),
62290
62455
  extname: envFn([prim.str()], prim.str(), strImpl1Abs(nodePath.extname)),
62291
62456
  relative: envFn([prim.str(), prim.str()], prim.str(), pathRelativeAbs),
62292
62457
  normalize: envFn([prim.str()], prim.str(), strImpl1Abs(nodePath.normalize)),
@@ -62364,6 +62529,7 @@ function defineEnv3() {
62364
62529
  toJSON: envFn([], prim.str())
62365
62530
  });
62366
62531
  const urlModule = {
62532
+ // base is optional in Node — required arity 1; label+type render `base?: string`.
62367
62533
  URL: envFn(
62368
62534
  [prim.str(), prim.str()],
62369
62535
  nodeUrlObj,
@@ -62391,7 +62557,8 @@ function defineEnv3() {
62391
62557
  } catch {
62392
62558
  return void 0;
62393
62559
  }
62394
- }
62560
+ },
62561
+ { params: ["href", "base?"] }
62395
62562
  ),
62396
62563
  URLSearchParams: envFn(
62397
62564
  [prim.unknown],
@@ -62489,15 +62656,27 @@ function defineEnv3() {
62489
62656
  fork: envFn([prim.str(), arrOf(prim.str()), prim.unknown], prim.unknown)
62490
62657
  };
62491
62658
  const utilModule = {
62492
- promisify: envFn([prim.unknown], prim.unknown),
62659
+ // Signature-level: promisify preserves fn-ness only as unknown (no generic).
62660
+ promisify: envFn([prim.unknown], prim.unknown, void 0, { name: "util.promisify" }),
62493
62661
  inspect: envFn([prim.unknown, prim.unknown], prim.str()),
62494
- format: envFn([prim.str()], prim.str()),
62662
+ format: envFnVariadic(prim.unknown, prim.str(), {
62663
+ // util.format() with zero args is valid in Node.
62664
+ restName: "...args",
62665
+ name: "util.format"
62666
+ }),
62667
+ callbackify: envFn([prim.unknown], prim.unknown),
62668
+ deprecate: envFn([prim.unknown, prim.str()], prim.unknown),
62669
+ inherits: envFn([prim.unknown, prim.unknown], undef2()),
62670
+ isDeepStrictEqual: envFn([prim.unknown, prim.unknown], prim.bool()),
62495
62671
  types: objAbs({
62496
62672
  isDate: envFn([prim.unknown], prim.bool()),
62497
62673
  isRegExp: envFn([prim.unknown], prim.bool()),
62498
62674
  isPromise: envFn([prim.unknown], prim.bool()),
62499
62675
  isArrayBuffer: envFn([prim.unknown], prim.bool()),
62500
- isTypedArray: envFn([prim.unknown], prim.bool())
62676
+ isTypedArray: envFn([prim.unknown], prim.bool()),
62677
+ isNativeError: envFn([prim.unknown], prim.bool()),
62678
+ isAsyncFunction: envFn([prim.unknown], prim.bool()),
62679
+ isGeneratorFunction: envFn([prim.unknown], prim.bool())
62501
62680
  }),
62502
62681
  TextEncoder: envFn(
62503
62682
  [],
@@ -62508,6 +62687,79 @@ function defineEnv3() {
62508
62687
  objAbs({ decode: envFn([prim.unknown], prim.str()) })
62509
62688
  )
62510
62689
  };
62690
+ const eventEmitterShape = objAbs({
62691
+ on: envFn([prim.str(), prim.unknown], prim.unknown),
62692
+ once: envFn([prim.str(), prim.unknown], prim.unknown),
62693
+ off: envFn([prim.str(), prim.unknown], prim.unknown),
62694
+ emit: envFn([prim.str()], prim.bool()),
62695
+ addListener: envFn([prim.str(), prim.unknown], prim.unknown),
62696
+ removeListener: envFn([prim.str(), prim.unknown], prim.unknown),
62697
+ removeAllListeners: envFn([prim.str()], prim.unknown),
62698
+ listeners: envFn([prim.str()], arrOf(prim.unknown)),
62699
+ listenerCount: envFn([prim.str()], prim.num()),
62700
+ eventNames: envFn([], arrOf(prim.str()))
62701
+ });
62702
+ const eventEmitterInstance = brandOf2("EventEmitter", eventEmitterShape);
62703
+ const EventEmitterCtor = envFn([prim.unknown], eventEmitterInstance, void 0, {
62704
+ params: ["options?"]
62705
+ });
62706
+ const eventsModule = {
62707
+ EventEmitter: EventEmitterCtor,
62708
+ once: envFn(
62709
+ [prim.unknown, prim.str()],
62710
+ promiseOf(arrOf(prim.unknown))
62711
+ ),
62712
+ on: envFn([prim.unknown, prim.str()], prim.unknown),
62713
+ listenerCount: envFn([prim.unknown, prim.str()], prim.num())
62714
+ };
62715
+ const streamIoMethods = {
62716
+ on: envFn([prim.str(), prim.unknown], prim.unknown),
62717
+ once: envFn([prim.str(), prim.unknown], prim.unknown),
62718
+ off: envFn([prim.str(), prim.unknown], prim.unknown),
62719
+ emit: envFn([prim.str()], prim.bool()),
62720
+ pipe: envFn([prim.unknown], prim.unknown),
62721
+ destroy: envFn([prim.unknown], undef2()),
62722
+ read: envFn([prim.num()], prim.unknown),
62723
+ write: envFn([unionOf(prim.str(), bufferBrand)], prim.bool()),
62724
+ end: envFn([prim.unknown], undef2()),
62725
+ pause: envFn([], prim.unknown),
62726
+ resume: envFn([], prim.unknown),
62727
+ setEncoding: envFn([prim.str()], prim.unknown)
62728
+ };
62729
+ const streamCtor = (brandName) => envFn([prim.unknown], brandOf2(brandName, objAbs(streamIoMethods)), void 0, {
62730
+ params: ["options?"]
62731
+ });
62732
+ const streamModule = {
62733
+ Readable: streamCtor("Readable"),
62734
+ Writable: streamCtor("Writable"),
62735
+ Duplex: streamCtor("Duplex"),
62736
+ Transform: streamCtor("Transform"),
62737
+ pipeline: envFnVariadic(prim.unknown, promiseOf(undef2()), {
62738
+ restName: "...streams"
62739
+ }),
62740
+ finished: envFn([prim.unknown], promiseOf(undef2()))
62741
+ };
62742
+ const parsedQueryString = brandOf2(
62743
+ "ParsedQueryString",
62744
+ objAbs({})
62745
+ );
62746
+ const querystringModule = {
62747
+ // sep/eq/options optional — required arity 1; labels+types render `sep?: string` etc.
62748
+ parse: envFn(
62749
+ [prim.str(), prim.str(), prim.str(), prim.unknown],
62750
+ parsedQueryString,
62751
+ void 0,
62752
+ { params: ["str", "sep?", "eq?", "options?"] }
62753
+ ),
62754
+ stringify: envFn(
62755
+ [prim.unknown, prim.str(), prim.str(), prim.unknown],
62756
+ prim.str(),
62757
+ void 0,
62758
+ { params: ["obj", "sep?", "eq?", "options?"] }
62759
+ ),
62760
+ escape: envFn([prim.str()], prim.str()),
62761
+ unescape: envFn([prim.str()], prim.str())
62762
+ };
62511
62763
  const nodeGlobals = {
62512
62764
  process: objAbs({
62513
62765
  env: objAbs({}),
@@ -62577,15 +62829,8 @@ function defineEnv3() {
62577
62829
  const modules = {
62578
62830
  fs: fsModule,
62579
62831
  "node:fs": fsModule,
62580
- "node:fs/promises": {
62581
- readFile: fsModule.readFile,
62582
- writeFile: fsModule.writeFile,
62583
- mkdir: fsModule.mkdir,
62584
- rm: fsModule.rm,
62585
- stat: fsModule.stat,
62586
- readdir: fsModule.readdir,
62587
- access: fsModule.access
62588
- },
62832
+ "fs/promises": fsPromisesModule,
62833
+ "node:fs/promises": fsPromisesModule,
62589
62834
  path: pathModule,
62590
62835
  "node:path": pathModule,
62591
62836
  os: osModule,
@@ -62597,7 +62842,13 @@ function defineEnv3() {
62597
62842
  child_process: childProcessModule,
62598
62843
  "node:child_process": childProcessModule,
62599
62844
  util: utilModule,
62600
- "node:util": utilModule
62845
+ "node:util": utilModule,
62846
+ events: eventsModule,
62847
+ "node:events": eventsModule,
62848
+ stream: streamModule,
62849
+ "node:stream": streamModule,
62850
+ querystring: querystringModule,
62851
+ "node:querystring": querystringModule
62601
62852
  };
62602
62853
  return {
62603
62854
  globals: { ...esEnv.globals, ...nodeGlobals },
@@ -63541,7 +63792,7 @@ var require_util2 = __commonJS({
63541
63792
  return path;
63542
63793
  }
63543
63794
  exports.normalize = normalize;
63544
- function join9(aRoot, aPath) {
63795
+ function join10(aRoot, aPath) {
63545
63796
  if (aRoot === "") {
63546
63797
  aRoot = ".";
63547
63798
  }
@@ -63573,7 +63824,7 @@ var require_util2 = __commonJS({
63573
63824
  }
63574
63825
  return joined;
63575
63826
  }
63576
- exports.join = join9;
63827
+ exports.join = join10;
63577
63828
  exports.isAbsolute = function(aPath) {
63578
63829
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
63579
63830
  };
@@ -63746,7 +63997,7 @@ var require_util2 = __commonJS({
63746
63997
  parsed.path = parsed.path.substring(0, index + 1);
63747
63998
  }
63748
63999
  }
63749
- sourceURL = join9(urlGenerate(parsed), sourceURL);
64000
+ sourceURL = join10(urlGenerate(parsed), sourceURL);
63750
64001
  }
63751
64002
  return normalize(sourceURL);
63752
64003
  }
@@ -74272,7 +74523,7 @@ ${lanes.join("\n")}
74272
74523
  return process.memoryUsage().heapUsed;
74273
74524
  },
74274
74525
  getFileSize(path) {
74275
- const stat = statSync9(path);
74526
+ const stat = statSync10(path);
74276
74527
  if (stat == null ? void 0 : stat.isFile()) {
74277
74528
  return stat.size;
74278
74529
  }
@@ -74316,7 +74567,7 @@ ${lanes.join("\n")}
74316
74567
  }
74317
74568
  };
74318
74569
  return nodeSystem;
74319
- function statSync9(path) {
74570
+ function statSync10(path) {
74320
74571
  try {
74321
74572
  return _fs.statSync(path, statSyncOptions);
74322
74573
  } catch {
@@ -74375,7 +74626,7 @@ ${lanes.join("\n")}
74375
74626
  activeSession.post("Profiler.stop", (err, { profile }) => {
74376
74627
  var _a;
74377
74628
  if (!err) {
74378
- if ((_a = statSync9(profilePath)) == null ? void 0 : _a.isDirectory()) {
74629
+ if ((_a = statSync10(profilePath)) == null ? void 0 : _a.isDirectory()) {
74379
74630
  profilePath = _path.join(profilePath, `${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`);
74380
74631
  }
74381
74632
  try {
@@ -74495,7 +74746,7 @@ ${lanes.join("\n")}
74495
74746
  let stat;
74496
74747
  if (typeof dirent === "string" || dirent.isSymbolicLink()) {
74497
74748
  const name = combinePaths(path, entry);
74498
- stat = statSync9(name);
74749
+ stat = statSync10(name);
74499
74750
  if (!stat) {
74500
74751
  continue;
74501
74752
  }
@@ -74519,7 +74770,7 @@ ${lanes.join("\n")}
74519
74770
  return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
74520
74771
  }
74521
74772
  function fileSystemEntryExists(path, entryKind) {
74522
- const stat = statSync9(path);
74773
+ const stat = statSync10(path);
74523
74774
  if (!stat) {
74524
74775
  return false;
74525
74776
  }
@@ -74561,7 +74812,7 @@ ${lanes.join("\n")}
74561
74812
  }
74562
74813
  function getModifiedTime3(path) {
74563
74814
  var _a;
74564
- return (_a = statSync9(path)) == null ? void 0 : _a.mtime;
74815
+ return (_a = statSync10(path)) == null ? void 0 : _a.mtime;
74565
74816
  }
74566
74817
  function setModifiedTime(path, time) {
74567
74818
  try {
@@ -275245,7 +275496,7 @@ function absFnSig(paramTypes, returnType) {
275245
275496
  returnType
275246
275497
  });
275247
275498
  }
275248
- function absUnion2(members) {
275499
+ function absUnion(members) {
275249
275500
  if (members.length === 0) return absNever();
275250
275501
  return members.reduce((acc, m) => joinAbs(acc, m));
275251
275502
  }
@@ -275560,7 +275811,7 @@ function materialize(ctx) {
275560
275811
  const ret = mapType(ctx, decl.type, 0);
275561
275812
  if (!isPlainUnknown(ret)) returns.push(ret);
275562
275813
  }
275563
- sig2.returnType = returns.length > 0 ? absUnion2(returns) : absUnknown3();
275814
+ sig2.returnType = returns.length > 0 ? absUnion(returns) : absUnknown3();
275564
275815
  }
275565
275816
  rec[p.name] = value;
275566
275817
  continue;
@@ -275584,7 +275835,7 @@ function materialize(ctx) {
275584
275835
  if (ctor) {
275585
275836
  for (const param of ctor.parameters) {
275586
275837
  let type = param.type ? mapType(ctx, param.type, 0) : absUnknown3();
275587
- if (param.questionToken) type = absUnion2([type, absUndefLit2()]);
275838
+ if (param.questionToken) type = absUnion([type, absUndefLit2()]);
275588
275839
  params.push(type);
275589
275840
  }
275590
275841
  }
@@ -275634,7 +275885,7 @@ function instanceFor(ctx, key) {
275634
275885
  const name = memberName(member);
275635
275886
  if (name === void 0) continue;
275636
275887
  let type = member.type ? mapType(ctx, member.type, 0) : absUnknown3();
275637
- if (member.questionToken) type = absUnion2([type, absUndefLit2()]);
275888
+ if (member.questionToken) type = absUnion([type, absUndefLit2()]);
275638
275889
  properties[name] = type;
275639
275890
  } else if (import_typescript.default.isMethodSignature(member) || import_typescript.default.isMethodDeclaration(member)) {
275640
275891
  const name = memberName(member);
@@ -275662,7 +275913,7 @@ function signatureToFnSig(ctx, sig2) {
275662
275913
  } else {
275663
275914
  type = param.type ? mapType(ctx, param.type, 0) : absUnknown3();
275664
275915
  }
275665
- if (param.questionToken && !param.dotDotDotToken) type = absUnion2([type, absUndefLit2()]);
275916
+ if (param.questionToken && !param.dotDotDotToken) type = absUnion([type, absUndefLit2()]);
275666
275917
  return type;
275667
275918
  });
275668
275919
  const returnType = sig2.type ? mapType(ctx, sig2.type, 0) : absUnknown3();
@@ -275675,7 +275926,7 @@ function mapTypeMembers(ctx, members, depth) {
275675
275926
  const name = memberName(member);
275676
275927
  if (name === void 0) continue;
275677
275928
  let type = member.type ? mapType(ctx, member.type, depth) : absUnknown3();
275678
- if (member.questionToken) type = absUnion2([type, absUndefLit2()]);
275929
+ if (member.questionToken) type = absUnion([type, absUndefLit2()]);
275679
275930
  properties[name] = type;
275680
275931
  } else if (import_typescript.default.isMethodSignature(member)) {
275681
275932
  const name = memberName(member);
@@ -275717,7 +275968,7 @@ function mapType(ctx, node, depth) {
275717
275968
  return absUnknown3();
275718
275969
  }
275719
275970
  if (import_typescript.default.isUnionTypeNode(node)) {
275720
- return absUnion2(node.types.map((t) => mapType(ctx, t, depth)));
275971
+ return absUnion(node.types.map((t) => mapType(ctx, t, depth)));
275721
275972
  }
275722
275973
  if (import_typescript.default.isIntersectionTypeNode(node)) {
275723
275974
  const members = node.types.map((t) => mapType(ctx, t, depth));
@@ -275751,7 +276002,7 @@ function mapType(ctx, node, depth) {
275751
276002
  const params = [];
275752
276003
  for (const param of ctor.parameters) {
275753
276004
  let type = param.type ? mapType(ctx, param.type, depth) : absUnknown3();
275754
- if (param.questionToken) type = absUnion2([type, absUndefLit2()]);
276005
+ if (param.questionToken) type = absUnion([type, absUndefLit2()]);
275755
276006
  params.push(type);
275756
276007
  }
275757
276008
  return absFnSig(params, ctor.type ? mapType(ctx, ctor.type, depth) : absUnknown3());
@@ -276035,7 +276286,9 @@ function barePackageName(spec) {
276035
276286
  if (spec.startsWith("@")) {
276036
276287
  return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
276037
276288
  }
276038
- return parts[0];
276289
+ const name = parts[0];
276290
+ if (NODE_BUILTINS.has(name)) return void 0;
276291
+ return name;
276039
276292
  }
276040
276293
  function collectBarePackages(source) {
276041
276294
  try {
@@ -276086,13 +276339,56 @@ function autoHarvestModules(source, fromDir) {
276086
276339
  }
276087
276340
  return modules;
276088
276341
  }
276089
- var harvestCache;
276342
+ var NODE_BUILTINS, harvestCache;
276090
276343
  var init_harvest_auto = __esm({
276091
276344
  "../service/src/harvest-auto.ts"() {
276092
276345
  "use strict";
276093
276346
  init_src2();
276094
276347
  init_static_imports();
276095
276348
  init_harvest_package();
276349
+ NODE_BUILTINS = /* @__PURE__ */ new Set([
276350
+ "assert",
276351
+ "async_hooks",
276352
+ "buffer",
276353
+ "child_process",
276354
+ "cluster",
276355
+ "console",
276356
+ "constants",
276357
+ "crypto",
276358
+ "dgram",
276359
+ "diagnostics_channel",
276360
+ "dns",
276361
+ "domain",
276362
+ "events",
276363
+ "fs",
276364
+ "http",
276365
+ "http2",
276366
+ "https",
276367
+ "inspector",
276368
+ "module",
276369
+ "net",
276370
+ "os",
276371
+ "path",
276372
+ "perf_hooks",
276373
+ "process",
276374
+ "punycode",
276375
+ "querystring",
276376
+ "readline",
276377
+ "repl",
276378
+ "stream",
276379
+ "string_decoder",
276380
+ "timers",
276381
+ "tls",
276382
+ "trace_events",
276383
+ "tty",
276384
+ "url",
276385
+ "util",
276386
+ "v8",
276387
+ "vm",
276388
+ "wasi",
276389
+ "worker_threads",
276390
+ "zlib"
276391
+ ]);
276096
276392
  harvestCache = /* @__PURE__ */ new Map();
276097
276393
  }
276098
276394
  });
@@ -276826,6 +277122,49 @@ function collectEnvModules(envNames) {
276826
277122
  }
276827
277123
  return out;
276828
277124
  }
277125
+ function setEnvHarvestConflictCollector(collector2) {
277126
+ const prev = envHarvestConflictCollector;
277127
+ envHarvestConflictCollector = collector2;
277128
+ return prev;
277129
+ }
277130
+ function getEnvHarvestConflictCollector() {
277131
+ return envHarvestConflictCollector;
277132
+ }
277133
+ function mergeHarvestUnderEnv(harvestModules, envModules, opts) {
277134
+ const out = {};
277135
+ for (const [mod2, exports] of Object.entries(harvestModules)) {
277136
+ const named = { ...exports.named };
277137
+ out[mod2] = exports.default !== void 0 ? { named, default: exports.default } : { named };
277138
+ }
277139
+ const notify = opts?.onConflict ?? envHarvestConflictCollector;
277140
+ for (const [mod2, envExports] of Object.entries(envModules)) {
277141
+ const existing = out[mod2];
277142
+ if (!existing) {
277143
+ const named2 = { ...envExports.named };
277144
+ out[mod2] = envExports.default !== void 0 ? { named: named2, default: envExports.default } : { named: named2 };
277145
+ continue;
277146
+ }
277147
+ const overwritten = [];
277148
+ for (const key of Object.keys(envExports.named)) {
277149
+ if (existing.named[key] !== void 0) overwritten.push(key);
277150
+ }
277151
+ const defaultOverwritten = envExports.default !== void 0 && existing.default !== void 0;
277152
+ if (notify && (overwritten.length > 0 || defaultOverwritten)) {
277153
+ try {
277154
+ notify({
277155
+ module: mod2,
277156
+ exports: overwritten,
277157
+ defaultOverwritten
277158
+ });
277159
+ } catch {
277160
+ }
277161
+ }
277162
+ const named = { ...existing.named, ...envExports.named };
277163
+ const defaultAbs = envExports.default !== void 0 ? envExports.default : existing.default;
277164
+ out[mod2] = defaultAbs !== void 0 ? { named, default: defaultAbs } : { named };
277165
+ }
277166
+ return out;
277167
+ }
276829
277168
  function collectBPathReplacements(source) {
276830
277169
  const targets = [];
276831
277170
  const values = {};
@@ -276938,7 +277277,7 @@ function tryRunBPath(source, filePath, opts = {}) {
276938
277277
  try {
276939
277278
  const { modules: graphMods, issues } = evalAbsModuleGraph(source, filePath);
276940
277279
  const envMods = collectEnvModules(opts.envNames ?? []);
276941
- const modules = { ...envMods, ...graphMods };
277280
+ const modules = mergeHarvestUnderEnv(graphMods, envMods);
276942
277281
  const { targets, values, asTargets, asValues } = collectBPathReplacements(source);
276943
277282
  const envGlobals = {
276944
277283
  ...collectEnvGlobals(opts.envNames ?? []),
@@ -277013,7 +277352,7 @@ function tryBPathCall(source, filePath, fnName, args, opts = {}) {
277013
277352
  if (r.shape.k === "unknown" && !r.term) return void 0;
277014
277353
  return r;
277015
277354
  }
277016
- var bRunByFile, MAX_B_RUN_CACHE;
277355
+ var envHarvestConflictCollector, bRunByFile, MAX_B_RUN_CACHE;
277017
277356
  var init_bpath_run = __esm({
277018
277357
  "../service/src/bpath-run.ts"() {
277019
277358
  "use strict";
@@ -277024,6 +277363,7 @@ var init_bpath_run = __esm({
277024
277363
  init_analysis_file_cache();
277025
277364
  init_fn_analysis_cache();
277026
277365
  init_load_module();
277366
+ envHarvestConflictCollector = null;
277027
277367
  bRunByFile = /* @__PURE__ */ new Map();
277028
277368
  MAX_B_RUN_CACHE = 32;
277029
277369
  }
@@ -277345,7 +277685,8 @@ function validateMockDirectives(directives, diagnostics) {
277345
277685
  for (const d of directives) {
277346
277686
  if (d.kind !== "mock" || !d.expression) continue;
277347
277687
  const expr = d.expression.trim();
277348
- if (expr.includes("(") && expr.includes(")") && !expr.startsWith("T.")) {
277688
+ const isTypeExpr = /^(number|string|boolean|any|array|shape|lit|union|fn|partial|pick|omit|record|required|readonly|nonNullable|and)\s*\(/.test(expr) || expr === "unknown" || expr === "any" || expr === "never" || expr === "true" || expr === "false" || expr === "null" || expr === "undefined" || /^-?\d+(\.\d+)?$/.test(expr) || (expr.startsWith('"') && expr.endsWith('"') || expr.startsWith("'") && expr.endsWith("'")) || expr.startsWith("{") || expr.startsWith("[");
277689
+ if (expr.includes("(") && expr.includes(")") && !isTypeExpr && !expr.includes("=>")) {
277349
277690
  diagnostics.push({
277350
277691
  range: { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } },
277351
277692
  severity: "warning",
@@ -277353,7 +277694,8 @@ function validateMockDirectives(directives, diagnostics) {
277353
277694
  code: "nudo:mock-invalid",
277354
277695
  suggestions: [
277355
277696
  "Supported formats: stub(), stub().returns(value), spy(), mock()",
277356
- "Arrow functions: (args) => expression or (args) => { statements; return value; }"
277697
+ "Arrow functions: (args) => expression or (args) => { statements; return value; }",
277698
+ "Type expressions: number(), string(), shape({...}), union(...), or concrete literals"
277357
277699
  ]
277358
277700
  });
277359
277701
  }
@@ -277879,6 +278221,24 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords,
277879
278221
  () => analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule)
277880
278222
  );
277881
278223
  }
278224
+ function findModuleImportLoc(source, module) {
278225
+ const bare = module.startsWith("node:") ? module.slice("node:".length) : module;
278226
+ const alts = [.../* @__PURE__ */ new Set([module, bare, `node:${bare}`])];
278227
+ const lines = source.split(/\r?\n/);
278228
+ for (let i = 0; i < lines.length; i++) {
278229
+ const line = lines[i];
278230
+ for (const alt of alts) {
278231
+ const re = new RegExp(
278232
+ `["'\`]${alt.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["'\`]`
278233
+ );
278234
+ const m = re.exec(line);
278235
+ if (m && m.index !== void 0) {
278236
+ return { line: i, column: m.index, length: m[0].length };
278237
+ }
278238
+ }
278239
+ }
278240
+ return null;
278241
+ }
277882
278242
  function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRecords, loadModule) {
277883
278243
  const ast = parse(source);
277884
278244
  const functions = extractDirectives(ast);
@@ -277887,6 +278247,18 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
277887
278247
  const nodeAbsMap = /* @__PURE__ */ new Map();
277888
278248
  const functionResults = [];
277889
278249
  const caseHints = [];
278250
+ const envHarvestConflicts = [];
278251
+ const prevHarvestConflictCollector = setEnvHarvestConflictCollector((c) => {
278252
+ if (!envHarvestConflicts.some((x) => x.module === c.module)) {
278253
+ envHarvestConflicts.push(c);
278254
+ } else {
278255
+ const prev = envHarvestConflicts.find((x) => x.module === c.module);
278256
+ for (const e of c.exports) {
278257
+ if (!prev.exports.includes(e)) prev.exports.push(e);
278258
+ }
278259
+ prev.defaultOverwritten ||= c.defaultOverwritten;
278260
+ }
278261
+ });
277890
278262
  const fileDirectives = extractFileDirectives(ast);
277891
278263
  const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
277892
278264
  const projectConfig = findProjectConfig(dirname9(filePath));
@@ -277976,7 +278348,7 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
277976
278348
  seedFns: seeds.seedFns,
277977
278349
  ...loadModule ? { loadModule } : {}
277978
278350
  });
277979
- absGraphModules = { ...collectEnvModules(envNames), ...g.modules };
278351
+ absGraphModules = mergeHarvestUnderEnv(g.modules, collectEnvModules(envNames));
277980
278352
  pushBModuleIssues(g.issues);
277981
278353
  } catch {
277982
278354
  }
@@ -278299,7 +278671,7 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
278299
278671
  diagnostics.push({
278300
278672
  range: { start: { line: directive.commentLine, column: 0 }, end: { line: directive.commentLine, column: 999 } },
278301
278673
  severity: "error",
278302
- message: `Case "${directive.name}": expected ${formatShape(directive.expected)}, got ${formatShape(caseAbs)}. The inferred return type does not match the expected type declared in the @nudo:case directive`,
278674
+ message: `debug "${directive.name}": expected ${formatShape(directive.expected)}, got ${formatShape(caseAbs)}. The inferred return type does not match the expected type declared in the @nudo:case witness`,
278303
278675
  code: "nudo:case-expected"
278304
278676
  });
278305
278677
  }
@@ -278582,6 +278954,24 @@ function analyzeFileUncachedInner(filePath, source, activeCases, externalCallRec
278582
278954
  filePath,
278583
278955
  analysisConfig(projectConfig?.config).callSiteBudget
278584
278956
  );
278957
+ setEnvHarvestConflictCollector(prevHarvestConflictCollector);
278958
+ for (const c of envHarvestConflicts) {
278959
+ const parts = [];
278960
+ if (c.exports.length > 0) parts.push(`export(s) ${c.exports.join(", ")}`);
278961
+ if (c.defaultOverwritten) parts.push("default");
278962
+ const detail = parts.length > 0 ? ` \u2014 ${parts.join("; ")}` : "";
278963
+ const loc = findModuleImportLoc(source, c.module);
278964
+ const range = loc ? {
278965
+ start: { line: loc.line, column: loc.column },
278966
+ end: { line: loc.line, column: loc.column + loc.length }
278967
+ } : { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } };
278968
+ diagnostics.push({
278969
+ range,
278970
+ severity: "warning",
278971
+ message: `handwritten @nudo:env wins over harvest on module "${c.module}"${detail}; harvest only fills missing slots (B8). code=nudo:env-harvest-conflict`,
278972
+ code: "nudo:env-harvest-conflict"
278973
+ });
278974
+ }
278585
278975
  return {
278586
278976
  functions: functionResults,
278587
278977
  diagnostics,
@@ -278694,7 +279084,7 @@ function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envN
278694
279084
  }
278695
279085
  }
278696
279086
  if (envNames.length > 0) {
278697
- modules = { ...collectEnvModules(envNames), ...modules ?? {} };
279087
+ modules = mergeHarvestUnderEnv(modules ?? {}, collectEnvModules(envNames));
278698
279088
  }
278699
279089
  try {
278700
279090
  importLocals = buildAbsImportLocalMap(source, filePath);
@@ -279897,18 +280287,93 @@ var init_dep_contents = __esm({
279897
280287
  });
279898
280288
 
279899
280289
  // ../service/src/harvest-node.ts
279900
- import { existsSync as existsSync11 } from "fs";
279901
- function harvestNodeTypes(fromDir, maxFiles = 12, maxMs = 2500) {
280290
+ import { existsSync as existsSync11, statSync as statSync8 } from "fs";
280291
+ import { join as join7 } from "path";
280292
+ function notFoundCacheKey(fromDir) {
280293
+ return `not-found|@types/node|${fromDir ?? ""}`;
280294
+ }
280295
+ function nodeHarvestCacheKey(root, maxFiles, maxMs) {
280296
+ let sig2 = "nostat";
280297
+ try {
280298
+ const st = statSync8(join7(root, "package.json"));
280299
+ sig2 = `${st.size}:${Math.floor(st.mtimeMs)}`;
280300
+ } catch {
280301
+ }
280302
+ return `${root}|${maxFiles}|${maxMs}|${sig2}`;
280303
+ }
280304
+ function clearNodeHarvestCache() {
280305
+ nodeHarvestCache.clear();
280306
+ }
280307
+ function getNodeHarvestCacheSize() {
280308
+ return nodeHarvestCache.size;
280309
+ }
280310
+ function isHarvestNodeDisabled(env = process.env) {
280311
+ return env.NUDO_HARVEST_NODE === "off";
280312
+ }
280313
+ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, maxMs = HARVEST_NODE_DEFAULT_MAX_MS) {
280314
+ if (isHarvestNodeDisabled()) {
280315
+ return {
280316
+ ok: false,
280317
+ error: "harvest disabled via NUDO_HARVEST_NODE=off",
280318
+ reason: "disabled"
280319
+ };
280320
+ }
279902
280321
  const root = resolvePackageRoot("@types/node", fromDir) ?? resolvePackageRoot("node", fromDir);
279903
280322
  if (!root || !existsSync11(root)) {
279904
- return { ok: false, error: "@types/node not found" };
280323
+ const missKey = notFoundCacheKey(fromDir);
280324
+ const miss = nodeHarvestCache.get(missKey);
280325
+ if (miss && !miss.ok && miss.reason === "not-found") {
280326
+ return { ...miss, cached: true };
280327
+ }
280328
+ const result = {
280329
+ ok: false,
280330
+ error: "@types/node not found",
280331
+ reason: "not-found"
280332
+ };
280333
+ nodeHarvestCache.set(missKey, result);
280334
+ return result;
280335
+ }
280336
+ const key = nodeHarvestCacheKey(root, maxFiles, maxMs);
280337
+ const hit = nodeHarvestCache.get(key);
280338
+ if (hit) {
280339
+ return hit.ok ? { ...hit, cached: true } : { ...hit, cached: true };
279905
280340
  }
279906
280341
  const dts = collectDtsFiles(root, maxFiles);
279907
280342
  if (dts.length === 0) {
279908
- return { ok: false, error: `no .d.ts under ${root}` };
280343
+ const result = {
280344
+ ok: false,
280345
+ error: `no .d.ts under ${root}`,
280346
+ reason: "no-dts"
280347
+ };
280348
+ nodeHarvestCache.set(key, result);
280349
+ return result;
280350
+ }
280351
+ try {
280352
+ const env = harvestDts(dts, { maxMs });
280353
+ const stats = {
280354
+ files: env.stats.files,
280355
+ symbols: env.stats.symbols,
280356
+ skipped: env.stats.skipped
280357
+ };
280358
+ const result = {
280359
+ ok: true,
280360
+ env,
280361
+ root,
280362
+ files: stats.files,
280363
+ stats,
280364
+ cached: false
280365
+ };
280366
+ nodeHarvestCache.set(key, result);
280367
+ return result;
280368
+ } catch (e) {
280369
+ const result = {
280370
+ ok: false,
280371
+ error: `harvest failed: ${e.message}`,
280372
+ reason: "failed"
280373
+ };
280374
+ nodeHarvestCache.set(key, result);
280375
+ return result;
279909
280376
  }
279910
- const env = harvestDts(dts, { maxMs });
279911
- return { ok: true, env, root, files: env.stats.files };
279912
280377
  }
279913
280378
  function summarizeNodeEnv(env) {
279914
280379
  return {
@@ -279917,11 +280382,15 @@ function summarizeNodeEnv(env) {
279917
280382
  symbolCount: env.stats.symbols
279918
280383
  };
279919
280384
  }
280385
+ var HARVEST_NODE_DEFAULT_MAX_FILES, HARVEST_NODE_DEFAULT_MAX_MS, nodeHarvestCache;
279920
280386
  var init_harvest_node = __esm({
279921
280387
  "../service/src/harvest-node.ts"() {
279922
280388
  "use strict";
279923
280389
  init_src3();
279924
280390
  init_harvest_package();
280391
+ HARVEST_NODE_DEFAULT_MAX_FILES = 12;
280392
+ HARVEST_NODE_DEFAULT_MAX_MS = 2500;
280393
+ nodeHarvestCache = /* @__PURE__ */ new Map();
279925
280394
  }
279926
280395
  });
279927
280396
 
@@ -280694,15 +281163,15 @@ function serializeCaseArg(a) {
280694
281163
  }
280695
281164
  switch (s.k) {
280696
281165
  case "prim":
280697
- if (s.type === "number" || s.type === "string" || s.type === "boolean") {
280698
- return `T.${s.type}`;
280699
- }
281166
+ if (s.type === "number") return "number()";
281167
+ if (s.type === "string") return "string()";
281168
+ if (s.type === "boolean") return "boolean()";
280700
281169
  return null;
280701
281170
  case "unknown":
280702
281171
  case "any":
280703
- return "T.unknown";
281172
+ return "any()";
280704
281173
  case "never":
280705
- return "T.never";
281174
+ return "never";
280706
281175
  case "sum": {
280707
281176
  const parts = [];
280708
281177
  for (const member of s.members) {
@@ -280710,11 +281179,11 @@ function serializeCaseArg(a) {
280710
281179
  if (ser === null) return null;
280711
281180
  parts.push(ser);
280712
281181
  }
280713
- return `T.union(${parts.join(", ")})`;
281182
+ return `union(${parts.join(", ")})`;
280714
281183
  }
280715
281184
  case "arr": {
280716
281185
  const el = serializeCaseArg(s.element);
280717
- return el === null ? null : `T.array(${el})`;
281186
+ return el === null ? null : `array(${el})`;
280718
281187
  }
280719
281188
  case "tuple": {
280720
281189
  const parts = [];
@@ -281542,7 +282011,7 @@ var init_interface_emitter = __esm({
281542
282011
 
281543
282012
  // ../service/src/interface-draft.ts
281544
282013
  import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync4, realpathSync as realpathSync2 } from "fs";
281545
- import { basename as basename3, dirname as dirname15, isAbsolute as isAbsolute2, join as join7, relative as relative4 } from "path";
282014
+ import { basename as basename3, dirname as dirname15, isAbsolute as isAbsolute2, join as join8, relative as relative4 } from "path";
281546
282015
  function collectParamBodyAccesses(source) {
281547
282016
  const out = /* @__PURE__ */ new Map();
281548
282017
  let ast;
@@ -282059,7 +282528,7 @@ function safeRealpath(p) {
282059
282528
  return realpathSync2(p);
282060
282529
  } catch {
282061
282530
  try {
282062
- return join7(realpathSync2(dirname15(p)), basename3(p));
282531
+ return join8(realpathSync2(dirname15(p)), basename3(p));
282063
282532
  } catch {
282064
282533
  return p;
282065
282534
  }
@@ -283068,6 +283537,8 @@ __export(src_exports2, {
283068
283537
  ANALYSIS_ABI: () => ANALYSIS_ABI,
283069
283538
  DEFAULT_ANALYSIS_MODE: () => DEFAULT_ANALYSIS_MODE,
283070
283539
  DiskCache: () => DiskCache,
283540
+ HARVEST_NODE_DEFAULT_MAX_FILES: () => HARVEST_NODE_DEFAULT_MAX_FILES,
283541
+ HARVEST_NODE_DEFAULT_MAX_MS: () => HARVEST_NODE_DEFAULT_MAX_MS,
283071
283542
  SEMANTIC_TOKEN_MODIFIERS: () => SEMANTIC_TOKEN_MODIFIERS,
283072
283543
  SEMANTIC_TOKEN_TYPES: () => SEMANTIC_TOKEN_TYPES,
283073
283544
  absToTSType: () => absToTSType,
@@ -283092,6 +283563,7 @@ __export(src_exports2, {
283092
283563
  clearEnvPathDeps: () => clearEnvPathDeps,
283093
283564
  clearFnAnalysisCache: () => clearFnAnalysisCache,
283094
283565
  clearHarvestCache: () => clearHarvestCache,
283566
+ clearNodeHarvestCache: () => clearNodeHarvestCache,
283095
283567
  clearPathEnvCaches: () => clearPathEnvCaches,
283096
283568
  collectAbsBindingsFromGraph: () => collectAbsBindingsFromGraph,
283097
283569
  collectAbsInlays: () => collectAbsInlays,
@@ -283144,7 +283616,9 @@ __export(src_exports2, {
283144
283616
  getAnalysisSession: () => getAnalysisSession,
283145
283617
  getCasesForFile: () => getCasesForFile,
283146
283618
  getCompletionsAtPosition: () => getCompletionsAtPosition,
283619
+ getEnvHarvestConflictCollector: () => getEnvHarvestConflictCollector,
283147
283620
  getHoverAtPosition: () => getHoverAtPosition,
283621
+ getNodeHarvestCacheSize: () => getNodeHarvestCacheSize,
283148
283622
  getTypeAtPosition: () => getTypeAtPosition,
283149
283623
  getTypeAtPositionAsync: () => getTypeAtPositionAsync,
283150
283624
  harvestNodeTypes: () => harvestNodeTypes,
@@ -283160,12 +283634,14 @@ __export(src_exports2, {
283160
283634
  isBPathCapable: () => isBPathCapable,
283161
283635
  isDraftableEntry: () => isDraftableEntry,
283162
283636
  isEnvTemplatePath: () => isEnvTemplatePath,
283637
+ isHarvestNodeDisabled: () => isHarvestNodeDisabled,
283163
283638
  isNudoTargetPath: () => isNudoTargetPath,
283164
283639
  isProjectConfigPath: () => isProjectConfigPath,
283165
283640
  isSidecarPath: () => isSidecarPath,
283166
283641
  isWatchRelevantPath: () => isWatchRelevantPath,
283167
283642
  lookupHarvested: () => lookupHarvested,
283168
283643
  matchesEmitAllowlist: () => matchesEmitAllowlist,
283644
+ mergeHarvestUnderEnv: () => mergeHarvestUnderEnv,
283169
283645
  mockDirectivesToAbsSeeds: () => mockDirectivesToAbsSeeds,
283170
283646
  noteEnvPathDeps: () => noteEnvPathDeps,
283171
283647
  packageHarvestToAbsModules: () => packageHarvestToAbsModules,
@@ -283175,6 +283651,7 @@ __export(src_exports2, {
283175
283651
  serializeCaseArg: () => serializeCaseArg,
283176
283652
  serializeInferJson: () => serializeInferJson,
283177
283653
  setAnalysisSession: () => setAnalysisSession,
283654
+ setEnvHarvestConflictCollector: () => setEnvHarvestConflictCollector,
283178
283655
  sha256Hex: () => sha256Hex,
283179
283656
  shouldAnalyzeFile: () => shouldAnalyzeFile,
283180
283657
  sidecarDraftPath: () => sidecarDraftPath,
@@ -283235,8 +283712,8 @@ var init_src4 = __esm({
283235
283712
  init_src();
283236
283713
  init_src4();
283237
283714
  init_src3();
283238
- import { readFileSync as readFileSync15, existsSync as existsSync16, watch, readdirSync as readdirSync2, statSync as statSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync3, realpathSync as realpathSync3 } from "fs";
283239
- import { resolve as resolve12, dirname as dirname17, relative as relative6, join as join8, basename as basename5, isAbsolute as isAbsolute3 } from "path";
283715
+ import { readFileSync as readFileSync15, existsSync as existsSync16, watch, readdirSync as readdirSync2, statSync as statSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync3, realpathSync as realpathSync3 } from "fs";
283716
+ import { resolve as resolve12, dirname as dirname17, relative as relative6, join as join9, basename as basename5, isAbsolute as isAbsolute3 } from "path";
283240
283717
  import { fileURLToPath } from "url";
283241
283718
  import { Command } from "commander";
283242
283719
 
@@ -283310,7 +283787,7 @@ function formatTestReport(r) {
283310
283787
  // src/index.ts
283311
283788
  var program = new Command();
283312
283789
  function readPackageVersion() {
283313
- const pkgPath = join8(dirname17(fileURLToPath(import.meta.url)), "..", "package.json");
283790
+ const pkgPath = join9(dirname17(fileURLToPath(import.meta.url)), "..", "package.json");
283314
283791
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
283315
283792
  return pkg.version ?? "0.0.0";
283316
283793
  }
@@ -283330,7 +283807,7 @@ function collectExternalRecords(sites) {
283330
283807
  process.exitCode = 1;
283331
283808
  continue;
283332
283809
  }
283333
- const siteFiles = statSync8(sitePath).isDirectory() ? collectNudoFiles(sitePath) : [sitePath];
283810
+ const siteFiles = statSync9(sitePath).isDirectory() ? collectNudoFiles(sitePath) : [sitePath];
283334
283811
  for (const sf of siteFiles) {
283335
283812
  records.push(...collectCallRecords(sf, readFileSync15(sf, "utf-8")));
283336
283813
  }
@@ -283352,7 +283829,7 @@ async function runInfer(file, options = {}) {
283352
283829
  }
283353
283830
  }
283354
283831
  if (result.functions.length === 0 && !result.externalFunctions?.length) {
283355
- console.log("No functions with @nudo:case directives found.");
283832
+ console.log("No functions found to analyze.");
283356
283833
  return;
283357
283834
  }
283358
283835
  const dtsLines = [];
@@ -283376,7 +283853,8 @@ async function runInfer(file, options = {}) {
283376
283853
  }
283377
283854
  for (const c of fn2.cases) {
283378
283855
  const argsStr = c.argAbs.map(formatShape).join(", ");
283379
- let line = `Case "${c.name}": (${argsStr}) => ${formatShape(c.abs)}`;
283856
+ const label = c.name.startsWith("call@") || c.name.startsWith("entry@") ? c.name : `debug "${c.name}"`;
283857
+ let line = `${label}: (${argsStr}) => ${formatShape(c.abs)}`;
283380
283858
  if (c.throwsAbs.shape.k !== "never") line += ` throws ${formatShape(c.throwsAbs)}`;
283381
283859
  console.log(line);
283382
283860
  if (c.intension?.display) {
@@ -283397,7 +283875,7 @@ async function runInfer(file, options = {}) {
283397
283875
  }
283398
283876
  if (fn2.cases.length > 1 && fn2.combinedAbs) {
283399
283877
  console.log(`
283400
- Combined: ${formatShape(fn2.combinedAbs)}`);
283878
+ Observed: ${formatShape(fn2.combinedAbs)}`);
283401
283879
  }
283402
283880
  console.log();
283403
283881
  }
@@ -283418,13 +283896,14 @@ Combined: ${formatShape(fn2.combinedAbs)}`);
283418
283896
  `);
283419
283897
  for (const c of fn2.cases) {
283420
283898
  const argsStr = c.argAbs.map(formatShape).join(", ");
283421
- let line = `Case "${c.name}": (${argsStr}) => ${formatShape(c.abs)}`;
283899
+ const label = c.name.startsWith("call@") || c.name.startsWith("entry@") ? c.name : `debug "${c.name}"`;
283900
+ let line = `${label}: (${argsStr}) => ${formatShape(c.abs)}`;
283422
283901
  if (c.throwsAbs.shape.k !== "never") line += ` throws ${formatShape(c.throwsAbs)}`;
283423
283902
  console.log(line);
283424
283903
  }
283425
283904
  if (fn2.cases.length > 1 && fn2.combinedAbs) {
283426
283905
  console.log(`
283427
- Combined: ${formatShape(fn2.combinedAbs)}`);
283906
+ Observed: ${formatShape(fn2.combinedAbs)}`);
283428
283907
  }
283429
283908
  console.log();
283430
283909
  }
@@ -283480,7 +283959,7 @@ async function runInferJson(file, externalRecords) {
283480
283959
  const { serializeInferJson: serializeInferJson2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
283481
283960
  console.log(JSON.stringify(serializeInferJson2(result, filePath), null, 2));
283482
283961
  }
283483
- program.command("infer").description("Infer types from a JS/TS file (or a directory of them) \u2014 functions with @nudo:case directives use them; all other functions are inferred from call sites (whole-program analysis)").argument("<file>", "Path to the JS/TS file (or directory)").option("--dts", "Generate .d.ts file").option("--loc", "Show source locations in output").option("--json", "Output as JSON").option("--callsites <paths...>", "Usage-site files (tests/apps) to harvest real call shapes from; their calls to this file's exports become synthesized cases").option("--emit-cases [mode]", "Write synthesized call-site cases back into the source as @nudo:case directives (call@ prefix); mode: update (default: add)").option("--dry-run", "With --emit-cases: print a unified diff instead of writing to disk").option("--exit-on-diff", "With --dry-run: exit with code 1 when the diff is non-empty").action(
283962
+ program.command("infer").description("Infer types from a JS/TS file (or a directory) by executing observed call sites; optional @nudo:case witnesses are for debug / nudo test only").argument("<file>", "Path to the JS/TS file (or directory)").option("--dts", "Generate .d.ts file").option("--loc", "Show source locations in output").option("--json", "Output as JSON").option("--callsites <paths...>", "Usage-site files (tests/apps) to harvest real call shapes from; their calls to this file's exports become synthesized cases").option("--emit-cases [mode]", "Debug solidification: write synthesized call-site witnesses back as @nudo:case directives (call@ prefix); not the interface product path. mode: update (default: add)").option("--dry-run", "With --emit-cases: print a unified diff instead of writing to disk").option("--exit-on-diff", "With --dry-run: exit with code 1 when the diff is non-empty").action(
283484
283963
  async (file, opts) => {
283485
283964
  let emit;
283486
283965
  if (opts.emitCases !== void 0) {
@@ -283509,7 +283988,7 @@ program.command("infer").description("Infer types from a JS/TS file (or a direct
283509
283988
  externalRecords = collectExternalRecords(opts.callsites);
283510
283989
  }
283511
283990
  const target = resolve12(file);
283512
- if (existsSync16(target) && statSync8(target).isDirectory()) {
283991
+ if (existsSync16(target) && statSync9(target).isDirectory()) {
283513
283992
  const files = collectNudoFiles(target);
283514
283993
  if (files.length === 0) {
283515
283994
  console.log("No nudo files found in directory.");
@@ -283673,7 +284152,7 @@ function resolveTargets(path) {
283673
284152
  process.exitCode = 1;
283674
284153
  return [];
283675
284154
  }
283676
- if (statSync8(resolved).isDirectory()) {
284155
+ if (statSync9(resolved).isDirectory()) {
283677
284156
  const files = collectNudoFiles(resolved);
283678
284157
  if (files.length === 0) {
283679
284158
  console.error(`No nudo files found in directory: ${resolved}`);
@@ -283793,7 +284272,7 @@ async function runInterfaceDraft(file, opts) {
283793
284272
  let dir = dirname17(filePath);
283794
284273
  const fsRoot = resolve12("/");
283795
284274
  while (dir !== fsRoot) {
283796
- if (existsSync16(join8(dir, "package.json"))) {
284275
+ if (existsSync16(join9(dir, "package.json"))) {
283797
284276
  projectRoot = dir;
283798
284277
  break;
283799
284278
  }
@@ -284022,7 +284501,7 @@ program.command("interface").alias("refine").description(
284022
284501
  }
284023
284502
  }
284024
284503
  );
284025
- program.command("test").description("Run @nudo:case directives as assertions (case-as-test); exit 1 on failure").argument("<file>", "Path to the JS/TS file or a directory of them").action(async (file) => {
284504
+ program.command("test").description("Run @nudo:case directives as debug assertions (case-as-test); exit 1 on failure").argument("<file>", "Path to the JS/TS file or a directory of them").action(async (file) => {
284026
284505
  const targets = resolveTargets(file);
284027
284506
  if (targets.length === 0) return;
284028
284507
  for (const filePath of targets) {
@@ -284115,7 +284594,7 @@ async function runDoctor(paths, opts) {
284115
284594
  reports.push({ file: displayPath(abs2), functions: 0, entryOnly: 0, uncovered: [], error: `File not found: ${abs2}` });
284116
284595
  continue;
284117
284596
  }
284118
- files.push(...statSync8(abs2).isDirectory() ? collectNudoFiles(abs2) : [abs2]);
284597
+ files.push(...statSync9(abs2).isDirectory() ? collectNudoFiles(abs2) : [abs2]);
284119
284598
  }
284120
284599
  for (const filePath of files) {
284121
284600
  reports.push(await doctorFile(filePath, externalRecords));
@@ -284166,12 +284645,12 @@ async function runDoctor(paths, opts) {
284166
284645
  const entryInfo = r.entryOnly > 0 ? `, ${r.entryOnly} entry-only` : "";
284167
284646
  console.log(` \xB7 ${r.functions} function(s)${entryInfo}`);
284168
284647
  if (r.uncovered.length > 0) {
284169
- console.log(` \u26A0 uncovered (no cases): ${r.uncovered.join(", ")}`);
284648
+ console.log(` \u26A0 uncovered (no call-site evidence): ${r.uncovered.join(", ")}`);
284170
284649
  }
284171
284650
  if (r.drift) {
284172
284651
  const refresh = `nudo infer ${r.file} --callsites ${(opts.callsites ?? []).join(" ")} --emit-cases=update`;
284173
284652
  console.log(
284174
- ` \u2717 drift: ${r.drift.added + r.drift.removed} directive(s) changed (+${r.drift.added} new, -${r.drift.removed} removed) \u2014 refresh with: ${refresh.trim()}`
284653
+ ` \u2717 drift: ${r.drift.added + r.drift.removed} debug witness directive(s) changed (+${r.drift.added} new, -${r.drift.removed} removed) \u2014 optional refresh: ${refresh.trim()}`
284175
284654
  );
284176
284655
  }
284177
284656
  if ((r.interfaceDrift ?? 0) > 0) {
@@ -284195,7 +284674,7 @@ program.command("doctor").description(
284195
284674
  });
284196
284675
  program.command("watch").description("Watch file(s) for changes and re-run inference").argument("<path>", "File or directory to watch").option("--dts", "Generate .d.ts files on each change").action((watchPath, opts) => {
284197
284676
  const resolved = resolve12(watchPath);
284198
- const isDir = existsSync16(resolved) && statSync8(resolved).isDirectory();
284677
+ const isDir = existsSync16(resolved) && statSync9(resolved).isDirectory();
284199
284678
  const getFiles = () => {
284200
284679
  if (!isDir) return [resolved];
284201
284680
  return collectNudoFiles(resolved);
@@ -284278,7 +284757,7 @@ program.command("watch").description("Watch file(s) for changes and re-run infer
284278
284757
  const pendingChanged = /* @__PURE__ */ new Set();
284279
284758
  watch(watchTarget, { recursive: isDir }, (_event, filename) => {
284280
284759
  if (!filename) return;
284281
- const fullPath = isDir ? join8(watchTarget, filename) : resolved;
284760
+ const fullPath = isDir ? join9(watchTarget, filename) : resolved;
284282
284761
  if (!isWatchRelevantPath(fullPath)) return;
284283
284762
  if (!existsSync16(fullPath)) {
284284
284763
  pendingChanged.add(fullPath);
@@ -284305,7 +284784,7 @@ program.command("watch").description("Watch file(s) for changes and re-run infer
284305
284784
  function collectNudoFiles(dir) {
284306
284785
  const results = [];
284307
284786
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
284308
- const fullPath = join8(dir, entry.name);
284787
+ const fullPath = join9(dir, entry.name);
284309
284788
  if (entry.isDirectory() && entry.name !== "node_modules") {
284310
284789
  results.push(...collectNudoFiles(fullPath));
284311
284790
  } else if (entry.isFile() && isNudoTargetPath(fullPath)) {
@@ -284318,18 +284797,16 @@ async function runGenerate(file, format, output) {
284318
284797
  const filePath = resolve12(file);
284319
284798
  const source = readFileSync15(filePath, "utf-8");
284320
284799
  const result = await analyzeFileAsync(filePath, source);
284321
- const functions = result.functions.filter(
284322
- (f) => f.cases.some((c) => c.source === "directive")
284323
- );
284800
+ const functions = result.functions.filter((f) => f.cases.length > 0);
284324
284801
  if (functions.length === 0) {
284325
- console.log("No functions with @nudo:case directives found.");
284802
+ console.log("No analyzed functions found.");
284326
284803
  return;
284327
284804
  }
284328
284805
  const zodChunks = [];
284329
284806
  const guardChunks = [];
284330
284807
  const dtsChunks = [];
284331
284808
  for (const fn2 of functions) {
284332
- const caseResults = fn2.cases.filter((c) => c.source === "directive");
284809
+ const caseResults = fn2.cases;
284333
284810
  const baseName = fn2.name;
284334
284811
  if (format === "zod" || format === "all") {
284335
284812
  const lines = [`
@@ -284337,7 +284814,8 @@ async function runGenerate(file, format, output) {
284337
284814
  for (const c of caseResults) {
284338
284815
  const inputSchemas = c.argAbs.map((a, i) => `arg${i}: ${absToZodSchema(a)}`).join(", ");
284339
284816
  const outputSchema = absToZodSchema(c.abs);
284340
- lines.push(`// Case "${c.name}":`);
284817
+ const label = c.name.startsWith("call@") || c.name.startsWith("entry@") ? c.name : `debug "${c.name}"`;
284818
+ lines.push(`// ${label}:`);
284341
284819
  lines.push(`// Input: { ${inputSchemas} }`);
284342
284820
  lines.push(`// Output: ${outputSchema}`);
284343
284821
  }
@@ -284346,9 +284824,9 @@ async function runGenerate(file, format, output) {
284346
284824
  if (format === "guard" || format === "all") {
284347
284825
  const lines = [`
284348
284826
  // === ${baseName} Type Guards ===`];
284349
- for (const c of caseResults) {
284350
- const guardName = `is${baseName}${c.name.charAt(0).toUpperCase() + c.name.slice(1)}Output`;
284351
- lines.push(generateGuardFunctionFromAbs(guardName, c.abs));
284827
+ const absForGuard = fn2.combinedAbs ?? caseResults[0]?.abs;
284828
+ if (absForGuard) {
284829
+ lines.push(generateGuardFunctionFromAbs(`is${baseName}Output`, absForGuard));
284352
284830
  }
284353
284831
  guardChunks.push(lines.join("\n"));
284354
284832
  }
@@ -284362,17 +284840,17 @@ async function runGenerate(file, format, output) {
284362
284840
  mkdirSync3(outDir, { recursive: true });
284363
284841
  const written = [];
284364
284842
  if (zodChunks.length > 0) {
284365
- const p = join8(outDir, `${stem}.nudo.zod.ts`);
284843
+ const p = join9(outDir, `${stem}.nudo.zod.ts`);
284366
284844
  writeFileSync6(p, zodChunks.join("\n") + "\n", "utf-8");
284367
284845
  written.push(p);
284368
284846
  }
284369
284847
  if (guardChunks.length > 0) {
284370
- const p = join8(outDir, `${stem}.nudo.guard.ts`);
284848
+ const p = join9(outDir, `${stem}.nudo.guard.ts`);
284371
284849
  writeFileSync6(p, guardChunks.join("\n") + "\n", "utf-8");
284372
284850
  written.push(p);
284373
284851
  }
284374
284852
  if (dtsChunks.length > 0) {
284375
- const p = join8(outDir, `${stem}.d.ts`);
284853
+ const p = join9(outDir, `${stem}.d.ts`);
284376
284854
  writeFileSync6(p, dtsChunks.join("\n") + "\n", "utf-8");
284377
284855
  written.push(p);
284378
284856
  }
@@ -284409,8 +284887,8 @@ function runHarvest(pkg, outOpt) {
284409
284887
  process.exitCode = 1;
284410
284888
  return;
284411
284889
  }
284412
- let entry = join8(typesDir, "index.d.ts");
284413
- const pkgJsonPath = join8(typesDir, "package.json");
284890
+ let entry = join9(typesDir, "index.d.ts");
284891
+ const pkgJsonPath = join9(typesDir, "package.json");
284414
284892
  if (existsSync16(pkgJsonPath)) {
284415
284893
  try {
284416
284894
  const pkgJson = JSON.parse(readFileSync15(pkgJsonPath, "utf-8"));
@@ -284447,10 +284925,10 @@ Usage \u2014 add this directive at the top of your JS file:`);
284447
284925
  const hintPath = resolve12(process.cwd()) === outDir ? basename5(out) : out;
284448
284926
  console.log(` /// @nudo:env ${hintPath}`);
284449
284927
  }
284450
- program.command("harvest").description("Convert @types/<pkg> .d.ts declarations into a Nudo env file (TS source using T.* constructors)").argument("[pkg]", "Package name under @types (e.g. node)").option("--out <file>", "Output .ts env file (default: ./nudo-harvest-<pkg>.ts)").option("--auto [dir]", "Scan directory (default .) for bare imports and report auto-harvestable @types packages").action(async (pkg, opts) => {
284928
+ program.command("harvest").description("Convert @types/<pkg> .d.ts declarations into a Nudo env file (constraint builders, not T.*)").argument("[pkg]", "Package name under @types (e.g. node)").option("--out <file>", "Output .ts env file (default: ./nudo-harvest-<pkg>.ts)").option("--auto [dir]", "Scan directory (default .) for bare imports and report auto-harvestable @types packages").action(async (pkg, opts) => {
284451
284929
  if (opts.auto !== void 0) {
284452
284930
  const dir = resolve12(typeof opts.auto === "string" ? opts.auto : ".");
284453
- const files = existsSync16(dir) && statSync8(dir).isDirectory() ? collectNudoFiles(dir) : existsSync16(dir) ? [dir] : [];
284931
+ const files = existsSync16(dir) && statSync9(dir).isDirectory() ? collectNudoFiles(dir) : existsSync16(dir) ? [dir] : [];
284454
284932
  if (files.length === 0) {
284455
284933
  console.error(`No inference targets under ${dir}`);
284456
284934
  process.exitCode = 1;