@nudojs/core 1.0.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  $add,
3
3
  $arr,
4
+ $arrMutContainer,
5
+ $arrRest,
4
6
  $async,
5
7
  $asyncReturn,
6
8
  $await,
@@ -12,6 +14,7 @@ import {
12
14
  $div,
13
15
  $elems,
14
16
  $eq,
17
+ $eqLoose,
15
18
  $fnVal,
16
19
  $for,
17
20
  $forIter,
@@ -25,22 +28,29 @@ import {
25
28
  $idxSet,
26
29
  $invoke,
27
30
  $invokeSuper,
31
+ $isForkExit,
28
32
  $join,
29
33
  $le,
30
34
  $len,
31
35
  $lit,
36
+ $loopReturn,
32
37
  $lt,
33
38
  $mod,
34
39
  $mul,
35
40
  $ne,
41
+ $neLoose,
36
42
  $neg,
37
43
  $new,
38
44
  $not,
45
+ $nullishTest,
39
46
  $obj,
47
+ $objRest,
40
48
  $optionalGet,
41
49
  $optionalInvoke,
42
50
  $orDefault,
51
+ $pushLoopExit,
43
52
  $regex,
53
+ $rethrowIfNudoReturn,
44
54
  $set,
45
55
  $setKey,
46
56
  $spread,
@@ -51,6 +61,10 @@ import {
51
61
  $thisGet,
52
62
  $thisSet,
53
63
  $throw,
64
+ $tryCurrentMark,
65
+ $tryMark,
66
+ $tryPopMark,
67
+ $tryTakeSince,
54
68
  $typeof,
55
69
  $while,
56
70
  $whileSeq,
@@ -58,6 +72,7 @@ import {
58
72
  DEFAULT_MAX_LOOP_ITERS,
59
73
  MAX_CALL_DEPTH,
60
74
  MAX_TOTAL_CALLS,
75
+ NudoReturn,
61
76
  NudoThrow,
62
77
  abortDerivationSession,
63
78
  abs,
@@ -79,22 +94,27 @@ import {
79
94
  asAbsVal,
80
95
  attachFnImpl,
81
96
  awaitAbs,
97
+ beginCollectionFork,
82
98
  beginDerivationSession,
83
99
  betaOf,
84
100
  bindImports,
85
101
  bool,
86
102
  boolLit,
87
103
  callAbsMethod,
104
+ callAtFunctionBoundary,
88
105
  callFunction,
89
106
  callFunctionFull,
90
107
  callTranspiledExport,
91
108
  callTranspiledExportFull,
92
109
  classFromMethods,
93
110
  clearBClasses,
111
+ clearCollectionTables,
94
112
  cmp,
95
113
  coerceAsyncReturn,
96
114
  collectAbsExports,
97
115
  collectAbsNodeTypes,
116
+ collectionElementJoin,
117
+ collectionExactLen,
98
118
  concatString,
99
119
  confJoin,
100
120
  createHofCollectCtx,
@@ -110,8 +130,10 @@ import {
110
130
  div,
111
131
  emptyEnv,
112
132
  emptyPhi,
133
+ endCollectionFork,
113
134
  endDerivationSession,
114
135
  eq,
136
+ errorBrandAbs,
115
137
  evalArrayStatic,
116
138
  evalBuiltinInstanceMethod,
117
139
  evalBuiltinNew,
@@ -157,14 +179,20 @@ import {
157
179
  instanceOf,
158
180
  instantiateClass,
159
181
  instantiateReturn,
182
+ isArrMutator,
160
183
  isDefinitelyFalse,
161
184
  isDefinitelyTrue,
185
+ isErrorCtorName,
186
+ isEvalMissingSlotEnabled,
162
187
  isExactLit,
188
+ isMapAbs,
189
+ isNudoReturn,
163
190
  isNudoThrow,
164
191
  isNullishLitAbs,
165
192
  isNumPrim,
166
193
  isObj,
167
194
  isRelFn,
195
+ isSetAbs,
168
196
  isStrPrim,
169
197
  isTemplateLike,
170
198
  joinAbs,
@@ -183,13 +211,25 @@ import {
183
211
  lookupMethod,
184
212
  lookupMethodWithOwner,
185
213
  lookupSuperMethod,
214
+ looseEqAbs,
186
215
  lt,
187
216
  ltNum,
217
+ makeMapAbs,
218
+ makeSetAbs,
188
219
  makeSum,
220
+ mapClearEntries,
221
+ mapDeleteEntry,
189
222
  mapElementFallback,
223
+ mapEntriesAbs,
224
+ mapGetEntry,
225
+ mapHasEntry,
226
+ mapSetEntry,
227
+ mapSizeAbs,
228
+ mapValuesAbs,
190
229
  matchRelIdentLit,
191
230
  maybeLeak,
192
231
  mergeAdjacentFixedViews,
232
+ mergeCollectionArms,
193
233
  mod,
194
234
  mul,
195
235
  namespaceNameOf,
@@ -198,9 +238,11 @@ import {
198
238
  never,
199
239
  not,
200
240
  notAbs,
241
+ noteCollectionWrite,
201
242
  noteDerivationAdd,
202
243
  noteDerivationJoin,
203
244
  noteMemberDispatchMiss,
245
+ noteObjSlotMissing,
204
246
  notePrimMemberMissing,
205
247
  noteUnknownMemberMissing,
206
248
  num,
@@ -213,6 +255,7 @@ import {
213
255
  pTrue,
214
256
  parseSource,
215
257
  phiAnd,
258
+ popCollectionArm,
216
259
  predEquals,
217
260
  predToString,
218
261
  predVars,
@@ -221,24 +264,37 @@ import {
221
264
  projectFlatMapResult,
222
265
  promoteParamShape,
223
266
  ptypeof,
267
+ pushCollectionArm,
268
+ pushLoopExit,
269
+ pushThrowExit,
224
270
  recordMemberDiag,
225
271
  refineAbsForRelTrue,
226
272
  registerBClass,
227
273
  relationFingerprint,
228
274
  relationFn,
275
+ requiredFnArity,
229
276
  resetAbsCallBudget,
230
277
  resetLeakCounter,
231
278
  resetParseSourceCache,
232
279
  runTranspiled,
280
+ runWithEvalMissingSlot,
281
+ runWithLoopExits,
233
282
  setAbsAssignCollector,
234
283
  setAbsCallCollector,
235
284
  setAbsNodeCollector,
236
285
  setAbsTruncationCollector,
286
+ setAddEntry,
237
287
  setApplyCallbackHost,
238
288
  setBCallCollector,
289
+ setClearEntries,
290
+ setDeleteEntry,
239
291
  setDerivation,
240
292
  setDerivationCollector,
293
+ setElementsAbs,
294
+ setEvalMissingSlotEnabled,
295
+ setHasEntry,
241
296
  setMemberDiagCollector,
297
+ setSizeAbs,
242
298
  shapeOfTerm,
243
299
  shapeOnlyFn,
244
300
  shapeToString,
@@ -256,6 +312,8 @@ import {
256
312
  superNameOf,
257
313
  tagAbsOrigin,
258
314
  tagDerivationRoot,
315
+ takeLoopExits,
316
+ takeThrowExits,
259
317
  templateMatchesValue,
260
318
  templatePartsOf,
261
319
  termDepth,
@@ -280,7 +338,7 @@ import {
280
338
  withExecPhi,
281
339
  withVar,
282
340
  wrapPromise
283
- } from "./chunk-P3G2TODS.js";
341
+ } from "./chunk-6REHMIAE.js";
284
342
 
285
343
  // src/environment.ts
286
344
  function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
@@ -409,6 +467,9 @@ function formatAbs(a, opts = {}) {
409
467
  parts.push(`where ${predToString(a.pred)}`);
410
468
  }
411
469
  parts.push(`#${a.conf}`);
470
+ if (a.pathNote) {
471
+ parts.push(a.pathNote);
472
+ }
412
473
  return parts.join(" ");
413
474
  }
414
475
  function formatShapeSlot(a) {
@@ -425,8 +486,10 @@ function formatShape(a) {
425
486
  return "never";
426
487
  case "any":
427
488
  return "any";
428
- case "unknown":
489
+ case "unknown": {
490
+ if (a.term?.op === "lit" && a.term.value === void 0) return "undefined";
429
491
  return "unknown";
492
+ }
430
493
  case "prim": {
431
494
  const lv = litValue(a);
432
495
  if (typeof lv === "number" && !Number.isFinite(lv)) return String(lv);
@@ -449,13 +512,39 @@ function formatShape(a) {
449
512
  case "tuple":
450
513
  return `[${s.elements.map(formatShape).join(", ")}]`;
451
514
  case "fn": {
452
- if (s.paramTypes && s.paramTypes.length > 0) {
453
- const ps = s.paramTypes.map((p) => formatShapeSlot(p));
454
- const ret2 = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
455
- return `(${ps.join(", ")}) => ${ret2}`;
456
- }
457
515
  const ret = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
458
- return `(${s.params.join(", ")}) => ${ret}`;
516
+ const labels = s.params ?? [];
517
+ const paramTypes = s.paramTypes;
518
+ const hasMarkers = labels.some(
519
+ (p) => p.startsWith("...") || p.endsWith("?")
520
+ );
521
+ const renderLabeled = () => {
522
+ const ps = [];
523
+ const n = paramTypes ? Math.max(paramTypes.length, labels.length) : labels.length;
524
+ for (let i = 0; i < n; i++) {
525
+ const label = labels[i];
526
+ const type = paramTypes?.[i];
527
+ const typeText = type !== void 0 ? formatShapeSlot(type) : void 0;
528
+ if (label?.startsWith("...")) {
529
+ ps.push(typeText ? `...${label.slice(3)}: ${typeText}` : label);
530
+ } else if (label?.endsWith("?")) {
531
+ ps.push(typeText ? `${label.slice(0, -1)}?: ${typeText}` : label);
532
+ } else if (typeText !== void 0) {
533
+ ps.push(typeText);
534
+ } else if (label) {
535
+ ps.push(label);
536
+ }
537
+ }
538
+ return ps;
539
+ };
540
+ if (hasMarkers) {
541
+ return `(${renderLabeled().join(", ")}) => ${ret}`;
542
+ }
543
+ if (paramTypes && paramTypes.length > 0) {
544
+ const ps = paramTypes.map((p) => formatShapeSlot(p));
545
+ return `(${ps.join(", ")}) => ${ret}`;
546
+ }
547
+ return `(${labels.join(", ")}) => ${ret}`;
459
548
  }
460
549
  case "brand":
461
550
  return `${s.name}`;
@@ -573,6 +662,9 @@ function string() {
573
662
  function boolean() {
574
663
  return makeBuilder("boolean", []);
575
664
  }
665
+ function any() {
666
+ return makeBuilder(void 0, []);
667
+ }
576
668
  function array(item) {
577
669
  if (!isConstraint(item))
578
670
  throw new Error("nudo: array(item) \u671F\u671B\u7EA6\u675F\u503C\uFF08number()/string()/\u2026\u6216\u5176\u7EC4\u5408\u5B50\uFF09");
@@ -1039,12 +1131,13 @@ function rewriteSidecarSource(src, fromFile) {
1039
1131
  const keepTs = fromFile?.endsWith(".nudo.ts") === true;
1040
1132
  const ast = parseSource(src, keepTs ? { keepTs: true } : void 0);
1041
1133
  const cuts = [];
1042
- const exportNames = [];
1134
+ const exportPairs = [];
1043
1135
  const imports = [];
1136
+ const DEFAULT_LOCAL = "__nudoDefault";
1044
1137
  const badExport = (what) => {
1045
1138
  collectDiag({
1046
1139
  code: "nudo:interface-load",
1047
- message: `sidecar ${what}; only 'export const <name> = \u2026' is a recognized export form`,
1140
+ message: `sidecar ${what}; recognized export forms are 'export const <name> = \u2026' and 'export default <constraint|fn>'`,
1048
1141
  file: fromFile
1049
1142
  });
1050
1143
  };
@@ -1068,7 +1161,7 @@ function rewriteSidecarSource(src, fromFile) {
1068
1161
  if (decl.kind === "const") {
1069
1162
  let simple = true;
1070
1163
  for (const d of decl.declarations) {
1071
- if (d.id.type === "Identifier") exportNames.push(d.id.name);
1164
+ if (d.id.type === "Identifier") exportPairs.push({ key: d.id.name, expr: d.id.name });
1072
1165
  else simple = false;
1073
1166
  }
1074
1167
  if (!simple) badExport("'export const' with destructuring pattern");
@@ -1092,15 +1185,40 @@ function rewriteSidecarSource(src, fromFile) {
1092
1185
  }
1093
1186
  if (stmt.type === "ExportDefaultDeclaration") {
1094
1187
  const decl = stmt.declaration;
1095
- if ((decl?.type === "FunctionDeclaration" || decl?.type === "ClassDeclaration") && stmt.start != null && decl.start != null) {
1096
- const kind = decl.type === "FunctionDeclaration" ? "function" : "class";
1097
- badExport(`'export default ${kind}'`);
1098
- cuts.push({ start: stmt.start, end: decl.start, text: "" });
1099
- } else {
1100
- badExport("'export default'");
1101
- if (stmt.start != null && stmt.end != null) {
1102
- cuts.push({ start: stmt.start, end: stmt.end, text: "" });
1188
+ if (decl && (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") && stmt.start != null && decl.start != null) {
1189
+ if (decl.id?.name) {
1190
+ exportPairs.push({ key: decl.id.name, expr: decl.id.name });
1191
+ exportPairs.push({ key: "default", expr: decl.id.name });
1192
+ } else {
1193
+ exportPairs.push({ key: "default", expr: DEFAULT_LOCAL });
1194
+ if (decl.start != null && decl.end != null) {
1195
+ const kind = decl.type === "FunctionDeclaration" ? "function" : "class";
1196
+ cuts.push({
1197
+ start: stmt.start,
1198
+ end: decl.start,
1199
+ text: `var ${DEFAULT_LOCAL} = `
1200
+ });
1201
+ void kind;
1202
+ }
1203
+ }
1204
+ if (decl.id?.name) {
1205
+ cuts.push({ start: stmt.start, end: decl.start, text: "" });
1103
1206
  }
1207
+ continue;
1208
+ }
1209
+ if (decl && stmt.start != null && decl.start != null && decl.end != null) {
1210
+ exportPairs.push({ key: "default", expr: DEFAULT_LOCAL });
1211
+ cuts.push({
1212
+ start: stmt.start,
1213
+ end: decl.start,
1214
+ text: `var ${DEFAULT_LOCAL} = `
1215
+ });
1216
+ cuts.push({ start: decl.end, end: stmt.end ?? decl.end, text: ";" });
1217
+ continue;
1218
+ }
1219
+ badExport("'export default'");
1220
+ if (stmt.start != null && stmt.end != null) {
1221
+ cuts.push({ start: stmt.start, end: stmt.end, text: "" });
1104
1222
  }
1105
1223
  continue;
1106
1224
  }
@@ -1120,20 +1238,21 @@ function rewriteSidecarSource(src, fromFile) {
1120
1238
  const merged = [];
1121
1239
  for (const c of cuts) {
1122
1240
  const last = merged[merged.length - 1];
1123
- if (last && c.start <= last.end) {
1241
+ if (last && c.start <= last.end && last.text === "" && c.text === "") {
1124
1242
  last.end = Math.max(last.end, c.end);
1125
1243
  } else {
1126
- merged.push({ start: c.start, end: c.end });
1244
+ merged.push({ start: c.start, end: c.end, text: c.text });
1127
1245
  }
1128
1246
  }
1129
1247
  let code = "";
1130
1248
  let pos = 0;
1131
1249
  for (const c of merged) {
1132
- code += src.slice(pos, c.start);
1250
+ if (c.start < pos) continue;
1251
+ code += src.slice(pos, c.start) + c.text;
1133
1252
  pos = c.end;
1134
1253
  }
1135
1254
  code += src.slice(pos);
1136
- return { code, exportNames, imports };
1255
+ return { code, exportPairs, imports };
1137
1256
  }
1138
1257
  var TS_STMT_CUT_TYPES = /* @__PURE__ */ new Set([
1139
1258
  "TSInterfaceDeclaration",
@@ -1207,7 +1326,7 @@ function collectTsSyntaxCuts(node, src, cuts = []) {
1207
1326
  }
1208
1327
  function execSidecar(src, opts, chain, preloaded) {
1209
1328
  const fromFile = opts?.fromFile;
1210
- const { code, exportNames, imports } = rewriteSidecarSource(src, fromFile);
1329
+ const { code, exportPairs, imports } = rewriteSidecarSource(src, fromFile);
1211
1330
  const prologue = [];
1212
1331
  const deps = {};
1213
1332
  for (const imp of imports) {
@@ -1319,9 +1438,12 @@ function execSidecar(src, opts, chain, preloaded) {
1319
1438
  for (const local of Object.keys(deps)) {
1320
1439
  prologue.push(`var ${local} = __nudoDeps[${JSON.stringify(local)}];`);
1321
1440
  }
1441
+ const returnObj = exportPairs.map(
1442
+ (p) => p.key === p.expr ? p.key : `${JSON.stringify(p.key)}: ${p.expr}`
1443
+ ).join(", ");
1322
1444
  const body = `${prologue.length > 0 ? `${prologue.join("\n")}
1323
1445
  ` : ""}${code}
1324
- return { ${exportNames.join(", ")} };`;
1446
+ return { ${returnObj} };`;
1325
1447
  const fn2 = new Function(
1326
1448
  ...Object.keys(sidecarInjects),
1327
1449
  "__nudoInjects",
@@ -1573,28 +1695,134 @@ function localNamedExports(source) {
1573
1695
  }
1574
1696
  }
1575
1697
  }
1698
+ const identName = (n) => {
1699
+ if (!n) return void 0;
1700
+ if (n.type === "Identifier" && typeof n.name === "string") return n.name;
1701
+ if (n.type === "Literal" && typeof n.value === "string") return n.value;
1702
+ return void 0;
1703
+ };
1704
+ const localClasses = /* @__PURE__ */ new Map();
1576
1705
  for (const stmt of ast.program.body) {
1577
- if (stmt.type !== "ExportNamedDeclaration") continue;
1578
- if (stmt.source) continue;
1579
- const d = stmt.declaration;
1580
- if (!d) {
1581
- for (const spec of stmt.specifiers) {
1582
- if (spec.type !== "ExportSpecifier") continue;
1583
- const exported = spec.exported;
1584
- const name = exported.type === "Identifier" ? exported.name : exported.value;
1585
- if (!name || name === "default") continue;
1586
- const local = spec.local;
1587
- const localName = local.type === "Identifier" ? local.name : local.value;
1588
- if (localName && importedLocalNames.has(localName)) continue;
1589
- out.add(name);
1706
+ const s = stmt;
1707
+ if (s.type !== "ClassDeclaration") continue;
1708
+ const idName = identName(s.id);
1709
+ if (idName) localClasses.set(idName, s);
1710
+ }
1711
+ const addClassMethodKeys = (out2, decl) => {
1712
+ if (!decl || decl.type !== "ClassDeclaration") return;
1713
+ const idName = identName(decl.id);
1714
+ if (!idName) return;
1715
+ const body = decl.body?.body ?? [];
1716
+ for (const m of body) {
1717
+ const mem = m;
1718
+ const isMethod = mem.type === "MethodDefinition" || mem.type === "ClassMethod" || mem.type === "TSDeclareMethod";
1719
+ if (!isMethod) continue;
1720
+ if (mem.kind && mem.kind !== "method") continue;
1721
+ if (mem.static) continue;
1722
+ const keyName = identName(mem.key);
1723
+ if (keyName) out2.add(`${idName}.${keyName}`);
1724
+ }
1725
+ };
1726
+ const isExportsTarget = (n) => {
1727
+ if (!n) return false;
1728
+ if (n.type === "Identifier") return n.name === "exports";
1729
+ if (n.type === "MemberExpression") {
1730
+ return identName(n.object) === "module" && n.property !== void 0 ? identName(n.property) === "exports" : false;
1731
+ }
1732
+ return false;
1733
+ };
1734
+ const cjsPropName = (left) => {
1735
+ if (!left || left.type !== "MemberExpression") return void 0;
1736
+ if (left.computed === true) return void 0;
1737
+ const obj2 = left.object;
1738
+ if (!isExportsTarget(obj2)) return void 0;
1739
+ return identName(left.property);
1740
+ };
1741
+ for (const stmt of ast.program.body) {
1742
+ const s = stmt;
1743
+ if (s.type === "ExportNamedDeclaration") {
1744
+ if (s.source) continue;
1745
+ const d = s.declaration;
1746
+ if (!d) {
1747
+ const specs = s.specifiers ?? [];
1748
+ for (const spec of specs) {
1749
+ if (spec.type !== "ExportSpecifier") continue;
1750
+ const name = identName(spec.exported);
1751
+ if (!name) continue;
1752
+ const localName = identName(spec.local);
1753
+ if (name === "default") {
1754
+ out.add("default");
1755
+ if (localName && !importedLocalNames.has(localName)) out.add(localName);
1756
+ if (localName && !importedLocalNames.has(localName)) {
1757
+ addClassMethodKeys(out, localClasses.get(localName));
1758
+ }
1759
+ continue;
1760
+ }
1761
+ if (localName && importedLocalNames.has(localName)) continue;
1762
+ out.add(name);
1763
+ if (localName) addClassMethodKeys(out, localClasses.get(localName));
1764
+ }
1765
+ continue;
1766
+ }
1767
+ if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
1768
+ const idName = identName(d.id);
1769
+ if (idName) out.add(idName);
1770
+ if (d.type === "ClassDeclaration" && idName) {
1771
+ addClassMethodKeys(out, d);
1772
+ }
1773
+ } else if (d.type === "VariableDeclaration") {
1774
+ const decls = d.declarations ?? [];
1775
+ for (const decl of decls) {
1776
+ const idName = identName(decl.id);
1777
+ if (idName) out.add(idName);
1778
+ }
1590
1779
  }
1591
1780
  continue;
1592
1781
  }
1593
- if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
1594
- if (d.id) out.add(d.id.name);
1595
- } else if (d.type === "VariableDeclaration") {
1596
- for (const decl of d.declarations) {
1597
- if (decl.id.type === "Identifier") out.add(decl.id.name);
1782
+ if (s.type === "ExportDefaultDeclaration") {
1783
+ out.add("default");
1784
+ const d = s.declaration;
1785
+ if (d) {
1786
+ if ((d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") && d.id) {
1787
+ const idName = identName(d.id);
1788
+ if (idName) out.add(idName);
1789
+ if (d.type === "ClassDeclaration" && idName) {
1790
+ addClassMethodKeys(out, d);
1791
+ }
1792
+ } else if (d.type === "Identifier") {
1793
+ const n = identName(d);
1794
+ if (n && !importedLocalNames.has(n)) {
1795
+ out.add(n);
1796
+ addClassMethodKeys(out, localClasses.get(n));
1797
+ }
1798
+ }
1799
+ }
1800
+ continue;
1801
+ }
1802
+ if (s.type === "ExpressionStatement") {
1803
+ const expr = s.expression;
1804
+ if (!expr || expr.type !== "AssignmentExpression" || expr.operator !== "=") continue;
1805
+ const left = expr.left;
1806
+ const right = expr.right;
1807
+ if (!left || !right) continue;
1808
+ const prop = cjsPropName(left);
1809
+ if (prop && prop !== "default") out.add(prop);
1810
+ if (isExportsTarget(left)) {
1811
+ if (right.type === "ObjectExpression") {
1812
+ const props = right.properties ?? [];
1813
+ for (const p of props) {
1814
+ if (p.type !== "ObjectProperty" && p.type !== "Property") continue;
1815
+ const keyName = identName(p.key);
1816
+ if (keyName && keyName !== "default") out.add(keyName);
1817
+ else {
1818
+ const valName = identName(p.value);
1819
+ if (valName) out.add(valName);
1820
+ }
1821
+ }
1822
+ } else if (right.type === "Identifier") {
1823
+ const n = identName(right);
1824
+ if (n && !importedLocalNames.has(n)) out.add(n);
1825
+ }
1598
1826
  }
1599
1827
  }
1600
1828
  }
@@ -1651,6 +1879,18 @@ function sidecarAutoBindAllowed(sidecarPath, autoBind) {
1651
1879
  if (typeof autoBind === "function") return autoBind(sidecarPath) === true;
1652
1880
  return false;
1653
1881
  }
1882
+ function isDefaultExportLocal(source, localName) {
1883
+ const reList = new RegExp(
1884
+ `export\\s*\\{[^}]*\\b${localName}\\s+as\\s+default\\b[^}]*\\}`,
1885
+ "m"
1886
+ );
1887
+ const reDefaultFn = new RegExp(
1888
+ `export\\s+default\\s+(?:async\\s+)?function\\s+${localName}\\b`,
1889
+ "m"
1890
+ );
1891
+ const reDefaultId = new RegExp(`export\\s+default\\s+${localName}\\b`, "m");
1892
+ return reList.test(source) || reDefaultFn.test(source) || reDefaultId.test(source);
1893
+ }
1654
1894
  function loadSidecarBinding(source, fnName, opts) {
1655
1895
  const { loadModule, fromFile, autoBind } = opts;
1656
1896
  if (!loadModule || !fromFile) return { ok: false };
@@ -1660,6 +1900,7 @@ function loadSidecarBinding(source, fnName, opts) {
1660
1900
  const spec = `./${sidecarPath.slice(sidecarPath.lastIndexOf("/") + 1)}`;
1661
1901
  const sidecarSrc = loadModule(spec, fromFile);
1662
1902
  if (sidecarSrc === void 0) return { ok: false };
1903
+ if (sidecarSrc === source) return { ok: false };
1663
1904
  let exports;
1664
1905
  try {
1665
1906
  exports = execNudoModule(sidecarSrc, { loadModule, fromFile: sidecarPath });
@@ -1671,7 +1912,20 @@ function loadSidecarBinding(source, fnName, opts) {
1671
1912
  });
1672
1913
  return { ok: false };
1673
1914
  }
1674
- const binding = exports[fnName];
1915
+ let binding = exports[fnName];
1916
+ if (binding === void 0 && exports.default !== void 0 && isDefaultExportLocal(source, fnName)) {
1917
+ binding = exports.default;
1918
+ }
1919
+ if (binding === void 0 && fnName.includes(".")) {
1920
+ const [cls, method] = fnName.split(".", 2);
1921
+ binding = exports[`${cls}_${method}`];
1922
+ if (binding === void 0) {
1923
+ const bag = exports[cls];
1924
+ if (bag && typeof bag === "object" && !isNudoConstraint(bag)) {
1925
+ binding = bag[method];
1926
+ }
1927
+ }
1928
+ }
1675
1929
  if (binding === void 0) return { ok: false };
1676
1930
  if (!isNudoConstraint(binding)) {
1677
1931
  collectDiag2({
@@ -1689,11 +1943,16 @@ function loadSidecarBinding(source, fnName, opts) {
1689
1943
  });
1690
1944
  return { ok: false };
1691
1945
  }
1946
+ const generatedNames = generatedExportNames(sidecarSrc);
1947
+ const isGenerated = generatedNames.has(fnName) || (fnName.includes(".") ? (() => {
1948
+ const [cls, method] = fnName.split(".", 2);
1949
+ return generatedNames.has(`${cls}_${method}`) || generatedNames.has(cls);
1950
+ })() : false);
1692
1951
  return {
1693
1952
  ok: true,
1694
1953
  sidecarPath,
1695
1954
  constraint: binding,
1696
- generated: generatedExportNames(sidecarSrc).has(fnName)
1955
+ generated: isGenerated
1697
1956
  };
1698
1957
  }
1699
1958
  function boundsOf(c) {
@@ -1990,6 +2249,27 @@ function fmtConstraint(c) {
1990
2249
  function formatConstraint(c) {
1991
2250
  return fmtConstraint(c);
1992
2251
  }
2252
+ function formatEffectiveInterfaceDisplay(eff) {
2253
+ const params = eff.params.map((p) => `${p.param}: ${formatConstraint(p.constraint)}`).join(", ");
2254
+ let s = `(${params})`;
2255
+ if (eff.returns) s += ` \u2192 ${formatConstraint(eff.returns.constraint)}`;
2256
+ return s;
2257
+ }
2258
+ function formatInterfaceTierLine(source) {
2259
+ return `\u25CF interface / ${source}`;
2260
+ }
2261
+ function interfaceTierOf(source, fnName, fromFile, opts = {}) {
2262
+ if (!localNamedExports(source).has(fnName)) return void 0;
2263
+ const eff = effectiveInterface(source, fnName, { fromFile, ...opts });
2264
+ const src = eff?.source ?? "implicit";
2265
+ if (eff && src !== "implicit") {
2266
+ return { source: src, display: formatEffectiveInterfaceDisplay(eff) };
2267
+ }
2268
+ return { source: src };
2269
+ }
2270
+ function interfaceSourceOf(source, fnName, fromFile, opts = {}) {
2271
+ return interfaceTierOf(source, fnName, fromFile, opts)?.source;
2272
+ }
1993
2273
 
1994
2274
  // src/algebra/fn-fp.ts
1995
2275
  var fnFpCache = /* @__PURE__ */ new Map();
@@ -2176,11 +2456,167 @@ function canSkipLiteralCallScan(source, file) {
2176
2456
  return true;
2177
2457
  }
2178
2458
 
2459
+ // src/algebra/param-surface.ts
2460
+ function collectPatternNames(p, top, nested, depth, propKey) {
2461
+ if (!p) return;
2462
+ if (p.type === "Identifier" && p.name) {
2463
+ if (depth === 0) top.push(p.name);
2464
+ else nested.push(p.name);
2465
+ return;
2466
+ }
2467
+ if (p.type === "AssignmentPattern") {
2468
+ collectPatternNames(p.left, top, nested, depth, propKey);
2469
+ return;
2470
+ }
2471
+ if (p.type === "RestElement") {
2472
+ collectPatternNames(p.argument, top, nested, depth, propKey);
2473
+ return;
2474
+ }
2475
+ if (p.type === "ObjectPattern") {
2476
+ for (const prop of p.properties ?? []) {
2477
+ if (prop.type === "RestElement" || prop.argument) {
2478
+ collectPatternNames(prop.argument, top, nested, depth, propKey);
2479
+ continue;
2480
+ }
2481
+ const keyName = prop.key?.type === "Identifier" && prop.key.name ? prop.key.name : prop.key && "value" in prop.key ? String(prop.key.value ?? "") : void 0;
2482
+ const v2 = prop.value ?? prop.key;
2483
+ if (!v2) continue;
2484
+ if (keyName && v2.type === "Identifier" && v2.name && keyName !== v2.name) {
2485
+ if (depth === 0) top.push(keyName);
2486
+ else nested.push(keyName);
2487
+ if (propKey && depth === 0) propKey[keyName] = keyName;
2488
+ }
2489
+ if (v2.type === "Identifier" && v2.name) {
2490
+ if (depth === 0) {
2491
+ top.push(v2.name);
2492
+ if (propKey && keyName) propKey[v2.name] = keyName;
2493
+ } else nested.push(v2.name);
2494
+ } else {
2495
+ collectPatternNames(v2, top, nested, depth + 1, propKey);
2496
+ }
2497
+ }
2498
+ return;
2499
+ }
2500
+ if (p.type === "ArrayPattern") {
2501
+ for (const el of p.elements ?? []) {
2502
+ if (!el) continue;
2503
+ if (el.type === "Identifier" && el.name) {
2504
+ if (depth === 0) top.push(el.name);
2505
+ else nested.push(el.name);
2506
+ } else {
2507
+ collectPatternNames(el, top, nested, depth + 1, propKey);
2508
+ }
2509
+ }
2510
+ }
2511
+ }
2512
+ function formalParamsFromNodes(params) {
2513
+ const list = params ?? [];
2514
+ return list.map((p, index) => {
2515
+ if (p?.type === "Identifier" && p.name) {
2516
+ return { kind: "id", name: p.name, index };
2517
+ }
2518
+ if (p?.type === "AssignmentPattern") {
2519
+ const left = p.left;
2520
+ if (left?.type === "Identifier" && left.name) {
2521
+ return { kind: "default", name: left.name, index };
2522
+ }
2523
+ const top2 = [];
2524
+ const nested2 = [];
2525
+ const propKey2 = {};
2526
+ if (left) collectPatternNames(left, top2, nested2, 0, propKey2);
2527
+ return {
2528
+ kind: "pattern",
2529
+ placeholder: `_p${index}`,
2530
+ bound: top2,
2531
+ propKey: propKey2,
2532
+ nested: nested2,
2533
+ index
2534
+ };
2535
+ }
2536
+ if (p?.type === "RestElement") {
2537
+ const arg = p.argument;
2538
+ const name = arg?.type === "Identifier" && arg.name ? arg.name : `arg${index}`;
2539
+ return { kind: "rest", name, display: `...${name}`, index };
2540
+ }
2541
+ const top = [];
2542
+ const nested = [];
2543
+ const propKey = {};
2544
+ if (p) collectPatternNames(p, top, nested, 0, propKey);
2545
+ return {
2546
+ kind: "pattern",
2547
+ placeholder: `_p${index}`,
2548
+ bound: top,
2549
+ propKey,
2550
+ nested,
2551
+ index
2552
+ };
2553
+ });
2554
+ }
2555
+ function formalParamDisplayNames(formals) {
2556
+ return formals.map((f) => {
2557
+ switch (f.kind) {
2558
+ case "id":
2559
+ case "default":
2560
+ return f.name;
2561
+ case "rest":
2562
+ return f.display;
2563
+ case "pattern":
2564
+ return f.placeholder;
2565
+ }
2566
+ });
2567
+ }
2568
+ function contractParamNameSet(formals) {
2569
+ const out = /* @__PURE__ */ new Set();
2570
+ for (const f of formals) {
2571
+ if (f.kind === "id" || f.kind === "default") out.add(f.name);
2572
+ else if (f.kind === "rest") {
2573
+ out.add(f.name);
2574
+ out.add(f.display);
2575
+ } else if (f.kind === "pattern") {
2576
+ out.add(f.placeholder);
2577
+ out.add("_");
2578
+ for (const b of f.bound) out.add(b);
2579
+ }
2580
+ }
2581
+ return out;
2582
+ }
2583
+ function locateContractParam(formals, contractName) {
2584
+ for (const f of formals) {
2585
+ if (f.kind === "id" || f.kind === "default") {
2586
+ if (f.name === contractName) return { index: f.index };
2587
+ } else if (f.kind === "rest") {
2588
+ if (f.name === contractName || f.display === contractName) {
2589
+ return { index: f.index, rest: true };
2590
+ }
2591
+ } else if (f.kind === "pattern") {
2592
+ if (f.placeholder === contractName || contractName === "_") {
2593
+ return { index: f.index };
2594
+ }
2595
+ if (f.bound.includes(contractName)) {
2596
+ return { index: f.index, field: f.propKey?.[contractName] ?? contractName };
2597
+ }
2598
+ }
2599
+ }
2600
+ return void 0;
2601
+ }
2602
+
2179
2603
  // src/algebra/load-deps-fp.ts
2180
2604
  var MAX_LOAD_DEP_NODES = 64;
2181
2605
  function extractAllLoadSpecs(source) {
2182
2606
  const specs = /* @__PURE__ */ new Set();
2183
2607
  for (const imp of extractNudoImports(source)) specs.add(imp.spec);
2608
+ const isPathLikeSpec = (spec) => spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || /\.(ts|js|mjs|cjs|tsx|jsx)$/.test(spec);
2609
+ const envRe = /@nudo:env\s+([^\n*]+)/g;
2610
+ let envM;
2611
+ while (envM = envRe.exec(source)) {
2612
+ for (const part of envM[1].split(",")) {
2613
+ const spec = part.trim().replace(/^['"]|['"]$/g, "");
2614
+ if (spec && isPathLikeSpec(spec)) specs.add(spec);
2615
+ }
2616
+ }
2617
+ const mockFromRe = /@nudo:mock-module\s+"[^"]+"\s+(?:\{[^}]*\}\s+)?from\s+["']([^"']+)["']/g;
2618
+ let mockM;
2619
+ while (mockM = mockFromRe.exec(source)) specs.add(mockM[1]);
2184
2620
  const patterns = [
2185
2621
  /\bfrom\s*['"]([^'"]+)['"]/g,
2186
2622
  /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
@@ -2200,6 +2636,7 @@ function sidecarSpecsOf(source) {
2200
2636
  function loadModuleDepsFingerprint(source, loadModule, fromFile) {
2201
2637
  const parts = [];
2202
2638
  const paths = [];
2639
+ const contents = [];
2203
2640
  const seen = /* @__PURE__ */ new Set();
2204
2641
  const queue = extractAllLoadSpecs(source).map(
2205
2642
  (spec) => ({ spec, from: fromFile })
@@ -2208,7 +2645,7 @@ function loadModuleDepsFingerprint(source, loadModule, fromFile) {
2208
2645
  let truncated = false;
2209
2646
  const finish = () => {
2210
2647
  parts.sort();
2211
- return truncated ? { fp: `trunc:${parts.join(",")}`, paths, truncated: true } : { fp: parts.join(","), paths, truncated: false };
2648
+ return truncated ? { fp: `trunc:${parts.join(",")}`, paths, contents, truncated: true } : { fp: parts.join(","), paths, contents, truncated: false };
2212
2649
  };
2213
2650
  const absorbSidecarClosure = (entryFile) => {
2214
2651
  const sidecarPath = sidecarPathOf(entryFile);
@@ -2224,6 +2661,7 @@ function loadModuleDepsFingerprint(source, loadModule, fromFile) {
2224
2661
  n++;
2225
2662
  parts.push(`sidecar:${sidecarPath}=${hashSource(sidecarSrc)}`);
2226
2663
  paths.push(sidecarPath);
2664
+ contents.push({ path: sidecarPath, content: sidecarSrc });
2227
2665
  const sidecarQueue = sidecarSpecsOf(
2228
2666
  sidecarSrc
2229
2667
  ).map((spec) => ({ spec, from: sidecarPath }));
@@ -2240,6 +2678,7 @@ function loadModuleDepsFingerprint(source, loadModule, fromFile) {
2240
2678
  const src = loadModule(cur.spec, cur.from);
2241
2679
  parts.push(`sidecar:${path}=${src === void 0 ? "miss" : hashSource(src)}`);
2242
2680
  paths.push(path);
2681
+ contents.push({ path, content: src ?? null });
2243
2682
  if (src === void 0) continue;
2244
2683
  for (const next of sidecarSpecsOf(src)) {
2245
2684
  const nextPath = resolveDepPath(path, next);
@@ -2260,6 +2699,7 @@ function loadModuleDepsFingerprint(source, loadModule, fromFile) {
2260
2699
  const src = loadModule(cur.spec, cur.from);
2261
2700
  parts.push(`${path}=${src === void 0 ? "miss" : hashSource(src)}`);
2262
2701
  paths.push(path);
2702
+ contents.push({ path, content: src ?? null });
2263
2703
  if (src === void 0) continue;
2264
2704
  for (const next of extractAllLoadSpecs(src)) {
2265
2705
  const nextPath = resolveDepPath(path, next);
@@ -2324,14 +2764,14 @@ function loadModuleId(fn2) {
2324
2764
  }
2325
2765
  function refineDepsFingerprint(source, refine) {
2326
2766
  if (!refine?.loadModule || !refine.fromFile) {
2327
- return { fp: "-", paths: [], truncated: false };
2767
+ return { fp: "-", paths: [], contents: [], truncated: false };
2328
2768
  }
2329
2769
  return loadModuleDepsFingerprint(source, refine.loadModule, refine.fromFile);
2330
2770
  }
2331
2771
  function generalizeMemoKey(fnName, source, opts) {
2332
2772
  const r = opts.refine;
2333
2773
  const budget = opts.budget ?? defaultLeakBudget;
2334
- const deps = opts.depsFp ?? (r ? refineDepsFingerprint(source, r) : { fp: "-", paths: [], truncated: false });
2774
+ const deps = opts.depsFp ?? (r ? refineDepsFingerprint(source, r) : { fp: "-", paths: [], contents: [], truncated: false });
2335
2775
  const sc = opts.sidecarFp ?? (r?.loadModule && r.fromFile ? sidecarClosureFingerprint(r.fromFile, r) : void 0);
2336
2776
  const scPath = sc !== void 0 && !sc.startsWith("trunc:") && r?.fromFile ? normPath(sidecarPathOf(r.fromFile)) : void 0;
2337
2777
  const srcPart = generalizeSourceKeyPart(source, fnName, opts.file);
@@ -2529,6 +2969,11 @@ function collectAbsVars(a, acc, seen) {
2529
2969
  return;
2530
2970
  }
2531
2971
  }
2972
+ function collectAbsFreeVars(a) {
2973
+ const acc = /* @__PURE__ */ new Set();
2974
+ collectAbsVars(a, acc, /* @__PURE__ */ new Set());
2975
+ return acc;
2976
+ }
2532
2977
  function renameTerm(t, map) {
2533
2978
  if (t.op === "var") {
2534
2979
  const to = map.get(t.id);
@@ -2645,6 +3090,7 @@ function alphaRenameResult(result, fromOrder, toOrder) {
2645
3090
  function extractFn(source, fnName, fileAst) {
2646
3091
  const file = fileAst ?? parseSource(source);
2647
3092
  const env = emptyEnv();
3093
+ const formalsByName = /* @__PURE__ */ new Map();
2648
3094
  for (const stmt of file.program.body) {
2649
3095
  let decl = stmt;
2650
3096
  if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
@@ -2654,30 +3100,53 @@ function extractFn(source, fnName, fileAst) {
2654
3100
  decl = stmt.declaration;
2655
3101
  }
2656
3102
  if (decl.type === "FunctionDeclaration" && decl.id) {
3103
+ const formals = formalParamsFromNodes(decl.params);
2657
3104
  env.fns.set(decl.id.name, {
2658
- params: decl.params.map((p) => p.type === "Identifier" ? p.name : "_"),
3105
+ params: formalParamDisplayNames(formals),
2659
3106
  body: decl.body,
2660
3107
  async: decl.async === true
2661
3108
  });
3109
+ formalsByName.set(decl.id.name, formals);
3110
+ }
3111
+ if (decl.type === "ClassDeclaration" && decl.id?.name) {
3112
+ const cname = decl.id.name;
3113
+ const body = decl.body?.body ?? [];
3114
+ for (const m of body) {
3115
+ const mem = m;
3116
+ const isMethod = mem.type === "MethodDefinition" || mem.type === "ClassMethod" || mem.type === "TSDeclareMethod";
3117
+ if (!isMethod || !mem.body) continue;
3118
+ if (mem.kind && mem.kind !== "method") continue;
3119
+ if (mem.static) continue;
3120
+ const keyName = mem.key?.type === "Identifier" ? mem.key.name : void 0;
3121
+ if (!keyName) continue;
3122
+ const fullName = `${cname}.${keyName}`;
3123
+ const formals = formalParamsFromNodes(mem.params ?? []);
3124
+ env.fns.set(fullName, {
3125
+ params: formalParamDisplayNames(formals),
3126
+ body: mem.body,
3127
+ async: mem.async === true
3128
+ });
3129
+ formalsByName.set(fullName, formals);
3130
+ }
2662
3131
  }
2663
3132
  if (decl.type === "VariableDeclaration") {
2664
3133
  for (const d of decl.declarations) {
2665
3134
  if (d.id.type === "Identifier" && d.init && (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")) {
2666
3135
  const init = d.init;
3136
+ const formals = formalParamsFromNodes(init.params);
2667
3137
  env.fns.set(d.id.name, {
2668
- params: init.params.map(
2669
- (p) => p.type === "Identifier" ? p.name ?? "_" : "_"
2670
- ),
3138
+ params: formalParamDisplayNames(formals),
2671
3139
  body: init.body,
2672
3140
  async: init.async === true
2673
3141
  });
3142
+ formalsByName.set(d.id.name, formals);
2674
3143
  }
2675
3144
  }
2676
3145
  }
2677
3146
  }
2678
3147
  const fn2 = env.fns.get(fnName);
2679
3148
  if (!fn2) return void 0;
2680
- return { params: fn2.params, body: fn2.body, env };
3149
+ return { params: fn2.params, body: fn2.body, env, formals: formalsByName.get(fnName) ?? [] };
2681
3150
  }
2682
3151
  function generalizeFromAst(fnName, source, opts = {}) {
2683
3152
  const { key, depPaths, truncated } = generalizeMemoKey(fnName, source, opts);
@@ -2696,7 +3165,7 @@ function generalizeFromAst(fnName, source, opts = {}) {
2696
3165
  function generalizeFromAstUncached(fnName, source, opts = {}) {
2697
3166
  const extracted = extractFn(source, fnName, opts.file);
2698
3167
  if (!extracted) return void 0;
2699
- const { params, body, env } = extracted;
3168
+ const { params, body, env, formals } = extracted;
2700
3169
  const budget = opts.budget ?? defaultLeakBudget;
2701
3170
  const label = opts.label ?? "A";
2702
3171
  const typeParams = params.map((p, i) => ({
@@ -2812,6 +3281,7 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
2812
3281
  entryShapes,
2813
3282
  fnRels
2814
3283
  ),
3284
+ ...formals.length > 0 ? { formals } : {},
2815
3285
  ...entryReqs ? { entryReqs } : {},
2816
3286
  ...fnRels ? { fnRels } : {},
2817
3287
  ...entryShapes ? { entryShapes } : {},
@@ -3506,6 +3976,26 @@ function hofFnArgOk(src, tgt) {
3506
3976
  function listTopFunctions(source, file) {
3507
3977
  const f = file ?? parseSource(source);
3508
3978
  const names = [];
3979
+ const localClasses = /* @__PURE__ */ new Map();
3980
+ for (const stmt of f.program.body) {
3981
+ if (stmt.type === "ClassDeclaration") {
3982
+ const cname = stmt.id?.name;
3983
+ if (cname) localClasses.set(cname, stmt);
3984
+ }
3985
+ }
3986
+ const exportedClassNames = /* @__PURE__ */ new Set();
3987
+ const collectClassMethods = (cname, decl) => {
3988
+ const body = decl.body?.body ?? [];
3989
+ for (const m of body) {
3990
+ const mem = m;
3991
+ const isMethod = mem.type === "MethodDefinition" || mem.type === "ClassMethod" || mem.type === "TSDeclareMethod";
3992
+ if (!isMethod) continue;
3993
+ if (mem.kind && mem.kind !== "method") continue;
3994
+ if (mem.static) continue;
3995
+ const keyName = mem.key?.type === "Identifier" ? mem.key.name : void 0;
3996
+ if (keyName) names.push(`${cname}.${keyName}`);
3997
+ }
3998
+ };
3509
3999
  for (const stmt of f.program.body) {
3510
4000
  let decl = stmt;
3511
4001
  if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
@@ -3522,121 +4012,48 @@ function listTopFunctions(source, file) {
3522
4012
  }
3523
4013
  }
3524
4014
  }
3525
- }
3526
- return names;
3527
- }
3528
- function absUnknown() {
3529
- return { shape: { k: "unknown" }, conf: "partial" };
3530
- }
3531
- function collectParamStructReqs(source, fnName, fileAst) {
3532
- const reqs = /* @__PURE__ */ new Map();
3533
- const extracted = extractFn(source, fnName, fileAst);
3534
- if (!extracted) return reqs;
3535
- const params = new Set(extracted.params);
3536
- const body = extracted.body;
3537
- const BUILTIN_METHODS = /* @__PURE__ */ new Set([
3538
- "map",
3539
- "filter",
3540
- "reduce",
3541
- "flatMap",
3542
- "forEach",
3543
- "some",
3544
- "every",
3545
- "find",
3546
- "findIndex",
3547
- "includes",
3548
- "indexOf",
3549
- "lastIndexOf",
3550
- "join",
3551
- "slice",
3552
- "splice",
3553
- "push",
3554
- "pop",
3555
- "shift",
3556
- "unshift",
3557
- "sort",
3558
- "reverse",
3559
- "concat",
3560
- "at",
3561
- "replace",
3562
- "replaceAll",
3563
- "split",
3564
- "trim",
3565
- "toLowerCase",
3566
- "toUpperCase",
3567
- "startsWith",
3568
- "endsWith",
3569
- "charAt",
3570
- "charCodeAt",
3571
- "padStart",
3572
- "padEnd",
3573
- "repeat",
3574
- "toString",
3575
- "valueOf",
3576
- "substring",
3577
- "match",
3578
- "search",
3579
- "hasOwnProperty",
3580
- "keys",
3581
- "values",
3582
- "entries",
3583
- "then",
3584
- "catch",
3585
- "finally"
3586
- ]);
3587
- const visit = (n, isMethodCallee = false, guarded = false) => {
3588
- if (!n || typeof n !== "object") return;
3589
- const obj2 = n;
3590
- if (obj2.type === "LogicalExpression") {
3591
- visit(obj2.left, false, guarded);
3592
- visit(obj2.right, false, true);
3593
- return;
4015
+ if (decl.type === "ClassDeclaration" && decl.id?.name && (stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration")) {
4016
+ const cname = decl.id.name;
4017
+ exportedClassNames.add(cname);
4018
+ collectClassMethods(cname, decl);
3594
4019
  }
3595
- if (obj2.type === "ConditionalExpression") {
3596
- visit(obj2.test, false, guarded);
3597
- visit(obj2.consequent, false, true);
3598
- visit(obj2.alternate, false, true);
3599
- return;
3600
- }
3601
- if (obj2.type === "IfStatement") {
3602
- visit(obj2.test, false, guarded);
3603
- visit(obj2.consequent, false, true);
3604
- visit(obj2.alternate, false, true);
3605
- return;
3606
- }
3607
- if (obj2.type === "MemberExpression" && !obj2.computed && !isMethodCallee) {
3608
- const o = obj2.object;
3609
- const p = obj2.property;
3610
- if (o?.type === "Identifier" && o.name && params.has(o.name) && p?.type === "Identifier" && p.name) {
3611
- if (BUILTIN_METHODS.has(p.name)) return;
3612
- if (guarded) return;
3613
- let set = reqs.get(o.name);
3614
- if (!set) {
3615
- set = /* @__PURE__ */ new Set();
3616
- reqs.set(o.name, set);
4020
+ }
4021
+ for (const stmt of f.program.body) {
4022
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration) {
4023
+ const specs = stmt.specifiers ?? [];
4024
+ for (const spec of specs) {
4025
+ const s = spec;
4026
+ if (s.type !== "ExportSpecifier") continue;
4027
+ const localName = s.local?.name;
4028
+ if (!localName || exportedClassNames.has(localName)) continue;
4029
+ const exportedName = s.exported?.name ?? s.exported?.value;
4030
+ const isDefault = exportedName === "default";
4031
+ if (!isDefault && exportedName !== localName) {
3617
4032
  }
3618
- set.add(p.name);
4033
+ const decl = localClasses.get(localName);
4034
+ if (!decl) continue;
4035
+ exportedClassNames.add(localName);
4036
+ collectClassMethods(localName, decl);
3619
4037
  }
3620
4038
  }
3621
- if (obj2.type === "CallExpression") {
3622
- visit(obj2.callee, true, guarded);
3623
- for (const key of Object.keys(obj2)) {
3624
- if (key === "loc" || key === "start" || key === "end" || key === "callee") continue;
3625
- const val = obj2[key];
3626
- if (Array.isArray(val)) val.forEach((v2) => visit(v2, false, guarded));
3627
- else if (val && typeof val === "object") visit(val, false, guarded);
4039
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
4040
+ const d = stmt.declaration;
4041
+ if (d.type === "Identifier") {
4042
+ const localName = d.name;
4043
+ if (!exportedClassNames.has(localName)) {
4044
+ const decl = localClasses.get(localName);
4045
+ if (decl) {
4046
+ exportedClassNames.add(localName);
4047
+ collectClassMethods(localName, decl);
4048
+ }
4049
+ }
3628
4050
  }
3629
- return;
3630
- }
3631
- for (const key of Object.keys(obj2)) {
3632
- if (key === "loc" || key === "start" || key === "end") continue;
3633
- const val = obj2[key];
3634
- if (Array.isArray(val)) val.forEach((v2) => visit(v2, isMethodCallee, guarded));
3635
- else if (val && typeof val === "object") visit(val, isMethodCallee, guarded);
3636
4051
  }
3637
- };
3638
- visit(body);
3639
- return reqs;
4052
+ }
4053
+ return names;
4054
+ }
4055
+ function absUnknown() {
4056
+ return { shape: { k: "unknown" }, conf: "partial" };
3640
4057
  }
3641
4058
  function evalArgAbs(node, lookupVar) {
3642
4059
  if (node.type === "Identifier" && typeof node.name === "string" && lookupVar) {
@@ -4009,14 +4426,37 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4009
4426
  eiCache.set(key, r);
4010
4427
  return r;
4011
4428
  };
4012
- const interfaceToIndexed = (ei, paramNames) => {
4429
+ const projectArgField = (arg, field) => {
4430
+ if (!arg) return void 0;
4431
+ if (arg.shape.k === "brand") {
4432
+ return projectArgField(arg.shape.shape, field);
4433
+ }
4434
+ if (arg.shape.k !== "obj") return void 0;
4435
+ return getSlot(arg.shape.slots, field)?.value;
4436
+ };
4437
+ const interfaceToIndexed = (ei, paramNames, formals) => {
4013
4438
  const conflict = new Set(ei.conflict?.params ?? []);
4014
4439
  const entries = [];
4015
4440
  for (const { param, constraint } of ei.params) {
4016
- if (conflict.has(param)) continue;
4441
+ if (!param || conflict.has(param)) continue;
4017
4442
  const idx = paramNames.indexOf(param);
4018
4443
  if (idx >= 0) {
4019
- entries.push([idx, { param, pred: instantiateConstraint(constraint, param), constraint }]);
4444
+ entries.push([
4445
+ idx,
4446
+ { param, pred: instantiateConstraint(constraint, param), constraint },
4447
+ void 0
4448
+ ]);
4449
+ continue;
4450
+ }
4451
+ if (formals && formals.length > 0) {
4452
+ const hit = locateContractParam(formals, param);
4453
+ if (hit) {
4454
+ entries.push([
4455
+ hit.index,
4456
+ { param, pred: instantiateConstraint(constraint, param), constraint },
4457
+ hit.field
4458
+ ]);
4459
+ }
4020
4460
  }
4021
4461
  }
4022
4462
  return entries;
@@ -4050,15 +4490,36 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4050
4490
  return lv <= n;
4051
4491
  };
4052
4492
  const checkReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
4053
- for (const [idx, pred] of reqs) {
4493
+ for (const [idx, pred, field, contractParam] of reqs) {
4054
4494
  const argIdx = argIndexOf(idx);
4055
4495
  if (argIdx === void 0) continue;
4056
- const arg = absArgs[argIdx];
4496
+ let arg = absArgs[argIdx];
4057
4497
  if (!arg) continue;
4498
+ const paramName = contractParam || paramNames[idx] || `arg${idx}`;
4499
+ if (field) {
4500
+ const projected = projectArgField(arg, field);
4501
+ if (!projected) {
4502
+ const k = arg.shape.k;
4503
+ if (k !== "unknown" && k !== "any") {
4504
+ out.push({
4505
+ severity: "error",
4506
+ code: "nudo:constraint-violated",
4507
+ message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4508
+ actual: formatAbs(arg),
4509
+ expected: `missing field ${field}`,
4510
+ suggestion: `\u8865\u5168\u5B57\u6BB5 ${field}`,
4511
+ fn: displayName,
4512
+ line: loc?.start.line,
4513
+ column: loc?.start.column
4514
+ });
4515
+ }
4516
+ continue;
4517
+ }
4518
+ arg = projected;
4519
+ }
4058
4520
  const lv = litValue(arg);
4059
4521
  const isStr = arg.shape.k === "prim" && arg.shape.type === "string";
4060
4522
  const strLen = typeof lv === "string" ? lv.length : void 0;
4061
- const paramName = paramNames[idx] ?? `arg${idx}`;
4062
4523
  for (const p of flattenPred(pred)) {
4063
4524
  if (p.op === "typeof") {
4064
4525
  if (p.t.op !== "var") continue;
@@ -4135,14 +4596,13 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4135
4596
  if (p.op === "lt") ok2 = strLen < n;
4136
4597
  if (p.op === "le") ok2 = strLen <= n;
4137
4598
  if (!ok2) {
4138
- const paramName2 = paramNames[idx] ?? `arg${idx}`;
4139
4599
  out.push({
4140
4600
  severity: "error",
4141
4601
  code: "nudo:constraint-violated",
4142
- message: `${displayName}[${paramName2}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4602
+ message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4143
4603
  actual: formatAbs(arg),
4144
- expected: `length(${paramName2}) ${opSym} ${n}`,
4145
- suggestion: `\u6539\u7528\u6EE1\u8DB3\u957F\u5EA6 ${opSym} ${n} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName2} \u7684\u524D\u7F6E`,
4604
+ expected: `length(${paramName}) ${opSym} ${n}`,
4605
+ suggestion: `\u6539\u7528\u6EE1\u8DB3\u957F\u5EA6 ${opSym} ${n} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName} \u7684\u524D\u7F6E`,
4146
4606
  fn: displayName,
4147
4607
  line: loc?.start.line,
4148
4608
  column: loc?.start.column
@@ -4157,14 +4617,13 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4157
4617
  if (p.op === "lt") ok = lv < n;
4158
4618
  if (p.op === "le") ok = lv <= n;
4159
4619
  if (!ok) {
4160
- const paramName2 = paramNames[idx] ?? `arg${idx}`;
4161
4620
  out.push({
4162
4621
  severity: "error",
4163
4622
  code: "nudo:constraint-violated",
4164
- message: `${displayName}[${paramName2}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4623
+ message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4165
4624
  actual: formatAbs(arg),
4166
4625
  expected: predToString(p),
4167
- suggestion: `\u6539\u7528\u6EE1\u8DB3 ${predToString(p)} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName2} \u7684\u524D\u7F6E`,
4626
+ suggestion: `\u6539\u7528\u6EE1\u8DB3 ${predToString(p)} \u7684\u503C\uFF0C\u6216\u653E\u5BBD ${paramName} \u7684\u524D\u7F6E`,
4168
4627
  fn: displayName,
4169
4628
  line: loc?.start.line,
4170
4629
  column: loc?.start.column
@@ -4346,14 +4805,35 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4346
4805
  }
4347
4806
  };
4348
4807
  const checkShapeReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
4349
- for (const [idx, entry] of reqs) {
4808
+ for (const [idx, entry, field] of reqs) {
4350
4809
  const c = entry.constraint;
4351
4810
  if (!c.fields && !c.element && !isIntFlag(c) && !c.prim) continue;
4352
4811
  const argIdx = argIndexOf(idx);
4353
4812
  if (argIdx === void 0) continue;
4354
- const arg = absArgs[argIdx];
4813
+ let arg = absArgs[argIdx];
4355
4814
  if (!arg) continue;
4356
4815
  const paramName = entry.param || paramNames[idx] || `arg${idx}`;
4816
+ if (field) {
4817
+ const projected = projectArgField(arg, field);
4818
+ if (!projected) {
4819
+ const k = arg.shape.k;
4820
+ if (k !== "unknown" && k !== "any") {
4821
+ out.push({
4822
+ severity: "error",
4823
+ code: "nudo:constraint-violated",
4824
+ message: `${displayName}[${paramName}]: \u5B9E\u53C2 \u22AD \u524D\u7F6E`,
4825
+ actual: formatAbs(arg),
4826
+ expected: `missing field ${field}`,
4827
+ suggestion: `\u8865\u5168\u5B57\u6BB5 ${field}`,
4828
+ fn: displayName,
4829
+ line: loc?.start.line,
4830
+ column: loc?.start.column
4831
+ });
4832
+ }
4833
+ continue;
4834
+ }
4835
+ arg = projected;
4836
+ }
4357
4837
  if (c.fields) {
4358
4838
  checkShapeAgainstAbs(displayName, paramName, c, arg, paramName, loc);
4359
4839
  }
@@ -4468,11 +4948,11 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4468
4948
  };
4469
4949
  const ownEi = effectiveInterfaceOf(fnName, source, optsR);
4470
4950
  if (ownEi?.source === "handwritten") {
4471
- const ownFull = interfaceToIndexed(ownEi, paramNames);
4951
+ const ownFull = interfaceToIndexed(ownEi, paramNames, g?.formals);
4472
4952
  checkShapeReqs(fnName, ownFull, paramNames, absArgs, (i) => i, loc);
4473
4953
  checkReqs(
4474
4954
  fnName,
4475
- ownFull.map(([i, e]) => [i, e.pred]),
4955
+ ownFull.map(([i, e, f]) => [i, e.pred, f, e.param]),
4476
4956
  paramNames,
4477
4957
  absArgs,
4478
4958
  (i) => i,
@@ -4484,7 +4964,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4484
4964
  const tg = generalizeFromAst(fwd.target, source, file ? { file } : {});
4485
4965
  const tParams = tg?.params ?? [];
4486
4966
  const tEi = effectiveInterfaceOf(fwd.target, source, optsR);
4487
- const tFull = tEi?.source === "handwritten" ? interfaceToIndexed(tEi, tParams) : [];
4967
+ const tFull = tEi?.source === "handwritten" ? interfaceToIndexed(tEi, tParams, tg?.formals) : [];
4488
4968
  if (tFull.length > 0) {
4489
4969
  const wrapperArgOfTarget = /* @__PURE__ */ new Map();
4490
4970
  fwd.map.forEach((wrapperIdx, targetIdx) => {
@@ -4494,7 +4974,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4494
4974
  checkShapeReqs(`${fnName}\u2192${fwd.target}`, tFull, tParams, absArgs, mapArg, loc);
4495
4975
  checkReqs(
4496
4976
  `${fnName}\u2192${fwd.target}`,
4497
- tFull.map(([i, e]) => [i, e.pred]),
4977
+ tFull.map(([i, e, f]) => [i, e.pred, f, e.param]),
4498
4978
  tParams,
4499
4979
  absArgs,
4500
4980
  mapArg,
@@ -4520,7 +5000,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4520
5000
  };
4521
5001
  const ei = effectiveInterfaceOf(ext.fnName, ext.source, eiOpts);
4522
5002
  if (ei?.source === "handwritten") {
4523
- full = interfaceToIndexed(ei, paramNames);
5003
+ full = interfaceToIndexed(ei, paramNames, g?.formals);
4524
5004
  }
4525
5005
  } catch {
4526
5006
  out.push({
@@ -4536,7 +5016,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4536
5016
  checkShapeReqs(displayName, full, paramNames, absArgs, (i) => i, loc);
4537
5017
  checkReqs(
4538
5018
  displayName,
4539
- full.map(([i, e]) => [i, e.pred]),
5019
+ full.map(([i, e, f]) => [i, e.pred, f, e.param]),
4540
5020
  paramNames,
4541
5021
  absArgs,
4542
5022
  (i) => i,
@@ -4580,19 +5060,15 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4580
5060
  }
4581
5061
  }
4582
5062
  };
4583
- const checkArgStructures = (fnName, fnSource, args, loc, displayName, interfaceFromFile) => {
4584
- let structReqs;
4585
- let paramNames;
5063
+ const checkArgStructures = (fnName, fnSource, args, loc, displayName, _interfaceFromFile) => {
4586
5064
  let gFn;
4587
5065
  try {
4588
5066
  const sameFile = fnSource === source;
4589
- structReqs = collectParamStructReqs(fnSource, fnName, sameFile ? file : void 0);
4590
5067
  gFn = generalizeFromAst(
4591
5068
  fnName,
4592
5069
  fnSource,
4593
5070
  sameFile && file ? { file } : {}
4594
5071
  );
4595
- paramNames = gFn?.params ?? [];
4596
5072
  } catch (e) {
4597
5073
  out.push({
4598
5074
  severity: "warning",
@@ -4613,65 +5089,6 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4613
5089
  (n) => evalArgAbs(n, (x) => varAbs.get(x))
4614
5090
  );
4615
5091
  }
4616
- if (structReqs.size === 0) return;
4617
- let refinedParams;
4618
- try {
4619
- const sameFile = fnSource === source;
4620
- const eiOpts = sameFile ? {
4621
- loadModule: opts?.loadModule,
4622
- fromFile: opts?.fromFile ?? "",
4623
- ...opts?.autoBind !== void 0 ? { autoBind: opts.autoBind } : {}
4624
- } : {
4625
- loadModule: opts?.loadModule,
4626
- fromFile: interfaceFromFile ?? opts?.fromFile ?? "",
4627
- ...interfaceFromFile ? opts?.autoBind !== void 0 ? { autoBind: opts.autoBind } : {} : { autoBind: false }
4628
- };
4629
- const ei = effectiveInterfaceOf(fnName, fnSource, eiOpts);
4630
- if (ei?.source === "handwritten" && ei.params.length > 0) {
4631
- refinedParams = new Set(ei.params.map((p) => p.param));
4632
- }
4633
- } catch {
4634
- out.push({
4635
- severity: "warning",
4636
- code: "nudo:no-signature",
4637
- message: `${displayName ?? fnName}: handwritten interface load failed; falling back to body structure inference`,
4638
- fn: displayName ?? fnName,
4639
- line: loc?.start.line,
4640
- column: loc?.start.column
4641
- });
4642
- }
4643
- for (let i = 0; i < args.length; i++) {
4644
- const pname = paramNames[i];
4645
- if (!pname) continue;
4646
- if (refinedParams?.has(pname)) continue;
4647
- const keys = structReqs.get(pname);
4648
- if (!keys || keys.size === 0) continue;
4649
- const argNode = args[i];
4650
- if (!argNode) continue;
4651
- const absArg = evalArgAbs(argNode, (n) => varAbs.get(n));
4652
- if (!absArg || absArg.shape.k === "unknown") continue;
4653
- const isLeny = absArg.shape.k === "arr" || absArg.shape.k === "tuple" || absArg.shape.k === "prim" && absArg.shape.type === "string";
4654
- const needKeys = isLeny ? [...keys].filter((k) => k !== "length") : [...keys];
4655
- if (needKeys.length === 0) continue;
4656
- const slots = {};
4657
- for (const k of needKeys) slots[k] = { value: absUnknown() };
4658
- const target = abs({ k: "obj", slots }, void 0, void 0, "exact");
4659
- const leq = leqAbs(absArg, target);
4660
- if (!leq.ok) {
4661
- const name = displayName ?? fnName;
4662
- out.push({
4663
- severity: "error",
4664
- code: "nudo:arg-structure",
4665
- message: `${name}[${pname}]: \u5B9E\u53C2\u7ED3\u6784 \u22AD \u5F62\u53C2`,
4666
- actual: formatAbs(absArg),
4667
- expected: formatAbs(target),
4668
- suggestion: leq.reason ?? `\u8865\u5168 ${pname} \u4E0A\u88AB\u8BBF\u95EE\u7684\u5B57\u6BB5`,
4669
- fn: name,
4670
- line: loc?.start.line,
4671
- column: loc?.start.column
4672
- });
4673
- }
4674
- }
4675
5092
  };
4676
5093
  const visit = (n) => {
4677
5094
  if (!n || typeof n !== "object") return;
@@ -4768,6 +5185,14 @@ function extractLiteralEvidence(rec, idx) {
4768
5185
  }
4769
5186
 
4770
5187
  // src/algebra/check.ts
5188
+ function projectCaseArgField(arg, field) {
5189
+ if (!arg) return void 0;
5190
+ if (arg.shape.k === "brand") {
5191
+ return projectCaseArgField(arg.shape.shape, field);
5192
+ }
5193
+ if (arg.shape.k !== "obj") return void 0;
5194
+ return getSlot(arg.shape.slots, field)?.value;
5195
+ }
4771
5196
  var checkReportMemo = /* @__PURE__ */ new Map();
4772
5197
  var checkKeyDeps = /* @__PURE__ */ new Map();
4773
5198
  var checkDepIndex = /* @__PURE__ */ new Map();
@@ -4791,7 +5216,7 @@ function loadModuleId2(fn2) {
4791
5216
  }
4792
5217
  function checkDepsFingerprint(source, opts) {
4793
5218
  if (!opts.loadModule || !opts.fromFile) {
4794
- return { fp: "-", paths: [], truncated: false };
5219
+ return { fp: "-", paths: [], contents: [], truncated: false };
4795
5220
  }
4796
5221
  return loadModuleDepsFingerprint(source, opts.loadModule, opts.fromFile);
4797
5222
  }
@@ -4838,7 +5263,8 @@ function checkMemoKey(filePath, source, identityOpts, deps, sidecarFp) {
4838
5263
  filePath,
4839
5264
  `${loadModuleId2(identityOpts.loadModule)}:${identityOpts.fromFile ?? ""}`,
4840
5265
  deps.fp,
4841
- sidecarFp ?? "-"
5266
+ sidecarFp ?? "-",
5267
+ identityOpts.autoBind === false ? "ab0" : "ab1"
4842
5268
  ].join("|");
4843
5269
  }
4844
5270
  function checkMemoGet(key) {
@@ -5011,6 +5437,26 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
5011
5437
  });
5012
5438
  if (hasRefineDirective || (exportedNames?.has(name) ?? false)) {
5013
5439
  const eff = effectiveInterfaceCached(name);
5440
+ if (eff?.source === "handwritten") {
5441
+ const contractNames = contractParamNameSet(g.formals ?? []);
5442
+ const formalDisplay = new Set(g.params);
5443
+ const conflictNames = new Set(eff.conflict?.params ?? []);
5444
+ const unknownParams = eff.params.map((p) => p.param).filter(
5445
+ (p) => p && !formalDisplay.has(p) && !contractNames.has(p) && !conflictNames.has(p)
5446
+ );
5447
+ if (unknownParams.length > 0) {
5448
+ const surface = [...contractNames].filter((n) => !n.startsWith("_p") && n !== "_").join(", ");
5449
+ issues.push({
5450
+ severity: "error",
5451
+ code: "nudo:interface-param-mismatch",
5452
+ message: `${name}: \u5951\u7EA6\u53C2\u6570\u540D\u4E0D\u5728\u5F62\u53C2\u8868\uFF08${unknownParams.join(", ")}\uFF09`,
5453
+ actual: unknownParams.join(", "),
5454
+ expected: surface || g.params.join(", ") || "(\u65E0\u53C2)",
5455
+ suggestion: `\u628A @nudo:refine / \u4FA7\u8F66\u7ED1\u5B9A\u53C2\u6570\u540D\u6539\u6210\u5F62\u53C2\u4E4B\u4E00\uFF1A${surface || g.params.join(", ") || "\uFF08\u51FD\u6570\u65E0\u53C2\uFF09"}`,
5456
+ fn: name
5457
+ });
5458
+ }
5459
+ }
5014
5460
  if (eff?.conflict) {
5015
5461
  if (eff.conflict.params.length > 0) {
5016
5462
  issues.push({
@@ -5340,10 +5786,19 @@ function scanCaseInconsistency(source, knownFns, opts) {
5340
5786
  });
5341
5787
  if (!eff || eff.source !== "handwritten") return;
5342
5788
  const conflictParams = new Set(eff.conflict?.params ?? []);
5789
+ const formals = g.formals ?? [];
5343
5790
  const reqs = [];
5344
5791
  for (const p of eff.params) {
5345
5792
  if (conflictParams.has(p.param)) continue;
5346
- const idx = paramNames.indexOf(p.param);
5793
+ let idx = paramNames.indexOf(p.param);
5794
+ let field;
5795
+ if (idx < 0 && formals.length > 0) {
5796
+ const hit = locateContractParam(formals, p.param);
5797
+ if (hit) {
5798
+ idx = hit.index;
5799
+ field = hit.field;
5800
+ }
5801
+ }
5347
5802
  if (idx < 0) continue;
5348
5803
  reqs.push([
5349
5804
  idx,
@@ -5351,15 +5806,40 @@ function scanCaseInconsistency(source, knownFns, opts) {
5351
5806
  param: p.param,
5352
5807
  pred: instantiateConstraint(p.constraint, p.param),
5353
5808
  constraint: p.constraint
5354
- }
5809
+ },
5810
+ field
5355
5811
  ]);
5356
5812
  }
5357
5813
  if (reqs.length === 0) return;
5358
5814
  const absArgs = args.map((a) => parseLitArg(a) ?? absUnknown());
5359
- for (const [idx, entry] of reqs) {
5815
+ for (const req of reqs) {
5816
+ const idx = req[0];
5817
+ const entry = req[1];
5818
+ const field = req[2];
5360
5819
  if (entry.constraint.fields) continue;
5361
- const arg = absArgs[idx];
5820
+ let arg = absArgs[idx];
5362
5821
  if (!arg) continue;
5822
+ if (field) {
5823
+ const projected = projectCaseArgField(arg, field);
5824
+ if (!projected) {
5825
+ const k = arg.shape.k;
5826
+ if (k !== "unknown" && k !== "any") {
5827
+ const paramName = entry.param || paramNames[idx] || `arg${idx}`;
5828
+ out.push({
5829
+ severity: "error",
5830
+ code: "nudo:case-inconsistency",
5831
+ message: `${fnName} case "${caseName}": \u89C1\u8BC1 \u22AD \u5951\u7EA6`,
5832
+ actual: formatAbs(arg),
5833
+ expected: `missing field ${field}`,
5834
+ suggestion: `case \u5B9E\u53C2\u8865\u5168\u5B57\u6BB5 ${field}\uFF08\u5951\u7EA6\u4F4D ${paramName}\uFF09`,
5835
+ fn: fnName,
5836
+ line
5837
+ });
5838
+ }
5839
+ continue;
5840
+ }
5841
+ arg = projected;
5842
+ }
5363
5843
  const lv = litValue(arg);
5364
5844
  if (lv === void 0 || typeof lv !== "number" && typeof lv !== "string" && typeof lv !== "boolean") {
5365
5845
  continue;
@@ -5658,8 +6138,12 @@ function formatReturnDisplay(g) {
5658
6138
  }
5659
6139
  return memberPath(abs2).text;
5660
6140
  }
5661
- function collectAbsInlays(source, refineOpts) {
6141
+ function collectAbsInlays(source, opts) {
5662
6142
  const inlays = [];
6143
+ const refineOpts = opts?.loadModule || opts?.fromFile ? {
6144
+ ...opts.loadModule ? { loadModule: opts.loadModule } : {},
6145
+ ...opts.fromFile ? { fromFile: opts.fromFile } : {}
6146
+ } : void 0;
5663
6147
  for (const { name, node } of listFunctions(source)) {
5664
6148
  let g;
5665
6149
  try {
@@ -5668,6 +6152,12 @@ function collectAbsInlays(source, refineOpts) {
5668
6152
  continue;
5669
6153
  }
5670
6154
  if (!g) continue;
6155
+ const tier = opts?.fromFile !== void 0 ? interfaceTierOf(source, name, opts.fromFile, {
6156
+ ...opts.loadModule ? { loadModule: opts.loadModule } : {},
6157
+ ...opts.autoBind !== void 0 ? { autoBind: opts.autoBind } : {}
6158
+ }) : void 0;
6159
+ const tierSrc = tier?.source;
6160
+ const isImplicitExport = tierSrc === "implicit";
5671
6161
  const params = g.params;
5672
6162
  const predsByName = /* @__PURE__ */ new Map();
5673
6163
  if (g.entryReqs) {
@@ -5688,7 +6178,8 @@ function collectAbsInlays(source, refineOpts) {
5688
6178
  line: p.loc.end.line,
5689
6179
  character: p.loc.end.column,
5690
6180
  label: ` where ${text}`,
5691
- kind: "parameter"
6181
+ kind: "parameter",
6182
+ ...tierSrc ? { interfaceSource: tierSrc } : {}
5692
6183
  });
5693
6184
  }
5694
6185
  }
@@ -5700,11 +6191,14 @@ function collectAbsInlays(source, refineOpts) {
5700
6191
  } catch {
5701
6192
  label = `: ${formatShape(g.symbolic)}`;
5702
6193
  }
6194
+ if (isImplicitExport) label += " \xB7 derived";
5703
6195
  inlays.push({
5704
6196
  line: bodyLoc.start.line,
5705
6197
  character: Math.max(0, bodyLoc.start.column),
5706
6198
  label: `${label} `,
5707
- kind: "type"
6199
+ kind: "type",
6200
+ ...tierSrc ? { interfaceSource: tierSrc } : {},
6201
+ ...isImplicitExport ? { derived: true } : {}
5708
6202
  });
5709
6203
  }
5710
6204
  }
@@ -5882,6 +6376,8 @@ function termIsValue(t, a) {
5882
6376
  export {
5883
6377
  $add,
5884
6378
  $arr,
6379
+ $arrMutContainer,
6380
+ $arrRest,
5885
6381
  $async,
5886
6382
  $asyncReturn,
5887
6383
  $await,
@@ -5893,6 +6389,7 @@ export {
5893
6389
  $div,
5894
6390
  $elems,
5895
6391
  $eq,
6392
+ $eqLoose,
5896
6393
  $fnVal,
5897
6394
  $for,
5898
6395
  $forIter,
@@ -5906,22 +6403,29 @@ export {
5906
6403
  $idxSet,
5907
6404
  $invoke,
5908
6405
  $invokeSuper,
6406
+ $isForkExit,
5909
6407
  $join,
5910
6408
  $le,
5911
6409
  $len,
5912
6410
  $lit,
6411
+ $loopReturn,
5913
6412
  $lt,
5914
6413
  $mod,
5915
6414
  $mul,
5916
6415
  $ne,
6416
+ $neLoose,
5917
6417
  $neg,
5918
6418
  $new,
5919
6419
  $not,
6420
+ $nullishTest,
5920
6421
  $obj,
6422
+ $objRest,
5921
6423
  $optionalGet,
5922
6424
  $optionalInvoke,
5923
6425
  $orDefault,
6426
+ $pushLoopExit,
5924
6427
  $regex,
6428
+ $rethrowIfNudoReturn,
5925
6429
  $set,
5926
6430
  $setKey,
5927
6431
  $spread,
@@ -5932,6 +6436,10 @@ export {
5932
6436
  $thisGet,
5933
6437
  $thisSet,
5934
6438
  $throw,
6439
+ $tryCurrentMark,
6440
+ $tryMark,
6441
+ $tryPopMark,
6442
+ $tryTakeSince,
5935
6443
  $typeof,
5936
6444
  $while,
5937
6445
  $whileSeq,
@@ -5939,6 +6447,7 @@ export {
5939
6447
  DEFAULT_MAX_LOOP_ITERS,
5940
6448
  MAX_CALL_DEPTH,
5941
6449
  MAX_TOTAL_CALLS,
6450
+ NudoReturn,
5942
6451
  NudoSidecarError,
5943
6452
  NudoThrow,
5944
6453
  SELF,
@@ -5955,6 +6464,7 @@ export {
5955
6464
  analyzeFn,
5956
6465
  analyzeFnFull,
5957
6466
  and,
6467
+ any,
5958
6468
  anyVar,
5959
6469
  app,
5960
6470
  applyAbsFn,
@@ -5964,6 +6474,7 @@ export {
5964
6474
  asAbsVal,
5965
6475
  attachFnImpl,
5966
6476
  awaitAbs,
6477
+ beginCollectionFork,
5967
6478
  beginDerivationSession,
5968
6479
  betaOf,
5969
6480
  bindImports,
@@ -5972,6 +6483,7 @@ export {
5972
6483
  boolean,
5973
6484
  buildArgsFromAssume,
5974
6485
  callAbsMethod,
6486
+ callAtFunctionBoundary,
5975
6487
  callFunction,
5976
6488
  callFunctionFull,
5977
6489
  callTranspiledExport,
@@ -5983,14 +6495,19 @@ export {
5983
6495
  checkSource,
5984
6496
  classFromMethods,
5985
6497
  clearBClasses,
6498
+ clearCollectionTables,
5986
6499
  cmp,
5987
6500
  coerceAsyncReturn,
5988
6501
  collectAbsExports,
6502
+ collectAbsFreeVars,
5989
6503
  collectAbsInlays,
5990
6504
  collectAbsNodeTypes,
6505
+ collectionElementJoin,
6506
+ collectionExactLen,
5991
6507
  concatString,
5992
6508
  confJoin,
5993
6509
  constraintToEntryAbs,
6510
+ contractParamNameSet,
5994
6511
  createEnvironment,
5995
6512
  createHofCollectCtx,
5996
6513
  createTemplateAbs,
@@ -6009,8 +6526,10 @@ export {
6009
6526
  effectiveInterface,
6010
6527
  emptyEnv,
6011
6528
  emptyPhi,
6529
+ endCollectionFork,
6012
6530
  endDerivationSession,
6013
6531
  eq,
6532
+ errorBrandAbs,
6014
6533
  evalArrayStatic,
6015
6534
  evalBuiltinInstanceMethod,
6016
6535
  evalBuiltinNew,
@@ -6047,11 +6566,15 @@ export {
6047
6566
  fnConstraintToEntryReqs,
6048
6567
  fnFingerprints,
6049
6568
  fnOf,
6569
+ formalParamDisplayNames,
6570
+ formalParamsFromNodes,
6050
6571
  formatAbs,
6051
6572
  formatAbsMultiline,
6052
6573
  formatCheckReport,
6053
6574
  formatConstraint,
6054
6575
  formatDiagnostics,
6576
+ formatEffectiveInterfaceDisplay,
6577
+ formatInterfaceTierLine,
6055
6578
  formatShape,
6056
6579
  formatShapeSlot,
6057
6580
  formatTemplateNameViews,
@@ -6084,17 +6607,25 @@ export {
6084
6607
  instantiateConstraint,
6085
6608
  instantiateReturn,
6086
6609
  interfaceDiagCount,
6610
+ interfaceSourceOf,
6611
+ interfaceTierOf,
6612
+ isArrMutator,
6087
6613
  isDefinitelyFalse,
6088
6614
  isDefinitelyTrue,
6615
+ isErrorCtorName,
6616
+ isEvalMissingSlotEnabled,
6089
6617
  isExactLit,
6090
6618
  isIntFlag,
6619
+ isMapAbs,
6091
6620
  isNodeModulesPath,
6092
6621
  isNudoConstraint,
6622
+ isNudoReturn,
6093
6623
  isNudoThrow,
6094
6624
  isNullishLitAbs,
6095
6625
  isNumPrim,
6096
6626
  isObj,
6097
6627
  isRelFn,
6628
+ isSetAbs,
6098
6629
  isStrPrim,
6099
6630
  isTemplateLike,
6100
6631
  joinAbs,
@@ -6116,16 +6647,29 @@ export {
6116
6647
  literalMeetsConstraint,
6117
6648
  loadModuleDepsFingerprint,
6118
6649
  localNamedExports,
6650
+ locateContractParam,
6119
6651
  lookupMethod,
6120
6652
  lookupMethodWithOwner,
6121
6653
  lookupSuperMethod,
6654
+ looseEqAbs,
6122
6655
  lt,
6123
6656
  ltNum,
6657
+ makeMapAbs,
6658
+ makeSetAbs,
6124
6659
  makeSum,
6660
+ mapClearEntries,
6661
+ mapDeleteEntry,
6125
6662
  mapElementFallback,
6663
+ mapEntriesAbs,
6664
+ mapGetEntry,
6665
+ mapHasEntry,
6666
+ mapSetEntry,
6667
+ mapSizeAbs,
6668
+ mapValuesAbs,
6126
6669
  matchRelIdentLit,
6127
6670
  maybeLeak,
6128
6671
  mergeAdjacentFixedViews,
6672
+ mergeCollectionArms,
6129
6673
  mock,
6130
6674
  mod,
6131
6675
  mul,
@@ -6136,9 +6680,11 @@ export {
6136
6680
  normPath,
6137
6681
  not,
6138
6682
  notAbs,
6683
+ noteCollectionWrite,
6139
6684
  noteDerivationAdd,
6140
6685
  noteDerivationJoin,
6141
6686
  noteMemberDispatchMiss,
6687
+ noteObjSlotMissing,
6142
6688
  notePrimMemberMissing,
6143
6689
  noteUnknownMemberMissing,
6144
6690
  num,
@@ -6155,6 +6701,7 @@ export {
6155
6701
  partial,
6156
6702
  phiAnd,
6157
6703
  pick,
6704
+ popCollectionArm,
6158
6705
  popPhi,
6159
6706
  predEquals,
6160
6707
  predToString,
@@ -6164,7 +6711,10 @@ export {
6164
6711
  projectFlatMapResult,
6165
6712
  promoteParamShape,
6166
6713
  ptypeof,
6714
+ pushCollectionArm,
6715
+ pushLoopExit,
6167
6716
  pushPhi,
6717
+ pushThrowExit,
6168
6718
  recordMemberDiag,
6169
6719
  refineAbsForRelTrue,
6170
6720
  refineDiagCount,
@@ -6172,6 +6722,7 @@ export {
6172
6722
  registerBClass,
6173
6723
  relationFingerprint,
6174
6724
  relationFn,
6725
+ requiredFnArity,
6175
6726
  resetAbsCallBudget,
6176
6727
  resetCheckSourceMemo,
6177
6728
  resetFnFpCache,
@@ -6183,18 +6734,27 @@ export {
6183
6734
  resetPhi,
6184
6735
  resolveDepPath,
6185
6736
  runTranspiled,
6737
+ runWithEvalMissingSlot,
6738
+ runWithLoopExits,
6186
6739
  serializeCheckJson,
6187
6740
  setAbsAssignCollector,
6188
6741
  setAbsCallCollector,
6189
6742
  setAbsNodeCollector,
6190
6743
  setAbsTruncationCollector,
6744
+ setAddEntry,
6191
6745
  setApplyCallbackHost,
6192
6746
  setBCallCollector,
6747
+ setClearEntries,
6748
+ setDeleteEntry,
6193
6749
  setDerivation,
6194
6750
  setDerivationCollector,
6751
+ setElementsAbs,
6752
+ setEvalMissingSlotEnabled,
6753
+ setHasEntry,
6195
6754
  setInterfaceDiagCollector,
6196
6755
  setMemberDiagCollector,
6197
6756
  setRefineDiagCollector,
6757
+ setSizeAbs,
6198
6758
  shape,
6199
6759
  shapeOfTerm,
6200
6760
  shapeOnlyFn,
@@ -6222,8 +6782,10 @@ export {
6222
6782
  tagDerivationRoot,
6223
6783
  takeInterfaceDiags,
6224
6784
  takeInterfaceDiagsSince,
6785
+ takeLoopExits,
6225
6786
  takeRefineDiags,
6226
6787
  takeRefineDiagsSince,
6788
+ takeThrowExits,
6227
6789
  templateMatchesValue,
6228
6790
  templatePartsOf,
6229
6791
  termDepth,