@nudojs/core 1.5.0 → 1.6.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.
@@ -28,10 +28,14 @@ function termEquals(a, b) {
28
28
  }
29
29
  return false;
30
30
  }
31
+ function litKeyString(v2) {
32
+ if (typeof v2 === "number" && Object.is(v2, -0)) return "-0";
33
+ return String(v2);
34
+ }
31
35
  function termToString(t) {
32
36
  if (t.op === "lit") {
33
37
  if (typeof t.value === "string") return JSON.stringify(t.value);
34
- if (typeof t.value === "number" && Object.is(t.value, -0)) return "-0";
38
+ if (typeof t.value === "number") return litKeyString(t.value);
35
39
  return String(t.value);
36
40
  }
37
41
  if (t.op === "var") return t.id;
@@ -961,6 +965,42 @@ function leNum(term, n) {
961
965
  return le(term, lit(n));
962
966
  }
963
967
 
968
+ // src/algebra/projection-budget.ts
969
+ var PROJECTION_MAX_DEPTH = 64;
970
+ var ProjectionBudget = class {
971
+ #maxDepth;
972
+ #path = [];
973
+ #onPath = /* @__PURE__ */ new Set();
974
+ /** 粘性:本次投影是否发生过截断("cycle" / "depth"),供台账记录 */
975
+ truncated;
976
+ constructor(maxDepth = PROJECTION_MAX_DEPTH) {
977
+ this.#maxDepth = maxDepth;
978
+ }
979
+ /**
980
+ * 进入一个 Abs(或 Shape)子树。返回 null → 继续递归,之后必须配对
981
+ * `exit()`;返回 "cycle" | "depth" → 不得递归,按出口各自的约定渲染
982
+ * 截断标记(不调用 exit——本次 enter 没有入栈)。
983
+ */
984
+ enter(a) {
985
+ if (this.#onPath.has(a)) {
986
+ this.truncated = "cycle";
987
+ return "cycle";
988
+ }
989
+ if (this.#path.length >= this.#maxDepth) {
990
+ this.truncated = "depth";
991
+ return "depth";
992
+ }
993
+ this.#path.push(a);
994
+ this.#onPath.add(a);
995
+ return null;
996
+ }
997
+ /** 离开子树(与返回 null 的 enter 严格配对)。 */
998
+ exit() {
999
+ const a = this.#path.pop();
1000
+ if (a !== void 0) this.#onPath.delete(a);
1001
+ }
1002
+ };
1003
+
964
1004
  // src/algebra/abs.ts
965
1005
  var never = { shape: { k: "never" }, conf: "exact" };
966
1006
  var unknown = { shape: { k: "unknown" }, conf: "partial" };
@@ -1027,13 +1067,34 @@ function confJoin(a, b) {
1027
1067
  return rank[Math.max(ia < 0 ? 5 : ia, ib < 0 ? 5 : ib)];
1028
1068
  }
1029
1069
  function absToString(a) {
1030
- const parts = [shapeToString(a.shape)];
1031
- if (a.term) parts.push(`term=${termToString(a.term)}`);
1032
- if (a.pred && a.pred.op !== "true") parts.push(predToString(a.pred));
1033
- parts.push(`#${a.conf}`);
1034
- return parts.join(" ");
1070
+ return absToStringB(a, new ProjectionBudget());
1071
+ }
1072
+ function absToStringB(a, budget) {
1073
+ const stop = budget.enter(a);
1074
+ if (stop) return stop === "cycle" ? "\u2026cycle" : "\u2026";
1075
+ try {
1076
+ const parts = [shapeToStringB(a.shape, budget)];
1077
+ if (a.term) parts.push(`term=${termToString(a.term)}`);
1078
+ if (a.pred && a.pred.op !== "true") parts.push(predToString(a.pred));
1079
+ parts.push(`#${a.conf}`);
1080
+ return parts.join(" ");
1081
+ } finally {
1082
+ budget.exit();
1083
+ }
1035
1084
  }
1036
1085
  function shapeToString(s) {
1086
+ return shapeToStringB(s, new ProjectionBudget());
1087
+ }
1088
+ function shapeToStringB(s, budget) {
1089
+ const stop = budget.enter(s);
1090
+ if (stop) return stop === "cycle" ? "\u2026cycle" : "\u2026";
1091
+ try {
1092
+ return shapeToStringInner(s, budget);
1093
+ } finally {
1094
+ budget.exit();
1095
+ }
1096
+ }
1097
+ function shapeToStringInner(s, budget) {
1037
1098
  switch (s.k) {
1038
1099
  case "never":
1039
1100
  return "never";
@@ -1045,22 +1106,22 @@ function shapeToString(s) {
1045
1106
  return s.type;
1046
1107
  case "obj": {
1047
1108
  const entries = Object.entries(s.slots).map(
1048
- ([k, slot]) => `${k}${slot.optional ? "?" : ""}: ${absToString(slot.value)}`
1109
+ ([k, slot]) => `${k}${slot.optional ? "?" : ""}: ${absToStringB(slot.value, budget)}`
1049
1110
  );
1050
1111
  return `{ ${entries.join(", ")} }`;
1051
1112
  }
1052
1113
  case "arr":
1053
- return `${shapeToString(s.element.shape)}[]`;
1114
+ return `${shapeToStringB(s.element.shape, budget)}[]`;
1054
1115
  case "tuple":
1055
- return `[${s.elements.map((e) => shapeToString(e.shape)).join(", ")}]`;
1116
+ return `[${s.elements.map((e) => shapeToStringB(e.shape, budget)).join(", ")}]`;
1056
1117
  case "fn":
1057
1118
  return `fn(${s.params.join(", ")})${s.name ? ` ${s.name}` : ""}`;
1058
1119
  case "brand":
1059
1120
  return s.name;
1060
1121
  case "eff":
1061
- return `${s.eff}<${shapeToString(s.inner.shape)}>`;
1122
+ return `${s.eff}<${shapeToStringB(s.inner.shape, budget)}>`;
1062
1123
  case "sum":
1063
- return s.members.map(absToString).join(" | ");
1124
+ return s.members.map((m) => absToStringB(m, budget)).join(" | ");
1064
1125
  default:
1065
1126
  return "\xB7";
1066
1127
  }
@@ -1352,7 +1413,7 @@ function absFunction(params, impl, opts) {
1352
1413
  return a;
1353
1414
  }
1354
1415
  function termKey2(t) {
1355
- if (t.op === "lit") return `L:${typeof t.value}:${String(t.value)}`;
1416
+ if (t.op === "lit") return `L:${typeof t.value}:${litKeyString(t.value)}`;
1356
1417
  if (t.op === "var") return `V:${t.id}`;
1357
1418
  return `A:${t.fn}(${t.args.map(termKey2).join(",")})`;
1358
1419
  }
@@ -1806,7 +1867,7 @@ function absShapeKey(a, seen = /* @__PURE__ */ new Set()) {
1806
1867
  try {
1807
1868
  const s = a.shape;
1808
1869
  if (s.k === "prim") {
1809
- if (a.term?.op === "lit") return `prim:${s.type}:${String(a.term.value)}`;
1870
+ if (a.term?.op === "lit") return `prim:${s.type}:${litKeyString(a.term.value)}`;
1810
1871
  const t = a.term ? termToString(a.term) : "";
1811
1872
  const p = a.pred && a.pred.op !== "true" ? predToString(a.pred) : "";
1812
1873
  return `prim:${s.type}:${t}:${p}`;
@@ -1818,7 +1879,7 @@ function absShapeKey(a, seen = /* @__PURE__ */ new Set()) {
1818
1879
  const v2 = a.term.value;
1819
1880
  if (v2 === void 0) return "unknown:undefined";
1820
1881
  if (v2 === null) return "unknown:null";
1821
- return `unknown:lit:${typeof v2}:${String(v2)}`;
1882
+ return `unknown:lit:${typeof v2}:${litKeyString(v2)}`;
1822
1883
  }
1823
1884
  if (a.term) return `unknown:${termToString(a.term)}`;
1824
1885
  return "unknown";
@@ -2491,7 +2552,7 @@ function bindTypeVars(pattern, arg, map, depth = 0) {
2491
2552
  }
2492
2553
  if (ps.k === "obj" && as.k === "obj") {
2493
2554
  for (const [key, slot] of Object.entries(ps.slots)) {
2494
- const aSlot = as.slots[key];
2555
+ const aSlot = getSlot(as.slots, key);
2495
2556
  if (aSlot) bindTypeVars(slot.value, aSlot.value, map, depth + 1);
2496
2557
  }
2497
2558
  return;
@@ -4886,6 +4947,7 @@ function absToJsonNative(a, seen) {
4886
4947
  if (t?.op === "lit") return t.value;
4887
4948
  const s = a.shape;
4888
4949
  if (s.k === "tuple") {
4950
+ if (a.shape.rest) return NOT_LITERAL;
4889
4951
  seen.add(a);
4890
4952
  const holes = a.shape.holes ?? [];
4891
4953
  const out = [];
@@ -7109,51 +7171,15 @@ function toNumericAbs(a) {
7109
7171
  }
7110
7172
  return n;
7111
7173
  }
7112
- function updateAddAbs(a) {
7174
+ function updateAddAbs(a, phi2 = pTrue) {
7113
7175
  const n = toNumericAbs(a);
7114
7176
  const one = n.shape.k === "prim" && n.shape.type === "bigint" ? bigintLit(1n) : numLit(1);
7115
- if (n.shape.k === "prim" && n.shape.type === "bigint" && !n.term) {
7116
- return abs({ k: "prim", type: "bigint" }, void 0, void 0, confJoin(n.conf, "exact"));
7117
- }
7118
- return addNumeric(n, one);
7177
+ return add(n, one, phi2);
7119
7178
  }
7120
- function updateSubAbs(a) {
7179
+ function updateSubAbs(a, phi2 = pTrue) {
7121
7180
  const n = toNumericAbs(a);
7122
7181
  const one = n.shape.k === "prim" && n.shape.type === "bigint" ? bigintLit(1n) : numLit(1);
7123
- if (n.shape.k === "prim" && n.shape.type === "bigint" && !n.term) {
7124
- return abs({ k: "prim", type: "bigint" }, void 0, void 0, confJoin(n.conf, "exact"));
7125
- }
7126
- return subNumeric(n, one);
7127
- }
7128
- function addNumeric(a, b) {
7129
- const ra = litValue(a);
7130
- const rb = litValue(b);
7131
- const va = ra.ok ? ra.value : void 0;
7132
- const vb = rb.ok ? rb.value : void 0;
7133
- if (typeof va === "bigint" && typeof vb === "bigint") return bigintLit(va + vb);
7134
- if (typeof va === "number" && typeof vb === "number") return numLit(va + vb);
7135
- const big = a.shape.k === "prim" && a.shape.type === "bigint" || typeof va === "bigint";
7136
- return abs(
7137
- { k: "prim", type: big ? "bigint" : "number" },
7138
- void 0,
7139
- void 0,
7140
- confJoin(confJoin(a.conf, b.conf), "path")
7141
- );
7142
- }
7143
- function subNumeric(a, b) {
7144
- const ra = litValue(a);
7145
- const rb = litValue(b);
7146
- const va = ra.ok ? ra.value : void 0;
7147
- const vb = rb.ok ? rb.value : void 0;
7148
- if (typeof va === "bigint" && typeof vb === "bigint") return bigintLit(va - vb);
7149
- if (typeof va === "number" && typeof vb === "number") return numLit(va - vb);
7150
- const big = a.shape.k === "prim" && a.shape.type === "bigint" || typeof va === "bigint";
7151
- return abs(
7152
- { k: "prim", type: big ? "bigint" : "number" },
7153
- void 0,
7154
- void 0,
7155
- confJoin(confJoin(a.conf, b.conf), "path")
7156
- );
7182
+ return sub(n, one, phi2);
7157
7183
  }
7158
7184
  function toNumberAbs(a) {
7159
7185
  if (isSymbolAbs(a)) {
@@ -8192,24 +8218,40 @@ function leqShape(src, tgt, phi2, env, depth) {
8192
8218
  return leqWithPred(s.element, t.element, phi2, env, depth + 1);
8193
8219
  }
8194
8220
  if (s.k === "tuple") {
8195
- return s.elements.every((el) => leqWithPred(el, t.element, phi2, env, depth + 1).ok) ? ok() : fail("tuple \u22AD arr");
8221
+ const elsOk = s.elements.every((el) => leqWithPred(el, t.element, phi2, env, depth + 1).ok);
8222
+ const restOk = !s.rest || leqWithPred(s.rest, t.element, phi2, env, depth + 1).ok;
8223
+ return elsOk && restOk ? ok() : fail("tuple \u22AD arr");
8196
8224
  }
8197
8225
  return fail(`shape ${s.k} \u22AD arr`);
8198
8226
  }
8199
8227
  if (t.k === "tuple") {
8200
8228
  if (s.k !== "tuple") return fail(`shape ${s.k} \u22AD tuple`);
8201
- if (s.elements.length !== t.elements.length) {
8229
+ if (!t.rest && s.elements.length !== t.elements.length) {
8202
8230
  return fail(`tuple arity ${s.elements.length} \u22AD ${t.elements.length}`);
8203
8231
  }
8232
+ if (t.rest && s.elements.length < t.elements.length) {
8233
+ return fail(`tuple arity ${s.elements.length} \u22AD ${t.elements.length} (target requires ${t.elements.length})`);
8234
+ }
8235
+ if (s.rest && !t.rest) {
8236
+ return fail(`tuple rest \u22AD fixed arity ${t.elements.length}`);
8237
+ }
8204
8238
  const sHoles = s.holes ?? [];
8205
8239
  const tHoles = t.holes ?? [];
8206
- if (sHoles.length !== tHoles.length || sHoles.some((h, i) => h !== tHoles[i])) {
8207
- return fail(`tuple holes [${sHoles.join(",")}] \u22AD [${tHoles.join(",")}]`);
8240
+ const overlap = Math.min(s.elements.length, t.elements.length);
8241
+ for (let i = 0; i < overlap; i++) {
8242
+ if (sHoles.includes(i) !== tHoles.includes(i)) {
8243
+ return fail(`tuple holes [${sHoles.join(",")}] \u22AD [${tHoles.join(",")}]`);
8244
+ }
8208
8245
  }
8209
- for (let i = 0; i < t.elements.length; i++) {
8210
- const r = leqWithPred(s.elements[i], t.elements[i], phi2, env, depth + 1);
8246
+ for (let i = 0; i < s.elements.length; i++) {
8247
+ const tgt2 = i < t.elements.length ? t.elements[i] : t.rest;
8248
+ const r = leqWithPred(s.elements[i], tgt2, phi2, env, depth + 1);
8211
8249
  if (!r.ok) return fail(`tuple[${i}]: ${r.reason}`);
8212
8250
  }
8251
+ if (s.rest && t.rest) {
8252
+ const r = leqWithPred(s.rest, t.rest, phi2, env, depth + 1);
8253
+ if (!r.ok) return fail(`tuple rest: ${r.reason}`);
8254
+ }
8213
8255
  return ok();
8214
8256
  }
8215
8257
  if (t.k === "fn") {
@@ -8284,13 +8326,30 @@ function formatAbs(a, opts = {}) {
8284
8326
  return parts.join(" ");
8285
8327
  }
8286
8328
  function formatShapeSlot(a) {
8287
- const base = formatShape(a);
8329
+ return formatShapeSlotB(a, new ProjectionBudget());
8330
+ }
8331
+ function formatShapeSlotB(a, budget) {
8332
+ const base = formatShapeB(a, budget);
8288
8333
  if (!a.term || a.term.op === "lit") return base;
8289
8334
  if (a.shape.k === "fn" || a.shape.k === "arr") return base;
8290
8335
  if (a.shape.k === "any" && a.term.op === "var") return termToString(a.term);
8291
8336
  return `${base} = ${termToString(a.term)}`;
8292
8337
  }
8338
+ var FORMAT_TRUNC = { cycle: "\u2026cycle", depth: "\u2026" };
8293
8339
  function formatShape(a) {
8340
+ return formatShapeB(a, new ProjectionBudget());
8341
+ }
8342
+ function formatShapeB(a, budget) {
8343
+ const stop = budget.enter(a);
8344
+ if (stop === "cycle") return FORMAT_TRUNC.cycle;
8345
+ if (stop === "depth") return FORMAT_TRUNC.depth;
8346
+ try {
8347
+ return formatShapeInner(a, budget);
8348
+ } finally {
8349
+ budget.exit();
8350
+ }
8351
+ }
8352
+ function formatShapeInner(a, budget) {
8294
8353
  const s = a.shape;
8295
8354
  switch (s.k) {
8296
8355
  case "never":
@@ -8317,28 +8376,28 @@ function formatShape(a) {
8317
8376
  const entries = Object.entries(s.slots).map(([k, slot]) => {
8318
8377
  const opt = slot.optional ? "?" : "";
8319
8378
  const key = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
8320
- return `${key}${opt}: ${formatShape(slot.value)}`;
8379
+ return `${key}${opt}: ${formatShapeB(slot.value, budget)}`;
8321
8380
  });
8322
8381
  return `{ ${entries.join(", ")} }`;
8323
8382
  }
8324
8383
  case "arr": {
8325
8384
  if (s.element.shape.k === "any" && s.element.term?.op === "var") {
8326
- return `arr(${formatShapeSlot(s.element)})`;
8385
+ return `arr(${formatShapeSlotB(s.element, budget)})`;
8327
8386
  }
8328
- return `${formatShape(s.element)}[]`;
8387
+ return `${formatShapeB(s.element, budget)}[]`;
8329
8388
  }
8330
8389
  case "tuple": {
8331
8390
  const holes = s.holes ?? [];
8332
8391
  const parts = s.elements.map(
8333
- (el, i) => holes.includes(i) ? "" : formatShape(el)
8392
+ (el, i) => holes.includes(i) ? "" : formatShapeB(el, budget)
8334
8393
  );
8335
8394
  if (s.rest) {
8336
- parts.push(`...${formatShape(s.rest)}`);
8395
+ parts.push(`...${formatShapeB(s.rest, budget)}`);
8337
8396
  }
8338
8397
  return `[${parts.join(", ")}]`;
8339
8398
  }
8340
8399
  case "fn": {
8341
- const ret = s.returnType !== void 0 ? formatShapeSlot(s.returnType) : "?";
8400
+ const ret = s.returnType !== void 0 ? formatShapeSlotB(s.returnType, budget) : "?";
8342
8401
  const labels = s.params ?? [];
8343
8402
  const paramTypes = s.paramTypes;
8344
8403
  const hasMarkers = labels.some(
@@ -8350,7 +8409,7 @@ function formatShape(a) {
8350
8409
  for (let i = 0; i < n; i++) {
8351
8410
  const label = labels[i];
8352
8411
  const type = paramTypes?.[i];
8353
- const typeText = type !== void 0 ? formatShapeSlot(type) : void 0;
8412
+ const typeText = type !== void 0 ? formatShapeSlotB(type, budget) : void 0;
8354
8413
  if (label?.startsWith("...")) {
8355
8414
  ps.push(typeText ? `...${label.slice(3)}: ${typeText}` : label);
8356
8415
  } else if (label?.endsWith("?")) {
@@ -8367,7 +8426,7 @@ function formatShape(a) {
8367
8426
  return `(${renderLabeled().join(", ")}) => ${ret}`;
8368
8427
  }
8369
8428
  if (paramTypes && paramTypes.length > 0) {
8370
- const ps = paramTypes.map((p) => formatShapeSlot(p));
8429
+ const ps = paramTypes.map((p) => formatShapeSlotB(p, budget));
8371
8430
  return `(${ps.join(", ")}) => ${ret}`;
8372
8431
  }
8373
8432
  return `(${labels.join(", ")}) => ${ret}`;
@@ -8378,7 +8437,7 @@ function formatShape(a) {
8378
8437
  if (slots) {
8379
8438
  const arg = (key) => {
8380
8439
  const sl = slots[key];
8381
- return sl ? formatShapeSlot(sl.value) : void 0;
8440
+ return sl ? formatShapeSlotB(sl.value, budget) : void 0;
8382
8441
  };
8383
8442
  if (s.name === "Map" || s.name === "ReadonlyMap" || s.name === "WeakMap") {
8384
8443
  const k = arg("__key");
@@ -8393,12 +8452,12 @@ function formatShape(a) {
8393
8452
  return `${s.name}`;
8394
8453
  }
8395
8454
  case "eff":
8396
- return `${s.eff}<${formatShape(s.inner)}>`;
8455
+ return `${s.eff}<${formatShapeB(s.inner, budget)}>`;
8397
8456
  case "sum": {
8398
8457
  const seen = /* @__PURE__ */ new Set();
8399
8458
  const parts = [];
8400
8459
  for (const m of s.members) {
8401
- const t = formatShape(m);
8460
+ const t = formatShapeB(m, budget);
8402
8461
  if (seen.has(t)) continue;
8403
8462
  seen.add(t);
8404
8463
  parts.push(t);
@@ -8707,8 +8766,10 @@ function identBoundaryRegex(name) {
8707
8766
  var NudoUnsupportedError = class extends Error {
8708
8767
  reason;
8709
8768
  loc;
8710
- constructor(reason, loc) {
8711
- super(`nudo:unsupported ${reason}`);
8769
+ constructor(reason, loc, detail) {
8770
+ super(
8771
+ detail ? `nudo:unsupported ${reason} (${detail})` : `nudo:unsupported ${reason}`
8772
+ );
8712
8773
  this.name = "NudoUnsupportedError";
8713
8774
  this.reason = reason;
8714
8775
  this.loc = loc;
@@ -10112,15 +10173,15 @@ function transpileStatement(stmt, depth, opts) {
10112
10173
  if (nsSpecs.length > 0) {
10113
10174
  if (!stmt.source) return `${pad}/* export * as without source skipped */`;
10114
10175
  return nsSpecs.map((s) => {
10115
- const exported = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
10176
+ const exported = s.exported.type === "Identifier" ? s.exported.name : JSON.stringify(s.exported.value);
10116
10177
  return `${pad}export * as ${exported} from ${JSON.stringify(stmt.source.value)};`;
10117
10178
  }).join("\n");
10118
10179
  }
10119
10180
  const specs = stmt.specifiers.filter(
10120
10181
  (s) => "local" in s
10121
10182
  ).map((s) => {
10122
- const local = s.local.type === "Identifier" ? s.local.name : s.local.value;
10123
- const exported = s.exported.type === "Identifier" ? s.exported.name : s.exported.value;
10183
+ const local = s.local.type === "Identifier" ? s.local.name : JSON.stringify(s.local.value);
10184
+ const exported = s.exported.type === "Identifier" ? s.exported.name : JSON.stringify(s.exported.value);
10124
10185
  return local === exported ? local : `${local} as ${exported}`;
10125
10186
  }).join(", ");
10126
10187
  if (stmt.source) {
@@ -12049,10 +12110,10 @@ function $toNumeric(a) {
12049
12110
  return toNumericAbs(a);
12050
12111
  }
12051
12112
  function $updateAdd(a) {
12052
- return updateAddAbs(a);
12113
+ return updateAddAbs(a, currentExecPhi());
12053
12114
  }
12054
12115
  function $updateSub(a) {
12055
- return updateSubAbs(a);
12116
+ return updateSubAbs(a, currentExecPhi());
12056
12117
  }
12057
12118
  function $neg(a) {
12058
12119
  return negAbs(a);
@@ -12298,14 +12359,44 @@ function noteObjSlotMissing(recv, name, loc) {
12298
12359
 
12299
12360
  // src/algebra/exec/class-registry.ts
12300
12361
  var classRegistry = /* @__PURE__ */ new Map();
12362
+ var classEpoch = 0;
12363
+ var classEpochs = /* @__PURE__ */ new Map();
12364
+ var classCollisions = [];
12365
+ function beginClassEpoch() {
12366
+ classEpoch++;
12367
+ }
12368
+ function drainClassCollisions() {
12369
+ return classCollisions.splice(0, classCollisions.length);
12370
+ }
12371
+ function specShape(spec) {
12372
+ const ctorName = spec.ctor?.name ?? "anon";
12373
+ const methods = Object.keys(spec.methods ?? {}).sort().join(",");
12374
+ const statics = Object.keys(spec.staticMethods ?? {}).sort().join(",");
12375
+ const accessors = Object.keys(spec.accessors ?? {}).sort().join(",");
12376
+ return `${ctorName}[${methods}](${statics}){${accessors}}`;
12377
+ }
12301
12378
  function registerEvalClass(spec) {
12379
+ const prev = classRegistry.get(spec.name);
12380
+ const prevEpoch = classEpochs.get(spec.name);
12381
+ if (prev && prev !== spec && // 同 epoch(同一次 run 内)注册两次才可能是真碰撞:
12382
+ // 块级作用域同名类。跨 epoch = 顺序重注册(常态)。
12383
+ prevEpoch === classEpoch && specShape(prev) !== specShape(spec)) {
12384
+ classCollisions.push({
12385
+ name: spec.name,
12386
+ previous: specShape(prev),
12387
+ next: specShape(spec)
12388
+ });
12389
+ }
12302
12390
  classRegistry.set(spec.name, spec);
12391
+ classEpochs.set(spec.name, classEpoch);
12303
12392
  }
12304
12393
  function getEvalClass(name) {
12305
12394
  return classRegistry.get(name);
12306
12395
  }
12307
12396
  function clearBClasses() {
12308
12397
  classRegistry.clear();
12398
+ classEpochs.clear();
12399
+ classCollisions.length = 0;
12309
12400
  }
12310
12401
 
12311
12402
  // src/algebra/exec/runtime.ts
@@ -12603,6 +12694,8 @@ function $copy(a) {
12603
12694
  {
12604
12695
  k: "tuple",
12605
12696
  elements: s.elements.map($copy),
12697
+ // rest 槽必须随快照迁移(与 hof snapshotAbs 同口径),否则副本丢尾段
12698
+ ...s.rest ? { rest: $copy(s.rest) } : {},
12606
12699
  holes: s.holes ? [...s.holes] : void 0
12607
12700
  },
12608
12701
  a.term,
@@ -12793,7 +12886,7 @@ function $idx(a, i) {
12793
12886
  if (slots.length === 0) return objShape.open ? unknown : undef();
12794
12887
  const joinSlotsWithUndef = () => joinAbs(slots.reduce((x, y) => joinAbs(x, y)), undef());
12795
12888
  if (typeof iv === "string" || typeof iv === "number" || typeof iv === "boolean") {
12796
- const slot = objShape.slots[String(iv)];
12889
+ const slot = getSlot(objShape.slots, String(iv));
12797
12890
  if (slot) return slot.value;
12798
12891
  if (objShape.open) return unknown;
12799
12892
  return undef();
@@ -13010,6 +13103,20 @@ function $arrRest(a, start) {
13010
13103
  return parts.length ? parts.reduce((x, y) => joinAbs(x, y)) : unknown;
13011
13104
  }
13012
13105
  if (a.shape.k === "tuple") {
13106
+ if (a.shape.rest) {
13107
+ const sliced = a.shape.elements.slice(start);
13108
+ if (start < a.shape.elements.length && !shouldWidenArrayLiteral(sliced.length + 1)) {
13109
+ return abs(
13110
+ { k: "tuple", elements: sliced, rest: a.shape.rest },
13111
+ void 0,
13112
+ void 0,
13113
+ a.conf
13114
+ );
13115
+ }
13116
+ const restEl = a.shape.rest.shape.k === "arr" ? a.shape.rest.shape.element : a.shape.rest;
13117
+ const el = sliced.reduce((x, y) => joinAbs(x, y), restEl);
13118
+ return abs({ k: "arr", element: el }, void 0, void 0, a.conf);
13119
+ }
13013
13120
  return tupleOrWiden(a.shape.elements.slice(start), a.conf);
13014
13121
  }
13015
13122
  if (a.shape.k === "arr") return a;
@@ -15638,7 +15745,7 @@ function namespaceAbsOf(mod2) {
15638
15745
  return abs({ k: "obj", slots, open: true }, void 0, void 0, "path");
15639
15746
  }
15640
15747
  function bindImports(node, env, modules) {
15641
- const mod2 = modules[node.source.value];
15748
+ const mod2 = Object.hasOwn(modules, node.source.value) ? modules[node.source.value] : void 0;
15642
15749
  if (!mod2) {
15643
15750
  for (const s of node.specifiers) {
15644
15751
  env.vars.set(s.local.name, unknown);
@@ -15650,7 +15757,8 @@ function bindImports(node, env, modules) {
15650
15757
  env.vars.set(s.local.name, mod2.default ?? unknown);
15651
15758
  } else if (s.type === "ImportSpecifier") {
15652
15759
  const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
15653
- env.vars.set(s.local.name, mod2.named[imported] ?? unknown);
15760
+ const named = Object.hasOwn(mod2.named, imported) ? mod2.named[imported] : void 0;
15761
+ env.vars.set(s.local.name, named ?? unknown);
15654
15762
  } else if (s.type === "ImportNamespaceSpecifier") {
15655
15763
  env.vars.set(s.local.name, namespaceAbsOf(mod2));
15656
15764
  }
@@ -15676,7 +15784,7 @@ function collectAbsExports(file, env, modules) {
15676
15784
  for (const stmt of file.program.body) {
15677
15785
  if (stmt.type === "ExportNamedDeclaration") {
15678
15786
  if (stmt.source && modules) {
15679
- const mod2 = modules[stmt.source.value];
15787
+ const mod2 = Object.hasOwn(modules, stmt.source.value) ? modules[stmt.source.value] : void 0;
15680
15788
  if (mod2) {
15681
15789
  for (const spec of stmt.specifiers) {
15682
15790
  if (spec.type === "ExportNamespaceSpecifier") {
@@ -15688,7 +15796,7 @@ function collectAbsExports(file, env, modules) {
15688
15796
  if (spec.type !== "ExportSpecifier") continue;
15689
15797
  const local = spec.local.type === "Identifier" ? spec.local.name : spec.local.value;
15690
15798
  const exported = spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value;
15691
- const v2 = local === "default" ? mod2.default : mod2.named[local];
15799
+ const v2 = local === "default" ? mod2.default : Object.hasOwn(mod2.named, local) ? mod2.named[local] : void 0;
15692
15800
  const slot = v2 ?? unknown;
15693
15801
  if (exported === "default") defaultExport = slot;
15694
15802
  else {
@@ -15734,7 +15842,7 @@ function collectAbsExports(file, env, modules) {
15734
15842
  }
15735
15843
  }
15736
15844
  } else if (stmt.type === "ExportAllDeclaration" && stmt.source && modules) {
15737
- const mod2 = modules[stmt.source.value];
15845
+ const mod2 = Object.hasOwn(modules, stmt.source.value) ? modules[stmt.source.value] : void 0;
15738
15846
  if (mod2?.named) {
15739
15847
  for (const [k, v2] of Object.entries(mod2.named)) {
15740
15848
  if (!explicitNames.has(k)) named[k] = v2;
@@ -16079,6 +16187,7 @@ ${js}`;
16079
16187
  const fn = new Function(...argNames, `${js}
16080
16188
  ${ret}`);
16081
16189
  setEvalBindingSink(bindings);
16190
+ beginClassEpoch();
16082
16191
  try {
16083
16192
  const result = fn(...args);
16084
16193
  runBindings.set(result, bindings);
@@ -16086,6 +16195,18 @@ ${ret}`);
16086
16195
  } finally {
16087
16196
  setEvalBindingSink(null);
16088
16197
  drainPromiseMicros();
16198
+ noteDrainedClassCollisions();
16199
+ }
16200
+ }
16201
+ function noteDrainedClassCollisions() {
16202
+ for (const c of drainClassCollisions()) {
16203
+ noteEvalFallback(
16204
+ new NudoUnsupportedError(
16205
+ "class-collision",
16206
+ void 0,
16207
+ `class '${c.name}' registered again with a different shape (${c.previous} -> ${c.next}); name-keyed lookup may resolve to either definition`
16208
+ )
16209
+ );
16089
16210
  }
16090
16211
  }
16091
16212
  var evalFallbackCollector = null;
@@ -16147,14 +16268,16 @@ function isAbsVal(v2) {
16147
16268
  }
16148
16269
  function callTranspiledExportFull(exports, name, args, opts) {
16149
16270
  enterEvalCallBudgetSession();
16271
+ beginClassEpoch();
16150
16272
  try {
16151
16273
  return callTranspiledExportFullInner(exports, name, args, opts);
16152
16274
  } finally {
16153
16275
  exitEvalCallBudgetSession();
16276
+ noteDrainedClassCollisions();
16154
16277
  }
16155
16278
  }
16156
16279
  function callTranspiledExportFullInner(exports, name, args, opts) {
16157
- const fn = exports[name];
16280
+ const fn = Object.hasOwn(exports, name) ? exports[name] : void 0;
16158
16281
  if (typeof fn === "function") {
16159
16282
  const callArgs = args.map(
16160
16283
  (a) => a && typeof a === "object" && "shape" in a ? $copy(a) : a
@@ -16826,6 +16949,7 @@ export {
16826
16949
  v,
16827
16950
  app,
16828
16951
  termEquals,
16952
+ litKeyString,
16829
16953
  termToString,
16830
16954
  simplifyTerm,
16831
16955
  TYPEOF_NAMES,
@@ -16857,6 +16981,8 @@ export {
16857
16981
  geNum,
16858
16982
  ltNum,
16859
16983
  leNum,
16984
+ PROJECTION_MAX_DEPTH,
16985
+ ProjectionBudget,
16860
16986
  never,
16861
16987
  unknown,
16862
16988
  anyAbs,
@@ -17333,6 +17459,7 @@ export {
17333
17459
  resetEvalFallbackStats,
17334
17460
  noteEvalFallback,
17335
17461
  tryRunTranspiled,
17462
+ isAbsVal,
17336
17463
  callTranspiledExportFull,
17337
17464
  callTranspiledExport,
17338
17465
  callTranspiledExportApply,
package/dist/exec.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as Abs, M as MayThrowEffect, b as Phi } from './run-WJfj477N.js';
2
- export { O as $assignRecord, Q as $call, U as $callNamed, V as $recordBinding, a1 as EvalAbsAssignRecord, a2 as EvalCallRecord, a3 as EvalFallback, f as EvalFallbackStats, a6 as MAX_EVAL_CALL_DEPTH, a7 as MAX_EVAL_TOTAL_CALLS, a8 as RUNTIME_IMPORT_RE, N as RunTranspiledOptions, aa as TranspiledCallResult, ao as bindingsOf, ar as callTranspiledExport, as as callTranspiledExportApply, at as callTranspiledExportFull, g as clearPureCallMemo, h as clearPureMemo, i as enterEvalCallBudgetSession, ay as evalExprAbs, k as exitEvalCallBudgetSession, q as getEvalCallBudgetState, aB as getEvalCallCollector, r as getEvalFallbackStats, aZ as noteEvalCallRecord, a_ as noteEvalFallback, bf as resetEvalCallBudget, z as resetEvalFallbackStats, bz as routeApplyThrows, bg as runTranspiled, bh as runTranspiledOptionsMemoKey, bi as setEvalAssignCollector, bj as setEvalBindingSink, bk as setEvalCallCollector, bl as setEvalFallbackCollector, bw as tryRunTranspiled, H as withEvalBudgetOverride } from './run-WJfj477N.js';
1
+ import { A as Abs, M as MayThrowEffect, b as Phi } from './run-Dg7gQO2z.js';
2
+ export { O as $assignRecord, Q as $call, U as $callNamed, V as $recordBinding, a1 as EvalAbsAssignRecord, a2 as EvalCallRecord, a3 as EvalFallback, f as EvalFallbackStats, a6 as MAX_EVAL_CALL_DEPTH, a7 as MAX_EVAL_TOTAL_CALLS, a8 as RUNTIME_IMPORT_RE, N as RunTranspiledOptions, aa as TranspiledCallResult, ao as bindingsOf, ar as callTranspiledExport, as as callTranspiledExportApply, at as callTranspiledExportFull, g as clearPureCallMemo, h as clearPureMemo, i as enterEvalCallBudgetSession, ay as evalExprAbs, k as exitEvalCallBudgetSession, q as getEvalCallBudgetState, aB as getEvalCallCollector, r as getEvalFallbackStats, aI as isAbsVal, a_ as noteEvalCallRecord, a$ as noteEvalFallback, bg as resetEvalCallBudget, z as resetEvalFallbackStats, bA as routeApplyThrows, bh as runTranspiled, bi as runTranspiledOptionsMemoKey, bj as setEvalAssignCollector, bk as setEvalBindingSink, bl as setEvalCallCollector, bm as setEvalFallbackCollector, bx as tryRunTranspiled, H as withEvalBudgetOverride } from './run-Dg7gQO2z.js';
3
3
  import { Node, Expression, File } from '@babel/types';
4
4
 
5
5
  /**
@@ -584,7 +584,9 @@ declare class NudoUnsupportedError extends Error {
584
584
  constructor(reason: string, loc?: {
585
585
  line: number;
586
586
  column: number;
587
- });
587
+ },
588
+ /** BUG-026:附加辨析面(如 class 碰撞的双方特征)——进 message 随回落观测 */
589
+ detail?: string);
588
590
  }
589
591
 
590
592
  export { $add, $arguments, $arr, $arrMutContainer, $arrRest, $arrWithHoles, $async, $asyncReturn, $await, $bitand, $bitnot, $bitor, $bitxor, $catchVal, $class, $classExpr, $collectionForEach, $concat, $copy, $del, $delRes, $div, $dynamicImport, $elems, $eq, $eqLoose, $fnVal, $for, $forInKeys, $forIter, $forOf, $fork, $ge, $gen, $get, $gt, $idx, $idxSet, $importMeta, $in, $instanceof, $instanceofNonIdent, $invoke, $invokeSuper, $isBreakTo, $isForkExit, $join, $le, $len, $lit, $loopBreak, $loopContinue, $loopReturn, $lt, $mod, $mul, $ne, $neLoose, $neg, $new, $not, $nullishTest, $obj, $objAccessor, $objRest, $optionalGet, $optionalInvoke, $orDefault, $pow, $pushLoopExit, $rawThis, $reStateCall, $regex, $rethrowIfNudoReturn, $set, $setKey, $setProto, $shl, $shr, $spread, $staticInvoke, $sub, $super, $switch, $thisGet, $thisSet, $throw, $toNumber, $toNumeric, $tryCurrentMark, $tryDetachSoftCatch, $tryDigestSoftCatch, $tryDiscardSoft, $tryMark, $tryOrphanSoft, $tryPopMark, $tryReleaseSoftCatch, $tryReleaseSoftOut, $tryTakeSince, $typeof, $unknown, $updateAdd, $updateSub, $ushr, $while, $whileSeq, $yield, DEFAULT_MAX_LOOP_ITERS, type EvalClassSpec, NudoLoopSignal, NudoReturn, NudoThrow, NudoUnsupportedError, type TranspileOptions, asAbsVal, callAtFunctionBoundary, clearBClasses, clearStaleTermPred, currentExecPhi, fillTuple, foldRequireSpecArg, foldStaticStringExpr, getEvalClass, isArrMutator, isDefinitelyFalse, isDefinitelyTrue, isNudoBreak, isNudoContinue, isNudoReturn, isNudoThrow, litTruth, lookupObjAccessor, namespaceNameOf, pushLoopExit, pushThrowExit, registerEvalClass, runWithLoopExits, runtimeImportOf, takeLoopExits, takeThrowExits, transpile, transpileBodyNode, transpileExpression, transpileFile, transpileSource, withExecPhi };
package/dist/exec.js CHANGED
@@ -140,6 +140,7 @@ import {
140
140
  getEvalCallCollector,
141
141
  getEvalClass,
142
142
  getEvalFallbackStats,
143
+ isAbsVal,
143
144
  isArrMutator,
144
145
  isDefinitelyFalse,
145
146
  isDefinitelyTrue,
@@ -176,7 +177,7 @@ import {
176
177
  tryRunTranspiled,
177
178
  withEvalBudgetOverride,
178
179
  withExecPhi
179
- } from "./chunk-W2MP6REI.js";
180
+ } from "./chunk-LZJC7X5J.js";
180
181
  export {
181
182
  $add,
182
183
  $arguments,
@@ -318,6 +319,7 @@ export {
318
319
  getEvalCallCollector,
319
320
  getEvalClass,
320
321
  getEvalFallbackStats,
322
+ isAbsVal,
321
323
  isArrMutator,
322
324
  isDefinitelyFalse,
323
325
  isDefinitelyTrue,