@nudojs/core 1.2.2 → 1.4.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.
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import "./chunk-US532PZA.js";
2
2
  import {
3
+ CONSTRAINT_BUILDERS,
4
+ CONSTRAINT_BUILDER_NAMES,
5
+ CONSTRAINT_EXPR_RE,
3
6
  NudoSidecarError,
4
7
  SELF,
5
8
  andC,
@@ -9,17 +12,25 @@ import {
9
12
  buildArgsFromAssume,
10
13
  canSkipLiteralCallScan,
11
14
  collectAbsFreeVars,
15
+ constraintAdmitsNullish,
12
16
  constraintToEntryAbs,
13
17
  effectiveInterface,
14
18
  evictGeneralizeMemoForPaths,
15
19
  execNudoModule,
20
+ extractBalancedParens,
16
21
  extractDeclaredThrows,
22
+ extractFileEnvNames,
17
23
  extractFn,
24
+ extractFnBudget,
25
+ extractMockModuleRecords,
26
+ extractNudoImportRecords,
18
27
  extractNudoImports,
19
28
  extractRefineReturnFromSource,
20
29
  extractRefinesFromSource,
30
+ findFnDirectiveScope,
21
31
  fn,
22
32
  fnConstraintToEntryReqs,
33
+ fnDirectiveCommentLines,
23
34
  formatConstraint,
24
35
  formatEffectiveInterfaceDisplay,
25
36
  formatInterfaceTierLine,
@@ -30,6 +41,7 @@ import {
30
41
  getTerm,
31
42
  hashSource,
32
43
  instantiateConstraint,
44
+ instantiateOnTerm,
33
45
  interfaceDiagCount,
34
46
  interfaceSourceOf,
35
47
  interfaceTierOf,
@@ -37,6 +49,7 @@ import {
37
49
  isNodeModulesPath,
38
50
  isNudoConstraint,
39
51
  lenTerm,
52
+ listFnDirectiveScopes,
40
53
  listFunctionNames,
41
54
  listTopFunctions,
42
55
  litC,
@@ -44,8 +57,12 @@ import {
44
57
  loadModuleDepsFingerprint,
45
58
  localNamedExports,
46
59
  normPath,
60
+ nullable,
47
61
  number,
48
62
  omit,
63
+ parseEnvPayload,
64
+ parseMockModulePayload,
65
+ parseNudoImportPayload,
49
66
  partial,
50
67
  pick,
51
68
  refineDiagCount,
@@ -53,13 +70,20 @@ import {
53
70
  resetGeneralizeMemo,
54
71
  resetHashSourceCache,
55
72
  resetNudoModuleExecCache,
73
+ resetSidecarLoadFailureCache,
74
+ scanBudgetDecl,
75
+ scanCaseArgSpans,
76
+ scanCaseTags,
77
+ scanContractSegments,
56
78
  scanLiteralCalls,
79
+ scanThrowsDecl,
57
80
  setInterfaceDiagCollector,
58
81
  setRefineDiagCollector,
59
82
  shape,
60
83
  sidecarClosureFingerprint,
61
84
  sidecarPathOf,
62
85
  stableAnalyzeKeySource,
86
+ stablePathKey,
63
87
  string,
64
88
  takeInterfaceDiags,
65
89
  takeInterfaceDiagsSince,
@@ -67,7 +91,7 @@ import {
67
91
  takeRefineDiagsSince,
68
92
  throwConstraintToKinds,
69
93
  union
70
- } from "./chunk-TOURNNEA.js";
94
+ } from "./chunk-7QUVTEYZ.js";
71
95
  import {
72
96
  $add,
73
97
  $arguments,
@@ -189,10 +213,13 @@ import {
189
213
  NudoThrow,
190
214
  NudoUnsupportedError,
191
215
  OBJECT_PROTO_METHOD_NAMES,
216
+ PROMISE_MICRO_ERROR_LABEL,
217
+ PROMISE_MICRO_OVERFLOW_LABEL,
192
218
  RUNTIME_IMPORT_RE,
193
219
  TYPEOF_NAMES,
194
220
  abs,
195
221
  absFunction,
222
+ absOnly,
196
223
  absShapeKey,
197
224
  absToString,
198
225
  add,
@@ -206,10 +233,12 @@ import {
206
233
  asAbs,
207
234
  asAbsVal,
208
235
  assignSourceSlots,
236
+ assumeFinite,
209
237
  attachFnImpl,
210
238
  beginCollectionFork,
211
239
  betaOf,
212
240
  bigintLit,
241
+ bindImports,
213
242
  bindingsOf,
214
243
  bitandAbs,
215
244
  bitnotAbs,
@@ -222,12 +251,14 @@ import {
222
251
  callAbsMethod,
223
252
  callAtFunctionBoundary,
224
253
  callTranspiledExport,
254
+ callTranspiledExportApply,
225
255
  callTranspiledExportFull,
226
256
  canonicalArrayIndex,
227
257
  clearBClasses,
228
258
  clearCollectionTables,
229
259
  clearStaleTermPred,
230
260
  cmp,
261
+ collectAbsExports,
231
262
  collectionElementJoin,
232
263
  collectionExactLen,
233
264
  confJoin,
@@ -275,6 +306,7 @@ import {
275
306
  foldRequireSpecArg,
276
307
  foldStaticStringExpr,
277
308
  formalParamDisplayNames,
309
+ formalParamSignatureNames,
278
310
  formalParamsFromNodes,
279
311
  formatAbs,
280
312
  formatAbsMultiline,
@@ -295,8 +327,10 @@ import {
295
327
  gt,
296
328
  gtNum,
297
329
  hostBuiltinCtorName,
330
+ identBoundaryRegex,
298
331
  implies,
299
332
  instantiateReturn,
333
+ isAbsApplyResult,
300
334
  isArrMutator,
301
335
  isBigPrim,
302
336
  isDefinitelyFalse,
@@ -333,6 +367,7 @@ import {
333
367
  looseEqAbs,
334
368
  lt,
335
369
  ltNum,
370
+ makeAbsApplyResult,
336
371
  makeArrayCtorAbs,
337
372
  makeMapAbs,
338
373
  makeSetAbs,
@@ -357,6 +392,7 @@ import {
357
392
  migrateNullProto,
358
393
  mod,
359
394
  mul,
395
+ namespaceAbsOf,
360
396
  namespaceNameOf,
361
397
  ne,
362
398
  negAbs,
@@ -438,6 +474,7 @@ import {
438
474
  strLit,
439
475
  strictEqAbs,
440
476
  stringOfSymbol,
477
+ stripStringsKeepComments,
441
478
  stripTypes,
442
479
  sub,
443
480
  substAbs,
@@ -470,7 +507,7 @@ import {
470
507
  v,
471
508
  withExecPhi,
472
509
  withVar
473
- } from "./chunk-4V3CRVSD.js";
510
+ } from "./chunk-LFCF6BLE.js";
474
511
 
475
512
  // src/environment.ts
476
513
  function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
@@ -599,15 +636,15 @@ function checkArg(arg, expect, phi = pTrue) {
599
636
  }
600
637
  const combined = arg.pred && arg.pred.op !== "true" ? phi.op === "true" ? arg.pred : { op: "and", args: [phi, arg.pred] } : phi;
601
638
  if (implies(combined, expect)) return void 0;
602
- const lv = litValue(arg);
603
- if (lv !== void 0 && (expect.op === "gt" || expect.op === "ge" || expect.op === "lt" || expect.op === "le")) {
604
- const ok = decideLitCmp(lv, expect);
639
+ const lvR = litValue(arg);
640
+ if (lvR.ok && (expect.op === "gt" || expect.op === "ge" || expect.op === "lt" || expect.op === "le")) {
641
+ const ok = decideLitCmp(lvR.value, expect);
605
642
  if (ok === true) return void 0;
606
643
  if (ok === false) {
607
644
  return {
608
645
  severity: "error",
609
646
  code: "nudo:constraint-violated",
610
- message: `argument ${JSON.stringify(lv)} does not satisfy ${predToString(expect)}`,
647
+ message: `argument ${JSON.stringify(lvR.value)} does not satisfy ${predToString(expect)}`,
611
648
  suggestion: `use a value satisfying the constraint, or relax the function precondition`
612
649
  };
613
650
  }
@@ -703,6 +740,257 @@ function formatDiagnostics(diags) {
703
740
  }).join("\n");
704
741
  }
705
742
 
743
+ // src/algebra/postcondition.ts
744
+ function proved() {
745
+ return { status: "proved" };
746
+ }
747
+ function disproved(reason) {
748
+ return { status: "disproved", reason };
749
+ }
750
+ function unprovable(reason) {
751
+ return { status: "unprovable", reason };
752
+ }
753
+ var RET_ANCHOR = "__nudo_return__";
754
+ function retAnchorTerm(ret) {
755
+ return ret.term ?? v(RET_ANCHOR);
756
+ }
757
+ function isValueSetUnknown(ret) {
758
+ if (ret.conf === "opaque" || ret.conf === "widened" || ret.conf === "mock") {
759
+ return true;
760
+ }
761
+ if (ret.shape.k === "unknown" && !ret.term) return true;
762
+ if (ret.shape.k === "any") return true;
763
+ return false;
764
+ }
765
+ function isBoundedEvidenceMissing(ret, constraint) {
766
+ if (constraint.preds.length === 0 && !constraint.members) return false;
767
+ if (ret.term?.op === "lit") return false;
768
+ if (ret.pred && ret.pred.op !== "true") return false;
769
+ return true;
770
+ }
771
+ function assertImplies(ret, constraint, opts) {
772
+ const phi = opts?.phi ?? pTrue;
773
+ if (ret.shape.k === "never") return proved();
774
+ if (isAnyConstraint(constraint)) return proved();
775
+ const nullishOk = constraintAdmitsNullish(constraint);
776
+ if (isNullishLitAbs(ret)) {
777
+ return nullishOk ? proved() : disproved(`nullish return \u22AD ${formatConstraint(constraint)}`);
778
+ }
779
+ let sumMembers;
780
+ if (ret.shape.k === "sum") {
781
+ const members = ret.shape.members;
782
+ const nullishMembers = members.filter((m) => isNullishLitAbs(m));
783
+ if (nullishMembers.length > 0 && !nullishOk) {
784
+ return disproved(
785
+ `nullish return arm \u22AD ${formatConstraint(constraint)} (contract does not admit nullish)`
786
+ );
787
+ }
788
+ sumMembers = members.filter((m) => !isNullishLitAbs(m));
789
+ }
790
+ if (isValueSetUnknown(ret)) {
791
+ return unprovable(
792
+ ret.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
793
+ );
794
+ }
795
+ if (constraint.members && constraint.members.length > 0) {
796
+ let sawUnprovable = false;
797
+ for (const m of constraint.members) {
798
+ const r = assertImplies(ret, m, opts);
799
+ if (r.status === "proved") return proved();
800
+ if (r.status === "unprovable") sawUnprovable = true;
801
+ }
802
+ return sawUnprovable ? unprovable("union: no member proved the return contract") : disproved(`return \u22AD union(${constraint.members.map((m) => formatConstraint(m)).join(", ")})`);
803
+ }
804
+ if (sumMembers && (constraint.fields || constraint.element)) {
805
+ let sawUnprovable = false;
806
+ for (const m of sumMembers) {
807
+ if (isValueSetUnknown(m)) continue;
808
+ const r = assertImplies(m, constraint, opts);
809
+ if (r.status === "disproved") return r;
810
+ if (r.status === "unprovable") sawUnprovable = true;
811
+ }
812
+ return sawUnprovable ? unprovable("sum: some arms unprovable") : proved();
813
+ }
814
+ if (constraint.fields) {
815
+ return assertShapeFields(ret, constraint, opts);
816
+ }
817
+ if (constraint.element) {
818
+ if (ret.shape.k !== "arr" && ret.shape.k !== "tuple") {
819
+ return disproved(`return shape ${ret.shape.k} \u22AD array(...)`);
820
+ }
821
+ return proved();
822
+ }
823
+ if (constraint.fn) return proved();
824
+ return assertScalar(ret, constraint, phi);
825
+ }
826
+ function isAnyConstraint(c) {
827
+ return !c.prim && c.preds.length === 0 && !c.fields && !c.element && !c.members && !c.fn && !isIntFlag(c);
828
+ }
829
+ function assertShapeFields(ret, constraint, opts) {
830
+ let slots;
831
+ if (ret.shape.k === "obj") {
832
+ slots = ret.shape.slots;
833
+ } else if (ret.shape.k === "brand") {
834
+ const inner = ret.shape.shape;
835
+ if (inner.shape.k === "obj") slots = inner.shape.slots;
836
+ }
837
+ if (!slots) {
838
+ return disproved(`return shape ${ret.shape.k} \u22AD object shape`);
839
+ }
840
+ for (const [key, field] of Object.entries(constraint.fields)) {
841
+ const slot = getSlot(slots, key);
842
+ if (!slot) {
843
+ if (!field.optional && !field.constraint.isOptional) {
844
+ return disproved(`missing field ${key}`);
845
+ }
846
+ continue;
847
+ }
848
+ const r = assertImplies(slot.value, field.constraint, opts);
849
+ if (r.status === "disproved") {
850
+ return disproved(`field ${key}: ${r.reason}`);
851
+ }
852
+ if (r.status === "unprovable") {
853
+ return unprovable(`field ${key}: ${r.reason}`);
854
+ }
855
+ }
856
+ return proved();
857
+ }
858
+ function assertScalar(ret, constraint, phi) {
859
+ if (constraint.prim) {
860
+ const retPrim = primOfAbs(ret);
861
+ if (retPrim && retPrim !== constraint.prim) {
862
+ return disproved(`typeof return = "${retPrim}" \u22AD "${constraint.prim}"`);
863
+ }
864
+ if (!retPrim) {
865
+ const k = ret.shape.k;
866
+ if (k === "obj" || k === "arr" || k === "tuple" || k === "fn" || k === "brand" || k === "eff") {
867
+ return disproved(`return shape ${k} \u22AD prim "${constraint.prim}"`);
868
+ }
869
+ }
870
+ }
871
+ if (isIntFlag(constraint) && constraint.prim === "number") {
872
+ const lvR = litValue(ret);
873
+ const lv = lvR.ok ? lvR.value : void 0;
874
+ if (typeof lv === "number" && !Number.isInteger(lv)) {
875
+ return disproved(`return ${lv} \u22AD int`);
876
+ }
877
+ }
878
+ const t = retAnchorTerm(ret);
879
+ const constraintPred = instantiateOnTerm(constraint, t);
880
+ const retPred = ret.pred && ret.pred.op !== "true" ? phi.op === "true" ? ret.pred : and(phi, ret.pred) : phi;
881
+ if (constraint.preds.length === 0 && constraintPred.op === "true") {
882
+ return proved();
883
+ }
884
+ if (constraint.preds.length === 0 && constraintPred.op === "typeof" && constraint.prim) {
885
+ return proved();
886
+ }
887
+ if (constraintPred.op !== "true") {
888
+ if (implies(retPred, constraintPred)) return proved();
889
+ if (ret.pred && ret.pred.op !== "true" && numericBoundsImplyLocal(ret.pred, constraintPred)) {
890
+ return proved();
891
+ }
892
+ if (isBoundedEvidenceMissing(ret, constraint)) {
893
+ return unprovable(
894
+ `cannot prove return satisfies ${formatConstraint(constraint)} (no bound evidence on the return value)`
895
+ );
896
+ }
897
+ }
898
+ if (ret.term?.op === "lit") {
899
+ const lv = ret.term.value;
900
+ if (typeof lv === "number" || typeof lv === "string" || typeof lv === "boolean" || lv === null || lv === void 0) {
901
+ if (literalMeetsConstraint(lv, constraint)) {
902
+ return proved();
903
+ }
904
+ const boundDesc = describeBoundViolation(lv, constraint);
905
+ return disproved(
906
+ boundDesc ?? `return ${JSON.stringify(lv)} \u22AD ${formatConstraint(constraint)}`
907
+ );
908
+ }
909
+ }
910
+ if (constraintPred.op !== "true" || constraint.preds.length > 0) {
911
+ if (isBoundedEvidenceMissing(ret, constraint)) {
912
+ return unprovable(
913
+ `cannot prove return satisfies ${formatConstraint(constraint)} (no bound evidence on the return value)`
914
+ );
915
+ }
916
+ return disproved(
917
+ `return (${formatAbs(ret)}) \u22AD ${formatConstraint(constraint)} (postcondition not implied)`
918
+ );
919
+ }
920
+ return proved();
921
+ }
922
+ function numericBoundsImplyLocal(src, tgt) {
923
+ const one = (p) => p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le";
924
+ if (!one(src) || !one(tgt)) return false;
925
+ if (src.op !== tgt.op) {
926
+ if (src.op === "gt" && tgt.op === "ge") {
927
+ return sameTermSide(src, tgt) && litGE(src.b, tgt.b);
928
+ }
929
+ if (src.op === "lt" && tgt.op === "le") {
930
+ return sameTermSide(src, tgt) && litLE(src.b, tgt.b);
931
+ }
932
+ return false;
933
+ }
934
+ if (!sameTermSide(src, tgt)) return false;
935
+ const sb = src.b.op === "lit" ? src.b.value : void 0;
936
+ const tb = tgt.b.op === "lit" ? tgt.b.value : void 0;
937
+ if (typeof sb !== "number" || typeof tb !== "number") return false;
938
+ if (src.op === "gt" || src.op === "ge") return sb >= tb;
939
+ return sb <= tb;
940
+ }
941
+ function sameTermSide(a, b) {
942
+ return a.a.op === b.a.op && termEquals(a.a, b.a);
943
+ }
944
+ function litGE(a, b) {
945
+ if (a.op !== "lit" || b.op !== "lit") return false;
946
+ return typeof a.value === "number" && typeof b.value === "number" && a.value >= b.value;
947
+ }
948
+ function litLE(a, b) {
949
+ if (a.op !== "lit" || b.op !== "lit") return false;
950
+ return typeof a.value === "number" && typeof b.value === "number" && a.value <= b.value;
951
+ }
952
+ function primOfAbs(a) {
953
+ if (a.shape.k === "prim") return a.shape.type;
954
+ const lvR = litValue(a);
955
+ const lv = lvR.ok ? lvR.value : void 0;
956
+ if (typeof lv === "number") return "number";
957
+ if (typeof lv === "string") return "string";
958
+ if (typeof lv === "boolean") return "boolean";
959
+ if (typeof lv === "bigint") return "bigint";
960
+ return void 0;
961
+ }
962
+ function describeBoundViolation(lv, constraint) {
963
+ const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" };
964
+ for (const p of constraint.preds) {
965
+ for (const atom of p.op === "and" ? p.args : [p]) {
966
+ if (atom.op !== "gt" && atom.op !== "ge" && atom.op !== "lt" && atom.op !== "le") continue;
967
+ if (atom.b.op !== "lit" || typeof atom.b.value !== "number") continue;
968
+ const n = atom.b.value;
969
+ const sym = opSym[atom.op];
970
+ if (typeof lv === "number") {
971
+ let ok = true;
972
+ if (atom.op === "gt") ok = lv > n;
973
+ if (atom.op === "ge") ok = lv >= n;
974
+ if (atom.op === "lt") ok = lv < n;
975
+ if (atom.op === "le") ok = lv <= n;
976
+ if (!ok) return `return ${sym} ${n}`;
977
+ }
978
+ if (typeof lv === "string" && isLengthSelfTerm(atom.a)) {
979
+ let ok = true;
980
+ if (atom.op === "gt") ok = lv.length > n;
981
+ if (atom.op === "ge") ok = lv.length >= n;
982
+ if (atom.op === "lt") ok = lv.length < n;
983
+ if (atom.op === "le") ok = lv.length <= n;
984
+ if (!ok) return `length(self) ${sym} ${n}`;
985
+ }
986
+ }
987
+ }
988
+ return void 0;
989
+ }
990
+ function isLengthSelfTerm(t) {
991
+ return t.op === "app" && t.fn === "length" && t.args.length === 1 && t.args[0].op === "var" && t.args[0].id === SELF;
992
+ }
993
+
706
994
  // src/algebra/projection.ts
707
995
  var PROJECTABLE_CONF = /* @__PURE__ */ new Set(["exact", "path"]);
708
996
  var ARR_FALLBACK_CONF = /* @__PURE__ */ new Set([
@@ -1137,8 +1425,9 @@ function estimateEntryParamCount(source, fnName, file) {
1137
1425
  }
1138
1426
  } catch {
1139
1427
  }
1428
+ const name = identBoundaryRegex(fnName);
1140
1429
  const re = new RegExp(
1141
- `(?:function\\s+${fnName}\\s*\\(([^)]*)\\)|${fnName}\\s*=\\s*(?:async\\s*)?function(?:\\s+${fnName})?\\s*\\(([^)]*)\\)|${fnName}\\s*=\\s*(?:async\\s*)?\\(([^)]*)\\)\\s*=>)`
1430
+ `(?:function\\s+${name}\\s*\\(([^)]*)\\)|${name}\\s*=\\s*(?:async\\s*)?function(?:\\s+${name})?\\s*\\(([^)]*)\\)|${name}\\s*=\\s*(?:async\\s*)?\\(([^)]*)\\)\\s*=>)`
1142
1431
  );
1143
1432
  const m = re.exec(source);
1144
1433
  if (m) {
@@ -1149,13 +1438,15 @@ function estimateEntryParamCount(source, fnName, file) {
1149
1438
  return 1;
1150
1439
  }
1151
1440
  function isDefaultExportName(source, fnName) {
1441
+ const name = identBoundaryRegex(fnName);
1152
1442
  const re = new RegExp(
1153
- `export\\s+default\\s+(?:async\\s+)?(?:function\\s+${fnName}\\b|${fnName}\\b)`
1443
+ `export\\s+default\\s+(?:async\\s+)?(?:function\\s+${name}|${name})`
1154
1444
  );
1155
1445
  return re.test(source);
1156
1446
  }
1157
1447
  function formatEntrySigLine(name, g, throws) {
1158
- const ps = g.params.map((p, i) => {
1448
+ const names = g.formals && g.formals.length > 0 ? formalParamSignatureNames(g.formals) : g.params;
1449
+ const ps = names.map((p, i) => {
1159
1450
  const t = g.typeParams[i]?.value;
1160
1451
  const shown = !t ? "any" : t.shape.k === "unknown" ? "unknown" : formatShape(t);
1161
1452
  return `${p}: ${shown}`;
@@ -1219,6 +1510,7 @@ function resetCheckSourceMemo() {
1219
1510
  checkKeyDeps.clear();
1220
1511
  checkDepIndex.clear();
1221
1512
  resetHashSourceCache();
1513
+ resetSidecarLoadFailureCache();
1222
1514
  }
1223
1515
  function loadModuleId(fn2) {
1224
1516
  if (!fn2) return 0;
@@ -1249,7 +1541,7 @@ function unindexCheckKey(key) {
1249
1541
  function evictCheckSourceMemoForPaths(paths) {
1250
1542
  let n = 0;
1251
1543
  for (const raw of paths) {
1252
- const p = normPath(raw);
1544
+ const p = stablePathKey(raw);
1253
1545
  const keys = checkDepIndex.get(p);
1254
1546
  if (!keys) continue;
1255
1547
  for (const key of [...keys]) {
@@ -1325,8 +1617,9 @@ function checkMemoSet(key, value, depPaths) {
1325
1617
  }
1326
1618
  checkReportMemo.set(key, value);
1327
1619
  if (depPaths.length > 0) {
1328
- checkKeyDeps.set(key, depPaths);
1329
- for (const p of depPaths) {
1620
+ const normDeps = [...new Set(depPaths.map(stablePathKey))];
1621
+ checkKeyDeps.set(key, normDeps);
1622
+ for (const p of normDeps) {
1330
1623
  let set = checkDepIndex.get(p);
1331
1624
  if (!set) {
1332
1625
  set = /* @__PURE__ */ new Set();
@@ -1525,8 +1818,9 @@ function projectCaseArgField(arg, field) {
1525
1818
  }
1526
1819
  function scanCaseInconsistency(source, knownFns, opts) {
1527
1820
  const out = [];
1528
- if (!source.includes("@nudo:case")) return out;
1529
- const hasContractOrigin = source.includes("@nudo:contract") || source.includes("@nudo:contract") || opts.sidecarPresent === true;
1821
+ const directiveSrc = stripStringsKeepComments(source);
1822
+ if (!directiveSrc.includes("@nudo:case")) return out;
1823
+ const hasContractOrigin = directiveSrc.includes("@nudo:contract") || opts.sidecarPresent === true;
1530
1824
  if (!hasContractOrigin) return out;
1531
1825
  const file = opts.file ?? parseSource(source);
1532
1826
  const parseLitArg = (s) => {
@@ -1650,13 +1944,14 @@ function scanCaseInconsistency(source, knownFns, opts) {
1650
1944
  }
1651
1945
  arg = projected;
1652
1946
  }
1653
- const lv = litValue(arg);
1654
- if (lv === void 0 || typeof lv !== "number" && typeof lv !== "string" && typeof lv !== "boolean") {
1947
+ const argLit = arg.term?.op === "lit" ? arg.term.value : void 0;
1948
+ const argIsLit = arg.term?.op === "lit";
1949
+ if (!argIsLit || typeof argLit !== "number" && typeof argLit !== "string" && typeof argLit !== "boolean" && argLit !== null && argLit !== void 0) {
1655
1950
  continue;
1656
1951
  }
1657
1952
  const hasEqOr = (entry.constraint.members?.length ?? 0) > 0 || entry.constraint.preds.some((p) => p.op === "eq");
1658
1953
  if (hasEqOr) {
1659
- if (!literalMeetsConstraint(lv, entry.constraint)) {
1954
+ if (!literalMeetsConstraint(argLit, entry.constraint)) {
1660
1955
  const paramName = entry.param || paramNames[idx] || `arg${idx}`;
1661
1956
  out.push({
1662
1957
  severity: "error",
@@ -1671,16 +1966,16 @@ function scanCaseInconsistency(source, knownFns, opts) {
1671
1966
  }
1672
1967
  continue;
1673
1968
  }
1674
- if (typeof lv !== "number") continue;
1969
+ if (typeof argLit !== "number") continue;
1675
1970
  const flatten = (p) => p.op === "and" ? p.args.flatMap(flatten) : p.op === "true" ? [] : [p];
1676
1971
  for (const p of flatten(entry.pred)) {
1677
1972
  if ((p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") && p.b.op === "lit" && typeof p.b.value === "number") {
1678
1973
  const n = p.b.value;
1679
1974
  let ok = true;
1680
- if (p.op === "gt") ok = lv > n;
1681
- if (p.op === "ge") ok = lv >= n;
1682
- if (p.op === "lt") ok = lv < n;
1683
- if (p.op === "le") ok = lv <= n;
1975
+ if (p.op === "gt") ok = argLit > n;
1976
+ if (p.op === "ge") ok = argLit >= n;
1977
+ if (p.op === "lt") ok = argLit < n;
1978
+ if (p.op === "le") ok = argLit <= n;
1684
1979
  if (!ok) {
1685
1980
  const paramName = entry.param || paramNames[idx] || `arg${idx}`;
1686
1981
  out.push({
@@ -1698,36 +1993,18 @@ function scanCaseInconsistency(source, knownFns, opts) {
1698
1993
  }
1699
1994
  }
1700
1995
  };
1701
- const visit = (n) => {
1702
- if (!n || typeof n !== "object") return;
1703
- const obj2 = n;
1704
- let decl = obj2;
1705
- if (obj2.type === "ExportNamedDeclaration" || obj2.type === "ExportDefaultDeclaration") {
1706
- decl = obj2.declaration;
1707
- }
1708
- if (decl && (decl.type === "FunctionDeclaration" || decl.type === "VariableDeclaration" && (decl.declarations ?? [])[0]?.init && ["ArrowFunctionExpression", "FunctionExpression"].includes(
1709
- String(
1710
- decl.declarations[0].init.type
1711
- )
1712
- ))) {
1713
- const id = decl.type === "FunctionDeclaration" ? decl.id?.name : decl.declarations[0]?.id?.name;
1714
- if (id && knownFns.includes(id)) {
1715
- for (const c of obj2.leadingComments ?? []) {
1716
- const caseArgs = parseCaseArgs(c.value);
1717
- if (!caseArgs) continue;
1718
- const caseName = /@nudo:case\s+"([^"]+)"/.exec(c.value)?.[1] ?? "?";
1719
- checkCaseAgainstReqs(id, caseName, caseArgs, c.loc?.start.line);
1720
- }
1996
+ for (const scope of listFnDirectiveScopes(file)) {
1997
+ if (!knownFns.includes(scope.name)) continue;
1998
+ for (let ci = 0; ci < scope.commentTexts.length; ci++) {
1999
+ const text = scope.commentTexts[ci];
2000
+ const startLine = scope.commentStartLines[ci] ?? 0;
2001
+ for (const tag of scanCaseTags(text)) {
2002
+ const caseArgs = parseCaseArgs(`@nudo:case "${tag.name}" (${tag.argsText})`);
2003
+ if (!caseArgs) continue;
2004
+ checkCaseAgainstReqs(scope.name, tag.name, caseArgs, startLine);
1721
2005
  }
1722
2006
  }
1723
- for (const key of Object.keys(obj2)) {
1724
- if (key === "loc" || key === "start" || key === "end" || key === "leadingComments") continue;
1725
- const val = obj2[key];
1726
- if (Array.isArray(val)) val.forEach(visit);
1727
- else if (val && typeof val === "object") visit(val);
1728
- }
1729
- };
1730
- visit(file);
2007
+ }
1731
2008
  return out;
1732
2009
  }
1733
2010
 
@@ -1749,6 +2026,7 @@ function checkSource(filePath, source, phi = pTrue, opts = {}) {
1749
2026
  }
1750
2027
  setRefineDiagCollector(null);
1751
2028
  setInterfaceDiagCollector(null);
2029
+ resetSidecarLoadFailureCache();
1752
2030
  const refineSince = refineDiagCount();
1753
2031
  const ifaceSince = interfaceDiagCount();
1754
2032
  const useMemo = phi.op === "true";
@@ -1819,12 +2097,13 @@ function sidecarDiagIssues(diags) {
1819
2097
  const k = `${d.code}\0${d.message}`;
1820
2098
  if (seen.has(k)) continue;
1821
2099
  seen.add(k);
1822
- out.push({ severity: "error", code: d.code, message: d.message });
2100
+ const severity = d.code === "nudo:contract-syntax" || d.code === "nudo:directive-syntax" ? "warning" : "error";
2101
+ out.push({ severity, code: d.code, message: d.message });
1823
2102
  }
1824
2103
  return out;
1825
2104
  }
1826
2105
  function checkSourceInner(filePath, source, file, phi, opts, issues, signatures, names, truncated, identityOpts = opts, depsFp, sidecarFp) {
1827
- const hasRefineDirective = source.includes("@nudo:contract") || source.includes("@nudo:contract");
2106
+ const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
1828
2107
  const exportedNames = sidecarFp !== void 0 ? localNamedExports(source) : void 0;
1829
2108
  const entryNames = localNamedExports(source);
1830
2109
  const entryThrowsMode = opts.entryThrows ?? "error";
@@ -1889,7 +2168,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
1889
2168
  eff.returns.constraint,
1890
2169
  declared
1891
2170
  );
1892
- if (retIssues.length > 0) returnViolated.add(name);
2171
+ if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
1893
2172
  issues.push(...retIssues);
1894
2173
  }
1895
2174
  continue;
@@ -1935,7 +2214,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
1935
2214
  message: `${name} (export): may throw ${gateDisplay}`,
1936
2215
  actual: `${name}(\u2026) throws ${gateDisplay}`,
1937
2216
  expected: "entry total, or @nudo:throws / try-catch",
1938
- suggestion: first.cause ? `${first.cause} \u2192 ${first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / refine / guard / try-catch`}` : first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / refine / guard / try-catch`,
2217
+ suggestion: first.cause ? `${first.cause} \u2192 ${first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / sidecar fn({ \u2026 }) contract / refine / guard / try-catch`}` : first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / sidecar fn({ \u2026 }) contract / refine / guard / try-catch`,
1939
2218
  fn: name,
1940
2219
  ...loc.line !== void 0 ? { line: loc.line } : {},
1941
2220
  ...loc.column !== void 0 ? { column: loc.column } : {}
@@ -1980,7 +2259,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
1980
2259
  message: `${name} (export): may throw ${gateDisplay}`,
1981
2260
  actual: `${formatEntrySigLine(name, g, gateDisplay)}`,
1982
2261
  expected: "entry total, or @nudo:throws / try-catch",
1983
- suggestion: first.cause ? `${first.cause} \u2192 ${first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / refine / guard / try-catch`}` : first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / refine / guard / try-catch`,
2262
+ suggestion: first.cause ? `${first.cause} \u2192 ${first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / sidecar fn({ \u2026 }) contract / refine / guard / try-catch`}` : first.kind === "ReferenceError" ? "re-export `export { x } from` \u4E0D\u662F\u5C40\u90E8\u7ED1\u5B9A\u2014\u2014\u6539 `import { x }` / @nudo:throws ReferenceError" : `@nudo:throws ${first.kind} / sidecar fn({ \u2026 }) contract / refine / guard / try-catch`,
1984
2263
  fn: name,
1985
2264
  ...loc.line !== void 0 ? { line: loc.line } : {},
1986
2265
  ...loc.column !== void 0 ? { column: loc.column } : {}
@@ -1998,7 +2277,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
1998
2277
  });
1999
2278
  signatures.push({
2000
2279
  name,
2001
- params: g.params,
2280
+ // 解构形参展示 `{ grade, findings }`,不落 `_p0`(#64:契约要上屏)
2281
+ params: g.formals && g.formals.length > 0 ? formalParamSignatureNames(g.formals) : g.params,
2002
2282
  paramTypes,
2003
2283
  abs: g.symbolic,
2004
2284
  display: throwsDisplay ? `${fmt.display} throws ${throwsDisplay}` : fmt.display,
@@ -2078,7 +2358,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2078
2358
  eff.returns.constraint,
2079
2359
  g.symbolic
2080
2360
  );
2081
- if (retIssues.length > 0) returnViolated.add(name);
2361
+ if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
2082
2362
  issues.push(...retIssues);
2083
2363
  }
2084
2364
  if (eff && eff.source === "generated") {
@@ -2105,7 +2385,25 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2105
2385
  severity: "info",
2106
2386
  code: "nudo:fork-truncated",
2107
2387
  message: `Branch expansion was truncated (fork budget); affected results widened to unknown#opaque (budget)`,
2108
- suggestion: "optional: simplify branching or raise nudo.analysis.maxForks \u2014 non-blocking"
2388
+ suggestion: "optional: raise nudo.analysis.maxForks, or @nudo:budget forks=\u2026 on the function \u2014 non-blocking"
2389
+ });
2390
+ continue;
2391
+ }
2392
+ if (label === PROMISE_MICRO_OVERFLOW_LABEL) {
2393
+ issues.push({
2394
+ severity: "info",
2395
+ code: "nudo:promise-micro-truncated",
2396
+ message: `Promise microtask queue hit its hard cap; further then/catch callbacks were dropped (results stay fail-closed)`,
2397
+ suggestion: "optional: reduce top-level Promise.then fan-out \u2014 non-blocking"
2398
+ });
2399
+ continue;
2400
+ }
2401
+ if (label === PROMISE_MICRO_ERROR_LABEL) {
2402
+ issues.push({
2403
+ severity: "info",
2404
+ code: "nudo:promise-micro-error",
2405
+ message: `A promise microtask threw while draining; the error was recorded and did not rewrite the synchronous return value`,
2406
+ suggestion: "optional: inspect the then/catch callback for a throw path \u2014 non-blocking"
2109
2407
  });
2110
2408
  continue;
2111
2409
  }
@@ -2124,7 +2422,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2124
2422
  severity: "info",
2125
2423
  code: "nudo:recursion-truncated",
2126
2424
  message: `Recursive evaluation of '${label}' was truncated (depth/size budget); result widened to unknown#opaque (budget \u2014 not inference debt)`,
2127
- suggestion: "narrow the recursion base case or declare an explicit @nudo:contract return contract",
2425
+ suggestion: "raise the function budget with @nudo:budget calls=\u2026 depth=\u2026 (or narrow the recursion base case / declare @nudo:contract return)",
2128
2426
  fn: label
2129
2427
  });
2130
2428
  }
@@ -2187,7 +2485,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2187
2485
  rc.constraint,
2188
2486
  rec.result
2189
2487
  );
2190
- if (retIssues.length > 0) returnViolated.add(rec.fnName);
2488
+ if (retIssues.some((i) => i.severity === "error")) returnViolated.add(rec.fnName);
2191
2489
  issues.push(...retIssues);
2192
2490
  }
2193
2491
  }
@@ -2246,118 +2544,33 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2246
2544
  };
2247
2545
  }
2248
2546
  function checkReturnConstraint(fnName, cName, constraint, ret) {
2249
- const out = [];
2250
- if (ret.shape.k === "unknown" && !ret.term) return out;
2251
- if (ret.shape.k === "any") return out;
2252
- if (isNullishLitAbs(ret)) return out;
2253
- if (ret.shape.k === "sum" && constraint.fields) {
2254
- const seen = /* @__PURE__ */ new Set();
2255
- for (const m of ret.shape.members) {
2256
- for (const issue of checkReturnConstraint(fnName, cName, constraint, m)) {
2257
- const key = `${issue.message}\0${issue.actual ?? ""}\0${issue.expected ?? ""}`;
2258
- if (seen.has(key)) continue;
2259
- seen.add(key);
2260
- out.push(issue);
2261
- }
2262
- }
2263
- return out;
2264
- }
2265
- const push = (actual, expected, suggestion) => {
2266
- out.push({
2267
- severity: "error",
2268
- code: "nudo:constraint-violated",
2547
+ const proof = assertImplies(ret, constraint);
2548
+ if (proof.status === "proved") return [];
2549
+ const push = (severity, actual, expected, suggestion) => [
2550
+ {
2551
+ severity,
2552
+ code: severity === "error" ? "nudo:constraint-violated" : "nudo:unproven-return",
2269
2553
  message: `${fnName}: return value \u22AD @nudo:contract return ${cName}`,
2270
2554
  actual,
2271
2555
  expected,
2272
2556
  suggestion,
2273
2557
  fn: fnName
2274
- });
2275
- };
2276
- if (constraint.fields) {
2277
- const slots = ret.shape.k === "obj" ? ret.shape.slots : void 0;
2278
- if (!slots) {
2279
- if (ret.shape.k !== "never") {
2280
- push(formatAbs(ret), `object shape (${cName})`, `return an object satisfying the ${cName} shape`);
2281
- }
2282
- return out;
2283
- }
2284
- for (const [key, field] of Object.entries(constraint.fields)) {
2285
- const slot = getSlot(slots, key);
2286
- if (!slot) {
2287
- if (!field.optional && !field.constraint.isOptional) {
2288
- push(formatAbs(ret), `missing field ${key}`, `add the missing field ${key} to the return value`);
2289
- }
2290
- continue;
2291
- }
2292
- if (field.constraint.prim && slot.value.shape.k === "prim") {
2293
- const actualPrim = slot.value.shape.type;
2294
- if (actualPrim !== field.constraint.prim) {
2295
- push(
2296
- formatAbs(slot.value),
2297
- `typeof ${key} = "${field.constraint.prim}"`,
2298
- `change the return value's ${key} to ${field.constraint.prim}`
2299
- );
2300
- continue;
2301
- }
2302
- }
2303
- const lv2 = litValue(slot.value);
2304
- if (lv2 !== void 0 && typeof lv2 === "number") {
2305
- for (const p of field.constraint.preds) {
2306
- const flat = p.op === "and" ? p.args : [p];
2307
- for (const atom of flat) {
2308
- if ((atom.op === "gt" || atom.op === "ge" || atom.op === "lt" || atom.op === "le") && atom.b.op === "lit" && typeof atom.b.value === "number") {
2309
- const n = atom.b.value;
2310
- let ok = true;
2311
- if (atom.op === "gt") ok = lv2 > n;
2312
- if (atom.op === "ge") ok = lv2 >= n;
2313
- if (atom.op === "lt") ok = lv2 < n;
2314
- if (atom.op === "le") ok = lv2 <= n;
2315
- if (!ok) {
2316
- const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[atom.op];
2317
- push(
2318
- formatAbs(slot.value),
2319
- `${key} ${opSym} ${n}`,
2320
- `the return value's ${key} should satisfy ${key} ${opSym} ${n}`
2321
- );
2322
- }
2323
- }
2324
- }
2325
- }
2326
- }
2327
- }
2328
- return out;
2329
- }
2330
- if (constraint.prim && ret.shape.k === "prim") {
2331
- const actualPrim = ret.shape.type;
2332
- if (actualPrim !== constraint.prim) {
2333
- push(formatAbs(ret), `typeof return = "${constraint.prim}"`, `return ${constraint.prim}`);
2334
- return out;
2335
2558
  }
2559
+ ];
2560
+ if (proof.status === "disproved") {
2561
+ return push(
2562
+ "error",
2563
+ formatAbs(ret),
2564
+ proof.reason,
2565
+ proof.reason.includes("nullish") ? `use nullable(${formatConstraint(constraint)}) or union(..., lit(null)) to allow nullish returns` : `return a value satisfying ${formatConstraint(constraint)}`
2566
+ );
2336
2567
  }
2337
- const lv = litValue(ret);
2338
- if (lv !== void 0 && typeof lv === "number") {
2339
- for (const p of constraint.preds) {
2340
- const flat = p.op === "and" ? p.args : [p];
2341
- for (const atom of flat) {
2342
- if ((atom.op === "gt" || atom.op === "ge" || atom.op === "lt" || atom.op === "le") && atom.b.op === "lit" && typeof atom.b.value === "number") {
2343
- const n = atom.b.value;
2344
- let ok = true;
2345
- if (atom.op === "gt") ok = lv > n;
2346
- if (atom.op === "ge") ok = lv >= n;
2347
- if (atom.op === "lt") ok = lv < n;
2348
- if (atom.op === "le") ok = lv <= n;
2349
- if (!ok) {
2350
- const opSym = { gt: ">", ge: "\u2265", lt: "<", le: "\u2264" }[atom.op];
2351
- push(formatAbs(ret), `return ${opSym} ${n}`, `return a value satisfying ${opSym} ${n}`);
2352
- }
2353
- }
2354
- }
2355
- }
2356
- }
2357
- if (lv !== void 0 && (typeof lv === "number" || typeof lv === "string" || typeof lv === "boolean") && ((constraint.members?.length ?? 0) > 0 || constraint.preds.some((p) => p.op === "eq") || typeof lv === "string" && constraint.preds.length > 0) && !literalMeetsConstraint(lv, constraint)) {
2358
- push(formatAbs(ret), formatConstraint(constraint), `return a value satisfying ${formatConstraint(constraint)}`);
2359
- }
2360
- return out;
2568
+ return push(
2569
+ "warning",
2570
+ formatAbs(ret),
2571
+ formatConstraint(constraint),
2572
+ `${proof.reason}; add a precondition, narrow the return expression, or relax the return contract`
2573
+ );
2361
2574
  }
2362
2575
 
2363
2576
  // src/algebra/check-report.ts
@@ -2393,6 +2606,12 @@ function actionsForIssue(i) {
2393
2606
  ];
2394
2607
  case "nudo:entry-may-throw":
2395
2608
  return [
2609
+ {
2610
+ kind: "draft",
2611
+ command: "nudo contract --draft",
2612
+ label: "narrow the entry with a sidecar fn({ \u2026 }) param contract \u2014 most honest fix for helpers (also tightens the signature)",
2613
+ ...i.fn ? { hint: i.fn } : {}
2614
+ },
2396
2615
  {
2397
2616
  kind: "relax",
2398
2617
  label: "declare intentional fail-fast: @nudo:throws Error (or case `!! throws`)",
@@ -2406,8 +2625,7 @@ function actionsForIssue(i) {
2406
2625
  kind: "ignore-throws",
2407
2626
  command: "nudo check --ignore-throws TypeError",
2408
2627
  label: "migration switch: ignore this throw class on L2"
2409
- },
2410
- draft
2628
+ }
2411
2629
  ];
2412
2630
  case "nudo:unknown-inference":
2413
2631
  case "nudo:opaque-result":
@@ -2424,10 +2642,12 @@ function actionsForIssue(i) {
2424
2642
  ];
2425
2643
  case "nudo:fork-truncated":
2426
2644
  case "nudo:recursion-truncated":
2645
+ case "nudo:promise-micro-truncated":
2646
+ case "nudo:promise-micro-error":
2427
2647
  return [
2428
2648
  {
2429
2649
  kind: "info",
2430
- label: "results widened to unknown \u2014 raise budget or narrow control flow"
2650
+ label: "results widened to unknown \u2014 raise budget (@nudo:budget forks/calls/depth on the function) or narrow control flow"
2431
2651
  }
2432
2652
  ];
2433
2653
  case "nudo:host-effect-blocked":
@@ -2661,107 +2881,6 @@ function formatGitlabCodeQuality(r, opts = {}) {
2661
2881
  };
2662
2882
  });
2663
2883
  }
2664
-
2665
- // src/algebra/abs-modules.ts
2666
- function bindImports(node, env, modules) {
2667
- const mod2 = modules[node.source.value];
2668
- if (!mod2) {
2669
- for (const s of node.specifiers) {
2670
- env.vars.set(s.local.name, unknown);
2671
- }
2672
- return;
2673
- }
2674
- for (const s of node.specifiers) {
2675
- if (s.type === "ImportDefaultSpecifier") {
2676
- env.vars.set(s.local.name, mod2.default ?? unknown);
2677
- } else if (s.type === "ImportSpecifier") {
2678
- const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
2679
- env.vars.set(s.local.name, mod2.named[imported] ?? unknown);
2680
- } else if (s.type === "ImportNamespaceSpecifier") {
2681
- const slots = {};
2682
- for (const [k, v2] of Object.entries(mod2.named)) slots[k] = { value: v2 };
2683
- if (mod2.default) slots["default"] = { value: mod2.default };
2684
- env.vars.set(s.local.name, obj(slots));
2685
- }
2686
- }
2687
- }
2688
- function fnToAbs(env, name) {
2689
- const impl = env.fns.get(name);
2690
- if (!impl) return void 0;
2691
- return absFunction(impl.params, {
2692
- body: impl.body,
2693
- async: impl.async,
2694
- env
2695
- });
2696
- }
2697
- function lookupExport(env, name) {
2698
- if (env.vars.has(name)) return env.vars.get(name);
2699
- return fnToAbs(env, name);
2700
- }
2701
- function collectAbsExports(file, env, modules) {
2702
- const named = {};
2703
- let defaultExport;
2704
- for (const stmt of file.program.body) {
2705
- if (stmt.type === "ExportNamedDeclaration") {
2706
- if (stmt.source && modules) {
2707
- const mod2 = modules[stmt.source.value];
2708
- if (mod2) {
2709
- for (const spec of stmt.specifiers) {
2710
- if (spec.type !== "ExportSpecifier") continue;
2711
- const local = spec.local.type === "Identifier" ? spec.local.name : spec.local.value;
2712
- const exported = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
2713
- const v2 = local === "default" ? mod2.default : mod2.named[local];
2714
- if (v2 === void 0) continue;
2715
- if (exported === "default") defaultExport = v2;
2716
- else named[exported] = v2;
2717
- }
2718
- }
2719
- continue;
2720
- }
2721
- const decl = stmt.declaration;
2722
- if (decl) {
2723
- if (decl.type === "FunctionDeclaration" && decl.id) {
2724
- const v2 = lookupExport(env, decl.id.name);
2725
- if (v2) named[decl.id.name] = v2;
2726
- } else if (decl.type === "ClassDeclaration" && decl.id) {
2727
- const v2 = lookupExport(env, decl.id.name);
2728
- if (v2) named[decl.id.name] = v2;
2729
- } else if (decl.type === "VariableDeclaration") {
2730
- for (const d of decl.declarations) {
2731
- if (d.id.type === "Identifier") {
2732
- const v2 = lookupExport(env, d.id.name);
2733
- if (v2) named[d.id.name] = v2;
2734
- }
2735
- }
2736
- }
2737
- }
2738
- for (const spec of stmt.specifiers) {
2739
- if (spec.type !== "ExportSpecifier") continue;
2740
- const local = spec.local.type === "Identifier" ? spec.local.name : spec.local.value;
2741
- const exported = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
2742
- const v2 = lookupExport(env, local);
2743
- if (v2) named[exported] = v2;
2744
- }
2745
- } else if (stmt.type === "ExportAllDeclaration" && stmt.source && modules) {
2746
- const mod2 = modules[stmt.source.value];
2747
- if (mod2?.named) {
2748
- for (const [k, v2] of Object.entries(mod2.named)) named[k] = v2;
2749
- }
2750
- } else if (stmt.type === "ExportDefaultDeclaration") {
2751
- const d = stmt.declaration;
2752
- if (d.type === "FunctionDeclaration") {
2753
- defaultExport = lookupExport(env, d.id ? d.id.name : "default");
2754
- } else if (d.type === "ClassDeclaration" && d.id) {
2755
- defaultExport = lookupExport(env, d.id.name);
2756
- } else if (d.type === "Identifier") {
2757
- defaultExport = lookupExport(env, d.name);
2758
- }
2759
- }
2760
- }
2761
- const result = { named };
2762
- if (defaultExport) result.default = defaultExport;
2763
- return result;
2764
- }
2765
2884
  export {
2766
2885
  $add,
2767
2886
  $arguments,
@@ -2873,6 +2992,9 @@ export {
2873
2992
  $while,
2874
2993
  $whileSeq,
2875
2994
  $yield,
2995
+ CONSTRAINT_BUILDERS,
2996
+ CONSTRAINT_BUILDER_NAMES,
2997
+ CONSTRAINT_EXPR_RE,
2876
2998
  DEFAULT_MAX_LOOP_ITERS,
2877
2999
  MAX_EVAL_CALL_DEPTH,
2878
3000
  MAX_EVAL_TOTAL_CALLS,
@@ -2887,6 +3009,7 @@ export {
2887
3009
  TYPEOF_NAMES,
2888
3010
  abs,
2889
3011
  absFunction,
3012
+ absOnly,
2890
3013
  absShapeKey,
2891
3014
  absToConstraint,
2892
3015
  absToString,
@@ -2905,6 +3028,7 @@ export {
2905
3028
  asAbs,
2906
3029
  asAbsVal,
2907
3030
  assignSourceSlots,
3031
+ assumeFinite,
2908
3032
  attachFnImpl,
2909
3033
  beginCollectionFork,
2910
3034
  betaOf,
@@ -2924,6 +3048,7 @@ export {
2924
3048
  callAbsMethod,
2925
3049
  callAtFunctionBoundary,
2926
3050
  callTranspiledExport,
3051
+ callTranspiledExportApply,
2927
3052
  callTranspiledExportFull,
2928
3053
  canonicalArrayIndex,
2929
3054
  checkArg,
@@ -2938,6 +3063,7 @@ export {
2938
3063
  collectionElementJoin,
2939
3064
  collectionExactLen,
2940
3065
  confJoin,
3066
+ constraintAdmitsNullish,
2941
3067
  constraintToEntryAbs,
2942
3068
  contractParamNameSet,
2943
3069
  createEnvironment,
@@ -2982,19 +3108,27 @@ export {
2982
3108
  evictGeneralizeMemoForPaths,
2983
3109
  execNudoModule,
2984
3110
  extStateOf,
3111
+ extractBalancedParens,
2985
3112
  extractDeclaredThrows,
3113
+ extractFileEnvNames,
2986
3114
  extractFn,
3115
+ extractFnBudget,
3116
+ extractMockModuleRecords,
3117
+ extractNudoImportRecords,
2987
3118
  extractNudoImports,
2988
3119
  extractRefineReturnFromSource,
2989
3120
  extractRefinesFromSource,
2990
3121
  falseConstraint,
2991
3122
  fillTuple,
3123
+ findFnDirectiveScope,
2992
3124
  fn,
2993
3125
  fnConstraintToEntryReqs,
3126
+ fnDirectiveCommentLines,
2994
3127
  fnOf,
2995
3128
  foldRequireSpecArg,
2996
3129
  foldStaticStringExpr,
2997
3130
  formalParamDisplayNames,
3131
+ formalParamSignatureNames,
2998
3132
  formalParamsFromNodes,
2999
3133
  formatAbs,
3000
3134
  formatAbsMultiline,
@@ -3031,6 +3165,7 @@ export {
3031
3165
  interfaceDiagCount,
3032
3166
  interfaceSourceOf,
3033
3167
  interfaceTierOf,
3168
+ isAbsApplyResult,
3034
3169
  isArrMutator,
3035
3170
  isBigPrim,
3036
3171
  isDefinitelyFalse,
@@ -3064,6 +3199,7 @@ export {
3064
3199
  leavePromiseExecutorScope,
3065
3200
  lenTerm,
3066
3201
  leqAbs,
3202
+ listFnDirectiveScopes,
3067
3203
  listFunctionNames,
3068
3204
  lit,
3069
3205
  litC,
@@ -3076,6 +3212,7 @@ export {
3076
3212
  looseEqAbs,
3077
3213
  lt,
3078
3214
  ltNum,
3215
+ makeAbsApplyResult,
3079
3216
  makeArrayCtorAbs,
3080
3217
  makeMapAbs,
3081
3218
  makeSetAbs,
@@ -3100,6 +3237,7 @@ export {
3100
3237
  mock,
3101
3238
  mod,
3102
3239
  mul,
3240
+ namespaceAbsOf,
3103
3241
  namespaceNameOf,
3104
3242
  ne,
3105
3243
  negAbs,
@@ -3111,6 +3249,7 @@ export {
3111
3249
  noteEvalCallRecord,
3112
3250
  noteEvalFallback,
3113
3251
  notePromiseExecutorFork,
3252
+ nullable,
3114
3253
  num,
3115
3254
  numLit,
3116
3255
  numVar,
@@ -3123,6 +3262,9 @@ export {
3123
3262
  or,
3124
3263
  pFalse,
3125
3264
  pTrue,
3265
+ parseEnvPayload,
3266
+ parseMockModulePayload,
3267
+ parseNudoImportPayload,
3126
3268
  parseSource,
3127
3269
  partial,
3128
3270
  phiAnd,
@@ -3160,10 +3302,16 @@ export {
3160
3302
  resetNudoModuleExecCache,
3161
3303
  resetParseSourceCache,
3162
3304
  resetPhi,
3305
+ resetSidecarLoadFailureCache,
3163
3306
  runTranspiled,
3164
3307
  runTranspiledOptionsMemoKey,
3165
3308
  runWithLoopExits,
3166
3309
  runtimeImportOf,
3310
+ scanBudgetDecl,
3311
+ scanCaseArgSpans,
3312
+ scanCaseTags,
3313
+ scanContractSegments,
3314
+ scanThrowsDecl,
3167
3315
  serializeCheckJson,
3168
3316
  serializeCheckJsonMulti,
3169
3317
  setAddEntry,