@nudojs/lsp 1.1.0 → 1.1.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 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
  }
@@ -242127,7 +242131,9 @@ __export(calls_exports, {
242127
242131
  $recordBinding: () => $recordBinding,
242128
242132
  MAX_B_CALL_DEPTH: () => MAX_B_CALL_DEPTH,
242129
242133
  MAX_B_TOTAL_CALLS: () => MAX_B_TOTAL_CALLS,
242134
+ blockHostSideEffect: () => blockHostSideEffect,
242130
242135
  getBCallCollector: () => getBCallCollector,
242136
+ neverExecHostName: () => neverExecHostName,
242131
242137
  noteBCallRecord: () => noteBCallRecord,
242132
242138
  resetBCallBudget: () => resetBCallBudget,
242133
242139
  setBAssignCollector: () => setBAssignCollector,
@@ -242176,6 +242182,32 @@ var GLOBAL_FNS = /* @__PURE__ */ new Set([
242176
242182
  "eval",
242177
242183
  "Symbol"
242178
242184
  ]);
242185
+ var NEVER_EXEC_HOST_FN_NAMES = [
242186
+ "fetch",
242187
+ "XMLHttpRequest",
242188
+ "WebSocket",
242189
+ "EventSource",
242190
+ "setTimeout",
242191
+ "setInterval",
242192
+ "setImmediate",
242193
+ "queueMicrotask",
242194
+ "requestAnimationFrame",
242195
+ "requestIdleCallback"
242196
+ ];
242197
+ function neverExecHostName(fn2) {
242198
+ if (typeof fn2 !== "function") return null;
242199
+ const g = globalThis;
242200
+ for (const n of NEVER_EXEC_HOST_FN_NAMES) {
242201
+ if (fn2 === g[n]) return n;
242202
+ }
242203
+ return null;
242204
+ }
242205
+ function blockHostSideEffect(fn2) {
242206
+ const hostName = neverExecHostName(fn2);
242207
+ if (hostName === null) return null;
242208
+ noteHostEffectBlocked(hostName);
242209
+ return abs({ k: "unknown" }, void 0, void 0, "opaque");
242210
+ }
242179
242211
  function noteBCallRecord(r) {
242180
242212
  if (!bCallCollector) return;
242181
242213
  try {
@@ -242257,8 +242289,11 @@ function $callNamed(name, fn2, args, loc, argLocs) {
242257
242289
  try {
242258
242290
  if (typeof fn2 === "function") {
242259
242291
  const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
242292
+ const blockedHost = g === void 0 ? blockHostSideEffect(fn2) : null;
242260
242293
  if (g !== void 0) {
242261
242294
  result = g;
242295
+ } else if (blockedHost !== null) {
242296
+ result = blockedHost;
242262
242297
  } else {
242263
242298
  const entered = bEnterCall(name, fn2, args);
242264
242299
  if (!entered.ok) {
@@ -242357,7 +242392,13 @@ function execRegexBrand(re, method, args) {
242357
242392
  if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
242358
242393
  const subject = args[0] ? litValue(args[0]) : void 0;
242359
242394
  if (typeof subject !== "string") {
242360
- return method === "test" ? abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial") : void 0;
242395
+ if (method === "test") {
242396
+ return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
242397
+ }
242398
+ const element = joinAbs(str(), undefAbs());
242399
+ const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
242400
+ const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
242401
+ return joinAbs(nullAbs, matchAbs);
242361
242402
  }
242362
242403
  try {
242363
242404
  return regexExecWithState(re, method, subject).result;
@@ -242523,6 +242564,8 @@ function $new(cls, args) {
242523
242564
  }
242524
242565
  }
242525
242566
  if (typeof cls === "function") {
242567
+ const blockedHost = blockHostSideEffect(cls);
242568
+ if (blockedHost) return blockedHost;
242526
242569
  if (cls === Array) {
242527
242570
  return makeArrayCtorAbs(args);
242528
242571
  }
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
  }
@@ -252527,7 +252531,9 @@ __export(calls_exports, {
252527
252531
  $recordBinding: () => $recordBinding,
252528
252532
  MAX_B_CALL_DEPTH: () => MAX_B_CALL_DEPTH,
252529
252533
  MAX_B_TOTAL_CALLS: () => MAX_B_TOTAL_CALLS,
252534
+ blockHostSideEffect: () => blockHostSideEffect,
252530
252535
  getBCallCollector: () => getBCallCollector,
252536
+ neverExecHostName: () => neverExecHostName,
252531
252537
  noteBCallRecord: () => noteBCallRecord,
252532
252538
  resetBCallBudget: () => resetBCallBudget,
252533
252539
  setBAssignCollector: () => setBAssignCollector,
@@ -252576,6 +252582,32 @@ var GLOBAL_FNS = /* @__PURE__ */ new Set([
252576
252582
  "eval",
252577
252583
  "Symbol"
252578
252584
  ]);
252585
+ var NEVER_EXEC_HOST_FN_NAMES = [
252586
+ "fetch",
252587
+ "XMLHttpRequest",
252588
+ "WebSocket",
252589
+ "EventSource",
252590
+ "setTimeout",
252591
+ "setInterval",
252592
+ "setImmediate",
252593
+ "queueMicrotask",
252594
+ "requestAnimationFrame",
252595
+ "requestIdleCallback"
252596
+ ];
252597
+ function neverExecHostName(fn2) {
252598
+ if (typeof fn2 !== "function") return null;
252599
+ const g = globalThis;
252600
+ for (const n of NEVER_EXEC_HOST_FN_NAMES) {
252601
+ if (fn2 === g[n]) return n;
252602
+ }
252603
+ return null;
252604
+ }
252605
+ function blockHostSideEffect(fn2) {
252606
+ const hostName = neverExecHostName(fn2);
252607
+ if (hostName === null) return null;
252608
+ noteHostEffectBlocked(hostName);
252609
+ return abs({ k: "unknown" }, void 0, void 0, "opaque");
252610
+ }
252579
252611
  function noteBCallRecord(r) {
252580
252612
  if (!bCallCollector) return;
252581
252613
  try {
@@ -252657,8 +252689,11 @@ function $callNamed(name, fn2, args, loc, argLocs) {
252657
252689
  try {
252658
252690
  if (typeof fn2 === "function") {
252659
252691
  const g = GLOBAL_FNS.has(name) && fn2 === globalThis[name] ? evalGlobalFn(name, args) : void 0;
252692
+ const blockedHost = g === void 0 ? blockHostSideEffect(fn2) : null;
252660
252693
  if (g !== void 0) {
252661
252694
  result = g;
252695
+ } else if (blockedHost !== null) {
252696
+ result = blockedHost;
252662
252697
  } else {
252663
252698
  const entered = bEnterCall(name, fn2, args);
252664
252699
  if (!entered.ok) {
@@ -252757,7 +252792,13 @@ function execRegexBrand(re, method, args) {
252757
252792
  if (method === "toString") return strLit(`/${parts.pat}/${parts.flags}`);
252758
252793
  const subject = args[0] ? litValue(args[0]) : void 0;
252759
252794
  if (typeof subject !== "string") {
252760
- return method === "test" ? abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial") : void 0;
252795
+ if (method === "test") {
252796
+ return abs({ k: "prim", type: "boolean" }, void 0, void 0, "partial");
252797
+ }
252798
+ const element = joinAbs(str(), undefAbs());
252799
+ const matchAbs = abs({ k: "arr", element }, void 0, void 0, "path");
252800
+ const nullAbs = abs({ k: "unknown" }, { op: "lit", value: null }, void 0, "exact");
252801
+ return joinAbs(nullAbs, matchAbs);
252761
252802
  }
252762
252803
  try {
252763
252804
  return regexExecWithState(re, method, subject).result;
@@ -252923,6 +252964,8 @@ function $new(cls, args) {
252923
252964
  }
252924
252965
  }
252925
252966
  if (typeof cls === "function") {
252967
+ const blockedHost = blockHostSideEffect(cls);
252968
+ if (blockedHost) return blockedHost;
252926
252969
  if (cls === Array) {
252927
252970
  return makeArrayCtorAbs(args);
252928
252971
  }
@@ -258057,6 +258100,17 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
258057
258100
  });
258058
258101
  continue;
258059
258102
  }
258103
+ if (label.startsWith(HOST_EFFECT_LABEL_PREFIX)) {
258104
+ const hostName = label.slice(HOST_EFFECT_LABEL_PREFIX.length);
258105
+ issues.push({
258106
+ severity: "info",
258107
+ code: "nudo:host-effect-blocked",
258108
+ message: `Host function '${hostName}' is not executed during analysis (network/timer side effect); result widened to unknown#opaque`,
258109
+ suggestion: "optional: mock it with @nudo:mock / @nudo:env, or pass the value in from a call site",
258110
+ fn: hostName
258111
+ });
258112
+ continue;
258113
+ }
258060
258114
  issues.push({
258061
258115
  severity: "info",
258062
258116
  code: "nudo:recursion-truncated",
@@ -258186,6 +258240,19 @@ function checkReturnConstraint(fnName, cName, constraint, ret) {
258186
258240
  const out = [];
258187
258241
  if (ret.shape.k === "unknown" && !ret.term) return out;
258188
258242
  if (ret.shape.k === "any") return out;
258243
+ if (isNullishLitAbs(ret)) return out;
258244
+ if (ret.shape.k === "sum" && constraint.fields) {
258245
+ const seen = /* @__PURE__ */ new Set();
258246
+ for (const m of ret.shape.members) {
258247
+ for (const issue of checkReturnConstraint(fnName, cName, constraint, m)) {
258248
+ const key = `${issue.message}\0${issue.actual ?? ""}\0${issue.expected ?? ""}`;
258249
+ if (seen.has(key)) continue;
258250
+ seen.add(key);
258251
+ out.push(issue);
258252
+ }
258253
+ }
258254
+ return out;
258255
+ }
258189
258256
  const push = (actual, expected, suggestion) => {
258190
258257
  out.push({
258191
258258
  severity: "error",
@@ -258354,6 +258421,11 @@ function actionsForIssue(i) {
258354
258421
  label: "results widened to unknown \u2014 raise budget or narrow control flow"
258355
258422
  }
258356
258423
  ];
258424
+ case "nudo:host-effect-blocked":
258425
+ return [
258426
+ { kind: "mock", label: "pin the host API with @nudo:mock / @nudo:env" },
258427
+ { kind: "info", label: "results widened to unknown \u2014 or feed the value in from a call site" }
258428
+ ];
258357
258429
  case "nudo:interface-drift":
258358
258430
  return [
258359
258431
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nudojs/lsp",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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.0",
53
- "@nudojs/core": "1.1.0",
54
- "@nudojs/parser": "1.1.0",
52
+ "@nudojs/service": "1.1.2",
53
+ "@nudojs/core": "1.1.2",
54
+ "@nudojs/parser": "1.1.2",
55
55
  "@babel/traverse": "^8.0.6",
56
56
  "@babel/types": "^8.0.6",
57
57
  "vscode-languageserver": "^10.1.2",