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.
- package/dist/index.js +157 -55
- 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
|
-
|
|
17696
|
-
|
|
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 =
|
|
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 =
|
|
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,
|
|
279241
|
-
if (
|
|
279242
|
-
|
|
279243
|
-
if (!
|
|
279244
|
-
|
|
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
|
|
279258
|
-
|
|
279259
|
-
|
|
279260
|
-
|
|
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
|
|
279271
|
-
|
|
279272
|
-
const
|
|
279273
|
-
if (
|
|
279274
|
-
const
|
|
279275
|
-
|
|
279276
|
-
|
|
279277
|
-
|
|
279278
|
-
|
|
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,
|
|
279296
|
-
const
|
|
279297
|
-
for (const
|
|
279298
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
50
|
-
"@nudojs/parser": "1.3.
|
|
51
|
-
"@nudojs/service": "1.
|
|
52
|
-
"@nudojs/harvester": "0.3.
|
|
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.
|
|
56
|
+
"@types/node": "^26.6.3"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup"
|