nudojs 1.3.0 → 1.3.1

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 +86 -31
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -37924,7 +37924,7 @@ function scanLiteralCalls(source, knownFns, phi2, opts) {
37924
37924
  if (absArg.shape.k === "unknown" && !absArg.term) return;
37925
37925
  const slots = absArg.shape.k === "obj" ? absArg.shape.slots : void 0;
37926
37926
  if (!slots) {
37927
- if (absArg.shape.k !== "unknown" && absArg.shape.k !== "never") {
37927
+ if (absArg.shape.k !== "unknown" && absArg.shape.k !== "never" && absArg.shape.k !== "any") {
37928
37928
  out.push({
37929
37929
  severity: "error",
37930
37930
  code: "nudo:constraint-violated",
@@ -279237,12 +279237,38 @@ function collectParamBodyReadTypes(source) {
279237
279237
  const visitFn = (fnName, fnNode, paramNames) => {
279238
279238
  if (paramNames.size === 0) return;
279239
279239
  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);
279240
+ const add2 = (pname, path, hint) => {
279241
+ if (path.length === 0) return;
279242
+ let m = byParam.get(pname);
279243
+ if (!m) byParam.set(pname, m = /* @__PURE__ */ new Map());
279244
+ const k = JSON.stringify(path);
279245
+ let e = m.get(k);
279246
+ if (!e) m.set(k, e = { path, hints: [] });
279247
+ if (hint) e.hints.push(hint);
279245
279248
  };
279249
+ const downChainPath = (mem) => {
279250
+ const keys = [];
279251
+ let cur = mem;
279252
+ let param;
279253
+ while (cur) {
279254
+ if (cur.type !== "MemberExpression" && cur.type !== "OptionalMemberExpression" || cur.computed === true) {
279255
+ return void 0;
279256
+ }
279257
+ const key = keyOf(cur.property);
279258
+ if (key === void 0) return void 0;
279259
+ keys.unshift(key);
279260
+ const obj2 = cur.object;
279261
+ if (!obj2) return void 0;
279262
+ if (obj2.type === "Identifier") {
279263
+ param = obj2.name;
279264
+ break;
279265
+ }
279266
+ cur = obj2;
279267
+ }
279268
+ if (param === void 0) return void 0;
279269
+ return { path: keys, param };
279270
+ };
279271
+ const synthMemberParent = (prop) => ({ type: "MemberExpression", property: { type: "Identifier", name: prop } });
279246
279272
  const walk = (node, shadowed, parent, parentKey) => {
279247
279273
  if (!node || typeof node !== "object") return;
279248
279274
  const n = node;
@@ -279254,29 +279280,31 @@ function collectParamBodyReadTypes(source) {
279254
279280
  const arg = n.argument;
279255
279281
  const mem = arg;
279256
279282
  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
- }
279283
+ const chain = downChainPath(mem);
279284
+ if (chain && paramNames.has(chain.param) && !shadowed.has(chain.param)) {
279285
+ const gp = parent;
279286
+ add2(chain.param, chain.path, hintFromTypeof(n, gp));
279266
279287
  }
279267
279288
  }
279268
279289
  }
279269
279290
  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);
279291
+ const innerLink = !!parent && (parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression") && parentKey === "object" && parent.computed !== true;
279292
+ if (innerLink) return;
279293
+ const chain = downChainPath(n);
279294
+ if (chain && paramNames.has(chain.param) && !shadowed.has(chain.param)) {
279295
+ const rev = chain.path;
279296
+ let leafLen = rev.length;
279297
+ let leafHint;
279298
+ for (let i = 0; i < rev.length; i++) {
279299
+ const nextProp = i + 1 < rev.length ? rev[i + 1] : void 0;
279300
+ const h = nextProp !== void 0 ? hintFromUse(synthMemberParent(nextProp), "object") : hintFromUse(parent, parentKey ?? "object");
279301
+ if (h) {
279302
+ leafLen = i + 1;
279303
+ leafHint = h;
279304
+ break;
279305
+ }
279279
279306
  }
279307
+ add2(chain.param, rev.slice(0, leafLen), leafHint);
279280
279308
  }
279281
279309
  }
279282
279310
  for (const k of Object.keys(n)) {
@@ -279292,13 +279320,20 @@ function collectParamBodyReadTypes(source) {
279292
279320
  walk(fnNode, /* @__PURE__ */ new Set());
279293
279321
  if (byParam.size === 0) return;
279294
279322
  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) });
279323
+ for (const [pname, pathMap] of byParam) {
279324
+ const root = { hints: [], children: /* @__PURE__ */ new Map() };
279325
+ for (const { path, hints } of pathMap.values()) {
279326
+ let node = root;
279327
+ for (const seg of path) {
279328
+ let child = node.children.get(seg);
279329
+ if (!child) {
279330
+ node.children.set(seg, child = { hints: [], children: /* @__PURE__ */ new Map() });
279331
+ }
279332
+ node = child;
279333
+ }
279334
+ node.hints.push(...hints);
279299
279335
  }
279300
- list.sort((a, b) => a.field < b.field ? -1 : 1);
279301
- fieldsOf.set(pname, list);
279336
+ fieldsOf.set(pname, emitFields(root));
279302
279337
  }
279303
279338
  out.set(fnName, fieldsOf);
279304
279339
  };
@@ -279377,6 +279412,24 @@ function collectParamBodyReadTypes(source) {
279377
279412
  }
279378
279413
  return out;
279379
279414
  }
279415
+ function emitFields(trie) {
279416
+ const list = [];
279417
+ for (const [name, child] of [...trie.children.entries()].sort(
279418
+ (a, b) => a[0] < b[0] ? -1 : 1
279419
+ )) {
279420
+ if (child.children.size > 0) {
279421
+ list.push({
279422
+ field: name,
279423
+ type: "shape({ \u2026 })",
279424
+ via: "dereferenced member read (nested shape)",
279425
+ fields: emitFields(child)
279426
+ });
279427
+ } else {
279428
+ list.push({ field: name, ...mergeHints(child.hints) });
279429
+ }
279430
+ }
279431
+ return list;
279432
+ }
279380
279433
  function mergeHints(hints) {
279381
279434
  if (hints.length === 0) return { type: "any()", via: "read (no type evidence)" };
279382
279435
  const types2 = [...new Set(hints.map((h) => h.type))];
@@ -279419,7 +279472,9 @@ function bodyReadFieldsFor(map, fnName, paramName2) {
279419
279472
  }
279420
279473
  function shapeDslFromFields(fields) {
279421
279474
  if (fields.length === 0) return "shape({})";
279422
- const parts = fields.map((f) => `${f.field}: ${f.type}`);
279475
+ const parts = fields.map(
279476
+ (f) => f.fields && f.fields.length > 0 ? `${f.field}: ${shapeDslFromFields(f.fields)}` : `${f.field}: ${f.type}`
279477
+ );
279423
279478
  return `shape({ ${parts.join(", ")} })`;
279424
279479
  }
279425
279480
  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.1",
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.0",
50
+ "@nudojs/parser": "1.3.1",
51
+ "@nudojs/service": "1.6.0",
52
+ "@nudojs/harvester": "0.3.1",
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"