nudojs 1.0.0 → 1.0.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 +69 -3
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -27989,6 +27989,9 @@ function exitCall() {
27989
27989
  _absCallDepth--;
27990
27990
  _activeCallKeys.pop();
27991
27991
  }
27992
+ function noteHostEffectBlocked(name) {
27993
+ noteAbsTruncation(`${HOST_EFFECT_LABEL_PREFIX}${name}`);
27994
+ }
27992
27995
  function noteBForkTruncation() {
27993
27996
  noteAbsTruncation(FORK_TRUNCATION_LABEL);
27994
27997
  }
@@ -28018,7 +28021,7 @@ function bumpBForkBudget() {
28018
28021
  }
28019
28022
  return false;
28020
28023
  }
28021
- var MAX_CALL_DEPTH, MAX_TOTAL_CALLS, _absCallDepth, _absTotalCalls, _activeCallKeys, _fnCallIds, _fnCallIdSeq, _callTruncated, absTruncCollector, MAX_B_TOTAL_FORKS, _bForkBudgetLimit, _bForkCount, _bForkTruncNoted, FORK_TRUNCATION_LABEL;
28024
+ var MAX_CALL_DEPTH, MAX_TOTAL_CALLS, _absCallDepth, _absTotalCalls, _activeCallKeys, _fnCallIds, _fnCallIdSeq, _callTruncated, absTruncCollector, MAX_B_TOTAL_FORKS, _bForkBudgetLimit, _bForkCount, _bForkTruncNoted, FORK_TRUNCATION_LABEL, HOST_EFFECT_LABEL_PREFIX;
28022
28025
  var init_call_budget = __esm({
28023
28026
  "../core/src/algebra/call-budget.ts"() {
28024
28027
  "use strict";
@@ -28038,6 +28041,7 @@ var init_call_budget = __esm({
28038
28041
  _bForkCount = 0;
28039
28042
  _bForkTruncNoted = false;
28040
28043
  FORK_TRUNCATION_LABEL = "#fork-budget";
28044
+ HOST_EFFECT_LABEL_PREFIX = "#host-effect:";
28041
28045
  }
28042
28046
  });
28043
28047
 
@@ -30247,6 +30251,14 @@ function setBBindingSink(sink) {
30247
30251
  function $recordBinding(name, value) {
30248
30252
  if (bindingSink) bindingSink.set(name, value);
30249
30253
  }
30254
+ function neverExecHostName(fn2) {
30255
+ if (typeof fn2 !== "function") return null;
30256
+ const g = globalThis;
30257
+ for (const n of NEVER_EXEC_HOST_FN_NAMES) {
30258
+ if (fn2 === g[n]) return n;
30259
+ }
30260
+ return null;
30261
+ }
30250
30262
  function noteBCallRecord(r) {
30251
30263
  if (!bCallCollector) return;
30252
30264
  try {
@@ -30321,8 +30333,12 @@ function $callNamed(name, fn2, args, loc, argLocs) {
30321
30333
  try {
30322
30334
  if (typeof fn2 === "function") {
30323
30335
  const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
30336
+ const blockedHost = g === void 0 ? neverExecHostName(fn2) : null;
30324
30337
  if (g !== void 0) {
30325
30338
  result = g;
30339
+ } else if (blockedHost !== null) {
30340
+ noteHostEffectBlocked(blockedHost);
30341
+ result = bTruncatedAbs();
30326
30342
  } else {
30327
30343
  const entered = bEnterCall(name, fn2, args);
30328
30344
  if (!entered.ok) {
@@ -30375,7 +30391,7 @@ function $callNamed(name, fn2, args, loc, argLocs) {
30375
30391
  }
30376
30392
  return result;
30377
30393
  }
30378
- var pureCallMemo, bCallCollector, bAssignCollector, bindingSink, GLOBAL_FNS, MAX_B_CALL_DEPTH, MAX_B_TOTAL_CALLS, bCallDepth, bTotalCalls, bActiveCallKeys, bFnCallIds, bFnCallIdSeq;
30394
+ var pureCallMemo, bCallCollector, bAssignCollector, bindingSink, GLOBAL_FNS, NEVER_EXEC_HOST_FN_NAMES, MAX_B_CALL_DEPTH, MAX_B_TOTAL_CALLS, bCallDepth, bTotalCalls, bActiveCallKeys, bFnCallIds, bFnCallIdSeq;
30379
30395
  var init_calls = __esm({
30380
30396
  "../core/src/algebra/exec/calls.ts"() {
30381
30397
  "use strict";
@@ -30403,6 +30419,18 @@ var init_calls = __esm({
30403
30419
  "eval",
30404
30420
  "Symbol"
30405
30421
  ]);
30422
+ NEVER_EXEC_HOST_FN_NAMES = [
30423
+ "fetch",
30424
+ "XMLHttpRequest",
30425
+ "WebSocket",
30426
+ "EventSource",
30427
+ "setTimeout",
30428
+ "setInterval",
30429
+ "setImmediate",
30430
+ "queueMicrotask",
30431
+ "requestAnimationFrame",
30432
+ "requestIdleCallback"
30433
+ ];
30406
30434
  MAX_B_CALL_DEPTH = 64;
30407
30435
  MAX_B_TOTAL_CALLS = 2e4;
30408
30436
  bCallDepth = 0;
@@ -30458,7 +30486,13 @@ function execRegexBrand(re, method, args) {
30458
30486
  if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
30459
30487
  const subject = args[0] ? litValue(args[0]) : void 0;
30460
30488
  if (typeof subject !== "string") {
30461
- return method === "test" ? abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial") : void 0;
30489
+ if (method === "test") {
30490
+ return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
30491
+ }
30492
+ const element = joinAbs(str(), undefAbs());
30493
+ const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
30494
+ const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
30495
+ return joinAbs(nullAbs, matchAbs);
30462
30496
  }
30463
30497
  try {
30464
30498
  return regexExecWithState(re, method, subject).result;
@@ -36245,6 +36279,17 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
36245
36279
  });
36246
36280
  continue;
36247
36281
  }
36282
+ if (label.startsWith(HOST_EFFECT_LABEL_PREFIX)) {
36283
+ const hostName = label.slice(HOST_EFFECT_LABEL_PREFIX.length);
36284
+ issues.push({
36285
+ severity: "info",
36286
+ code: "nudo:host-effect-blocked",
36287
+ message: `Host function '${hostName}' is not executed during analysis (network/timer side effect); result widened to unknown#opaque`,
36288
+ suggestion: "optional: mock it with @nudo:mock / @nudo:env, or pass the value in from a call site",
36289
+ fn: hostName
36290
+ });
36291
+ continue;
36292
+ }
36248
36293
  issues.push({
36249
36294
  severity: "info",
36250
36295
  code: "nudo:recursion-truncated",
@@ -36374,6 +36419,19 @@ function checkReturnConstraint(fnName, cName, constraint, ret) {
36374
36419
  const out = [];
36375
36420
  if (ret.shape.k === "unknown" && !ret.term) return out;
36376
36421
  if (ret.shape.k === "any") return out;
36422
+ if (isNullishLitAbs(ret)) return out;
36423
+ if (ret.shape.k === "sum" && constraint.fields) {
36424
+ const seen = /* @__PURE__ */ new Set();
36425
+ for (const m of ret.shape.members) {
36426
+ for (const issue of checkReturnConstraint(fnName, cName, constraint, m)) {
36427
+ const key = `${issue.message}\0${issue.actual ?? ""}\0${issue.expected ?? ""}`;
36428
+ if (seen.has(key)) continue;
36429
+ seen.add(key);
36430
+ out.push(issue);
36431
+ }
36432
+ }
36433
+ return out;
36434
+ }
36377
36435
  const push = (actual, expected, suggestion) => {
36378
36436
  out.push({
36379
36437
  severity: "error",
@@ -36475,6 +36533,7 @@ var init_check = __esm({
36475
36533
  "../core/src/algebra/check.ts"() {
36476
36534
  "use strict";
36477
36535
  init_parse_source();
36536
+ init_surface();
36478
36537
  init_call_budget();
36479
36538
  init_refine();
36480
36539
  init_interface();
@@ -36574,6 +36633,11 @@ function actionsForIssue(i) {
36574
36633
  label: "results widened to unknown \u2014 raise budget or narrow control flow"
36575
36634
  }
36576
36635
  ];
36636
+ case "nudo:host-effect-blocked":
36637
+ return [
36638
+ { kind: "mock", label: "pin the host API with @nudo:mock / @nudo:env" },
36639
+ { kind: "info", label: "results widened to unknown \u2014 or feed the value in from a call site" }
36640
+ ];
36577
36641
  case "nudo:interface-drift":
36578
36642
  return [
36579
36643
  {
@@ -46028,6 +46092,7 @@ __export(internal_exports, {
46028
46092
  $tryReleaseSoft: () => $tryReleaseSoft,
46029
46093
  ERROR_FAMILY: () => ERROR_FAMILY,
46030
46094
  FORK_TRUNCATION_LABEL: () => FORK_TRUNCATION_LABEL,
46095
+ HOST_EFFECT_LABEL_PREFIX: () => HOST_EFFECT_LABEL_PREFIX,
46031
46096
  MAX_B_TOTAL_FORKS: () => MAX_B_TOTAL_FORKS,
46032
46097
  MAX_CALL_DEPTH: () => MAX_CALL_DEPTH,
46033
46098
  MAX_TOTAL_CALLS: () => MAX_TOTAL_CALLS,
@@ -46105,6 +46170,7 @@ __export(internal_exports, {
46105
46170
  noteBForkTruncation: () => noteBForkTruncation,
46106
46171
  noteDerivationAdd: () => noteDerivationAdd,
46107
46172
  noteDerivationJoin: () => noteDerivationJoin,
46173
+ noteHostEffectBlocked: () => noteHostEffectBlocked,
46108
46174
  noteMemberDispatchMiss: () => noteMemberDispatchMiss,
46109
46175
  noteNullishMemberThrows: () => noteNullishMemberThrows,
46110
46176
  noteObjSlotMissing: () => noteObjSlotMissing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nudojs",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },
@@ -46,10 +46,10 @@
46
46
  "dependencies": {
47
47
  "@babel/generator": "^8.0.6",
48
48
  "@babel/types": "^8.0.6",
49
- "@nudojs/core": "1.1.0",
50
- "@nudojs/parser": "1.1.0",
51
- "@nudojs/service": "1.1.0",
52
- "@nudojs/harvester": "0.2.8",
49
+ "@nudojs/core": "1.1.1",
50
+ "@nudojs/parser": "1.1.1",
51
+ "@nudojs/service": "1.1.1",
52
+ "@nudojs/harvester": "0.2.9",
53
53
  "commander": "^15.0.0"
54
54
  },
55
55
  "devDependencies": {