nudojs 1.0.0-beta.3 → 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.
- package/dist/index.js +69 -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
|
|
|
@@ -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
|
-
|
|
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.
|
|
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": "
|
|
50
|
-
"@nudojs/parser": "1.1.
|
|
51
|
-
"@nudojs/service": "
|
|
52
|
-
"@nudojs/harvester": "
|
|
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": {
|