nudojs 1.2.0 → 1.3.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 +1585 -550
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -59,10 +59,14 @@ function termEquals(a, b) {
59
59
  }
60
60
  return false;
61
61
  }
62
+ function litKeyString(v2) {
63
+ if (typeof v2 === "number" && Object.is(v2, -0)) return "-0";
64
+ return String(v2);
65
+ }
62
66
  function termToString(t) {
63
67
  if (t.op === "lit") {
64
68
  if (typeof t.value === "string") return JSON.stringify(t.value);
65
- if (typeof t.value === "number" && Object.is(t.value, -0)) return "-0";
69
+ if (typeof t.value === "number") return litKeyString(t.value);
66
70
  return String(t.value);
67
71
  }
68
72
  if (t.op === "var") return t.id;
@@ -1012,6 +1016,48 @@ var init_pred = __esm({
1012
1016
  }
1013
1017
  });
1014
1018
 
1019
+ // ../core/src/algebra/projection-budget.ts
1020
+ var PROJECTION_MAX_DEPTH, ProjectionBudget;
1021
+ var init_projection_budget = __esm({
1022
+ "../core/src/algebra/projection-budget.ts"() {
1023
+ "use strict";
1024
+ PROJECTION_MAX_DEPTH = 64;
1025
+ ProjectionBudget = class {
1026
+ #maxDepth;
1027
+ #path = [];
1028
+ #onPath = /* @__PURE__ */ new Set();
1029
+ /** 粘性:本次投影是否发生过截断("cycle" / "depth"),供台账记录 */
1030
+ truncated;
1031
+ constructor(maxDepth = PROJECTION_MAX_DEPTH) {
1032
+ this.#maxDepth = maxDepth;
1033
+ }
1034
+ /**
1035
+ * 进入一个 Abs(或 Shape)子树。返回 null → 继续递归,之后必须配对
1036
+ * `exit()`;返回 "cycle" | "depth" → 不得递归,按出口各自的约定渲染
1037
+ * 截断标记(不调用 exit——本次 enter 没有入栈)。
1038
+ */
1039
+ enter(a) {
1040
+ if (this.#onPath.has(a)) {
1041
+ this.truncated = "cycle";
1042
+ return "cycle";
1043
+ }
1044
+ if (this.#path.length >= this.#maxDepth) {
1045
+ this.truncated = "depth";
1046
+ return "depth";
1047
+ }
1048
+ this.#path.push(a);
1049
+ this.#onPath.add(a);
1050
+ return null;
1051
+ }
1052
+ /** 离开子树(与返回 null 的 enter 严格配对)。 */
1053
+ exit() {
1054
+ const a = this.#path.pop();
1055
+ if (a !== void 0) this.#onPath.delete(a);
1056
+ }
1057
+ };
1058
+ }
1059
+ });
1060
+
1015
1061
  // ../core/src/algebra/abs.ts
1016
1062
  function anyVar(id, conf = "path") {
1017
1063
  return { shape: { k: "any" }, term: { op: "var", id }, conf };
@@ -1075,13 +1121,34 @@ function confJoin(a, b) {
1075
1121
  return rank[Math.max(ia < 0 ? 5 : ia, ib < 0 ? 5 : ib)];
1076
1122
  }
1077
1123
  function absToString(a) {
1078
- const parts = [shapeToString(a.shape)];
1079
- if (a.term) parts.push(`term=${termToString(a.term)}`);
1080
- if (a.pred && a.pred.op !== "true") parts.push(predToString(a.pred));
1081
- parts.push(`#${a.conf}`);
1082
- return parts.join(" ");
1124
+ return absToStringB(a, new ProjectionBudget());
1125
+ }
1126
+ function absToStringB(a, budget) {
1127
+ const stop2 = budget.enter(a);
1128
+ if (stop2) return stop2 === "cycle" ? "\u2026cycle" : "\u2026";
1129
+ try {
1130
+ const parts = [shapeToStringB(a.shape, budget)];
1131
+ if (a.term) parts.push(`term=${termToString(a.term)}`);
1132
+ if (a.pred && a.pred.op !== "true") parts.push(predToString(a.pred));
1133
+ parts.push(`#${a.conf}`);
1134
+ return parts.join(" ");
1135
+ } finally {
1136
+ budget.exit();
1137
+ }
1083
1138
  }
1084
1139
  function shapeToString(s) {
1140
+ return shapeToStringB(s, new ProjectionBudget());
1141
+ }
1142
+ function shapeToStringB(s, budget) {
1143
+ const stop2 = budget.enter(s);
1144
+ if (stop2) return stop2 === "cycle" ? "\u2026cycle" : "\u2026";
1145
+ try {
1146
+ return shapeToStringInner(s, budget);
1147
+ } finally {
1148
+ budget.exit();
1149
+ }
1150
+ }
1151
+ function shapeToStringInner(s, budget) {
1085
1152
  switch (s.k) {
1086
1153
  case "never":
1087
1154
  return "never";
@@ -1093,22 +1160,22 @@ function shapeToString(s) {
1093
1160
  return s.type;
1094
1161
  case "obj": {
1095
1162
  const entries = Object.entries(s.slots).map(
1096
- ([k, slot]) => `${k}${slot.optional ? "?" : ""}: ${absToString(slot.value)}`
1163
+ ([k, slot]) => `${k}${slot.optional ? "?" : ""}: ${absToStringB(slot.value, budget)}`
1097
1164
  );
1098
1165
  return `{ ${entries.join(", ")} }`;
1099
1166
  }
1100
1167
  case "arr":
1101
- return `${shapeToString(s.element.shape)}[]`;
1168
+ return `${shapeToStringB(s.element.shape, budget)}[]`;
1102
1169
  case "tuple":
1103
- return `[${s.elements.map((e) => shapeToString(e.shape)).join(", ")}]`;
1170
+ return `[${s.elements.map((e) => shapeToStringB(e.shape, budget)).join(", ")}]`;
1104
1171
  case "fn":
1105
1172
  return `fn(${s.params.join(", ")})${s.name ? ` ${s.name}` : ""}`;
1106
1173
  case "brand":
1107
1174
  return s.name;
1108
1175
  case "eff":
1109
- return `${s.eff}<${shapeToString(s.inner.shape)}>`;
1176
+ return `${s.eff}<${shapeToStringB(s.inner.shape, budget)}>`;
1110
1177
  case "sum":
1111
- return s.members.map(absToString).join(" | ");
1178
+ return s.members.map((m) => absToStringB(m, budget)).join(" | ");
1112
1179
  default:
1113
1180
  return "\xB7";
1114
1181
  }
@@ -1153,6 +1220,7 @@ var init_abs = __esm({
1153
1220
  "use strict";
1154
1221
  init_term();
1155
1222
  init_pred();
1223
+ init_projection_budget();
1156
1224
  never = { shape: { k: "never" }, conf: "exact" };
1157
1225
  unknown = { shape: { k: "unknown" }, conf: "partial" };
1158
1226
  anyAbs = { shape: { k: "any" }, conf: "path" };
@@ -15788,7 +15856,7 @@ function absFunction(params, impl, opts) {
15788
15856
  return a;
15789
15857
  }
15790
15858
  function termKey2(t) {
15791
- if (t.op === "lit") return `L:${typeof t.value}:${String(t.value)}`;
15859
+ if (t.op === "lit") return `L:${typeof t.value}:${litKeyString(t.value)}`;
15792
15860
  if (t.op === "var") return `V:${t.id}`;
15793
15861
  return `A:${t.fn}(${t.args.map(termKey2).join(",")})`;
15794
15862
  }
@@ -15904,6 +15972,7 @@ var init_abs_fn = __esm({
15904
15972
  "../core/src/algebra/abs-fn.ts"() {
15905
15973
  "use strict";
15906
15974
  init_abs();
15975
+ init_term();
15907
15976
  implByAbs = /* @__PURE__ */ new WeakMap();
15908
15977
  }
15909
15978
  });
@@ -16258,7 +16327,7 @@ function absShapeKey(a, seen = /* @__PURE__ */ new Set()) {
16258
16327
  try {
16259
16328
  const s = a.shape;
16260
16329
  if (s.k === "prim") {
16261
- if (a.term?.op === "lit") return `prim:${s.type}:${String(a.term.value)}`;
16330
+ if (a.term?.op === "lit") return `prim:${s.type}:${litKeyString(a.term.value)}`;
16262
16331
  const t = a.term ? termToString(a.term) : "";
16263
16332
  const p = a.pred && a.pred.op !== "true" ? predToString(a.pred) : "";
16264
16333
  return `prim:${s.type}:${t}:${p}`;
@@ -16270,7 +16339,7 @@ function absShapeKey(a, seen = /* @__PURE__ */ new Set()) {
16270
16339
  const v2 = a.term.value;
16271
16340
  if (v2 === void 0) return "unknown:undefined";
16272
16341
  if (v2 === null) return "unknown:null";
16273
- return `unknown:lit:${typeof v2}:${String(v2)}`;
16342
+ return `unknown:lit:${typeof v2}:${litKeyString(v2)}`;
16274
16343
  }
16275
16344
  if (a.term) return `unknown:${termToString(a.term)}`;
16276
16345
  return "unknown";
@@ -16952,7 +17021,7 @@ function bindTypeVars(pattern, arg, map, depth = 0) {
16952
17021
  }
16953
17022
  if (ps.k === "obj" && as.k === "obj") {
16954
17023
  for (const [key, slot] of Object.entries(ps.slots)) {
16955
- const aSlot = as.slots[key];
17024
+ const aSlot = getSlot(as.slots, key);
16956
17025
  if (aSlot) bindTypeVars(slot.value, aSlot.value, map, depth + 1);
16957
17026
  }
16958
17027
  return;
@@ -19471,6 +19540,7 @@ function absToJsonNative(a, seen) {
19471
19540
  if (t?.op === "lit") return t.value;
19472
19541
  const s = a.shape;
19473
19542
  if (s.k === "tuple") {
19543
+ if (a.shape.rest) return NOT_LITERAL;
19474
19544
  seen.add(a);
19475
19545
  const holes = a.shape.holes ?? [];
19476
19546
  const out = [];
@@ -21902,51 +21972,15 @@ function toNumericAbs(a) {
21902
21972
  }
21903
21973
  return n;
21904
21974
  }
21905
- function updateAddAbs(a) {
21975
+ function updateAddAbs(a, phi2 = pTrue) {
21906
21976
  const n = toNumericAbs(a);
21907
21977
  const one = n.shape.k === "prim" && n.shape.type === "bigint" ? bigintLit(1n) : numLit(1);
21908
- if (n.shape.k === "prim" && n.shape.type === "bigint" && !n.term) {
21909
- return abs({ k: "prim", type: "bigint" }, void 0, void 0, confJoin(n.conf, "exact"));
21910
- }
21911
- return addNumeric(n, one);
21978
+ return add(n, one, phi2);
21912
21979
  }
21913
- function updateSubAbs(a) {
21980
+ function updateSubAbs(a, phi2 = pTrue) {
21914
21981
  const n = toNumericAbs(a);
21915
21982
  const one = n.shape.k === "prim" && n.shape.type === "bigint" ? bigintLit(1n) : numLit(1);
21916
- if (n.shape.k === "prim" && n.shape.type === "bigint" && !n.term) {
21917
- return abs({ k: "prim", type: "bigint" }, void 0, void 0, confJoin(n.conf, "exact"));
21918
- }
21919
- return subNumeric(n, one);
21920
- }
21921
- function addNumeric(a, b) {
21922
- const ra = litValue(a);
21923
- const rb = litValue(b);
21924
- const va = ra.ok ? ra.value : void 0;
21925
- const vb = rb.ok ? rb.value : void 0;
21926
- if (typeof va === "bigint" && typeof vb === "bigint") return bigintLit(va + vb);
21927
- if (typeof va === "number" && typeof vb === "number") return numLit(va + vb);
21928
- const big = a.shape.k === "prim" && a.shape.type === "bigint" || typeof va === "bigint";
21929
- return abs(
21930
- { k: "prim", type: big ? "bigint" : "number" },
21931
- void 0,
21932
- void 0,
21933
- confJoin(confJoin(a.conf, b.conf), "path")
21934
- );
21935
- }
21936
- function subNumeric(a, b) {
21937
- const ra = litValue(a);
21938
- const rb = litValue(b);
21939
- const va = ra.ok ? ra.value : void 0;
21940
- const vb = rb.ok ? rb.value : void 0;
21941
- if (typeof va === "bigint" && typeof vb === "bigint") return bigintLit(va - vb);
21942
- if (typeof va === "number" && typeof vb === "number") return numLit(va - vb);
21943
- const big = a.shape.k === "prim" && a.shape.type === "bigint" || typeof va === "bigint";
21944
- return abs(
21945
- { k: "prim", type: big ? "bigint" : "number" },
21946
- void 0,
21947
- void 0,
21948
- confJoin(confJoin(a.conf, b.conf), "path")
21949
- );
21983
+ return sub(n, one, phi2);
21950
21984
  }
21951
21985
  function toNumberAbs(a) {
21952
21986
  if (isSymbolAbs(a)) {
@@ -22227,6 +22261,7 @@ var init_surface = __esm({
22227
22261
  init_symbol_id();
22228
22262
  init_term();
22229
22263
  init_pred();
22264
+ init_arithmetic();
22230
22265
  init_nudo_throw();
22231
22266
  init_may_throw();
22232
22267
  }
@@ -23019,24 +23054,40 @@ function leqShape(src, tgt, phi2, env2, depth) {
23019
23054
  return leqWithPred(s.element, t.element, phi2, env2, depth + 1);
23020
23055
  }
23021
23056
  if (s.k === "tuple") {
23022
- return s.elements.every((el) => leqWithPred(el, t.element, phi2, env2, depth + 1).ok) ? ok() : fail("tuple \u22AD arr");
23057
+ const elsOk = s.elements.every((el) => leqWithPred(el, t.element, phi2, env2, depth + 1).ok);
23058
+ const restOk = !s.rest || leqWithPred(s.rest, t.element, phi2, env2, depth + 1).ok;
23059
+ return elsOk && restOk ? ok() : fail("tuple \u22AD arr");
23023
23060
  }
23024
23061
  return fail(`shape ${s.k} \u22AD arr`);
23025
23062
  }
23026
23063
  if (t.k === "tuple") {
23027
23064
  if (s.k !== "tuple") return fail(`shape ${s.k} \u22AD tuple`);
23028
- if (s.elements.length !== t.elements.length) {
23065
+ if (!t.rest && s.elements.length !== t.elements.length) {
23029
23066
  return fail(`tuple arity ${s.elements.length} \u22AD ${t.elements.length}`);
23030
23067
  }
23068
+ if (t.rest && s.elements.length < t.elements.length) {
23069
+ return fail(`tuple arity ${s.elements.length} \u22AD ${t.elements.length} (target requires ${t.elements.length})`);
23070
+ }
23071
+ if (s.rest && !t.rest) {
23072
+ return fail(`tuple rest \u22AD fixed arity ${t.elements.length}`);
23073
+ }
23031
23074
  const sHoles = s.holes ?? [];
23032
23075
  const tHoles = t.holes ?? [];
23033
- if (sHoles.length !== tHoles.length || sHoles.some((h, i) => h !== tHoles[i])) {
23034
- return fail(`tuple holes [${sHoles.join(",")}] \u22AD [${tHoles.join(",")}]`);
23076
+ const overlap = Math.min(s.elements.length, t.elements.length);
23077
+ for (let i = 0; i < overlap; i++) {
23078
+ if (sHoles.includes(i) !== tHoles.includes(i)) {
23079
+ return fail(`tuple holes [${sHoles.join(",")}] \u22AD [${tHoles.join(",")}]`);
23080
+ }
23035
23081
  }
23036
- for (let i = 0; i < t.elements.length; i++) {
23037
- const r = leqWithPred(s.elements[i], t.elements[i], phi2, env2, depth + 1);
23082
+ for (let i = 0; i < s.elements.length; i++) {
23083
+ const tgt2 = i < t.elements.length ? t.elements[i] : t.rest;
23084
+ const r = leqWithPred(s.elements[i], tgt2, phi2, env2, depth + 1);
23038
23085
  if (!r.ok) return fail(`tuple[${i}]: ${r.reason}`);
23039
23086
  }
23087
+ if (s.rest && t.rest) {
23088
+ const r = leqWithPred(s.rest, t.rest, phi2, env2, depth + 1);
23089
+ if (!r.ok) return fail(`tuple rest: ${r.reason}`);
23090
+ }
23040
23091
  return ok();
23041
23092
  }
23042
23093
  if (t.k === "fn") {
@@ -23121,13 +23172,29 @@ function formatAbs(a, opts = {}) {
23121
23172
  return parts.join(" ");
23122
23173
  }
23123
23174
  function formatShapeSlot(a) {
23124
- const base = formatShape(a);
23175
+ return formatShapeSlotB(a, new ProjectionBudget());
23176
+ }
23177
+ function formatShapeSlotB(a, budget) {
23178
+ const base = formatShapeB(a, budget);
23125
23179
  if (!a.term || a.term.op === "lit") return base;
23126
23180
  if (a.shape.k === "fn" || a.shape.k === "arr") return base;
23127
23181
  if (a.shape.k === "any" && a.term.op === "var") return termToString(a.term);
23128
23182
  return `${base} = ${termToString(a.term)}`;
23129
23183
  }
23130
23184
  function formatShape(a) {
23185
+ return formatShapeB(a, new ProjectionBudget());
23186
+ }
23187
+ function formatShapeB(a, budget) {
23188
+ const stop2 = budget.enter(a);
23189
+ if (stop2 === "cycle") return FORMAT_TRUNC.cycle;
23190
+ if (stop2 === "depth") return FORMAT_TRUNC.depth;
23191
+ try {
23192
+ return formatShapeInner(a, budget);
23193
+ } finally {
23194
+ budget.exit();
23195
+ }
23196
+ }
23197
+ function formatShapeInner(a, budget) {
23131
23198
  const s = a.shape;
23132
23199
  switch (s.k) {
23133
23200
  case "never":
@@ -23154,28 +23221,28 @@ function formatShape(a) {
23154
23221
  const entries = Object.entries(s.slots).map(([k, slot]) => {
23155
23222
  const opt = slot.optional ? "?" : "";
23156
23223
  const key = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
23157
- return `${key}${opt}: ${formatShape(slot.value)}`;
23224
+ return `${key}${opt}: ${formatShapeB(slot.value, budget)}`;
23158
23225
  });
23159
23226
  return `{ ${entries.join(", ")} }`;
23160
23227
  }
23161
23228
  case "arr": {
23162
23229
  if (s.element.shape.k === "any" && s.element.term?.op === "var") {
23163
- return `arr(${formatShapeSlot(s.element)})`;
23230
+ return `arr(${formatShapeSlotB(s.element, budget)})`;
23164
23231
  }
23165
- return `${formatShape(s.element)}[]`;
23232
+ return `${formatShapeB(s.element, budget)}[]`;
23166
23233
  }
23167
23234
  case "tuple": {
23168
23235
  const holes = s.holes ?? [];
23169
23236
  const parts = s.elements.map(
23170
- (el, i) => holes.includes(i) ? "" : formatShape(el)
23237
+ (el, i) => holes.includes(i) ? "" : formatShapeB(el, budget)
23171
23238
  );
23172
23239
  if (s.rest) {
23173
- parts.push(`...${formatShape(s.rest)}`);
23240
+ parts.push(`...${formatShapeB(s.rest, budget)}`);
23174
23241
  }
23175
23242
  return `[${parts.join(", ")}]`;
23176
23243
  }
23177
23244
  case "fn": {
23178
- const ret = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
23245
+ const ret = s.returnType !== void 0 ? formatShapeSlotB(s.returnType, budget) : "?";
23179
23246
  const labels = s.params ?? [];
23180
23247
  const paramTypes = s.paramTypes;
23181
23248
  const hasMarkers = labels.some(
@@ -23187,7 +23254,7 @@ function formatShape(a) {
23187
23254
  for (let i = 0; i < n; i++) {
23188
23255
  const label = labels[i];
23189
23256
  const type = paramTypes?.[i];
23190
- const typeText = type !== void 0 ? formatShapeSlot(type) : void 0;
23257
+ const typeText = type !== void 0 ? formatShapeSlotB(type, budget) : void 0;
23191
23258
  if (label?.startsWith("...")) {
23192
23259
  ps.push(typeText ? `...${label.slice(3)}: ${typeText}` : label);
23193
23260
  } else if (label?.endsWith("?")) {
@@ -23204,7 +23271,7 @@ function formatShape(a) {
23204
23271
  return `(${renderLabeled().join(", ")}) => ${ret}`;
23205
23272
  }
23206
23273
  if (paramTypes && paramTypes.length > 0) {
23207
- const ps = paramTypes.map((p) => formatShapeSlot(p));
23274
+ const ps = paramTypes.map((p) => formatShapeSlotB(p, budget));
23208
23275
  return `(${ps.join(", ")}) => ${ret}`;
23209
23276
  }
23210
23277
  return `(${labels.join(", ")}) => ${ret}`;
@@ -23215,7 +23282,7 @@ function formatShape(a) {
23215
23282
  if (slots) {
23216
23283
  const arg = (key) => {
23217
23284
  const sl = slots[key];
23218
- return sl ? formatShapeSlot(sl.value) : void 0;
23285
+ return sl ? formatShapeSlotB(sl.value, budget) : void 0;
23219
23286
  };
23220
23287
  if (s.name === "Map" || s.name === "ReadonlyMap" || s.name === "WeakMap") {
23221
23288
  const k = arg("__key");
@@ -23230,12 +23297,12 @@ function formatShape(a) {
23230
23297
  return `${s.name}`;
23231
23298
  }
23232
23299
  case "eff":
23233
- return `${s.eff}<${formatShape(s.inner)}>`;
23300
+ return `${s.eff}<${formatShapeB(s.inner, budget)}>`;
23234
23301
  case "sum": {
23235
23302
  const seen = /* @__PURE__ */ new Set();
23236
23303
  const parts = [];
23237
23304
  for (const m of s.members) {
23238
- const t = formatShape(m);
23305
+ const t = formatShapeB(m, budget);
23239
23306
  if (seen.has(t)) continue;
23240
23307
  seen.add(t);
23241
23308
  parts.push(t);
@@ -23259,12 +23326,15 @@ function formatAbsMultiline(a, label) {
23259
23326
  lines.push(` conf: ${a.conf}`);
23260
23327
  return lines.join("\n");
23261
23328
  }
23329
+ var FORMAT_TRUNC;
23262
23330
  var init_format = __esm({
23263
23331
  "../core/src/algebra/format.ts"() {
23264
23332
  "use strict";
23265
23333
  init_abs();
23266
23334
  init_term();
23267
23335
  init_pred();
23336
+ init_projection_budget();
23337
+ FORMAT_TRUNC = { cycle: "\u2026cycle", depth: "\u2026" };
23268
23338
  }
23269
23339
  });
23270
23340
 
@@ -23938,12 +24008,19 @@ function parseNudoImportPayload(payload) {
23938
24008
  if (s === "") return void 0;
23939
24009
  const named = s.match(/^\{([^}]+)\}\s*from\s*["']([^"']+)["']$/);
23940
24010
  if (named) {
23941
- const names2 = named[1].split("\n").map((line) => line.replace(/^\s*\/\/\/?\s?/, "").trim()).join("\n").split(",").map((x) => x.trim()).filter(Boolean).map((x) => x.split(/\s+as\s+/).pop().trim());
23942
- return { names: names2, spec: named[2] };
24011
+ const names2 = [];
24012
+ const importedOfLocal = {};
24013
+ for (const x of named[1].split("\n").map((line) => line.replace(/^\s*\/\/\/?\s?/, "").trim()).join("\n").split(",").map((x2) => x2.trim()).filter(Boolean)) {
24014
+ const seg = x.split(/\s+as\s+/);
24015
+ const local = seg[seg.length - 1].trim();
24016
+ names2.push(local);
24017
+ if (seg.length > 1) importedOfLocal[local] = seg[0].trim();
24018
+ }
24019
+ return { names: names2, importedOfLocal, spec: named[2] };
23943
24020
  }
23944
24021
  const ns = s.match(/^\*\s+as\s+(\w+)\s+from\s*["']([^"']+)["']$/);
23945
24022
  if (ns) {
23946
- return { names: [`*${ns[1]}`], spec: ns[2] };
24023
+ return { names: [`*${ns[1]}`], importedOfLocal: {}, spec: ns[2] };
23947
24024
  }
23948
24025
  if (/^[\w$]+\s+from\b/.test(s) || /^[\w$]+\s*,/.test(s)) return "malformed-default";
23949
24026
  return "malformed";
@@ -23953,7 +24030,7 @@ function fileDirectiveVisibleText(source) {
23953
24030
  }
23954
24031
  function extractFileEnvNames(source) {
23955
24032
  const text = fileDirectiveVisibleText(source);
23956
- const re = new RegExp(`${LINE_COMMENT_PREFIX}\\s*@nudo:env\\s+([^\\n*]+)`, "g");
24033
+ const re = new RegExp(`${LINE_COMMENT_PREFIX}[ \\t]*@nudo:env[ \\t]+([^\\n*]+)`, "g");
23957
24034
  const out = [];
23958
24035
  let m;
23959
24036
  while (m = re.exec(text)) {
@@ -23964,7 +24041,7 @@ function extractFileEnvNames(source) {
23964
24041
  function extractMockModuleRecords(source) {
23965
24042
  const text = fileDirectiveVisibleText(source);
23966
24043
  const re = new RegExp(
23967
- `${LINE_COMMENT_PREFIX}\\s*@nudo:mock-module\\s+([^\\n]+)`,
24044
+ `${LINE_COMMENT_PREFIX}[ \\t]*@nudo:mock-module[ \\t]+([^\\n]+)`,
23968
24045
  "g"
23969
24046
  );
23970
24047
  const out = [];
@@ -24165,7 +24242,7 @@ var init_directive_scan = __esm({
24165
24242
  init_parse_source();
24166
24243
  isPathLikeEnv = (s) => /^[^\s]+$/.test(s) && (s.startsWith("./") || s.startsWith("../") || s.startsWith("/") || /\.(ts|js|mjs|cjs|tsx|jsx)$/.test(s));
24167
24244
  LINE_COMMENT_PREFIX = "(?:^|[^/:])\\/\\/\\/?";
24168
- CASE_NAME_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/\/\/?\s*)?@nudo:case\s+"([^"]+)"\s*\(/g;
24245
+ CASE_NAME_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:case[ \t]+"([^"\n]+)"[ \t]*\(/g;
24169
24246
  }
24170
24247
  });
24171
24248
 
@@ -24782,11 +24859,7 @@ var init_constraint = __esm({
24782
24859
  });
24783
24860
 
24784
24861
  // ../core/src/algebra/refine.ts
24785
- function extractNudoImports(source) {
24786
- const out = [];
24787
- for (const rec of extractNudoImportRecords(source)) {
24788
- out.push({ names: rec.names, spec: rec.spec });
24789
- }
24862
+ function diagnoseMalformedNudoImports(source) {
24790
24863
  for (const bad of scanMalformedNudoImports(source)) {
24791
24864
  if (bad === "malformed-default") {
24792
24865
  collectDiag({
@@ -24800,7 +24873,13 @@ function extractNudoImports(source) {
24800
24873
  });
24801
24874
  }
24802
24875
  }
24803
- return out;
24876
+ }
24877
+ function extractNudoImports(source) {
24878
+ diagnoseMalformedNudoImports(source);
24879
+ return extractNudoImportRecords(source).map((rec) => ({
24880
+ names: rec.names,
24881
+ spec: rec.spec
24882
+ }));
24804
24883
  }
24805
24884
  function setRefineDiagCollector(fn2) {
24806
24885
  refineDiagCollector = fn2;
@@ -24897,6 +24976,21 @@ function rewriteSidecarSource(src, fromFile) {
24897
24976
  cuts.push({ start: stmt.start, end: decl.start, text: "" });
24898
24977
  continue;
24899
24978
  }
24979
+ if (!stmt.source) {
24980
+ let aliasPairs = false;
24981
+ for (const spec of stmt.specifiers) {
24982
+ if (spec.type !== "ExportSpecifier") continue;
24983
+ if (spec.local.type !== "Identifier") continue;
24984
+ const key = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
24985
+ if (key === void 0) continue;
24986
+ exportPairs.push({ key, expr: spec.local.name });
24987
+ aliasPairs = true;
24988
+ }
24989
+ if (aliasPairs) {
24990
+ cuts.push({ start: stmt.start, end: stmt.end, text: "" });
24991
+ continue;
24992
+ }
24993
+ }
24900
24994
  badExport("'export { \u2026 }' (list / re-export)");
24901
24995
  if (stmt.start != null && stmt.end != null) {
24902
24996
  cuts.push({ start: stmt.start, end: stmt.end, text: "" });
@@ -25053,14 +25147,15 @@ function execSidecar(src, opts, chain, preloaded) {
25053
25147
  });
25054
25148
  prologue.push(`var ${n.local} = __nudoInjects;`);
25055
25149
  } else if (n.imported !== void 0) {
25056
- if (!(n.imported in sidecarInjects)) {
25150
+ const hasOwnInject = Object.hasOwn(sidecarInjects, n.imported);
25151
+ if (!hasOwnInject) {
25057
25152
  collectDiag({
25058
25153
  code: "nudo:interface-load",
25059
25154
  message: `sidecar import '{ ${n.imported} }' from '${imp.spec}' is not an injected builder`,
25060
25155
  file: fromFile
25061
25156
  });
25062
- }
25063
- if (n.local !== n.imported || !(n.imported in sidecarInjects)) {
25157
+ prologue.push(`var ${n.local} = undefined;`);
25158
+ } else if (n.local !== n.imported) {
25064
25159
  prologue.push(`var ${n.local} = __nudoInjects[${JSON.stringify(n.imported)}];`);
25065
25160
  }
25066
25161
  } else {
@@ -25129,21 +25224,23 @@ function execSidecar(src, opts, chain, preloaded) {
25129
25224
  if (n.ns) {
25130
25225
  deps[n.local] = depExports;
25131
25226
  } else if (n.imported !== void 0) {
25132
- if (!(n.imported in depExports)) {
25227
+ if (Object.hasOwn(depExports, n.imported)) {
25228
+ deps[n.local] = depExports[n.imported];
25229
+ } else {
25133
25230
  collectDiag({
25134
25231
  code: "nudo:interface-load",
25135
25232
  message: `sidecar '${imp.spec}' has no export '${n.imported}'`,
25136
25233
  file: fromFile
25137
25234
  });
25235
+ deps[n.local] = void 0;
25138
25236
  }
25139
- deps[n.local] = depExports[n.imported];
25140
25237
  } else {
25141
25238
  collectDiag({
25142
25239
  code: "nudo:interface-load",
25143
25240
  message: `sidecar default import '${n.local}' from '${imp.spec}' has no default export (sidecars export named constraints only)`,
25144
25241
  file: fromFile
25145
25242
  });
25146
- deps[n.local] = depExports.default;
25243
+ deps[n.local] = Object.hasOwn(depExports, "default") ? depExports.default : void 0;
25147
25244
  }
25148
25245
  }
25149
25246
  }
@@ -25247,7 +25344,8 @@ function resetNudoModuleExecCache() {
25247
25344
  }
25248
25345
  function collectConstraints(source, opts) {
25249
25346
  const map = /* @__PURE__ */ new Map();
25250
- const imports = extractNudoImports(source);
25347
+ diagnoseMalformedNudoImports(source);
25348
+ const imports = extractNudoImportRecords(source);
25251
25349
  for (const imp of imports) {
25252
25350
  if (!opts.loadModule || !opts.fromFile) {
25253
25351
  collectDiag({
@@ -25302,19 +25400,20 @@ function collectConstraints(source, opts) {
25302
25400
  }
25303
25401
  continue;
25304
25402
  }
25305
- const v2 = exports[name];
25403
+ const imported = imp.importedOfLocal[name] ?? name;
25404
+ const v2 = Object.hasOwn(exports, imported) ? exports[imported] : void 0;
25306
25405
  if (isNudoConstraint(v2)) {
25307
25406
  map.set(name, v2);
25308
25407
  } else if (v2 === void 0) {
25309
25408
  collectDiag({
25310
25409
  code: "nudo:interface-load",
25311
- message: `sidecar '${imp.spec}' has no export '${name}'`,
25410
+ message: `sidecar '${imp.spec}' has no export '${imported}'`,
25312
25411
  file: opts.fromFile
25313
25412
  });
25314
25413
  } else {
25315
25414
  collectDiag({
25316
25415
  code: "nudo:interface-load",
25317
- message: `sidecar '${imp.spec}' export '${name}' is not a Nudo constraint`,
25416
+ message: `sidecar '${imp.spec}' export '${imported}' is not a Nudo constraint`,
25318
25417
  file: opts.fromFile
25319
25418
  });
25320
25419
  }
@@ -25345,7 +25444,13 @@ function extractRefinesFromSource(source, fnName, opts = {}) {
25345
25444
  const [, param, cName] = m;
25346
25445
  if (param === "return") continue;
25347
25446
  const c = constraints.get(cName);
25348
- if (!c) continue;
25447
+ if (!c) {
25448
+ collectDiag({
25449
+ code: "nudo:contract-syntax",
25450
+ message: `Unknown constraint name '${cName}' in @nudo:contract (expected a name bound by @nudo:import)`
25451
+ });
25452
+ continue;
25453
+ }
25349
25454
  out.push({
25350
25455
  param,
25351
25456
  pred: instantiateConstraint(c, param),
@@ -25382,7 +25487,13 @@ function extractRefineReturnFromSource(source, fnName, opts = {}) {
25382
25487
  }
25383
25488
  const cName = m[1];
25384
25489
  const c = constraints.get(cName);
25385
- if (!c) continue;
25490
+ if (!c) {
25491
+ collectDiag({
25492
+ code: "nudo:contract-syntax",
25493
+ message: `Unknown constraint name '${cName}' in @nudo:contract return (expected a name bound by @nudo:import)`
25494
+ });
25495
+ continue;
25496
+ }
25386
25497
  return { name: cName, constraint: c };
25387
25498
  }
25388
25499
  }
@@ -25451,8 +25562,10 @@ var init_unsupported = __esm({
25451
25562
  NudoUnsupportedError = class extends Error {
25452
25563
  reason;
25453
25564
  loc;
25454
- constructor(reason, loc) {
25455
- super(`nudo:unsupported ${reason}`);
25565
+ constructor(reason, loc, detail) {
25566
+ super(
25567
+ detail ? `nudo:unsupported ${reason} (${detail})` : `nudo:unsupported ${reason}`
25568
+ );
25456
25569
  this.name = "NudoUnsupportedError";
25457
25570
  this.reason = reason;
25458
25571
  this.loc = loc;
@@ -26911,15 +27024,15 @@ function transpileStatement(stmt, depth, opts) {
26911
27024
  if (nsSpecs.length > 0) {
26912
27025
  if (!stmt.source) return `${pad}/* export * as without source skipped */`;
26913
27026
  return nsSpecs.map((s) => {
26914
- const exported = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
27027
+ const exported = s.exported.type === "Identifier" ? s.exported.name : JSON.stringify(s.exported.value);
26915
27028
  return `${pad}export * as ${exported} from ${JSON.stringify(stmt.source.value)};`;
26916
27029
  }).join("\n");
26917
27030
  }
26918
27031
  const specs = stmt.specifiers.filter(
26919
27032
  (s) => "local" in s
26920
27033
  ).map((s) => {
26921
- const local = s.local.type === "Identifier" ? s.local.name : s.local.value;
26922
- const exported = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
27034
+ const local = s.local.type === "Identifier" ? s.local.name : JSON.stringify(s.local.value);
27035
+ const exported = s.exported.type === "Identifier" ? s.exported.name : JSON.stringify(s.exported.value);
26923
27036
  return local === exported ? local : `${local} as ${exported}`;
26924
27037
  }).join(", ");
26925
27038
  if (stmt.source) {
@@ -28886,10 +28999,10 @@ function $toNumeric(a) {
28886
28999
  return toNumericAbs(a);
28887
29000
  }
28888
29001
  function $updateAdd(a) {
28889
- return updateAddAbs(a);
29002
+ return updateAddAbs(a, currentExecPhi());
28890
29003
  }
28891
29004
  function $updateSub(a) {
28892
- return updateSubAbs(a);
29005
+ return updateSubAbs(a, currentExecPhi());
28893
29006
  }
28894
29007
  function $neg(a) {
28895
29008
  return negAbs(a);
@@ -29153,21 +29266,51 @@ var init_member_diag = __esm({
29153
29266
  });
29154
29267
 
29155
29268
  // ../core/src/algebra/exec/class-registry.ts
29269
+ function beginClassEpoch() {
29270
+ classEpoch++;
29271
+ }
29272
+ function drainClassCollisions() {
29273
+ return classCollisions.splice(0, classCollisions.length);
29274
+ }
29275
+ function specShape(spec) {
29276
+ const ctorName = spec.ctor?.name ?? "anon";
29277
+ const methods2 = Object.keys(spec.methods ?? {}).sort().join(",");
29278
+ const statics = Object.keys(spec.staticMethods ?? {}).sort().join(",");
29279
+ const accessors = Object.keys(spec.accessors ?? {}).sort().join(",");
29280
+ return `${ctorName}[${methods2}](${statics}){${accessors}}`;
29281
+ }
29156
29282
  function registerEvalClass(spec) {
29283
+ const prev = classRegistry.get(spec.name);
29284
+ const prevEpoch = classEpochs.get(spec.name);
29285
+ if (prev && prev !== spec && // 同 epoch(同一次 run 内)注册两次才可能是真碰撞:
29286
+ // 块级作用域同名类。跨 epoch = 顺序重注册(常态)。
29287
+ prevEpoch === classEpoch && specShape(prev) !== specShape(spec)) {
29288
+ classCollisions.push({
29289
+ name: spec.name,
29290
+ previous: specShape(prev),
29291
+ next: specShape(spec)
29292
+ });
29293
+ }
29157
29294
  classRegistry.set(spec.name, spec);
29295
+ classEpochs.set(spec.name, classEpoch);
29158
29296
  }
29159
29297
  function getEvalClass(name) {
29160
29298
  return classRegistry.get(name);
29161
29299
  }
29162
29300
  function clearBClasses() {
29163
29301
  classRegistry.clear();
29302
+ classEpochs.clear();
29303
+ classCollisions.length = 0;
29164
29304
  }
29165
- var classRegistry;
29305
+ var classRegistry, classEpoch, classEpochs, classCollisions;
29166
29306
  var init_class_registry = __esm({
29167
29307
  "../core/src/algebra/exec/class-registry.ts"() {
29168
29308
  "use strict";
29169
29309
  init_class_mark();
29170
29310
  classRegistry = /* @__PURE__ */ new Map();
29311
+ classEpoch = 0;
29312
+ classEpochs = /* @__PURE__ */ new Map();
29313
+ classCollisions = [];
29171
29314
  }
29172
29315
  });
29173
29316
 
@@ -29691,6 +29834,8 @@ function $copy(a) {
29691
29834
  {
29692
29835
  k: "tuple",
29693
29836
  elements: s.elements.map($copy),
29837
+ // rest 槽必须随快照迁移(与 hof snapshotAbs 同口径),否则副本丢尾段
29838
+ ...s.rest ? { rest: $copy(s.rest) } : {},
29694
29839
  holes: s.holes ? [...s.holes] : void 0
29695
29840
  },
29696
29841
  a.term,
@@ -29881,7 +30026,7 @@ function $idx(a, i) {
29881
30026
  if (slots.length === 0) return objShape.open ? unknown : undef();
29882
30027
  const joinSlotsWithUndef = () => joinAbs(slots.reduce((x, y) => joinAbs(x, y)), undef());
29883
30028
  if (typeof iv === "string" || typeof iv === "number" || typeof iv === "boolean") {
29884
- const slot = objShape.slots[String(iv)];
30029
+ const slot = getSlot(objShape.slots, String(iv));
29885
30030
  if (slot) return slot.value;
29886
30031
  if (objShape.open) return unknown;
29887
30032
  return undef();
@@ -30098,6 +30243,20 @@ function $arrRest(a, start) {
30098
30243
  return parts.length ? parts.reduce((x, y) => joinAbs(x, y)) : unknown;
30099
30244
  }
30100
30245
  if (a.shape.k === "tuple") {
30246
+ if (a.shape.rest) {
30247
+ const sliced = a.shape.elements.slice(start);
30248
+ if (start < a.shape.elements.length && !shouldWidenArrayLiteral(sliced.length + 1)) {
30249
+ return abs(
30250
+ { k: "tuple", elements: sliced, rest: a.shape.rest },
30251
+ void 0,
30252
+ void 0,
30253
+ a.conf
30254
+ );
30255
+ }
30256
+ const restEl = a.shape.rest.shape.k === "arr" ? a.shape.rest.shape.element : a.shape.rest;
30257
+ const el = sliced.reduce((x, y) => joinAbs(x, y), restEl);
30258
+ return abs({ k: "arr", element: el }, void 0, void 0, a.conf);
30259
+ }
30101
30260
  return tupleOrWiden(a.shape.elements.slice(start), a.conf);
30102
30261
  }
30103
30262
  if (a.shape.k === "arr") return a;
@@ -32610,7 +32769,7 @@ function namespaceAbsOf(mod2) {
32610
32769
  return abs({ k: "obj", slots, open: true }, void 0, void 0, "path");
32611
32770
  }
32612
32771
  function bindImports(node, env2, modules) {
32613
- const mod2 = modules[node.source.value];
32772
+ const mod2 = Object.hasOwn(modules, node.source.value) ? modules[node.source.value] : void 0;
32614
32773
  if (!mod2) {
32615
32774
  for (const s of node.specifiers) {
32616
32775
  env2.vars.set(s.local.name, unknown);
@@ -32622,7 +32781,8 @@ function bindImports(node, env2, modules) {
32622
32781
  env2.vars.set(s.local.name, mod2.default ?? unknown);
32623
32782
  } else if (s.type === "ImportSpecifier") {
32624
32783
  const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
32625
- env2.vars.set(s.local.name, mod2.named[imported] ?? unknown);
32784
+ const named = Object.hasOwn(mod2.named, imported) ? mod2.named[imported] : void 0;
32785
+ env2.vars.set(s.local.name, named ?? unknown);
32626
32786
  } else if (s.type === "ImportNamespaceSpecifier") {
32627
32787
  env2.vars.set(s.local.name, namespaceAbsOf(mod2));
32628
32788
  }
@@ -32648,7 +32808,7 @@ function collectAbsExports(file, env2, modules) {
32648
32808
  for (const stmt of file.program.body) {
32649
32809
  if (stmt.type === "ExportNamedDeclaration") {
32650
32810
  if (stmt.source && modules) {
32651
- const mod2 = modules[stmt.source.value];
32811
+ const mod2 = Object.hasOwn(modules, stmt.source.value) ? modules[stmt.source.value] : void 0;
32652
32812
  if (mod2) {
32653
32813
  for (const spec of stmt.specifiers) {
32654
32814
  if (spec.type === "ExportNamespaceSpecifier") {
@@ -32660,7 +32820,7 @@ function collectAbsExports(file, env2, modules) {
32660
32820
  if (spec.type !== "ExportSpecifier") continue;
32661
32821
  const local = spec.local.type === "Identifier" ? spec.local.name : spec.local.value;
32662
32822
  const exported = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
32663
- const v2 = local === "default" ? mod2.default : mod2.named[local];
32823
+ const v2 = local === "default" ? mod2.default : Object.hasOwn(mod2.named, local) ? mod2.named[local] : void 0;
32664
32824
  const slot = v2 ?? unknown;
32665
32825
  if (exported === "default") defaultExport = slot;
32666
32826
  else {
@@ -32706,7 +32866,7 @@ function collectAbsExports(file, env2, modules) {
32706
32866
  }
32707
32867
  }
32708
32868
  } else if (stmt.type === "ExportAllDeclaration" && stmt.source && modules) {
32709
- const mod2 = modules[stmt.source.value];
32869
+ const mod2 = Object.hasOwn(modules, stmt.source.value) ? modules[stmt.source.value] : void 0;
32710
32870
  if (mod2?.named) {
32711
32871
  for (const [k, v2] of Object.entries(mod2.named)) {
32712
32872
  if (!explicitNames.has(k)) named[k] = v2;
@@ -33061,6 +33221,7 @@ ${js}`;
33061
33221
  const fn2 = new Function(...argNames, `${js}
33062
33222
  ${ret}`);
33063
33223
  setEvalBindingSink(bindings);
33224
+ beginClassEpoch();
33064
33225
  try {
33065
33226
  const result = fn2(...args);
33066
33227
  runBindings.set(result, bindings);
@@ -33068,6 +33229,18 @@ ${ret}`);
33068
33229
  } finally {
33069
33230
  setEvalBindingSink(null);
33070
33231
  drainPromiseMicros();
33232
+ noteDrainedClassCollisions();
33233
+ }
33234
+ }
33235
+ function noteDrainedClassCollisions() {
33236
+ for (const c of drainClassCollisions()) {
33237
+ noteEvalFallback(
33238
+ new NudoUnsupportedError(
33239
+ "class-collision",
33240
+ void 0,
33241
+ `class '${c.name}' registered again with a different shape (${c.previous} -> ${c.next}); name-keyed lookup may resolve to either definition`
33242
+ )
33243
+ );
33071
33244
  }
33072
33245
  }
33073
33246
  function setEvalFallbackCollector(collector2) {
@@ -33125,14 +33298,16 @@ function isAbsVal(v2) {
33125
33298
  }
33126
33299
  function callTranspiledExportFull(exports, name, args, opts) {
33127
33300
  enterEvalCallBudgetSession();
33301
+ beginClassEpoch();
33128
33302
  try {
33129
33303
  return callTranspiledExportFullInner(exports, name, args, opts);
33130
33304
  } finally {
33131
33305
  exitEvalCallBudgetSession();
33306
+ noteDrainedClassCollisions();
33132
33307
  }
33133
33308
  }
33134
33309
  function callTranspiledExportFullInner(exports, name, args, opts) {
33135
- const fn2 = exports[name];
33310
+ const fn2 = Object.hasOwn(exports, name) ? exports[name] : void 0;
33136
33311
  if (typeof fn2 === "function") {
33137
33312
  const callArgs = args.map(
33138
33313
  (a) => a && typeof a === "object" && "shape" in a ? $copy(a) : a
@@ -33236,6 +33411,7 @@ var init_run = __esm({
33236
33411
  init_transpile();
33237
33412
  init_intrinsics();
33238
33413
  init_unsupported();
33414
+ init_class_registry();
33239
33415
  init_export_names();
33240
33416
  init_may_throw();
33241
33417
  init_builtins();
@@ -33949,7 +34125,9 @@ function localNamedExports(source) {
33949
34125
  const identName = (n) => {
33950
34126
  if (!n) return void 0;
33951
34127
  if (n.type === "Identifier" && typeof n.name === "string") return n.name;
33952
- if (n.type === "Literal" && typeof n.value === "string") return n.value;
34128
+ if ((n.type === "StringLiteral" || n.type === "Literal") && typeof n.value === "string") {
34129
+ return n.value;
34130
+ }
33953
34131
  return void 0;
33954
34132
  };
33955
34133
  const localClasses = /* @__PURE__ */ new Map();
@@ -34097,6 +34275,25 @@ function localNamedExports(source) {
34097
34275
  }
34098
34276
  return out;
34099
34277
  }
34278
+ function exportedNameOfLocal(source, localName) {
34279
+ let ast;
34280
+ try {
34281
+ ast = parseSource(source, { errorRecovery: true });
34282
+ } catch {
34283
+ return void 0;
34284
+ }
34285
+ for (const stmt of ast.program.body) {
34286
+ if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
34287
+ for (const spec of stmt.specifiers) {
34288
+ if (spec.type !== "ExportSpecifier") continue;
34289
+ if (spec.local.type !== "Identifier" || spec.local.name !== localName) continue;
34290
+ const name = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
34291
+ if (name === void 0 || name === localName || name === "default") continue;
34292
+ return name;
34293
+ }
34294
+ }
34295
+ return void 0;
34296
+ }
34100
34297
  function generatedExportNames(sidecarSrc) {
34101
34298
  const out = /* @__PURE__ */ new Set();
34102
34299
  let ast;
@@ -34110,8 +34307,18 @@ function generatedExportNames(sidecarSrc) {
34110
34307
  const stmt = stmts[i];
34111
34308
  if (stmt.type !== "ExportNamedDeclaration") continue;
34112
34309
  if (stmt.source) continue;
34310
+ if (stmt.start == null) continue;
34113
34311
  const d = stmt.declaration;
34114
- if (!d || stmt.start == null) continue;
34312
+ if (!d) {
34313
+ if (regionHasGeneratedMarker(stmts, i, sidecarSrc)) {
34314
+ for (const spec of stmt.specifiers) {
34315
+ if (spec.type !== "ExportSpecifier") continue;
34316
+ const name = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
34317
+ if (name !== void 0) out.add(name);
34318
+ }
34319
+ }
34320
+ continue;
34321
+ }
34115
34322
  const names2 = [];
34116
34323
  if (d.type === "VariableDeclaration") {
34117
34324
  for (const decl of d.declarations) {
@@ -34182,6 +34389,8 @@ function sidecarMayBind(sidecarNames, fnName, source) {
34182
34389
  if (!sidecarNames) return true;
34183
34390
  if (sidecarNames.size === 0) return true;
34184
34391
  if (sidecarNames.has(fnName)) return true;
34392
+ const aliasExport = exportedNameOfLocal(source, fnName);
34393
+ if (aliasExport !== void 0 && sidecarNames.has(aliasExport)) return true;
34185
34394
  if (sidecarNames.has("default") && isDefaultExportLocal(source, fnName)) return true;
34186
34395
  if (fnName.includes(".")) {
34187
34396
  const [cls, method] = fnName.split(".", 2);
@@ -34251,20 +34460,26 @@ function loadSidecarBinding(source, fnName, opts) {
34251
34460
  }
34252
34461
  return { ok: false };
34253
34462
  }
34254
- let binding = exports[fnName];
34463
+ let binding = Object.hasOwn(exports, fnName) ? exports[fnName] : void 0;
34464
+ if (binding === void 0) {
34465
+ const aliasExport2 = exportedNameOfLocal(source, fnName);
34466
+ if (aliasExport2 !== void 0 && Object.hasOwn(exports, aliasExport2)) {
34467
+ binding = exports[aliasExport2];
34468
+ }
34469
+ }
34255
34470
  if (binding === void 0 && exports.default !== void 0 && isDefaultExportLocal(source, fnName)) {
34256
34471
  binding = exports.default;
34257
34472
  }
34258
34473
  if (binding === void 0 && fnName.includes(".")) {
34259
34474
  const [cls, method] = fnName.split(".", 2);
34260
- binding = exports[`${cls}_${method}`];
34475
+ binding = Object.hasOwn(exports, `${cls}_${method}`) ? exports[`${cls}_${method}`] : void 0;
34261
34476
  if (binding === void 0) {
34262
- const bag = exports[cls];
34477
+ const bag = Object.hasOwn(exports, cls) ? exports[cls] : void 0;
34263
34478
  if (bag && typeof bag === "object" && !isNudoConstraint(bag)) {
34264
- binding = bag[method];
34479
+ binding = Object.hasOwn(bag, method) ? bag[method] : void 0;
34265
34480
  }
34266
34481
  }
34267
- if (binding === void 0 && method !== void 0 && exports[method] !== void 0) {
34482
+ if (binding === void 0 && method !== void 0 && Object.hasOwn(exports, method)) {
34268
34483
  collectDiag2({
34269
34484
  code: "nudo:interface-load",
34270
34485
  message: `sidecar key '${method}' does not bind '${fnName}' \u2014 use '${cls}.${method}', '${cls}_${method}', or nested { ${cls}: { ${method}: \u2026 } }`,
@@ -34290,7 +34505,8 @@ function loadSidecarBinding(source, fnName, opts) {
34290
34505
  return { ok: false };
34291
34506
  }
34292
34507
  const generatedNames = generatedExportNames(sidecarSrc);
34293
- const isGenerated2 = generatedNames.has(fnName) || (fnName.includes(".") ? (() => {
34508
+ const aliasExport = exportedNameOfLocal(source, fnName);
34509
+ const isGenerated2 = generatedNames.has(fnName) || (aliasExport !== void 0 ? generatedNames.has(aliasExport) : false) || (fnName.includes(".") ? (() => {
34294
34510
  const [cls, method] = fnName.split(".", 2);
34295
34511
  return generatedNames.has(`${cls}_${method}`) || generatedNames.has(cls);
34296
34512
  })() : false);
@@ -35216,7 +35432,7 @@ var init_promote_scan = __esm({
35216
35432
  function termKey5(t, rename) {
35217
35433
  switch (t.op) {
35218
35434
  case "lit":
35219
- return `L:${typeof t.value}:${String(t.value)}`;
35435
+ return `L:${typeof t.value}:${litKeyString(t.value)}`;
35220
35436
  case "var":
35221
35437
  return `V:${rename?.get(t.id) ?? t.id}`;
35222
35438
  case "app":
@@ -35323,6 +35539,9 @@ function collectPredVars(p, acc) {
35323
35539
  case "typeof":
35324
35540
  collectTermVars(p.t, acc);
35325
35541
  return;
35542
+ case "assumeFinite":
35543
+ collectTermVars(p.t, acc);
35544
+ return;
35326
35545
  }
35327
35546
  }
35328
35547
  function collectAbsVars(a, acc, seen) {
@@ -38282,8 +38501,17 @@ var init_scan = __esm({
38282
38501
  });
38283
38502
 
38284
38503
  // ../core/src/algebra/projection.ts
38285
- function absToConstraint(a) {
38504
+ function absToConstraint(a, budget = new ProjectionBudget()) {
38286
38505
  if (!PROJECTABLE_CONF.has(a.conf)) return void 0;
38506
+ const stop2 = budget.enter(a);
38507
+ if (stop2) return void 0;
38508
+ try {
38509
+ return absToConstraintInner(a, budget);
38510
+ } finally {
38511
+ budget.exit();
38512
+ }
38513
+ }
38514
+ function absToConstraintInner(a, budget) {
38287
38515
  const s = a.shape;
38288
38516
  switch (s.k) {
38289
38517
  case "prim":
@@ -38301,9 +38529,9 @@ function absToConstraint(a) {
38301
38529
  case "any":
38302
38530
  return a.pred?.op === "or" ? projectOrLiterals(a) : void 0;
38303
38531
  case "obj":
38304
- return projectObj(s.slots, s.index, s.open);
38532
+ return projectObj(s.slots, s.index, s.open, budget);
38305
38533
  case "arr":
38306
- return projectArr(s.element);
38534
+ return projectArr(s.element, budget);
38307
38535
  case "sum":
38308
38536
  return projectSum(s.members);
38309
38537
  default:
@@ -38486,18 +38714,18 @@ function projectOrLiterals(a) {
38486
38714
  }
38487
38715
  return lits.length > 0 ? union(...lits) : void 0;
38488
38716
  }
38489
- function projectObj(slots, index2, open) {
38717
+ function projectObj(slots, index2, open, budget) {
38490
38718
  if (index2 || open) return void 0;
38491
38719
  const fields = {};
38492
38720
  for (const [k, slot] of Object.entries(slots)) {
38493
- const fc = absToConstraint(slot.value);
38721
+ const fc = absToConstraint(slot.value, budget);
38494
38722
  if (!fc) return void 0;
38495
38723
  fields[k] = { constraint: fc, ...slot.optional ? { optional: true } : {} };
38496
38724
  }
38497
38725
  return { __nudoConstraint: true, preds: [], fields };
38498
38726
  }
38499
- function projectArr(element) {
38500
- const ec = absToConstraint(element);
38727
+ function projectArr(element, budget) {
38728
+ const ec = absToConstraint(element, budget);
38501
38729
  if (ec) return array(ec);
38502
38730
  if (element.shape.k === "prim" && isBuilderPrim(element.shape.type) && (!element.pred || element.pred.op === "true") && ARR_FALLBACK_CONF.has(element.conf)) {
38503
38731
  return array(primConstraint(element.shape.type));
@@ -38607,6 +38835,7 @@ var init_projection = __esm({
38607
38835
  init_objects();
38608
38836
  init_term();
38609
38837
  init_term();
38838
+ init_projection_budget();
38610
38839
  PROJECTABLE_CONF = /* @__PURE__ */ new Set(["exact", "path"]);
38611
38840
  ARR_FALLBACK_CONF = /* @__PURE__ */ new Set([
38612
38841
  "exact",
@@ -39045,7 +39274,7 @@ function evalThrowsOf(source, fnName, args, opts = {}, phi2 = pTrue) {
39045
39274
  }
39046
39275
  };
39047
39276
  try {
39048
- if (fnName in exports) return call(fnName, args);
39277
+ if (Object.hasOwn(exports, fnName)) return call(fnName, args);
39049
39278
  if (fnName.includes(".")) {
39050
39279
  const [clsName, methodName] = fnName.split(".", 2);
39051
39280
  const clsAbs = exports[clsName ?? ""];
@@ -39091,12 +39320,9 @@ function widenForAssign(a) {
39091
39320
  if (s.k === "tuple") {
39092
39321
  const holes = new Set(s.holes ?? []);
39093
39322
  const els = s.elements.filter((_, i) => !holes.has(i));
39094
- let el = anyAbs;
39095
- if (els.length > 0) {
39096
- el = els.map(widenForAssign).reduce((x, y) => joinAbs(x, y));
39097
- } else if (s.rest) {
39098
- el = widenForAssign(s.rest);
39099
- }
39323
+ const parts = els.map(widenForAssign);
39324
+ if (s.rest) parts.push(widenForAssign(s.rest));
39325
+ const el = parts.length > 0 ? parts.reduce((x, y) => joinAbs(x, y)) : anyAbs;
39100
39326
  return abs({ k: "arr", element: el }, void 0, void 0, a.conf);
39101
39327
  }
39102
39328
  if (s.k === "obj") {
@@ -40641,6 +40867,7 @@ __export(src_exports, {
40641
40867
  interfaceSourceOf: () => interfaceSourceOf,
40642
40868
  interfaceTierOf: () => interfaceTierOf,
40643
40869
  isAbsApplyResult: () => isAbsApplyResult,
40870
+ isAbsVal: () => isAbsVal,
40644
40871
  isArrMutator: () => isArrMutator,
40645
40872
  isBigPrim: () => isBigPrim,
40646
40873
  isDefinitelyFalse: () => isDefinitelyFalse,
@@ -41046,6 +41273,7 @@ function paramName(param) {
41046
41273
  return void 0;
41047
41274
  }
41048
41275
  function unwrapDefaultExport(mod2) {
41276
+ if (mod2 == null) return void 0;
41049
41277
  if (typeof mod2 === "function") return mod2;
41050
41278
  return mod2.default;
41051
41279
  }
@@ -41111,9 +41339,73 @@ function emitDirectiveDiag(d) {
41111
41339
  function resetDirectiveDiagSeen() {
41112
41340
  directiveDiagSeen.clear();
41113
41341
  }
41342
+ function isSafeTypeExprNode(node) {
41343
+ switch (node.type) {
41344
+ case "NumericLiteral":
41345
+ case "StringLiteral":
41346
+ case "BooleanLiteral":
41347
+ case "NullLiteral":
41348
+ return true;
41349
+ case "Identifier":
41350
+ return node.name === "undefined";
41351
+ case "UnaryExpression":
41352
+ return node.operator === "-" && node.argument.type === "NumericLiteral";
41353
+ case "ArrayExpression":
41354
+ return node.elements.every(
41355
+ (el) => el !== null && el.type !== "SpreadElement" && isSafeTypeExprNode(el)
41356
+ );
41357
+ case "ObjectExpression":
41358
+ return node.properties.every((prop) => {
41359
+ if (prop.type !== "ObjectProperty") return false;
41360
+ if (prop.computed) return false;
41361
+ const key = prop.key;
41362
+ if (key.type !== "Identifier" && key.type !== "StringLiteral") return false;
41363
+ return isSafeTypeExprNode(prop.value);
41364
+ });
41365
+ case "CallExpression": {
41366
+ const argsOk = node.arguments.every(
41367
+ (arg) => arg.type !== "SpreadElement" && arg.type !== "ArgumentPlaceholder" && isSafeTypeExprNode(arg)
41368
+ );
41369
+ if (!argsOk) return false;
41370
+ return isSafeTypeExprCallee(node.callee);
41371
+ }
41372
+ default:
41373
+ return false;
41374
+ }
41375
+ }
41376
+ function isSafeTypeExprCallee(node) {
41377
+ if (node.type === "Identifier") {
41378
+ return CONSTRAINT_BUILDER_NAMES.includes(node.name);
41379
+ }
41380
+ if (node.type === "MemberExpression") {
41381
+ if (node.computed) return false;
41382
+ if (node.property.type !== "Identifier") return false;
41383
+ if (!TYPE_EXPR_CHAIN_METHODS.has(node.property.name)) return false;
41384
+ return isSafeTypeExprNode(node.object);
41385
+ }
41386
+ return false;
41387
+ }
41388
+ function isSafeTypeExprSource(s) {
41389
+ try {
41390
+ const ast = parse2(`(${s});`);
41391
+ if (ast.program.body.length !== 1) return false;
41392
+ const stmt = ast.program.body[0];
41393
+ if (stmt.type !== "ExpressionStatement") return false;
41394
+ return isSafeTypeExprNode(stmt.expression);
41395
+ } catch {
41396
+ return false;
41397
+ }
41398
+ }
41114
41399
  function tryParseConstraint(expr) {
41115
41400
  const s = expr.trim();
41116
41401
  if (!CONSTRAINT_EXPR_RE.test(s)) return void 0;
41402
+ if (!isSafeTypeExprSource(s)) {
41403
+ emitDirectiveDiag({
41404
+ code: "nudo:directive-syntax",
41405
+ message: `Unsafe constraint expression rejected: ${s.slice(0, 80)} (only builder calls, chain methods, and literals are allowed)`
41406
+ });
41407
+ return void 0;
41408
+ }
41117
41409
  try {
41118
41410
  const src = `export const __nudo_case_arg = (${s});`;
41119
41411
  const exports = execNudoModule(src);
@@ -41149,9 +41441,10 @@ function constraintSelfEqLit(c, selfIds) {
41149
41441
  if (isSelf(b) && a.op === "lit") return { found: true, value: a.value };
41150
41442
  return { found: false };
41151
41443
  }
41152
- function constraintToCaseArgAbs(c) {
41444
+ function constraintToCaseArgAbs(c, depth) {
41445
+ if (depth > MAX_CASE_ARG_DEPTH) return depthCapDiag(constraintPreview(c));
41153
41446
  if (c.members && c.members.length > 0) {
41154
- const parts = c.members.map((m) => constraintToCaseArgAbs(m));
41447
+ const parts = c.members.map((m) => constraintToCaseArgAbs(m, depth + 1));
41155
41448
  if (parts.length === 1) return parts[0];
41156
41449
  return { shape: { k: "sum", members: parts }, conf: "path" };
41157
41450
  }
@@ -41166,12 +41459,12 @@ function constraintToCaseArgAbs(c) {
41166
41459
  return absUndefLit();
41167
41460
  }
41168
41461
  if (c.element) {
41169
- return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element) });
41462
+ return absExact({ k: "arr", element: constraintToCaseArgAbs(c.element, depth + 1) });
41170
41463
  }
41171
41464
  if (c.fields) {
41172
41465
  const slots = {};
41173
41466
  for (const [key, field] of Object.entries(c.fields)) {
41174
- slots[key] = { value: constraintToCaseArgAbs(field.constraint) };
41467
+ slots[key] = { value: constraintToCaseArgAbs(field.constraint, depth + 1) };
41175
41468
  }
41176
41469
  return absExact({ k: "obj", slots });
41177
41470
  }
@@ -41184,18 +41477,33 @@ function constraintToCaseArgAbs(c) {
41184
41477
  }
41185
41478
  return constraintToEntryAbs(c, "__arg");
41186
41479
  }
41187
- function parseCaseArgExpr(expr) {
41480
+ function depthCapDiag(expr) {
41481
+ emitDirectiveDiag({
41482
+ code: "nudo:directive-syntax",
41483
+ message: `Type expression nesting exceeds depth ${MAX_CASE_ARG_DEPTH}: ${expr.trim().slice(0, 40)} \u2026 (deeper structure collapses to unknown)`
41484
+ });
41485
+ return absUnknown2();
41486
+ }
41487
+ function constraintPreview(c) {
41488
+ if (c.element) return "array(\u2026)";
41489
+ if (c.members) return "union(\u2026)";
41490
+ if (c.fields) return "shape({ \u2026 })";
41491
+ if (c.fn) return "fn(\u2026)";
41492
+ return c.prim ? `${c.prim}()` : "constraint";
41493
+ }
41494
+ function parseCaseArgExpr(expr, depth = 0) {
41495
+ if (depth > MAX_CASE_ARG_DEPTH) return depthCapDiag(expr);
41188
41496
  const constraint = tryParseConstraint(expr);
41189
41497
  if (constraint) {
41190
41498
  try {
41191
- return constraintToCaseArgAbs(constraint);
41499
+ return constraintToCaseArgAbs(constraint, depth);
41192
41500
  } catch {
41193
41501
  return absUnknown2();
41194
41502
  }
41195
41503
  }
41196
- return parseLiteralOrStructure(expr);
41504
+ return parseLiteralOrStructure(expr, depth);
41197
41505
  }
41198
- function parseLiteralOrStructure(expr) {
41506
+ function parseLiteralOrStructure(expr, depth) {
41199
41507
  const s = expr.trim();
41200
41508
  if (s === "true") return absLit(true);
41201
41509
  if (s === "false") return absLit(false);
@@ -41214,17 +41522,20 @@ function parseLiteralOrStructure(expr) {
41214
41522
  return absLit(s.slice(1, -1));
41215
41523
  }
41216
41524
  if (s.startsWith("{") && s.endsWith("}")) {
41217
- return parseObjectLiteral(s.slice(1, -1).trim());
41525
+ return parseObjectLiteral(s.slice(1, -1).trim(), depth);
41218
41526
  }
41219
41527
  if (s.startsWith("[") && s.endsWith("]")) {
41220
41528
  const content = s.slice(1, -1).trim();
41221
41529
  if (!content) return absExact({ k: "tuple", elements: [] });
41222
41530
  const elements = splitTopLevelArgs(content);
41223
- return absExact({ k: "tuple", elements: elements.map((e) => parseCaseArgExpr(e)) });
41531
+ return absExact({
41532
+ k: "tuple",
41533
+ elements: elements.map((e) => parseCaseArgExpr(e, depth + 1))
41534
+ });
41224
41535
  }
41225
41536
  return absUnknown2();
41226
41537
  }
41227
- function parseObjectLiteral(content) {
41538
+ function parseObjectLiteral(content, depth) {
41228
41539
  if (!content) return absExact({ k: "obj", slots: {} });
41229
41540
  const entries = splitTopLevelArgs(content);
41230
41541
  const slots = {};
@@ -41233,7 +41544,7 @@ function parseObjectLiteral(content) {
41233
41544
  if (colonIdx === -1) continue;
41234
41545
  const key = entry.slice(0, colonIdx).trim().replace(/^["']|["']$/g, "");
41235
41546
  const val = entry.slice(colonIdx + 1).trim();
41236
- slots[key] = { value: parseCaseArgExpr(val) };
41547
+ slots[key] = { value: parseCaseArgExpr(val, depth + 1) };
41237
41548
  }
41238
41549
  return absExact({ k: "obj", slots });
41239
41550
  }
@@ -41399,6 +41710,13 @@ function parseSkipReturnsExpr(rest) {
41399
41710
  if (inner !== null && s.slice(inner.length + 2).trim() === "") {
41400
41711
  return parseAbsExprDiag(inner, "@nudo:skip (...)");
41401
41712
  }
41713
+ if (inner === null) {
41714
+ emitDirectiveDiag({
41715
+ code: "nudo:directive-syntax",
41716
+ message: `@nudo:skip type expression has unclosed parenthesis (expected @nudo:skip (<typeExpr>)) \u2014 got: ${s.slice(0, 60)}`
41717
+ });
41718
+ return void 0;
41719
+ }
41402
41720
  }
41403
41721
  return void 0;
41404
41722
  }
@@ -41492,7 +41810,8 @@ function parseSinonExpr(expr) {
41492
41810
  return null;
41493
41811
  }
41494
41812
  function parseNudoMockExpr(expr) {
41495
- const s = expr.trim();
41813
+ let s = expr.trim();
41814
+ while (s.startsWith("sinon.")) s = s.slice("sinon.".length);
41496
41815
  const stubReturnsOnFirstMatch = s.match(/^stub\(\)\.returns\((.+)\)\.onFirstCall\(\)$/);
41497
41816
  if (stubReturnsOnFirstMatch) {
41498
41817
  const helper = stub.returns(parseAbsExpr(stubReturnsOnFirstMatch[1].trim()));
@@ -41563,9 +41882,6 @@ function parseNudoMockExpr(expr) {
41563
41882
  if (s === "mock()") {
41564
41883
  return mock();
41565
41884
  }
41566
- if (s.startsWith("sinon.")) {
41567
- return parseNudoMockExpr(s.slice("sinon.".length));
41568
- }
41569
41885
  return null;
41570
41886
  }
41571
41887
  function parseDirectivesFromCommentTexts(comments) {
@@ -41675,8 +41991,17 @@ function scanMalformedCaseNames(text, caseArgSpans) {
41675
41991
  while ((m = CASE_TAG_REGEX.exec(text)) !== null) {
41676
41992
  if (inCaseArgs(m.index)) continue;
41677
41993
  const rest = m[1].trim();
41678
- if (/^"[^"]+"\s*\(/.test(rest)) continue;
41679
- if (/^"[^"]+"\s*\(/.test(rest)) continue;
41994
+ const legal = rest.match(/^("[^"]+")[ \t]*\(/);
41995
+ if (legal) {
41996
+ const parenIdx = m.index + m[0].length - m[1].length + legal[0].length - 1;
41997
+ if (extractBalancedParens(text, parenIdx) === null) {
41998
+ emitDirectiveDiag({
41999
+ code: "nudo:directive-syntax",
42000
+ message: `Malformed @nudo:case: unclosed argument list (missing closing ")") \u2014 got: @nudo:case ${rest.slice(0, 60)}`
42001
+ });
42002
+ }
42003
+ continue;
42004
+ }
41680
42005
  let reason;
41681
42006
  if (rest.startsWith("'")) {
41682
42007
  reason = `single-quoted name (use double quotes: @nudo:case "name" (\u2026))`;
@@ -41716,6 +42041,14 @@ function scanMalformedMockNames(text, caseArgSpans) {
41716
42041
  code: "nudo:directive-syntax",
41717
42042
  message: `Malformed @nudo:mock name '${name}': not a valid identifier (use letters, digits, _, $; Unicode letters allowed)`
41718
42043
  });
42044
+ continue;
42045
+ }
42046
+ const afterName = rest.slice(name.length);
42047
+ if (/^[ \t]+from[ \t]+"[^"\n]*$/.test(afterName)) {
42048
+ emitDirectiveDiag({
42049
+ code: "nudo:directive-syntax",
42050
+ message: `Malformed @nudo:mock from: unclosed quote in path (expected @nudo:mock ${name} from "path") \u2014 got: @nudo:mock ${rest.slice(0, 60)}`
42051
+ });
41719
42052
  }
41720
42053
  }
41721
42054
  }
@@ -41742,6 +42075,28 @@ function extractDirectivesQuiet(ast) {
41742
42075
  takeDirectiveDiagsSince(since);
41743
42076
  return out;
41744
42077
  }
42078
+ function stripInlineCommentPrefix(line, kind) {
42079
+ const s = kind === "block" ? line.replace(/^\s*\*\s?/, "") : line.replace(/^\//, "");
42080
+ return s.trim();
42081
+ }
42082
+ function findTrailingCommentStart(expr) {
42083
+ let inString = null;
42084
+ for (let i = 0; i < expr.length; i++) {
42085
+ const ch = expr[i];
42086
+ if (inString) {
42087
+ if (ch === inString) inString = null;
42088
+ continue;
42089
+ }
42090
+ if (ch === '"' || ch === "'") {
42091
+ inString = ch;
42092
+ continue;
42093
+ }
42094
+ if ((ch === " " || ch === " ") && /^[ \t]+(?:\/\/|\/\*)/.test(expr.slice(i))) {
42095
+ return i;
42096
+ }
42097
+ }
42098
+ return -1;
42099
+ }
41745
42100
  function extractInlineDirectives(node) {
41746
42101
  resetDirectiveDiagSeen();
41747
42102
  const comments = node.leadingComments;
@@ -41749,39 +42104,56 @@ function extractInlineDirectives(node) {
41749
42104
  const results = [];
41750
42105
  for (const comment of comments) {
41751
42106
  if (comment.type !== "CommentLine" && comment.type !== "CommentBlock") continue;
41752
- const text = comment.value;
41753
- const asMatch = text.match(AS_REGEX);
41754
- if (asMatch) {
41755
- const rawExpr = asMatch[1].trim();
41756
- const trailing = rawExpr.match(AS_TRAILING_COMMENT_RE);
41757
- if (trailing) {
41758
- const typePart = rawExpr.slice(0, trailing.index).trim();
42107
+ const kind = comment.type === "CommentLine" ? "line" : "block";
42108
+ for (const rawLine of comment.value.split("\n")) {
42109
+ const line = stripInlineCommentPrefix(rawLine, kind);
42110
+ const asMatch = line.match(AS_LINE_REGEX);
42111
+ if (asMatch) {
42112
+ const rawExpr = asMatch[1].trim();
42113
+ const trailingIdx = findTrailingCommentStart(rawExpr);
42114
+ if (trailingIdx !== -1) {
42115
+ const typePart = rawExpr.slice(0, trailingIdx).trim();
42116
+ emitDirectiveDiag({
42117
+ code: "nudo:directive-syntax",
42118
+ message: `Trailing comment in @nudo:as type expression: '${rawExpr}' (remove the comment; the type must be a clean expression)`
42119
+ });
42120
+ results.push({ kind: "as", typeAbs: typePart ? parseAbsExprDiag(typePart, "@nudo:as") : absUnknown2() });
42121
+ continue;
42122
+ }
42123
+ results.push({ kind: "as", typeAbs: parseAbsExprDiag(rawExpr, "@nudo:as") });
42124
+ continue;
42125
+ }
42126
+ if (AS_BARE_RE.test(line)) {
41759
42127
  emitDirectiveDiag({
41760
42128
  code: "nudo:directive-syntax",
41761
- message: `Trailing comment in @nudo:as type expression: '${rawExpr}' (remove the comment; the type must be a clean expression)`
42129
+ message: `@nudo:as requires a type expression (e.g. @nudo:as number())`
41762
42130
  });
41763
- results.push({ kind: "as", typeAbs: typePart ? parseAbsExprDiag(typePart, "@nudo:as") : absUnknown2() });
41764
42131
  continue;
41765
42132
  }
41766
- results.push({ kind: "as", typeAbs: parseAbsExprDiag(rawExpr, "@nudo:as") });
41767
- continue;
41768
- }
41769
- const replaceMatch = text.match(REPLACE_REGEX);
41770
- if (replaceMatch) {
41771
- const raw = replaceMatch[1].trim();
41772
- const sepIdx = findReplaceSeparator(raw);
41773
- if (sepIdx !== -1) {
41774
- const targetSource = raw.slice(0, sepIdx).trim();
41775
- const typeExprStr = raw.slice(sepIdx + 1).trim();
41776
- results.push({
41777
- kind: "replace",
41778
- targetSource,
41779
- typeAbs: parseAbsExprDiag(typeExprStr, "@nudo:replace")
41780
- });
41781
- } else {
42133
+ const replaceMatch = line.match(REPLACE_LINE_REGEX);
42134
+ if (replaceMatch) {
42135
+ const raw = replaceMatch[1].trim();
42136
+ const sepIdx = findReplaceSeparator(raw);
42137
+ if (sepIdx !== -1) {
42138
+ const targetSource = raw.slice(0, sepIdx).trim();
42139
+ const typeExprStr = raw.slice(sepIdx + 1).trim();
42140
+ results.push({
42141
+ kind: "replace",
42142
+ targetSource,
42143
+ typeAbs: parseAbsExprDiag(typeExprStr, "@nudo:replace")
42144
+ });
42145
+ } else {
42146
+ emitDirectiveDiag({
42147
+ code: "nudo:directive-syntax",
42148
+ message: `Malformed @nudo:replace: cannot separate target from type (expected @nudo:replace <target> <typeExpr>) \u2014 got: ${raw.slice(0, 60)}`
42149
+ });
42150
+ }
42151
+ continue;
42152
+ }
42153
+ if (REPLACE_BARE_RE.test(line)) {
41782
42154
  emitDirectiveDiag({
41783
42155
  code: "nudo:directive-syntax",
41784
- message: `Malformed @nudo:replace: cannot separate target from type (expected @nudo:replace <target> <typeExpr>) \u2014 got: ${raw.slice(0, 60)}`
42156
+ message: `@nudo:replace requires a target and type expression (expected @nudo:replace <target> <typeExpr>)`
41785
42157
  });
41786
42158
  }
41787
42159
  }
@@ -41846,7 +42218,7 @@ function extractFileDirectives(ast) {
41846
42218
  }
41847
42219
  return results;
41848
42220
  }
41849
- var directiveDiagCollector, directiveDiagSeq, directiveDiags, MAX_DIRECTIVE_DIAGS, directiveDiagSeen, CASE_TAG_REGEX, JS_IDENT_RE, MOCK_INLINE_REGEX, MOCK_FROM_REGEX, MOCK_TAG_REGEX, PURE_REGEX, SKIP_REGEX, SAMPLE_REGEX, SAMPLE_NUM_RE, AS_REGEX, REPLACE_REGEX, AS_TRAILING_COMMENT_RE, ENV_REGEX, MOCK_MODULE_REGEX;
42221
+ var directiveDiagCollector, directiveDiagSeq, directiveDiags, MAX_DIRECTIVE_DIAGS, directiveDiagSeen, CASE_TAG_REGEX, JS_IDENT_RE, MOCK_INLINE_REGEX, MOCK_FROM_REGEX, MOCK_TAG_REGEX, PURE_REGEX, SKIP_REGEX, SAMPLE_REGEX, SAMPLE_NUM_RE, TYPE_EXPR_CHAIN_METHODS, MAX_CASE_ARG_DEPTH, AS_LINE_REGEX, REPLACE_LINE_REGEX, AS_BARE_RE, REPLACE_BARE_RE, ENV_REGEX, MOCK_MODULE_REGEX;
41850
42222
  var init_directives = __esm({
41851
42223
  "../parser/src/directives.ts"() {
41852
42224
  "use strict";
@@ -41857,18 +42229,32 @@ var init_directives = __esm({
41857
42229
  directiveDiags = [];
41858
42230
  MAX_DIRECTIVE_DIAGS = 1024;
41859
42231
  directiveDiagSeen = /* @__PURE__ */ new Set();
41860
- CASE_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:case\s+([^\n]+)/g;
42232
+ CASE_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:case\b[ \t]*([^\n]*)/g;
41861
42233
  JS_IDENT_RE = /^[\p{L}$_][\p{L}\p{N}$_]*$/u;
41862
- MOCK_INLINE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:mock\s+([^\s=]+)\s*=\s*(.+)/g;
41863
- MOCK_FROM_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:mock\s+([^\s]+)\s+from\s+"([^"]+)"/g;
41864
- MOCK_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:mock\s+([^\n]+)/g;
41865
- PURE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:pure\b/g;
41866
- SKIP_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:skip\b(?:[ \t]+(\S[^\n]*))?/g;
41867
- SAMPLE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*)?@nudo:sample\b(?:[ \t]+(\S+))?/g;
42234
+ MOCK_INLINE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]+([^\s=]+)[ \t]*=[ \t]*(.+)/g;
42235
+ MOCK_FROM_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]+([^\s]+)[ \t]+from[ \t]+"([^"\n]+)"/g;
42236
+ MOCK_TAG_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:mock\b[ \t]*([^\n]*)/g;
42237
+ PURE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:pure\b/g;
42238
+ SKIP_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:skip\b(?:[ \t]+(\S[^\n]*))?/g;
42239
+ SAMPLE_REGEX = /(?:^|\n)[ \t]*(?:\*[ \t]*|\/[ \t]*)?@nudo:sample\b(?:[ \t]+(\S+))?/g;
41868
42240
  SAMPLE_NUM_RE = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
41869
- AS_REGEX = /^\s*@nudo:as\s+(.+)/;
41870
- REPLACE_REGEX = /^\s*@nudo:replace\s+(.+)/;
41871
- AS_TRAILING_COMMENT_RE = /\s+(?:\/\/|\/\*)/;
42241
+ TYPE_EXPR_CHAIN_METHODS = /* @__PURE__ */ new Set([
42242
+ "gt",
42243
+ "ge",
42244
+ "lt",
42245
+ "le",
42246
+ "int",
42247
+ "min",
42248
+ "max",
42249
+ "length",
42250
+ "shift",
42251
+ "optional"
42252
+ ]);
42253
+ MAX_CASE_ARG_DEPTH = 32;
42254
+ AS_LINE_REGEX = /^@nudo:as[ \t]+(.+)$/;
42255
+ REPLACE_LINE_REGEX = /^@nudo:replace[ \t]+(.+)$/;
42256
+ AS_BARE_RE = /^@nudo:as[ \t]*$/;
42257
+ REPLACE_BARE_RE = /^@nudo:replace[ \t]*$/;
41872
42258
  ENV_REGEX = /^\/?\s*@nudo:env\s+(.+)/;
41873
42259
  MOCK_MODULE_REGEX = /^\/?\s*@nudo:mock-module\s+(.+)/;
41874
42260
  }
@@ -42112,6 +42498,27 @@ var init_inlay = __esm({
42112
42498
  function isJsIdent(name) {
42113
42499
  return JS_IDENT.test(name);
42114
42500
  }
42501
+ function isJsBindingIdent(name) {
42502
+ return JS_BINDING_IDENT.test(name) && !JS_BINDING_RESERVED.has(name);
42503
+ }
42504
+ function toJsBindingIdent(name, used) {
42505
+ let base = name;
42506
+ if (!JS_BINDING_IDENT.test(base)) {
42507
+ base = base.replace(/[^\p{ID_Continue}$\u200C\u200D]/gu, "_");
42508
+ if (base.length === 0) base = "_";
42509
+ else if (!/^[$_\p{ID_Start}]/u.test(base)) base = `_${base}`;
42510
+ }
42511
+ if (JS_BINDING_RESERVED.has(base)) base = `_${base}`;
42512
+ if (!used) return base;
42513
+ let n = base;
42514
+ let i = 2;
42515
+ while (used.has(n)) {
42516
+ n = `${base}_${i}`;
42517
+ i++;
42518
+ }
42519
+ used.add(n);
42520
+ return n;
42521
+ }
42115
42522
  function formatObjectKey(k) {
42116
42523
  if (JS_IDENT.test(k)) return k;
42117
42524
  if (JS_CANONICAL_INT.test(k)) return k;
@@ -42126,27 +42533,90 @@ function escapeTemplateTypeFixed(text) {
42126
42533
  function sanitizeCommentText(text) {
42127
42534
  return text.replace(/\*\//g, "*\\/").replace(/[\r\n\u2028\u2029\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, " ");
42128
42535
  }
42129
- var JS_IDENT, JS_CANONICAL_INT;
42536
+ var JS_IDENT, JS_BINDING_IDENT, JS_BINDING_RESERVED, JS_CANONICAL_INT;
42130
42537
  var init_codegen_escape = __esm({
42131
42538
  "../core/src/algebra/codegen-escape.ts"() {
42132
42539
  "use strict";
42133
42540
  JS_IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
42541
+ JS_BINDING_IDENT = /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u;
42542
+ JS_BINDING_RESERVED = /* @__PURE__ */ new Set([
42543
+ // ECMA-262 keywords
42544
+ "break",
42545
+ "case",
42546
+ "catch",
42547
+ "class",
42548
+ "const",
42549
+ "continue",
42550
+ "debugger",
42551
+ "default",
42552
+ "delete",
42553
+ "do",
42554
+ "else",
42555
+ "enum",
42556
+ "export",
42557
+ "extends",
42558
+ "false",
42559
+ "finally",
42560
+ "for",
42561
+ "function",
42562
+ "if",
42563
+ "import",
42564
+ "in",
42565
+ "instanceof",
42566
+ "new",
42567
+ "null",
42568
+ "return",
42569
+ "super",
42570
+ "switch",
42571
+ "this",
42572
+ "throw",
42573
+ "true",
42574
+ "try",
42575
+ "typeof",
42576
+ "var",
42577
+ "void",
42578
+ "while",
42579
+ "with",
42580
+ "yield",
42581
+ // strict mode / module-code reserved
42582
+ "let",
42583
+ "static",
42584
+ "await",
42585
+ "implements",
42586
+ "interface",
42587
+ "package",
42588
+ "private",
42589
+ "protected",
42590
+ "public",
42591
+ // never legal bindings in strict / module code
42592
+ "eval",
42593
+ "arguments"
42594
+ ]);
42134
42595
  JS_CANONICAL_INT = /^(?:0|[1-9]\d*)$/;
42135
42596
  }
42136
42597
  });
42137
42598
 
42138
42599
  // ../core/src/algebra/denote.ts
42139
42600
  function denoteGuard(a, v2) {
42140
- if (a.term?.op === "lit" && (!a.pred || a.pred.op === "true")) {
42141
- return eqGuard(v2, a.term.value);
42601
+ return denoteGuardB(a, v2, new ProjectionBudget());
42602
+ }
42603
+ function denoteGuardB(a, v2, budget) {
42604
+ const stop2 = budget.enter(a);
42605
+ if (stop2) return `/* nudo:truncated:${stop2} */ true`;
42606
+ try {
42607
+ if (a.term?.op === "lit" && (!a.pred || a.pred.op === "true")) {
42608
+ return eqGuard(v2, a.term.value);
42609
+ }
42610
+ const shape2 = denoteShape(a.shape, v2, budget);
42611
+ const pred = denotePred(a, v2);
42612
+ if (shape2 === "true") return pred;
42613
+ if (pred === "true") return shape2;
42614
+ return `(${shape2}) && (${pred})`;
42615
+ } finally {
42616
+ budget.exit();
42142
42617
  }
42143
- const shape2 = denoteShape(a.shape, v2);
42144
- const pred = denotePred(a, v2);
42145
- if (shape2 === "true") return pred;
42146
- if (pred === "true") return shape2;
42147
- return `(${shape2}) && (${pred})`;
42148
42618
  }
42149
- function denoteShape(s, v2) {
42619
+ function denoteShape(s, v2, budget) {
42150
42620
  switch (s.k) {
42151
42621
  case "never":
42152
42622
  return "false";
@@ -42172,7 +42642,7 @@ function denoteShape(s, v2) {
42172
42642
  const checks = [`typeof ${v2} === "object"`, `${v2} !== null`];
42173
42643
  for (const [key, slot] of Object.entries(s.slots)) {
42174
42644
  const access = safeMemberAccess(v2, key);
42175
- const inner = denoteGuard(slot.value, access);
42645
+ const inner = denoteGuardB(slot.value, access, budget);
42176
42646
  if (slot.optional) {
42177
42647
  if (inner !== "true") checks.push(`(${access} === undefined || ${inner})`);
42178
42648
  } else {
@@ -42182,7 +42652,7 @@ function denoteShape(s, v2) {
42182
42652
  return checks.join(" && ");
42183
42653
  }
42184
42654
  case "arr":
42185
- return `Array.isArray(${v2}) && ${v2}.every((item) => ${denoteGuard(s.element, "item")})`;
42655
+ return `Array.isArray(${v2}) && ${v2}.every((item) => ${denoteGuardB(s.element, "item", budget)})`;
42186
42656
  case "tuple": {
42187
42657
  const checks = [`Array.isArray(${v2})`];
42188
42658
  const minLen = s.elements.length;
@@ -42193,14 +42663,14 @@ function denoteShape(s, v2) {
42193
42663
  checks.push(`!(${i} in ${v2})`);
42194
42664
  return;
42195
42665
  }
42196
- const inner = denoteGuard(el, `${v2}[${i}]`);
42666
+ const inner = denoteGuardB(el, `${v2}[${i}]`, budget);
42197
42667
  if (inner !== "true") checks.push(inner);
42198
42668
  });
42199
42669
  for (const i of holes) {
42200
42670
  if (i >= minLen) checks.push(`!(${i} in ${v2})`);
42201
42671
  }
42202
42672
  if (s.rest) {
42203
- const rest = denoteGuard(s.rest, "item");
42673
+ const rest = denoteGuardB(s.rest, "item", budget);
42204
42674
  if (rest !== "true") {
42205
42675
  checks.push(`${v2}.slice(${minLen}).every((item) => ${rest})`);
42206
42676
  }
@@ -42213,10 +42683,10 @@ function denoteShape(s, v2) {
42213
42683
  if (s.eff === "promise") return `${v2} instanceof Promise`;
42214
42684
  return "true";
42215
42685
  case "brand":
42216
- return denoteShape(s.shape.shape, v2);
42686
+ return denoteShape(s.shape.shape, v2, budget);
42217
42687
  case "sum": {
42218
42688
  if (s.members.length === 0) return "false";
42219
- const parts = s.members.map((m) => denoteGuard(m, v2));
42689
+ const parts = s.members.map((m) => denoteGuardB(m, v2, budget));
42220
42690
  return `(${parts.join(" || ")})`;
42221
42691
  }
42222
42692
  }
@@ -42310,6 +42780,7 @@ var init_denote = __esm({
42310
42780
  "../core/src/algebra/denote.ts"() {
42311
42781
  "use strict";
42312
42782
  init_codegen_escape();
42783
+ init_projection_budget();
42313
42784
  }
42314
42785
  });
42315
42786
 
@@ -42327,8 +42798,10 @@ __export(internal_exports, {
42327
42798
  MAX_PROMISE_MICROS: () => MAX_PROMISE_MICROS,
42328
42799
  MAX_TOTAL_CALLS: () => MAX_TOTAL_CALLS,
42329
42800
  OBJECT_PROTO_NAMES: () => OBJECT_PROTO_NAMES,
42801
+ PROJECTION_MAX_DEPTH: () => PROJECTION_MAX_DEPTH,
42330
42802
  PROMISE_MICRO_ERROR_LABEL: () => PROMISE_MICRO_ERROR_LABEL,
42331
42803
  PROMISE_MICRO_OVERFLOW_LABEL: () => PROMISE_MICRO_OVERFLOW_LABEL,
42804
+ ProjectionBudget: () => ProjectionBudget,
42332
42805
  abortDerivationSession: () => abortDerivationSession,
42333
42806
  absTemplateViews: () => absTemplateViews,
42334
42807
  allFixedTextOfViews: () => allFixedTextOfViews,
@@ -42363,6 +42836,7 @@ __export(internal_exports, {
42363
42836
  exceedsBudget: () => exceedsBudget,
42364
42837
  exitCall: () => exitCall,
42365
42838
  exitEvalCallBudgetSession: () => exitEvalCallBudgetSession,
42839
+ exportedNameOfLocal: () => exportedNameOfLocal,
42366
42840
  extractAllLoadSpecs: () => extractAllLoadSpecs,
42367
42841
  filterDeclaredThrows: () => filterDeclaredThrows,
42368
42842
  filterGateThrows: () => filterGateThrows,
@@ -42391,6 +42865,7 @@ __export(internal_exports, {
42391
42865
  instanceOf: () => instanceOf,
42392
42866
  instantiateClass: () => instantiateClass,
42393
42867
  isEvalMissingSlotEnabled: () => isEvalMissingSlotEnabled,
42868
+ isJsBindingIdent: () => isJsBindingIdent,
42394
42869
  isJsIdent: () => isJsIdent,
42395
42870
  isNullishAbs: () => isNullishAbs,
42396
42871
  isTemplateLike: () => isTemplateLike,
@@ -42467,6 +42942,7 @@ __export(internal_exports, {
42467
42942
  termNodes: () => termNodes,
42468
42943
  throwAbsToKinds: () => throwAbsToKinds,
42469
42944
  throwsKindCovered: () => throwsKindCovered,
42945
+ toJsBindingIdent: () => toJsBindingIdent,
42470
42946
  truncatedAbs: () => truncatedAbs,
42471
42947
  viewTemplateParts: () => viewTemplateParts,
42472
42948
  withEvalBudgetOverride: () => withEvalBudgetOverride,
@@ -42478,6 +42954,7 @@ var init_internal = __esm({
42478
42954
  "use strict";
42479
42955
  init_code_text();
42480
42956
  init_leak();
42957
+ init_projection_budget();
42481
42958
  init_call_budget();
42482
42959
  init_promise();
42483
42960
  init_calls();
@@ -42486,6 +42963,7 @@ var init_internal = __esm({
42486
42963
  init_stable_source_key();
42487
42964
  init_fn_fp();
42488
42965
  init_load_deps_fp();
42966
+ init_interface();
42489
42967
  init_derivation();
42490
42968
  init_inlay();
42491
42969
  init_template();
@@ -52779,20 +53257,20 @@ function envLimits(env2) {
52779
53257
  };
52780
53258
  }
52781
53259
  function pick2(key, env2, fallback) {
52782
- const e = env2[key];
52783
- if (typeof e === "number" && Number.isFinite(e)) return e;
52784
53260
  const x = explicit[key];
52785
53261
  if (typeof x === "number" && Number.isFinite(x)) return clampEntries(x, fallback);
53262
+ const e = env2[key];
53263
+ if (typeof e === "number" && Number.isFinite(e)) return e;
52786
53264
  const p = fromProject[key];
52787
53265
  if (typeof p === "number" && Number.isFinite(p)) return clampEntries(p, fallback);
52788
53266
  return fallback;
52789
53267
  }
52790
53268
  function getSessionCacheLimits(env2 = process.env) {
52791
- if (envCache === null) envCache = envLimits(env2);
53269
+ const fromEnv = envLimits(env2);
52792
53270
  return {
52793
- maxFiles: pick2("maxFiles", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxFiles),
52794
- maxFns: pick2("maxFns", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxFns),
52795
- maxEvalRuns: pick2("maxEvalRuns", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxEvalRuns)
53271
+ maxFiles: pick2("maxFiles", fromEnv, DEFAULT_SESSION_CACHE_LIMITS.maxFiles),
53272
+ maxFns: pick2("maxFns", fromEnv, DEFAULT_SESSION_CACHE_LIMITS.maxFns),
53273
+ maxEvalRuns: pick2("maxEvalRuns", fromEnv, DEFAULT_SESSION_CACHE_LIMITS.maxEvalRuns)
52796
53274
  };
52797
53275
  }
52798
53276
  function setSessionCacheLimits(partial2) {
@@ -52805,9 +53283,8 @@ function setSessionCacheFromProject(partial2) {
52805
53283
  function resetSessionCacheLimitState() {
52806
53284
  explicit = {};
52807
53285
  fromProject = {};
52808
- envCache = null;
52809
53286
  }
52810
- var DEFAULT_SESSION_CACHE_LIMITS, HARD_CAP, explicit, fromProject, envCache;
53287
+ var DEFAULT_SESSION_CACHE_LIMITS, HARD_CAP, explicit, fromProject;
52811
53288
  var init_session_cache_limits = __esm({
52812
53289
  "../service/src/session-cache-limits.ts"() {
52813
53290
  "use strict";
@@ -52819,7 +53296,6 @@ var init_session_cache_limits = __esm({
52819
53296
  HARD_CAP = 65536;
52820
53297
  explicit = {};
52821
53298
  fromProject = {};
52822
- envCache = null;
52823
53299
  }
52824
53300
  });
52825
53301
 
@@ -52954,15 +53430,22 @@ function findProjectConfig(startDir) {
52954
53430
  while (dir !== root) {
52955
53431
  const pkgPath = resolve6(dir, "package.json");
52956
53432
  if (existsSync7(pkgPath)) {
53433
+ let pkg;
52957
53434
  try {
52958
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
52959
- if (pkg.nudo) {
52960
- const nudo = pkg.nudo;
52961
- setSessionCacheFromProject(nudo.sessionCache);
52962
- applyBForkBudgetFromConfig(nudo);
52963
- return { config: nudo, projectDir: dir };
52964
- }
52965
- } catch {
53435
+ pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
53436
+ } catch (err) {
53437
+ process.stderr?.write?.(
53438
+ `nudo: package.json parse failed at ${pkgPath} (${err instanceof Error ? err.message : String(err)}); project config disabled for this subtree (defaults + env apply)
53439
+ `
53440
+ );
53441
+ applyBForkBudgetFromConfig(null);
53442
+ return null;
53443
+ }
53444
+ const nudo = pkg && typeof pkg === "object" ? pkg.nudo : void 0;
53445
+ if (nudo) {
53446
+ setSessionCacheFromProject(nudo.sessionCache);
53447
+ applyBForkBudgetFromConfig(nudo);
53448
+ return { config: nudo, projectDir: dir };
52966
53449
  }
52967
53450
  }
52968
53451
  const parent = dirname5(dir);
@@ -267994,8 +268477,8 @@ function collectExports(sourceFile) {
267994
268477
  }
267995
268478
  return out;
267996
268479
  }
267997
- function draftExportName(name) {
267998
- return name.includes(".") ? name.replace(/\./g, "_") : name;
268480
+ function draftExportName(name, used) {
268481
+ return toJsBindingIdent(name.includes(".") ? name.replace(/\./g, "_") : name, used);
267999
268482
  }
268000
268483
  function formatDtsContractDraft(label, files, exports) {
268001
268484
  const draftable = exports.filter((e) => e.dsl !== void 0);
@@ -268007,6 +268490,7 @@ function formatDtsContractDraft(label, files, exports) {
268007
268490
  }
268008
268491
  if (draftable.some((e) => /\bany\s*\(/.test(e.dsl))) used.add("any");
268009
268492
  const importList = BUILDERS.filter((b) => used.has(b)).join(", ");
268493
+ const exportUsed = /* @__PURE__ */ new Set();
268010
268494
  const lines = [
268011
268495
  "// @nudo:draft",
268012
268496
  `// Generated by \`nudo contract --from-dts\` from ${label}`,
@@ -268033,7 +268517,7 @@ function formatDtsContractDraft(label, files, exports) {
268033
268517
  }
268034
268518
  if (e.note) lines.push(`// ${e.name} \u2014 ${e.note}`);
268035
268519
  else lines.push(`// ${e.name} \u2014 from TypeScript`);
268036
- lines.push(`export const ${draftExportName(e.name)} = ${e.dsl};`);
268520
+ lines.push(`export const ${draftExportName(e.name, exportUsed)} = ${e.dsl};`);
268037
268521
  if (e.name.includes(".")) {
268038
268522
  lines.push(`// sidecar key may also be written as \`${e.name}\``);
268039
268523
  }
@@ -268115,6 +268599,7 @@ var init_dts_contract_draft = __esm({
268115
268599
  "../harvester/src/dts-contract-draft.ts"() {
268116
268600
  "use strict";
268117
268601
  import_typescript2 = __toESM(require_typescript(), 1);
268602
+ init_internal();
268118
268603
  BUILDERS = [
268119
268604
  "fn",
268120
268605
  "number",
@@ -268265,8 +268750,8 @@ function formatHarvestSummary(h) {
268265
268750
  return lines.join("\n");
268266
268751
  }
268267
268752
  function lookupHarvested(h, moduleName, exportName) {
268268
- const mod2 = h.env.modules[moduleName];
268269
- const a = mod2?.[exportName] ?? h.env.globals[exportName];
268753
+ const mod2 = Object.hasOwn(h.env.modules, moduleName) ? h.env.modules[moduleName] : void 0;
268754
+ const a = (mod2 !== void 0 && Object.hasOwn(mod2, exportName) ? mod2[exportName] : void 0) ?? (Object.hasOwn(h.env.globals, exportName) ? h.env.globals[exportName] : void 0);
268270
268755
  if (!a) return void 0;
268271
268756
  return formatShape(a);
268272
268757
  }
@@ -269300,7 +269785,7 @@ function collectRequireSpecs(node, out) {
269300
269785
  };
269301
269786
  visit2(node);
269302
269787
  }
269303
- function buildModulesForFile(source, fromFile, load, evalDep, depth, maxDepth, onIssue) {
269788
+ function buildModulesForFile(source, fromFile, load, evalDep, depth, maxDepth, onIssue, localDepsOut) {
269304
269789
  const modules = {};
269305
269790
  for (const spec of importSpecs(source)) {
269306
269791
  if (spec.startsWith(".") || spec.startsWith("/")) {
@@ -269310,6 +269795,7 @@ function buildModulesForFile(source, fromFile, load, evalDep, depth, maxDepth, o
269310
269795
  continue;
269311
269796
  }
269312
269797
  modules[spec] = evalDep(childPath, spec, fromFile, depth + 1);
269798
+ localDepsOut?.push(stablePathKey(childPath));
269313
269799
  } else if (!spec.startsWith("node:")) {
269314
269800
  const resolved = resolveNpmJsEntryDetailed(spec, dirname10(fromFile));
269315
269801
  if (resolved.exportsUnresolved) {
@@ -269321,6 +269807,7 @@ function buildModulesForFile(source, fromFile, load, evalDep, depth, maxDepth, o
269321
269807
  }
269322
269808
  if (resolved.path) {
269323
269809
  const executed = evalDep(resolved.path, spec, fromFile, depth + 1);
269810
+ localDepsOut?.push(stablePathKey(resolved.path));
269324
269811
  if (executed && (executed.default !== void 0 || Object.keys(executed.named ?? {}).length > 0)) {
269325
269812
  modules[spec] = executed;
269326
269813
  continue;
@@ -269342,6 +269829,18 @@ function clearAbsModuleCache() {
269342
269829
  function evictAbsModuleCacheFiles(paths) {
269343
269830
  for (const p of paths) absModuleCache.delete(stablePathKey(p));
269344
269831
  }
269832
+ function depFingerprintCurrent(deps) {
269833
+ for (const d of deps) {
269834
+ let src;
269835
+ try {
269836
+ src = readFileSync13(d.path, "utf-8");
269837
+ } catch {
269838
+ return false;
269839
+ }
269840
+ if (hashSource(src) !== d.hash) return false;
269841
+ }
269842
+ return true;
269843
+ }
269345
269844
  function moduleLabel(p) {
269346
269845
  const parts = p.split(/[/\\]/);
269347
269846
  return parts[parts.length - 1] || p;
@@ -269426,7 +269925,7 @@ function collectMissingExportIssues(source, modules, fromFile) {
269426
269925
  const checkName = (spec, name, kind) => {
269427
269926
  const mod2 = modules[spec];
269428
269927
  if (!mod2?.evaluated) return;
269429
- const has = name === "default" ? mod2.default !== void 0 : name in mod2.named;
269928
+ const has = name === "default" ? mod2.default !== void 0 : Object.hasOwn(mod2.named, name);
269430
269929
  if (!has) push(spec, name, kind);
269431
269930
  };
269432
269931
  const body = file.program?.body ?? [];
@@ -269459,11 +269958,15 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
269459
269958
  const loading = [];
269460
269959
  const issues = [];
269461
269960
  const seenIssue = /* @__PURE__ */ new Set();
269961
+ const issueFlow = [];
269962
+ const depMeta = /* @__PURE__ */ new Map();
269462
269963
  const pushIssue2 = (kind, label, reason) => {
269964
+ const iss = { kind, label, reason };
269965
+ issueFlow.push(iss);
269463
269966
  const key = `${kind}:${label}`;
269464
269967
  if (seenIssue.has(key)) return;
269465
269968
  seenIssue.add(key);
269466
- issues.push({ kind, label, reason });
269969
+ issues.push(iss);
269467
269970
  };
269468
269971
  function evalDep(absPathRaw, spec, fromFile, depth) {
269469
269972
  const absPath = stablePathKey(absPathRaw);
@@ -269481,9 +269984,13 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
269481
269984
  if (shared) {
269482
269985
  try {
269483
269986
  const st = statSync9(absPath);
269484
- if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
269987
+ if (st.mtimeMs === shared.mtimeMs && st.size === shared.size && depFingerprintCurrent(shared.depFingerprints)) {
269485
269988
  for (const iss of shared.issues) pushIssue2(iss.kind, iss.label, iss.reason);
269486
269989
  cache2.set(absPath, shared.exports);
269990
+ depMeta.set(absPath, {
269991
+ contentHash: shared.contentHash,
269992
+ depFingerprints: shared.depFingerprints
269993
+ });
269487
269994
  return shared.exports;
269488
269995
  }
269489
269996
  } catch {
@@ -269521,7 +270028,8 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
269521
270028
  loading.pop();
269522
270029
  return placeholder;
269523
270030
  }
269524
- const issueStart = issues.length;
270031
+ const issueStart = issueFlow.length;
270032
+ const localDeps = [];
269525
270033
  const modules2 = buildModulesForFile(
269526
270034
  source,
269527
270035
  absPath,
@@ -269531,7 +270039,8 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
269531
270039
  maxDepth,
269532
270040
  (kind, label, reason) => {
269533
270041
  pushIssue2(kind, label, reason);
269534
- }
270042
+ },
270043
+ localDeps
269535
270044
  );
269536
270045
  let exports;
269537
270046
  const evalRun = tryRunTranspiled(source, { mode: "analyze", modules: modules2 });
@@ -269548,17 +270057,40 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
269548
270057
  if (exports.default !== void 0) placeholder.default = exports.default;
269549
270058
  if (exports.evaluated) placeholder.evaluated = true;
269550
270059
  cache2.set(absPath, placeholder);
270060
+ const depFingerprints = [];
270061
+ const seenDep = /* @__PURE__ */ new Set();
270062
+ let untrackable = false;
270063
+ for (const depPath of localDeps) {
270064
+ const meta = depMeta.get(depPath);
270065
+ if (!meta) {
270066
+ untrackable = true;
270067
+ break;
270068
+ }
270069
+ if (!seenDep.has(depPath)) {
270070
+ seenDep.add(depPath);
270071
+ depFingerprints.push({ path: depPath, hash: meta.contentHash });
270072
+ }
270073
+ for (const e of meta.depFingerprints) {
270074
+ if (seenDep.has(e.path)) continue;
270075
+ seenDep.add(e.path);
270076
+ depFingerprints.push(e);
270077
+ }
270078
+ }
270079
+ const subtree = untrackable ? null : { contentHash: hashSource(source), depFingerprints };
270080
+ depMeta.set(absPath, subtree);
269551
270081
  let fingerprint;
269552
270082
  try {
269553
270083
  const st = statSync9(absPath);
269554
270084
  fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
269555
270085
  } catch {
269556
270086
  }
269557
- if (fingerprint) {
270087
+ if (fingerprint && subtree) {
269558
270088
  absModuleCache.set(absPath, {
269559
270089
  ...fingerprint,
270090
+ contentHash: subtree.contentHash,
270091
+ depFingerprints: subtree.depFingerprints,
269560
270092
  exports: placeholder,
269561
- issues: issues.slice(issueStart)
270093
+ issues: issueFlow.slice(issueStart)
269562
270094
  });
269563
270095
  }
269564
270096
  return placeholder;
@@ -269602,7 +270134,7 @@ function importLocalBindings(source, modules) {
269602
270134
  } else if (sp.type === "ImportSpecifier") {
269603
270135
  const imported = sp.imported?.type === "StringLiteral" ? sp.imported.value : sp.imported?.name;
269604
270136
  if (imported === void 0) continue;
269605
- const absVal = imported === "default" ? mod2.default : mod2.named[imported];
270137
+ const absVal = imported === "default" ? mod2.default : Object.hasOwn(mod2.named, imported) ? mod2.named[imported] : void 0;
269606
270138
  out.set(local, absVal ?? unknown);
269607
270139
  }
269608
270140
  }
@@ -269800,7 +270332,7 @@ function pickNamed(mock2, names2) {
269800
270332
  if (mock2.default) def = mock2.default;
269801
270333
  continue;
269802
270334
  }
269803
- const v2 = mock2.named[n];
270335
+ const v2 = Object.hasOwn(mock2.named, n) ? mock2.named[n] : void 0;
269804
270336
  if (v2 !== void 0) named[n] = v2;
269805
270337
  }
269806
270338
  return def !== void 0 ? { named, default: def } : { named };
@@ -269885,6 +270417,7 @@ function trimAnalysisFileCache() {
269885
270417
  }
269886
270418
  }
269887
270419
  function analysisCacheGet(filePath, source, auxKey) {
270420
+ if (getSessionCacheLimits().maxFiles <= 0) return void 0;
269888
270421
  const key = stablePathKey(filePath);
269889
270422
  const e = analysisByFile.get(key);
269890
270423
  if (!e) return void 0;
@@ -269902,6 +270435,7 @@ function analysisCacheSet(filePath, source, auxKey, value) {
269902
270435
  if (oldest === void 0) break;
269903
270436
  analysisByFile.delete(oldest);
269904
270437
  }
270438
+ analysisByFile.delete(key);
269905
270439
  analysisByFile.set(key, { source, auxKey, value });
269906
270440
  }
269907
270441
  function evictAnalysisFileCacheForFiles(files) {
@@ -269943,6 +270477,7 @@ function trimFnAnalysisCache() {
269943
270477
  }
269944
270478
  }
269945
270479
  function fnAnalysisCacheGet(key) {
270480
+ if (getSessionCacheLimits().maxFns <= 0) return void 0;
269946
270481
  const k = normalizeFnCacheKey(key);
269947
270482
  const hit = fnAnalysisCache.get(k);
269948
270483
  if (hit !== void 0) {
@@ -269960,6 +270495,7 @@ function fnAnalysisCacheSet(key, value) {
269960
270495
  if (oldest === void 0) break;
269961
270496
  fnAnalysisCache.delete(oldest);
269962
270497
  }
270498
+ fnAnalysisCache.delete(k);
269963
270499
  fnAnalysisCache.set(k, value);
269964
270500
  }
269965
270501
  function evictFnAnalysisCacheForFiles(files) {
@@ -270150,7 +270686,11 @@ function collectEvalReplacements(source) {
270150
270686
  }
270151
270687
  };
270152
270688
  visitStmts(file.program.body);
270153
- } catch {
270689
+ } catch (err) {
270690
+ if (targets.length > 0 || asTargets.length > 0) {
270691
+ throw err;
270692
+ }
270693
+ noteEvalFallback(err);
270154
270694
  }
270155
270695
  return { targets, values, asTargets, asValues };
270156
270696
  }
@@ -270192,6 +270732,7 @@ function evalCacheSet(filePath, stableSource, mode, envKey, mockKey, depKey, val
270192
270732
  if (oldest === void 0) break;
270193
270733
  evalRunByFile.delete(oldest);
270194
270734
  }
270735
+ evalRunByFile.delete(key);
270195
270736
  evalRunByFile.set(key, { stableSource, mode, envKey, mockKey, depKey, value });
270196
270737
  }
270197
270738
  function trimEvalCache() {
@@ -270210,7 +270751,7 @@ function tryRunEval(source, filePath, opts = {}) {
270210
270751
  const stable = stableAnalyzeKeySource(source);
270211
270752
  const depKey = evalDepKey(source, filePath);
270212
270753
  const canCache = depKey !== null;
270213
- if (canCache) {
270754
+ if (canCache && getSessionCacheLimits().maxEvalRuns > 0) {
270214
270755
  const cacheKey = stablePathKey(filePath);
270215
270756
  const cached2 = evalRunByFile.get(cacheKey);
270216
270757
  if (cached2 && cached2.stableSource === stable && cached2.mode === mode && cached2.envKey === envKey && cached2.mockKey === mockKey && cached2.depKey === depKey) {
@@ -270274,7 +270815,7 @@ function tryRunEval(source, filePath, opts = {}) {
270274
270815
  function tryEvalCallFull(source, filePath, fnName, args, opts = {}) {
270275
270816
  const run = tryRunEval(source, filePath, { envNames: opts.envNames, mocks: opts.mocks });
270276
270817
  if (!run) return void 0;
270277
- if (!(fnName in run.exports)) return void 0;
270818
+ if (!Object.hasOwn(run.exports, fnName)) return void 0;
270278
270819
  const collected = [];
270279
270820
  const memberDiags = [];
270280
270821
  const prevCall = opts.collectCalls ? setEvalCallCollector((r) => collected.push(r)) : void 0;
@@ -270299,7 +270840,14 @@ function tryEvalCall(source, filePath, fnName, args, opts = {}) {
270299
270840
  const full = tryEvalCallFull(source, filePath, fnName, args, opts);
270300
270841
  if (!full) return void 0;
270301
270842
  const r = full.result;
270302
- if (!r) return void 0;
270843
+ if (!isAbsVal(r)) {
270844
+ noteEvalFallback(
270845
+ new Error(
270846
+ `tryEvalCall: '${fnName}' result is not an Abs value (producer contract violation)`
270847
+ )
270848
+ );
270849
+ return void 0;
270850
+ }
270303
270851
  if (r.shape.k === "never" && full.throws.shape.k !== "never") {
270304
270852
  return void 0;
270305
270853
  }
@@ -270502,13 +271050,14 @@ function safeAbsOrUnknown(a) {
270502
271050
  }
270503
271051
  function callRecordFromAbsCall(r, impMap) {
270504
271052
  const argAbs = r.args.map(safeAbsOrUnknown);
270505
- const threw = !!r.threw;
271053
+ const threw = r.threw === true;
271054
+ const threwUnknown = r.threw === void 0;
270506
271055
  const thrownOrResult = safeAbsOrUnknown(r.result);
270507
271056
  const rec = {
270508
271057
  fnName: r.fnName,
270509
271058
  argAbs,
270510
271059
  resultAbs: threw ? neverAbs : thrownOrResult,
270511
- throwsAbs: threw ? thrownOrResult : neverAbs,
271060
+ throwsAbs: threw ? thrownOrResult : threwUnknown ? unknown : neverAbs,
270512
271061
  callLoc: r.callLoc
270513
271062
  };
270514
271063
  const imp = impMap?.get(r.fnName) ?? // `ns.helper` / `Class.method`:先整名,再按首段点号拆命名空间成员
@@ -270978,19 +271527,23 @@ function collectDeclared(file) {
270978
271527
  if (id.type === "Identifier") names2.add(id.name);
270979
271528
  if (id.type === "ObjectPattern") {
270980
271529
  for (const p of id.properties) {
270981
- addId(p.value);
271530
+ addId(p.type === "RestElement" ? p.argument : p.value);
270982
271531
  }
270983
271532
  }
270984
271533
  if (id.type === "ArrayPattern") {
270985
271534
  for (const el of id.elements) addId(el);
270986
271535
  }
271536
+ if (id.type === "AssignmentPattern") addId(id.left);
271537
+ if (id.type === "RestElement") addId(id.argument);
270987
271538
  };
270988
271539
  const visit2 = (node) => {
270989
271540
  if (!node || typeof node !== "object") return;
270990
271541
  const o = node;
270991
271542
  switch (o.type) {
270992
271543
  case "FunctionDeclaration":
271544
+ case "FunctionExpression":
270993
271545
  case "ClassDeclaration":
271546
+ case "ClassExpression":
270994
271547
  addId(o.id);
270995
271548
  break;
270996
271549
  case "VariableDeclarator":
@@ -271035,7 +271588,7 @@ function isStaticStringExpr(node) {
271035
271588
  function isFreeUnknown(name, declared, seen, known) {
271036
271589
  return !declared.has(name) && !known.has(name) && !name.startsWith("$") && !seen.has(name);
271037
271590
  }
271038
- function walkBuiltinUnknown(node, declared, out, seen, known, parentKey) {
271591
+ function walkBuiltinUnknown(node, declared, out, seen, known, namePosition = false) {
271039
271592
  const n = node;
271040
271593
  const flag = (idNode, name) => {
271041
271594
  if (!isFreeUnknown(name, declared, seen, known)) return;
@@ -271057,20 +271610,21 @@ function walkBuiltinUnknown(node, declared, out, seen, known, parentKey) {
271057
271610
  } else if (callee?.type === "Identifier") {
271058
271611
  flag(n.callee, callee.name);
271059
271612
  }
271060
- } else if (n.type === "Identifier" && typeof n.name === "string" && n.name !== "require" && parentKey !== "key" && parentKey !== "property" && parentKey !== "local" && parentKey !== "id") {
271613
+ } else if (n.type === "Identifier" && typeof n.name === "string" && n.name !== "require" && !namePosition) {
271061
271614
  flag(node, n.name);
271062
271615
  }
271063
271616
  for (const key of Object.keys(n)) {
271064
271617
  if (key === "loc" || key === "start" || key === "end") continue;
271618
+ const childIsName = (key === "key" || key === "property") && n.computed !== true || key === "local" || key === "id";
271065
271619
  const v2 = n[key];
271066
271620
  if (Array.isArray(v2)) {
271067
271621
  v2.forEach((x) => {
271068
271622
  if (x && typeof x === "object" && "type" in x) {
271069
- walkBuiltinUnknown(x, declared, out, seen, known, key);
271623
+ walkBuiltinUnknown(x, declared, out, seen, known, childIsName);
271070
271624
  }
271071
271625
  });
271072
271626
  } else if (v2 && typeof v2 === "object" && "type" in v2) {
271073
- walkBuiltinUnknown(v2, declared, out, seen, known, key);
271627
+ walkBuiltinUnknown(v2, declared, out, seen, known, childIsName);
271074
271628
  }
271075
271629
  }
271076
271630
  }
@@ -272834,10 +273388,16 @@ var init_disk_cache = __esm({
272834
273388
  });
272835
273389
 
272836
273390
  // ../service/src/watch-paths.ts
273391
+ function matchFormalSidecarSuffix(path) {
273392
+ for (const suffix of FORMAL_SIDECAR_SUFFIXES) {
273393
+ if (path.slice(-suffix.length).toLowerCase() === suffix) return suffix;
273394
+ }
273395
+ return void 0;
273396
+ }
272837
273397
  function isSidecarPath(path) {
272838
273398
  const lower = path.toLowerCase().replace(/\\/g, "/");
272839
273399
  if (lower.includes(".nudo.draft.")) return false;
272840
- return lower.endsWith(".nudo.js") || lower.endsWith(".nudo.mjs") || lower.endsWith(".nudo.ts");
273400
+ return matchFormalSidecarSuffix(lower) !== void 0;
272841
273401
  }
272842
273402
  function isProjectConfigPath(path) {
272843
273403
  const norm = path.replace(/\\/g, "/");
@@ -272850,13 +273410,13 @@ function isWatchRelevantPath(path) {
272850
273410
  }
272851
273411
  function ambientSourcesOfSidecar(sidecarPath) {
272852
273412
  const norm = sidecarPath.replace(/\\/g, "/");
272853
- const m = norm.match(/^(.*)\.nudo\.(js|ts)$/i);
272854
- if (!m || norm.toLowerCase().includes(".nudo.draft.")) return [];
272855
- const base = m[1];
272856
- const ext = m[2].toLowerCase();
272857
- return ext === "js" ? [`${base}.js`, `${base}.mjs`] : [`${base}.ts`, `${base}.mts`];
273413
+ if (norm.toLowerCase().includes(".nudo.draft.")) return [];
273414
+ const suffix = matchFormalSidecarSuffix(norm);
273415
+ if (!suffix) return [];
273416
+ const base = norm.slice(0, norm.length - suffix.length);
273417
+ return SIDECAR_AMBIENT_SUFFIXES[suffix].map((ext) => `${base}${ext}`);
272858
273418
  }
272859
- var PROJECT_CONFIG_BASENAMES;
273419
+ var PROJECT_CONFIG_BASENAMES, FORMAL_SIDECAR_SUFFIXES, SIDECAR_AMBIENT_SUFFIXES;
272860
273420
  var init_watch_paths = __esm({
272861
273421
  "../service/src/watch-paths.ts"() {
272862
273422
  "use strict";
@@ -272871,6 +273431,12 @@ var init_watch_paths = __esm({
272871
273431
  ".nudorc",
272872
273432
  ".nudorc.json"
272873
273433
  ]);
273434
+ FORMAL_SIDECAR_SUFFIXES = [".nudo.js", ".nudo.mjs", ".nudo.ts"];
273435
+ SIDECAR_AMBIENT_SUFFIXES = {
273436
+ ".nudo.js": [".js", ".mjs"],
273437
+ ".nudo.mjs": [".js", ".mjs"],
273438
+ ".nudo.ts": [".ts", ".mts"]
273439
+ };
272874
273440
  }
272875
273441
  });
272876
273442
 
@@ -273343,7 +273909,7 @@ var init_chunk_4TAFH6ZZ = __esm({
273343
273909
  }
273344
273910
  });
273345
273911
 
273346
- // ../service/dist/chunk-RHEXBPQ6.js
273912
+ // ../service/dist/chunk-2RP2Q6LJ.js
273347
273913
  import { readFileSync as readFileSync17, existsSync as existsSync16, statSync as statSync10, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
273348
273914
  import { resolve as resolvePath2, join as joinPath2 } from "path";
273349
273915
  import { pathToFileURL as pathToFileURL2 } from "url";
@@ -273524,20 +274090,20 @@ function envLimits2(env2) {
273524
274090
  };
273525
274091
  }
273526
274092
  function pick3(key, env2, fallback) {
273527
- const e = env2[key];
273528
- if (typeof e === "number" && Number.isFinite(e)) return e;
273529
274093
  const x = explicit2[key];
273530
274094
  if (typeof x === "number" && Number.isFinite(x)) return clampEntries2(x, fallback);
274095
+ const e = env2[key];
274096
+ if (typeof e === "number" && Number.isFinite(e)) return e;
273531
274097
  const p = fromProject2[key];
273532
274098
  if (typeof p === "number" && Number.isFinite(p)) return clampEntries2(p, fallback);
273533
274099
  return fallback;
273534
274100
  }
273535
274101
  function getSessionCacheLimits2(env2 = process.env) {
273536
- if (envCache2 === null) envCache2 = envLimits2(env2);
274102
+ const fromEnv = envLimits2(env2);
273537
274103
  return {
273538
- maxFiles: pick3("maxFiles", envCache2, DEFAULT_SESSION_CACHE_LIMITS2.maxFiles),
273539
- maxFns: pick3("maxFns", envCache2, DEFAULT_SESSION_CACHE_LIMITS2.maxFns),
273540
- maxEvalRuns: pick3("maxEvalRuns", envCache2, DEFAULT_SESSION_CACHE_LIMITS2.maxEvalRuns)
274104
+ maxFiles: pick3("maxFiles", fromEnv, DEFAULT_SESSION_CACHE_LIMITS2.maxFiles),
274105
+ maxFns: pick3("maxFns", fromEnv, DEFAULT_SESSION_CACHE_LIMITS2.maxFns),
274106
+ maxEvalRuns: pick3("maxEvalRuns", fromEnv, DEFAULT_SESSION_CACHE_LIMITS2.maxEvalRuns)
273541
274107
  };
273542
274108
  }
273543
274109
  function setSessionCacheFromProject2(partial2) {
@@ -273646,15 +274212,22 @@ function findProjectConfig2(startDir) {
273646
274212
  while (dir !== root) {
273647
274213
  const pkgPath = resolve10(dir, "package.json");
273648
274214
  if (existsSync22(pkgPath)) {
274215
+ let pkg;
273649
274216
  try {
273650
- const pkg = JSON.parse(readFileSync22(pkgPath, "utf-8"));
273651
- if (pkg.nudo) {
273652
- const nudo = pkg.nudo;
273653
- setSessionCacheFromProject2(nudo.sessionCache);
273654
- applyBForkBudgetFromConfig2(nudo);
273655
- return { config: nudo, projectDir: dir };
273656
- }
273657
- } catch {
274217
+ pkg = JSON.parse(readFileSync22(pkgPath, "utf-8"));
274218
+ } catch (err) {
274219
+ process.stderr?.write?.(
274220
+ `nudo: package.json parse failed at ${pkgPath} (${err instanceof Error ? err.message : String(err)}); project config disabled for this subtree (defaults + env apply)
274221
+ `
274222
+ );
274223
+ applyBForkBudgetFromConfig2(null);
274224
+ return null;
274225
+ }
274226
+ const nudo = pkg && typeof pkg === "object" ? pkg.nudo : void 0;
274227
+ if (nudo) {
274228
+ setSessionCacheFromProject2(nudo.sessionCache);
274229
+ applyBForkBudgetFromConfig2(nudo);
274230
+ return { config: nudo, projectDir: dir };
273658
274231
  }
273659
274232
  }
273660
274233
  const parent = dirname18(dir);
@@ -273806,9 +274379,9 @@ function resolveNpmJsEntryDetailed2(source, fromDir) {
273806
274379
  }
273807
274380
  return { path: tryInsidePkg2(pkgDir, subpath, tryFile), exportsUnresolved: false };
273808
274381
  }
273809
- var BUILTIN_ERROR_CLASSES2, BUILTIN_PROTOTYPE_CLASSES2, numA2, strA2, boolA2, unkA2, undefA2, nullA2, OBJECT_PROTOTYPE_METHODS2, BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS2, BoundedLruMap4, envFactories2, impliedDeps2, PATH_ENV_CACHE_MAX2, PATH_ENV_BY_PATH_MAX2, PATH_ENV_BASE_DIRS_MAX2, pathEnvCache2, pathEnvByPath2, pathEnvBaseDirs2, DEFAULT_SESSION_CACHE_LIMITS2, HARD_CAP2, explicit2, fromProject2, envCache2, DEFAULT_ANALYSIS_EXCLUDE2, DEFAULT_ANALYSIS_MODE2, JS_CONDITIONS2;
273810
- var init_chunk_RHEXBPQ6 = __esm({
273811
- "../service/dist/chunk-RHEXBPQ6.js"() {
274382
+ var BUILTIN_ERROR_CLASSES2, BUILTIN_PROTOTYPE_CLASSES2, numA2, strA2, boolA2, unkA2, undefA2, nullA2, OBJECT_PROTOTYPE_METHODS2, BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS2, BoundedLruMap4, envFactories2, impliedDeps2, PATH_ENV_CACHE_MAX2, PATH_ENV_BY_PATH_MAX2, PATH_ENV_BASE_DIRS_MAX2, pathEnvCache2, pathEnvByPath2, pathEnvBaseDirs2, DEFAULT_SESSION_CACHE_LIMITS2, HARD_CAP2, explicit2, fromProject2, DEFAULT_ANALYSIS_EXCLUDE2, DEFAULT_ANALYSIS_MODE2, JS_CONDITIONS2;
274383
+ var init_chunk_2RP2Q6LJ = __esm({
274384
+ "../service/dist/chunk-2RP2Q6LJ.js"() {
273812
274385
  "use strict";
273813
274386
  init_src();
273814
274387
  init_es();
@@ -274074,7 +274647,6 @@ var init_chunk_RHEXBPQ6 = __esm({
274074
274647
  HARD_CAP2 = 65536;
274075
274648
  explicit2 = {};
274076
274649
  fromProject2 = {};
274077
- envCache2 = null;
274078
274650
  DEFAULT_ANALYSIS_EXCLUDE2 = [
274079
274651
  "**/node_modules/**",
274080
274652
  "**/dist/**",
@@ -274085,7 +274657,7 @@ var init_chunk_RHEXBPQ6 = __esm({
274085
274657
  }
274086
274658
  });
274087
274659
 
274088
- // ../service/dist/chunk-XY75TNBY.js
274660
+ // ../service/dist/chunk-6GFPZGQS.js
274089
274661
  import { existsSync as existsSync17, statSync as statSync11, readFileSync as readFileSync18 } from "fs";
274090
274662
  import { dirname as dirname19, resolve as resolve11, join as join13 } from "path";
274091
274663
  import { readFileSync as readFileSync42, statSync as statSync32 } from "fs";
@@ -274696,7 +275268,7 @@ function collectRequireSpecs2(node, out) {
274696
275268
  };
274697
275269
  visit2(node);
274698
275270
  }
274699
- function buildModulesForFile2(source, fromFile, load, evalDep, depth, maxDepth, onIssue) {
275271
+ function buildModulesForFile2(source, fromFile, load, evalDep, depth, maxDepth, onIssue, localDepsOut) {
274700
275272
  const modules = {};
274701
275273
  for (const spec of importSpecs2(source)) {
274702
275274
  if (spec.startsWith(".") || spec.startsWith("/")) {
@@ -274706,6 +275278,7 @@ function buildModulesForFile2(source, fromFile, load, evalDep, depth, maxDepth,
274706
275278
  continue;
274707
275279
  }
274708
275280
  modules[spec] = evalDep(childPath, spec, fromFile, depth + 1);
275281
+ localDepsOut?.push(stablePathKey(childPath));
274709
275282
  } else if (!spec.startsWith("node:")) {
274710
275283
  const resolved = resolveNpmJsEntryDetailed2(spec, dirname32(fromFile));
274711
275284
  if (resolved.exportsUnresolved) {
@@ -274717,6 +275290,7 @@ function buildModulesForFile2(source, fromFile, load, evalDep, depth, maxDepth,
274717
275290
  }
274718
275291
  if (resolved.path) {
274719
275292
  const executed = evalDep(resolved.path, spec, fromFile, depth + 1);
275293
+ localDepsOut?.push(stablePathKey(resolved.path));
274720
275294
  if (executed && (executed.default !== void 0 || Object.keys(executed.named ?? {}).length > 0)) {
274721
275295
  modules[spec] = executed;
274722
275296
  continue;
@@ -274729,6 +275303,18 @@ function buildModulesForFile2(source, fromFile, load, evalDep, depth, maxDepth,
274729
275303
  void maxDepth;
274730
275304
  return modules;
274731
275305
  }
275306
+ function depFingerprintCurrent2(deps) {
275307
+ for (const d of deps) {
275308
+ let src;
275309
+ try {
275310
+ src = readFileSync42(d.path, "utf-8");
275311
+ } catch {
275312
+ return false;
275313
+ }
275314
+ if (hashSource(src) !== d.hash) return false;
275315
+ }
275316
+ return true;
275317
+ }
274732
275318
  function moduleLabel2(p) {
274733
275319
  const parts = p.split(/[/\\]/);
274734
275320
  return parts[parts.length - 1] || p;
@@ -274813,7 +275399,7 @@ function collectMissingExportIssues2(source, modules, fromFile) {
274813
275399
  const checkName = (spec, name, kind) => {
274814
275400
  const mod2 = modules[spec];
274815
275401
  if (!mod2?.evaluated) return;
274816
- const has = name === "default" ? mod2.default !== void 0 : name in mod2.named;
275402
+ const has = name === "default" ? mod2.default !== void 0 : Object.hasOwn(mod2.named, name);
274817
275403
  if (!has) push(spec, name, kind);
274818
275404
  };
274819
275405
  const body = file.program?.body ?? [];
@@ -274846,11 +275432,15 @@ function evalAbsModuleGraph2(entrySource, entryFile, opts = {}) {
274846
275432
  const loading = [];
274847
275433
  const issues = [];
274848
275434
  const seenIssue = /* @__PURE__ */ new Set();
275435
+ const issueFlow = [];
275436
+ const depMeta = /* @__PURE__ */ new Map();
274849
275437
  const pushIssue2 = (kind, label, reason) => {
275438
+ const iss = { kind, label, reason };
275439
+ issueFlow.push(iss);
274850
275440
  const key = `${kind}:${label}`;
274851
275441
  if (seenIssue.has(key)) return;
274852
275442
  seenIssue.add(key);
274853
- issues.push({ kind, label, reason });
275443
+ issues.push(iss);
274854
275444
  };
274855
275445
  function evalDep(absPathRaw, spec, fromFile, depth) {
274856
275446
  const absPath = stablePathKey(absPathRaw);
@@ -274868,9 +275458,13 @@ function evalAbsModuleGraph2(entrySource, entryFile, opts = {}) {
274868
275458
  if (shared) {
274869
275459
  try {
274870
275460
  const st = statSync32(absPath);
274871
- if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
275461
+ if (st.mtimeMs === shared.mtimeMs && st.size === shared.size && depFingerprintCurrent2(shared.depFingerprints)) {
274872
275462
  for (const iss of shared.issues) pushIssue2(iss.kind, iss.label, iss.reason);
274873
275463
  cache2.set(absPath, shared.exports);
275464
+ depMeta.set(absPath, {
275465
+ contentHash: shared.contentHash,
275466
+ depFingerprints: shared.depFingerprints
275467
+ });
274874
275468
  return shared.exports;
274875
275469
  }
274876
275470
  } catch {
@@ -274908,7 +275502,8 @@ function evalAbsModuleGraph2(entrySource, entryFile, opts = {}) {
274908
275502
  loading.pop();
274909
275503
  return placeholder;
274910
275504
  }
274911
- const issueStart = issues.length;
275505
+ const issueStart = issueFlow.length;
275506
+ const localDeps = [];
274912
275507
  const modules2 = buildModulesForFile2(
274913
275508
  source,
274914
275509
  absPath,
@@ -274918,7 +275513,8 @@ function evalAbsModuleGraph2(entrySource, entryFile, opts = {}) {
274918
275513
  maxDepth,
274919
275514
  (kind, label, reason) => {
274920
275515
  pushIssue2(kind, label, reason);
274921
- }
275516
+ },
275517
+ localDeps
274922
275518
  );
274923
275519
  let exports;
274924
275520
  const evalRun = tryRunTranspiled(source, { mode: "analyze", modules: modules2 });
@@ -274935,17 +275531,40 @@ function evalAbsModuleGraph2(entrySource, entryFile, opts = {}) {
274935
275531
  if (exports.default !== void 0) placeholder.default = exports.default;
274936
275532
  if (exports.evaluated) placeholder.evaluated = true;
274937
275533
  cache2.set(absPath, placeholder);
275534
+ const depFingerprints = [];
275535
+ const seenDep = /* @__PURE__ */ new Set();
275536
+ let untrackable = false;
275537
+ for (const depPath of localDeps) {
275538
+ const meta = depMeta.get(depPath);
275539
+ if (!meta) {
275540
+ untrackable = true;
275541
+ break;
275542
+ }
275543
+ if (!seenDep.has(depPath)) {
275544
+ seenDep.add(depPath);
275545
+ depFingerprints.push({ path: depPath, hash: meta.contentHash });
275546
+ }
275547
+ for (const e of meta.depFingerprints) {
275548
+ if (seenDep.has(e.path)) continue;
275549
+ seenDep.add(e.path);
275550
+ depFingerprints.push(e);
275551
+ }
275552
+ }
275553
+ const subtree = untrackable ? null : { contentHash: hashSource(source), depFingerprints };
275554
+ depMeta.set(absPath, subtree);
274938
275555
  let fingerprint;
274939
275556
  try {
274940
275557
  const st = statSync32(absPath);
274941
275558
  fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
274942
275559
  } catch {
274943
275560
  }
274944
- if (fingerprint) {
275561
+ if (fingerprint && subtree) {
274945
275562
  absModuleCache2.set(absPath, {
274946
275563
  ...fingerprint,
275564
+ contentHash: subtree.contentHash,
275565
+ depFingerprints: subtree.depFingerprints,
274947
275566
  exports: placeholder,
274948
- issues: issues.slice(issueStart)
275567
+ issues: issueFlow.slice(issueStart)
274949
275568
  });
274950
275569
  }
274951
275570
  return placeholder;
@@ -274989,7 +275608,7 @@ function importLocalBindings2(source, modules) {
274989
275608
  } else if (sp.type === "ImportSpecifier") {
274990
275609
  const imported = sp.imported?.type === "StringLiteral" ? sp.imported.value : sp.imported?.name;
274991
275610
  if (imported === void 0) continue;
274992
- const absVal = imported === "default" ? mod2.default : mod2.named[imported];
275611
+ const absVal = imported === "default" ? mod2.default : Object.hasOwn(mod2.named, imported) ? mod2.named[imported] : void 0;
274993
275612
  out.set(local, absVal ?? unknown);
274994
275613
  }
274995
275614
  }
@@ -275132,7 +275751,7 @@ function pickNamed2(mock2, names2) {
275132
275751
  if (mock2.default) def = mock2.default;
275133
275752
  continue;
275134
275753
  }
275135
- const v2 = mock2.named[n];
275754
+ const v2 = Object.hasOwn(mock2.named, n) ? mock2.named[n] : void 0;
275136
275755
  if (v2 !== void 0) named[n] = v2;
275137
275756
  }
275138
275757
  return def !== void 0 ? { named, default: def } : { named };
@@ -275192,6 +275811,7 @@ function applyMockModuleDirectivesFromSource2(source, base, opts) {
275192
275811
  return applyMockModuleDirectives2(base, fileDirectives, opts);
275193
275812
  }
275194
275813
  function analysisCacheGet2(filePath, source, auxKey) {
275814
+ if (getSessionCacheLimits2().maxFiles <= 0) return void 0;
275195
275815
  const key = stablePathKey(filePath);
275196
275816
  const e = analysisByFile2.get(key);
275197
275817
  if (!e) return void 0;
@@ -275209,6 +275829,7 @@ function analysisCacheSet2(filePath, source, auxKey, value) {
275209
275829
  if (oldest === void 0) break;
275210
275830
  analysisByFile2.delete(oldest);
275211
275831
  }
275832
+ analysisByFile2.delete(key);
275212
275833
  analysisByFile2.set(key, { source, auxKey, value });
275213
275834
  }
275214
275835
  function normalizeFnCacheKey2(key) {
@@ -275218,6 +275839,7 @@ function normalizeFnCacheKey2(key) {
275218
275839
  return stablePathKey(filePath) + rest;
275219
275840
  }
275220
275841
  function fnAnalysisCacheGet2(key) {
275842
+ if (getSessionCacheLimits2().maxFns <= 0) return void 0;
275221
275843
  const k = normalizeFnCacheKey2(key);
275222
275844
  const hit = fnAnalysisCache2.get(k);
275223
275845
  if (hit !== void 0) {
@@ -275235,6 +275857,7 @@ function fnAnalysisCacheSet2(key, value) {
275235
275857
  if (oldest === void 0) break;
275236
275858
  fnAnalysisCache2.delete(oldest);
275237
275859
  }
275860
+ fnAnalysisCache2.delete(k);
275238
275861
  fnAnalysisCache2.set(k, value);
275239
275862
  }
275240
275863
  function caseDirectiveKey2(directives, formatAbs6) {
@@ -275399,7 +276022,11 @@ function collectEvalReplacements2(source) {
275399
276022
  }
275400
276023
  };
275401
276024
  visitStmts(file.program.body);
275402
- } catch {
276025
+ } catch (err) {
276026
+ if (targets.length > 0 || asTargets.length > 0) {
276027
+ throw err;
276028
+ }
276029
+ noteEvalFallback(err);
275403
276030
  }
275404
276031
  return { targets, values, asTargets, asValues };
275405
276032
  }
@@ -275426,6 +276053,7 @@ function evalCacheSet2(filePath, stableSource, mode, envKey, mockKey, depKey, va
275426
276053
  if (oldest === void 0) break;
275427
276054
  evalRunByFile2.delete(oldest);
275428
276055
  }
276056
+ evalRunByFile2.delete(key);
275429
276057
  evalRunByFile2.set(key, { stableSource, mode, envKey, mockKey, depKey, value });
275430
276058
  }
275431
276059
  function tryRunEval2(source, filePath, opts = {}) {
@@ -275436,7 +276064,7 @@ function tryRunEval2(source, filePath, opts = {}) {
275436
276064
  const stable = stableAnalyzeKeySource(source);
275437
276065
  const depKey = evalDepKey2(source, filePath);
275438
276066
  const canCache = depKey !== null;
275439
- if (canCache) {
276067
+ if (canCache && getSessionCacheLimits2().maxEvalRuns > 0) {
275440
276068
  const cacheKey = stablePathKey(filePath);
275441
276069
  const cached2 = evalRunByFile2.get(cacheKey);
275442
276070
  if (cached2 && cached2.stableSource === stable && cached2.mode === mode && cached2.envKey === envKey && cached2.mockKey === mockKey && cached2.depKey === depKey) {
@@ -275500,7 +276128,7 @@ function tryRunEval2(source, filePath, opts = {}) {
275500
276128
  function tryEvalCallFull2(source, filePath, fnName, args, opts = {}) {
275501
276129
  const run = tryRunEval2(source, filePath, { envNames: opts.envNames, mocks: opts.mocks });
275502
276130
  if (!run) return void 0;
275503
- if (!(fnName in run.exports)) return void 0;
276131
+ if (!Object.hasOwn(run.exports, fnName)) return void 0;
275504
276132
  const collected = [];
275505
276133
  const memberDiags = [];
275506
276134
  const prevCall = opts.collectCalls ? setEvalCallCollector((r) => collected.push(r)) : void 0;
@@ -275525,7 +276153,14 @@ function tryEvalCall2(source, filePath, fnName, args, opts = {}) {
275525
276153
  const full = tryEvalCallFull2(source, filePath, fnName, args, opts);
275526
276154
  if (!full) return void 0;
275527
276155
  const r = full.result;
275528
- if (!r) return void 0;
276156
+ if (!isAbsVal(r)) {
276157
+ noteEvalFallback(
276158
+ new Error(
276159
+ `tryEvalCall: '${fnName}' result is not an Abs value (producer contract violation)`
276160
+ )
276161
+ );
276162
+ return void 0;
276163
+ }
275529
276164
  if (r.shape.k === "never" && full.throws.shape.k !== "never") {
275530
276165
  return void 0;
275531
276166
  }
@@ -275686,7 +276321,7 @@ function absFromSinon2(sinonExpr) {
275686
276321
  }
275687
276322
  return constantMockFn2(unknown);
275688
276323
  }
275689
- function isAbsVal22(v2) {
276324
+ function isAbsVal32(v2) {
275690
276325
  return !!v2 && typeof v2 === "object" && "shape" in v2 && "conf" in v2;
275691
276326
  }
275692
276327
  function loadFromMockBinding2(name, fromPath, fromFile, loadModule) {
@@ -275718,7 +276353,7 @@ function loadFromMockBinding2(name, fromPath, fromFile, loadModule) {
275718
276353
  let absVal;
275719
276354
  if (val === void 0) {
275720
276355
  absVal = undefAbs();
275721
- } else if (isAbsVal22(val)) {
276356
+ } else if (isAbsVal32(val)) {
275722
276357
  absVal = val;
275723
276358
  } else if (typeof val === "function") {
275724
276359
  const fn2 = val;
@@ -275979,13 +276614,14 @@ function safeAbsOrUnknown2(a) {
275979
276614
  }
275980
276615
  function callRecordFromAbsCall2(r, impMap) {
275981
276616
  const argAbs = r.args.map(safeAbsOrUnknown2);
275982
- const threw = !!r.threw;
276617
+ const threw = r.threw === true;
276618
+ const threwUnknown = r.threw === void 0;
275983
276619
  const thrownOrResult = safeAbsOrUnknown2(r.result);
275984
276620
  const rec = {
275985
276621
  fnName: r.fnName,
275986
276622
  argAbs,
275987
276623
  resultAbs: threw ? neverAbs2 : thrownOrResult,
275988
- throwsAbs: threw ? thrownOrResult : neverAbs2,
276624
+ throwsAbs: threw ? thrownOrResult : threwUnknown ? unknown : neverAbs2,
275989
276625
  callLoc: r.callLoc
275990
276626
  };
275991
276627
  const imp = impMap?.get(r.fnName) ?? // `ns.helper` / `Class.method`:先整名,再按首段点号拆命名空间成员
@@ -276089,19 +276725,23 @@ function collectDeclared2(file) {
276089
276725
  if (id.type === "Identifier") names2.add(id.name);
276090
276726
  if (id.type === "ObjectPattern") {
276091
276727
  for (const p of id.properties) {
276092
- addId(p.value);
276728
+ addId(p.type === "RestElement" ? p.argument : p.value);
276093
276729
  }
276094
276730
  }
276095
276731
  if (id.type === "ArrayPattern") {
276096
276732
  for (const el of id.elements) addId(el);
276097
276733
  }
276734
+ if (id.type === "AssignmentPattern") addId(id.left);
276735
+ if (id.type === "RestElement") addId(id.argument);
276098
276736
  };
276099
276737
  const visit2 = (node) => {
276100
276738
  if (!node || typeof node !== "object") return;
276101
276739
  const o = node;
276102
276740
  switch (o.type) {
276103
276741
  case "FunctionDeclaration":
276742
+ case "FunctionExpression":
276104
276743
  case "ClassDeclaration":
276744
+ case "ClassExpression":
276105
276745
  addId(o.id);
276106
276746
  break;
276107
276747
  case "VariableDeclarator":
@@ -276146,7 +276786,7 @@ function isStaticStringExpr2(node) {
276146
276786
  function isFreeUnknown2(name, declared, seen, known) {
276147
276787
  return !declared.has(name) && !known.has(name) && !name.startsWith("$") && !seen.has(name);
276148
276788
  }
276149
- function walkBuiltinUnknown2(node, declared, out, seen, known, parentKey) {
276789
+ function walkBuiltinUnknown2(node, declared, out, seen, known, namePosition = false) {
276150
276790
  const n = node;
276151
276791
  const flag = (idNode, name) => {
276152
276792
  if (!isFreeUnknown2(name, declared, seen, known)) return;
@@ -276168,20 +276808,21 @@ function walkBuiltinUnknown2(node, declared, out, seen, known, parentKey) {
276168
276808
  } else if (callee?.type === "Identifier") {
276169
276809
  flag(n.callee, callee.name);
276170
276810
  }
276171
- } else if (n.type === "Identifier" && typeof n.name === "string" && n.name !== "require" && parentKey !== "key" && parentKey !== "property" && parentKey !== "local" && parentKey !== "id") {
276811
+ } else if (n.type === "Identifier" && typeof n.name === "string" && n.name !== "require" && !namePosition) {
276172
276812
  flag(node, n.name);
276173
276813
  }
276174
276814
  for (const key of Object.keys(n)) {
276175
276815
  if (key === "loc" || key === "start" || key === "end") continue;
276816
+ const childIsName = (key === "key" || key === "property") && n.computed !== true || key === "local" || key === "id";
276176
276817
  const v2 = n[key];
276177
276818
  if (Array.isArray(v2)) {
276178
276819
  v2.forEach((x) => {
276179
276820
  if (x && typeof x === "object" && "type" in x) {
276180
- walkBuiltinUnknown2(x, declared, out, seen, known, key);
276821
+ walkBuiltinUnknown2(x, declared, out, seen, known, childIsName);
276181
276822
  }
276182
276823
  });
276183
276824
  } else if (v2 && typeof v2 === "object" && "type" in v2) {
276184
- walkBuiltinUnknown2(v2, declared, out, seen, known, key);
276825
+ walkBuiltinUnknown2(v2, declared, out, seen, known, childIsName);
276185
276826
  }
276186
276827
  }
276187
276828
  }
@@ -277379,10 +278020,10 @@ function analyzeFile2(filePath, source, activeCases, externalCallRecords, loadMo
277379
278020
  return cloneAnalysisResult2(result);
277380
278021
  }
277381
278022
  var MODULE_RESOLVE_EXTS2, ModuleReadError2, neverAbs2, undefAbs4, MAX_RECORD_TYPE_NODES2, ABS_MODULE_CACHE_MAX2, absModuleCache2, ENV_DEPENDENTS_MAX2, ENV_DEPENDENTS_PER_KEY_MAX2, envDependents2, analysisByFile2, fnAnalysisCache2, envHarvestConflictCollector2, evalRunByFile2, KNOWN_GLOBALS2, DEFAULT_CALLSITE_BUDGET3, COLLAPSE_LITERAL_THRESHOLD2;
277382
- var init_chunk_XY75TNBY = __esm({
277383
- "../service/dist/chunk-XY75TNBY.js"() {
278023
+ var init_chunk_6GFPZGQS = __esm({
278024
+ "../service/dist/chunk-6GFPZGQS.js"() {
277384
278025
  "use strict";
277385
- init_chunk_RHEXBPQ6();
278026
+ init_chunk_2RP2Q6LJ();
277386
278027
  init_src2();
277387
278028
  init_src();
277388
278029
  init_src2();
@@ -277518,12 +278159,14 @@ __export(emit_exports, {
277518
278159
  insertGeneratedCaseDirectives: () => insertGeneratedCaseDirectives,
277519
278160
  interfaceSurface: () => interfaceSurface,
277520
278161
  isDraftableEntry: () => isDraftableEntry,
278162
+ matchSidecarFnDecl: () => matchSidecarFnDecl,
277521
278163
  materializeAction: () => materializeAction,
277522
278164
  projectAbsToSchema: () => projectAbsToSchema,
277523
278165
  schemaNodeToZod: () => schemaNodeToZod,
277524
278166
  serializeCaseArg: () => serializeCaseArg,
277525
278167
  serializeCaseJson: () => serializeCaseJson,
277526
278168
  shapeDslFromFields: () => shapeDslFromFields,
278169
+ sidecarBindingFor: () => sidecarBindingFor,
277527
278170
  sidecarDraftPath: () => sidecarDraftPath,
277528
278171
  stripGeneratedCaseDirectives: () => stripGeneratedCaseDirectives,
277529
278172
  titleKindFor: () => titleKindFor,
@@ -277711,6 +278354,18 @@ function serializeObjectKey(key) {
277711
278354
  return `"${key}"`;
277712
278355
  }
277713
278356
  function serializeCaseArg(a) {
278357
+ return serializeCaseArgB(a, new ProjectionBudget());
278358
+ }
278359
+ function serializeCaseArgB(a, budget) {
278360
+ const stop2 = budget.enter(a);
278361
+ if (stop2) return null;
278362
+ try {
278363
+ return serializeCaseArgInner(a, budget);
278364
+ } finally {
278365
+ budget.exit();
278366
+ }
278367
+ }
278368
+ function serializeCaseArgInner(a, budget) {
277714
278369
  const s = a.shape;
277715
278370
  if (a.term?.op === "lit") {
277716
278371
  const v2 = a.term.value;
@@ -277740,20 +278395,21 @@ function serializeCaseArg(a) {
277740
278395
  case "sum": {
277741
278396
  const parts = [];
277742
278397
  for (const member of s.members) {
277743
- const ser = serializeCaseArg(member);
278398
+ const ser = serializeCaseArgB(member, budget);
277744
278399
  if (ser === null) return null;
277745
278400
  parts.push(ser);
277746
278401
  }
277747
278402
  return `union(${parts.join(", ")})`;
277748
278403
  }
277749
278404
  case "arr": {
277750
- const el = serializeCaseArg(s.element);
278405
+ const el = serializeCaseArgB(s.element, budget);
277751
278406
  return el === null ? null : `array(${el})`;
277752
278407
  }
277753
278408
  case "tuple": {
278409
+ if (s.rest) return null;
277754
278410
  const parts = [];
277755
278411
  for (const el of s.elements) {
277756
- const ser = serializeCaseArg(el);
278412
+ const ser = serializeCaseArgB(el, budget);
277757
278413
  if (ser === null) return null;
277758
278414
  parts.push(ser);
277759
278415
  }
@@ -277762,7 +278418,7 @@ function serializeCaseArg(a) {
277762
278418
  case "obj": {
277763
278419
  const parts = [];
277764
278420
  for (const [key, slot] of Object.entries(s.slots)) {
277765
- const vs = serializeCaseArg(slot.value);
278421
+ const vs = serializeCaseArgB(slot.value, budget);
277766
278422
  if (vs === null) return null;
277767
278423
  const ks = serializeObjectKey(key);
277768
278424
  if (ks === null) return null;
@@ -278039,6 +278695,17 @@ function unifiedDiff(a, b, path) {
278039
278695
  }
278040
278696
  return out.join("\n") + "\n";
278041
278697
  }
278698
+ function exportNameRepr(name) {
278699
+ return JS_EXPORT_NAME.test(name) ? name : JSON.stringify(name);
278700
+ }
278701
+ function directBindingOk(name, taken) {
278702
+ return isJsBindingIdent(name) && !taken(name);
278703
+ }
278704
+ function allocNudoBinding(taken) {
278705
+ let n = 1;
278706
+ while (taken(`_nudo_${n}`)) n++;
278707
+ return `_nudo_${n}`;
278708
+ }
278042
278709
  async function emitInterface(filePath, opts) {
278043
278710
  const abs2 = resolve23(filePath);
278044
278711
  if (isNodeModulesPath(abs2) || isNodeModulesPath(sidecarPathOf(abs2))) {
@@ -278077,7 +278744,10 @@ async function emitInterface(filePath, opts) {
278077
278744
  `sidecar '${relative6(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
278078
278745
  );
278079
278746
  }
278080
- for (const n of generatedNames) declared.delete(n);
278747
+ for (const s of sections) {
278748
+ for (const n of s.names) declared.delete(n);
278749
+ for (const b of s.bindings) declared.delete(b);
278750
+ }
278081
278751
  const analysis = await analyzeFileAsync2(
278082
278752
  abs2,
278083
278753
  source,
@@ -278090,16 +278760,47 @@ async function emitInterface(filePath, opts) {
278090
278760
  for (const f of analysis.functions) {
278091
278761
  if (!fnByName.has(f.name)) fnByName.set(f.name, f);
278092
278762
  }
278093
- const fileExportOrder = analysis.functions.map((f) => f.name).filter((n) => exported.has(n));
278763
+ const identityOfLocal = (local) => exportedNameOfLocal(source, local) ?? local;
278764
+ const localOf = /* @__PURE__ */ new Map();
278765
+ const identityOfFn = /* @__PURE__ */ new Map();
278766
+ for (const f of analysis.functions) {
278767
+ if (identityOfFn.has(f.name)) continue;
278768
+ const identity = identityOfLocal(f.name);
278769
+ identityOfFn.set(f.name, identity);
278770
+ if (!localOf.has(identity)) localOf.set(identity, f.name);
278771
+ }
278772
+ const fileExportOrder = [
278773
+ ...new Set(
278774
+ analysis.functions.map((f) => identityOfFn.get(f.name)).filter((n) => exported.has(n))
278775
+ )
278776
+ ];
278094
278777
  let targetNames;
278095
278778
  if (opts.fnNames && opts.fnNames.length > 0) {
278096
- targetNames = [...new Set(opts.fnNames)];
278779
+ targetNames = [...new Set(opts.fnNames.map((n) => identityOfFn.get(n) ?? n))];
278097
278780
  } else if (opts.all) {
278098
278781
  targetNames = fileExportOrder;
278099
278782
  } else {
278100
278783
  targetNames = fileExportOrder.filter((n) => generatedNames.has(n));
278101
278784
  }
278102
278785
  const targetSet = new Set(targetNames);
278786
+ const takenBindings = /* @__PURE__ */ new Set();
278787
+ const claimFrom = (text) => {
278788
+ for (const n of topLevelDeclaredNames(text) ?? []) takenBindings.add(n);
278789
+ };
278790
+ if (opts.mode === "update") {
278791
+ claimFrom(removeSections(sidecarSrc, sections));
278792
+ for (const s of sections) {
278793
+ if (!s.names.some((n) => targetSet.has(n))) claimFrom(s.text);
278794
+ }
278795
+ } else {
278796
+ claimFrom(sidecarSrc);
278797
+ }
278798
+ const bindingOf = /* @__PURE__ */ new Map();
278799
+ for (const name of targetNames) {
278800
+ const binding = directBindingOk(name, (n) => takenBindings.has(n)) ? name : allocNudoBinding((n) => takenBindings.has(n));
278801
+ takenBindings.add(binding);
278802
+ bindingOf.set(name, binding);
278803
+ }
278103
278804
  const written = [];
278104
278805
  const skipped = [];
278105
278806
  const issues = [];
@@ -278108,9 +278809,9 @@ async function emitInterface(filePath, opts) {
278108
278809
  const planFor = (name) => {
278109
278810
  const hit = planCache.get(name);
278110
278811
  if (hit) return hit;
278111
- const fn2 = fnByName.get(name);
278812
+ const fn2 = fnByName.get(localOf.get(name) ?? name);
278112
278813
  const dsl = fn2 === void 0 ? void 0 : projectFunctionDsl(fn2);
278113
- const roundTrip = dsl !== void 0 && roundTrips(sectionText(name, dsl, srcRel), name);
278814
+ const roundTrip = dsl !== void 0 && roundTrips(sectionText(name, dsl, srcRel, bindingOf.get(name)), name);
278114
278815
  const rec = { dsl, roundTrip };
278115
278816
  planCache.set(name, rec);
278116
278817
  return rec;
@@ -278125,7 +278826,7 @@ async function emitInterface(filePath, opts) {
278125
278826
  }
278126
278827
  const acceptedAtomic = /* @__PURE__ */ new Set();
278127
278828
  for (const name of targetNames) {
278128
- if (!exported.has(name) || !fnByName.has(name)) {
278829
+ if (!exported.has(name) || !fnByName.has(localOf.get(name) ?? name)) {
278129
278830
  skipped.push({
278130
278831
  fn: name,
278131
278832
  reason: "not-an-export"
@@ -278165,7 +278866,7 @@ async function emitInterface(filePath, opts) {
278165
278866
  }
278166
278867
  continue;
278167
278868
  }
278168
- const text = sectionText(name, plan.dsl, srcRel);
278869
+ const text = sectionText(name, plan.dsl, srcRel, bindingOf.get(name));
278169
278870
  if (opts.mode === "add" && prev !== void 0) {
278170
278871
  skipped.push({ fn: name, reason: "no-change" });
278171
278872
  continue;
@@ -278262,10 +278963,12 @@ function projectFunctionDsl(fn2) {
278262
278963
  const ret = retConstraint === void 0 ? "" : `, ${formatConstraint(retConstraint)}`;
278263
278964
  return `fn({ ${paramParts.join(", ")} }${ret})`;
278264
278965
  }
278265
- function sectionText(fn2, dsl, srcRel) {
278966
+ function sectionText(fn2, dsl, srcRel, binding = fn2) {
278967
+ const decl = binding === fn2 ? `export const ${fn2} = ${dsl};` : `const ${binding} = ${dsl};
278968
+ export { ${binding} as ${exportNameRepr(fn2)} };`;
278266
278969
  return `${GENERATED_HEADER}
278267
278970
  // source: ${sanitizeCommentText(`${srcRel}:${fn2}`)}
278268
- export const ${fn2} = ${dsl};
278971
+ ${decl}
278269
278972
  `;
278270
278973
  }
278271
278974
  function roundTrips(text, fn2) {
@@ -278288,23 +278991,62 @@ function collectGeneratedSections(sidecarSrc) {
278288
278991
  } catch {
278289
278992
  throw new Error("sidecar is not parseable");
278290
278993
  }
278994
+ const stmts = ast.program.body;
278291
278995
  const out = [];
278292
- for (const stmt of ast.program.body) {
278996
+ for (let i = 0; i < stmts.length; i++) {
278997
+ const stmt = stmts[i];
278293
278998
  if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
278294
- const d = stmt.declaration;
278295
- if (!d || stmt.start == null || stmt.end == null) continue;
278999
+ if (stmt.end == null) continue;
279000
+ const chain = [];
279001
+ for (let j = i; j >= 0; j--) {
279002
+ const p = stmts[j];
279003
+ if (j !== i && p.type !== "ImportDeclaration" && p.type !== "VariableDeclaration") break;
279004
+ chain.unshift(p);
279005
+ }
279006
+ let blockStart;
279007
+ for (const c of chain) {
279008
+ if (c.start == null) continue;
279009
+ const b = generatedCommentBlockStart(sidecarSrc, c.start);
279010
+ if (b !== void 0) {
279011
+ blockStart = b;
279012
+ break;
279013
+ }
279014
+ }
279015
+ if (blockStart === void 0) continue;
278296
279016
  const names2 = [];
278297
- if (d.type === "VariableDeclaration") {
278298
- for (const decl of d.declarations) {
278299
- if (decl.id.type === "Identifier") names2.push(decl.id.name);
279017
+ const bindings = [];
279018
+ const d = stmt.declaration;
279019
+ if (d) {
279020
+ if (d.type === "VariableDeclaration") {
279021
+ for (const decl of d.declarations) {
279022
+ if (decl.id.type === "Identifier") {
279023
+ names2.push(decl.id.name);
279024
+ bindings.push(decl.id.name);
279025
+ }
279026
+ }
279027
+ } else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
279028
+ if (d.id) {
279029
+ names2.push(d.id.name);
279030
+ bindings.push(d.id.name);
279031
+ }
279032
+ }
279033
+ } else {
279034
+ for (const spec of stmt.specifiers) {
279035
+ if (spec.type !== "ExportSpecifier") continue;
279036
+ names2.push(
279037
+ spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value
279038
+ );
279039
+ if (spec.local.type === "Identifier") bindings.push(spec.local.name);
278300
279040
  }
278301
- } else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
278302
- if (d.id) names2.push(d.id.name);
278303
279041
  }
278304
279042
  if (names2.length === 0) continue;
278305
- const blockStart = generatedCommentBlockStart(sidecarSrc, stmt.start);
278306
- if (blockStart === void 0) continue;
278307
- out.push({ names: names2, start: blockStart, end: stmt.end, text: sidecarSrc.slice(blockStart, stmt.end) });
279043
+ out.push({
279044
+ names: names2,
279045
+ bindings,
279046
+ start: blockStart,
279047
+ end: stmt.end,
279048
+ text: sidecarSrc.slice(blockStart, stmt.end)
279049
+ });
278308
279050
  }
278309
279051
  return out;
278310
279052
  }
@@ -279042,8 +279784,8 @@ function toDraftBuilderDsl(display) {
279042
279784
  }
279043
279785
  );
279044
279786
  }
279045
- function draftExportName2(fnName) {
279046
- return fnName.includes(".") ? fnName.replace(/\./g, "_") : fnName;
279787
+ function draftExportName2(fnName, used) {
279788
+ return toJsBindingIdent(fnName.includes(".") ? fnName.replace(/\./g, "_") : fnName, used);
279047
279789
  }
279048
279790
  function draftDsl(entry) {
279049
279791
  const parts = entry.params.filter((p) => p.projected && p.constraint !== void 0).map((p) => {
@@ -279158,6 +279900,7 @@ ${suggestedBodyDsl(e.fn, e.params) ?? ""}`;
279158
279900
  }
279159
279901
  }
279160
279902
  const importList = BUILDERS2.filter((b) => used.has(b)).join(", ");
279903
+ const exportUsed = /* @__PURE__ */ new Set();
279161
279904
  const lines = [
279162
279905
  "// @nudo:draft",
279163
279906
  `// Generated by \`nudo contract --draft\` from ${sanitizeCommentText(filePath)}`,
@@ -279196,7 +279939,7 @@ ${suggestedBodyDsl(e.fn, e.params) ?? ""}`;
279196
279939
  } else if (e.returnEvidence === "symbolic") {
279197
279940
  lines.push(`// returns: ${sanitizeCommentText(e.returns?.display ?? "")} (symbolic)`);
279198
279941
  }
279199
- lines.push(`export const ${draftExportName2(e.fn)} = ${e.dsl};`);
279942
+ lines.push(`export const ${draftExportName2(e.fn, exportUsed)} = ${e.dsl};`);
279200
279943
  if (e.fn.includes(".")) {
279201
279944
  lines.push(
279202
279945
  `// sidecar key may also be written as \`${sanitizeCommentText(e.fn)}\` / nested { ${sanitizeCommentText(e.fn.split(".")[1] ?? "")}: \u2026 }`
@@ -279875,7 +280618,8 @@ function formatDerivedSection(row, opts) {
279875
280618
  retDsl = `, ${row.returns.dsl}`;
279876
280619
  }
279877
280620
  const namer = new NameAllocator(opts.takenNames);
279878
- namer.claim(row.fn);
280621
+ const directBinding = directBindingOk(row.fn, (n) => namer.has(n));
280622
+ const binding = directBinding ? namer.claim(row.fn) : namer.claim(allocNudoBinding((n) => namer.has(n)));
279879
280623
  const renames = /* @__PURE__ */ new Map();
279880
280624
  const importLocals = [];
279881
280625
  for (const [name, from] of [...importMap.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
@@ -279906,11 +280650,13 @@ function formatDerivedSection(row, opts) {
279906
280650
  const spec = original === local ? local : `${original} as ${local}`;
279907
280651
  return `import { ${spec} } from ${JSON.stringify(from)};`;
279908
280652
  }).sort((a, b) => a.localeCompare(b));
280653
+ const exportStmt = directBinding ? `export const ${binding} = fn({ ${paramParts.join(", ")} }${retPart});` : `const ${binding} = fn({ ${paramParts.join(", ")} }${retPart});
280654
+ export { ${binding} as ${exportNameRepr(row.fn)} };`;
279909
280655
  const lines = [
279910
280656
  ...importLineTexts,
279911
280657
  ...importLineTexts.length > 0 && renamedPreludes.length > 0 ? [""] : [],
279912
280658
  ...renamedPreludes,
279913
- `export const ${row.fn} = fn({ ${paramParts.join(", ")} }${retPart});`
280659
+ exportStmt
279914
280660
  ];
279915
280661
  const usedNames = /* @__PURE__ */ new Set();
279916
280662
  for (const { local } of importLocals) usedNames.add(local);
@@ -279918,7 +280664,7 @@ function formatDerivedSection(row, opts) {
279918
280664
  const n = preludeLocalName(line);
279919
280665
  if (n) usedNames.add(n);
279920
280666
  }
279921
- usedNames.add(row.fn);
280667
+ usedNames.add(binding);
279922
280668
  return { text: lines.join("\n"), usedNames: [...usedNames] };
279923
280669
  }
279924
280670
  function collectTopLevelNames(src) {
@@ -280168,18 +280914,35 @@ function collectGeneratedSectionsRaw(src) {
280168
280914
  if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
280169
280915
  if (stmt.start == null || stmt.end == null || stmt.start < headerPos) continue;
280170
280916
  const d = stmt.declaration;
280171
- if (!d) continue;
280172
280917
  const names2 = [];
280173
- if (d.type === "VariableDeclaration") {
280174
- for (const decl of d.declarations) {
280175
- if (decl.id.type === "Identifier") names2.push(decl.id.name);
280918
+ const bindings = [];
280919
+ if (d) {
280920
+ if (d.type === "VariableDeclaration") {
280921
+ for (const decl of d.declarations) {
280922
+ if (decl.id.type === "Identifier") {
280923
+ names2.push(decl.id.name);
280924
+ bindings.push(decl.id.name);
280925
+ }
280926
+ }
280927
+ } else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
280928
+ if (d.id) {
280929
+ names2.push(d.id.name);
280930
+ bindings.push(d.id.name);
280931
+ }
280932
+ }
280933
+ } else {
280934
+ for (const spec of stmt.specifiers) {
280935
+ if (spec.type !== "ExportSpecifier") continue;
280936
+ names2.push(
280937
+ spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value
280938
+ );
280939
+ if (spec.local.type === "Identifier") bindings.push(spec.local.name);
280176
280940
  }
280177
- } else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
280178
- if (d.id) names2.push(d.id.name);
280179
280941
  }
280180
280942
  if (names2.length === 0) continue;
280181
280943
  out.push({
280182
280944
  names: names2,
280945
+ bindings,
280183
280946
  start: headerPos,
280184
280947
  end: stmt.end,
280185
280948
  text: src.slice(headerPos, stmt.end)
@@ -280201,9 +280964,6 @@ function findGeneratedHeaderOffsets(src) {
280201
280964
  }
280202
280965
  return out;
280203
280966
  }
280204
- function generatedSectionNames(src) {
280205
- return new Set(collectGeneratedSectionsRaw(src).flatMap((s) => s.names));
280206
- }
280207
280967
  function handwrittenNames(src) {
280208
280968
  if (src.trim() === "") return /* @__PURE__ */ new Set();
280209
280969
  let ast;
@@ -280236,7 +280996,10 @@ function handwrittenNames(src) {
280236
280996
  collect(stmt);
280237
280997
  }
280238
280998
  }
280239
- for (const n of generatedSectionNames(src)) names2.delete(n);
280999
+ for (const s of collectGeneratedSectionsRaw(src)) {
281000
+ for (const n of s.names) names2.delete(n);
281001
+ for (const b of s.bindings) names2.delete(b);
281002
+ }
280240
281003
  return names2;
280241
281004
  }
280242
281005
  function findGeneratedSectionText(src, fn2) {
@@ -280289,32 +281052,53 @@ function tsTypeParamName(id) {
280289
281052
  if (n.length === 0) n = "T";
280290
281053
  return n;
280291
281054
  }
280292
- function wrapComplexAbs(a, typeVars) {
280293
- const ts3 = absToTSType(a, typeVars);
281055
+ function wrapComplexAbs(a, typeVars, budget) {
281056
+ const ts3 = absToTSTypeB(a, typeVars, budget);
280294
281057
  if (a.shape.k === "sum" || a.shape.k === "fn") return `(${ts3})`;
280295
281058
  return ts3;
280296
281059
  }
280297
- function wrapUnionMember(a, typeVars) {
280298
- const ts3 = absToTSType(a, typeVars);
281060
+ function wrapUnionMember(a, typeVars, budget) {
281061
+ const ts3 = absToTSTypeB(a, typeVars, budget);
280299
281062
  if (a.shape.k === "fn") return `(${ts3})`;
280300
281063
  return ts3;
280301
281064
  }
280302
- function isTsIdent(name) {
280303
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !TS_PARAM_RESERVED.has(name);
281065
+ function tsTplSafe(ts3) {
281066
+ if (ts3 === TS_TRUNC.cycle) return TS_TRUNC.cycleTpl;
281067
+ if (ts3 === TS_TRUNC.depth) return TS_TRUNC.depthTpl;
281068
+ return ts3;
280304
281069
  }
280305
281070
  function sanitizeParamName(name, index2) {
280306
- if (name.startsWith("...")) {
280307
- const rest = name.slice(3);
280308
- if (isTsIdent(rest)) return name;
281071
+ let optional = false;
281072
+ let base = name;
281073
+ if (base.endsWith("?")) {
281074
+ optional = true;
281075
+ base = base.slice(0, -1);
281076
+ }
281077
+ if (base.startsWith("...")) {
281078
+ const rest = base.slice(3);
281079
+ if (isJsBindingIdent(rest)) return `...${rest}${optional ? "?" : ""}`;
280309
281080
  return `...arg${index2}`;
280310
281081
  }
280311
- if (isTsIdent(name)) return name;
281082
+ if (isJsBindingIdent(base)) return `${base}${optional ? "?" : ""}`;
280312
281083
  return `arg${index2}`;
280313
281084
  }
280314
281085
  function formatPropKey(k) {
280315
281086
  return formatObjectKey(k);
280316
281087
  }
280317
281088
  function absToTSType(a, typeVars) {
281089
+ return absToTSTypeB(a, typeVars, new ProjectionBudget());
281090
+ }
281091
+ function absToTSTypeB(a, typeVars, budget) {
281092
+ const stop2 = budget.enter(a);
281093
+ if (stop2 === "cycle") return TS_TRUNC.cycle;
281094
+ if (stop2 === "depth") return TS_TRUNC.depth;
281095
+ try {
281096
+ return absToTSTypeInner(a, typeVars, budget);
281097
+ } finally {
281098
+ budget.exit();
281099
+ }
281100
+ }
281101
+ function absToTSTypeInner(a, typeVars, budget) {
280318
281102
  if (a.shape.k === "any" && a.term?.op === "var" && typeVars) {
280319
281103
  const mapped = typeVars.get(a.term.id);
280320
281104
  if (mapped) return mapped;
@@ -280342,7 +281126,7 @@ function absToTSType(a, typeVars) {
280342
281126
  const lvR = litValue(p);
280343
281127
  const lv = lvR.ok ? lvR.value : void 0;
280344
281128
  if (typeof lv === "string") return escapeTemplateTypeFixed(lv);
280345
- return `\${${absToTSType(p, typeVars)}}`;
281129
+ return `\${${tsTplSafe(absToTSTypeB(p, typeVars, budget))}}`;
280346
281130
  }).join("");
280347
281131
  return `\`${inner}\``;
280348
281132
  }
@@ -280356,9 +281140,9 @@ function absToTSType(a, typeVars) {
280356
281140
  return a.shape.type;
280357
281141
  case "obj": {
280358
281142
  const entries = Object.entries(a.shape.slots).map(([k, slot]) => {
280359
- const inner = absToTSType(slot.value, typeVars);
281143
+ const inner = absToTSTypeB(slot.value, typeVars, budget);
280360
281144
  if (slot.optional) {
280361
- const t = wrapComplexAbs(slot.value, typeVars);
281145
+ const t = wrapComplexAbs(slot.value, typeVars, budget);
280362
281146
  return `${formatPropKey(k)}: ${t} | undefined`;
280363
281147
  }
280364
281148
  return `${formatPropKey(k)}: ${inner}`;
@@ -280367,16 +281151,16 @@ function absToTSType(a, typeVars) {
280367
281151
  return `{ ${entries.join("; ")} }`;
280368
281152
  }
280369
281153
  case "arr":
280370
- return `${wrapComplexAbs(a.shape.element, typeVars)}[]`;
281154
+ return `${wrapComplexAbs(a.shape.element, typeVars, budget)}[]`;
280371
281155
  case "tuple": {
280372
281156
  const holes = a.shape.holes ?? [];
280373
281157
  const parts = a.shape.elements.map((e, i) => {
280374
- const ts3 = absToTSType(e, typeVars);
281158
+ const ts3 = absToTSTypeB(e, typeVars, budget);
280375
281159
  return holes.includes(i) ? `hole: ${ts3}` : ts3;
280376
281160
  });
280377
281161
  if (a.shape.rest) {
280378
281162
  const rest = a.shape.rest;
280379
- const restTs = rest.shape.k === "arr" ? absToTSType(rest, typeVars) : `${wrapComplexAbs(rest, typeVars)}[]`;
281163
+ const restTs = rest.shape.k === "arr" ? absToTSTypeB(rest, typeVars, budget) : `${wrapComplexAbs(rest, typeVars, budget)}[]`;
280380
281164
  parts.push(`...${restTs}`);
280381
281165
  }
280382
281166
  return `[${parts.join(", ")}]`;
@@ -280388,22 +281172,27 @@ function absToTSType(a, typeVars) {
280388
281172
  const name = sanitizeParamName(p, i);
280389
281173
  const pt = paramTypes?.[i];
280390
281174
  let typeStr;
280391
- if (pt) typeStr = absToTSType(pt, typeVars);
280392
- else if (isRest) typeStr = "unknown[]";
281175
+ if (pt) {
281176
+ if (isRest) {
281177
+ typeStr = pt.shape.k === "arr" ? absToTSTypeB(pt, typeVars, budget) : `${wrapComplexAbs(pt, typeVars, budget)}[]`;
281178
+ } else {
281179
+ typeStr = absToTSTypeB(pt, typeVars, budget);
281180
+ }
281181
+ } else if (isRest) typeStr = "unknown[]";
280393
281182
  else typeStr = "unknown";
280394
281183
  return `${name}: ${typeStr}`;
280395
281184
  }).join(", ");
280396
- const ret = a.shape.returnType ? absToTSType(a.shape.returnType, typeVars) : "unknown";
281185
+ const ret = a.shape.returnType ? absToTSTypeB(a.shape.returnType, typeVars, budget) : "unknown";
280397
281186
  return `(${params}) => ${ret}`;
280398
281187
  }
280399
281188
  case "brand":
280400
- return isTsIdent(a.shape.name) || /^[A-Z][A-Za-z0-9_$]*$/.test(a.shape.name) ? a.shape.name : "unknown";
281189
+ return isJsBindingIdent(a.shape.name) || /^[A-Z][A-Za-z0-9_$]*$/.test(a.shape.name) ? a.shape.name : "unknown";
280401
281190
  case "eff":
280402
281191
  if (a.shape.eff === "promise")
280403
- return `Promise<${absToTSType(a.shape.inner, typeVars)}>`;
280404
- return absToTSType(a.shape.inner, typeVars);
281192
+ return `Promise<${absToTSTypeB(a.shape.inner, typeVars, budget)}>`;
281193
+ return absToTSTypeB(a.shape.inner, typeVars, budget);
280405
281194
  case "sum": {
280406
- const parts = a.shape.members.map((m) => wrapUnionMember(m, typeVars)).filter((p) => p !== "never");
281195
+ const parts = a.shape.members.map((m) => wrapUnionMember(m, typeVars, budget)).filter((p) => p !== "never");
280407
281196
  const uniq = [...new Set(parts)];
280408
281197
  if (uniq.length === 0) return "never";
280409
281198
  if (uniq.length === 1) return uniq[0];
@@ -280419,32 +281208,47 @@ function caseArgAbs(c, i) {
280419
281208
  function caseResultAbs(c) {
280420
281209
  return c.abs;
280421
281210
  }
280422
- function widenParamAbs(a) {
281211
+ function widenParamAbs(a, budget = new ProjectionBudget()) {
281212
+ const stop2 = budget.enter(a);
281213
+ if (stop2) return a;
281214
+ try {
281215
+ return widenParamAbsInner(a, budget);
281216
+ } finally {
281217
+ budget.exit();
281218
+ }
281219
+ }
281220
+ function widenParamAbsInner(a, budget) {
280423
281221
  const s = a.shape;
280424
281222
  switch (s.k) {
280425
281223
  case "prim":
280426
281224
  return abs(s, void 0, void 0, "exact");
280427
281225
  case "tuple": {
280428
- const widened = s.elements.map(widenParamAbs);
281226
+ const widened = s.elements.map((el) => widenParamAbs(el, budget));
281227
+ const widenedRest = s.rest ? widenParamAbs(s.rest, budget) : void 0;
280429
281228
  const holes = s.holes;
280430
281229
  const first = widened[0];
280431
- if (!holes?.length && first && widened.length > 0 && widened.every((el) => absToTSType(el) === absToTSType(first))) {
281230
+ if (!holes?.length && first && widened.length > 0 && widened.every((el) => absToTSType(el) === absToTSType(first)) && (!widenedRest || absToTSType(widenedRest) === absToTSType(first))) {
280432
281231
  return abs({ k: "arr", element: first }, void 0, void 0, "exact");
280433
281232
  }
280434
281233
  return abs(
280435
- { k: "tuple", elements: widened, ...holes?.length ? { holes: [...holes] } : {} },
281234
+ {
281235
+ k: "tuple",
281236
+ elements: widened,
281237
+ ...widenedRest ? { rest: widenedRest } : {},
281238
+ ...holes?.length ? { holes: [...holes] } : {}
281239
+ },
280436
281240
  void 0,
280437
281241
  void 0,
280438
281242
  "exact"
280439
281243
  );
280440
281244
  }
280441
281245
  case "arr":
280442
- return abs({ k: "arr", element: widenParamAbs(s.element) }, void 0, void 0, "exact");
281246
+ return abs({ k: "arr", element: widenParamAbs(s.element, budget) }, void 0, void 0, "exact");
280443
281247
  case "obj": {
280444
281248
  const slots = {};
280445
281249
  for (const [k, slot] of Object.entries(s.slots)) {
280446
281250
  slots[k] = {
280447
- value: widenParamAbs(slot.value),
281251
+ value: widenParamAbs(slot.value, budget),
280448
281252
  ...slot.optional ? { optional: true } : {}
280449
281253
  };
280450
281254
  }
@@ -280452,21 +281256,21 @@ function widenParamAbs(a) {
280452
281256
  }
280453
281257
  case "eff":
280454
281258
  return abs(
280455
- { k: "eff", eff: s.eff, inner: widenParamAbs(s.inner) },
281259
+ { k: "eff", eff: s.eff, inner: widenParamAbs(s.inner, budget) },
280456
281260
  void 0,
280457
281261
  void 0,
280458
281262
  "exact"
280459
281263
  );
280460
281264
  case "brand":
280461
281265
  return abs(
280462
- { k: "brand", name: s.name, shape: widenParamAbs(s.shape) },
281266
+ { k: "brand", name: s.name, shape: widenParamAbs(s.shape, budget) },
280463
281267
  void 0,
280464
281268
  void 0,
280465
281269
  "exact"
280466
281270
  );
280467
281271
  case "sum":
280468
281272
  return abs(
280469
- { k: "sum", members: s.members.map(widenParamAbs) },
281273
+ { k: "sum", members: s.members.map((m) => widenParamAbs(m, budget)) },
280470
281274
  void 0,
280471
281275
  void 0,
280472
281276
  "exact"
@@ -280543,7 +281347,7 @@ function computeMainSignature(fn2) {
280543
281347
  let name = getParamName(fn2, i);
280544
281348
  const isRest = name.startsWith("...");
280545
281349
  const bare = isRest ? name.slice(3) : name;
280546
- if (!isTsIdent(bare)) {
281350
+ if (!isJsBindingIdent(bare)) {
280547
281351
  name = isRest ? `...arg${i}` : `arg${i}`;
280548
281352
  }
280549
281353
  if (usedNames.has(name)) {
@@ -280616,7 +281420,7 @@ function computeHofSignature(fn2) {
280616
281420
  const typeParams = [];
280617
281421
  for (const id of [...free].sort()) {
280618
281422
  let n = tsTypeParamName(id);
280619
- if (used.has(n) || TS_PARAM_RESERVED.has(n)) {
281423
+ if (used.has(n) || !isJsBindingIdent(n)) {
280620
281424
  let i = 2;
280621
281425
  while (used.has(`${n}${i}`)) i++;
280622
281426
  n = `${n}${i}`;
@@ -280745,6 +281549,7 @@ function numericBound2(p, self, allowSelfVar) {
280745
281549
  const nR = litOf(p.b);
280746
281550
  const n = nR.ok ? nR.value : void 0;
280747
281551
  if (typeof n !== "number") return "drop";
281552
+ if (!Number.isFinite(n)) return "drop";
280748
281553
  if (p.a.op === "app" && p.a.fn === "length") return "skip";
280749
281554
  if (allowSelfVar && isSelfVar2(p.a, self)) return { op: p.op, n };
280750
281555
  if (p.a.op === "app" && (p.a.fn === "get" || p.a.fn === "length")) return "skip";
@@ -280757,6 +281562,7 @@ function lengthBound3(p, self) {
280757
281562
  const nR = litOf(p.b);
280758
281563
  const n = nR.ok ? nR.value : void 0;
280759
281564
  if (typeof n !== "number") return void 0;
281565
+ if (!Number.isFinite(n)) return void 0;
280760
281566
  if (p.op === "ge") return { dir: "min", n: Math.ceil(n) };
280761
281567
  if (p.op === "gt") return { dir: "min", n: Math.floor(n) + 1 };
280762
281568
  if (p.op === "le") return { dir: "max", n: Math.floor(n) };
@@ -280817,7 +281623,7 @@ function refinementsFromPreds(preds, opts) {
280817
281623
  }
280818
281624
  dropped.push(`pred not projected: ${predToString(p)}`);
280819
281625
  }
280820
- return { refinements, ...eqLit !== void 0 ? { eqLit } : {}, dropped };
281626
+ return { refinements, ...hasEqLit ? { eqLit } : {}, hasEqLit, dropped };
280821
281627
  }
280822
281628
  function constraintToSchemaNode(c) {
280823
281629
  if (c.fn) return { k: "fn" };
@@ -280839,11 +281645,11 @@ function constraintToSchemaNode(c) {
280839
281645
  }
280840
281646
  const preds = c.preds ?? [];
280841
281647
  const kind = c.prim === "number" ? "number" : c.prim === "string" ? "string" : c.prim === "boolean" ? "boolean" : c.prim ? "other" : "other";
280842
- const { refinements, eqLit, dropped } = refinementsFromPreds(preds, {
281648
+ const { refinements, eqLit, hasEqLit, dropped } = refinementsFromPreds(preds, {
280843
281649
  kind
280844
281650
  // constraint 路径:absToConstraint 已把 self 改写成 __nudo_self__
280845
281651
  });
280846
- if (eqLit !== void 0 && (kind === "number" || kind === "string" || kind === "boolean" || !c.prim)) {
281652
+ if (hasEqLit && (kind === "number" || kind === "string" || kind === "boolean" || !c.prim)) {
280847
281653
  return { k: "lit", value: eqLit };
280848
281654
  }
280849
281655
  if (isIntFlag(c) && !refinements.some((r) => r.kind === "int")) {
@@ -280876,17 +281682,18 @@ function constraintDropped(c, prefix2 = "") {
280876
281682
  }
280877
281683
  if (n.fn) return;
280878
281684
  const kind = n.prim === "number" ? "number" : n.prim === "string" ? "string" : n.prim === "boolean" ? "boolean" : "other";
280879
- const { eqLit, dropped } = refinementsFromPreds(n.preds ?? [], {
281685
+ const { dropped } = refinementsFromPreds(n.preds ?? [], {
280880
281686
  kind
280881
281687
  });
280882
281688
  for (const d of dropped) out.push(path ? `${path}: ${d}` : d);
280883
- if (eqLit === void 0 && n.preds?.some((p) => p.op === "eq") && !isIntFlag(n)) {
280884
- }
280885
281689
  };
280886
281690
  visit2(c, prefix2);
280887
281691
  return out;
280888
281692
  }
280889
281693
  function absToSchemaNode(a) {
281694
+ return absToSchemaNodeB(a, new ProjectionBudget());
281695
+ }
281696
+ function absToSchemaNodeB(a, budget) {
280890
281697
  const dropped = [];
280891
281698
  if (a.term?.op === "lit") {
280892
281699
  const v2 = a.term.value;
@@ -280901,7 +281708,7 @@ function absToSchemaNode(a) {
280901
281708
  }
280902
281709
  const conf = a.conf;
280903
281710
  const lowConf = conf !== "exact" && conf !== "path";
280904
- const projected = absToConstraint(a);
281711
+ const projected = absToConstraint(a, budget);
280905
281712
  if (projected) {
280906
281713
  const node = constraintToSchemaNode(projected);
280907
281714
  dropped.push(...constraintDropped(projected));
@@ -280910,6 +281717,18 @@ function absToSchemaNode(a) {
280910
281717
  if (lowConf) {
280911
281718
  dropped.push(`conf=${conf}: contract projection skipped; shape-only fallback`);
280912
281719
  }
281720
+ const stop2 = budget.enter(a);
281721
+ if (stop2) {
281722
+ dropped.push(`shape truncated (${stop2}) projected as unknown`);
281723
+ return { node: { k: "unknown" }, dropped };
281724
+ }
281725
+ try {
281726
+ return absToSchemaShape(a, budget, dropped);
281727
+ } finally {
281728
+ budget.exit();
281729
+ }
281730
+ }
281731
+ function absToSchemaShape(a, budget, dropped) {
280913
281732
  const s = a.shape;
280914
281733
  switch (s.k) {
280915
281734
  case "never":
@@ -280926,12 +281745,12 @@ function absToSchemaNode(a) {
280926
281745
  }
280927
281746
  return { node: { k: "prim", type: s.type, refinements: [] }, dropped };
280928
281747
  }
280929
- const { refinements, eqLit, dropped: d } = refinementsFromPreds(leaves, {
281748
+ const { refinements, eqLit, hasEqLit, dropped: d } = refinementsFromPreds(leaves, {
280930
281749
  kind,
280931
281750
  self: a.term
280932
281751
  });
280933
281752
  dropped.push(...d);
280934
- if (eqLit !== void 0 && !Number.isNaN(eqLit)) {
281753
+ if (hasEqLit && (eqLit === void 0 || !Number.isNaN(eqLit))) {
280935
281754
  return { node: { k: "lit", value: eqLit }, dropped };
280936
281755
  }
280937
281756
  return { node: { k: "prim", type: s.type, refinements }, dropped };
@@ -280945,7 +281764,7 @@ function absToSchemaNode(a) {
280945
281764
  }
280946
281765
  const slots = [];
280947
281766
  for (const [key, slot] of Object.entries(s.slots)) {
280948
- const sub2 = absToSchemaNode(slot.value);
281767
+ const sub2 = absToSchemaNodeB(slot.value, budget);
280949
281768
  dropped.push(...sub2.dropped.map((n) => `${key}: ${n}`));
280950
281769
  slots.push({ key, node: sub2.node, ...slot.optional ? { optional: true } : {} });
280951
281770
  }
@@ -280955,7 +281774,7 @@ function absToSchemaNode(a) {
280955
281774
  if (a.pred && a.pred.op !== "true") {
280956
281775
  dropped.push(`arr pred not projected: ${predToString(a.pred)}`);
280957
281776
  }
280958
- const sub2 = absToSchemaNode(s.element);
281777
+ const sub2 = absToSchemaNodeB(s.element, budget);
280959
281778
  dropped.push(...sub2.dropped);
280960
281779
  return { node: { k: "arr", element: sub2.node }, dropped };
280961
281780
  }
@@ -280966,15 +281785,21 @@ function absToSchemaNode(a) {
280966
281785
  dropped.push(`tuple hole at ${i} not projected (slot absent \u2260 undefined)`);
280967
281786
  return { k: "unknown" };
280968
281787
  }
280969
- const sub2 = absToSchemaNode(e);
281788
+ const sub2 = absToSchemaNodeB(e, budget);
280970
281789
  dropped.push(...sub2.dropped);
280971
281790
  return sub2.node;
280972
281791
  });
280973
- return { node: { k: "tuple", elements }, dropped };
281792
+ let rest;
281793
+ if (s.rest) {
281794
+ const sub2 = absToSchemaNodeB(s.rest, budget);
281795
+ dropped.push(...sub2.dropped);
281796
+ rest = sub2.node;
281797
+ }
281798
+ return { node: { k: "tuple", elements, ...rest ? { rest } : {} }, dropped };
280974
281799
  }
280975
281800
  case "sum": {
280976
281801
  const members = s.members.map((m) => {
280977
- const sub2 = absToSchemaNode(m);
281802
+ const sub2 = absToSchemaNodeB(m, budget);
280978
281803
  dropped.push(...sub2.dropped);
280979
281804
  return sub2.node;
280980
281805
  });
@@ -280983,7 +281808,7 @@ function absToSchemaNode(a) {
280983
281808
  case "fn":
280984
281809
  return { node: { k: "fn" }, dropped };
280985
281810
  case "eff": {
280986
- const inner = absToSchemaNode(s.inner);
281811
+ const inner = absToSchemaNodeB(s.inner, budget);
280987
281812
  dropped.push(...inner.dropped);
280988
281813
  return {
280989
281814
  node: s.eff === "promise" ? { k: "promise", inner: inner.node } : inner.node,
@@ -281042,8 +281867,10 @@ function schemaNodeToZod(node) {
281042
281867
  }
281043
281868
  case "arr":
281044
281869
  return `z.array(${schemaNodeToZod(node.element)})`;
281045
- case "tuple":
281046
- return `z.tuple([${node.elements.map(schemaNodeToZod).join(", ")}])`;
281870
+ case "tuple": {
281871
+ const base = `z.tuple([${node.elements.map(schemaNodeToZod).join(", ")}])`;
281872
+ return node.rest ? `${base}.rest(${schemaNodeToZod(node.rest)})` : base;
281873
+ }
281047
281874
  case "union":
281048
281875
  return `z.union([${node.members.map(schemaNodeToZod).join(", ")}])`;
281049
281876
  case "fn":
@@ -281068,24 +281895,23 @@ function projectAbsToSchema(a, opts) {
281068
281895
  function absToSchemaSource(a, opts) {
281069
281896
  return projectAbsToSchema(a, opts).source;
281070
281897
  }
281071
- function zodExportIdent(name) {
281072
- if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) return name;
281073
- const cleaned = name.replace(/[^A-Za-z0-9_$]/g, "_");
281074
- return /^[A-Za-z_$]/.test(cleaned) ? cleaned : `_${cleaned}`;
281898
+ function zodExportIdent(name, used) {
281899
+ return toJsBindingIdent(name, used);
281075
281900
  }
281076
281901
  function formatJsObjectKey(k) {
281077
281902
  return formatObjectKey(k);
281078
281903
  }
281079
- function safeExportIdent(name) {
281080
- return zodExportIdent(name);
281904
+ function safeExportIdent(name, used) {
281905
+ return zodExportIdent(name, used);
281081
281906
  }
281082
281907
  function absToZodSchemaModule(exports, opts) {
281083
281908
  const dropped = [];
281084
281909
  const decls = [];
281910
+ const used = /* @__PURE__ */ new Set();
281085
281911
  for (const [name, abs2] of Object.entries(exports)) {
281086
281912
  const p = projectAbsToSchema(abs2, { dialect: "zod" });
281087
281913
  dropped.push(...p.dropped.map((n) => `${name}: ${n}`));
281088
- decls.push(`export const ${zodExportIdent(name)} = ${p.source};`);
281914
+ decls.push(`export const ${zodExportIdent(name, used)} = ${p.source};`);
281089
281915
  }
281090
281916
  const banner = opts?.banner ?? `// @generated by nudo export --format schema \u2014 dialect: zod
281091
281917
  // One-way lossy projection of Abs; do not edit. nudo check remains the gate.`;
@@ -281179,10 +282005,11 @@ function checkNode(node, value, path, issues) {
281179
282005
  const rec = value;
281180
282006
  for (const slot of node.slots) {
281181
282007
  const key = slot.key;
281182
- if (!(key in rec) || rec[key] === void 0) {
282008
+ if (!Object.hasOwn(rec, key)) {
281183
282009
  if (!slot.optional) pushIssue(issues, [...path, key], "required");
281184
282010
  continue;
281185
282011
  }
282012
+ if (rec[key] === void 0 && slot.optional) continue;
281186
282013
  checkNode(slot.node, rec[key], [...path, key], issues);
281187
282014
  }
281188
282015
  return;
@@ -281205,6 +282032,11 @@ function checkNode(node, value, path, issues) {
281205
282032
  node.elements.forEach((el, i) => {
281206
282033
  checkNode(el, value[i], [...path, i], issues);
281207
282034
  });
282035
+ if (node.rest) {
282036
+ for (let i = node.elements.length; i < value.length; i++) {
282037
+ checkNode(node.rest, value[i], [...path, i], issues);
282038
+ }
282039
+ }
281208
282040
  return;
281209
282041
  }
281210
282042
  case "union": {
@@ -281229,10 +282061,10 @@ function validateSchemaNode(node, value) {
281229
282061
  checkNode(node, value, [], issues);
281230
282062
  return issues.length === 0 ? { value } : { issues };
281231
282063
  }
281232
- function makeStandardSchemaSource(exportName, node, dropped) {
282064
+ function makeStandardSchemaSource(exportName, node, dropped, used) {
281233
282065
  const json = JSON.stringify(node);
281234
282066
  const notes = dropped.length > 0 ? dropped.map((d) => `// ${sanitizeCommentText(d)}`).join("\n") + "\n" : "";
281235
- return `${notes}export const ${safeExportIdent(exportName)} = {
282067
+ return `${notes}export const ${safeExportIdent(exportName, used)} = {
281236
282068
  "~standard": {
281237
282069
  version: 1,
281238
282070
  vendor: "nudo",
@@ -281247,10 +282079,11 @@ function makeStandardSchemaSource(exportName, node, dropped) {
281247
282079
  function absToStandardSchemaModule(exports, opts) {
281248
282080
  const dropped = [];
281249
282081
  const bodies = [];
282082
+ const used = /* @__PURE__ */ new Set();
281250
282083
  for (const [name, abs2] of Object.entries(exports)) {
281251
282084
  const { node, dropped: d } = absToSchemaNode(abs2);
281252
282085
  dropped.push(...d.map((n) => `${name}: ${n}`));
281253
- bodies.push(makeStandardSchemaSource(name, node, d.map((n) => `${name}: ${n}`)));
282086
+ bodies.push(makeStandardSchemaSource(name, node, d.map((n) => `${name}: ${n}`), used));
281254
282087
  }
281255
282088
  const banner = opts?.banner ?? `// @generated by nudo export --format standard \u2014 Standard Schema v1 (vendor: nudo)
281256
282089
  // One-way lossy projection of Abs; do not edit. nudo check remains the gate.`;
@@ -281460,15 +282293,85 @@ ${tag}
281460
282293
  ]
281461
282294
  };
281462
282295
  }
281463
- function wrapReturnNullable(sidecarSource, fnName) {
281464
- const fn2 = escapeRegExp22(fnName);
282296
+ function* exportSpecifierParts(src) {
282297
+ const re = /\bexport\s*\{/g;
282298
+ let m;
282299
+ while ((m = re.exec(src)) !== null) {
282300
+ const parts = [];
282301
+ let cur = "";
282302
+ let quote;
282303
+ let close = -1;
282304
+ for (let i = m.index + m[0].length; i < src.length; i++) {
282305
+ const ch = src[i];
282306
+ if (quote !== void 0) {
282307
+ cur += ch;
282308
+ if (ch === "\\") {
282309
+ if (i + 1 < src.length) cur += src[i + 1];
282310
+ i++;
282311
+ } else if (ch === quote) {
282312
+ quote = void 0;
282313
+ }
282314
+ continue;
282315
+ }
282316
+ if (ch === '"' || ch === "'") {
282317
+ quote = ch;
282318
+ cur += ch;
282319
+ } else if (ch === ",") {
282320
+ parts.push(cur);
282321
+ cur = "";
282322
+ } else if (ch === "}") {
282323
+ parts.push(cur);
282324
+ cur = "";
282325
+ close = i;
282326
+ break;
282327
+ } else {
282328
+ cur += ch;
282329
+ }
282330
+ }
282331
+ if (close === -1) break;
282332
+ re.lastIndex = close + 1;
282333
+ for (const p of parts) yield p.trim();
282334
+ }
282335
+ }
282336
+ function sidecarBindingFor(sidecarSource, exportName) {
282337
+ for (const t of exportSpecifierParts(sidecarSource)) {
282338
+ if (!t) continue;
282339
+ const asMatch = /^([\w$]+)\s+as\s+(.+)$/.exec(t);
282340
+ if (!asMatch) continue;
282341
+ const local = asMatch[1];
282342
+ let exported = asMatch[2].trim();
282343
+ if (exported.startsWith('"') || exported.startsWith("'")) {
282344
+ try {
282345
+ exported = JSON.parse(exported.replace(/^'|'$/g, '"'));
282346
+ } catch {
282347
+ continue;
282348
+ }
282349
+ }
282350
+ if (exported === exportName) return local;
282351
+ }
282352
+ return void 0;
282353
+ }
282354
+ function matchSidecarFnDecl(sidecarSource, exportName) {
282355
+ const fn2 = escapeRegExp22(exportName);
281465
282356
  const startRe = new RegExp(
281466
282357
  `(?:export\\s+const\\s+${fn2}\\s*=\\s*|(?<![\\w$])${fn2}\\s*=\\s*)fn\\s*\\(`
281467
282358
  );
281468
- const m = sidecarSource.match(startRe);
282359
+ let m = sidecarSource.match(startRe);
282360
+ if (!m || m.index === void 0) {
282361
+ const binding = sidecarBindingFor(sidecarSource, exportName);
282362
+ if (binding === void 0) return void 0;
282363
+ m = sidecarSource.match(
282364
+ new RegExp(`(?<![\\w$])${escapeRegExp22(binding)}\\s*=\\s*fn\\s*\\(`)
282365
+ );
282366
+ }
281469
282367
  if (!m || m.index === void 0) return void 0;
281470
- const start = m.index;
281471
- const open = start + m[0].lastIndexOf("(");
282368
+ return { start: m.index, openParen: m.index + m[0].lastIndexOf("(") };
282369
+ }
282370
+ function wrapReturnNullable(sidecarSource, fnName) {
282371
+ const located = matchSidecarFnDecl(sidecarSource, fnName);
282372
+ if (located === void 0) return void 0;
282373
+ const start = located.start;
282374
+ const open = located.openParen;
281472
282375
  let depth = 0;
281473
282376
  let end = -1;
281474
282377
  for (let i = open; i < sidecarSource.length; i++) {
@@ -281587,8 +282490,19 @@ function materializeAction(input) {
281587
282490
  fields = fieldNames.map((f) => ({ field: f, type: "any()", via: "read (no type evidence)" }));
281588
282491
  }
281589
282492
  const shapeText = shapeDslFromFields(fields);
281590
- const clause = `export const ${fn2} = fn({ ${paramName2}: ${shapeText} });
282493
+ const direct = isJsBindingIdent(fn2);
282494
+ const clause = direct ? `export const ${fn2} = fn({ ${paramName2}: ${shapeText} });
282495
+ ` : (() => {
282496
+ const used = new Set(
282497
+ [...(sidecarText ?? "").matchAll(/(?<![\w$])_nudo_(\d+)(?![\w$])/g)].map(
282498
+ (x) => x[0]
282499
+ )
282500
+ );
282501
+ const binding = allocNudoBinding((n) => used.has(n));
282502
+ return `const ${binding} = fn({ ${paramName2}: ${shapeText} });
282503
+ export { ${binding} as ${exportNameRepr(fn2)} };
281591
282504
  `;
282505
+ })();
281592
282506
  const next = sidecarText && sidecarText.trim().length > 0 ? sidecarText.endsWith("\n") ? sidecarText + clause : sidecarText + "\n" + clause : `// Generated by quickfix \u2014 review before accepting
281593
282507
  ${clause}`;
281594
282508
  return {
@@ -281637,17 +282551,19 @@ ${clause}`;
281637
282551
  }
281638
282552
  return void 0;
281639
282553
  }
281640
- var GENERATED_PREFIX, GENERATED_CASE_LINE_REGEX, IDENT_KEY_REGEX, NUMBER_LITERAL_REGEX, UNSAFE_STRING_CHARS, UNSAFE_KEY_CHARS, CONTROL_CHARS, DIFF_CONTEXT, GENERATED_HEADER, STRING_METHODS2, NUMBER_METHODS, ARRAY_METHODS, NameAllocator, DERIVED_HEADER, TS_PARAM_RESERVED, DIALECT_RENDERERS, CHECK_FN_SOURCE;
282554
+ var GENERATED_PREFIX, GENERATED_CASE_LINE_REGEX, IDENT_KEY_REGEX, NUMBER_LITERAL_REGEX, UNSAFE_STRING_CHARS, UNSAFE_KEY_CHARS, CONTROL_CHARS, DIFF_CONTEXT, JS_EXPORT_NAME, GENERATED_HEADER, STRING_METHODS2, NUMBER_METHODS, ARRAY_METHODS, NameAllocator, DERIVED_HEADER, TS_TRUNC, DIALECT_RENDERERS, CHECK_FN_SOURCE;
281641
282555
  var init_emit2 = __esm({
281642
282556
  "../service/dist/emit/index.js"() {
281643
282557
  "use strict";
281644
282558
  init_chunk_4TAFH6ZZ();
281645
- init_chunk_XY75TNBY();
281646
- init_chunk_RHEXBPQ6();
282559
+ init_chunk_6GFPZGQS();
282560
+ init_chunk_2RP2Q6LJ();
281647
282561
  init_src();
281648
282562
  init_src();
281649
282563
  init_internal();
282564
+ init_internal();
281650
282565
  init_src2();
282566
+ init_internal();
281651
282567
  init_src();
281652
282568
  init_internal();
281653
282569
  init_src2();
@@ -281666,6 +282582,7 @@ var init_emit2 = __esm({
281666
282582
  init_internal();
281667
282583
  init_internal();
281668
282584
  init_src();
282585
+ init_internal();
281669
282586
  GENERATED_PREFIX = "call@";
281670
282587
  GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
281671
282588
  IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
@@ -281674,6 +282591,7 @@ var init_emit2 = __esm({
281674
282591
  UNSAFE_KEY_CHARS = /[,()[\]{}:]/;
281675
282592
  CONTROL_CHARS = /[\x00-\x1f\x7f]/;
281676
282593
  DIFF_CONTEXT = 3;
282594
+ JS_EXPORT_NAME = /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u;
281677
282595
  GENERATED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo contract --emit`";
281678
282596
  STRING_METHODS2 = /* @__PURE__ */ new Set([
281679
282597
  "toLowerCase",
@@ -281740,6 +282658,10 @@ var init_emit2 = __esm({
281740
282658
  constructor(initial) {
281741
282659
  this.taken = new Set(initial ?? []);
281742
282660
  }
282661
+ /** 名字是否已被占用(绑定别名分配用) */
282662
+ has(preferred) {
282663
+ return this.taken.has(preferred);
282664
+ }
281743
282665
  claim(preferred) {
281744
282666
  if (!this.taken.has(preferred)) {
281745
282667
  this.taken.add(preferred);
@@ -281753,57 +282675,12 @@ var init_emit2 = __esm({
281753
282675
  }
281754
282676
  };
281755
282677
  DERIVED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo contract --emit`";
281756
- TS_PARAM_RESERVED = /* @__PURE__ */ new Set([
281757
- "break",
281758
- "case",
281759
- "catch",
281760
- "class",
281761
- "const",
281762
- "continue",
281763
- "debugger",
281764
- "default",
281765
- "delete",
281766
- "do",
281767
- "else",
281768
- "enum",
281769
- "export",
281770
- "extends",
281771
- "false",
281772
- "finally",
281773
- "for",
281774
- "function",
281775
- "if",
281776
- "import",
281777
- "in",
281778
- "instanceof",
281779
- "new",
281780
- "null",
281781
- "return",
281782
- "super",
281783
- "switch",
281784
- "this",
281785
- "throw",
281786
- "true",
281787
- "try",
281788
- "typeof",
281789
- "var",
281790
- "void",
281791
- "while",
281792
- "with",
281793
- "yield",
281794
- "let",
281795
- "static",
281796
- "await",
281797
- "implements",
281798
- "interface",
281799
- "package",
281800
- "private",
281801
- "protected",
281802
- "public",
281803
- "arguments",
281804
- "eval",
281805
- "constructor"
281806
- ]);
282678
+ TS_TRUNC = {
282679
+ cycle: "/* nudo:truncated:cycle */ unknown",
282680
+ depth: "/* nudo:truncated:depth */ unknown",
282681
+ cycleTpl: "/* nudo:truncated:cycle */ string",
282682
+ depthTpl: "/* nudo:truncated:depth */ string"
282683
+ };
281807
282684
  DIALECT_RENDERERS = {
281808
282685
  zod: schemaNodeToZod
281809
282686
  };
@@ -281870,10 +282747,12 @@ function __nudoCheck(node, value, path, issues) {
281870
282747
  }
281871
282748
  for (const slot of node.slots) {
281872
282749
  const key = slot.key;
281873
- if (!(key in value) || value[key] === undefined) {
282750
+ // \u7F3A\u952E\u5224\u5B9A\u53EA\u7528\u81EA\u6709\u5C5E\u6027\uFF08BUG-018/S6-003\uFF1B\u4E24\u4EFD\u5B9E\u73B0\u987B\u540C\u53E3\u5F84\uFF09
282751
+ if (!Object.hasOwn(value, key)) {
281874
282752
  if (!slot.optional) { path.push(key); push("required"); path.pop(); }
281875
282753
  continue;
281876
282754
  }
282755
+ if (value[key] === undefined && slot.optional) continue;
281877
282756
  path.push(key);
281878
282757
  __nudoCheck(slot.node, value[key], path, issues);
281879
282758
  path.pop();
@@ -281896,6 +282775,13 @@ function __nudoCheck(node, value, path, issues) {
281896
282775
  __nudoCheck(node.elements[i], value[i], path, issues);
281897
282776
  path.pop();
281898
282777
  }
282778
+ if (node.rest) {
282779
+ for (var ri = node.elements.length; ri < value.length; ri++) {
282780
+ path.push(ri);
282781
+ __nudoCheck(node.rest, value[ri], path, issues);
282782
+ path.pop();
282783
+ }
282784
+ }
281899
282785
  return;
281900
282786
  }
281901
282787
  case "union": {
@@ -282089,7 +282975,7 @@ __export(migrate_exports, {
282089
282975
  stripTsToJs: () => stripTsToJs
282090
282976
  });
282091
282977
  import {
282092
- existsSync as existsSync25,
282978
+ existsSync as existsSync26,
282093
282979
  mkdirSync as mkdirSync7,
282094
282980
  readFileSync as readFileSync28,
282095
282981
  readdirSync as readdirSync4,
@@ -282110,7 +282996,7 @@ function readJson(path) {
282110
282996
  }
282111
282997
  function writeFileAtomic(path, data) {
282112
282998
  const dir = dirname27(path);
282113
- if (!existsSync25(dir)) mkdirSync7(dir, { recursive: true });
282999
+ if (!existsSync26(dir)) mkdirSync7(dir, { recursive: true });
282114
283000
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
282115
283001
  try {
282116
283002
  writeFileSync10(tmp, data, "utf-8");
@@ -282128,7 +283014,7 @@ function writeJson(path, value) {
282128
283014
  `);
282129
283015
  }
282130
283016
  function snapshotFile(path) {
282131
- if (!existsSync25(path)) return { path, kind: "missing" };
283017
+ if (!existsSync26(path)) return { path, kind: "missing" };
282132
283018
  try {
282133
283019
  if (statSync15(path).isFile()) {
282134
283020
  return { path, kind: "file", content: readFileSync28(path, "utf-8") };
@@ -282140,7 +283026,7 @@ function snapshotFile(path) {
282140
283026
  }
282141
283027
  function restoreSnapshot(snap) {
282142
283028
  if (snap.kind === "missing") {
282143
- if (existsSync25(snap.path)) rmSync3(snap.path, { force: true });
283029
+ if (existsSync26(snap.path)) rmSync3(snap.path, { force: true });
282144
283030
  return;
282145
283031
  }
282146
283032
  if (snap.kind === "file") writeFileAtomic(snap.path, snap.content);
@@ -282237,7 +283123,7 @@ function unquoteYamlScalar(s) {
282237
283123
  }
282238
283124
  function parsePnpmWorkspacePackages(root) {
282239
283125
  const yamlPath = join19(root, "pnpm-workspace.yaml");
282240
- if (!existsSync25(yamlPath)) return [];
283126
+ if (!existsSync26(yamlPath)) return [];
282241
283127
  let text;
282242
283128
  try {
282243
283129
  text = readFileSync28(yamlPath, "utf-8");
@@ -282299,17 +283185,17 @@ function packageRoots(root) {
282299
283185
  if (w.startsWith("!")) continue;
282300
283186
  if (w.endsWith("/*") || w.endsWith("/**")) {
282301
283187
  const base = join19(root, w.replace(/\/\*\*?$/, ""));
282302
- if (!existsSync25(base)) continue;
283188
+ if (!existsSync26(base)) continue;
282303
283189
  for (const name of readdirSync4(base)) {
282304
283190
  const p = join19(base, name);
282305
- if (!existsSync25(join19(p, "package.json"))) continue;
283191
+ if (!existsSync26(join19(p, "package.json"))) continue;
282306
283192
  if (seen.has(p)) continue;
282307
283193
  seen.add(p);
282308
283194
  roots.push(p);
282309
283195
  }
282310
283196
  } else {
282311
283197
  const p = join19(root, w);
282312
- if (!existsSync25(join19(p, "package.json"))) continue;
283198
+ if (!existsSync26(join19(p, "package.json"))) continue;
282313
283199
  if (seen.has(p)) continue;
282314
283200
  seen.add(p);
282315
283201
  roots.push(p);
@@ -282537,7 +283423,7 @@ function findWorkflowDir(from) {
282537
283423
  let cur = resolve19(from);
282538
283424
  for (let i = 0; i < 6; i++) {
282539
283425
  const wf = join19(cur, ".github", "workflows");
282540
- if (existsSync25(wf) && statSync15(wf).isDirectory()) return wf;
283426
+ if (existsSync26(wf) && statSync15(wf).isDirectory()) return wf;
282541
283427
  const parent = dirname27(cur);
282542
283428
  if (parent === cur) break;
282543
283429
  cur = parent;
@@ -282583,7 +283469,7 @@ function rewriteWorkflowsNear(from, opts = {}) {
282583
283469
  }
282584
283470
  function resolveMigrateRoot(rootDir) {
282585
283471
  const raw = resolve19(rootDir);
282586
- if (!existsSync25(raw)) {
283472
+ if (!existsSync26(raw)) {
282587
283473
  throw new Error(`not found: ${rootDir}`);
282588
283474
  }
282589
283475
  return statSync15(raw).isDirectory() ? raw : dirname27(raw);
@@ -282607,9 +283493,9 @@ function migrateStatus(rootDir) {
282607
283493
  ...pkg?.devDependencies ?? {}
282608
283494
  };
282609
283495
  const markerPath = join19(r, ".nudo", "migrate-retired.json");
282610
- const retired = existsSync25(markerPath) && statSync15(markerPath).isFile();
283496
+ const retired = existsSync26(markerPath) && statSync15(markerPath).isFile();
282611
283497
  const retiringPath = join19(r, ".nudo", "migrate-retiring.json");
282612
- const retiring = !retired && existsSync25(retiringPath) && statSync15(retiringPath).isFile();
283498
+ const retiring = !retired && existsSync26(retiringPath) && statSync15(retiringPath).isFile();
282613
283499
  const blockers = [];
282614
283500
  if (retiring) {
282615
283501
  blockers.push(
@@ -282630,9 +283516,9 @@ function migrateStatus(rootDir) {
282630
283516
  }
282631
283517
  rows.push({
282632
283518
  root: relative12(process.cwd(), r) || ".",
282633
- packageJson: existsSync25(pkgPath),
283519
+ packageJson: existsSync26(pkgPath),
282634
283520
  typescriptDep: Boolean(deps.typescript),
282635
- tsconfig: existsSync25(join19(r, "tsconfig.json")),
283521
+ tsconfig: existsSync26(join19(r, "tsconfig.json")),
282636
283522
  tscScripts,
282637
283523
  tsFiles,
282638
283524
  tsxFiles,
@@ -282672,7 +283558,7 @@ async function migrateStrip(paths, opts = {}) {
282672
283558
  const files = [];
282673
283559
  for (const p of paths) {
282674
283560
  const abs2 = resolve19(p);
282675
- if (!existsSync25(abs2)) {
283561
+ if (!existsSync26(abs2)) {
282676
283562
  throw new Error(`not found: ${p}`);
282677
283563
  }
282678
283564
  if (statSync15(abs2).isDirectory()) {
@@ -282696,9 +283582,9 @@ async function migrateStrip(paths, opts = {}) {
282696
283582
  const outFile = TSX_EXT.has(ext) ? file.replace(/\.tsx$/i, ".jsx") : file.replace(/\.tsx$/i, ".js").replace(/\.mts$/i, ".mjs").replace(/\.cts$/i, ".cjs").replace(/\.ts$/i, ".js");
282697
283583
  const wouldClobberNonTs = outFile === file && !isStripSource(file);
282698
283584
  if (opts.write && !wouldClobberNonTs) {
282699
- if (opts.backup && existsSync25(file) && !existsSync25(`${file}.bak`)) {
283585
+ if (opts.backup && existsSync26(file) && !existsSync26(`${file}.bak`)) {
282700
283586
  renameSync4(file, `${file}.bak`);
282701
- } else if (opts.backup !== true && existsSync25(file) && extname(file).toLowerCase() !== extname(outFile).toLowerCase()) {
283587
+ } else if (opts.backup !== true && existsSync26(file) && extname(file).toLowerCase() !== extname(outFile).toLowerCase()) {
282702
283588
  }
282703
283589
  writeFileSync10(outFile, code2, "utf-8");
282704
283590
  }
@@ -282708,7 +283594,7 @@ async function migrateStrip(paths, opts = {}) {
282708
283594
  const draft = await draftInterface(outFile, { source: code2, bodyAccesses: true });
282709
283595
  if (draft.draftSource && draft.draftSource.trim().length > 0) {
282710
283596
  const sc = sidecarPathOf(outFile);
282711
- if (!existsSync25(sc)) {
283597
+ if (!existsSync26(sc)) {
282712
283598
  mkdirSync7(dirname27(sc), { recursive: true });
282713
283599
  writeFileSync10(sc, draft.draftSource, "utf-8");
282714
283600
  sidecarDraft = sc;
@@ -282744,11 +283630,11 @@ function runTscBaseline(root, files) {
282744
283630
  }
282745
283631
  async function migrateVerify(paths, opts = {}) {
282746
283632
  const { checkSource: checkSource2, formatCheckReport: formatCheckReport2, pTrue: pTrue3 } = await Promise.resolve().then(() => (init_src(), src_exports));
282747
- const { defaultLoadModule: defaultLoadModule3, findProjectConfig: findProjectConfig3, interfaceConfig: interfaceConfig3, checkConfig: checkConfig2 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
283633
+ const { collectSkipReturns: collectSkipReturns2, defaultLoadModule: defaultLoadModule3, findProjectConfig: findProjectConfig3, interfaceConfig: interfaceConfig3, checkConfig: checkConfig2 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
282748
283634
  const files = [];
282749
283635
  for (const p of paths) {
282750
283636
  const abs2 = resolve19(p);
282751
- if (!existsSync25(abs2)) {
283637
+ if (!existsSync26(abs2)) {
282752
283638
  throw new Error(`not found: ${p}`);
282753
283639
  }
282754
283640
  if (statSync15(abs2).isDirectory()) {
@@ -282774,7 +283660,9 @@ async function migrateVerify(paths, opts = {}) {
282774
283660
  ...autoBind === false ? { autoBind: false } : {},
282775
283661
  ...proj?.projectDir ? { projectDir: proj.projectDir } : {},
282776
283662
  entryThrows: cCfg.entryThrows,
282777
- ...cCfg.ignoreThrows && cCfg.ignoreThrows.length > 0 ? { ignoreThrows: cCfg.ignoreThrows } : {}
283663
+ ...cCfg.ignoreThrows && cCfg.ignoreThrows.length > 0 ? { ignoreThrows: cCfg.ignoreThrows } : {},
283664
+ // 与 nudo check 同源:@nudo:skip 的函数不评估 body(verify 的 ok 必须等于 check)
283665
+ skips: collectSkipReturns2(source)
282778
283666
  });
282779
283667
  const summary = `${report.summary.errors} error \xB7 ${report.summary.warnings} warning \xB7 ${report.summary.functions} fn`;
282780
283668
  const row = {
@@ -282843,7 +283731,7 @@ function migrateRetire(rootDir, opts = {}) {
282843
283731
  mkdirSync7(markerDir, { recursive: true });
282844
283732
  commitWriteSet(root, ops);
282845
283733
  const retiring = join19(markerDir, "migrate-retiring.json");
282846
- if (existsSync25(retiring)) rmSync3(retiring, { force: true });
283734
+ if (existsSync26(retiring)) rmSync3(retiring, { force: true });
282847
283735
  }
282848
283736
  return {
282849
283737
  root: relative12(process.cwd(), root) || ".",
@@ -283026,6 +283914,21 @@ function usageError(message, fix) {
283026
283914
  console.error(message);
283027
283915
  console.error(`fix: ${fix}`);
283028
283916
  }
283917
+ function displayPathOf(p) {
283918
+ const rel = relative7(process.cwd(), p);
283919
+ return rel === "" || rel.startsWith("..") ? p : rel;
283920
+ }
283921
+ function variadicSwallowError(command, flags) {
283922
+ const list = flags.join(", ");
283923
+ const first = flags[0] ?? "--from";
283924
+ console.error(
283925
+ `error: \`nudo ${command}\` received no paths \u2014 variadic flag(s) ${list} consumed the following arguments`
283926
+ );
283927
+ console.error(
283928
+ `fix: separate variadic flags from paths with \`--\` (e.g. \`nudo ${command} ${first} a.js -- b.js\`) or list paths before the flags`
283929
+ );
283930
+ process.exitCode = 1;
283931
+ }
283029
283932
  function reportPathErrors(errors) {
283030
283933
  for (const e of errors) usageError(e.message, e.suggestion);
283031
283934
  if (errors.length > 0) process.exitCode = 1;
@@ -283213,7 +284116,8 @@ function startWatch(paths, runOne, label) {
283213
284116
  console.log(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Watching for changes...`);
283214
284117
  graph = buildModuleGraph(getFiles());
283215
284118
  };
283216
- runAll().catch(() => {
284119
+ runAll().catch((err) => {
284120
+ console.error(`nudo ${label} failed to start: ${err.message}`);
283217
284121
  });
283218
284122
  let debounceTimer = null;
283219
284123
  const watchTarget = isDir ? primary : dirname21(primary);
@@ -283432,6 +284336,7 @@ async function runCheck(file, opts = {}) {
283432
284336
  let cachedJson;
283433
284337
  let algebraReport;
283434
284338
  let mockFromErrors = [];
284339
+ let injectionSetupFailed = false;
283435
284340
  if (cached2) {
283436
284341
  cachedJson = cached2;
283437
284342
  algebraReport = reportFromCachedJson(cached2);
@@ -283478,7 +284383,12 @@ async function runCheck(file, opts = {}) {
283478
284383
  ...reps.targets.length > 0 ? { replacements: reps.values, replacementTargets: reps.targets } : {},
283479
284384
  ...reps.asTargets.length > 0 ? { asOverrides: reps.asValues, asOverrideTargets: reps.asTargets } : {}
283480
284385
  };
283481
- } catch {
284386
+ } catch (err) {
284387
+ console.error(
284388
+ `error: eval injection setup failed: ${err.message}`
284389
+ );
284390
+ injectionSetupFailed = true;
284391
+ if (opts.gateFlags) opts.gateFlags.injectionSetupFailed = true;
283482
284392
  }
283483
284393
  algebraReport = checkSource2(filePath, source, pTrue3, {
283484
284394
  loadModule,
@@ -283596,31 +284506,32 @@ async function runCheck(file, opts = {}) {
283596
284506
  })) {
283597
284507
  printDocsLinks(algebraReport.issues);
283598
284508
  }
283599
- if (useDisk && cacheKey && !cached2 && !opts.abs) {
284509
+ if (useDisk && cacheKey && !cached2 && !opts.abs && !injectionSetupFailed) {
283600
284510
  try {
283601
284511
  disk.set(cacheKey, serializeCheckJson2(algebraReport));
283602
284512
  } catch {
283603
284513
  }
283604
284514
  }
283605
284515
  if (opts.json && !opts.jsonCollect) {
283606
- process.exitCode = checkJson.ok ? 0 : 1;
283607
- } else if (!opts.jsonCollect && !algebraReport.ok) {
284516
+ process.exitCode = checkJson.ok && !injectionSetupFailed ? 0 : 1;
284517
+ } else if (!opts.jsonCollect && (!algebraReport.ok || injectionSetupFailed)) {
283608
284518
  process.exitCode = 1;
283609
284519
  }
283610
284520
  }
283611
284521
  async function runCheckFix(opts) {
283612
284522
  const core = await Promise.resolve().then(() => (init_src(), src_exports));
283613
284523
  const { checkSource: checkSource2, pTrue: pTrue3, sidecarPathOf: sidecarPathOf2, actionsForIssue: actionsForIssue2 } = core;
283614
- const { collectSkipReturns: collectSkipReturns2, defaultLoadModule: defaultLoadModule3 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
284524
+ const { collectSkipReturns: collectSkipReturns2, defaultLoadModule: defaultLoadModule3, findProjectConfig: findProjectConfig3, checkConfig: checkConfig2 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
283615
284525
  const {
283616
284526
  materializeAction: materializeAction2,
283617
284527
  applyTextEdits: applyTextEdits2,
283618
284528
  unifiedDiff: unifiedDiff2
283619
284529
  } = await Promise.resolve().then(() => (init_emit2(), emit_exports));
283620
- const { writeFileSync: writeFileSync11, readFileSync: readFileSync29, existsSync: existsSync26 } = await import("fs");
284530
+ const { writeFileSync: writeFileSync11, readFileSync: readFileSync29, existsSync: existsSync27 } = await import("fs");
283621
284531
  const only = opts.only && opts.only.length > 0 ? new Set(opts.only) : void 0;
283622
284532
  let planned = 0;
283623
284533
  let written = 0;
284534
+ let residualErrors = 0;
283624
284535
  for (const file of opts.targets) {
283625
284536
  let source;
283626
284537
  try {
@@ -283628,17 +284539,26 @@ async function runCheckFix(opts) {
283628
284539
  } catch {
283629
284540
  continue;
283630
284541
  }
284542
+ const proj = findProjectConfig3(dirname24(file));
284543
+ const cCfg = checkConfig2(proj?.config);
284544
+ const gate = checkGateFromConfig(proj?.config);
284545
+ const entryThrows = resolveEntryThrows(
284546
+ { entryThrows: opts.entryThrows, profile: opts.profile },
284547
+ gate,
284548
+ cCfg.entryThrows
284549
+ );
284550
+ const ignoreThrows = mergeIgnoreThrows(opts.ignoreThrows, cCfg.ignoreThrows);
283631
284551
  const report = checkSource2(file, source, pTrue3, {
283632
284552
  loadModule: defaultLoadModule3,
283633
284553
  fromFile: file,
283634
- ...opts.ignoreThrows ? { ignoreThrows: opts.ignoreThrows } : {},
283635
- ...opts.entryThrows ? { entryThrows: opts.entryThrows } : {},
284554
+ ...ignoreThrows.length > 0 ? { ignoreThrows } : {},
284555
+ entryThrows,
283636
284556
  skips: collectSkipReturns2(source)
283637
284557
  });
283638
284558
  const sidecar = sidecarPathOf2(file);
283639
284559
  let sidecarText;
283640
284560
  try {
283641
- sidecarText = existsSync26(sidecar) ? readFileSync29(sidecar, "utf-8") : void 0;
284561
+ sidecarText = existsSync27(sidecar) ? readFileSync29(sidecar, "utf-8") : void 0;
283642
284562
  } catch {
283643
284563
  sidecarText = void 0;
283644
284564
  }
@@ -283699,6 +284619,16 @@ async function runCheckFix(opts) {
283699
284619
  }
283700
284620
  }
283701
284621
  }
284622
+ if (report.issues.some((i) => i.severity === "error")) {
284623
+ const post = checkSource2(file, source, pTrue3, {
284624
+ loadModule: defaultLoadModule3,
284625
+ fromFile: file,
284626
+ ...ignoreThrows.length > 0 ? { ignoreThrows } : {},
284627
+ entryThrows,
284628
+ skips: collectSkipReturns2(source)
284629
+ });
284630
+ residualErrors += post.issues.filter((i) => i.severity === "error").length;
284631
+ }
283702
284632
  }
283703
284633
  const mode = opts.write ? "written" : "planned (dry-run)";
283704
284634
  console.log(
@@ -283710,9 +284640,13 @@ check --fix: ${planned} edit(s) ${mode}${opts.write ? `, ${written} applied` : "
283710
284640
  "hint: only [fix]/[adjust] are auto-applied; [silence]/[review] need a human (or LSP quickfix)"
283711
284641
  );
283712
284642
  }
284643
+ if (residualErrors > 0) {
284644
+ console.log(`check --fix: ${residualErrors} residual error(s) remain \u2014 gate stays red`);
284645
+ }
284646
+ return { planned, written, residualErrors };
283713
284647
  }
283714
284648
  function registerCheckCommand(program3) {
283715
- program3.command("check").description("Gate contracts + entry throws; print signatures (CI). Day 0 observation lives here.").argument("<paths...>", "File(s) or directory(s) to check").option("--watch, -w", "Watch files and re-run check on change").option("--json", "Emit stable CheckJson (1 file) or CheckJsonMulti envelope (N files) for CI / Agent").option("--gha", "GitHub Actions inline annotations (::error/::warning). Auto when GITHUB_ACTIONS=true").option("--gitlab", "GitLab Code Quality JSON array (write as gl-code-quality-report.json)").option("--verbose", "Expand Abs signatures (term/pred/conf detail)").option("--abs", "Algebra face: term/pred/conf per function").option("--fn <name>", "With --abs: only this function").option("--assume <pred...>", "With --abs: assume constraints, e.g. x>0 y>=1").option("--generalize", "With --abs: polymorphic signatures via symbolic execution").option(
284649
+ program3.command("check").description("Gate contracts + entry throws; print signatures (CI). Day 0 observation lives here.").argument("[paths...]", "File(s) or directory(s) to check").option("--watch, -w", "Watch files and re-run check on change").option("--json", "Emit stable CheckJson (1 file) or CheckJsonMulti envelope (N files) for CI / Agent").option("--gha", "GitHub Actions inline annotations (::error/::warning). Auto when GITHUB_ACTIONS=true").option("--gitlab", "GitLab Code Quality JSON array (write as gl-code-quality-report.json)").option("--verbose", "Expand Abs signatures (term/pred/conf detail)").option("--abs", "Algebra face: term/pred/conf per function").option("--fn <name>", "With --abs: only this function").option("--assume <pred...>", "With --abs: assume constraints, e.g. x>0 y>=1").option("--generalize", "With --abs: polymorphic signatures via symbolic execution").option(
283716
284650
  "--from <paths...>",
283717
284651
  "Usage-site files: inject call records for cross-file domain evidence"
283718
284652
  ).option(
@@ -283729,6 +284663,28 @@ function registerCheckCommand(program3) {
283729
284663
  "AI3: assume `name:type` bindings (e.g. raw:string) and report --target"
283730
284664
  ).option("--target <name>", "With --what-if: binding name whose inferred type to print").option("--fix", "Materialize one fix/adjust edit per issue (default --dry-run: print unified diff)").option("--only <codes...>", "With --fix: only these diagnostic codes (e.g. nudo:entry-may-throw)").option("--write", "With --fix: apply edits to disk (default dry-run)").option("--dry-run", "With --fix: print diffs only (default; kept for explicitness)").action(
283731
284665
  async (paths, opts) => {
284666
+ if (paths.length === 0) {
284667
+ const swallowed = [
284668
+ ...opts.from?.length ? ["--from"] : [],
284669
+ ...opts.assume?.length ? ["--assume"] : [],
284670
+ ...opts.whatIf?.length ? ["--what-if"] : [],
284671
+ ...opts.only?.length ? ["--only"] : []
284672
+ ];
284673
+ if (swallowed.length > 0) {
284674
+ variadicSwallowError("check", swallowed);
284675
+ return;
284676
+ }
284677
+ console.error("Usage error: `nudo check` needs at least one path.");
284678
+ process.exitCode = 1;
284679
+ return;
284680
+ }
284681
+ if (opts.fix && (opts.json || opts.abs || opts.gha || opts.gitlab || opts.watch || opts.verbose || opts.from && opts.from.length > 0 || opts.whatIf && opts.whatIf.length > 0)) {
284682
+ console.error(
284683
+ "error: --fix cannot be combined with --json/--abs/--gha/--gitlab/--watch/--verbose/--from/--what-if (materialization face; the CI gate is plain `nudo check`)"
284684
+ );
284685
+ process.exitCode = 1;
284686
+ return;
284687
+ }
283732
284688
  if (opts.whatIf && opts.whatIf.length > 0) {
283733
284689
  const targets2 = [];
283734
284690
  for (const p of paths) targets2.push(...resolveTargets(p));
@@ -283786,7 +284742,7 @@ function registerCheckCommand(program3) {
283786
284742
  const entryThrows = isEntryThrowsMode(opts.entryThrows) ? opts.entryThrows : void 0;
283787
284743
  const profile = isGateProfile(opts.profile) ? opts.profile : void 0;
283788
284744
  if (opts.fix) {
283789
- await runCheckFix({
284745
+ const fix = await runCheckFix({
283790
284746
  targets,
283791
284747
  only: opts.only,
283792
284748
  write: opts.write === true,
@@ -283794,6 +284750,9 @@ function registerCheckCommand(program3) {
283794
284750
  entryThrows,
283795
284751
  profile
283796
284752
  });
284753
+ if (fix.residualErrors > 0) {
284754
+ process.exitCode = 1;
284755
+ }
283797
284756
  return;
283798
284757
  }
283799
284758
  const shared = {
@@ -283817,13 +284776,14 @@ function registerCheckCommand(program3) {
283817
284776
  const wantMulti = targets.length > 1 || allPathErrors.length > 0;
283818
284777
  if (wantMulti) {
283819
284778
  const collected = [];
284779
+ const gateFlags = {};
283820
284780
  for (const t of targets) {
283821
- await runCheck(t, { ...shared, json: true, jsonCollect: collected });
284781
+ await runCheck(t, { ...shared, json: true, jsonCollect: collected, gateFlags });
283822
284782
  }
283823
284783
  const { serializeCheckJsonMulti: multi } = await Promise.resolve().then(() => (init_src(), src_exports));
283824
284784
  const envelope = attachPathErrors(multi(collected), allPathErrors);
283825
284785
  console.log(JSON.stringify(envelope, null, 2));
283826
- process.exitCode = envelope.ok ? 0 : 1;
284786
+ process.exitCode = envelope.ok && !gateFlags.injectionSetupFailed ? 0 : 1;
283827
284787
  return;
283828
284788
  }
283829
284789
  if (targets.length === 1) {
@@ -284046,13 +285006,13 @@ async function runTest(file, opts = {}) {
284046
285006
  if (report.failed > 0 || pathErrors.length > 0) process.exitCode = 1;
284047
285007
  else process.exitCode = 0;
284048
285008
  } else if (opts.abs) {
284049
- const report = buildTestReport(filePath, result);
285009
+ const report = buildTestReport(displayPathOf(filePath), result);
284050
285010
  await runAbsView(filePath, {});
284051
285011
  console.log("");
284052
285012
  console.log(formatTestReport(report));
284053
285013
  if (report.failed > 0) process.exitCode = 1;
284054
285014
  } else {
284055
- const report = buildTestReport(filePath, result);
285015
+ const report = buildTestReport(displayPathOf(filePath), result);
284056
285016
  console.log(formatTestReport(report));
284057
285017
  if (report.failed > 0) process.exitCode = 1;
284058
285018
  }
@@ -284091,8 +285051,17 @@ freeze \u2192 ${relPath} (${directiveCount} directive(s) across ${emitOut.writte
284091
285051
  }
284092
285052
  }
284093
285053
  function registerTestCommand(program3) {
284094
- program3.command("test").description("Report every inferred case (call@/entry@ + debug witnesses); assert declared expectations").argument("<paths...>", "File(s) or directory(s)").option("--watch, -w", "Watch files and re-run test on change").option("--from <paths...>", "Usage-site files whose calls become synthesized cases").option("--freeze [mode]", "Solidify call-site witnesses as @nudo:case (mode: update | add; add is the default when the value is omitted)").option("--dry-run", "With --freeze: print a unified diff instead of writing").option("--exit-on-diff", "With --freeze --dry-run: exit 1 when the diff is non-empty").option("--json", "Output case facts as JSON (single file)").option("--abs", "Algebra face via check --abs").action(
285054
+ program3.command("test").description("Report every inferred case (call@/entry@ + debug witnesses); assert declared expectations").argument("[paths...]", "File(s) or directory(s)").option("--watch, -w", "Watch files and re-run test on change").option("--from <paths...>", "Usage-site files whose calls become synthesized cases").option("--freeze [mode]", "Solidify call-site witnesses as @nudo:case (mode: update | add; add is the default when the value is omitted)").option("--dry-run", "With --freeze: print a unified diff instead of writing").option("--exit-on-diff", "With --freeze --dry-run: exit 1 when the diff is non-empty").option("--json", "Output case facts as JSON (single file)").option("--abs", "Algebra face via check --abs").action(
284095
285055
  async (paths, opts) => {
285056
+ if (paths.length === 0) {
285057
+ if (opts.from?.length) {
285058
+ variadicSwallowError("test", ["--from"]);
285059
+ return;
285060
+ }
285061
+ console.error("Usage error: `nudo test` needs at least one path.");
285062
+ process.exitCode = 1;
285063
+ return;
285064
+ }
284096
285065
  const targets = [];
284097
285066
  const pathErrors = [];
284098
285067
  for (const p of paths) {
@@ -284481,6 +285450,10 @@ function registerContractCommand(program3) {
284481
285450
  return;
284482
285451
  }
284483
285452
  if (paths.length === 0) {
285453
+ if (opts.from?.length) {
285454
+ variadicSwallowError("contract", ["--from"]);
285455
+ return;
285456
+ }
284484
285457
  console.error(
284485
285458
  "Usage error: `nudo contract` needs at least one path. " + (opts.emit ? "Writing filters: --fn <names> / --all (default: only refresh existing @generated segments)." : opts.draft ? "Draft mode: pass a file or directory." : "Print-only: pass a file or directory.")
284486
285459
  );
@@ -284572,11 +285545,11 @@ function registerContractCommand(program3) {
284572
285545
  }
284573
285546
 
284574
285547
  // src/commands/export.ts
285548
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync9, mkdirSync as mkdirSync6 } from "fs";
285549
+ import { resolve as resolve17, relative as relative10, join as join18, basename as basename8 } from "path";
284575
285550
  init_src();
284576
285551
  init_emit2();
284577
285552
  init_src4();
284578
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync9, mkdirSync as mkdirSync6 } from "fs";
284579
- import { resolve as resolve17, relative as relative10, join as join18, basename as basename8 } from "path";
284580
285553
 
284581
285554
  // src/export-format.ts
284582
285555
  var EXPORT_FORMATS = ["schema", "standard", "guard", "dts", "all"];
@@ -284704,34 +285677,50 @@ ${mod2.source}`);
284704
285677
  const schemaModule = Object.keys(schemaExports).length > 0 ? absToZodSchemaModule(schemaExports) : void 0;
284705
285678
  if (output) {
284706
285679
  const outDir = resolve17(output);
284707
- mkdirSync6(outDir, { recursive: true });
285680
+ const ioError = (op) => ({
285681
+ path: outDir,
285682
+ code: "nudo:path-io",
285683
+ message: `export --out ${op} failed: ${displayPathOf(outDir)}`,
285684
+ suggestion: `check --out <dir> is writable and the path is a directory, then re-run nudo export`
285685
+ });
285686
+ try {
285687
+ mkdirSync6(outDir, { recursive: true });
285688
+ } catch {
285689
+ reportPathErrors([ioError("mkdir")]);
285690
+ return;
285691
+ }
284708
285692
  const written = [];
284709
- if (schemaModule) {
284710
- const p = join18(outDir, schemaFileName(stem, effectiveDialect));
284711
- writeFileSync9(p, schemaModule.source, "utf-8");
284712
- written.push(p);
284713
- }
284714
- if (standardChunks.length > 0) {
284715
- for (const chunk of standardChunks) {
284716
- const m = chunk.match(/\/\/ === (\S+) Standard Schema ===/);
284717
- const fnStem = m?.[1] ?? stem;
284718
- const p = join18(outDir, `${fnStem}.nudo.standard.ts`);
284719
- const header = `// @generated by nudo export --format standard \u2014 Standard Schema v1 (vendor: nudo)
285693
+ try {
285694
+ if (schemaModule) {
285695
+ const p = join18(outDir, schemaFileName(stem, effectiveDialect));
285696
+ writeFileSync9(p, schemaModule.source, "utf-8");
285697
+ written.push(p);
285698
+ }
285699
+ if (standardChunks.length > 0) {
285700
+ for (const chunk of standardChunks) {
285701
+ const m = chunk.match(/\/\/ === (\S+) Standard Schema ===/);
285702
+ const fnStem = m?.[1] ?? stem;
285703
+ const p = join18(outDir, `${fnStem}.nudo.standard.ts`);
285704
+ const header = `// @generated by nudo export --format standard \u2014 Standard Schema v1 (vendor: nudo)
284720
285705
  `;
284721
- const body = chunk.startsWith("\n") ? chunk.slice(1) : chunk;
284722
- writeFileSync9(p, body.includes("@generated") ? body + "\n" : header + body + "\n", "utf-8");
285706
+ const body = chunk.startsWith("\n") ? chunk.slice(1) : chunk;
285707
+ writeFileSync9(p, body.includes("@generated") ? body + "\n" : header + body + "\n", "utf-8");
285708
+ written.push(p);
285709
+ }
285710
+ }
285711
+ if (guardChunks.length > 0) {
285712
+ const p = join18(outDir, `${stem}.nudo.guard.ts`);
285713
+ writeFileSync9(p, guardChunks.join("\n") + "\n", "utf-8");
284723
285714
  written.push(p);
284724
285715
  }
284725
- }
284726
- if (guardChunks.length > 0) {
284727
- const p = join18(outDir, `${stem}.nudo.guard.ts`);
284728
- writeFileSync9(p, guardChunks.join("\n") + "\n", "utf-8");
284729
- written.push(p);
284730
- }
284731
- if (dtsChunks.length > 0) {
284732
- const p = join18(outDir, `${stem}.d.ts`);
284733
- writeFileSync9(p, dtsChunks.join("\n") + "\n", "utf-8");
284734
- written.push(p);
285716
+ if (dtsChunks.length > 0) {
285717
+ const p = join18(outDir, `${stem}.d.ts`);
285718
+ writeFileSync9(p, dtsChunks.join("\n") + "\n", "utf-8");
285719
+ written.push(p);
285720
+ }
285721
+ } catch {
285722
+ reportPathErrors([ioError("write")]);
285723
+ return;
284735
285724
  }
284736
285725
  for (const p of written) {
284737
285726
  console.log(`wrote ${relative10(process.cwd(), p)}`);
@@ -284776,21 +285765,30 @@ function registerExportCommand(program3) {
284776
285765
  process.exitCode = 1;
284777
285766
  return;
284778
285767
  }
284779
- await runExport(file, format, options.out, dialect);
285768
+ const { targets, errors } = resolveTargetsCollect(file);
285769
+ if (errors.length > 0) {
285770
+ reportPathErrors(errors);
285771
+ return;
285772
+ }
285773
+ if (targets.length !== 1) {
285774
+ console.error(
285775
+ `error: export takes exactly one analysis file (got ${targets.length} from ${file})`
285776
+ );
285777
+ console.error("fix: pass the single .js/.mjs/.ts file to project");
285778
+ process.exitCode = 1;
285779
+ return;
285780
+ }
285781
+ await runExport(targets[0], format, options.out, dialect);
284780
285782
  }
284781
285783
  );
284782
285784
  }
284783
285785
 
284784
285786
  // src/commands/health.ts
284785
285787
  init_src4();
284786
- import { readFileSync as readFileSync27, existsSync as existsSync24, statSync as statSync14 } from "fs";
284787
- import { resolve as resolve18, dirname as dirname26, relative as relative11 } from "path";
284788
- var displayPath = (p) => {
284789
- const rel = relative11(process.cwd(), p);
284790
- return rel === "" || rel.startsWith("..") ? p : rel;
284791
- };
285788
+ import { readFileSync as readFileSync27, existsSync as existsSync25, statSync as statSync14 } from "fs";
285789
+ import { resolve as resolve18, dirname as dirname26 } from "path";
284792
285790
  async function healthFile(filePath, records) {
284793
- const report = { file: displayPath(filePath), functions: 0, entryOnly: 0, uncovered: [] };
285791
+ const report = { file: displayPathOf(filePath), functions: 0, entryOnly: 0, uncovered: [] };
284794
285792
  let source;
284795
285793
  try {
284796
285794
  source = readFileSync27(filePath, "utf-8");
@@ -284859,13 +285857,16 @@ async function countInterfaceDrift(filePath) {
284859
285857
  }
284860
285858
  if (!/@generated/.test(scSrc)) return { count: 0 };
284861
285859
  try {
284862
- const { findProjectConfig: findProjectConfig3, interfaceConfig: interfaceConfig3 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
285860
+ const { collectSkipReturns: collectSkipReturns2, findProjectConfig: findProjectConfig3, interfaceConfig: interfaceConfig3 } = await Promise.resolve().then(() => (init_src4(), src_exports4));
284863
285861
  const proj = findProjectConfig3(dirname26(abs2));
284864
285862
  const autoBind = interfaceConfig3(proj?.config).autoBind;
284865
- const r = checkSource2(abs2, rf(abs2, "utf-8"), pTrue3, {
285863
+ const source = rf(abs2, "utf-8");
285864
+ const r = checkSource2(abs2, source, pTrue3, {
284866
285865
  loadModule: defaultLoadModule3,
284867
285866
  fromFile: abs2,
284868
- ...autoBind === false ? { autoBind: false } : {}
285867
+ ...autoBind === false ? { autoBind: false } : {},
285868
+ // 与 nudo check 同源:@nudo:skip 的函数不评估 body,不产出 drift 候选
285869
+ skips: collectSkipReturns2(source)
284869
285870
  });
284870
285871
  return { count: r.issues.filter((i) => i.code === "nudo:interface-drift").length };
284871
285872
  } catch (e) {
@@ -284874,13 +285875,14 @@ async function countInterfaceDrift(filePath) {
284874
285875
  }
284875
285876
  async function runHealth(paths, opts) {
284876
285877
  const targetPaths = paths.length > 0 ? paths : ["."];
284877
- const externalRecords = opts.from?.length ? collectExternalRecords(opts.from) : void 0;
285878
+ const fromErrors = [];
285879
+ const externalRecords = opts.from?.length ? collectExternalRecords(opts.from, fromErrors) : void 0;
284878
285880
  const files = [];
284879
285881
  const reports = [];
284880
285882
  for (const p of targetPaths) {
284881
285883
  const abs2 = resolve18(p);
284882
- if (!existsSync24(abs2)) {
284883
- reports.push({ file: displayPath(abs2), functions: 0, entryOnly: 0, uncovered: [], error: `File not found: ${abs2}` });
285884
+ if (!existsSync25(abs2)) {
285885
+ reports.push({ file: displayPathOf(abs2), functions: 0, entryOnly: 0, uncovered: [], error: `File not found: ${displayPathOf(abs2)}` });
284884
285886
  continue;
284885
285887
  }
284886
285888
  files.push(...statSync14(abs2).isDirectory() ? collectNudoFiles(abs2) : [abs2]);
@@ -284893,11 +285895,21 @@ async function runHealth(paths, opts) {
284893
285895
  const errorCount = reports.filter((r) => r.error).length;
284894
285896
  const uncoveredTotal = reports.reduce((n, r) => n + r.uncovered.length, 0);
284895
285897
  const failed = driftCount > 0 || ifaceDriftCount > 0 || errorCount > 0;
285898
+ const ok2 = !failed && fromErrors.length === 0;
285899
+ process.exitCode = ok2 ? 0 : 1;
284896
285900
  if (opts.json) {
284897
285901
  console.log(
284898
285902
  JSON.stringify(
284899
285903
  {
284900
- ok: !failed,
285904
+ ok: ok2,
285905
+ ...fromErrors.length > 0 ? {
285906
+ pathErrors: fromErrors.map((e) => ({
285907
+ path: e.path,
285908
+ code: e.code,
285909
+ message: e.message,
285910
+ suggestion: e.suggestion
285911
+ }))
285912
+ } : {},
284901
285913
  files: reports.map((r) => ({
284902
285914
  file: r.file,
284903
285915
  functions: r.functions,
@@ -284912,7 +285924,7 @@ async function runHealth(paths, opts) {
284912
285924
  files: reports.length,
284913
285925
  drift: driftCount,
284914
285926
  interfaceDrift: ifaceDriftCount,
284915
- errors: errorCount,
285927
+ errors: errorCount + fromErrors.length,
284916
285928
  uncovered: uncoveredTotal,
284917
285929
  internal_fallbacks: reports.reduce((n, r) => n + (r.internalFallbacks ?? 0), 0)
284918
285930
  }
@@ -284922,8 +285934,16 @@ async function runHealth(paths, opts) {
284922
285934
  )
284923
285935
  );
284924
285936
  } else {
285937
+ for (const e of fromErrors) {
285938
+ console.error(e.message);
285939
+ console.error(`fix: ${e.suggestion}`);
285940
+ }
284925
285941
  if (reports.length === 0) {
284926
- console.log("No files to check.");
285942
+ if (fromErrors.length > 0) {
285943
+ console.log("Result: FAIL (path errors found)");
285944
+ } else {
285945
+ console.log("No files to check.");
285946
+ }
284927
285947
  return;
284928
285948
  }
284929
285949
  for (const r of reports) {
@@ -284962,12 +285982,27 @@ async function runHealth(paths, opts) {
284962
285982
  `
284963
285983
  Summary: ${reports.length} file(s) \xB7 ${driftCount} case drift \xB7 ${ifaceDriftCount} contract drift \xB7 ${errorCount} error(s) \xB7 ${uncoveredTotal} uncovered function(s)` + (internalTotal > 0 ? ` \xB7 ${internalTotal} internal_fallback(s)` : "")
284964
285984
  );
284965
- console.log(failed ? "Result: FAIL (drift or errors found)" : "Result: OK (uncovered function(s) are informational only)");
285985
+ console.log(
285986
+ ok2 ? "Result: OK (uncovered function(s) are informational only)" : fromErrors.length > 0 && !failed ? "Result: FAIL (path errors found)" : "Result: FAIL (drift or errors found)"
285987
+ );
284966
285988
  }
284967
- if (failed) process.exitCode = 1;
284968
285989
  }
284969
285990
  function registerHealthCommand(program3) {
284970
- program3.command("health").description("Project health & drift: uncovered functions, witness drift, contract drift, analysis errors").argument("[paths...]", "File(s) or directory(s) (default: current directory)").option("--watch, -w", "Watch and re-run health on change").option("--from <paths...>", "Usage-site files for freeze-drift detection").option("--json", "Output as JSON").action(async (paths, opts) => {
285991
+ program3.command("health").description("Project health & drift: uncovered functions, witness drift, contract drift, analysis errors").argument("[paths...]", "File(s) or directory(s) (default: current directory; with --from, pass paths explicitly \u2014 e.g. `nudo health . --from usage.js`)").option("--watch, -w", "Watch and re-run health on change").option("--from <paths...>", "Usage-site files for freeze-drift detection").option("--json", "Output as JSON").action(async (paths, opts) => {
285992
+ if (opts.from?.length) {
285993
+ if (opts.from.length > 1) {
285994
+ variadicSwallowError("health", ["--from"]);
285995
+ return;
285996
+ }
285997
+ if (paths.length === 0) {
285998
+ console.error(
285999
+ "error: `nudo health` --from requires explicit paths when used without positional paths (previously defaulted to cwd scan)"
286000
+ );
286001
+ console.error("fix: use: `nudo health . --from usage.js`");
286002
+ process.exitCode = 1;
286003
+ return;
286004
+ }
286005
+ }
284971
286006
  const fromPaths = opts.from;
284972
286007
  const runOneDir = async () => {
284973
286008
  await runHealth(paths, { ...fromPaths ? { from: fromPaths } : {}, json: opts.json });
@@ -285099,7 +286134,7 @@ function registerMigrateCommand(program3) {
285099
286134
 
285100
286135
  // src/index.ts
285101
286136
  var program2 = new Command();
285102
- var argv = process.argv.filter((a, i) => !(i >= 2 && a === "--"));
286137
+ var argv = process.argv[2] === "--" ? [process.argv[0], process.argv[1], ...process.argv.slice(3)] : process.argv;
285103
286138
  program2.name("nudo").description("JavaScript types, computed \u2014 check / test / contract / export / health / migrate").version(formatVersionOutput()).addHelpText(
285104
286139
  "after",
285105
286140
  `