@nudojs/lsp 1.1.0 → 1.1.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 +35 -1
- package/dist/server.js +64 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -240131,6 +240131,10 @@ var _bForkBudgetLimit = MAX_B_TOTAL_FORKS;
|
|
|
240131
240131
|
var _bForkCount = 0;
|
|
240132
240132
|
var _bForkTruncNoted = false;
|
|
240133
240133
|
var FORK_TRUNCATION_LABEL = "#fork-budget";
|
|
240134
|
+
var HOST_EFFECT_LABEL_PREFIX = "#host-effect:";
|
|
240135
|
+
function noteHostEffectBlocked(name) {
|
|
240136
|
+
noteAbsTruncation(`${HOST_EFFECT_LABEL_PREFIX}${name}`);
|
|
240137
|
+
}
|
|
240134
240138
|
function noteBForkTruncation() {
|
|
240135
240139
|
noteAbsTruncation(FORK_TRUNCATION_LABEL);
|
|
240136
240140
|
}
|
|
@@ -242176,6 +242180,26 @@ var GLOBAL_FNS = /* @__PURE__ */ new Set([
|
|
|
242176
242180
|
"eval",
|
|
242177
242181
|
"Symbol"
|
|
242178
242182
|
]);
|
|
242183
|
+
var NEVER_EXEC_HOST_FN_NAMES = [
|
|
242184
|
+
"fetch",
|
|
242185
|
+
"XMLHttpRequest",
|
|
242186
|
+
"WebSocket",
|
|
242187
|
+
"EventSource",
|
|
242188
|
+
"setTimeout",
|
|
242189
|
+
"setInterval",
|
|
242190
|
+
"setImmediate",
|
|
242191
|
+
"queueMicrotask",
|
|
242192
|
+
"requestAnimationFrame",
|
|
242193
|
+
"requestIdleCallback"
|
|
242194
|
+
];
|
|
242195
|
+
function neverExecHostName(fn2) {
|
|
242196
|
+
if (typeof fn2 !== "function") return null;
|
|
242197
|
+
const g = globalThis;
|
|
242198
|
+
for (const n of NEVER_EXEC_HOST_FN_NAMES) {
|
|
242199
|
+
if (fn2 === g[n]) return n;
|
|
242200
|
+
}
|
|
242201
|
+
return null;
|
|
242202
|
+
}
|
|
242179
242203
|
function noteBCallRecord(r) {
|
|
242180
242204
|
if (!bCallCollector) return;
|
|
242181
242205
|
try {
|
|
@@ -242257,8 +242281,12 @@ function $callNamed(name, fn2, args, loc, argLocs) {
|
|
|
242257
242281
|
try {
|
|
242258
242282
|
if (typeof fn2 === "function") {
|
|
242259
242283
|
const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
|
|
242284
|
+
const blockedHost = g === void 0 ? neverExecHostName(fn2) : null;
|
|
242260
242285
|
if (g !== void 0) {
|
|
242261
242286
|
result = g;
|
|
242287
|
+
} else if (blockedHost !== null) {
|
|
242288
|
+
noteHostEffectBlocked(blockedHost);
|
|
242289
|
+
result = bTruncatedAbs();
|
|
242262
242290
|
} else {
|
|
242263
242291
|
const entered = bEnterCall(name, fn2, args);
|
|
242264
242292
|
if (!entered.ok) {
|
|
@@ -242357,7 +242385,13 @@ function execRegexBrand(re, method, args) {
|
|
|
242357
242385
|
if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
|
|
242358
242386
|
const subject = args[0] ? litValue(args[0]) : void 0;
|
|
242359
242387
|
if (typeof subject !== "string") {
|
|
242360
|
-
|
|
242388
|
+
if (method === "test") {
|
|
242389
|
+
return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
|
|
242390
|
+
}
|
|
242391
|
+
const element = joinAbs(str(), undefAbs());
|
|
242392
|
+
const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
|
|
242393
|
+
const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
|
|
242394
|
+
return joinAbs(nullAbs, matchAbs);
|
|
242361
242395
|
}
|
|
242362
242396
|
try {
|
|
242363
242397
|
return regexExecWithState(re, method, subject).result;
|
package/dist/server.js
CHANGED
|
@@ -250531,6 +250531,10 @@ var _bForkBudgetLimit = MAX_B_TOTAL_FORKS;
|
|
|
250531
250531
|
var _bForkCount = 0;
|
|
250532
250532
|
var _bForkTruncNoted = false;
|
|
250533
250533
|
var FORK_TRUNCATION_LABEL = "#fork-budget";
|
|
250534
|
+
var HOST_EFFECT_LABEL_PREFIX = "#host-effect:";
|
|
250535
|
+
function noteHostEffectBlocked(name) {
|
|
250536
|
+
noteAbsTruncation(`${HOST_EFFECT_LABEL_PREFIX}${name}`);
|
|
250537
|
+
}
|
|
250534
250538
|
function noteBForkTruncation() {
|
|
250535
250539
|
noteAbsTruncation(FORK_TRUNCATION_LABEL);
|
|
250536
250540
|
}
|
|
@@ -252576,6 +252580,26 @@ var GLOBAL_FNS = /* @__PURE__ */ new Set([
|
|
|
252576
252580
|
"eval",
|
|
252577
252581
|
"Symbol"
|
|
252578
252582
|
]);
|
|
252583
|
+
var NEVER_EXEC_HOST_FN_NAMES = [
|
|
252584
|
+
"fetch",
|
|
252585
|
+
"XMLHttpRequest",
|
|
252586
|
+
"WebSocket",
|
|
252587
|
+
"EventSource",
|
|
252588
|
+
"setTimeout",
|
|
252589
|
+
"setInterval",
|
|
252590
|
+
"setImmediate",
|
|
252591
|
+
"queueMicrotask",
|
|
252592
|
+
"requestAnimationFrame",
|
|
252593
|
+
"requestIdleCallback"
|
|
252594
|
+
];
|
|
252595
|
+
function neverExecHostName(fn2) {
|
|
252596
|
+
if (typeof fn2 !== "function") return null;
|
|
252597
|
+
const g = globalThis;
|
|
252598
|
+
for (const n of NEVER_EXEC_HOST_FN_NAMES) {
|
|
252599
|
+
if (fn2 === g[n]) return n;
|
|
252600
|
+
}
|
|
252601
|
+
return null;
|
|
252602
|
+
}
|
|
252579
252603
|
function noteBCallRecord(r) {
|
|
252580
252604
|
if (!bCallCollector) return;
|
|
252581
252605
|
try {
|
|
@@ -252657,8 +252681,12 @@ function $callNamed(name, fn2, args, loc, argLocs) {
|
|
|
252657
252681
|
try {
|
|
252658
252682
|
if (typeof fn2 === "function") {
|
|
252659
252683
|
const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
|
|
252684
|
+
const blockedHost = g === void 0 ? neverExecHostName(fn2) : null;
|
|
252660
252685
|
if (g !== void 0) {
|
|
252661
252686
|
result = g;
|
|
252687
|
+
} else if (blockedHost !== null) {
|
|
252688
|
+
noteHostEffectBlocked(blockedHost);
|
|
252689
|
+
result = bTruncatedAbs();
|
|
252662
252690
|
} else {
|
|
252663
252691
|
const entered = bEnterCall(name, fn2, args);
|
|
252664
252692
|
if (!entered.ok) {
|
|
@@ -252757,7 +252785,13 @@ function execRegexBrand(re, method, args) {
|
|
|
252757
252785
|
if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
|
|
252758
252786
|
const subject = args[0] ? litValue(args[0]) : void 0;
|
|
252759
252787
|
if (typeof subject !== "string") {
|
|
252760
|
-
|
|
252788
|
+
if (method === "test") {
|
|
252789
|
+
return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
|
|
252790
|
+
}
|
|
252791
|
+
const element = joinAbs(str(), undefAbs());
|
|
252792
|
+
const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
|
|
252793
|
+
const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
|
|
252794
|
+
return joinAbs(nullAbs, matchAbs);
|
|
252761
252795
|
}
|
|
252762
252796
|
try {
|
|
252763
252797
|
return regexExecWithState(re, method, subject).result;
|
|
@@ -258057,6 +258091,17 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
258057
258091
|
});
|
|
258058
258092
|
continue;
|
|
258059
258093
|
}
|
|
258094
|
+
if (label.startsWith(HOST_EFFECT_LABEL_PREFIX)) {
|
|
258095
|
+
const hostName = label.slice(HOST_EFFECT_LABEL_PREFIX.length);
|
|
258096
|
+
issues.push({
|
|
258097
|
+
severity: "info",
|
|
258098
|
+
code: "nudo:host-effect-blocked",
|
|
258099
|
+
message: `Host function '${hostName}' is not executed during analysis (network/timer side effect); result widened to unknown#opaque`,
|
|
258100
|
+
suggestion: "optional: mock it with @nudo:mock / @nudo:env, or pass the value in from a call site",
|
|
258101
|
+
fn: hostName
|
|
258102
|
+
});
|
|
258103
|
+
continue;
|
|
258104
|
+
}
|
|
258060
258105
|
issues.push({
|
|
258061
258106
|
severity: "info",
|
|
258062
258107
|
code: "nudo:recursion-truncated",
|
|
@@ -258186,6 +258231,19 @@ function checkReturnConstraint(fnName, cName, constraint, ret) {
|
|
|
258186
258231
|
const out = [];
|
|
258187
258232
|
if (ret.shape.k === "unknown" && !ret.term) return out;
|
|
258188
258233
|
if (ret.shape.k === "any") return out;
|
|
258234
|
+
if (isNullishLitAbs(ret)) return out;
|
|
258235
|
+
if (ret.shape.k === "sum" && constraint.fields) {
|
|
258236
|
+
const seen = /* @__PURE__ */ new Set();
|
|
258237
|
+
for (const m of ret.shape.members) {
|
|
258238
|
+
for (const issue of checkReturnConstraint(fnName, cName, constraint, m)) {
|
|
258239
|
+
const key = `${issue.message}\0${issue.actual ?? ""}\0${issue.expected ?? ""}`;
|
|
258240
|
+
if (seen.has(key)) continue;
|
|
258241
|
+
seen.add(key);
|
|
258242
|
+
out.push(issue);
|
|
258243
|
+
}
|
|
258244
|
+
}
|
|
258245
|
+
return out;
|
|
258246
|
+
}
|
|
258189
258247
|
const push = (actual, expected, suggestion) => {
|
|
258190
258248
|
out.push({
|
|
258191
258249
|
severity: "error",
|
|
@@ -258354,6 +258412,11 @@ function actionsForIssue(i) {
|
|
|
258354
258412
|
label: "results widened to unknown \u2014 raise budget or narrow control flow"
|
|
258355
258413
|
}
|
|
258356
258414
|
];
|
|
258415
|
+
case "nudo:host-effect-blocked":
|
|
258416
|
+
return [
|
|
258417
|
+
{ kind: "mock", label: "pin the host API with @nudo:mock / @nudo:env" },
|
|
258418
|
+
{ kind: "info", label: "results widened to unknown \u2014 or feed the value in from a call site" }
|
|
258419
|
+
];
|
|
258357
258420
|
case "nudo:interface-drift":
|
|
258358
258421
|
return [
|
|
258359
258422
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/lsp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"registry": "https://registry.npmjs.org"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@nudojs/service": "1.1.
|
|
53
|
-
"@nudojs/core": "1.1.
|
|
54
|
-
"@nudojs/parser": "1.1.
|
|
52
|
+
"@nudojs/service": "1.1.1",
|
|
53
|
+
"@nudojs/core": "1.1.1",
|
|
54
|
+
"@nudojs/parser": "1.1.1",
|
|
55
55
|
"@babel/traverse": "^8.0.6",
|
|
56
56
|
"@babel/types": "^8.0.6",
|
|
57
57
|
"vscode-languageserver": "^10.1.2",
|