nudojs 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +157 -55
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -17635,6 +17635,29 @@ var init_may_throw = __esm({
17635
17635
  });
17636
17636
 
17637
17637
  // ../core/src/algebra/arithmetic.ts
17638
+ function distributeSumBinOp(op, a, b, phi2, eval2) {
17639
+ if (a.shape.k !== "sum" && b.shape.k !== "sum") return void 0;
17640
+ const parts = [];
17641
+ const as = a.shape.k === "sum" ? a.shape.members : [a];
17642
+ const bs = b.shape.k === "sum" ? b.shape.members : [b];
17643
+ for (const x of as) {
17644
+ for (const y of bs) {
17645
+ const r = eval2(x, y, phi2);
17646
+ if (r.shape.k === "never") continue;
17647
+ if (r.shape.k === "sum") parts.push(...r.shape.members);
17648
+ else parts.push(r);
17649
+ }
17650
+ }
17651
+ const uniq = dedupAbsMembers(parts);
17652
+ const term = a.term && b.term ? simplifyTerm(app(op, [a.term, b.term])) : void 0;
17653
+ if (uniq.length === 0) {
17654
+ return abs({ k: "unknown" }, term, void 0, "partial");
17655
+ }
17656
+ if (uniq.length === 1) {
17657
+ return abs(uniq[0].shape, term, uniq[0].pred, confJoin(a.conf, b.conf));
17658
+ }
17659
+ return abs({ k: "sum", members: uniq }, term, void 0, "partial");
17660
+ }
17638
17661
  function add(a, b, phi2 = pTrue) {
17639
17662
  if (isSymbolAbs(a) || isSymbolAbs(b)) {
17640
17663
  throw new NudoThrow(errorTypeAbs("TypeError"));
@@ -17692,28 +17715,8 @@ function add(a, b, phi2 = pTrue) {
17692
17715
  "partial"
17693
17716
  );
17694
17717
  }
17695
- if (a.shape.k === "sum" || b.shape.k === "sum") {
17696
- const parts = [];
17697
- const as = a.shape.k === "sum" ? a.shape.members : [a];
17698
- const bs = b.shape.k === "sum" ? b.shape.members : [b];
17699
- for (const x of as) {
17700
- for (const y of bs) {
17701
- const r = add(x, y, phi2);
17702
- if (r.shape.k === "never") continue;
17703
- if (r.shape.k === "sum") parts.push(...r.shape.members);
17704
- else parts.push(r);
17705
- }
17706
- }
17707
- const uniq = dedupAbsMembers(parts);
17708
- const term = a.term && b.term ? simplifyTerm(app("+", [a.term, b.term])) : void 0;
17709
- if (uniq.length === 0) {
17710
- return abs({ k: "unknown" }, term, void 0, "partial");
17711
- }
17712
- if (uniq.length === 1) {
17713
- return abs(uniq[0].shape, term, uniq[0].pred, confJoin(a.conf, b.conf));
17714
- }
17715
- return abs({ k: "sum", members: uniq }, term, void 0, "partial");
17716
- }
17718
+ const distributed = distributeSumBinOp("+", a, b, phi2, add);
17719
+ if (distributed) return distributed;
17717
17720
  if (isNumPrim(a) || isNumPrim(b) || isBigPrim(a) || isBigPrim(b) || a.shape.k === "unknown" || b.shape.k === "unknown" || a.shape.k === "obj" || b.shape.k === "obj" || a.shape.k === "fn" || b.shape.k === "fn" || a.shape.k === "brand" || b.shape.k === "brand") {
17718
17721
  return abs({ k: "sum", members: [num(), str()] }, void 0, void 0, "partial");
17719
17722
  }
@@ -17830,6 +17833,35 @@ function addPred(a, b, sumTerm, phi2) {
17830
17833
  return and(...facts);
17831
17834
  }
17832
17835
  function numericBounds(a, phi2 = pTrue) {
17836
+ if (a.shape.k === "sum") {
17837
+ const members = a.shape.members;
17838
+ let lo;
17839
+ let hi;
17840
+ let loOpen = false;
17841
+ let hiOpen = false;
17842
+ for (const m of members) {
17843
+ const mb = numericBounds(m, phi2);
17844
+ if (!mb) return void 0;
17845
+ if (mb.lo === void 0) {
17846
+ loOpen = true;
17847
+ } else if (!loOpen) {
17848
+ if (lo === void 0 || mb.lo.value < lo.value || mb.lo.value === lo.value && !mb.lo.strict && lo.strict) {
17849
+ lo = mb.lo;
17850
+ }
17851
+ }
17852
+ if (mb.hi === void 0) {
17853
+ hiOpen = true;
17854
+ } else if (!hiOpen) {
17855
+ if (hi === void 0 || mb.hi.value > hi.value || mb.hi.value === hi.value && !mb.hi.strict && hi.strict) {
17856
+ hi = mb.hi;
17857
+ }
17858
+ }
17859
+ }
17860
+ if (loOpen) lo = void 0;
17861
+ if (hiOpen) hi = void 0;
17862
+ if (lo === void 0 && hi === void 0) return void 0;
17863
+ return { lo, hi };
17864
+ }
17833
17865
  if (!isNumPrim(a)) return void 0;
17834
17866
  const result = {};
17835
17867
  if (a.pred && a.term) {
@@ -17936,6 +17968,8 @@ function sub(a, b, phi2 = pTrue) {
17936
17968
  if (isAnyLike(a) || isAnyLike(b)) {
17937
17969
  return toNumberResult(a, b, "-");
17938
17970
  }
17971
+ const distributed = distributeSumBinOp("-", a, b, phi2, sub);
17972
+ if (distributed) return distributed;
17939
17973
  return abs({ k: "unknown" }, void 0, void 0, "partial");
17940
17974
  }
17941
17975
  function mul(a, b, phi2 = pTrue) {
@@ -18008,6 +18042,8 @@ function mul(a, b, phi2 = pTrue) {
18008
18042
  if (isAnyLike(a) || isAnyLike(b)) {
18009
18043
  return toNumberResult(a, b, "*");
18010
18044
  }
18045
+ const distributed = distributeSumBinOp("*", a, b, phi2, mul);
18046
+ if (distributed) return distributed;
18011
18047
  return abs({ k: "unknown" }, void 0, void 0, "partial");
18012
18048
  }
18013
18049
  function div(a, b, phi2 = pTrue) {
@@ -18068,6 +18104,8 @@ function div(a, b, phi2 = pTrue) {
18068
18104
  if (isAnyLike(a) || isAnyLike(b)) {
18069
18105
  return toNumberResult(a, b, "/");
18070
18106
  }
18107
+ const distributed = distributeSumBinOp("/", a, b, phi2, div);
18108
+ if (distributed) return distributed;
18071
18109
  return abs({ k: "unknown" }, void 0, void 0, "partial");
18072
18110
  }
18073
18111
  function mod(a, b, phi2 = pTrue) {
@@ -18118,6 +18156,8 @@ function mod(a, b, phi2 = pTrue) {
18118
18156
  if (isAnyLike(a) || isAnyLike(b)) {
18119
18157
  return toNumberResult(a, b, "%");
18120
18158
  }
18159
+ const distributed = distributeSumBinOp("%", a, b, phi2, mod);
18160
+ if (distributed) return distributed;
18121
18161
  return abs({ k: "unknown" }, void 0, void 0, "partial");
18122
18162
  }
18123
18163
  function cmp(op, a, b, phi2 = pTrue) {
@@ -18722,6 +18762,11 @@ function isNonNaN(a, phi2) {
18722
18762
  if (a.term?.op === "lit") {
18723
18763
  return typeof a.term.value === "number" && !Number.isNaN(a.term.value);
18724
18764
  }
18765
+ if (a.shape.k === "sum") {
18766
+ return a.shape.members.every(
18767
+ (m) => isNonNaN(m, phi2)
18768
+ );
18769
+ }
18725
18770
  const hasNonNaNFact = (p) => {
18726
18771
  if (!p) return false;
18727
18772
  if (p.op === "and") return p.args.some(hasNonNaNFact);
@@ -18843,7 +18888,9 @@ function roundingAbs(name, arg) {
18843
18888
  const impl = name === "round" ? Math.round : name === "floor" ? Math.floor : name === "ceil" ? Math.ceil : Math.trunc;
18844
18889
  const b = numericBounds(arg, phi2);
18845
18890
  const mayNaN = !isNonNaN(arg, phi2);
18846
- const term = arg.term ? simplifyTerm(app(`Math.${name}`, [arg.term])) : void 0;
18891
+ const term = simplifyTerm(
18892
+ app(`Math.${name}`, [arg.term ?? app("arg0", [])])
18893
+ );
18847
18894
  let out = {};
18848
18895
  if (b) {
18849
18896
  if (b.lo !== void 0) {
@@ -18855,7 +18902,7 @@ function roundingAbs(name, arg) {
18855
18902
  out.hi = Number.isNaN(v2) ? void 0 : { value: v2, strict: false };
18856
18903
  }
18857
18904
  }
18858
- const pred = term ? boundsToPred(term, out) : void 0;
18905
+ const pred = boundsToPred(term, out);
18859
18906
  const bounded = abs({ k: "prim", type: "number" }, term, pred, "path");
18860
18907
  if (!mayNaN) return bounded;
18861
18908
  return makeSum(numLit(NaN), bounded);
@@ -37924,7 +37971,7 @@ function scanLiteralCalls(source, knownFns, phi2, opts) {
37924
37971
  if (absArg.shape.k === "unknown" && !absArg.term) return;
37925
37972
  const slots = absArg.shape.k === "obj" ? absArg.shape.slots : void 0;
37926
37973
  if (!slots) {
37927
- if (absArg.shape.k !== "unknown" && absArg.shape.k !== "never") {
37974
+ if (absArg.shape.k !== "unknown" && absArg.shape.k !== "never" && absArg.shape.k !== "any") {
37928
37975
  out.push({
37929
37976
  severity: "error",
37930
37977
  code: "nudo:constraint-violated",
@@ -279237,12 +279284,38 @@ function collectParamBodyReadTypes(source) {
279237
279284
  const visitFn = (fnName, fnNode, paramNames) => {
279238
279285
  if (paramNames.size === 0) return;
279239
279286
  const byParam = /* @__PURE__ */ new Map();
279240
- const add2 = (pname, field, hint) => {
279241
- if (!byParam.has(pname)) byParam.set(pname, /* @__PURE__ */ new Map());
279242
- const fields = byParam.get(pname);
279243
- if (!fields.has(field)) fields.set(field, []);
279244
- if (hint) fields.get(field).push(hint);
279287
+ const add2 = (pname, path, hint) => {
279288
+ if (path.length === 0) return;
279289
+ let m = byParam.get(pname);
279290
+ if (!m) byParam.set(pname, m = /* @__PURE__ */ new Map());
279291
+ const k = JSON.stringify(path);
279292
+ let e = m.get(k);
279293
+ if (!e) m.set(k, e = { path, hints: [] });
279294
+ if (hint) e.hints.push(hint);
279245
279295
  };
279296
+ const downChainPath = (mem) => {
279297
+ const keys = [];
279298
+ let cur = mem;
279299
+ let param;
279300
+ while (cur) {
279301
+ if (cur.type !== "MemberExpression" && cur.type !== "OptionalMemberExpression" || cur.computed === true) {
279302
+ return void 0;
279303
+ }
279304
+ const key = keyOf(cur.property);
279305
+ if (key === void 0) return void 0;
279306
+ keys.unshift(key);
279307
+ const obj2 = cur.object;
279308
+ if (!obj2) return void 0;
279309
+ if (obj2.type === "Identifier") {
279310
+ param = obj2.name;
279311
+ break;
279312
+ }
279313
+ cur = obj2;
279314
+ }
279315
+ if (param === void 0) return void 0;
279316
+ return { path: keys, param };
279317
+ };
279318
+ const synthMemberParent = (prop) => ({ type: "MemberExpression", property: { type: "Identifier", name: prop } });
279246
279319
  const walk = (node, shadowed, parent, parentKey) => {
279247
279320
  if (!node || typeof node !== "object") return;
279248
279321
  const n = node;
@@ -279254,29 +279327,31 @@ function collectParamBodyReadTypes(source) {
279254
279327
  const arg = n.argument;
279255
279328
  const mem = arg;
279256
279329
  if (mem && (mem.type === "MemberExpression" || mem.type === "OptionalMemberExpression")) {
279257
- const obj2 = mem.object;
279258
- const prop = mem.property;
279259
- if (obj2?.type === "Identifier" && paramNames.has(obj2.name) && !shadowed.has(obj2.name) && prop && mem.computed !== true) {
279260
- const key = keyOf(prop);
279261
- const pname = obj2.name;
279262
- if (key !== void 0) {
279263
- const gp = parent;
279264
- add2(pname, key, hintFromTypeof(n, gp));
279265
- }
279330
+ const chain = downChainPath(mem);
279331
+ if (chain && paramNames.has(chain.param) && !shadowed.has(chain.param)) {
279332
+ const gp = parent;
279333
+ add2(chain.param, chain.path, hintFromTypeof(n, gp));
279266
279334
  }
279267
279335
  }
279268
279336
  }
279269
279337
  if (n.type === "MemberExpression" || n.type === "OptionalMemberExpression") {
279270
- const obj2 = n.object;
279271
- const prop = n.property;
279272
- const computed = n.computed === true;
279273
- if (obj2?.type === "Identifier" && paramNames.has(obj2.name) && !shadowed.has(obj2.name) && prop && !computed) {
279274
- const key = keyOf(prop);
279275
- const pname = obj2.name;
279276
- if (key !== void 0) {
279277
- const hint = hintFromUse(parent, parentKey ?? "object");
279278
- add2(pname, key, hint);
279338
+ const innerLink = !!parent && (parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression") && parentKey === "object" && parent.computed !== true;
279339
+ if (innerLink) return;
279340
+ const chain = downChainPath(n);
279341
+ if (chain && paramNames.has(chain.param) && !shadowed.has(chain.param)) {
279342
+ const rev = chain.path;
279343
+ let leafLen = rev.length;
279344
+ let leafHint;
279345
+ for (let i = 0; i < rev.length; i++) {
279346
+ const nextProp = i + 1 < rev.length ? rev[i + 1] : void 0;
279347
+ const h = nextProp !== void 0 ? hintFromUse(synthMemberParent(nextProp), "object") : hintFromUse(parent, parentKey ?? "object");
279348
+ if (h) {
279349
+ leafLen = i + 1;
279350
+ leafHint = h;
279351
+ break;
279352
+ }
279279
279353
  }
279354
+ add2(chain.param, rev.slice(0, leafLen), leafHint);
279280
279355
  }
279281
279356
  }
279282
279357
  for (const k of Object.keys(n)) {
@@ -279292,13 +279367,20 @@ function collectParamBodyReadTypes(source) {
279292
279367
  walk(fnNode, /* @__PURE__ */ new Set());
279293
279368
  if (byParam.size === 0) return;
279294
279369
  const fieldsOf = /* @__PURE__ */ new Map();
279295
- for (const [pname, fieldMap] of byParam) {
279296
- const list = [];
279297
- for (const [field, hints] of fieldMap) {
279298
- list.push({ field, ...mergeHints(hints) });
279370
+ for (const [pname, pathMap] of byParam) {
279371
+ const root = { hints: [], children: /* @__PURE__ */ new Map() };
279372
+ for (const { path, hints } of pathMap.values()) {
279373
+ let node = root;
279374
+ for (const seg of path) {
279375
+ let child = node.children.get(seg);
279376
+ if (!child) {
279377
+ node.children.set(seg, child = { hints: [], children: /* @__PURE__ */ new Map() });
279378
+ }
279379
+ node = child;
279380
+ }
279381
+ node.hints.push(...hints);
279299
279382
  }
279300
- list.sort((a, b) => a.field < b.field ? -1 : 1);
279301
- fieldsOf.set(pname, list);
279383
+ fieldsOf.set(pname, emitFields(root));
279302
279384
  }
279303
279385
  out.set(fnName, fieldsOf);
279304
279386
  };
@@ -279377,6 +279459,24 @@ function collectParamBodyReadTypes(source) {
279377
279459
  }
279378
279460
  return out;
279379
279461
  }
279462
+ function emitFields(trie) {
279463
+ const list = [];
279464
+ for (const [name, child] of [...trie.children.entries()].sort(
279465
+ (a, b) => a[0] < b[0] ? -1 : 1
279466
+ )) {
279467
+ if (child.children.size > 0) {
279468
+ list.push({
279469
+ field: name,
279470
+ type: "shape({ \u2026 })",
279471
+ via: "dereferenced member read (nested shape)",
279472
+ fields: emitFields(child)
279473
+ });
279474
+ } else {
279475
+ list.push({ field: name, ...mergeHints(child.hints) });
279476
+ }
279477
+ }
279478
+ return list;
279479
+ }
279380
279480
  function mergeHints(hints) {
279381
279481
  if (hints.length === 0) return { type: "any()", via: "read (no type evidence)" };
279382
279482
  const types2 = [...new Set(hints.map((h) => h.type))];
@@ -279419,7 +279519,9 @@ function bodyReadFieldsFor(map, fnName, paramName2) {
279419
279519
  }
279420
279520
  function shapeDslFromFields(fields) {
279421
279521
  if (fields.length === 0) return "shape({})";
279422
- const parts = fields.map((f) => `${f.field}: ${f.type}`);
279522
+ const parts = fields.map(
279523
+ (f) => f.fields && f.fields.length > 0 ? `${f.field}: ${shapeDslFromFields(f.fields)}` : `${f.field}: ${f.type}`
279524
+ );
279423
279525
  return `shape({ ${parts.join(", ")} })`;
279424
279526
  }
279425
279527
  function collectParamBodyAccesses(source) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nudojs",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },
@@ -46,14 +46,14 @@
46
46
  "dependencies": {
47
47
  "@babel/generator": "^8.0.6",
48
48
  "@babel/types": "^8.0.6",
49
- "@nudojs/core": "1.6.0",
50
- "@nudojs/parser": "1.3.0",
51
- "@nudojs/service": "1.5.0",
52
- "@nudojs/harvester": "0.3.0",
49
+ "@nudojs/core": "1.7.1",
50
+ "@nudojs/parser": "1.3.2",
51
+ "@nudojs/service": "1.6.1",
52
+ "@nudojs/harvester": "0.3.2",
53
53
  "commander": "^15.0.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/node": "^26.6.2"
56
+ "@types/node": "^26.6.3"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsup"