nudojs 1.0.0 → 1.0.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 +78 -3
- 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
|
|
|
@@ -30215,7 +30219,9 @@ __export(calls_exports, {
|
|
|
30215
30219
|
$recordBinding: () => $recordBinding,
|
|
30216
30220
|
MAX_B_CALL_DEPTH: () => MAX_B_CALL_DEPTH,
|
|
30217
30221
|
MAX_B_TOTAL_CALLS: () => MAX_B_TOTAL_CALLS,
|
|
30222
|
+
blockHostSideEffect: () => blockHostSideEffect,
|
|
30218
30223
|
getBCallCollector: () => getBCallCollector,
|
|
30224
|
+
neverExecHostName: () => neverExecHostName,
|
|
30219
30225
|
noteBCallRecord: () => noteBCallRecord,
|
|
30220
30226
|
resetBCallBudget: () => resetBCallBudget,
|
|
30221
30227
|
setBAssignCollector: () => setBAssignCollector,
|
|
@@ -30247,6 +30253,20 @@ function setBBindingSink(sink) {
|
|
|
30247
30253
|
function $recordBinding(name, value) {
|
|
30248
30254
|
if (bindingSink) bindingSink.set(name, value);
|
|
30249
30255
|
}
|
|
30256
|
+
function neverExecHostName(fn2) {
|
|
30257
|
+
if (typeof fn2 !== "function") return null;
|
|
30258
|
+
const g = globalThis;
|
|
30259
|
+
for (const n of NEVER_EXEC_HOST_FN_NAMES) {
|
|
30260
|
+
if (fn2 === g[n]) return n;
|
|
30261
|
+
}
|
|
30262
|
+
return null;
|
|
30263
|
+
}
|
|
30264
|
+
function blockHostSideEffect(fn2) {
|
|
30265
|
+
const hostName = neverExecHostName(fn2);
|
|
30266
|
+
if (hostName === null) return null;
|
|
30267
|
+
noteHostEffectBlocked(hostName);
|
|
30268
|
+
return abs({ k: "unknown" }, void 0, void 0, "opaque");
|
|
30269
|
+
}
|
|
30250
30270
|
function noteBCallRecord(r) {
|
|
30251
30271
|
if (!bCallCollector) return;
|
|
30252
30272
|
try {
|
|
@@ -30321,8 +30341,11 @@ function $callNamed(name, fn2, args, loc, argLocs) {
|
|
|
30321
30341
|
try {
|
|
30322
30342
|
if (typeof fn2 === "function") {
|
|
30323
30343
|
const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
|
|
30344
|
+
const blockedHost = g === void 0 ? blockHostSideEffect(fn2) : null;
|
|
30324
30345
|
if (g !== void 0) {
|
|
30325
30346
|
result = g;
|
|
30347
|
+
} else if (blockedHost !== null) {
|
|
30348
|
+
result = blockedHost;
|
|
30326
30349
|
} else {
|
|
30327
30350
|
const entered = bEnterCall(name, fn2, args);
|
|
30328
30351
|
if (!entered.ok) {
|
|
@@ -30375,7 +30398,7 @@ function $callNamed(name, fn2, args, loc, argLocs) {
|
|
|
30375
30398
|
}
|
|
30376
30399
|
return result;
|
|
30377
30400
|
}
|
|
30378
|
-
var pureCallMemo, bCallCollector, bAssignCollector, bindingSink, GLOBAL_FNS, MAX_B_CALL_DEPTH, MAX_B_TOTAL_CALLS, bCallDepth, bTotalCalls, bActiveCallKeys, bFnCallIds, bFnCallIdSeq;
|
|
30401
|
+
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
30402
|
var init_calls = __esm({
|
|
30380
30403
|
"../core/src/algebra/exec/calls.ts"() {
|
|
30381
30404
|
"use strict";
|
|
@@ -30403,6 +30426,18 @@ var init_calls = __esm({
|
|
|
30403
30426
|
"eval",
|
|
30404
30427
|
"Symbol"
|
|
30405
30428
|
]);
|
|
30429
|
+
NEVER_EXEC_HOST_FN_NAMES = [
|
|
30430
|
+
"fetch",
|
|
30431
|
+
"XMLHttpRequest",
|
|
30432
|
+
"WebSocket",
|
|
30433
|
+
"EventSource",
|
|
30434
|
+
"setTimeout",
|
|
30435
|
+
"setInterval",
|
|
30436
|
+
"setImmediate",
|
|
30437
|
+
"queueMicrotask",
|
|
30438
|
+
"requestAnimationFrame",
|
|
30439
|
+
"requestIdleCallback"
|
|
30440
|
+
];
|
|
30406
30441
|
MAX_B_CALL_DEPTH = 64;
|
|
30407
30442
|
MAX_B_TOTAL_CALLS = 2e4;
|
|
30408
30443
|
bCallDepth = 0;
|
|
@@ -30458,7 +30493,13 @@ function execRegexBrand(re, method, args) {
|
|
|
30458
30493
|
if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
|
|
30459
30494
|
const subject = args[0] ? litValue(args[0]) : void 0;
|
|
30460
30495
|
if (typeof subject !== "string") {
|
|
30461
|
-
|
|
30496
|
+
if (method === "test") {
|
|
30497
|
+
return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
|
|
30498
|
+
}
|
|
30499
|
+
const element = joinAbs(str(), undefAbs());
|
|
30500
|
+
const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
|
|
30501
|
+
const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
|
|
30502
|
+
return joinAbs(nullAbs, matchAbs);
|
|
30462
30503
|
}
|
|
30463
30504
|
try {
|
|
30464
30505
|
return regexExecWithState(re, method, subject).result;
|
|
@@ -30657,6 +30698,8 @@ function $new(cls, args) {
|
|
|
30657
30698
|
}
|
|
30658
30699
|
}
|
|
30659
30700
|
if (typeof cls === "function") {
|
|
30701
|
+
const blockedHost = blockHostSideEffect(cls);
|
|
30702
|
+
if (blockedHost) return blockedHost;
|
|
30660
30703
|
if (cls === Array) {
|
|
30661
30704
|
return makeArrayCtorAbs(args);
|
|
30662
30705
|
}
|
|
@@ -36245,6 +36288,17 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
36245
36288
|
});
|
|
36246
36289
|
continue;
|
|
36247
36290
|
}
|
|
36291
|
+
if (label.startsWith(HOST_EFFECT_LABEL_PREFIX)) {
|
|
36292
|
+
const hostName = label.slice(HOST_EFFECT_LABEL_PREFIX.length);
|
|
36293
|
+
issues.push({
|
|
36294
|
+
severity: "info",
|
|
36295
|
+
code: "nudo:host-effect-blocked",
|
|
36296
|
+
message: `Host function '${hostName}' is not executed during analysis (network/timer side effect); result widened to unknown#opaque`,
|
|
36297
|
+
suggestion: "optional: mock it with @nudo:mock / @nudo:env, or pass the value in from a call site",
|
|
36298
|
+
fn: hostName
|
|
36299
|
+
});
|
|
36300
|
+
continue;
|
|
36301
|
+
}
|
|
36248
36302
|
issues.push({
|
|
36249
36303
|
severity: "info",
|
|
36250
36304
|
code: "nudo:recursion-truncated",
|
|
@@ -36374,6 +36428,19 @@ function checkReturnConstraint(fnName, cName, constraint, ret) {
|
|
|
36374
36428
|
const out = [];
|
|
36375
36429
|
if (ret.shape.k === "unknown" && !ret.term) return out;
|
|
36376
36430
|
if (ret.shape.k === "any") return out;
|
|
36431
|
+
if (isNullishLitAbs(ret)) return out;
|
|
36432
|
+
if (ret.shape.k === "sum" && constraint.fields) {
|
|
36433
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36434
|
+
for (const m of ret.shape.members) {
|
|
36435
|
+
for (const issue of checkReturnConstraint(fnName, cName, constraint, m)) {
|
|
36436
|
+
const key = `${issue.message}\0${issue.actual ?? ""}\0${issue.expected ?? ""}`;
|
|
36437
|
+
if (seen.has(key)) continue;
|
|
36438
|
+
seen.add(key);
|
|
36439
|
+
out.push(issue);
|
|
36440
|
+
}
|
|
36441
|
+
}
|
|
36442
|
+
return out;
|
|
36443
|
+
}
|
|
36377
36444
|
const push = (actual, expected, suggestion) => {
|
|
36378
36445
|
out.push({
|
|
36379
36446
|
severity: "error",
|
|
@@ -36475,6 +36542,7 @@ var init_check = __esm({
|
|
|
36475
36542
|
"../core/src/algebra/check.ts"() {
|
|
36476
36543
|
"use strict";
|
|
36477
36544
|
init_parse_source();
|
|
36545
|
+
init_surface();
|
|
36478
36546
|
init_call_budget();
|
|
36479
36547
|
init_refine();
|
|
36480
36548
|
init_interface();
|
|
@@ -36574,6 +36642,11 @@ function actionsForIssue(i) {
|
|
|
36574
36642
|
label: "results widened to unknown \u2014 raise budget or narrow control flow"
|
|
36575
36643
|
}
|
|
36576
36644
|
];
|
|
36645
|
+
case "nudo:host-effect-blocked":
|
|
36646
|
+
return [
|
|
36647
|
+
{ kind: "mock", label: "pin the host API with @nudo:mock / @nudo:env" },
|
|
36648
|
+
{ kind: "info", label: "results widened to unknown \u2014 or feed the value in from a call site" }
|
|
36649
|
+
];
|
|
36577
36650
|
case "nudo:interface-drift":
|
|
36578
36651
|
return [
|
|
36579
36652
|
{
|
|
@@ -46028,6 +46101,7 @@ __export(internal_exports, {
|
|
|
46028
46101
|
$tryReleaseSoft: () => $tryReleaseSoft,
|
|
46029
46102
|
ERROR_FAMILY: () => ERROR_FAMILY,
|
|
46030
46103
|
FORK_TRUNCATION_LABEL: () => FORK_TRUNCATION_LABEL,
|
|
46104
|
+
HOST_EFFECT_LABEL_PREFIX: () => HOST_EFFECT_LABEL_PREFIX,
|
|
46031
46105
|
MAX_B_TOTAL_FORKS: () => MAX_B_TOTAL_FORKS,
|
|
46032
46106
|
MAX_CALL_DEPTH: () => MAX_CALL_DEPTH,
|
|
46033
46107
|
MAX_TOTAL_CALLS: () => MAX_TOTAL_CALLS,
|
|
@@ -46105,6 +46179,7 @@ __export(internal_exports, {
|
|
|
46105
46179
|
noteBForkTruncation: () => noteBForkTruncation,
|
|
46106
46180
|
noteDerivationAdd: () => noteDerivationAdd,
|
|
46107
46181
|
noteDerivationJoin: () => noteDerivationJoin,
|
|
46182
|
+
noteHostEffectBlocked: () => noteHostEffectBlocked,
|
|
46108
46183
|
noteMemberDispatchMiss: () => noteMemberDispatchMiss,
|
|
46109
46184
|
noteNullishMemberThrows: () => noteNullishMemberThrows,
|
|
46110
46185
|
noteObjSlotMissing: () => noteObjSlotMissing,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nudojs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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.
|
|
50
|
-
"@nudojs/parser": "1.1.
|
|
51
|
-
"@nudojs/service": "1.1.
|
|
52
|
-
"@nudojs/harvester": "0.2.
|
|
49
|
+
"@nudojs/core": "1.1.2",
|
|
50
|
+
"@nudojs/parser": "1.1.2",
|
|
51
|
+
"@nudojs/service": "1.1.2",
|
|
52
|
+
"@nudojs/harvester": "0.2.10",
|
|
53
53
|
"commander": "^15.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|