@nudojs/core 1.9.3 → 1.10.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.
@@ -15,6 +15,7 @@ import {
15
15
  fnConstraintToEntryReqs,
16
16
  foldRequireSpecArg,
17
17
  formalParamDisplayNames,
18
+ formalParamSignatureNames,
18
19
  formalParamsFromNodes,
19
20
  formatAbs,
20
21
  formatShape,
@@ -46,7 +47,7 @@ import {
46
47
  withEvalBudgetOverride,
47
48
  withExecPhi,
48
49
  withFnBudgetOverride
49
- } from "./chunk-HD6XZFOU.js";
50
+ } from "./chunk-YREYFITQ.js";
50
51
 
51
52
  // src/algebra/directive-scan.ts
52
53
  function cleanDirectiveLine(raw, kind) {
@@ -987,6 +988,75 @@ function rewriteSidecarSource(src, fromFile) {
987
988
  }
988
989
  }
989
990
  }
991
+ const aliasToImported = /* @__PURE__ */ new Map();
992
+ const fnAliases = /* @__PURE__ */ new Set(["fn"]);
993
+ for (const imp of imports) {
994
+ for (const n of imp.names) {
995
+ if (n.ns) continue;
996
+ if (n.imported !== void 0 && n.local !== void 0 && n.imported !== n.local) {
997
+ aliasToImported.set(n.local, n.imported);
998
+ }
999
+ if (n.imported === "fn" && n.local !== void 0) fnAliases.add(n.local);
1000
+ }
1001
+ }
1002
+ const paramTextsOfFnCall = (init) => {
1003
+ const call = init;
1004
+ if (!call || call.type !== "CallExpression") return void 0;
1005
+ if (call.callee?.type !== "Identifier" || !fnAliases.has(call.callee.name ?? "")) {
1006
+ return void 0;
1007
+ }
1008
+ const first = call.arguments?.[0];
1009
+ if (!first || first.type !== "ObjectExpression") return void 0;
1010
+ const texts = /* @__PURE__ */ new Map();
1011
+ for (const prop of first.properties ?? []) {
1012
+ if (prop.type !== "ObjectProperty" || prop.computed) continue;
1013
+ const key = prop.key?.type === "Identifier" ? prop.key.name : prop.key?.type === "StringLiteral" ? prop.key.value : void 0;
1014
+ if (key === void 0) continue;
1015
+ const v2 = prop.value;
1016
+ if (!v2 || v2.start == null || v2.end == null) continue;
1017
+ texts.set(
1018
+ key,
1019
+ v2.type === "Identifier" && aliasToImported.has(v2.name ?? "") ? aliasToImported.get(v2.name ?? "") : src.slice(v2.start, v2.end)
1020
+ );
1021
+ }
1022
+ return texts;
1023
+ };
1024
+ const paramInfosByLocal = /* @__PURE__ */ new Map();
1025
+ const collectVarFns = (decls) => {
1026
+ for (const d of decls) {
1027
+ if (d?.id?.type !== "Identifier" || d.id.name === void 0) continue;
1028
+ const texts = paramTextsOfFnCall(d.init);
1029
+ if (texts)
1030
+ paramInfosByLocal.set(d.id.name, {
1031
+ texts,
1032
+ line: d.loc?.start?.line ?? 1
1033
+ });
1034
+ }
1035
+ };
1036
+ for (const stmt of ast.program.body) {
1037
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration") {
1038
+ collectVarFns(
1039
+ stmt.declaration?.declarations ?? []
1040
+ );
1041
+ } else if (stmt.type === "VariableDeclaration") {
1042
+ collectVarFns(
1043
+ stmt.declarations ?? []
1044
+ );
1045
+ } else if (stmt.type === "ExportDefaultDeclaration") {
1046
+ const decl = stmt.declaration;
1047
+ const texts = paramTextsOfFnCall(decl);
1048
+ if (texts)
1049
+ paramInfosByLocal.set(DEFAULT_LOCAL, {
1050
+ texts,
1051
+ line: stmt.loc?.start?.line ?? 1
1052
+ });
1053
+ }
1054
+ }
1055
+ const bindingInfos = /* @__PURE__ */ new Map();
1056
+ for (const p of exportPairs) {
1057
+ const info = paramInfosByLocal.get(p.expr);
1058
+ if (info) bindingInfos.set(p.key, info);
1059
+ }
990
1060
  if (fromFile?.endsWith(".nudo.ts")) {
991
1061
  for (const cut of collectTsSyntaxCuts(ast.program, src)) {
992
1062
  cuts.push({ start: cut.start, end: cut.end, text: "" });
@@ -1010,7 +1080,7 @@ function rewriteSidecarSource(src, fromFile) {
1010
1080
  pos = c.end;
1011
1081
  }
1012
1082
  code += src.slice(pos);
1013
- return { code, exportPairs, imports };
1083
+ return { code, exportPairs, imports, bindingInfos };
1014
1084
  }
1015
1085
  var TS_STMT_CUT_TYPES = /* @__PURE__ */ new Set([
1016
1086
  "TSInterfaceDeclaration",
@@ -1082,9 +1152,25 @@ function collectTsSyntaxCuts(node, src, cuts = []) {
1082
1152
  }
1083
1153
  return cuts;
1084
1154
  }
1155
+ function registerSidecarParamTexts(sidecarPath, contentKey, infos) {
1156
+ let perPath = sidecarParamTextsRegistry.get(sidecarPath);
1157
+ if (perPath === void 0) {
1158
+ perPath = /* @__PURE__ */ new Map();
1159
+ sidecarParamTextsRegistry.set(sidecarPath, perPath);
1160
+ if (sidecarParamTextsRegistry.size > MAX_SIDECAR_PARAM_TEXTS_PATHS) {
1161
+ const oldest = sidecarParamTextsRegistry.keys().next().value;
1162
+ if (oldest !== void 0) sidecarParamTextsRegistry.delete(oldest);
1163
+ }
1164
+ }
1165
+ perPath.set(contentKey, infos);
1166
+ if (perPath.size > MAX_SIDECAR_PARAM_TEXTS_VERSIONS) {
1167
+ const oldest = perPath.keys().next().value;
1168
+ if (oldest !== void 0) perPath.delete(oldest);
1169
+ }
1170
+ }
1085
1171
  function execSidecar(src, opts, chain, preloaded) {
1086
1172
  const fromFile = opts?.fromFile;
1087
- const { code, exportPairs, imports } = rewriteSidecarSource(src, fromFile);
1173
+ const { code, exportPairs, imports, bindingInfos } = rewriteSidecarSource(src, fromFile);
1088
1174
  const prologue = [];
1089
1175
  const deps = {};
1090
1176
  for (const imp of imports) {
@@ -1166,12 +1252,13 @@ function execSidecar(src, opts, chain, preloaded) {
1166
1252
  for (const n of imp.names) deps[n.local] = void 0;
1167
1253
  continue;
1168
1254
  }
1169
- const depExports = execSidecar(
1255
+ const depOut = execSidecar(
1170
1256
  depSrc,
1171
1257
  { loadModule: load, fromFile: canonical },
1172
1258
  [...chain, canonical],
1173
1259
  preloaded
1174
1260
  );
1261
+ const depExports = depOut.exports;
1175
1262
  for (const n of imp.names) {
1176
1263
  if (n.ns) {
1177
1264
  deps[n.local] = depExports;
@@ -1211,7 +1298,11 @@ return { ${returnObj} };`;
1211
1298
  "__nudoDeps",
1212
1299
  body
1213
1300
  );
1214
- return fn(...Object.values(sidecarInjects), sidecarInjects, deps);
1301
+ const result = fn(...Object.values(sidecarInjects), sidecarInjects, deps);
1302
+ if (fromFile !== void 0 && bindingInfos.size > 0) {
1303
+ registerSidecarParamTexts(fromFile, hashSource(src), bindingInfos);
1304
+ }
1305
+ return { exports: result, bindingInfos };
1215
1306
  }
1216
1307
  function execNudoModule(src, opts) {
1217
1308
  return execNudoModuleCached(src, opts ?? {});
@@ -1267,22 +1358,29 @@ function execNudoModuleCached(src, opts) {
1267
1358
  if (hit !== void 0) {
1268
1359
  nudoModuleExecCache.delete(key);
1269
1360
  nudoModuleExecCache.set(key, hit);
1270
- return hit;
1361
+ if (opts.fromFile !== void 0 && hit.bindingInfos.size > 0) {
1362
+ registerSidecarParamTexts(
1363
+ opts.fromFile,
1364
+ hashSource(src),
1365
+ hit.bindingInfos
1366
+ );
1367
+ }
1368
+ return hit.exports;
1271
1369
  }
1272
1370
  if (nudoModuleFailCache.has(key)) {
1273
1371
  throw nudoModuleFailCache.get(key);
1274
1372
  }
1275
1373
  }
1276
1374
  try {
1277
- const out = execSidecar(src, opts, opts.fromFile ? [opts.fromFile] : [], preloaded);
1375
+ const { exports, bindingInfos } = execSidecar(src, opts, opts.fromFile ? [opts.fromFile] : [], preloaded);
1278
1376
  if (key !== void 0) {
1279
1377
  if (nudoModuleExecCache.size >= MAX_NUDO_MODULE_EXEC) {
1280
1378
  const oldest = nudoModuleExecCache.keys().next().value;
1281
1379
  if (oldest !== void 0) nudoModuleExecCache.delete(oldest);
1282
1380
  }
1283
- nudoModuleExecCache.set(key, out);
1381
+ nudoModuleExecCache.set(key, { exports, bindingInfos });
1284
1382
  }
1285
- return out;
1383
+ return exports;
1286
1384
  } catch (e) {
1287
1385
  if (key !== void 0) {
1288
1386
  if (nudoModuleFailCache.size >= MAX_NUDO_MODULE_EXEC) {
@@ -1297,6 +1395,17 @@ function execNudoModuleCached(src, opts) {
1297
1395
  function resetNudoModuleExecCache() {
1298
1396
  nudoModuleExecCache.clear();
1299
1397
  nudoModuleFailCache.clear();
1398
+ sidecarParamTextsRegistry.clear();
1399
+ }
1400
+ var sidecarParamTextsRegistry = /* @__PURE__ */ new Map();
1401
+ var MAX_SIDECAR_PARAM_TEXTS_PATHS = 64;
1402
+ var MAX_SIDECAR_PARAM_TEXTS_VERSIONS = 32;
1403
+ function lookupSidecarParamTexts(sidecarPath, srcHash, bindingName) {
1404
+ const perPath = sidecarParamTextsRegistry.get(sidecarPath);
1405
+ if (perPath === void 0) return void 0;
1406
+ const perHash = perPath.get(srcHash);
1407
+ if (perHash === void 0) return void 0;
1408
+ return perHash.get(bindingName);
1300
1409
  }
1301
1410
  function collectConstraints(source, opts) {
1302
1411
  const map = /* @__PURE__ */ new Map();
@@ -1410,7 +1519,8 @@ function extractRefinesFromSource(source, fnName, opts = {}) {
1410
1519
  out.push({
1411
1520
  param,
1412
1521
  pred: instantiateConstraint(c, param),
1413
- constraint: c
1522
+ constraint: c,
1523
+ name: cName
1414
1524
  });
1415
1525
  }
1416
1526
  }
@@ -1857,18 +1967,25 @@ function loadSidecarBinding(source, fnName, opts) {
1857
1967
  return { ok: false };
1858
1968
  }
1859
1969
  let binding = Object.hasOwn(exports, fnName) ? exports[fnName] : void 0;
1970
+ let bindingKey;
1971
+ if (binding !== void 0) bindingKey = fnName;
1860
1972
  if (binding === void 0) {
1861
1973
  const aliasExport2 = exportedNameOfLocal(source, fnName);
1862
1974
  if (aliasExport2 !== void 0 && Object.hasOwn(exports, aliasExport2)) {
1863
1975
  binding = exports[aliasExport2];
1976
+ bindingKey = aliasExport2;
1864
1977
  }
1865
1978
  }
1866
1979
  if (binding === void 0 && exports.default !== void 0 && isDefaultExportLocal(source, fnName)) {
1867
1980
  binding = exports.default;
1981
+ bindingKey = "default";
1868
1982
  }
1869
1983
  if (binding === void 0 && fnName.includes(".")) {
1870
1984
  const [cls, method] = fnName.split(".", 2);
1871
- binding = Object.hasOwn(exports, `${cls}_${method}`) ? exports[`${cls}_${method}`] : void 0;
1985
+ if (Object.hasOwn(exports, `${cls}_${method}`)) {
1986
+ binding = exports[`${cls}_${method}`];
1987
+ bindingKey = `${cls}_${method}`;
1988
+ }
1872
1989
  if (binding === void 0) {
1873
1990
  const bag = Object.hasOwn(exports, cls) ? exports[cls] : void 0;
1874
1991
  if (bag && typeof bag === "object" && !isNudoConstraint(bag)) {
@@ -1906,11 +2023,14 @@ function loadSidecarBinding(source, fnName, opts) {
1906
2023
  const [cls, method] = fnName.split(".", 2);
1907
2024
  return generatedNames.has(`${cls}_${method}`) || generatedNames.has(cls);
1908
2025
  })() : false);
2026
+ const info = bindingKey !== void 0 ? lookupSidecarParamTexts(sidecarPath, hashSource(sidecarSrc), bindingKey) : void 0;
1909
2027
  return {
1910
2028
  ok: true,
1911
2029
  sidecarPath,
1912
2030
  constraint: binding,
1913
- generated: isGenerated
2031
+ generated: isGenerated,
2032
+ ...info?.texts !== void 0 ? { paramTexts: info.texts } : {},
2033
+ ...info?.line !== void 0 ? { sidecarLine: info.line } : {}
1914
2034
  };
1915
2035
  }
1916
2036
  function boundsOf(c) {
@@ -2013,8 +2133,24 @@ function effectiveInterface(source, fnName, opts = {}) {
2013
2133
  collectDiag2({ code: d.code, message: d.message, ...d.file ? { file: d.file } : {} });
2014
2134
  }
2015
2135
  const sidecarFn = sidecar.ok ? sidecar.constraint : void 0;
2136
+ const sidecarParamTexts = sidecar.ok ? sidecar.paramTexts : void 0;
2016
2137
  const sidecarGenerated = sidecar.ok && sidecar.generated;
2017
2138
  const sidecarParams = sidecarFn ? fnConstraintToEntryReqs(sidecarFn) : [];
2139
+ const sidecarParamSet = new Set(sidecarParams.map((p) => p.param));
2140
+ const sidecarTexts = /* @__PURE__ */ new Map();
2141
+ if (sidecarParamTexts) {
2142
+ for (const param of sidecarParamSet) {
2143
+ const t = sidecarParamTexts.get(param);
2144
+ if (t !== void 0) sidecarTexts.set(param, t);
2145
+ }
2146
+ }
2147
+ const directiveNames = /* @__PURE__ */ new Map();
2148
+ for (const e of sourceEntries) {
2149
+ if (e.name === void 0) continue;
2150
+ const list = directiveNames.get(e.param);
2151
+ if (list === void 0) directiveNames.set(e.param, [e.name]);
2152
+ else if (!list.includes(e.name)) list.push(e.name);
2153
+ }
2018
2154
  const sidecarReturns = sidecarFn?.fn.returns;
2019
2155
  const sidecarThrowsKinds = throwConstraintToKinds(sidecarFn?.fn.throws);
2020
2156
  const mergedThrows = (() => {
@@ -2027,9 +2163,16 @@ function effectiveInterface(source, fnName, opts = {}) {
2027
2163
  if (sidecarFn !== void 0 && sidecarGenerated) {
2028
2164
  return {
2029
2165
  fnName,
2030
- params: sidecarParams,
2166
+ params: sidecarParams.map(({ param, constraint }) => {
2167
+ const display = sidecarTexts.get(param);
2168
+ return display !== void 0 ? { param, constraint, display } : { param, constraint };
2169
+ }),
2031
2170
  ...sidecarReturns !== void 0 ? { returns: { constraint: sidecarReturns } } : {},
2032
2171
  ...mergedThrows !== void 0 ? { throws: { kinds: mergedThrows } } : {},
2172
+ ...sidecar.ok ? {
2173
+ sidecarPath: sidecar.sidecarPath,
2174
+ ...sidecar.sidecarLine !== void 0 ? { sidecarLine: sidecar.sidecarLine } : {}
2175
+ } : {},
2033
2176
  source: "generated"
2034
2177
  };
2035
2178
  }
@@ -2082,9 +2225,27 @@ function effectiveInterface(source, fnName, opts = {}) {
2082
2225
  }
2083
2226
  return {
2084
2227
  fnName,
2085
- params: [...params.entries()].map(([param, constraint]) => ({ param, constraint })),
2228
+ params: [...params.entries()].map(([param, constraint]) => {
2229
+ const sidecarText = sidecarTexts.get(param);
2230
+ const names = directiveNames.get(param);
2231
+ const fromSidecar = sidecarFn !== void 0 && !sidecarGenerated && sidecarParamSet.has(param);
2232
+ const fromDirective = names !== void 0;
2233
+ let display;
2234
+ if (fromSidecar && fromDirective) {
2235
+ display = sidecarText !== void 0 ? `${sidecarText} \u2227 ${names.join(" \u2227 ")}` : void 0;
2236
+ } else if (fromSidecar) {
2237
+ display = sidecarText;
2238
+ } else if (fromDirective) {
2239
+ display = names.join(" \u2227 ");
2240
+ }
2241
+ return display !== void 0 ? { param, constraint, display } : { param, constraint };
2242
+ }),
2086
2243
  ...returnsC !== void 0 ? { returns: { constraint: returnsC } } : {},
2087
2244
  ...mergedThrows !== void 0 ? { throws: { kinds: mergedThrows } } : {},
2245
+ ...sidecar.ok ? {
2246
+ sidecarPath: sidecar.sidecarPath,
2247
+ ...sidecar.sidecarLine !== void 0 ? { sidecarLine: sidecar.sidecarLine } : {}
2248
+ } : {},
2088
2249
  source: "handwritten",
2089
2250
  ...conflictParams.length > 0 || conflictReturns ? {
2090
2251
  conflict: {
@@ -2235,7 +2396,7 @@ function formatConstraint(c) {
2235
2396
  return fmtConstraint(c);
2236
2397
  }
2237
2398
  function formatEffectiveInterfaceDisplay(eff) {
2238
- const params = eff.params.map((p) => `${p.param}: ${formatConstraint(p.constraint)}`).join(", ");
2399
+ const params = eff.params.map((p) => `${p.param}: ${p.display ?? formatConstraint(p.constraint)}`).join(", ");
2239
2400
  let s = `(${params})`;
2240
2401
  if (eff.returns) s += ` \u2192 ${formatConstraint(eff.returns.constraint)}`;
2241
2402
  return s;
@@ -2253,7 +2414,12 @@ function interfaceTierOf(source, fnName, fromFile, opts = {}) {
2253
2414
  const eff = effectiveInterface(source, fnName, { fromFile, ...opts });
2254
2415
  const src = eff?.source ?? "implicit";
2255
2416
  if (eff && src !== "implicit") {
2256
- return { source: src, display: formatEffectiveInterfaceDisplay(eff) };
2417
+ return {
2418
+ source: src,
2419
+ display: formatEffectiveInterfaceDisplay(eff),
2420
+ ...eff.sidecarPath !== void 0 ? { sidecarPath: eff.sidecarPath } : {},
2421
+ ...eff.sidecarLine !== void 0 ? { sidecarLine: eff.sidecarLine } : {}
2422
+ };
2257
2423
  }
2258
2424
  return { source: src };
2259
2425
  }
@@ -3479,7 +3645,8 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3479
3645
  if (reqs.length > 0) {
3480
3646
  entryReqs = reqs.map((e) => ({
3481
3647
  param: e.param,
3482
- pred: instantiateConstraint(e.constraint, e.param)
3648
+ pred: instantiateConstraint(e.constraint, e.param),
3649
+ ...e.display !== void 0 ? { display: e.display } : {}
3483
3650
  }));
3484
3651
  const patternFields = /* @__PURE__ */ new Map();
3485
3652
  for (const e of reqs) {
@@ -3504,10 +3671,33 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3504
3671
  patternFields.set(hit.index, bag);
3505
3672
  }
3506
3673
  const fieldAbs = constraintToEntryAbs(e.constraint, e.param);
3507
- bag[hit.field] = {
3674
+ const leaf = {
3508
3675
  abs: fieldAbs,
3509
3676
  ...e.constraint.isOptional ? { optional: true } : {}
3510
3677
  };
3678
+ const segs = hit.fieldPath ?? [hit.field];
3679
+ if (segs.length <= 1) {
3680
+ bag[hit.field] = leaf;
3681
+ continue;
3682
+ }
3683
+ let level = bag;
3684
+ let skip = false;
3685
+ for (let i = 0; i < segs.length - 1; i++) {
3686
+ const node = level[segs[i]];
3687
+ if (node === void 0) {
3688
+ const sub = { slots: {} };
3689
+ level[segs[i]] = sub;
3690
+ level = sub.slots;
3691
+ } else if ("slots" in node) {
3692
+ level = node.slots;
3693
+ } else {
3694
+ skip = true;
3695
+ break;
3696
+ }
3697
+ }
3698
+ if (!skip) {
3699
+ level[segs[segs.length - 1]] = leaf;
3700
+ }
3511
3701
  } else if (hit) {
3512
3702
  const entryAbs = constraintToEntryAbs(e.constraint, e.param);
3513
3703
  typeParams[hit.index] = {
@@ -3520,20 +3710,34 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3520
3710
  });
3521
3711
  }
3522
3712
  }
3523
- for (const [pIdx, bag] of patternFields) {
3713
+ const sigNames = formalParamSignatureNames(formals);
3714
+ const buildPatternObjAbs = (slotsBag, sigName, walked) => {
3524
3715
  const slots = {};
3525
- for (const [key, rec] of Object.entries(bag)) {
3526
- slots[key] = {
3527
- value: rec.abs,
3528
- ...rec.optional ? { optional: true } : {}
3529
- };
3716
+ for (const [key, node] of Object.entries(slotsBag)) {
3717
+ if ("abs" in node) {
3718
+ slots[key] = {
3719
+ value: node.abs,
3720
+ ...node.optional ? { optional: true } : {}
3721
+ };
3722
+ } else {
3723
+ slots[key] = {
3724
+ value: buildPatternObjAbs(node.slots, sigName, [...walked, key])
3725
+ };
3726
+ }
3530
3727
  }
3531
- const objAbs = abs(
3728
+ return abs(
3532
3729
  { k: "obj", slots },
3533
- v(params[pIdx] ?? `_p${pIdx}`),
3730
+ v(walked.length === 0 ? sigName : `${sigName}.${walked.join(".")}`),
3534
3731
  void 0,
3535
3732
  "path"
3536
3733
  );
3734
+ };
3735
+ for (const [pIdx, bag] of patternFields) {
3736
+ const objAbs = buildPatternObjAbs(
3737
+ bag,
3738
+ sigNames[pIdx] ?? params[pIdx] ?? `_p${pIdx}`,
3739
+ []
3740
+ );
3537
3741
  typeParams[pIdx] = {
3538
3742
  id: typeParams[pIdx].id,
3539
3743
  value: objAbs
@@ -3677,6 +3881,7 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3677
3881
  }));
3678
3882
  }
3679
3883
  }
3884
+ const renderNames = formals.length > 0 ? formalParamSignatureNames(formals) : params;
3680
3885
  return {
3681
3886
  name: fnName,
3682
3887
  params,
@@ -3685,6 +3890,7 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3685
3890
  instantiate: (args, phi) => run(args, phi ?? pTrue),
3686
3891
  display: formatPoly(
3687
3892
  fnName,
3893
+ renderNames,
3688
3894
  params,
3689
3895
  typeParams,
3690
3896
  symbolic,
@@ -3699,12 +3905,13 @@ function generalizeFromAstUncached(fnName, source, opts = {}) {
3699
3905
  ...hofSites ? { hofSites } : {}
3700
3906
  };
3701
3907
  }
3702
- function formatPoly(name, params, typeParams, symbolic, entryReqs, entryShapes, fnRels) {
3908
+ function formatPoly(name, renderNames, params, typeParams, symbolic, entryReqs, entryShapes, fnRels) {
3703
3909
  const reqByParam = new Map((entryReqs ?? []).map((r) => [r.param, r.pred]));
3704
- const ps = params.map((p, i) => {
3910
+ const ps = renderNames.map((p, i) => {
3911
+ const key = params[i] ?? p;
3705
3912
  const id = typeParams[i]?.id ?? "unknown";
3706
- const pred = reqByParam.get(p);
3707
- const promoted = entryShapes?.get(p)?.abs ?? fnRels?.get(p)?.abs;
3913
+ const pred = reqByParam.get(key);
3914
+ const promoted = entryShapes?.get(key)?.abs ?? fnRels?.get(key)?.abs;
3708
3915
  if (promoted) {
3709
3916
  const slot = formatShapeSlot(promoted);
3710
3917
  if (pred && pred.op !== "true") {
@@ -4480,13 +4687,17 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4480
4687
  eiCache.set(key, r);
4481
4688
  return r;
4482
4689
  };
4483
- const projectArgField = (arg, field) => {
4484
- if (!arg) return void 0;
4485
- if (arg.shape.k === "brand") {
4486
- return projectArgField(arg.shape.shape, field);
4690
+ const projectArgPath = (arg, segments) => {
4691
+ let cur = arg;
4692
+ for (const seg of segments) {
4693
+ if (!cur) return void 0;
4694
+ if (cur.shape.k === "brand") {
4695
+ cur = cur.shape.shape;
4696
+ }
4697
+ if (cur.shape.k !== "obj") return void 0;
4698
+ cur = getSlot(cur.shape.slots, seg)?.value;
4487
4699
  }
4488
- if (arg.shape.k !== "obj") return void 0;
4489
- return getSlot(arg.shape.slots, field)?.value;
4700
+ return cur;
4490
4701
  };
4491
4702
  const interfaceToIndexed = (ei, paramNames, formals) => {
4492
4703
  const conflict = new Set(ei.conflict?.params ?? []);
@@ -4508,7 +4719,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4508
4719
  entries.push([
4509
4720
  hit.index,
4510
4721
  { param, pred: instantiateConstraint(constraint, param), constraint },
4511
- hit.field
4722
+ hit.fieldPath ?? (hit.field !== void 0 ? [hit.field] : void 0)
4512
4723
  ]);
4513
4724
  }
4514
4725
  }
@@ -4544,17 +4755,18 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4544
4755
  return lv <= n;
4545
4756
  };
4546
4757
  const checkReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
4547
- for (const [idx, pred, field, contractParam] of reqs) {
4758
+ for (const [idx, pred, fieldPath, contractParam] of reqs) {
4548
4759
  const argIdx = argIndexOf(idx);
4549
4760
  if (argIdx === void 0) continue;
4550
4761
  let arg = absArgs[argIdx];
4551
4762
  if (!arg) continue;
4552
4763
  const paramName = contractParam || paramNames[idx] || `arg${idx}`;
4553
- if (field) {
4554
- const projected = projectArgField(arg, field);
4764
+ if (fieldPath) {
4765
+ const projected = projectArgPath(arg, fieldPath);
4555
4766
  if (!projected) {
4556
4767
  const k = arg.shape.k;
4557
4768
  if (k !== "unknown" && k !== "any") {
4769
+ const field = fieldPath.join(".");
4558
4770
  out.push({
4559
4771
  severity: "error",
4560
4772
  code: "nudo:constraint-violated",
@@ -4895,7 +5107,7 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4895
5107
  }
4896
5108
  };
4897
5109
  const checkShapeReqs = (displayName, reqs, paramNames, absArgs, argIndexOf, loc) => {
4898
- for (const [idx, entry, field] of reqs) {
5110
+ for (const [idx, entry, fieldPath] of reqs) {
4899
5111
  const c = entry.constraint;
4900
5112
  if (!c.fields && !c.element && !isIntFlag(c) && !c.prim) continue;
4901
5113
  const argIdx = argIndexOf(idx);
@@ -4903,11 +5115,12 @@ function scanLiteralCalls(source, knownFns, phi, opts) {
4903
5115
  let arg = absArgs[argIdx];
4904
5116
  if (!arg) continue;
4905
5117
  const paramName = entry.param || paramNames[idx] || `arg${idx}`;
4906
- if (field) {
4907
- const projected = projectArgField(arg, field);
5118
+ if (fieldPath) {
5119
+ const projected = projectArgPath(arg, fieldPath);
4908
5120
  if (!projected) {
4909
5121
  const k = arg.shape.k;
4910
5122
  if (k !== "unknown" && k !== "any") {
5123
+ const field = fieldPath.join(".");
4911
5124
  out.push({
4912
5125
  severity: "error",
4913
5126
  code: "nudo:constraint-violated",