@nudojs/service 0.2.1 → 0.3.0

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 (64) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.d.ts +502 -4
  3. package/dist/index.js +3017 -212
  4. package/package.json +5 -4
  5. package/src/__tests__/abs-bindings.test.ts +53 -0
  6. package/src/__tests__/abs-call-records.test.ts +33 -0
  7. package/src/__tests__/abs-calls.test.ts +59 -0
  8. package/src/__tests__/abs-module-cache.test.ts +121 -0
  9. package/src/__tests__/abs-modules-graph.test.ts +100 -0
  10. package/src/__tests__/abs-typevalue-diff.test.ts +111 -0
  11. package/src/__tests__/analyzer.test.ts +71 -3
  12. package/src/__tests__/bpath-arg-provenance.test.ts +104 -0
  13. package/src/__tests__/bpath-as.test.ts +54 -0
  14. package/src/__tests__/bpath-cache-key.test.ts +33 -0
  15. package/src/__tests__/bpath-calls.test.ts +55 -0
  16. package/src/__tests__/bpath-diagnostics.test.ts +78 -0
  17. package/src/__tests__/bpath-env-modules.test.ts +30 -0
  18. package/src/__tests__/bpath-env.test.ts +80 -0
  19. package/src/__tests__/bpath-hosted-skip.test.ts +71 -0
  20. package/src/__tests__/bpath-import-default-ns.test.ts +106 -0
  21. package/src/__tests__/bpath-method-dedupe.test.ts +65 -0
  22. package/src/__tests__/bpath-method-diag.test.ts +45 -0
  23. package/src/__tests__/bpath-module-diags.test.ts +101 -0
  24. package/src/__tests__/bpath-path-env.test.ts +46 -0
  25. package/src/__tests__/bpath-reexport.test.ts +131 -0
  26. package/src/__tests__/bpath-replace.test.ts +52 -0
  27. package/src/__tests__/bpath-require.test.ts +66 -0
  28. package/src/__tests__/bpath-run.test.ts +109 -0
  29. package/src/__tests__/cjs-require.test.ts +54 -0
  30. package/src/__tests__/diagnostics.test.ts +0 -30
  31. package/src/__tests__/fn-cache-evict.test.ts +56 -0
  32. package/src/__tests__/harvest-auto.test.ts +43 -0
  33. package/src/__tests__/harvest-node.test.ts +24 -0
  34. package/src/__tests__/harvest-package.test.ts +38 -0
  35. package/src/__tests__/harvest-to-abs.test.ts +46 -0
  36. package/src/__tests__/hover-intension.test.ts +39 -0
  37. package/src/__tests__/infer-json.test.ts +35 -0
  38. package/src/__tests__/integration.test.ts +89 -0
  39. package/src/__tests__/m3-intension.test.ts +28 -0
  40. package/src/__tests__/mock-seed-fingerprint.test.ts +64 -0
  41. package/src/__tests__/session-cache-evict.test.ts +90 -0
  42. package/src/__tests__/static-imports.test.ts +44 -0
  43. package/src/abs-modules-graph.ts +387 -0
  44. package/src/analysis-file-cache.ts +51 -0
  45. package/src/analyzer.ts +1416 -124
  46. package/src/bpath-diagnostics.ts +283 -0
  47. package/src/bpath-run.ts +409 -0
  48. package/src/case-emitter.ts +2 -0
  49. package/src/dts-generator.ts +3 -3
  50. package/src/env-to-abs.ts +57 -0
  51. package/src/fn-analysis-cache.ts +119 -0
  52. package/src/guard-generator.ts +8 -1
  53. package/src/harvest-auto.ts +86 -0
  54. package/src/harvest-node.ts +48 -0
  55. package/src/harvest-package.ts +204 -0
  56. package/src/harvest-to-abs.ts +129 -0
  57. package/src/index.ts +104 -1
  58. package/src/infer-json.ts +137 -0
  59. package/src/load-module.ts +34 -0
  60. package/src/mock-abs.ts +275 -0
  61. package/src/semantic-tokens.ts +216 -0
  62. package/src/session-cache.ts +50 -0
  63. package/src/static-imports.ts +241 -0
  64. package/src/target-path.ts +17 -0
package/dist/index.js CHANGED
@@ -1,19 +1,37 @@
1
1
  // src/analyzer.ts
2
- import { readFileSync, existsSync, statSync, realpathSync } from "fs";
3
- import { resolve, dirname } from "path";
2
+ import { readFileSync as readFileSync4, existsSync as existsSync3, statSync as statSync3, realpathSync } from "fs";
3
+ import { resolve as resolve4, dirname as dirname5 } from "path";
4
4
  import traverse from "@babel/traverse";
5
5
  import {
6
- T,
7
- typeValueToString,
6
+ T as T2,
7
+ typeValueToString as typeValueToString3,
8
8
  simplifyUnion,
9
9
  widenLiteral,
10
10
  collapseLiteralUnion,
11
- createEnvironment,
11
+ createEnvironment as createEnvironment2,
12
12
  isSubtypeOf,
13
- mockHelperToTypeValue
13
+ mockHelperToTypeValue,
14
+ getFnSig as getFnSig3,
15
+ generalizeFromAst as generalizeFromAst2,
16
+ termToString,
17
+ predToString,
18
+ analyzeFn,
19
+ evalProgramAbs as evalProgramAbs2,
20
+ setAbsCallCollector,
21
+ setAbsNodeCollector,
22
+ absFunction as absFunction5,
23
+ collectAbsNodeTypes,
24
+ findAbsAtPosition,
25
+ typeValueToAbs as typeValueToAbs5,
26
+ absToTypeValue as absToTypeValue2,
27
+ formatAbs as formatAbs3,
28
+ formatAbsMultiline,
29
+ stableAnalyzeKeySource as stableAnalyzeKeySource2,
30
+ fnFingerprints
14
31
  } from "@nudojs/core";
15
- import { parse, extractDirectives, extractFileDirectives, parseTypeValueExpr } from "@nudojs/parser";
32
+ import { parse as parse5, extractDirectives, extractFileDirectives, parseTypeValueExpr } from "@nudojs/parser";
16
33
  import {
34
+ evaluate,
17
35
  evaluateFunction,
18
36
  evaluateFunctionFull,
19
37
  memberMayExistOn,
@@ -36,32 +54,1530 @@ import {
36
54
  setMockModules,
37
55
  resetMockModules,
38
56
  setCurrentSource,
39
- loadEnvs,
57
+ loadEnvs as loadEnvs2,
40
58
  preloadPathEnvs,
41
59
  findProjectConfig,
42
- resolveNpmNudo
60
+ resolveNpmNudo,
61
+ BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS
43
62
  } from "@nudojs/cli/evaluator";
63
+
64
+ // src/mock-abs.ts
65
+ import {
66
+ absFunction,
67
+ emptyEnv,
68
+ typeValueToAbs,
69
+ abs as makeAbs,
70
+ confJoin,
71
+ unknown as absUnknown,
72
+ litValue,
73
+ formatAbs,
74
+ getFnImpl,
75
+ typeValueToString
76
+ } from "@nudojs/core";
77
+ function markMockConf(a) {
78
+ a.conf = confJoin(a.conf, "mock");
79
+ return a;
80
+ }
81
+ function astContentKey(n, depth = 0) {
82
+ if (n == null || depth > 24) return "";
83
+ if (typeof n !== "object") return String(n);
84
+ if (Array.isArray(n)) return n.map((x) => astContentKey(x, depth + 1)).join(",");
85
+ const obj = n;
86
+ const type = typeof obj.type === "string" ? obj.type : "";
87
+ const parts = [type];
88
+ for (const key of ["name", "value", "raw", "operator", "computed"]) {
89
+ const v = obj[key];
90
+ if (v !== void 0 && (typeof v !== "object" || v === null)) parts.push(`${key}=${String(v)}`);
91
+ }
92
+ for (const key of Object.keys(obj)) {
93
+ if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments" || key === "type" || key === "name" || key === "value" || key === "raw" || key === "operator" || key === "computed") {
94
+ continue;
95
+ }
96
+ const v = obj[key];
97
+ if (v && typeof v === "object") parts.push(`${key}:{${astContentKey(v, depth + 1)}}`);
98
+ }
99
+ return parts.join("|");
100
+ }
101
+ function stampFingerprint(a, fp) {
102
+ const impl = getFnImpl(a);
103
+ if (impl) impl.fingerprint = fp;
104
+ return a;
105
+ }
106
+ function constantMockFn(result) {
107
+ const marked = markMockConf(result);
108
+ const mockEnv = emptyEnv();
109
+ const retName = "__nudo_mock_ret";
110
+ mockEnv.vars.set(retName, marked);
111
+ const body = {
112
+ type: "BlockStatement",
113
+ body: [
114
+ {
115
+ type: "ReturnStatement",
116
+ argument: { type: "Identifier", name: retName }
117
+ }
118
+ ]
119
+ };
120
+ let retKey;
121
+ try {
122
+ retKey = formatAbs(marked);
123
+ } catch {
124
+ retKey = "?";
125
+ }
126
+ return stampFingerprint(
127
+ markMockConf(absFunction(["...args"], { body, env: mockEnv })),
128
+ `ret=${retKey}`
129
+ );
130
+ }
131
+ function absArgMatches(declared, actual) {
132
+ if (!actual) return false;
133
+ const declaredAbs = typeValueToAbs(declared);
134
+ const av = litValue(actual);
135
+ const dv = litValue(declaredAbs);
136
+ if (dv !== void 0) {
137
+ return av !== void 0 && Object.is(av, dv);
138
+ }
139
+ if (declared.kind === "primitive") {
140
+ if (actual.shape.k === "prim") return actual.shape.type === declared.type;
141
+ if (av !== void 0) {
142
+ const t = typeof av;
143
+ return declared.type === "number" && t === "number" || declared.type === "string" && t === "string" || declared.type === "boolean" && t === "boolean" || declared.type === "bigint" && t === "bigint";
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+ function dispatchMockFn(defaultReturn, cases) {
149
+ if (!cases?.length) return constantMockFn(defaultReturn);
150
+ const caseAbs = cases.map((c) => ({
151
+ declared: c.args,
152
+ result: markMockConf(typeValueToAbs(c.returnValue))
153
+ }));
154
+ const dummyBody = {
155
+ type: "BlockStatement",
156
+ body: [{ type: "ReturnStatement", argument: null }]
157
+ };
158
+ const markedDefault = markMockConf(defaultReturn);
159
+ let defaultKey;
160
+ try {
161
+ defaultKey = formatAbs(markedDefault);
162
+ } catch {
163
+ defaultKey = "?";
164
+ }
165
+ const caseKey = cases.map(
166
+ (c) => `${c.args.map((a) => typeValueToString(a)).join(",")}->${typeValueToString(c.returnValue)}`
167
+ ).join("|");
168
+ return stampFingerprint(
169
+ markMockConf(
170
+ absFunction(["...args"], {
171
+ body: dummyBody,
172
+ env: emptyEnv(),
173
+ apply: (args) => {
174
+ for (const c of caseAbs) {
175
+ if (c.declared.every((d, i) => absArgMatches(d, args[i]))) {
176
+ return c.result;
177
+ }
178
+ }
179
+ return markedDefault;
180
+ }
181
+ })
182
+ ),
183
+ `dispatch=default=${defaultKey};cases=${caseKey}`
184
+ );
185
+ }
186
+ function absFromMockHelper(h) {
187
+ if (h.callsFakeImpl && h.callsFakeImpl.kind === "function") {
188
+ const fn = h.callsFakeImpl;
189
+ const body = fn.body;
190
+ return stampFingerprint(
191
+ absFunction(fn.params, { body, async: false }),
192
+ `fake=${fn.params.join(",")}:${astContentKey(body)}`
193
+ );
194
+ }
195
+ let defaultReturn;
196
+ if (h.resolvedValue) {
197
+ defaultReturn = makeAbs(
198
+ { k: "eff", eff: "promise", inner: typeValueToAbs(h.resolvedValue) },
199
+ void 0,
200
+ void 0,
201
+ "path"
202
+ );
203
+ } else if (h.rejectedValue) {
204
+ defaultReturn = makeAbs({ k: "never" }, void 0, void 0, "exact");
205
+ } else if (h.returnValue) {
206
+ defaultReturn = typeValueToAbs(h.returnValue);
207
+ } else if (h.onFirstCallValue) {
208
+ defaultReturn = typeValueToAbs(h.onFirstCallValue);
209
+ } else {
210
+ defaultReturn = absUnknown;
211
+ }
212
+ return dispatchMockFn(defaultReturn, h.withArgsCases);
213
+ }
214
+ function absFromSinon(sinonExpr) {
215
+ if (sinonExpr.resolvedValue) {
216
+ return constantMockFn(
217
+ makeAbs(
218
+ { k: "eff", eff: "promise", inner: typeValueToAbs(sinonExpr.resolvedValue) },
219
+ void 0,
220
+ void 0,
221
+ "path"
222
+ )
223
+ );
224
+ }
225
+ if (sinonExpr.rejectedValue) {
226
+ return constantMockFn(makeAbs({ k: "never" }, void 0, void 0, "exact"));
227
+ }
228
+ if (sinonExpr.returnValue) {
229
+ return constantMockFn(typeValueToAbs(sinonExpr.returnValue));
230
+ }
231
+ return constantMockFn(absUnknown);
232
+ }
233
+ function mockSeedsToAbsMocks(seeds) {
234
+ const out = { ...seeds.seedVars };
235
+ for (const [name, fn] of Object.entries(seeds.seedFns)) {
236
+ out[name] = absFunction(fn.params, { body: fn.body, async: fn.async ?? false });
237
+ }
238
+ return out;
239
+ }
240
+ function mockDirectivesToAbsSeeds(functions) {
241
+ const seedVars = {};
242
+ const seedFns = {};
243
+ for (const fn of functions) {
244
+ for (const d of fn.directives) {
245
+ if (d.kind !== "mock") continue;
246
+ if (d.arrowFn) {
247
+ const body = d.arrowFn.body;
248
+ seedFns[d.name] = {
249
+ params: d.arrowFn.params,
250
+ body,
251
+ async: false,
252
+ fingerprint: `fake=${d.arrowFn.params.join(",")}:${astContentKey(body)}`
253
+ };
254
+ } else if (d.nudoMock) {
255
+ seedVars[d.name] = markMockConf(absFromMockHelper(d.nudoMock));
256
+ } else if (d.sinonExpr) {
257
+ seedVars[d.name] = markMockConf(absFromSinon(d.sinonExpr));
258
+ }
259
+ }
260
+ }
261
+ return { seedVars, seedFns };
262
+ }
263
+
264
+ // src/harvest-auto.ts
265
+ import { parse as parse2 } from "@nudojs/parser";
266
+
267
+ // src/static-imports.ts
268
+ import { parse } from "@nudojs/parser";
269
+ import { readFileSync, existsSync } from "fs";
270
+ import { resolve, dirname, join } from "path";
271
+ import { generalizeFromAst } from "@nudojs/core";
272
+ function resolveRelative(fromFile, spec) {
273
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
274
+ const base = resolve(dirname(fromFile), spec);
275
+ for (const cand of [
276
+ base,
277
+ base + ".js",
278
+ base + ".cjs",
279
+ base + ".mjs",
280
+ base + ".ts",
281
+ base + ".mts",
282
+ base + ".cts",
283
+ join(base, "index.js"),
284
+ join(base, "index.cjs"),
285
+ join(base, "index.mjs"),
286
+ join(base, "index.ts")
287
+ ]) {
288
+ if (existsSync(cand)) return cand;
289
+ }
290
+ return void 0;
291
+ }
292
+ function collectDependencySpecs(ast) {
293
+ const specs = [];
294
+ for (const stmt of ast.program.body) {
295
+ if (stmt.type === "ImportDeclaration") {
296
+ specs.push(stmt.source.value);
297
+ }
298
+ if (stmt.type === "ExportNamedDeclaration" && stmt.source) {
299
+ specs.push(stmt.source.value);
300
+ }
301
+ if (stmt.type === "ExportAllDeclaration" && stmt.source) {
302
+ specs.push(stmt.source.value);
303
+ }
304
+ collectRequires(stmt, specs);
305
+ }
306
+ return specs;
307
+ }
308
+ function collectRequires(node, out) {
309
+ const visit = (n) => {
310
+ if (!n || typeof n !== "object") return;
311
+ const obj = n;
312
+ if (obj.type === "CallExpression" && obj.callee?.type === "Identifier" && obj.callee.name === "require" && obj.arguments?.[0]?.type === "StringLiteral") {
313
+ const spec = obj.arguments[0].value;
314
+ if (typeof spec === "string") out.push(spec);
315
+ }
316
+ for (const key of Object.keys(obj)) {
317
+ if (key === "loc" || key === "start" || key === "end") continue;
318
+ const v = obj[key];
319
+ if (Array.isArray(v)) v.forEach(visit);
320
+ else if (v && typeof v === "object") visit(v);
321
+ }
322
+ };
323
+ visit(node);
324
+ }
325
+ function analyzeExportsFromSource(filePath, source) {
326
+ const file = parse(source);
327
+ const named = /* @__PURE__ */ new Map();
328
+ let defaultExport;
329
+ const poly = /* @__PURE__ */ new Map();
330
+ for (const stmt of file.program.body) {
331
+ let decl = stmt;
332
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
333
+ decl = stmt.declaration;
334
+ }
335
+ if (decl.type === "FunctionDeclaration" && decl.id) {
336
+ named.set(decl.id.name, decl.id.name);
337
+ }
338
+ if (decl.type === "VariableDeclaration") {
339
+ for (const d of decl.declarations) {
340
+ if (d.id.type === "Identifier") named.set(d.id.name, d.id.name);
341
+ }
342
+ }
343
+ if (decl.type === "ClassDeclaration" && decl.id) {
344
+ named.set(decl.id.name, decl.id.name);
345
+ }
346
+ if (stmt.type === "ExportDefaultDeclaration") {
347
+ const d = stmt.declaration;
348
+ if (d.type === "FunctionDeclaration" && d.id) defaultExport = d.id.name;
349
+ if (d.type === "Identifier") defaultExport = d.name;
350
+ }
351
+ collectCjsExports(stmt, named, (n) => {
352
+ defaultExport = n;
353
+ });
354
+ }
355
+ for (const name of named.values()) {
356
+ const g = generalizeFromAst(name, source);
357
+ if (g) poly.set(name, g);
358
+ }
359
+ if (defaultExport) {
360
+ const g = generalizeFromAst(defaultExport, source);
361
+ if (g) poly.set("default", g);
362
+ }
363
+ return { path: filePath, named, defaultExport, source, poly };
364
+ }
365
+ function collectCjsExports(stmt, named, setDefault) {
366
+ const visit = (n) => {
367
+ if (!n || typeof n !== "object") return;
368
+ const obj = n;
369
+ if (obj.type !== "AssignmentExpression") {
370
+ for (const key of Object.keys(obj)) {
371
+ if (key === "loc" || key === "start" || key === "end") continue;
372
+ const v = obj[key];
373
+ if (Array.isArray(v)) v.forEach(visit);
374
+ else if (v && typeof v === "object") visit(v);
375
+ }
376
+ return;
377
+ }
378
+ const left = obj.left;
379
+ const right = obj.right;
380
+ if (left?.type === "MemberExpression" && left.object?.type === "Identifier" && left.object.name === "exports" && left.property?.type === "Identifier" && left.property.name) {
381
+ named.set(left.property.name, left.property.name);
382
+ }
383
+ if (left?.type === "MemberExpression" && left.object?.type === "Identifier" && left.object.name === "module" && left.property?.type === "Identifier" && left.property.name === "exports") {
384
+ if (right?.type === "Identifier" && typeof right.name === "string") {
385
+ named.set(right.name, right.name);
386
+ setDefault(right.name);
387
+ }
388
+ if (right?.type === "ObjectExpression") {
389
+ const props = right.properties;
390
+ for (const p of props) {
391
+ if (p.type !== "ObjectProperty") continue;
392
+ const key = p.key;
393
+ const val = p.value;
394
+ const exportName = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? key.value : void 0;
395
+ if (exportName) named.set(exportName, exportName);
396
+ if (val?.type === "Identifier" && val.name && exportName) {
397
+ }
398
+ }
399
+ }
400
+ }
401
+ };
402
+ visit(stmt);
403
+ }
404
+ function collectStaticImports(entryFile, maxDepth = 8) {
405
+ const graph = /* @__PURE__ */ new Map();
406
+ const queue = [
407
+ { file: resolve(entryFile), depth: 0 }
408
+ ];
409
+ const seen = /* @__PURE__ */ new Set();
410
+ while (queue.length > 0) {
411
+ const { file, depth } = queue.shift();
412
+ if (seen.has(file) || depth > maxDepth) continue;
413
+ seen.add(file);
414
+ if (!existsSync(file)) continue;
415
+ const source = readFileSync(file, "utf8");
416
+ const mod = analyzeExportsFromSource(file, source);
417
+ graph.set(file, mod);
418
+ const ast = parse(source);
419
+ for (const spec of collectDependencySpecs(ast)) {
420
+ const resolved = resolveRelative(file, spec);
421
+ if (resolved) queue.push({ file: resolved, depth: depth + 1 });
422
+ }
423
+ }
424
+ return graph;
425
+ }
426
+
427
+ // src/harvest-package.ts
428
+ import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
429
+ import { join as join2, resolve as resolve2, dirname as dirname2, basename } from "path";
430
+ import { harvestDts } from "@nudojs/harvester";
431
+ import { typeValueToString as typeValueToString2 } from "@nudojs/core";
432
+ function resolvePackageRoot(pkg, fromDir = process.cwd()) {
433
+ let dir = resolve2(fromDir);
434
+ const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
435
+ for (let i = 0; i < 8; i++) {
436
+ const typesPath = join2(dir, "node_modules", "@types", bare);
437
+ if (existsSync2(typesPath)) return typesPath;
438
+ const pkgPath = join2(dir, "node_modules", pkg);
439
+ if (existsSync2(pkgPath)) return pkgPath;
440
+ const parent = resolve2(dir, "..");
441
+ if (parent === dir) break;
442
+ dir = parent;
443
+ }
444
+ return void 0;
445
+ }
446
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "test", "tests", "docs", "doc", "examples", "__tests__"]);
447
+ var MAX_DTS_BYTES = 15e5;
448
+ function dtsPriority(p, root) {
449
+ const base = basename(p);
450
+ const rel = p.slice(root.length);
451
+ if (base === "index.d.ts") return 0;
452
+ if (base === "index.d.mts") return 1;
453
+ if (rel.includes("/types/") && base === "index.d.ts") return 2;
454
+ if (base.endsWith(".d.ts") && !base.includes(".")) return 3;
455
+ return 10;
456
+ }
457
+ function collectDtsFiles(root, maxFiles = 8) {
458
+ const out = [];
459
+ const walk = (dir) => {
460
+ if (out.length >= maxFiles * 3) return;
461
+ let entries;
462
+ try {
463
+ entries = readdirSync(dir);
464
+ } catch {
465
+ return;
466
+ }
467
+ for (const name of entries) {
468
+ if (out.length >= maxFiles * 3) return;
469
+ if (SKIP_DIRS.has(name)) continue;
470
+ const p = join2(dir, name);
471
+ let st;
472
+ try {
473
+ st = statSync(p);
474
+ } catch {
475
+ continue;
476
+ }
477
+ if (st.isDirectory()) walk(p);
478
+ else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) {
479
+ if (st.size > MAX_DTS_BYTES) continue;
480
+ out.push(p);
481
+ }
482
+ }
483
+ };
484
+ walk(root);
485
+ out.sort((a, b) => dtsPriority(a, root) - dtsPriority(b, root) || a.length - b.length);
486
+ return out.slice(0, maxFiles);
487
+ }
488
+ function entryDtsFromPackageJson(root) {
489
+ const pj = join2(root, "package.json");
490
+ if (!existsSync2(pj)) return void 0;
491
+ try {
492
+ const raw = JSON.parse(readFileSync2(pj, "utf8"));
493
+ const entry = raw.types ?? raw.typings;
494
+ if (typeof entry === "string" && entry.endsWith(".d.ts")) {
495
+ const p = resolve2(root, entry);
496
+ if (existsSync2(p)) {
497
+ const st = statSync(p);
498
+ if (st.size <= MAX_DTS_BYTES) return p;
499
+ }
500
+ }
501
+ } catch {
502
+ }
503
+ return void 0;
504
+ }
505
+ function collectDtsFromEntry(entry, maxFiles = 200) {
506
+ const files = [];
507
+ const seen = /* @__PURE__ */ new Set();
508
+ const queue = [entry];
509
+ const REFERENCE_PATH_REGEX = /<reference\s+path=["']([^"']+)["']\s*\/>/g;
510
+ const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
511
+ while (queue.length > 0 && files.length < maxFiles) {
512
+ const current = queue.shift();
513
+ if (seen.has(current) || !existsSync2(current)) continue;
514
+ seen.add(current);
515
+ if (!current.endsWith(".d.ts")) continue;
516
+ files.push(current);
517
+ let text;
518
+ try {
519
+ text = readFileSync2(current, "utf-8");
520
+ } catch {
521
+ continue;
522
+ }
523
+ const dir = dirname2(current);
524
+ for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
525
+ queue.push(resolve2(dir, match[1]));
526
+ }
527
+ for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
528
+ const base = resolve2(dir, match[1]);
529
+ for (const candidate of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
530
+ if (existsSync2(candidate)) {
531
+ queue.push(candidate);
532
+ break;
533
+ }
534
+ }
535
+ }
536
+ }
537
+ return files;
538
+ }
539
+ function harvestPackage(pkg, fromDir, maxFiles = 8) {
540
+ const root = resolvePackageRoot(pkg, fromDir);
541
+ if (!root) return { error: `package not found: ${pkg}` };
542
+ const entry = entryDtsFromPackageJson(root) ?? join2(root, "index.d.ts");
543
+ let dtsFiles = existsSync2(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
544
+ if (dtsFiles.length === 0) {
545
+ const collected = collectDtsFiles(root, maxFiles);
546
+ dtsFiles = entry && existsSync2(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
547
+ }
548
+ if (dtsFiles.length === 0) {
549
+ return { error: `no .d.ts under ${root}` };
550
+ }
551
+ const env = harvestDts(dtsFiles);
552
+ return { pkg, root, dtsFiles, env };
553
+ }
554
+ function formatHarvestSummary(h) {
555
+ const lines = [];
556
+ lines.push(`${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols`);
557
+ const mods = Object.keys(h.env.modules);
558
+ for (const m of mods.slice(0, 5)) {
559
+ const exports = h.env.modules[m];
560
+ const names = Object.keys(exports).slice(0, 12);
561
+ lines.push(` module "${m}": ${names.join(", ")}${Object.keys(exports).length > 12 ? "\u2026" : ""}`);
562
+ }
563
+ if (mods.length > 5) lines.push(` \u2026 ${mods.length - 5} more modules`);
564
+ return lines.join("\n");
565
+ }
566
+ function lookupHarvested(h, moduleName, exportName) {
567
+ const mod = h.env.modules[moduleName];
568
+ const tv = mod?.[exportName] ?? h.env.globals[exportName];
569
+ if (!tv) return void 0;
570
+ return typeValueToString2(tv);
571
+ }
572
+
573
+ // src/harvest-auto.ts
574
+ function barePackageName(spec) {
575
+ if (!spec) return void 0;
576
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) return void 0;
577
+ const parts = spec.split("/");
578
+ if (spec.startsWith("@")) {
579
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
580
+ }
581
+ return parts[0];
582
+ }
583
+ function collectBarePackages(source) {
584
+ try {
585
+ const ast = parse2(source);
586
+ const names = /* @__PURE__ */ new Set();
587
+ for (const spec of collectDependencySpecs(ast)) {
588
+ const pkg = barePackageName(spec);
589
+ if (pkg) names.add(pkg);
590
+ }
591
+ return [...names];
592
+ } catch {
593
+ return [];
594
+ }
595
+ }
596
+ var harvestCache = /* @__PURE__ */ new Map();
597
+ function harvestPackageCached(pkg, fromDir) {
598
+ const key = `${fromDir}::${pkg}`;
599
+ if (harvestCache.has(key)) return harvestCache.get(key);
600
+ let result = null;
601
+ try {
602
+ const h = harvestPackage(pkg, fromDir);
603
+ if (!("error" in h)) result = h;
604
+ } catch {
605
+ result = null;
606
+ }
607
+ harvestCache.set(key, result);
608
+ return result;
609
+ }
610
+ function clearHarvestCache() {
611
+ harvestCache.clear();
612
+ }
613
+ function autoHarvestModules(source, fromDir) {
614
+ const packages = collectBarePackages(source);
615
+ if (packages.length === 0) return {};
616
+ const modules = {};
617
+ for (const pkg of packages) {
618
+ const h = harvestPackageCached(pkg, fromDir);
619
+ if (!h) continue;
620
+ for (const [mod, exports] of Object.entries(h.env.modules)) {
621
+ modules[mod] = exports;
622
+ if (!modules[pkg]) modules[pkg] = exports;
623
+ const base = mod.replace(/^@types\//, "").replace(/\\/g, "/");
624
+ if (base && !modules[base]) modules[base] = exports;
625
+ }
626
+ for (const [k, v] of Object.entries(h.env.globals)) {
627
+ if (!modules[pkg]) modules[pkg] = {};
628
+ if (modules[pkg][k] === void 0) modules[pkg][k] = v;
629
+ }
630
+ }
631
+ return modules;
632
+ }
633
+
634
+ // src/abs-modules-graph.ts
635
+ import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
636
+ import { dirname as dirname4, resolve as resolve3 } from "path";
637
+ import { parse as parse3 } from "@nudojs/parser";
638
+ import {
639
+ evalProgramAbs,
640
+ collectAbsExports,
641
+ absFunction as absFunction3
642
+ } from "@nudojs/core";
643
+
644
+ // src/harvest-to-abs.ts
645
+ import {
646
+ T,
647
+ typeValueToAbs as typeValueToAbs2,
648
+ confJoin as confJoin2,
649
+ getFnSig,
650
+ absFunction as absFunction2
651
+ } from "@nudojs/core";
652
+ import { dirname as dirname3 } from "path";
653
+ function markMockConf2(a) {
654
+ a.conf = confJoin2(a.conf, "mock");
655
+ return a;
656
+ }
657
+ function harvestedValueToAbs(tv) {
658
+ if (!tv) return markMockConf2(typeValueToAbs2(T.unknown));
659
+ const sig = getFnSig(tv);
660
+ if (sig) {
661
+ const ret = markMockConf2(typeValueToAbs2(sig.returnType));
662
+ const params = tv.kind === "function" && tv.params?.length ? tv.params : sig.paramTypes.map((_, i) => `_arg${i}`);
663
+ const dummyBody = {
664
+ type: "BlockStatement",
665
+ body: [],
666
+ directives: []
667
+ };
668
+ return markMockConf2(
669
+ absFunction2(params, {
670
+ body: dummyBody,
671
+ apply: () => ret
672
+ })
673
+ );
674
+ }
675
+ return markMockConf2(typeValueToAbs2(tv));
676
+ }
677
+ function harvestToAbsModules(pkg, fromDir) {
678
+ const h = harvestPackageCached(pkg, fromDir);
679
+ if (!h) return {};
680
+ return packageHarvestToAbsModules(pkg, h);
681
+ }
682
+ function packageHarvestToAbsModules(pkg, h) {
683
+ const out = {};
684
+ const convertRecord = (rec) => {
685
+ const named = {};
686
+ for (const [k, v] of Object.entries(rec)) {
687
+ named[k] = harvestedValueToAbs(v);
688
+ }
689
+ return named;
690
+ };
691
+ const register = (key, exports) => {
692
+ if (!out[key]) out[key] = exports;
693
+ };
694
+ for (const [mod, rec] of Object.entries(h.env.modules)) {
695
+ const named = convertRecord(rec);
696
+ const exports = { named };
697
+ const def = rec["default"];
698
+ if (def) exports.default = harvestedValueToAbs(def);
699
+ register(mod, exports);
700
+ register(pkg, exports);
701
+ const stripped = mod.replace(/^@types\//, "").replace(/\\/g, "/");
702
+ if (stripped) register(stripped, exports);
703
+ const base = stripped.split("/").filter(Boolean).pop();
704
+ if (base && base !== pkg) register(base, exports);
705
+ }
706
+ const globalNamed = convertRecord(h.env.globals);
707
+ if (Object.keys(globalNamed).length > 0) {
708
+ const existing = out[pkg];
709
+ if (existing) {
710
+ out[pkg] = { named: { ...globalNamed, ...existing.named }, default: existing.default };
711
+ } else {
712
+ out[pkg] = { named: globalNamed };
713
+ }
714
+ }
715
+ return out;
716
+ }
717
+ function bareSpecToAbsModules(spec, fromFile) {
718
+ if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) {
719
+ return void 0;
720
+ }
721
+ const parts = spec.split("/");
722
+ const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
723
+ if (!pkg) return void 0;
724
+ const table = harvestToAbsModules(pkg, dirname3(fromFile));
725
+ return table[spec] ?? table[pkg];
726
+ }
727
+
728
+ // src/abs-modules-graph.ts
729
+ function defaultAbsLoadModule(spec, fromFile) {
730
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
731
+ try {
732
+ const p = resolve3(dirname4(resolve3(fromFile)), spec);
733
+ for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve3(p, "index.js")]) {
734
+ try {
735
+ return readFileSync3(cand, "utf-8");
736
+ } catch {
737
+ }
738
+ }
739
+ return void 0;
740
+ } catch {
741
+ return void 0;
742
+ }
743
+ }
744
+ function relCandidates(spec, fromFile) {
745
+ const p = resolve3(dirname4(resolve3(fromFile)), spec);
746
+ return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve3(p, "index.js")];
747
+ }
748
+ function resolveRel(spec, fromFile) {
749
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
750
+ for (const cand of relCandidates(spec, fromFile)) {
751
+ try {
752
+ readFileSync3(cand, "utf-8");
753
+ return cand;
754
+ } catch {
755
+ }
756
+ }
757
+ return null;
758
+ }
759
+ function importSpecs(source) {
760
+ try {
761
+ const file = parse3(source);
762
+ const out = [];
763
+ for (const stmt of file.program.body) {
764
+ if (stmt.type === "ImportDeclaration" || stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportAllDeclaration") {
765
+ const src = stmt.source;
766
+ if (src && typeof src.value === "string") {
767
+ out.push(src.value);
768
+ }
769
+ }
770
+ collectRequireSpecs(stmt, out);
771
+ }
772
+ return out;
773
+ } catch {
774
+ return [];
775
+ }
776
+ }
777
+ function collectRequireSpecs(node, out) {
778
+ const visit = (n) => {
779
+ if (!n || typeof n !== "object") return;
780
+ const o = n;
781
+ if (o.type === "CallExpression" && o.callee?.type === "Identifier" && o.callee.name === "require" && o.arguments?.[0]?.type === "StringLiteral") {
782
+ const spec = o.arguments[0].value;
783
+ if (typeof spec === "string") out.push(spec);
784
+ }
785
+ for (const key of Object.keys(o)) {
786
+ if (key === "loc" || key === "start" || key === "end") continue;
787
+ const v = o[key];
788
+ if (Array.isArray(v)) v.forEach(visit);
789
+ else if (v && typeof v === "object") visit(v);
790
+ }
791
+ };
792
+ visit(node);
793
+ }
794
+ function buildModulesForFile(source, fromFile, load, evalDep, depth, maxDepth, onMissing) {
795
+ const modules = {};
796
+ for (const spec of importSpecs(source)) {
797
+ if (spec.startsWith(".") || spec.startsWith("/")) {
798
+ const childPath = resolveRel(spec, fromFile);
799
+ if (!childPath) {
800
+ onMissing(spec, fromFile, relCandidates(spec, fromFile));
801
+ continue;
802
+ }
803
+ modules[spec] = evalDep(childPath, spec, fromFile, depth + 1);
804
+ } else if (!spec.startsWith("node:")) {
805
+ const bare = bareSpecToAbsModules(spec, fromFile);
806
+ if (bare) modules[spec] = bare;
807
+ }
808
+ }
809
+ void maxDepth;
810
+ return modules;
811
+ }
812
+ var absModuleCache = /* @__PURE__ */ new Map();
813
+ function clearAbsModuleCache() {
814
+ absModuleCache.clear();
815
+ }
816
+ function evictAbsModuleCacheFiles(paths) {
817
+ for (const p of paths) absModuleCache.delete(p);
818
+ }
819
+ function moduleLabel(p) {
820
+ const parts = p.split(/[/\\]/);
821
+ return parts[parts.length - 1] || p;
822
+ }
823
+ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
824
+ const load = opts.loadModule ?? defaultAbsLoadModule;
825
+ const maxDepth = opts.maxDepth ?? 16;
826
+ const cache = /* @__PURE__ */ new Map();
827
+ const loading = [];
828
+ const issues = [];
829
+ const seenIssue = /* @__PURE__ */ new Set();
830
+ const pushIssue = (kind, label, reason) => {
831
+ const key = `${kind}:${label}`;
832
+ if (seenIssue.has(key)) return;
833
+ seenIssue.add(key);
834
+ issues.push({ kind, label, reason });
835
+ };
836
+ function evalDep(absPath, spec, fromFile, depth) {
837
+ const cycleIndex = loading.indexOf(absPath);
838
+ if (cycleIndex !== -1) {
839
+ const chain = [...loading.slice(cycleIndex), absPath];
840
+ pushIssue(
841
+ "cycle",
842
+ moduleLabel(absPath),
843
+ `Circular module load: ${chain.join(" -> ")} (bindings inside the cycle resolve to their partially evaluated types)`
844
+ );
845
+ return cache.get(absPath) ?? { named: {} };
846
+ }
847
+ const shared = absModuleCache.get(absPath);
848
+ if (shared) {
849
+ try {
850
+ const st = statSync2(absPath);
851
+ if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
852
+ for (const iss of shared.issues) pushIssue(iss.kind, iss.label, iss.reason);
853
+ cache.set(absPath, shared.exports);
854
+ return shared.exports;
855
+ }
856
+ } catch {
857
+ }
858
+ absModuleCache.delete(absPath);
859
+ }
860
+ if (cache.has(absPath)) return cache.get(absPath);
861
+ if (depth > maxDepth) {
862
+ const chain = [...loading, absPath];
863
+ pushIssue(
864
+ "depth",
865
+ moduleLabel(absPath),
866
+ `Module load chain too deep (depth ${chain.length} > ${maxDepth} max): ${chain.join(" -> ")} (loading truncated, deeper modules typed as unknown)`
867
+ );
868
+ const empty = { named: {} };
869
+ cache.set(absPath, empty);
870
+ return empty;
871
+ }
872
+ cache.set(absPath, { named: {} });
873
+ loading.push(absPath);
874
+ const source = load(spec, fromFile);
875
+ if (source === void 0) {
876
+ pushIssue(
877
+ "missing",
878
+ spec,
879
+ `Module file not found for '${spec}' (from ${moduleLabel(fromFile)}); tried: ${absPath}`
880
+ );
881
+ const empty = { named: {} };
882
+ cache.set(absPath, empty);
883
+ loading.pop();
884
+ return empty;
885
+ }
886
+ const issueStart = issues.length;
887
+ const modules2 = buildModulesForFile(
888
+ source,
889
+ absPath,
890
+ load,
891
+ evalDep,
892
+ depth,
893
+ maxDepth,
894
+ (spec2, fromFile2, tried) => {
895
+ pushIssue(
896
+ "missing",
897
+ spec2,
898
+ `Module file not found for '${spec2}' (from ${moduleLabel(fromFile2)}); tried: ${tried.join(", ")}`
899
+ );
900
+ }
901
+ );
902
+ let exports;
903
+ try {
904
+ const file = parse3(source);
905
+ const { env } = evalProgramAbs(source, { file, modules: modules2 });
906
+ exports = collectAbsExports(file, env, modules2);
907
+ } catch {
908
+ exports = { named: {} };
909
+ } finally {
910
+ loading.pop();
911
+ }
912
+ cache.set(absPath, exports);
913
+ let fingerprint;
914
+ try {
915
+ const st = statSync2(absPath);
916
+ fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
917
+ } catch {
918
+ }
919
+ if (fingerprint) {
920
+ absModuleCache.set(absPath, {
921
+ ...fingerprint,
922
+ exports,
923
+ issues: issues.slice(issueStart)
924
+ });
925
+ }
926
+ return exports;
927
+ }
928
+ const modules = buildModulesForFile(
929
+ entrySource,
930
+ entryFile,
931
+ load,
932
+ evalDep,
933
+ 0,
934
+ maxDepth,
935
+ (spec, fromFile, tried) => {
936
+ pushIssue(
937
+ "missing",
938
+ spec,
939
+ `Module file not found for '${spec}' (from ${moduleLabel(fromFile)}); tried: ${tried.join(", ")}`
940
+ );
941
+ }
942
+ );
943
+ return { modules, byPath: cache, issues };
944
+ }
945
+ function evalProgramAbsWithModules(source, entryFile, opts = {}) {
946
+ const { modules } = evalAbsModuleGraph(source, entryFile, opts);
947
+ return evalProgramAbs(source, {
948
+ file: opts.file,
949
+ modules,
950
+ seedVars: opts.seedVars,
951
+ seedFns: opts.seedFns
952
+ });
953
+ }
954
+ function collectAbsBindingsFromGraph(source, filePath, opts = {}) {
955
+ const out = /* @__PURE__ */ new Map();
956
+ try {
957
+ const { env } = evalProgramAbsWithModules(source, filePath, opts);
958
+ for (const [k, v] of env.vars) {
959
+ out.set(k, v);
960
+ }
961
+ for (const [name, impl] of env.fns) {
962
+ if (!out.has(name)) {
963
+ out.set(
964
+ name,
965
+ absFunction3(impl.params, { body: impl.body, async: impl.async, env })
966
+ );
967
+ }
968
+ }
969
+ } catch {
970
+ }
971
+ return out;
972
+ }
973
+
974
+ // src/bpath-run.ts
975
+ import {
976
+ runTranspiled,
977
+ callTranspiledExport,
978
+ callTranspiledExportFull,
979
+ setBCallCollector,
980
+ setMemberDiagCollector,
981
+ setAbsTruncationCollector,
982
+ typeValueToAbs as typeValueToAbs4,
983
+ createEnvironment,
984
+ stableAnalyzeKeySource,
985
+ formatAbs as formatAbs2,
986
+ hashSource,
987
+ getFnImpl as getFnImpl2
988
+ } from "@nudojs/core";
989
+ import { parse as parse4, extractInlineDirectives } from "@nudojs/parser";
990
+ import { loadEnvs } from "@nudojs/cli/evaluator";
991
+
992
+ // src/env-to-abs.ts
993
+ import {
994
+ typeValueToAbs as typeValueToAbs3,
995
+ absToTypeValue,
996
+ getFnSig as getFnSig2,
997
+ absFunction as absFunction4,
998
+ objOf
999
+ } from "@nudojs/core";
1000
+ function envValueToAbs(tv) {
1001
+ if (!tv) return typeValueToAbs3({ kind: "unknown" });
1002
+ if (tv.kind === "function") {
1003
+ const sig = getFnSig2(tv);
1004
+ if (sig?.impl) {
1005
+ const params = tv.kind === "function" && tv.params?.length ? tv.params : sig.paramTypes.map((_, i) => `_arg${i}`);
1006
+ const dummyBody = {
1007
+ type: "BlockStatement",
1008
+ body: [],
1009
+ directives: []
1010
+ };
1011
+ const impl = sig.impl;
1012
+ return absFunction4(params, {
1013
+ body: dummyBody,
1014
+ apply: (args) => {
1015
+ const tvArgs = args.map((a) => absToTypeValue(a));
1016
+ try {
1017
+ const r = impl(tvArgs);
1018
+ if (!r) return typeValueToAbs3(sig.returnType);
1019
+ return envValueToAbs(r);
1020
+ } catch {
1021
+ return typeValueToAbs3(sig.returnType);
1022
+ }
1023
+ }
1024
+ });
1025
+ }
1026
+ }
1027
+ if (tv.kind === "object") {
1028
+ const slots = {};
1029
+ for (const [k, v] of Object.entries(tv.properties)) {
1030
+ slots[k] = { value: envValueToAbs(v) };
1031
+ }
1032
+ return objOf(slots);
1033
+ }
1034
+ return typeValueToAbs3(tv);
1035
+ }
1036
+
1037
+ // src/analysis-file-cache.ts
1038
+ var analysisByFile = /* @__PURE__ */ new Map();
1039
+ var MAX_ANALYSIS_FILE_CACHE = 64;
1040
+ function clearAnalysisFileCache() {
1041
+ analysisByFile.clear();
1042
+ }
1043
+ function getAnalysisFileCacheSize() {
1044
+ return analysisByFile.size;
1045
+ }
1046
+ function analysisCacheGet(filePath, source, auxKey) {
1047
+ const e = analysisByFile.get(filePath);
1048
+ if (!e) return void 0;
1049
+ if (e.source !== source || e.auxKey !== auxKey) return void 0;
1050
+ analysisByFile.delete(filePath);
1051
+ analysisByFile.set(filePath, e);
1052
+ return e.value;
1053
+ }
1054
+ function analysisCacheSet(filePath, source, auxKey, value) {
1055
+ if (analysisByFile.size >= MAX_ANALYSIS_FILE_CACHE && !analysisByFile.has(filePath)) {
1056
+ const oldest = analysisByFile.keys().next().value;
1057
+ if (oldest !== void 0) analysisByFile.delete(oldest);
1058
+ }
1059
+ analysisByFile.set(filePath, { source, auxKey, value });
1060
+ }
1061
+ function evictAnalysisFileCacheForFiles(files) {
1062
+ let n = 0;
1063
+ for (const f of files) {
1064
+ if (analysisByFile.delete(f)) n++;
1065
+ }
1066
+ return n;
1067
+ }
1068
+
1069
+ // src/fn-analysis-cache.ts
1070
+ var fnAnalysisCache = /* @__PURE__ */ new Map();
1071
+ var MAX_FN_ANALYSIS_CACHE = 1024;
1072
+ function clearFnAnalysisCache() {
1073
+ fnAnalysisCache.clear();
1074
+ }
1075
+ function fnAnalysisCacheGet(key) {
1076
+ const hit = fnAnalysisCache.get(key);
1077
+ if (hit !== void 0) {
1078
+ fnAnalysisCache.delete(key);
1079
+ fnAnalysisCache.set(key, hit);
1080
+ }
1081
+ return hit;
1082
+ }
1083
+ function fnAnalysisCacheSet(key, value) {
1084
+ if (fnAnalysisCache.size >= MAX_FN_ANALYSIS_CACHE && !fnAnalysisCache.has(key)) {
1085
+ const oldest = fnAnalysisCache.keys().next().value;
1086
+ if (oldest !== void 0) fnAnalysisCache.delete(oldest);
1087
+ }
1088
+ fnAnalysisCache.set(key, value);
1089
+ }
1090
+ function evictFnAnalysisCacheForFiles(files) {
1091
+ if (files.length === 0 || fnAnalysisCache.size === 0) return 0;
1092
+ const prefixes = files.map((f) => `${f}\0`);
1093
+ let n = 0;
1094
+ for (const key of [...fnAnalysisCache.keys()]) {
1095
+ if (prefixes.some((p) => key.startsWith(p))) {
1096
+ fnAnalysisCache.delete(key);
1097
+ n++;
1098
+ }
1099
+ }
1100
+ return n;
1101
+ }
1102
+ function caseDirectiveKey(directives, typeValueToString5) {
1103
+ return directives.map((d) => {
1104
+ if (d.kind === "case") {
1105
+ const args = (d.args ?? []).map((a) => typeValueToString5(a)).join(",");
1106
+ return `c:${d.name ?? ""}:${args}`;
1107
+ }
1108
+ return d.kind;
1109
+ }).join("|");
1110
+ }
1111
+
1112
+ // src/bpath-run.ts
1113
+ function absSeedPart(a) {
1114
+ const impl = getFnImpl2(a);
1115
+ if (impl?.fingerprint) return impl.fingerprint;
1116
+ if (a.shape.k === "fn") {
1117
+ const ret = impl?.env?.vars?.get("__nudo_mock_ret");
1118
+ if (ret) {
1119
+ try {
1120
+ return `ret=${formatAbs2(ret)}`;
1121
+ } catch {
1122
+ return "ret=?";
1123
+ }
1124
+ }
1125
+ if (impl?.apply) return "apply";
1126
+ }
1127
+ try {
1128
+ return formatAbs2(a);
1129
+ } catch {
1130
+ return "?";
1131
+ }
1132
+ }
1133
+ function seedFnPart(name, fn) {
1134
+ if (fn.fingerprint) return `${name}:${fn.fingerprint}`;
1135
+ const body = fn.body;
1136
+ const bodyKey = body && typeof body.start === "number" && typeof body.end === "number" ? `${body.start}:${body.end}:${body.type ?? ""}` : String(body?.type ?? "?");
1137
+ return `${name}(${fn.params.join(",")})@${bodyKey}`;
1138
+ }
1139
+ function mockSeedFingerprint(mocks, seedFns) {
1140
+ const parts = [];
1141
+ if (mocks) {
1142
+ const names = Object.keys(mocks).sort();
1143
+ for (const n of names) parts.push(`${n}=${absSeedPart(mocks[n])}`);
1144
+ }
1145
+ if (seedFns) {
1146
+ const names = Object.keys(seedFns).sort();
1147
+ for (const n of names) parts.push(`fn:${seedFnPart(n, seedFns[n])}`);
1148
+ }
1149
+ if (parts.length === 0) return "-";
1150
+ return hashSource(parts.join(";"));
1151
+ }
1152
+ function collectEnvGlobals(envNames) {
1153
+ if (envNames.length === 0) return {};
1154
+ const env = createEnvironment();
1155
+ try {
1156
+ loadEnvs(envNames, env);
1157
+ } catch {
1158
+ return {};
1159
+ }
1160
+ const out = {};
1161
+ for (const [k, v] of Object.entries(env.getOwnBindings())) {
1162
+ out[k] = envValueToAbs(v);
1163
+ }
1164
+ return out;
1165
+ }
1166
+ function collectEnvModules(envNames) {
1167
+ if (envNames.length === 0) return {};
1168
+ const env = createEnvironment();
1169
+ let mods = {};
1170
+ try {
1171
+ mods = loadEnvs(envNames, env).modules;
1172
+ } catch {
1173
+ return {};
1174
+ }
1175
+ const out = {};
1176
+ for (const [spec, exports] of Object.entries(mods)) {
1177
+ const named = {};
1178
+ for (const [k, v] of Object.entries(exports)) {
1179
+ named[k] = envValueToAbs(v);
1180
+ }
1181
+ out[spec] = { named };
1182
+ }
1183
+ return out;
1184
+ }
1185
+ function collectBPathReplacements(source) {
1186
+ const targets = [];
1187
+ const values = {};
1188
+ const asTargets = [];
1189
+ const asValues = {};
1190
+ let i = 0;
1191
+ try {
1192
+ const file = parse4(source);
1193
+ const visitStmts = (stmts) => {
1194
+ for (const stmt of stmts) {
1195
+ if (!stmt || typeof stmt !== "object") continue;
1196
+ const loc = stmt.loc;
1197
+ const dirs = extractInlineDirectives(stmt);
1198
+ for (const d of dirs) {
1199
+ if (d.kind === "replace") {
1200
+ const varName = `__rep${i++}`;
1201
+ targets.push({
1202
+ target: d.targetSource,
1203
+ varName,
1204
+ stmtStart: loc?.start.line,
1205
+ stmtEnd: loc?.end.line
1206
+ });
1207
+ values[varName] = typeValueToAbs4(d.typeExpr);
1208
+ } else if (d.kind === "as" && loc) {
1209
+ const varName = `__as${i++}`;
1210
+ asTargets.push({
1211
+ varName,
1212
+ stmtStart: loc.start.line,
1213
+ stmtEnd: loc.end.line
1214
+ });
1215
+ asValues[varName] = typeValueToAbs4(d.typeExpr);
1216
+ }
1217
+ }
1218
+ const s = stmt;
1219
+ if (s.type === "BlockStatement" && Array.isArray(s.body)) visitStmts(s.body);
1220
+ if (s.type === "FunctionDeclaration" || s.type === "FunctionExpression") {
1221
+ if (s.body) visitStmts([s.body]);
1222
+ }
1223
+ if (s.type === "ExportNamedDeclaration" && s.declaration) {
1224
+ visitStmts([s.declaration]);
1225
+ }
1226
+ if (s.type === "IfStatement") {
1227
+ if (s.consequent) visitStmts([s.consequent]);
1228
+ if (s.alternate) visitStmts([s.alternate]);
1229
+ }
1230
+ }
1231
+ };
1232
+ visitStmts(file.program.body);
1233
+ } catch {
1234
+ }
1235
+ return { targets, values, asTargets, asValues };
1236
+ }
1237
+ function isBPathCapable(source, envNames = []) {
1238
+ void envNames;
1239
+ if (/(^|[^.\w$])this\s*\./.test(source) && !/\bclass\s+/.test(source)) return false;
1240
+ return true;
1241
+ }
1242
+ var bRunByFile = /* @__PURE__ */ new Map();
1243
+ var MAX_B_RUN_CACHE = 32;
1244
+ function clearBPathCache() {
1245
+ bRunByFile.clear();
1246
+ clearAnalysisFileCache();
1247
+ clearFnAnalysisCache();
1248
+ }
1249
+ function evictBPathCacheForFiles(files) {
1250
+ let n = 0;
1251
+ for (const f of files) {
1252
+ if (bRunByFile.delete(f)) n++;
1253
+ }
1254
+ return n;
1255
+ }
1256
+ function bPathCacheSet(filePath, stableSource, mode, envKey, mockKey, value) {
1257
+ if (bRunByFile.size >= MAX_B_RUN_CACHE && !bRunByFile.has(filePath)) {
1258
+ const oldest = bRunByFile.keys().next().value;
1259
+ if (oldest !== void 0) bRunByFile.delete(oldest);
1260
+ }
1261
+ bRunByFile.set(filePath, { stableSource, mode, envKey, mockKey, value });
1262
+ }
1263
+ function tryRunBPath(source, filePath, opts = {}) {
1264
+ if (!isBPathCapable(source, opts.envNames ?? [])) return void 0;
1265
+ const mode = opts.mode ?? "analyze";
1266
+ const envKey = (opts.envNames ?? []).join(",");
1267
+ const mockKey = mockSeedFingerprint(opts.mocks);
1268
+ const stable = stableAnalyzeKeySource(source);
1269
+ const cached = bRunByFile.get(filePath);
1270
+ if (cached && cached.stableSource === stable && cached.mode === mode && cached.envKey === envKey && cached.mockKey === mockKey) {
1271
+ bRunByFile.delete(filePath);
1272
+ bRunByFile.set(filePath, cached);
1273
+ return cached.value ?? void 0;
1274
+ }
1275
+ let out = null;
1276
+ try {
1277
+ const memberDiags = [];
1278
+ const truncated = /* @__PURE__ */ new Set();
1279
+ const topCalls = [];
1280
+ setMemberDiagCollector((d) => memberDiags.push(d));
1281
+ setAbsTruncationCollector((label) => truncated.add(label));
1282
+ setBCallCollector((r) => topCalls.push(r));
1283
+ try {
1284
+ const { modules: graphMods, issues } = evalAbsModuleGraph(source, filePath);
1285
+ const envMods = collectEnvModules(opts.envNames ?? []);
1286
+ const modules = { ...envMods, ...graphMods };
1287
+ const { targets, values, asTargets, asValues } = collectBPathReplacements(source);
1288
+ const envGlobals = {
1289
+ ...collectEnvGlobals(opts.envNames ?? []),
1290
+ ...opts.mocks ?? {}
1291
+ };
1292
+ const exports = runTranspiled(source, {
1293
+ modules,
1294
+ maxLoopIters: opts.maxLoopIters,
1295
+ mode: opts.mode ?? "analyze",
1296
+ replacementTargets: targets.length ? targets : void 0,
1297
+ replacements: targets.length ? values : void 0,
1298
+ asOverrideTargets: asTargets.length ? asTargets : void 0,
1299
+ asOverrides: asTargets.length ? asValues : void 0,
1300
+ envGlobals: Object.keys(envGlobals).length ? envGlobals : void 0
1301
+ });
1302
+ out = {
1303
+ exports,
1304
+ modules,
1305
+ memberDiags: memberDiags.length ? memberDiags : void 0,
1306
+ moduleIssues: issues.length ? issues : void 0,
1307
+ truncatedFns: truncated.size ? [...truncated] : void 0,
1308
+ calls: topCalls.length ? topCalls : void 0
1309
+ };
1310
+ } finally {
1311
+ setMemberDiagCollector(null);
1312
+ setAbsTruncationCollector(null);
1313
+ setBCallCollector(null);
1314
+ }
1315
+ } catch {
1316
+ out = null;
1317
+ }
1318
+ bPathCacheSet(filePath, stable, mode, envKey, mockKey, out);
1319
+ return out ?? void 0;
1320
+ }
1321
+ function tryBPathCallFull(source, filePath, fnName, args, opts = {}) {
1322
+ const run = tryRunBPath(source, filePath, { envNames: opts.envNames, mocks: opts.mocks });
1323
+ if (!run) return void 0;
1324
+ if (!(fnName in run.exports)) return void 0;
1325
+ const collected = [...run.calls ?? []];
1326
+ const memberDiags = [];
1327
+ if (opts.collectCalls) {
1328
+ setBCallCollector((r) => collected.push(r));
1329
+ }
1330
+ if (opts.collectMemberDiags ?? true) {
1331
+ setMemberDiagCollector((d) => memberDiags.push(d));
1332
+ }
1333
+ try {
1334
+ const full = callTranspiledExportFull(run.exports, fnName, args);
1335
+ const all = [...run.memberDiags ?? [], ...memberDiags];
1336
+ return {
1337
+ ...full,
1338
+ calls: opts.collectCalls || run.calls?.length ? collected : void 0,
1339
+ memberDiags: all.length ? all : void 0,
1340
+ moduleIssues: run.moduleIssues,
1341
+ truncatedFns: run.truncatedFns
1342
+ };
1343
+ } finally {
1344
+ if (opts.collectCalls) setBCallCollector(null);
1345
+ setMemberDiagCollector(null);
1346
+ }
1347
+ }
1348
+ function tryBPathCall(source, filePath, fnName, args, opts = {}) {
1349
+ const full = tryBPathCallFull(source, filePath, fnName, args, opts);
1350
+ if (!full) return void 0;
1351
+ const r = full.result;
1352
+ if (!r) return void 0;
1353
+ if (r.shape.k === "never" && full.throws.shape.k !== "never") {
1354
+ return void 0;
1355
+ }
1356
+ if (r.shape.k === "unknown" && !r.term) return void 0;
1357
+ return r;
1358
+ }
1359
+
1360
+ // src/bpath-diagnostics.ts
1361
+ import { parseSource } from "@nudojs/core";
1362
+ function locOf(node) {
1363
+ if (!node.loc) return null;
1364
+ return {
1365
+ start: { line: node.loc.start.line, column: node.loc.start.column },
1366
+ end: { line: node.loc.end.line, column: node.loc.end.column }
1367
+ };
1368
+ }
1369
+ function isExit(stmt) {
1370
+ return stmt.type === "ReturnStatement" || stmt.type === "ThrowStatement";
1371
+ }
1372
+ function walkUnreachable(node, out) {
1373
+ const n = node;
1374
+ const checkBlock = (block) => {
1375
+ if (!block) return;
1376
+ const body = block.body ?? (Array.isArray(block) ? block : null);
1377
+ if (!Array.isArray(body)) return;
1378
+ for (let i = 0; i < body.length; i++) {
1379
+ walkUnreachable(body[i], out);
1380
+ if (isExit(body[i])) {
1381
+ for (let j = i + 1; j < body.length; j++) {
1382
+ const r = locOf(body[j]);
1383
+ if (r) out.push({ range: r });
1384
+ }
1385
+ break;
1386
+ }
1387
+ }
1388
+ };
1389
+ switch (n.type) {
1390
+ case "Program":
1391
+ case "BlockStatement":
1392
+ checkBlock(n.body);
1393
+ break;
1394
+ case "FunctionDeclaration":
1395
+ case "FunctionExpression":
1396
+ case "ArrowFunctionExpression":
1397
+ if (n.body) walkUnreachable(n.body, out);
1398
+ break;
1399
+ case "IfStatement":
1400
+ if (n.consequent) walkUnreachable(n.consequent, out);
1401
+ if (n.alternate) walkUnreachable(n.alternate, out);
1402
+ break;
1403
+ case "ForStatement":
1404
+ case "ForInStatement":
1405
+ case "ForOfStatement":
1406
+ case "WhileStatement":
1407
+ case "DoWhileStatement":
1408
+ if (n.body) walkUnreachable(n.body, out);
1409
+ break;
1410
+ case "TryStatement":
1411
+ if (n.block) walkUnreachable(n.block, out);
1412
+ if (n.handler?.body) walkUnreachable(n.handler.body, out);
1413
+ if (n.finalizer) walkUnreachable(n.finalizer, out);
1414
+ break;
1415
+ case "SwitchStatement":
1416
+ for (const c of n.cases ?? []) {
1417
+ checkBlock(c.consequent);
1418
+ }
1419
+ break;
1420
+ case "ExportNamedDeclaration":
1421
+ case "ExportDefaultDeclaration": {
1422
+ const decl = n.declaration;
1423
+ if (decl) walkUnreachable(decl, out);
1424
+ break;
1425
+ }
1426
+ default:
1427
+ break;
1428
+ }
1429
+ }
1430
+ function collectDeclared(file) {
1431
+ const names = /* @__PURE__ */ new Set();
1432
+ const addId = (id) => {
1433
+ if (!id) return;
1434
+ if (id.type === "Identifier") names.add(id.name);
1435
+ if (id.type === "ObjectPattern") {
1436
+ for (const p of id.properties) {
1437
+ addId(p.value);
1438
+ }
1439
+ }
1440
+ if (id.type === "ArrayPattern") {
1441
+ for (const el of id.elements) addId(el);
1442
+ }
1443
+ };
1444
+ const visit = (node) => {
1445
+ if (!node || typeof node !== "object") return;
1446
+ const o = node;
1447
+ switch (o.type) {
1448
+ case "FunctionDeclaration":
1449
+ case "ClassDeclaration":
1450
+ addId(o.id);
1451
+ break;
1452
+ case "VariableDeclarator":
1453
+ addId(o.id);
1454
+ break;
1455
+ case "ImportSpecifier":
1456
+ case "ImportDefaultSpecifier":
1457
+ case "ImportNamespaceSpecifier":
1458
+ addId(o.local);
1459
+ break;
1460
+ default:
1461
+ break;
1462
+ }
1463
+ if (o.params && Array.isArray(o.params)) {
1464
+ for (const p of o.params) addId(p);
1465
+ }
1466
+ for (const key of Object.keys(o)) {
1467
+ if (key === "loc" || key === "start" || key === "end") continue;
1468
+ const v = o[key];
1469
+ if (Array.isArray(v)) v.forEach(visit);
1470
+ else if (v && typeof v === "object") visit(v);
1471
+ }
1472
+ };
1473
+ visit(file);
1474
+ return names;
1475
+ }
1476
+ var KNOWN_GLOBALS = /* @__PURE__ */ new Set([
1477
+ "Object",
1478
+ "Array",
1479
+ "String",
1480
+ "Number",
1481
+ "Boolean",
1482
+ "Symbol",
1483
+ "Math",
1484
+ "JSON",
1485
+ "Promise",
1486
+ "Error",
1487
+ "TypeError",
1488
+ "RangeError",
1489
+ "SyntaxError",
1490
+ "RegExp",
1491
+ "Date",
1492
+ "Map",
1493
+ "Set",
1494
+ "WeakMap",
1495
+ "WeakSet",
1496
+ "console",
1497
+ "undefined",
1498
+ "NaN",
1499
+ "Infinity",
1500
+ "globalThis",
1501
+ "parseInt",
1502
+ "parseFloat",
1503
+ "isNaN",
1504
+ "isFinite"
1505
+ ]);
1506
+ function isFreeUnknown(name, declared, seen, known) {
1507
+ return !declared.has(name) && !known.has(name) && !name.startsWith("$") && !seen.has(name);
1508
+ }
1509
+ function walkBuiltinUnknown(node, declared, out, seen, known, parentKey) {
1510
+ const n = node;
1511
+ const flag = (idNode, name) => {
1512
+ if (!isFreeUnknown(name, declared, seen, known)) return;
1513
+ const range = locOf(idNode) ?? locOf(node);
1514
+ if (range) {
1515
+ out.push({ name, range });
1516
+ seen.add(name);
1517
+ }
1518
+ };
1519
+ if (n.type === "CallExpression" && n.callee?.type === "Identifier") {
1520
+ flag(n.callee, n.callee.name);
1521
+ } else if (n.type === "Identifier" && typeof n.name === "string" && parentKey !== "key" && parentKey !== "property" && parentKey !== "local" && parentKey !== "id") {
1522
+ flag(node, n.name);
1523
+ }
1524
+ for (const key of Object.keys(n)) {
1525
+ if (key === "loc" || key === "start" || key === "end") continue;
1526
+ const v = n[key];
1527
+ if (Array.isArray(v)) {
1528
+ v.forEach((x) => {
1529
+ if (x && typeof x === "object" && "type" in x) {
1530
+ walkBuiltinUnknown(x, declared, out, seen, known, key);
1531
+ }
1532
+ });
1533
+ } else if (v && typeof v === "object" && "type" in v) {
1534
+ walkBuiltinUnknown(v, declared, out, seen, known, key);
1535
+ }
1536
+ }
1537
+ }
1538
+ function collectBPathDiagnostics(source, extraKnown) {
1539
+ let file;
1540
+ try {
1541
+ file = parseSource(source);
1542
+ } catch {
1543
+ return { unreachable: [], builtinUnknown: [] };
1544
+ }
1545
+ const unreachable = [];
1546
+ const root = file.program ?? file;
1547
+ walkUnreachable(root, unreachable);
1548
+ const declared = collectDeclared(file);
1549
+ const known = new Set(KNOWN_GLOBALS);
1550
+ if (extraKnown) {
1551
+ for (const n of extraKnown) known.add(n);
1552
+ }
1553
+ const builtinUnknown = [];
1554
+ walkBuiltinUnknown(file, declared, builtinUnknown, /* @__PURE__ */ new Set(), known);
1555
+ return { unreachable, builtinUnknown };
1556
+ }
1557
+
1558
+ // src/analyzer.ts
1559
+ import { setAbsTruncationCollector as setAbsTruncationCollector2 } from "@nudojs/core";
44
1560
  function resolveModule(source, fromDir) {
45
1561
  const extensions = [".js", ".ts", ".mjs"];
46
1562
  const nudoPath = resolveNpmNudo(source, fromDir);
47
1563
  if (nudoPath) {
48
- const src = readFileSync(nudoPath, "utf-8");
49
- return { ast: parse(src), filePath: nudoPath };
1564
+ const src = readFileSync4(nudoPath, "utf-8");
1565
+ return { ast: parse5(src), filePath: nudoPath };
50
1566
  }
51
- const basePath = resolve(fromDir, source);
1567
+ const basePath = resolve4(fromDir, source);
52
1568
  for (const ext of ["", ...extensions]) {
53
1569
  const candidate = basePath + ext;
54
- if (!existsSync(candidate)) continue;
55
- if (statSync(candidate).isDirectory()) {
1570
+ if (!existsSync3(candidate)) continue;
1571
+ if (statSync3(candidate).isDirectory()) {
56
1572
  let entry = null;
57
- const pkgPath = resolve(candidate, "package.json");
58
- if (existsSync(pkgPath)) {
1573
+ const pkgPath = resolve4(candidate, "package.json");
1574
+ if (existsSync3(pkgPath)) {
59
1575
  try {
60
- const main = JSON.parse(readFileSync(pkgPath, "utf-8")).main;
1576
+ const main = JSON.parse(readFileSync4(pkgPath, "utf-8")).main;
61
1577
  if (typeof main === "string") {
62
1578
  for (const e of ["", ...extensions]) {
63
- const p = resolve(candidate, main + e);
64
- if (existsSync(p) && statSync(p).isFile()) {
1579
+ const p = resolve4(candidate, main + e);
1580
+ if (existsSync3(p) && statSync3(p).isFile()) {
65
1581
  entry = p;
66
1582
  break;
67
1583
  }
@@ -72,34 +1588,34 @@ function resolveModule(source, fromDir) {
72
1588
  }
73
1589
  if (!entry) {
74
1590
  for (const e of ["", ...extensions]) {
75
- const p = resolve(candidate, "index" + e);
76
- if (existsSync(p) && statSync(p).isFile()) {
1591
+ const p = resolve4(candidate, "index" + e);
1592
+ if (existsSync3(p) && statSync3(p).isFile()) {
77
1593
  entry = p;
78
1594
  break;
79
1595
  }
80
1596
  }
81
1597
  }
82
1598
  if (!entry) return null;
83
- const src2 = readFileSync(entry, "utf-8");
84
- return { ast: parse(src2), filePath: entry };
1599
+ const src2 = readFileSync4(entry, "utf-8");
1600
+ return { ast: parse5(src2), filePath: entry };
85
1601
  }
86
1602
  if (candidate.endsWith(".json")) {
87
1603
  try {
88
- return { ast: parse("module.exports = undefined;"), filePath: candidate, json: JSON.parse(readFileSync(candidate, "utf-8")) };
1604
+ return { ast: parse5("module.exports = undefined;"), filePath: candidate, json: JSON.parse(readFileSync4(candidate, "utf-8")) };
89
1605
  } catch {
90
1606
  return null;
91
1607
  }
92
1608
  }
93
- const src = readFileSync(candidate, "utf-8");
94
- return { ast: parse(src), filePath: candidate };
1609
+ const src = readFileSync4(candidate, "utf-8");
1610
+ return { ast: parse5(src), filePath: candidate };
95
1611
  }
96
1612
  return null;
97
1613
  }
98
1614
  function resolveImportPath(specifier, fromDir) {
99
- const basePath = resolve(fromDir, specifier);
1615
+ const basePath = resolve4(fromDir, specifier);
100
1616
  for (const ext of ["", ".js", ".ts", ".mjs"]) {
101
1617
  const candidate = basePath + ext;
102
- if (existsSync(candidate)) return candidate;
1618
+ if (existsSync3(candidate)) return candidate;
103
1619
  }
104
1620
  return null;
105
1621
  }
@@ -119,7 +1635,7 @@ function buildModuleGraph(files, cache) {
119
1635
  if (cache) {
120
1636
  let stat = null;
121
1637
  try {
122
- stat = statSync(file);
1638
+ stat = statSync3(file);
123
1639
  } catch {
124
1640
  }
125
1641
  const cached = stat ? cache.get(file) : void 0;
@@ -140,7 +1656,7 @@ function extractImportEdges(file) {
140
1656
  const edges = [];
141
1657
  let ast;
142
1658
  try {
143
- ast = parse(readFileSync(file, "utf-8"));
1659
+ ast = parse5(readFileSync4(file, "utf-8"));
144
1660
  } catch {
145
1661
  return edges;
146
1662
  }
@@ -148,7 +1664,7 @@ function extractImportEdges(file) {
148
1664
  if (stmt.type !== "ImportDeclaration") continue;
149
1665
  const specifier = stmt.source.value;
150
1666
  if (!specifier.startsWith(".") && !specifier.startsWith("/")) continue;
151
- const resolved = resolveImportPath(specifier, dirname(file));
1667
+ const resolved = resolveImportPath(specifier, dirname5(file));
152
1668
  if (resolved) edges.push(resolved);
153
1669
  }
154
1670
  return edges;
@@ -201,7 +1717,7 @@ function applyMocks(directives, env, filePath, diagnostics) {
201
1717
  for (const d of directives) {
202
1718
  if (d.kind !== "mock") continue;
203
1719
  if (d.arrowFn) {
204
- const fnType = T.fn(d.arrowFn.params, d.arrowFn.body, env);
1720
+ const fnType = T2.fn(d.arrowFn.params, d.arrowFn.body, env);
205
1721
  fnType._paramPatterns = d.arrowFn.paramPatterns;
206
1722
  env.bind(d.name, fnType);
207
1723
  } else if (d.nudoMock) {
@@ -226,10 +1742,10 @@ function applyMocks(directives, env, filePath, diagnostics) {
226
1742
  }
227
1743
  env.bind(d.name, parseTypeValueExpr(d.expression));
228
1744
  } else if (d.fromPath) {
229
- const mockPath = resolve(dirname(filePath), d.fromPath);
230
- const mockSource = readFileSync(mockPath, "utf-8");
231
- const mockAst = parse(mockSource);
232
- const mockEnv = createEnvironment();
1745
+ const mockPath = resolve4(dirname5(filePath), d.fromPath);
1746
+ const mockSource = readFileSync4(mockPath, "utf-8");
1747
+ const mockAst = parse5(mockSource);
1748
+ const mockEnv = createEnvironment2();
233
1749
  evaluateProgram(mockAst, mockEnv);
234
1750
  const mockVal = mockEnv.lookup(d.name);
235
1751
  env.bind(d.name, mockVal);
@@ -239,19 +1755,19 @@ function applyMocks(directives, env, filePath, diagnostics) {
239
1755
  function createSinonTypeValue(sinonExpr, env) {
240
1756
  if (sinonExpr.type === "stub" || sinonExpr.type === "spy") {
241
1757
  const body = { type: "BlockStatement", body: [] };
242
- const fn = T.fn(["...args"], body, env);
1758
+ const fn = T2.fn(["...args"], body, env);
243
1759
  if (sinonExpr.returnValue) {
244
1760
  fn._directReturn = sinonExpr.returnValue;
245
1761
  } else if (sinonExpr.resolvedValue) {
246
- fn._directReturn = T.promise(sinonExpr.resolvedValue);
1762
+ fn._directReturn = T2.promise(sinonExpr.resolvedValue);
247
1763
  } else if (sinonExpr.rejectedValue) {
248
- fn._directReturn = T.never;
1764
+ fn._directReturn = T2.never;
249
1765
  } else {
250
- fn._directReturn = T.unknown;
1766
+ fn._directReturn = T2.unknown;
251
1767
  }
252
1768
  return fn;
253
1769
  }
254
- return T.unknown;
1770
+ return T2.unknown;
255
1771
  }
256
1772
  function locFromNode(node) {
257
1773
  return {
@@ -516,7 +2032,7 @@ function synthesizeExternalFunctions(records, currentFile) {
516
2032
  (_, i) => (
517
2033
  // 缺参按真实 JS 语义 widen 成 undefined 而非 unknown——可选参守卫
518
2034
  // (target || [] 等)对 unknown 全塌,对 undefined 正常走默认分支
519
- widenType(simplifyUnion(remaining.map((rec) => rec.argTypes[i] ?? T.undefined)))
2035
+ widenType(simplifyUnion(remaining.map((rec) => rec.argTypes[i] ?? T2.undefined)))
520
2036
  )
521
2037
  ),
522
2038
  result: collapseLiteralUnion(simplifyUnion(remaining.map((r) => r.resultType)), COLLAPSE_LITERAL_THRESHOLD),
@@ -548,7 +2064,7 @@ function receiverTypeToDisplay(tv) {
548
2064
  case "union":
549
2065
  return tv.members.map(receiverTypeToDisplay).join(" | ");
550
2066
  default:
551
- return typeValueToString(tv);
2067
+ return typeValueToString3(tv);
552
2068
  }
553
2069
  }
554
2070
  function receiverIsConcrete(tv) {
@@ -645,34 +2161,34 @@ function unknownRecordsToDiagnostics(records) {
645
2161
  return out;
646
2162
  }
647
2163
  function collectEnvNames(filePath, source, includeProject) {
648
- const ast = parse(source);
2164
+ const ast = parse5(source);
649
2165
  const fileDirectives = extractFileDirectives(ast);
650
2166
  const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
651
2167
  if (!includeProject) return fileEnvNames;
652
- const projectConfig = findProjectConfig(dirname(filePath));
2168
+ const projectConfig = findProjectConfig(dirname5(filePath));
653
2169
  const projectEnvNames = projectConfig?.config.env ?? [];
654
2170
  return [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
655
2171
  }
656
2172
  async function analyzeFileAsync(filePath, source, activeCases, externalCallRecords) {
657
2173
  const envNames = collectEnvNames(filePath, source, true);
658
2174
  if (envNames.length > 0) {
659
- await preloadPathEnvs(envNames, dirname(filePath));
2175
+ await preloadPathEnvs(envNames, dirname5(filePath));
660
2176
  }
661
2177
  return analyzeFile(filePath, source, activeCases, externalCallRecords);
662
2178
  }
663
2179
  function collectCallRecords(filePath, source) {
664
2180
  const records = [];
665
- const ast = parse(source, { errorRecovery: true });
2181
+ const ast = parse5(source, { errorRecovery: true });
666
2182
  resetMemo();
667
2183
  resetUnreachableRanges();
668
2184
  resetEnvModules();
669
2185
  resetMockModules();
670
2186
  setModuleResolver(resolveModule);
671
- setCurrentFileDir(dirname(filePath));
2187
+ setCurrentFileDir(dirname5(filePath));
672
2188
  setCurrentSource(source);
673
2189
  setCallCollector((record) => records.push(record));
674
2190
  try {
675
- const env = createEnvironment();
2191
+ const env = createEnvironment2();
676
2192
  evaluateProgram(ast, env);
677
2193
  const runCallbacks = (statements) => {
678
2194
  for (const stmt of statements) {
@@ -688,7 +2204,7 @@ function collectCallRecords(filePath, source) {
688
2204
  if (!cb) continue;
689
2205
  try {
690
2206
  const params = cb.params ?? [];
691
- evaluateFunction(cb, params.map(() => T.unknown), env);
2207
+ evaluateFunction(cb, params.map(() => T2.unknown), env);
692
2208
  } catch {
693
2209
  }
694
2210
  if (name === "describe" && cb.body.type === "BlockStatement") {
@@ -706,11 +2222,88 @@ function collectCallRecords(filePath, source) {
706
2222
  return records;
707
2223
  }
708
2224
  var TEST_CALLBACK_NAMES = /* @__PURE__ */ new Set(["it", "test", "describe"]);
2225
+ var externalRecordIds = /* @__PURE__ */ new WeakMap();
2226
+ var nextExternalRecordId = 1;
2227
+ function analysisFileCacheKey(filePath, source, activeCases, externalCallRecords) {
2228
+ let cases = "-";
2229
+ if (activeCases && activeCases.size > 0) {
2230
+ cases = [...activeCases.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([k, v]) => `${k}=${v}`).join(",");
2231
+ }
2232
+ let ext = "-";
2233
+ if (externalCallRecords && externalCallRecords.length > 0) {
2234
+ let id = externalRecordIds.get(externalCallRecords);
2235
+ if (id === void 0) {
2236
+ id = nextExternalRecordId++;
2237
+ externalRecordIds.set(externalCallRecords, id);
2238
+ }
2239
+ ext = `n${externalCallRecords.length}#id${id}`;
2240
+ }
2241
+ return {
2242
+ filePath,
2243
+ // 尾部无 @nudo 注释/空行不进键:comment-only 编辑命中 AnalysisResult
2244
+ source: stableAnalyzeKeySource2(source),
2245
+ auxKey: `${cases}\0${ext}`
2246
+ };
2247
+ }
2248
+ function cloneAnalysisResult(r) {
2249
+ return {
2250
+ functions: r.functions.map(cloneFunctionAnalysis),
2251
+ diagnostics: r.diagnostics.map((d) => ({ ...d })),
2252
+ bindings: new Map(r.bindings),
2253
+ // Node 键与 AST LRU 共享身份;Map 浅拷贝即可
2254
+ nodeTypeMap: new Map(r.nodeTypeMap),
2255
+ caseHints: r.caseHints.map((h) => ({ ...h })),
2256
+ ...r.externalFunctions ? { externalFunctions: r.externalFunctions.map(cloneFunctionAnalysis) } : {}
2257
+ };
2258
+ }
2259
+ function cloneFunctionAnalysis(a) {
2260
+ return {
2261
+ ...a,
2262
+ paramNames: [...a.paramNames],
2263
+ cases: a.cases.map((c) => ({
2264
+ ...c,
2265
+ args: [...c.args],
2266
+ ...c.intension ? { intension: { ...c.intension } } : {}
2267
+ })),
2268
+ loc: { start: { ...a.loc.start }, end: { ...a.loc.end } }
2269
+ };
2270
+ }
2271
+ function shiftSourceLoc(loc, lineDelta) {
2272
+ return {
2273
+ start: { line: loc.start.line + lineDelta, column: loc.start.column },
2274
+ end: { line: loc.end.line + lineDelta, column: loc.end.column }
2275
+ };
2276
+ }
2277
+ function shiftDiagnosticLines(d, lineDelta) {
2278
+ if (lineDelta === 0) return { ...d };
2279
+ const out = { ...d, range: shiftSourceLoc(d.range, lineDelta) };
2280
+ if (d.origin) {
2281
+ out.origin = { line: d.origin.line + lineDelta, column: d.origin.column };
2282
+ }
2283
+ return out;
2284
+ }
2285
+ function shiftCallRecordLines(r, lineDelta) {
2286
+ if (lineDelta === 0 || !r.callLoc) return { ...r };
2287
+ return {
2288
+ ...r,
2289
+ callLoc: { line: r.callLoc.line + lineDelta, column: r.callLoc.column }
2290
+ };
2291
+ }
709
2292
  function analyzeFile(filePath, source, activeCases, externalCallRecords) {
2293
+ const k = analysisFileCacheKey(filePath, source, activeCases, externalCallRecords);
2294
+ const hit = analysisCacheGet(k.filePath, k.source, k.auxKey);
2295
+ if (hit !== void 0) {
2296
+ return cloneAnalysisResult(hit);
2297
+ }
2298
+ const result = analyzeFileUncached(filePath, source, activeCases, externalCallRecords);
2299
+ analysisCacheSet(k.filePath, k.source, k.auxKey, result);
2300
+ return cloneAnalysisResult(result);
2301
+ }
2302
+ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords) {
710
2303
  for (const rec of externalCallRecords ?? []) {
711
2304
  for (const a of rec.argTypes) setUsageSiteTag(a);
712
2305
  }
713
- const ast = parse(source);
2306
+ const ast = parse5(source);
714
2307
  const functions = extractDirectives(ast);
715
2308
  const diagnostics = [];
716
2309
  const bindings = /* @__PURE__ */ new Map();
@@ -722,14 +2315,15 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
722
2315
  resetEnvModules();
723
2316
  resetMockModules();
724
2317
  setModuleResolver(resolveModule);
725
- setCurrentFileDir(dirname(filePath));
2318
+ setCurrentFileDir(dirname5(filePath));
726
2319
  setCurrentSource(source);
727
2320
  const fileDirectives = extractFileDirectives(ast);
728
2321
  const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
729
- const projectConfig = findProjectConfig(dirname(filePath));
2322
+ const projectConfig = findProjectConfig(dirname5(filePath));
730
2323
  const projectEnvNames = projectConfig?.config.env ?? [];
731
2324
  const envNames = [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
732
2325
  setUnknownBuiltinHandler((name, loc) => {
2326
+ if (isBPathCapable(source, envNames)) return;
733
2327
  diagnostics.push({
734
2328
  range: loc ? { start: loc.start, end: loc.end } : { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } },
735
2329
  severity: "warning",
@@ -737,19 +2331,21 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
737
2331
  code: "nudo:builtin-unknown",
738
2332
  suggestions: [
739
2333
  `Use @nudo:mock to define the type: @nudo:mock ${name} = stub().returns(...)`,
740
- `Or use @nudo:returns to declare the expected return type`
2334
+ `Or use @nudo:refine return <constraint> to declare the return contract`
741
2335
  ]
742
2336
  });
743
2337
  });
744
- const globalEnv = createEnvironment();
745
- if (envNames.length > 0) {
746
- const loaded = loadEnvs(envNames, globalEnv);
747
- setEnvModules(loaded.modules);
2338
+ const globalEnv = createEnvironment2();
2339
+ {
2340
+ const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
2341
+ const auto = autoHarvestModules(source, dirname5(filePath));
2342
+ const modules = { ...loaded.modules, ...auto };
2343
+ if (Object.keys(modules).length > 0) setEnvModules(modules);
748
2344
  }
749
2345
  const mocks = /* @__PURE__ */ new Map();
750
2346
  if (projectConfig?.config.mocks) {
751
2347
  for (const [source2, mockPath] of Object.entries(projectConfig.config.mocks)) {
752
- mocks.set(source2, { fromPath: resolve(projectConfig.projectDir, mockPath) });
2348
+ mocks.set(source2, { fromPath: resolve4(projectConfig.projectDir, mockPath) });
753
2349
  }
754
2350
  }
755
2351
  const mockModuleDirectives = fileDirectives.filter((d) => d.kind === "mock-module");
@@ -764,27 +2360,225 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
764
2360
  const unknownRecords = [];
765
2361
  setUnknownCollector((r) => unknownRecords.push(r));
766
2362
  setProvenanceTracking(true);
2363
+ const bMemberDiagNames = /* @__PURE__ */ new Set();
2364
+ const bMemberDiagSeen = /* @__PURE__ */ new Set();
2365
+ const pushBMemberDiag = (d, fallbackLine) => {
2366
+ bMemberDiagNames.add(d.name);
2367
+ const key = `${d.kind}:${d.name}:${d.receiver}:${d.line ?? fallbackLine}:${d.column ?? 0}`;
2368
+ if (bMemberDiagSeen.has(key)) return;
2369
+ bMemberDiagSeen.add(key);
2370
+ if (d.receiver === "unknown") {
2371
+ diagnostics.push({
2372
+ range: {
2373
+ start: { line: d.line ?? fallbackLine, column: d.column ?? 0 },
2374
+ end: { line: d.line ?? fallbackLine, column: (d.column ?? 0) + d.name.length }
2375
+ },
2376
+ severity: "warning",
2377
+ message: `Cannot resolve '${d.name}' on unknown value`,
2378
+ code: "nudo:unknown-recv",
2379
+ ...d.origin ? { origin: d.origin } : {}
2380
+ });
2381
+ return;
2382
+ }
2383
+ diagnostics.push({
2384
+ range: {
2385
+ start: { line: d.line ?? fallbackLine, column: d.column ?? 0 },
2386
+ end: { line: d.line ?? fallbackLine, column: (d.column ?? 0) + d.name.length }
2387
+ },
2388
+ severity: d.receiver === "number" || d.receiver === "boolean" || d.receiver === "bigint" || d.receiver === "symbol" ? "error" : "warning",
2389
+ message: d.kind === "method" ? `Method '${d.name}' does not exist on type '${d.receiver}'` : `Property '${d.name}' does not exist on type '${d.receiver}'`,
2390
+ code: "nudo:no-method",
2391
+ ...d.origin ? { origin: d.origin } : {}
2392
+ });
2393
+ };
767
2394
  for (const fn of functions) {
768
2395
  applyMocks(fn.directives, globalEnv, filePath, diagnostics);
769
2396
  }
770
- evaluateProgram(ast, globalEnv);
771
- const unreachableRanges = getUnreachableRanges();
772
- for (const ur of unreachableRanges) {
773
- diagnostics.push({
774
- range: ur,
775
- severity: "info",
776
- message: "Code after return/throw statement is unreachable",
777
- tags: ["unnecessary"],
778
- code: "nudo-unreachable",
779
- suggestions: ["Remove the unreachable code after the return/throw statement"]
2397
+ const selfContained = isSelfContainedSource(source, envNames);
2398
+ const canAbsModules = absModulesOk(source, envNames);
2399
+ const bCapable = isBPathCapable(source, envNames);
2400
+ const bModuleIssueKinds = /* @__PURE__ */ new Set();
2401
+ const bTruncatedFns = /* @__PURE__ */ new Set();
2402
+ const bBuiltinUnknownNames = /* @__PURE__ */ new Set();
2403
+ let bHostedEval = false;
2404
+ const pushBModuleIssues = (issues) => {
2405
+ if (!issues) return;
2406
+ for (const iss of issues) {
2407
+ bModuleIssueKinds.add(iss.kind);
2408
+ const code = iss.kind === "cycle" ? "nudo:module-cycle" : iss.kind === "depth" ? "nudo:module-depth" : "nudo:module-missing";
2409
+ diagnostics.push({
2410
+ range: { start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
2411
+ severity: iss.kind === "missing" ? "error" : "warning",
2412
+ message: iss.reason,
2413
+ code
2414
+ });
2415
+ }
2416
+ };
2417
+ const seeds = mockDirectivesToAbsSeeds(functions);
2418
+ let absCallRecords = [];
2419
+ let bTopCallRecords = [];
2420
+ let absGraphModules;
2421
+ let absBindsShared;
2422
+ let absNodesShared;
2423
+ if (bCapable && filePath) {
2424
+ try {
2425
+ const g = evalAbsModuleGraph(source, filePath, {
2426
+ seedVars: seeds.seedVars,
2427
+ seedFns: seeds.seedFns
2428
+ });
2429
+ absGraphModules = g.modules;
2430
+ pushBModuleIssues(g.issues);
2431
+ } catch {
2432
+ }
2433
+ const bRun = tryRunBPath(source, filePath, {
2434
+ envNames,
2435
+ mocks: mockSeedsToAbsMocks(seeds)
780
2436
  });
2437
+ if (bRun) {
2438
+ bHostedEval = true;
2439
+ if (bRun.memberDiags?.length) {
2440
+ for (const d of bRun.memberDiags) {
2441
+ pushBMemberDiag(d, 1);
2442
+ }
2443
+ }
2444
+ if (bRun.truncatedFns) {
2445
+ for (const fn of bRun.truncatedFns) bTruncatedFns.add(fn);
2446
+ }
2447
+ if (bRun.calls?.length) {
2448
+ const impMap = buildAbsImportLocalMap(source, filePath);
2449
+ for (const c of bRun.calls) {
2450
+ const safeAbs = (a) => {
2451
+ if (!a || typeof a !== "object" || !("shape" in a) || !a.shape) {
2452
+ return T2.unknown;
2453
+ }
2454
+ try {
2455
+ return absToTypeValue2(a);
2456
+ } catch {
2457
+ return T2.unknown;
2458
+ }
2459
+ };
2460
+ const rec = {
2461
+ fnName: c.fnName,
2462
+ argTypes: c.args.map(safeAbs),
2463
+ resultType: c.threw ? T2.never : safeAbs(c.result),
2464
+ throws: c.threw ? safeAbs(c.result) : T2.never,
2465
+ callLoc: c.callLoc
2466
+ };
2467
+ const imp = impMap.get(c.fnName);
2468
+ if (imp) {
2469
+ rec.targetModule = imp.modulePath;
2470
+ rec.targetExport = imp.exportName;
2471
+ }
2472
+ bTopCallRecords.push(rec);
2473
+ }
2474
+ }
2475
+ }
2476
+ }
2477
+ if (selfContained || canAbsModules) {
2478
+ setAbsTruncationCollector2((label) => bTruncatedFns.add(label));
2479
+ try {
2480
+ absCallRecords = collectAbsCallRecords(source, seeds, filePath, absGraphModules);
2481
+ } finally {
2482
+ setAbsTruncationCollector2(null);
2483
+ }
2484
+ }
2485
+ if (bHostedEval) {
2486
+ try {
2487
+ const collected = collectAbsBindsAndNodes(source, seeds, absGraphModules);
2488
+ absBindsShared = collected.binds;
2489
+ absNodesShared = collected.nodes;
2490
+ for (const [name, absVal] of absBindsShared) {
2491
+ if (absVal?.shape?.k === "fn") continue;
2492
+ if (!globalEnv.has(name)) {
2493
+ globalEnv.bind(name, absToTypeValue2(absVal));
2494
+ }
2495
+ }
2496
+ for (const [node, absVal] of absNodesShared) {
2497
+ nodeTypeMap.set(node, absToTypeValue2(absVal));
2498
+ }
2499
+ } catch {
2500
+ }
2501
+ } else {
2502
+ evaluateProgram(ast, globalEnv);
2503
+ }
2504
+ if ((selfContained || canAbsModules) && absCallRecords.length > 0) {
2505
+ callRecords.length = 0;
2506
+ callRecords.push(...absCallRecords);
2507
+ } else if (bTopCallRecords.length > 0) {
2508
+ callRecords.length = 0;
2509
+ callRecords.push(...bTopCallRecords);
2510
+ }
2511
+ const unreachableRanges = bCapable ? [] : getUnreachableRanges();
2512
+ if (bCapable) {
2513
+ const bKnownGlobals = /* @__PURE__ */ new Set([
2514
+ ...Object.keys(collectEnvGlobals(envNames)),
2515
+ ...Object.keys(mockSeedsToAbsMocks(seeds))
2516
+ ]);
2517
+ const bDiag = collectBPathDiagnostics(source, bKnownGlobals);
2518
+ for (const ur of bDiag.unreachable) {
2519
+ diagnostics.push({
2520
+ range: ur.range,
2521
+ severity: "info",
2522
+ message: "Code after return/throw statement is unreachable",
2523
+ tags: ["unnecessary"],
2524
+ code: "nudo-unreachable",
2525
+ suggestions: ["Remove the unreachable code after the return/throw statement"]
2526
+ });
2527
+ }
2528
+ for (const b of bDiag.builtinUnknown) {
2529
+ bBuiltinUnknownNames.add(b.name);
2530
+ diagnostics.push({
2531
+ range: b.range,
2532
+ severity: "warning",
2533
+ message: `Built-in API "${b.name}" is not covered by Nudo's type inference`,
2534
+ code: "nudo:builtin-unknown",
2535
+ suggestions: [
2536
+ `Use @nudo:mock to define the type: @nudo:mock ${b.name} = stub().returns(...)`,
2537
+ `Or use @nudo:refine return <constraint> to declare the return contract`
2538
+ ]
2539
+ });
2540
+ }
2541
+ } else {
2542
+ for (const ur of unreachableRanges) {
2543
+ diagnostics.push({
2544
+ range: ur,
2545
+ severity: "info",
2546
+ message: "Code after return/throw statement is unreachable",
2547
+ tags: ["unnecessary"],
2548
+ code: "nudo-unreachable",
2549
+ suggestions: ["Remove the unreachable code after the return/throw statement"]
2550
+ });
2551
+ }
781
2552
  }
782
2553
  collectBindings(ast, globalEnv, bindings);
2554
+ if (isBPathCapable(source, envNames)) {
2555
+ try {
2556
+ const absBinds = absBindsShared ?? collectAbsBindingsFromGraph(source, filePath, {
2557
+ seedVars: seeds.seedVars,
2558
+ seedFns: seeds.seedFns
2559
+ });
2560
+ for (const [name, absVal] of absBinds) {
2561
+ const prev = bindings.get(name);
2562
+ bindings.set(name, {
2563
+ type: absToTypeValue2(absVal),
2564
+ loc: prev?.loc
2565
+ });
2566
+ }
2567
+ } catch {
2568
+ }
2569
+ }
783
2570
  const synthCandidates = [];
2571
+ let fnFpMap;
2572
+ try {
2573
+ fnFpMap = fnFingerprints(source, ast);
2574
+ } catch {
2575
+ fnFpMap = void 0;
2576
+ }
2577
+ const envKeyFn = envNames.join(",");
2578
+ const mockKeyFn = mockSeedFingerprint(seeds.seedVars, seeds.seedFns);
784
2579
  for (const fn of functions) {
785
2580
  const isPure = fn.directives.some((d) => d.kind === "pure");
786
2581
  const skipDirective = fn.directives.find((d) => d.kind === "skip");
787
- const returnsDirective = fn.directives.find((d) => d.kind === "returns");
788
2582
  const fnLoc = locFromNode(fn.node);
789
2583
  const paramNames = extractParamNames(fn.node);
790
2584
  const analysis = { name: fn.name, loc: fnLoc, paramNames, cases: [] };
@@ -796,6 +2590,51 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
796
2590
  functionResults.push(analysis);
797
2591
  continue;
798
2592
  }
2593
+ const caseDirectives = fn.directives.filter((d) => d.kind === "case");
2594
+ const activeCaseIdx = activeCases?.get(fn.name) ?? 0;
2595
+ const fp = fnFpMap?.get(fn.name);
2596
+ const fnCacheKey = fp && caseDirectives.length > 0 ? [
2597
+ filePath,
2598
+ fp.own,
2599
+ fp.deps,
2600
+ String(activeCaseIdx),
2601
+ caseDirectiveKey(caseDirectives, typeValueToString3),
2602
+ envKeyFn,
2603
+ mockKeyFn
2604
+ ].join("\0") : void 0;
2605
+ const dLen0 = diagnostics.length;
2606
+ const hLen0 = caseHints.length;
2607
+ const cLen0 = callRecords.length;
2608
+ if (fnCacheKey) {
2609
+ const hitFn = fnAnalysisCacheGet(fnCacheKey);
2610
+ if (hitFn) {
2611
+ const cached = cloneFunctionAnalysis(hitFn.analysis);
2612
+ const lineDelta = fnLoc.start.line - cached.loc.start.line;
2613
+ cached.loc = fnLoc;
2614
+ if (lineDelta !== 0) {
2615
+ for (const c of cached.cases) {
2616
+ if (c.throwLoc) c.throwLoc = shiftSourceLoc(c.throwLoc, lineDelta);
2617
+ }
2618
+ }
2619
+ if (lineDelta === 0) {
2620
+ diagnostics.push(...hitFn.diagnostics);
2621
+ caseHints.push(...hitFn.caseHints);
2622
+ callRecords.push(...hitFn.callRecords);
2623
+ } else {
2624
+ for (const d of hitFn.diagnostics) {
2625
+ diagnostics.push(shiftDiagnosticLines(d, lineDelta));
2626
+ }
2627
+ for (const h of hitFn.caseHints) {
2628
+ caseHints.push({ ...h, line: h.line + lineDelta });
2629
+ }
2630
+ for (const r of hitFn.callRecords) {
2631
+ callRecords.push(shiftCallRecordLines(r, lineDelta));
2632
+ }
2633
+ }
2634
+ functionResults.push(cached);
2635
+ continue;
2636
+ }
2637
+ }
799
2638
  if (isPure) {
800
2639
  const fnVal = globalEnv.has(fn.name) ? globalEnv.lookup(fn.name) : null;
801
2640
  if (fnVal && fnVal.kind === "function") {
@@ -808,37 +2647,114 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
808
2647
  } else {
809
2648
  setSampleCount(3);
810
2649
  }
811
- const caseDirectives = fn.directives.filter((d) => d.kind === "case");
812
- const activeCaseIdx = activeCases?.get(fn.name) ?? 0;
813
2650
  if (caseDirectives.length === 0) {
814
2651
  synthCandidates.push({ name: fn.name, node: fn.node, analysis });
815
2652
  }
816
2653
  for (let ci = 0; ci < caseDirectives.length; ci++) {
817
2654
  const directive = caseDirectives[ci];
818
2655
  resetUnreachableRanges();
819
- const fullResult = evaluateFunctionFull(fn.node, directive.args, globalEnv);
820
- const caseUnreachable = [...getUnreachableRanges()];
821
- analysis.cases.push({
2656
+ let caseValue;
2657
+ let caseAbs;
2658
+ let caseThrows;
2659
+ let fullResult = null;
2660
+ let caseUnreachable = [];
2661
+ const bCapable2 = isBPathCapable(source, envNames);
2662
+ const bPrimary = bCapable2;
2663
+ if (bCapable2 && filePath) {
2664
+ const bFull = tryBPathCallFull(
2665
+ source,
2666
+ filePath,
2667
+ fn.name,
2668
+ directive.args.map((a) => typeValueToAbs5(a)),
2669
+ { collectCalls: true, envNames, mocks: mockSeedsToAbsMocks(seeds) }
2670
+ );
2671
+ const res = bFull?.result;
2672
+ const weakUnknown = !!res && res.shape.k === "unknown" && (!res.term || res.term.op === "lit" && res.term.value === void 0);
2673
+ const bOk = !!res && (bHostedEval || !weakUnknown && res.conf !== "opaque");
2674
+ if (bOk && bFull && bPrimary) {
2675
+ caseAbs = bFull.result;
2676
+ caseValue = absToTypeValue2(bFull.result);
2677
+ caseThrows = bFull.throws.shape.k !== "never" ? absToTypeValue2(bFull.throws) : T2.never;
2678
+ fullResult = { value: caseValue, throws: caseThrows };
2679
+ for (const d of bFull.memberDiags ?? []) {
2680
+ pushBMemberDiag(d, fnLoc.start.line);
2681
+ }
2682
+ if (bFull.calls?.length) {
2683
+ const impMap = buildAbsImportLocalMap(source, filePath);
2684
+ const safeAbs = (a) => {
2685
+ if (!a || typeof a !== "object" || !("shape" in a) || !a.shape) {
2686
+ return T2.unknown;
2687
+ }
2688
+ try {
2689
+ return absToTypeValue2(a);
2690
+ } catch {
2691
+ return T2.unknown;
2692
+ }
2693
+ };
2694
+ for (const c of bFull.calls) {
2695
+ const rec = {
2696
+ fnName: c.fnName,
2697
+ argTypes: c.args.map(safeAbs),
2698
+ resultType: c.threw ? T2.never : safeAbs(c.result),
2699
+ throws: c.threw ? safeAbs(c.result) : T2.never,
2700
+ callLoc: c.callLoc
2701
+ };
2702
+ const imp = impMap.get(c.fnName);
2703
+ if (imp) {
2704
+ rec.targetModule = imp.modulePath;
2705
+ rec.targetExport = imp.exportName;
2706
+ }
2707
+ callRecords.push(rec);
2708
+ }
2709
+ }
2710
+ }
2711
+ }
2712
+ if (!fullResult) {
2713
+ if (bHostedEval) {
2714
+ fullResult = { value: T2.unknown, throws: T2.never };
2715
+ caseValue = T2.unknown;
2716
+ } else {
2717
+ fullResult = evaluateFunctionFull(fn.node, directive.args, globalEnv);
2718
+ caseUnreachable = [...getUnreachableRanges()];
2719
+ caseValue = fullResult.value;
2720
+ if ((selfContained || canAbsModules) && fullResult.value.kind !== "never") {
2721
+ caseAbs = tryEvalAbsRaw(source, fn.name, directive.args, filePath, mockSeedsToAbsMocks(seeds));
2722
+ if (caseAbs) {
2723
+ const projected = absToTypeValue2(caseAbs);
2724
+ if (absIsBetter(projected, fullResult.value)) caseValue = projected;
2725
+ }
2726
+ }
2727
+ }
2728
+ }
2729
+ const tv = caseValue;
2730
+ const throwsTv = caseThrows ?? fullResult.throws;
2731
+ const caseEntry = {
822
2732
  name: directive.name,
823
2733
  args: directive.args,
824
- result: fullResult.value,
825
- throws: fullResult.throws,
826
- throwLoc: fullResult.throwLoc
827
- });
2734
+ result: tv,
2735
+ throws: throwsTv,
2736
+ throwLoc: fullResult.throwLoc,
2737
+ expected: directive.expected,
2738
+ source: "directive"
2739
+ };
2740
+ tryAttachIntension(caseEntry, source, fn.name);
2741
+ if (caseAbs) attachAbsToIntension(caseEntry, caseAbs, fn.name);
2742
+ analysis.cases.push(caseEntry);
828
2743
  if (directive.commentLine) {
829
- const hasThrow = fullResult.throws.kind !== "never";
830
- const resultStr = fullResult.value.kind !== "never" ? typeValueToString(fullResult.value) : "";
831
- const throwStr = hasThrow ? `throws ${typeValueToString(fullResult.throws)}` : "";
2744
+ const hasThrow = throwsTv.kind !== "never";
2745
+ const resultStr = tv.kind !== "never" ? typeValueToString3(tv) : "";
2746
+ const throwStr = hasThrow ? `throws ${typeValueToString3(throwsTv)}` : "";
832
2747
  const label = [resultStr, throwStr].filter(Boolean).join(" ");
833
2748
  const hintLabel = `=> ${label}`;
834
2749
  let ok = true;
835
2750
  if (directive.expected) {
836
- ok = isSubtypeOf(fullResult.value, directive.expected);
2751
+ ok = isSubtypeOf(tv, directive.expected);
837
2752
  if (!ok) {
838
2753
  diagnostics.push({
839
2754
  range: { start: { line: directive.commentLine, column: 0 }, end: { line: directive.commentLine, column: 999 } },
840
2755
  severity: "error",
841
- message: `Case "${directive.name}": expected ${typeValueToString(directive.expected)}, got ${typeValueToString(fullResult.value)}. The inferred return type does not match the expected type declared in the @nudo:case directive`
2756
+ message: `Case "${directive.name}": expected ${typeValueToString3(directive.expected)}, got ${typeValueToString3(tv)}. The inferred return type does not match the expected type declared in the @nudo:case directive`,
2757
+ code: "nudo:case-expected"
842
2758
  });
843
2759
  }
844
2760
  }
@@ -846,24 +2762,26 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
846
2762
  }
847
2763
  const isActive = ci === Math.min(activeCaseIdx, caseDirectives.length - 1);
848
2764
  if (isActive) {
849
- if (fullResult.throws.kind !== "never") {
850
- const throwRange = fullResult.throwLoc ?? fnLoc;
2765
+ if (throwsTv.kind !== "never") {
2766
+ const throwRange = fullResult?.throwLoc ?? fnLoc;
851
2767
  diagnostics.push({
852
2768
  range: throwRange,
853
2769
  severity: "warning",
854
- message: `Function "${fn.name}" case "${directive.name}" may throw: ${typeValueToString(fullResult.throws)}. Consider adding a try-catch block or using @nudo:returns to declare expected behavior`,
2770
+ message: `Function "${fn.name}" case "${directive.name}" may throw: ${typeValueToString3(throwsTv)}. Consider adding a try-catch block or using @nudo:refine return <constraint>`,
855
2771
  code: "nudo-may-throw"
856
2772
  });
857
2773
  }
858
- for (const ur of caseUnreachable) {
859
- diagnostics.push({
860
- range: ur,
861
- severity: "info",
862
- message: "Code after return/throw statement is unreachable",
863
- tags: ["unnecessary"],
864
- code: "nudo-unreachable",
865
- suggestions: ["Remove the unreachable code after the return/throw statement"]
866
- });
2774
+ if (!isBPathCapable(source, envNames)) {
2775
+ for (const ur of caseUnreachable) {
2776
+ diagnostics.push({
2777
+ range: ur,
2778
+ severity: "info",
2779
+ message: "Code after return/throw statement is unreachable",
2780
+ tags: ["unnecessary"],
2781
+ code: "nudo-unreachable",
2782
+ suggestions: ["Remove the unreachable code after the return/throw statement"]
2783
+ });
2784
+ }
867
2785
  }
868
2786
  }
869
2787
  }
@@ -872,26 +2790,13 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
872
2790
  } else if (analysis.cases.length === 1) {
873
2791
  analysis.combined = analysis.cases[0].result;
874
2792
  }
875
- if (returnsDirective && returnsDirective.kind === "returns") {
876
- analysis.assertionErrors = [];
877
- for (const directive of caseDirectives) {
878
- const result = evaluateFunction(fn.node, directive.args, globalEnv);
879
- const matches = isSubtypeOf(result, returnsDirective.expected);
880
- if (!matches) {
881
- const msg = `@nudo:returns assertion failed for case "${directive.name}": expected ${typeValueToString(returnsDirective.expected)}, got ${typeValueToString(result)}. Update the @nudo:returns directive to match the inferred type, or fix the function implementation`;
882
- analysis.assertionErrors.push(msg);
883
- diagnostics.push({
884
- range: fnLoc,
885
- severity: "error",
886
- message: msg,
887
- code: "nudo-assertion-failed",
888
- suggestions: [
889
- "Update @nudo:returns to match the inferred type",
890
- "Fix the function to return the expected type"
891
- ]
892
- });
893
- }
894
- }
2793
+ if (fnCacheKey) {
2794
+ fnAnalysisCacheSet(fnCacheKey, {
2795
+ analysis: cloneFunctionAnalysis(analysis),
2796
+ diagnostics: diagnostics.slice(dLen0),
2797
+ caseHints: caseHints.slice(hLen0),
2798
+ callRecords: callRecords.slice(cLen0)
2799
+ });
895
2800
  }
896
2801
  functionResults.push(analysis);
897
2802
  }
@@ -918,7 +2823,7 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
918
2823
  }
919
2824
  return n;
920
2825
  };
921
- const currentModulePath = normalizeModulePath(resolve(filePath));
2826
+ const currentModulePath = normalizeModulePath(resolve4(filePath));
922
2827
  const singleExportFn = findSingleModuleExportsFunction(ast);
923
2828
  for (const candidate of synthCandidates) {
924
2829
  const singleExportHit = singleExportFn !== null && candidate.node === singleExportFn;
@@ -948,13 +2853,25 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
948
2853
  const ordered = records.map((r, i) => ({ r, i })).sort((a, b) => informativeness(a.r) - informativeness(b.r) || a.i - b.i).map(({ r }) => r);
949
2854
  const precise = ordered.slice(0, MAX_PRECISE_CALLSITE_CASES);
950
2855
  for (const rec of precise) {
951
- candidate.analysis.cases.push({
2856
+ let absRaw;
2857
+ let absResult;
2858
+ if (rec.resultType.kind !== "never" && rec.argTypes.length > 0) {
2859
+ absRaw = tryEvalAbsRaw(source, candidate.name, rec.argTypes, filePath, mockSeedsToAbsMocks(seeds));
2860
+ if (absRaw) {
2861
+ const projected = absToTypeValue2(absRaw);
2862
+ if (absIsBetter(projected, rec.resultType)) absResult = projected;
2863
+ }
2864
+ }
2865
+ const caseResult2 = {
952
2866
  name: `call@L${rec.callLoc?.line ?? candidate.analysis.loc.start.line}`,
953
2867
  args: rec.argTypes,
954
- result: rec.resultType,
2868
+ result: absResult ?? rec.resultType,
955
2869
  throws: rec.throws,
956
2870
  source: "callsite"
957
- });
2871
+ };
2872
+ tryAttachIntension(caseResult2, source, candidate.name);
2873
+ if (absRaw) attachAbsToIntension(caseResult2, absRaw, candidate.name);
2874
+ candidate.analysis.cases.push(caseResult2);
958
2875
  }
959
2876
  const remaining = ordered.slice(MAX_PRECISE_CALLSITE_CASES).filter((rec) => !rec.argTypes.some((a) => a.kind === "unknown"));
960
2877
  if (remaining.length > 0) {
@@ -965,19 +2882,50 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
965
2882
  (_, i) => (
966
2883
  // 缺参按真实 JS 语义 widen 成 undefined 而非 unknown——可选参守卫
967
2884
  // (target || [] 等)对 unknown 全塌,对 undefined 正常走默认分支
968
- widenType(simplifyUnion(remaining.map((rec) => rec.argTypes[i] ?? T.undefined)))
2885
+ widenType(simplifyUnion(remaining.map((rec) => rec.argTypes[i] ?? T2.undefined)))
969
2886
  )
970
2887
  );
971
- const full2 = evaluateFunctionFull(fnNode2, widenedArgs, globalEnv);
972
- candidate.analysis.cases.push({
2888
+ const absSymRaw = tryEvalAbs(source, candidate.name, widenedArgs, filePath, mockSeedsToAbsMocks(seeds));
2889
+ const absSym = absSymRaw && absIsBetter(
2890
+ absSymRaw,
2891
+ /* 无先验:仅 unknown 时 */
2892
+ { kind: "unknown" }
2893
+ ) ? absSymRaw : void 0;
2894
+ let symValue;
2895
+ let symThrows = T2.never;
2896
+ let symLoc;
2897
+ let symAbs;
2898
+ if (isBPathCapable(source, envNames) && filePath) {
2899
+ const bSym = tryBPathCall(
2900
+ source,
2901
+ filePath,
2902
+ candidate.name,
2903
+ widenedArgs.map((a) => typeValueToAbs5(a)),
2904
+ { envNames, mocks: mockSeedsToAbsMocks(seeds) }
2905
+ );
2906
+ if (bSym && (bHostedEval || !(bSym.shape.k === "unknown" && !bSym.term))) {
2907
+ symAbs = bSym;
2908
+ symValue = absToTypeValue2(bSym);
2909
+ }
2910
+ }
2911
+ if (!symAbs && !bHostedEval) {
2912
+ const full = evaluateFunctionFull(fnNode2, widenedArgs, globalEnv);
2913
+ symValue = full.value.kind === "unknown" && absSym ? absSym : full.value;
2914
+ symThrows = full.throws;
2915
+ symLoc = full.throwLoc;
2916
+ }
2917
+ const symCase = {
973
2918
  name: "call@symbolic",
974
2919
  args: widenedArgs,
975
- result: full2.value,
976
- throws: full2.throws,
977
- throwLoc: full2.throwLoc,
2920
+ result: symValue ?? T2.unknown,
2921
+ throws: symThrows,
2922
+ throwLoc: symLoc,
978
2923
  source: "callsite",
979
2924
  aggregatedFrom: remaining.length
980
- });
2925
+ };
2926
+ tryAttachIntension(symCase, source, candidate.name);
2927
+ if (symAbs) attachAbsToIntension(symCase, symAbs, candidate.name);
2928
+ candidate.analysis.cases.push(symCase);
981
2929
  }
982
2930
  candidate.analysis.combined = collapseLiteralUnion(
983
2931
  simplifyUnion(records.map((r) => r.resultType)),
@@ -985,20 +2933,59 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
985
2933
  );
986
2934
  continue;
987
2935
  }
988
- const fnNode = resolveFunctionNode(candidate.node);
989
- const args = extractParamNames(fnNode).map(() => T.unknown);
990
- const full = evaluateFunctionFull(fnNode, args, globalEnv);
991
- candidate.analysis.cases.push({
2936
+ const fnNode = resolveFunctionNode(candidate.node);
2937
+ const args = extractParamNames(fnNode).map(() => T2.unknown);
2938
+ let entryValue;
2939
+ let entryThrows = T2.never;
2940
+ let entryLoc;
2941
+ let entryAbs;
2942
+ if (isBPathCapable(source, envNames) && filePath) {
2943
+ const bEntryFull = tryBPathCallFull(
2944
+ source,
2945
+ filePath,
2946
+ candidate.analysis.name,
2947
+ args.map((a) => typeValueToAbs5(a)),
2948
+ { envNames, mocks: mockSeedsToAbsMocks(seeds), collectMemberDiags: true }
2949
+ );
2950
+ if (bEntryFull?.memberDiags?.length) {
2951
+ for (const d of bEntryFull.memberDiags) {
2952
+ pushBMemberDiag(d, candidate.analysis.loc.start.line);
2953
+ }
2954
+ }
2955
+ const bEntry = bEntryFull?.result;
2956
+ if (bEntry && (bHostedEval || !(bEntry.shape.k === "unknown" && !bEntry.term))) {
2957
+ entryAbs = bEntry;
2958
+ entryValue = absToTypeValue2(bEntry);
2959
+ }
2960
+ }
2961
+ if (!entryValue) {
2962
+ if (bHostedEval) {
2963
+ entryValue = T2.unknown;
2964
+ entryThrows = T2.never;
2965
+ } else {
2966
+ const absEntry = tryEvalEntryAbs(source, candidate.analysis.name, args, filePath, seeds.seedVars);
2967
+ const full = absEntry ? { value: absEntry, throws: T2.never, throwLoc: void 0 } : evaluateFunctionFull(fnNode, args, globalEnv);
2968
+ entryValue = full.value;
2969
+ entryThrows = full.throws;
2970
+ entryLoc = full.throwLoc;
2971
+ }
2972
+ }
2973
+ const caseResult = {
992
2974
  name: `entry@L${candidate.analysis.loc.start.line}`,
993
2975
  args,
994
- result: full.value,
995
- throws: full.throws,
996
- throwLoc: full.throwLoc
997
- });
2976
+ result: entryValue,
2977
+ throws: entryThrows,
2978
+ throwLoc: entryLoc
2979
+ };
2980
+ tryAttachIntension(caseResult, source, candidate.analysis.name);
2981
+ if (entryAbs) attachAbsToIntension(caseResult, entryAbs, candidate.analysis.name);
2982
+ candidate.analysis.cases.push(caseResult);
998
2983
  candidate.analysis.entryOnly = true;
999
- candidate.analysis.combined = collapseLiteralUnion(full.value, COLLAPSE_LITERAL_THRESHOLD);
2984
+ candidate.analysis.combined = collapseLiteralUnion(entryValue, COLLAPSE_LITERAL_THRESHOLD);
2985
+ }
2986
+ if (!bHostedEval) {
2987
+ buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
1000
2988
  }
1001
- buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
1002
2989
  setUnknownBuiltinHandler(null);
1003
2990
  setCallCollector(null);
1004
2991
  setUnknownCollector(null);
@@ -1009,9 +2996,29 @@ function analyzeFile(filePath, source, activeCases, externalCallRecords) {
1009
2996
  const maxLine = source.split("\n").length;
1010
2997
  diagnostics.push(
1011
2998
  ...unknownRecordsToDiagnostics(
1012
- unknownRecords.filter(
1013
- (r) => (r.loc?.line ?? 0) <= maxLine && r.originModule !== USAGE_SITE_MODULE
1014
- )
2999
+ unknownRecords.filter((r) => {
3000
+ if ((r.loc?.line ?? 0) > maxLine) return false;
3001
+ if (r.originModule === USAGE_SITE_MODULE) return false;
3002
+ if (bHostedEval && (r.kind === "method" || r.kind === "property")) {
3003
+ return false;
3004
+ }
3005
+ if ((r.kind === "method" || r.kind === "property") && bMemberDiagNames.has(r.name)) {
3006
+ return false;
3007
+ }
3008
+ if (r.kind === "global") {
3009
+ if (r.name.startsWith("module-cycle:") && bModuleIssueKinds.has("cycle")) return false;
3010
+ if (r.name.startsWith("module-depth:") && bModuleIssueKinds.has("depth")) return false;
3011
+ if (r.name.startsWith("module-missing:") && bModuleIssueKinds.has("missing")) return false;
3012
+ if (r.name.startsWith("recursion:")) {
3013
+ const fn = r.name.slice("recursion:".length);
3014
+ if (bTruncatedFns.has(fn) || bTruncatedFns.has(fn.split(".").pop() ?? fn)) {
3015
+ return false;
3016
+ }
3017
+ }
3018
+ if (bBuiltinUnknownNames.has(r.name)) return false;
3019
+ }
3020
+ return true;
3021
+ })
1015
3022
  )
1016
3023
  );
1017
3024
  const externalFunctions = synthesizeExternalFunctions(callRecords, filePath);
@@ -1028,19 +3035,19 @@ function collectBindings(ast, env, bindings) {
1028
3035
  if (ast.type !== "File") return;
1029
3036
  for (const stmt of ast.program.body) {
1030
3037
  if (stmt.type === "FunctionDeclaration" && stmt.id) {
1031
- const val = env.has(stmt.id.name) ? env.lookup(stmt.id.name) : T.unknown;
3038
+ const val = env.has(stmt.id.name) ? env.lookup(stmt.id.name) : T2.unknown;
1032
3039
  bindings.set(stmt.id.name, { type: val, loc: locFromNode(stmt) });
1033
3040
  }
1034
3041
  if (stmt.type === "VariableDeclaration") {
1035
3042
  for (const decl of stmt.declarations) {
1036
3043
  if (decl.id.type === "Identifier") {
1037
- const val = env.has(decl.id.name) ? env.lookup(decl.id.name) : T.unknown;
3044
+ const val = env.has(decl.id.name) ? env.lookup(decl.id.name) : T2.unknown;
1038
3045
  bindings.set(decl.id.name, { type: val, loc: locFromNode(decl) });
1039
3046
  }
1040
3047
  }
1041
3048
  }
1042
3049
  if (stmt.type === "ClassDeclaration" && stmt.id) {
1043
- const val = env.has(stmt.id.name) ? env.lookup(stmt.id.name) : T.unknown;
3050
+ const val = env.has(stmt.id.name) ? env.lookup(stmt.id.name) : T2.unknown;
1044
3051
  bindings.set(stmt.id.name, { type: val, loc: locFromNode(stmt) });
1045
3052
  }
1046
3053
  if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
@@ -1061,16 +3068,16 @@ function buildNodeTypeMap(ast, env, nodeTypeMap) {
1061
3068
  }
1062
3069
  }
1063
3070
  if (node.type === "NumericLiteral") {
1064
- nodeTypeMap.set(node, T.literal(node.value));
3071
+ nodeTypeMap.set(node, T2.literal(node.value));
1065
3072
  }
1066
3073
  if (node.type === "StringLiteral") {
1067
- nodeTypeMap.set(node, T.literal(node.value));
3074
+ nodeTypeMap.set(node, T2.literal(node.value));
1068
3075
  }
1069
3076
  if (node.type === "BooleanLiteral") {
1070
- nodeTypeMap.set(node, T.literal(node.value));
3077
+ nodeTypeMap.set(node, T2.literal(node.value));
1071
3078
  }
1072
3079
  if (node.type === "NullLiteral") {
1073
- nodeTypeMap.set(node, T.null);
3080
+ nodeTypeMap.set(node, T2.null);
1074
3081
  }
1075
3082
  } catch {
1076
3083
  }
@@ -1080,7 +3087,7 @@ function buildNodeTypeMap(ast, env, nodeTypeMap) {
1080
3087
  }
1081
3088
  }
1082
3089
  function getCasesForFile(filePath, source) {
1083
- const ast = parse(source);
3090
+ const ast = parse5(source);
1084
3091
  const functions = extractDirectives(ast);
1085
3092
  return functions.map((fn) => {
1086
3093
  const cases = fn.directives.filter((d) => d.kind === "case").map((d, i) => ({ name: d.name, index: i }));
@@ -1090,23 +3097,48 @@ function getCasesForFile(filePath, source) {
1090
3097
  async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
1091
3098
  const envNames = collectEnvNames(filePath, source, false);
1092
3099
  if (envNames.length > 0) {
1093
- await preloadPathEnvs(envNames, dirname(filePath));
3100
+ await preloadPathEnvs(envNames, dirname5(filePath));
1094
3101
  }
1095
3102
  return getTypeAtPosition(filePath, source, line, column, activeCases);
1096
3103
  }
1097
3104
  function getTypeAtPosition(filePath, source, line, column, activeCases) {
1098
- const ast = parse(source);
3105
+ const ast = parse5(source);
3106
+ const fileDirectives = extractFileDirectives(ast);
3107
+ const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
3108
+ if (isBPathCapable(source, envNames)) {
3109
+ try {
3110
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives(ast));
3111
+ const { modules } = evalAbsModuleGraph(source, filePath);
3112
+ const absNodes = collectAbsNodeTypes(source, {
3113
+ ...seeds,
3114
+ modules,
3115
+ file: ast
3116
+ });
3117
+ const absAt = findAbsAtPosition(absNodes, line, column);
3118
+ if (absAt) return absToTypeValue2(absAt);
3119
+ const ident = findIdentNameAtPosition(source, line, column, ast);
3120
+ if (ident) {
3121
+ const binds = collectAbsBindingsFromGraph(source, filePath, {
3122
+ seedVars: seeds.seedVars,
3123
+ seedFns: seeds.seedFns
3124
+ });
3125
+ const bound = binds.get(ident);
3126
+ if (bound) return absToTypeValue2(bound);
3127
+ }
3128
+ } catch {
3129
+ }
3130
+ }
1099
3131
  resetMemo();
1100
3132
  resetEnvModules();
1101
3133
  setModuleResolver(resolveModule);
1102
- setCurrentFileDir(dirname(filePath));
3134
+ setCurrentFileDir(dirname5(filePath));
1103
3135
  setCurrentSource(source);
1104
- const fileDirectives = extractFileDirectives(ast);
1105
- const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
1106
- const globalEnv = createEnvironment();
1107
- if (envNames.length > 0) {
1108
- const loaded = loadEnvs(envNames, globalEnv);
1109
- setEnvModules(loaded.modules);
3136
+ const globalEnv = createEnvironment2();
3137
+ {
3138
+ const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
3139
+ const auto = autoHarvestModules(source, dirname5(filePath));
3140
+ const modules = { ...loaded.modules, ...auto };
3141
+ if (Object.keys(modules).length > 0) setEnvModules(modules);
1110
3142
  }
1111
3143
  evaluateProgram(ast, globalEnv);
1112
3144
  const nodeTypeMap = /* @__PURE__ */ new Map();
@@ -1132,6 +3164,160 @@ function getTypeAtPosition(filePath, source, line, column, activeCases) {
1132
3164
  resetMockModules();
1133
3165
  return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
1134
3166
  }
3167
+ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3168
+ let file;
3169
+ try {
3170
+ file = parse5(source);
3171
+ } catch {
3172
+ file = void 0;
3173
+ }
3174
+ const envNames = collectEnvNames(filePath, source, false);
3175
+ if (isBPathCapable(source, envNames)) {
3176
+ try {
3177
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3178
+ const { modules } = evalAbsModuleGraph(source, filePath);
3179
+ const absNodes = collectAbsNodeTypes(source, {
3180
+ ...seeds,
3181
+ modules,
3182
+ ...file ? { file } : {}
3183
+ });
3184
+ const absAt = findAbsAtPosition(absNodes, line, column);
3185
+ const ident2 = findIdentNameAtPosition(source, line, column, file);
3186
+ if (ident2 && !findFunctionNameAtPosition(source, line, column, file)) {
3187
+ const binds = collectAbsBindingsFromGraph(source, filePath, {
3188
+ seedVars: seeds.seedVars,
3189
+ seedFns: seeds.seedFns
3190
+ });
3191
+ const bound = binds.get(ident2);
3192
+ if (bound) {
3193
+ const absLine = formatAbs3(bound);
3194
+ const absMulti = formatAbsMultiline(bound, ident2);
3195
+ return { typeText: absLine, abs: absLine, absMultiline: absMulti };
3196
+ }
3197
+ }
3198
+ if (absAt) {
3199
+ const absLine = formatAbs3(absAt);
3200
+ const absMulti = formatAbsMultiline(absAt, void 0);
3201
+ return { typeText: absLine, abs: absLine, absMultiline: absMulti };
3202
+ }
3203
+ } catch {
3204
+ }
3205
+ }
3206
+ const tv = getTypeAtPosition(filePath, source, line, column, activeCases);
3207
+ const info = tv ? { typeText: typeValueToString3(tv) } : null;
3208
+ const fnName = findFunctionNameAtPosition(source, line, column, file);
3209
+ if (fnName) {
3210
+ try {
3211
+ const g = generalizeFromAst2(fnName, source, file ? { file } : {});
3212
+ if (g) {
3213
+ if (info) {
3214
+ info.intension = g.display;
3215
+ info.abs = formatAbs3(g.symbolic);
3216
+ info.absMultiline = formatAbsMultiline(g.symbolic, fnName);
3217
+ } else {
3218
+ return {
3219
+ typeText: g.display,
3220
+ intension: g.display,
3221
+ abs: formatAbs3(g.symbolic),
3222
+ absMultiline: formatAbsMultiline(g.symbolic, fnName)
3223
+ };
3224
+ }
3225
+ }
3226
+ } catch {
3227
+ }
3228
+ }
3229
+ const ident = findIdentNameAtPosition(source, line, column, file);
3230
+ if (ident && !fnName) {
3231
+ try {
3232
+ if (isBPathCapable(source, []) || !/\brequire\s*\(/.test(source)) {
3233
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3234
+ const absBinds = collectAbsBindingsFromGraph(source, filePath, {
3235
+ seedVars: seeds.seedVars,
3236
+ seedFns: seeds.seedFns
3237
+ });
3238
+ const absBound = absBinds.get(ident);
3239
+ if (absBound) {
3240
+ const absLine = formatAbs3(absBound);
3241
+ const absMulti = formatAbsMultiline(absBound, ident);
3242
+ if (info) {
3243
+ info.abs = absLine;
3244
+ info.absMultiline = absMulti;
3245
+ return info;
3246
+ }
3247
+ return { typeText: absLine, abs: absLine, absMultiline: absMulti };
3248
+ }
3249
+ }
3250
+ } catch {
3251
+ }
3252
+ }
3253
+ try {
3254
+ if (isBPathCapable(source, []) || !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source)) {
3255
+ const seeds = mockDirectivesToAbsSeeds(
3256
+ extractDirectives(file ?? parse5(source))
3257
+ );
3258
+ const { modules } = evalAbsModuleGraph(source, filePath);
3259
+ const nodeTypes = collectAbsNodeTypes(source, {
3260
+ ...seeds,
3261
+ modules,
3262
+ ...file ? { file } : {}
3263
+ });
3264
+ const absAt = findAbsAtPosition(nodeTypes, line, column);
3265
+ if (absAt) {
3266
+ const absLine = formatAbs3(absAt);
3267
+ const absMulti = formatAbsMultiline(absAt, void 0);
3268
+ if (info) {
3269
+ info.abs = absLine;
3270
+ info.absMultiline = absMulti;
3271
+ } else {
3272
+ return { typeText: absLine, abs: absLine, absMultiline: absMulti };
3273
+ }
3274
+ }
3275
+ }
3276
+ } catch {
3277
+ }
3278
+ return info;
3279
+ }
3280
+ function findIdentNameAtPosition(source, line, column, fileAst) {
3281
+ try {
3282
+ const ast = fileAst ?? parse5(source);
3283
+ let found;
3284
+ traverse(ast, {
3285
+ Identifier(path) {
3286
+ const loc = path.node.loc;
3287
+ if (!loc) return;
3288
+ if (loc.start.line !== line) return;
3289
+ if (column < loc.start.column || column > loc.end.column) return;
3290
+ found = path.node.name;
3291
+ }
3292
+ });
3293
+ return found;
3294
+ } catch {
3295
+ return void 0;
3296
+ }
3297
+ }
3298
+ function findFunctionNameAtPosition(source, line, column, fileAst) {
3299
+ try {
3300
+ const ast = fileAst ?? parse5(source);
3301
+ let found;
3302
+ traverse(ast, {
3303
+ Identifier(path) {
3304
+ const loc = path.node.loc;
3305
+ if (!loc) return;
3306
+ if (loc.start.line !== line) return;
3307
+ if (column < loc.start.column || column > loc.end.column) return;
3308
+ const parent = path.parent;
3309
+ if (parent.type === "FunctionDeclaration" && parent.id === path.node) {
3310
+ found = path.node.name;
3311
+ } else if (parent.type === "CallExpression" && parent.callee === path.node) {
3312
+ found = path.node.name;
3313
+ }
3314
+ }
3315
+ });
3316
+ return found;
3317
+ } catch {
3318
+ return void 0;
3319
+ }
3320
+ }
1135
3321
  function findEnclosingFunction(functions, line) {
1136
3322
  for (const fn of functions) {
1137
3323
  const loc = fn.node.loc;
@@ -1190,18 +3376,18 @@ function getCompletionsAtPosition(filePath, source, line, column) {
1190
3376
  const safeSource = sanitizeSourceForParsing(source);
1191
3377
  let ast;
1192
3378
  try {
1193
- ast = parse(safeSource);
3379
+ ast = parse5(safeSource);
1194
3380
  } catch {
1195
3381
  try {
1196
- ast = parse(source);
3382
+ ast = parse5(source);
1197
3383
  } catch {
1198
3384
  return [];
1199
3385
  }
1200
3386
  }
1201
3387
  resetMemo();
1202
3388
  setModuleResolver(resolveModule);
1203
- setCurrentFileDir(dirname(filePath));
1204
- const globalEnv = createEnvironment();
3389
+ setCurrentFileDir(dirname5(filePath));
3390
+ const globalEnv = createEnvironment2();
1205
3391
  evaluateProgram(ast, globalEnv);
1206
3392
  if (!globalEnv.has(objName)) {
1207
3393
  setModuleResolver(null);
@@ -1221,11 +3407,11 @@ function getTextBeforePosition(source, line, column) {
1221
3407
  return lines[line - 1].slice(0, column);
1222
3408
  }
1223
3409
  function getVariableCompletions(filePath, source) {
1224
- const ast = parse(source);
3410
+ const ast = parse5(source);
1225
3411
  resetMemo();
1226
3412
  setModuleResolver(resolveModule);
1227
- setCurrentFileDir(dirname(filePath));
1228
- const globalEnv = createEnvironment();
3413
+ setCurrentFileDir(dirname5(filePath));
3414
+ const globalEnv = createEnvironment2();
1229
3415
  evaluateProgram(ast, globalEnv);
1230
3416
  const ownBindings = globalEnv.getOwnBindings();
1231
3417
  const completions = [];
@@ -1234,12 +3420,97 @@ function getVariableCompletions(filePath, source) {
1234
3420
  completions.push({
1235
3421
  label: name,
1236
3422
  kind: tv.kind === "function" ? "method" : "variable",
1237
- detail: typeValueToString(tv)
3423
+ detail: typeValueToString3(tv)
1238
3424
  });
1239
3425
  }
1240
3426
  setModuleResolver(null);
1241
3427
  return completions;
1242
3428
  }
3429
+ function builtinMemberType(memberExpr) {
3430
+ try {
3431
+ const result = evaluate(parse5(`${memberExpr};`), createEnvironment2());
3432
+ if (!result || typeof result !== "object" || !("kind" in result)) return null;
3433
+ return result;
3434
+ } catch {
3435
+ return null;
3436
+ }
3437
+ }
3438
+ function builtinProtoMembers(className) {
3439
+ const table = BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS[className];
3440
+ return table ? Object.keys(table) : [];
3441
+ }
3442
+ function describeMember(label, tv, fallbackClass) {
3443
+ if (tv) {
3444
+ const sig = getFnSig3(tv);
3445
+ if (sig) {
3446
+ const paramNames = tv.kind === "function" ? tv.params : [];
3447
+ const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${typeValueToString3(p)}`).join(", ");
3448
+ return `(${params}) => ${typeValueToString3(sig.returnType)}`;
3449
+ }
3450
+ if (tv.kind !== "function") return typeValueToString3(tv);
3451
+ }
3452
+ return `${label}(\u2026)@${fallbackClass}`;
3453
+ }
3454
+ function getArrayCompletions(tv) {
3455
+ const completions = [];
3456
+ for (const m of builtinProtoMembers("Array")) {
3457
+ const detail = describeMember(m, builtinMemberType(`Array.prototype.${m}`), "Array");
3458
+ completions.push({ label: m, kind: "method", detail });
3459
+ }
3460
+ completions.push({
3461
+ label: "length",
3462
+ kind: "property",
3463
+ // tuple 长度是精确字面量;array 是 number(保持原展示语义)
3464
+ detail: tv.kind === "tuple" ? `${tv.elements.length}` : "number"
3465
+ });
3466
+ return completions;
3467
+ }
3468
+ function getPromiseCompletions() {
3469
+ return builtinProtoMembers("Promise").map((m) => ({
3470
+ label: m,
3471
+ kind: "method",
3472
+ detail: describeMember(m, builtinMemberType(`Promise.prototype.${m}`), "Promise")
3473
+ }));
3474
+ }
3475
+ function getStringCompletions() {
3476
+ const completions = [];
3477
+ for (const m of builtinProtoMembers("String")) {
3478
+ completions.push({
3479
+ label: m,
3480
+ kind: "method",
3481
+ detail: describeMember(m, builtinMemberType(`"s".${m}`), "String")
3482
+ });
3483
+ }
3484
+ completions.push({ label: "length", kind: "property", detail: "number" });
3485
+ return completions;
3486
+ }
3487
+ function getUnionCompletions(tv) {
3488
+ const members = tv.members;
3489
+ if (members.length === 0) return [];
3490
+ const labelsByMember = members.map((m) => getCompletionsForType(m));
3491
+ const baseIdx = labelsByMember.findIndex((labels) => labels.length > 0);
3492
+ if (baseIdx === -1) return [];
3493
+ const common = [];
3494
+ for (const base of labelsByMember[baseIdx]) {
3495
+ let allPresent = true;
3496
+ const memberTypes = [base.detail ?? base.label];
3497
+ for (let i = 0; i < members.length; i++) {
3498
+ if (i === baseIdx) continue;
3499
+ const labels = labelsByMember[i];
3500
+ if (labels.length === 0) continue;
3501
+ const hit = labels.find((l) => l.label === base.label);
3502
+ if (!hit) {
3503
+ allPresent = false;
3504
+ break;
3505
+ }
3506
+ memberTypes.push(hit.detail ?? hit.label);
3507
+ }
3508
+ if (allPresent) {
3509
+ common.push({ ...base, detail: memberTypes.join(" | ") });
3510
+ }
3511
+ }
3512
+ return common;
3513
+ }
1243
3514
  function getCompletionsForType(tv) {
1244
3515
  const completions = [];
1245
3516
  if (tv.kind === "object") {
@@ -1247,7 +3518,7 @@ function getCompletionsForType(tv) {
1247
3518
  completions.push({
1248
3519
  label: key,
1249
3520
  kind: val.kind === "function" ? "method" : "property",
1250
- detail: typeValueToString(val)
3521
+ detail: typeValueToString3(val)
1251
3522
  });
1252
3523
  }
1253
3524
  return completions;
@@ -1257,52 +3528,523 @@ function getCompletionsForType(tv) {
1257
3528
  completions.push({
1258
3529
  label: key,
1259
3530
  kind: val.kind === "function" ? "method" : "property",
1260
- detail: typeValueToString(val)
3531
+ detail: typeValueToString3(val)
1261
3532
  });
1262
3533
  }
1263
3534
  return completions;
1264
3535
  }
3536
+ if (tv.kind === "union") {
3537
+ return getUnionCompletions(tv);
3538
+ }
1265
3539
  if (tv.kind === "array" || tv.kind === "tuple") {
1266
- const arrayMethods = [
1267
- { label: "map", detail: "map(callback)" },
1268
- { label: "filter", detail: "filter(callback)" },
1269
- { label: "reduce", detail: "reduce(callback, init)" },
1270
- { label: "find", detail: "find(callback)" },
1271
- { label: "some", detail: "some(callback)" },
1272
- { label: "every", detail: "every(callback)" },
1273
- { label: "forEach", detail: "forEach(callback)" },
1274
- { label: "flatMap", detail: "flatMap(callback)" },
1275
- { label: "includes", detail: "includes(value)" },
1276
- { label: "indexOf", detail: "indexOf(value)" },
1277
- { label: "join", detail: "join(separator)" },
1278
- { label: "slice", detail: "slice(start, end)" },
1279
- { label: "concat", detail: "concat(other)" },
1280
- { label: "push", detail: "push(value)" },
1281
- { label: "length", detail: tv.kind === "tuple" ? `${tv.elements.length}` : "number" }
1282
- ];
1283
- for (const m of arrayMethods) {
1284
- completions.push({ label: m.label, kind: "method", detail: m.detail });
1285
- }
1286
- return completions;
3540
+ return getArrayCompletions(tv);
1287
3541
  }
1288
3542
  if (tv.kind === "promise") {
1289
- completions.push({ label: "then", kind: "method", detail: "then(callback)" });
1290
- completions.push({ label: "catch", kind: "method", detail: "catch(callback)" });
1291
- completions.push({ label: "finally", kind: "method", detail: "finally(callback)" });
1292
- return completions;
3543
+ return getPromiseCompletions();
1293
3544
  }
1294
3545
  if (tv.kind === "primitive" && tv.type === "string") {
1295
- const stringMethods = ["toUpperCase", "toLowerCase", "trim", "split", "slice", "includes", "indexOf", "replace", "startsWith", "endsWith", "charAt", "length"];
1296
- for (const m of stringMethods) {
1297
- completions.push({ label: m, kind: "method", detail: `string.${m}` });
1298
- }
1299
- return completions;
3546
+ return getStringCompletions();
1300
3547
  }
1301
3548
  return completions;
1302
3549
  }
3550
+ function absIsBetter(absTv, prev) {
3551
+ if (prev.kind === "unknown") return absTv.kind !== "unknown";
3552
+ if (absTv.kind === "unknown" || absTv.kind === "never") return false;
3553
+ if (prev.kind === "literal" || prev.kind === "object" || prev.kind === "array" || prev.kind === "tuple" || prev.kind === "union" || prev.kind === "function" || prev.kind === "instance" || prev.kind === "promise") {
3554
+ return false;
3555
+ }
3556
+ if (absTv.kind === "refined" && prev.kind === "primitive") return true;
3557
+ return false;
3558
+ }
3559
+ function isSelfContainedSource(source, envNames) {
3560
+ if (envNames.length > 0) return false;
3561
+ return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
3562
+ }
3563
+ function absModulesOk(source, envNames) {
3564
+ void source;
3565
+ void envNames;
3566
+ return true;
3567
+ }
3568
+ function collectAbsCallRecords(source, seeds, filePath, precomputedModules) {
3569
+ const absCalls = [];
3570
+ let modules = precomputedModules;
3571
+ let importLocals = /* @__PURE__ */ new Map();
3572
+ if (filePath && absModulesOk(source, [])) {
3573
+ if (!modules) {
3574
+ try {
3575
+ const graph = evalAbsModuleGraph(source, filePath, {
3576
+ seedVars: seeds?.seedVars,
3577
+ seedFns: seeds?.seedFns
3578
+ });
3579
+ modules = graph.modules;
3580
+ } catch {
3581
+ modules = void 0;
3582
+ }
3583
+ }
3584
+ try {
3585
+ importLocals = buildAbsImportLocalMap(source, filePath);
3586
+ } catch {
3587
+ importLocals = /* @__PURE__ */ new Map();
3588
+ }
3589
+ }
3590
+ setAbsCallCollector((r) => absCalls.push(r));
3591
+ try {
3592
+ evalProgramAbs2(source, { ...seeds, modules });
3593
+ } catch {
3594
+ } finally {
3595
+ setAbsCallCollector(null);
3596
+ }
3597
+ return absCalls.map((r) => {
3598
+ const rec = {
3599
+ fnName: r.fnName,
3600
+ argTypes: r.args.map((a) => absToTypeValue2(a)),
3601
+ resultType: r.threw ? T2.never : absToTypeValue2(r.result),
3602
+ throws: r.threw ? absToTypeValue2(r.result) : T2.never,
3603
+ callLoc: r.callLoc
3604
+ };
3605
+ const imp = importLocals.get(r.fnName);
3606
+ if (imp) {
3607
+ rec.targetModule = imp.modulePath;
3608
+ rec.targetExport = imp.exportName;
3609
+ }
3610
+ return rec;
3611
+ });
3612
+ }
3613
+ function collectAbsBindsAndNodes(source, seeds, modules) {
3614
+ const binds = /* @__PURE__ */ new Map();
3615
+ const nodes = /* @__PURE__ */ new Map();
3616
+ setAbsNodeCollector((node, value) => {
3617
+ nodes.set(node, value);
3618
+ });
3619
+ try {
3620
+ const { env } = evalProgramAbs2(source, {
3621
+ ...seeds,
3622
+ modules
3623
+ });
3624
+ for (const [k, v] of env.vars) binds.set(k, v);
3625
+ for (const [name, impl] of env.fns) {
3626
+ if (!binds.has(name)) {
3627
+ binds.set(
3628
+ name,
3629
+ absFunction5(impl.params, { body: impl.body, async: impl.async, env })
3630
+ );
3631
+ }
3632
+ }
3633
+ } catch {
3634
+ } finally {
3635
+ setAbsNodeCollector(null);
3636
+ }
3637
+ return { binds, nodes };
3638
+ }
3639
+ function buildAbsImportLocalMap(source, fromFile) {
3640
+ const out = /* @__PURE__ */ new Map();
3641
+ try {
3642
+ const file = parse5(source);
3643
+ for (const stmt of file.program.body) {
3644
+ if (stmt.type !== "ImportDeclaration") continue;
3645
+ const spec = stmt.source.value;
3646
+ let modulePath = null;
3647
+ if (spec.startsWith(".") || spec.startsWith("/")) {
3648
+ modulePath = resolveImportAbs(spec, fromFile);
3649
+ } else if (!spec.startsWith("node:")) {
3650
+ modulePath = spec;
3651
+ }
3652
+ if (!modulePath) continue;
3653
+ for (const s of stmt.specifiers) {
3654
+ if (s.type === "ImportSpecifier") {
3655
+ const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
3656
+ out.set(s.local.name, { modulePath, exportName: imported });
3657
+ } else if (s.type === "ImportDefaultSpecifier") {
3658
+ out.set(s.local.name, { modulePath, exportName: "default" });
3659
+ }
3660
+ }
3661
+ }
3662
+ } catch {
3663
+ }
3664
+ return out;
3665
+ }
3666
+ function resolveImportAbs(spec, fromFile) {
3667
+ const base = dirname5(resolve4(fromFile));
3668
+ const p = resolve4(base, spec);
3669
+ for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
3670
+ if (existsSync3(cand) && !statSync3(cand).isDirectory()) return cand;
3671
+ }
3672
+ return null;
3673
+ }
3674
+ function tryEvalAbs(source, fnName, args, filePath, mocks) {
3675
+ const raw = tryEvalAbsRaw(source, fnName, args, filePath, mocks);
3676
+ return raw ? absToTypeValue2(raw) : void 0;
3677
+ }
3678
+ function tryEvalAbsRaw(source, fnName, args, filePath, mocks) {
3679
+ if (/\brequire\s*\(/.test(source)) return void 0;
3680
+ try {
3681
+ const absArgs = args.map((a) => typeValueToAbs5(a));
3682
+ if (filePath) {
3683
+ const viaB = tryBPathCall(source, filePath, fnName, absArgs, { mocks });
3684
+ if (viaB) return viaB;
3685
+ }
3686
+ let modules;
3687
+ if (filePath && /\bimport\s*[{'"*]/.test(source)) {
3688
+ modules = evalAbsModuleGraph(source, filePath).modules;
3689
+ }
3690
+ const result = analyzeFn(source, fnName, absArgs, void 0, void 0, void 0, modules);
3691
+ if (result.shape.k === "unknown" && !result.term) {
3692
+ return void 0;
3693
+ }
3694
+ return result;
3695
+ } catch {
3696
+ return void 0;
3697
+ }
3698
+ }
3699
+ function tryEvalEntryAbs(source, fnName, args, filePath, mocks) {
3700
+ return tryEvalAbs(source, fnName, args, filePath, mocks);
3701
+ }
3702
+ function tryAttachIntension(caseResult, source, fnName) {
3703
+ try {
3704
+ const g = generalizeFromAst2(fnName, source);
3705
+ if (!g) return;
3706
+ const sym = g.symbolic;
3707
+ const intension = {
3708
+ display: g.display,
3709
+ abs: formatAbs3(sym),
3710
+ absMultiline: formatAbsMultiline(sym, fnName)
3711
+ };
3712
+ if (sym.term && sym.term.op !== "lit") {
3713
+ intension.term = termToString(sym.term);
3714
+ }
3715
+ if (sym.pred && sym.pred.op !== "true") {
3716
+ intension.pred = predToString(sym.pred);
3717
+ }
3718
+ intension.conf = sym.conf;
3719
+ caseResult.intension = intension;
3720
+ } catch {
3721
+ }
3722
+ }
3723
+ function attachAbsToIntension(caseResult, absVal, label) {
3724
+ const prev = caseResult.intension ?? {};
3725
+ caseResult.abs = absVal;
3726
+ caseResult.intension = {
3727
+ ...prev,
3728
+ abs: formatAbs3(absVal),
3729
+ absMultiline: formatAbsMultiline(absVal, label ?? caseResult.name),
3730
+ conf: absVal.conf
3731
+ };
3732
+ }
3733
+
3734
+ // src/index.ts
3735
+ import { collectAbsInlays } from "@nudojs/core";
3736
+
3737
+ // src/infer-json.ts
3738
+ import { typeValueToString as typeValueToString4 } from "@nudojs/core";
3739
+ function mapCase(c) {
3740
+ const out = {
3741
+ name: c.name,
3742
+ args: c.args.map((a) => typeValueToString4(a)),
3743
+ result: typeValueToString4(c.result),
3744
+ throws: c.throws.kind !== "never" ? typeValueToString4(c.throws) : null,
3745
+ source: c.source ?? null
3746
+ };
3747
+ if (c.aggregatedFrom !== void 0) out.aggregatedFrom = c.aggregatedFrom;
3748
+ if (c.intension) {
3749
+ const i = c.intension;
3750
+ out.intension = {
3751
+ ...i.display !== void 0 ? { display: i.display } : {},
3752
+ ...i.abs !== void 0 ? { abs: i.abs } : {},
3753
+ ...i.absMultiline !== void 0 ? { absMultiline: i.absMultiline } : {},
3754
+ ...i.term !== void 0 ? { term: i.term } : {},
3755
+ ...i.pred !== void 0 ? { pred: i.pred } : {},
3756
+ ...i.conf !== void 0 ? { conf: i.conf } : {}
3757
+ };
3758
+ }
3759
+ return out;
3760
+ }
3761
+ function mapFunction(f) {
3762
+ const out = {
3763
+ name: f.name,
3764
+ loc: f.loc,
3765
+ entryOnly: f.entryOnly ?? false,
3766
+ cases: f.cases.map(mapCase)
3767
+ };
3768
+ if (f.noDeclaration) out.noDeclaration = true;
3769
+ if (f.combined) out.combined = typeValueToString4(f.combined);
3770
+ return out;
3771
+ }
3772
+ function serializeInferJson(result, file) {
3773
+ const functions = result.functions.map(mapFunction);
3774
+ const external = result.externalFunctions?.map((f) => ({
3775
+ name: f.name,
3776
+ fromModule: f.fromModule,
3777
+ cases: f.cases.map(mapCase)
3778
+ }));
3779
+ const caseCount = functions.reduce((n, f) => n + f.cases.length, 0) + (external ?? []).reduce((n, f) => n + f.cases.length, 0);
3780
+ const json = {
3781
+ version: 1,
3782
+ file,
3783
+ summary: {
3784
+ functions: functions.length,
3785
+ externalFunctions: external?.length ?? 0,
3786
+ cases: caseCount,
3787
+ diagnostics: result.diagnostics.length
3788
+ },
3789
+ functions,
3790
+ diagnostics: result.diagnostics.map((d) => ({
3791
+ range: d.range,
3792
+ severity: d.severity,
3793
+ message: d.message,
3794
+ ...d.code !== void 0 ? { code: d.code } : {},
3795
+ ...d.suggestions ? { suggestions: d.suggestions } : {},
3796
+ ...d.tags ? { tags: d.tags } : {},
3797
+ ...d.origin ? { origin: d.origin } : {}
3798
+ }))
3799
+ };
3800
+ if (external) json.externalFunctions = external;
3801
+ return json;
3802
+ }
3803
+
3804
+ // src/target-path.ts
3805
+ function isNudoTargetPath(path) {
3806
+ const lower = path.toLowerCase();
3807
+ if (lower.endsWith(".d.ts")) return false;
3808
+ return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".ts");
3809
+ }
3810
+
3811
+ // src/load-module.ts
3812
+ import { existsSync as existsSync4, statSync as statSync4, readFileSync as readFileSync5 } from "fs";
3813
+ import { dirname as dirname6, resolve as resolve5, join as join3 } from "path";
3814
+ function defaultLoadModule(spec, fromFile) {
3815
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
3816
+ try {
3817
+ const base = dirname6(resolve5(fromFile));
3818
+ const p = resolve5(base, spec);
3819
+ for (const cand of [
3820
+ p,
3821
+ `${p}.js`,
3822
+ `${p}.mjs`,
3823
+ `${p}.ts`,
3824
+ join3(p, "index.js"),
3825
+ join3(p, "index.mjs"),
3826
+ join3(p, "index.ts")
3827
+ ]) {
3828
+ if (existsSync4(cand) && !statSync4(cand).isDirectory()) {
3829
+ return readFileSync5(cand, "utf-8");
3830
+ }
3831
+ }
3832
+ return void 0;
3833
+ } catch {
3834
+ return void 0;
3835
+ }
3836
+ }
3837
+
3838
+ // src/session-cache.ts
3839
+ import {
3840
+ resetGeneralizeMemo,
3841
+ resetCheckSourceMemo,
3842
+ resetNudoModuleExecCache,
3843
+ resetParseSourceCache
3844
+ } from "@nudojs/core";
3845
+ function evictAnalysisCachesForFiles(files) {
3846
+ if (files.length === 0) return;
3847
+ evictBPathCacheForFiles(files);
3848
+ evictAnalysisFileCacheForFiles(files);
3849
+ evictFnAnalysisCacheForFiles(files);
3850
+ evictAbsModuleCacheFiles(files);
3851
+ }
3852
+ function clearAnalysisSessionCaches() {
3853
+ clearBPathCache();
3854
+ clearAbsModuleCache();
3855
+ resetGeneralizeMemo();
3856
+ resetCheckSourceMemo();
3857
+ resetNudoModuleExecCache();
3858
+ }
3859
+ function resetAllAnalysisCaches() {
3860
+ clearAnalysisSessionCaches();
3861
+ resetParseSourceCache();
3862
+ }
3863
+
3864
+ // src/harvest-node.ts
3865
+ import { existsSync as existsSync5 } from "fs";
3866
+ import { harvestDts as harvestDts2 } from "@nudojs/harvester";
3867
+ function harvestNodeTypes(fromDir, maxFiles = 12, maxMs = 2500) {
3868
+ const root = resolvePackageRoot("@types/node", fromDir) ?? resolvePackageRoot("node", fromDir);
3869
+ if (!root || !existsSync5(root)) {
3870
+ return { ok: false, error: "@types/node not found" };
3871
+ }
3872
+ const dts = collectDtsFiles(root, maxFiles);
3873
+ if (dts.length === 0) {
3874
+ return { ok: false, error: `no .d.ts under ${root}` };
3875
+ }
3876
+ const env = harvestDts2(dts, { maxMs });
3877
+ return { ok: true, env, root, files: env.stats.files };
3878
+ }
3879
+ function summarizeNodeEnv(env) {
3880
+ return {
3881
+ modules: Object.keys(env.modules).sort(),
3882
+ globals: Object.keys(env.globals).sort().slice(0, 50),
3883
+ symbolCount: env.stats.symbols
3884
+ };
3885
+ }
3886
+
3887
+ // src/semantic-tokens.ts
3888
+ import { dirname as dirname7 } from "path";
3889
+ import { createEnvironment as createEnvironment3 } from "@nudojs/core";
3890
+ import { parse as parse6 } from "@nudojs/parser";
3891
+ import {
3892
+ evaluateProgram as evaluateProgram2,
3893
+ setModuleResolver as setModuleResolver2,
3894
+ setCurrentFileDir as setCurrentFileDir2,
3895
+ resetMemo as resetMemo2
3896
+ } from "@nudojs/cli/evaluator";
3897
+ var SEMANTIC_TOKEN_TYPES = [
3898
+ "function",
3899
+ "variable",
3900
+ "parameter",
3901
+ "property",
3902
+ "type",
3903
+ "keyword",
3904
+ "string",
3905
+ "number",
3906
+ "comment",
3907
+ "decorator",
3908
+ "method"
3909
+ ];
3910
+ var SEMANTIC_TOKEN_MODIFIERS = [
3911
+ "declaration",
3912
+ "readonly",
3913
+ "deprecated",
3914
+ "unreachable"
3915
+ ];
3916
+ function encodeSemanticTokens(tokens) {
3917
+ const result = [];
3918
+ let prevLine = 0;
3919
+ let prevChar = 0;
3920
+ for (const token of tokens) {
3921
+ const deltaLine = token.line - prevLine;
3922
+ const deltaChar = deltaLine === 0 ? token.char - prevChar : token.char;
3923
+ result.push(deltaLine, deltaChar, token.length, token.typeIndex, token.modifierBitmask);
3924
+ prevLine = token.line;
3925
+ prevChar = token.char;
3926
+ }
3927
+ return result;
3928
+ }
3929
+ var TYPE_FUNCTION = SEMANTIC_TOKEN_TYPES.indexOf("function");
3930
+ var TYPE_VARIABLE = SEMANTIC_TOKEN_TYPES.indexOf("variable");
3931
+ var TYPE_PARAMETER = SEMANTIC_TOKEN_TYPES.indexOf("parameter");
3932
+ var TYPE_PROPERTY = SEMANTIC_TOKEN_TYPES.indexOf("property");
3933
+ var TYPE_METHOD = SEMANTIC_TOKEN_TYPES.indexOf("method");
3934
+ var MOD_DECLARATION = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration");
3935
+ function buildSemanticTokens(filePath, source) {
3936
+ let ast;
3937
+ try {
3938
+ ast = parse6(source);
3939
+ } catch {
3940
+ return [];
3941
+ }
3942
+ const env = createEnvironment3();
3943
+ resetMemo2();
3944
+ setModuleResolver2(resolveModule);
3945
+ setCurrentFileDir2(dirname7(filePath));
3946
+ try {
3947
+ evaluateProgram2(ast, env);
3948
+ } catch {
3949
+ } finally {
3950
+ setModuleResolver2(null);
3951
+ }
3952
+ const program = ast.program ?? ast;
3953
+ const ownBindings = env.getOwnBindings();
3954
+ const topLevelDeclarators = /* @__PURE__ */ new Set();
3955
+ for (const stmt of program.body ?? []) {
3956
+ const decl = stmt.type === "ExportNamedDeclaration" || stmt.type === "ExportDefaultDeclaration" ? stmt.declaration ?? null : stmt;
3957
+ if (decl && decl.type === "VariableDeclaration") {
3958
+ for (const d of decl.declarations) {
3959
+ topLevelDeclarators.add(d);
3960
+ }
3961
+ }
3962
+ }
3963
+ const tokens = [];
3964
+ const pushIdentifier = (id, typeIndex) => {
3965
+ const loc = id.loc;
3966
+ const name = id.name;
3967
+ if (!loc || typeof name !== "string") return;
3968
+ tokens.push({
3969
+ line: loc.start.line - 1,
3970
+ char: loc.start.column,
3971
+ length: name.length,
3972
+ typeIndex,
3973
+ modifierBitmask: MOD_DECLARATION
3974
+ });
3975
+ };
3976
+ const collectParams = (params) => {
3977
+ for (const p of params) {
3978
+ if (!p) continue;
3979
+ if (p.type === "Identifier") pushIdentifier(p, TYPE_PARAMETER);
3980
+ else if (p.type === "AssignmentPattern" && p.left?.type === "Identifier") {
3981
+ pushIdentifier(p.left, TYPE_PARAMETER);
3982
+ } else if (p.type === "RestElement" && p.argument?.type === "Identifier") {
3983
+ pushIdentifier(p.argument, TYPE_PARAMETER);
3984
+ }
3985
+ }
3986
+ };
3987
+ const isFunctionValue = (name) => ownBindings[name]?.kind === "function";
3988
+ const visit = (node) => {
3989
+ if (!node || typeof node !== "object") return;
3990
+ const n = node;
3991
+ switch (n.type) {
3992
+ case "VariableDeclarator": {
3993
+ const id = n.id;
3994
+ if (id?.type === "Identifier") {
3995
+ const isTopLevel = topLevelDeclarators.has(node);
3996
+ const typeIndex = isTopLevel && isFunctionValue(id.name) ? TYPE_FUNCTION : TYPE_VARIABLE;
3997
+ pushIdentifier(id, typeIndex);
3998
+ }
3999
+ break;
4000
+ }
4001
+ case "FunctionDeclaration":
4002
+ case "FunctionExpression": {
4003
+ const id = n.id;
4004
+ if (id?.type === "Identifier") pushIdentifier(id, TYPE_FUNCTION);
4005
+ collectParams(n.params ?? []);
4006
+ break;
4007
+ }
4008
+ case "ArrowFunctionExpression":
4009
+ collectParams(n.params ?? []);
4010
+ break;
4011
+ case "ObjectMethod": {
4012
+ const key = n.key;
4013
+ if (key?.type === "Identifier" && !n.computed) pushIdentifier(key, TYPE_METHOD);
4014
+ collectParams(n.params ?? []);
4015
+ break;
4016
+ }
4017
+ case "ObjectProperty": {
4018
+ const key = n.key;
4019
+ const value = n.value;
4020
+ if (key?.type === "Identifier" && !n.computed) {
4021
+ const isFn = value?.type === "FunctionExpression" || value?.type === "ArrowFunctionExpression";
4022
+ pushIdentifier(key, isFn ? TYPE_METHOD : TYPE_PROPERTY);
4023
+ }
4024
+ break;
4025
+ }
4026
+ }
4027
+ for (const key of Object.keys(n)) {
4028
+ if (key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments" || key === "innerComments") continue;
4029
+ const child = n[key];
4030
+ if (Array.isArray(child)) {
4031
+ for (const item of child) {
4032
+ if (item && typeof item === "object" && typeof item.type === "string") {
4033
+ visit(item);
4034
+ }
4035
+ }
4036
+ } else if (child && typeof child === "object" && typeof child.type === "string") {
4037
+ visit(child);
4038
+ }
4039
+ }
4040
+ };
4041
+ visit(program);
4042
+ tokens.sort((a, b) => a.line - b.line || a.char - b.char);
4043
+ return encodeSemanticTokens(tokens);
4044
+ }
1303
4045
 
1304
4046
  // src/dts-generator.ts
1305
- import { T as T2, isTemplate, getTemplateParts, simplifyUnion as simplifyUnion2, typeValueEquals } from "@nudojs/core";
4047
+ import { T as T3, isTemplate, getTemplateParts, simplifyUnion as simplifyUnion2, typeValueEquals, getFnSig as getFnSig4 } from "@nudojs/core";
1306
4048
  function typeValueToTSType(tv) {
1307
4049
  switch (tv.kind) {
1308
4050
  case "literal": {
@@ -1337,8 +4079,8 @@ function typeValueToTSType(tv) {
1337
4079
  }
1338
4080
  case "function": {
1339
4081
  const params = tv.params.map((p) => `${p}: unknown`).join(", ");
1340
- const returnType = tv._returnType;
1341
- const retStr = returnType ? typeValueToTSType(returnType) : "unknown";
4082
+ const sig = getFnSig4(tv);
4083
+ const retStr = sig?.returnType ? typeValueToTSType(sig.returnType) : "unknown";
1342
4084
  return `(${params}) => ${retStr}`;
1343
4085
  }
1344
4086
  case "promise":
@@ -1367,10 +4109,10 @@ function getParamName(fn, index) {
1367
4109
  function widenLiteralToBase(tv) {
1368
4110
  if (tv.kind !== "literal") return tv;
1369
4111
  const v = tv.value;
1370
- if (typeof v === "number") return T2.number;
1371
- if (typeof v === "string") return T2.string;
1372
- if (typeof v === "boolean") return T2.boolean;
1373
- if (typeof v === "bigint") return T2.bigint;
4112
+ if (typeof v === "number") return T3.number;
4113
+ if (typeof v === "string") return T3.string;
4114
+ if (typeof v === "boolean") return T3.boolean;
4115
+ if (typeof v === "bigint") return T3.bigint;
1374
4116
  return tv;
1375
4117
  }
1376
4118
  function widenTopLevel(tv) {
@@ -1392,18 +4134,18 @@ function widenParamType(tv) {
1392
4134
  const widened = tv.elements.map(widenParamType);
1393
4135
  const first = widened[0];
1394
4136
  if (first && widened.every((el) => typeValueEquals(el, first))) {
1395
- return T2.array(first);
4137
+ return T3.array(first);
1396
4138
  }
1397
- return T2.tuple(widened);
4139
+ return T3.tuple(widened);
1398
4140
  }
1399
4141
  case "array":
1400
- return T2.array(widenParamType(tv.element));
4142
+ return T3.array(widenParamType(tv.element));
1401
4143
  case "object":
1402
- return T2.object(widenProperties(tv.properties));
4144
+ return T3.object(widenProperties(tv.properties));
1403
4145
  case "promise":
1404
- return T2.promise(widenParamType(tv.value));
4146
+ return T3.promise(widenParamType(tv.value));
1405
4147
  case "instance":
1406
- return T2.instanceOf(tv.className, widenProperties(tv.properties));
4148
+ return T3.instanceOf(tv.className, widenProperties(tv.properties));
1407
4149
  case "refined":
1408
4150
  return widenParamType(tv.base);
1409
4151
  default:
@@ -1539,12 +4281,19 @@ function typeValueToZodSchema(tv) {
1539
4281
  }
1540
4282
 
1541
4283
  // src/guard-generator.ts
4284
+ import { denoteGuard } from "@nudojs/core";
1542
4285
  function generateGuardFunction(name, tv) {
1543
4286
  const body = generateGuardBody(tv, "data");
1544
4287
  return `export function ${name}(data) {
1545
4288
  return ${body};
1546
4289
  }`;
1547
4290
  }
4291
+ function generateGuardFunctionFromAbs(name, abs) {
4292
+ const body = denoteGuard(abs, "data");
4293
+ return `export function ${name}(data) {
4294
+ return ${body};
4295
+ }`;
4296
+ }
1548
4297
  function generateGuardBody(tv, varName) {
1549
4298
  switch (tv.kind) {
1550
4299
  case "literal": {
@@ -1590,7 +4339,7 @@ function generateGuardBody(tv, varName) {
1590
4339
  }
1591
4340
 
1592
4341
  // src/case-emitter.ts
1593
- import { parse as parse2, extractDirectives as extractDirectives2 } from "@nudojs/parser";
4342
+ import { parse as parse7, extractDirectives as extractDirectives2 } from "@nudojs/parser";
1594
4343
  var GENERATED_PREFIX = "call@";
1595
4344
  var GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
1596
4345
  var IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
@@ -1636,6 +4385,7 @@ function serializeCaseArg(tv) {
1636
4385
  if (typeof v === "boolean") return v ? "true" : "false";
1637
4386
  if (v === null) return "null";
1638
4387
  if (v === void 0) return "undefined";
4388
+ if (typeof v === "bigint") return null;
1639
4389
  return serializeStringLiteral(v);
1640
4390
  }
1641
4391
  case "union": {
@@ -1732,7 +4482,7 @@ function collectExistingCases(source) {
1732
4482
  const byName = /* @__PURE__ */ new Map();
1733
4483
  let ast;
1734
4484
  try {
1735
- ast = parse2(source, { errorRecovery: true });
4485
+ ast = parse7(source, { errorRecovery: true });
1736
4486
  } catch {
1737
4487
  return byName;
1738
4488
  }
@@ -1942,23 +4692,78 @@ function unifiedDiff(a, b, path) {
1942
4692
  return out.join("\n") + "\n";
1943
4693
  }
1944
4694
  export {
4695
+ SEMANTIC_TOKEN_MODIFIERS,
4696
+ SEMANTIC_TOKEN_TYPES,
4697
+ analyzeExportsFromSource,
1945
4698
  analyzeFile,
1946
4699
  analyzeFileAsync,
4700
+ autoHarvestModules,
4701
+ barePackageName,
4702
+ bareSpecToAbsModules,
1947
4703
  buildCaseDirective,
1948
4704
  buildModuleGraph,
4705
+ buildSemanticTokens,
4706
+ clearAbsModuleCache,
4707
+ clearAnalysisFileCache,
4708
+ clearAnalysisSessionCaches,
4709
+ clearBPathCache,
4710
+ clearFnAnalysisCache,
4711
+ clearHarvestCache,
4712
+ collectAbsBindingsFromGraph,
4713
+ collectAbsInlays,
4714
+ collectBPathDiagnostics,
4715
+ collectBPathReplacements,
4716
+ collectBarePackages,
1949
4717
  collectCallRecords,
4718
+ collectDependencySpecs,
4719
+ collectDtsFromEntry,
4720
+ collectEnvGlobals,
4721
+ collectEnvModules,
4722
+ collectStaticImports,
1950
4723
  computeDirtySet,
4724
+ defaultAbsLoadModule,
4725
+ defaultLoadModule,
4726
+ encodeSemanticTokens,
4727
+ envValueToAbs,
4728
+ evalAbsModuleGraph,
4729
+ evalProgramAbsWithModules,
4730
+ evictAbsModuleCacheFiles,
4731
+ evictAnalysisCachesForFiles,
4732
+ evictAnalysisFileCacheForFiles,
4733
+ evictBPathCacheForFiles,
4734
+ evictFnAnalysisCacheForFiles,
4735
+ formatHarvestSummary,
1951
4736
  generateDts,
1952
4737
  generateFunctionDtsLines,
1953
4738
  generateGuardFunction,
4739
+ generateGuardFunctionFromAbs,
4740
+ getAnalysisFileCacheSize,
1954
4741
  getCasesForFile,
1955
4742
  getCompletionsAtPosition,
4743
+ getHoverAtPosition,
1956
4744
  getTypeAtPosition,
1957
4745
  getTypeAtPositionAsync,
4746
+ harvestNodeTypes,
4747
+ harvestPackage,
4748
+ harvestPackageCached,
4749
+ harvestToAbsModules,
4750
+ harvestedValueToAbs,
1958
4751
  insertGeneratedCaseDirectives,
4752
+ isBPathCapable,
4753
+ isNudoTargetPath,
4754
+ lookupHarvested,
4755
+ mockDirectivesToAbsSeeds,
4756
+ packageHarvestToAbsModules,
4757
+ resetAllAnalysisCaches,
4758
+ resolvePackageRoot,
1959
4759
  serializeCaseArg,
4760
+ serializeInferJson,
1960
4761
  stripGeneratedCaseDirectives,
4762
+ summarizeNodeEnv,
1961
4763
  topoSortDirty,
4764
+ tryBPathCall,
4765
+ tryBPathCallFull,
4766
+ tryRunBPath,
1962
4767
  typeValueToTSType,
1963
4768
  typeValueToZodSchema,
1964
4769
  unifiedDiff