@nudojs/service 0.3.2 → 1.0.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.
package/dist/index.js CHANGED
@@ -1,6 +1,38 @@
1
+ import {
2
+ BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS,
3
+ USAGE_SITE_MODULE,
4
+ evaluate,
5
+ evaluateFunction,
6
+ evaluateFunctionFull,
7
+ evaluateProgram,
8
+ findProjectConfig,
9
+ getUnreachableRanges,
10
+ interfaceConfig,
11
+ loadEnvs,
12
+ memberMayExistOn,
13
+ preloadPathEnvs,
14
+ resetEnvModules,
15
+ resetMemo,
16
+ resetMockModules,
17
+ resetUnreachableRanges,
18
+ resolveNpmNudo,
19
+ setCallCollector,
20
+ setCurrentFileDir,
21
+ setCurrentSource,
22
+ setEnvModules,
23
+ setMockModules,
24
+ setModuleResolver,
25
+ setNodeTypeCollector,
26
+ setProvenanceTracking,
27
+ setSampleCount,
28
+ setUnknownBuiltinHandler,
29
+ setUnknownCollector,
30
+ setUsageSiteTag
31
+ } from "./chunk-FSIHAE7I.js";
32
+
1
33
  // src/analyzer.ts
2
- import { readFileSync as readFileSync4, existsSync as existsSync3, statSync as statSync3, realpathSync } from "fs";
3
- import { resolve as resolve4, dirname as dirname5 } from "path";
34
+ import { readFileSync as readFileSync5, existsSync as existsSync4, statSync as statSync4, realpathSync } from "fs";
35
+ import { resolve as resolve5, dirname as dirname6 } from "path";
4
36
  import traverse from "@babel/traverse";
5
37
  import {
6
38
  T as T2,
@@ -11,7 +43,6 @@ import {
11
43
  createEnvironment as createEnvironment2,
12
44
  isSubtypeOf,
13
45
  mockHelperToTypeValue,
14
- getFnSig as getFnSig3,
15
46
  generalizeFromAst as generalizeFromAst2,
16
47
  termToString,
17
48
  predToString,
@@ -20,8 +51,7 @@ import {
20
51
  setAbsCallCollector,
21
52
  setAbsNodeCollector,
22
53
  absFunction as absFunction5,
23
- collectAbsNodeTypes,
24
- findAbsAtPosition,
54
+ checkInjectedDomainEvidence,
25
55
  typeValueToAbs as typeValueToAbs5,
26
56
  absToTypeValue as absToTypeValue2,
27
57
  formatAbs as formatAbs3,
@@ -30,36 +60,6 @@ import {
30
60
  fnFingerprints
31
61
  } from "@nudojs/core";
32
62
  import { parse as parse5, extractDirectives, extractFileDirectives, parseTypeValueExpr as parseTypeValueExpr2 } from "@nudojs/parser";
33
- import {
34
- evaluate,
35
- evaluateFunction,
36
- evaluateFunctionFull,
37
- memberMayExistOn,
38
- setUsageSiteTag,
39
- USAGE_SITE_MODULE,
40
- evaluateProgram,
41
- setModuleResolver,
42
- setCurrentFileDir,
43
- resetMemo,
44
- getUnreachableRanges,
45
- resetUnreachableRanges,
46
- setNodeTypeCollector,
47
- setCallCollector,
48
- setUnknownCollector,
49
- setProvenanceTracking,
50
- setSampleCount,
51
- setUnknownBuiltinHandler,
52
- setEnvModules,
53
- resetEnvModules,
54
- setMockModules,
55
- resetMockModules,
56
- setCurrentSource,
57
- loadEnvs as loadEnvs2,
58
- preloadPathEnvs,
59
- findProjectConfig,
60
- resolveNpmNudo,
61
- BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS
62
- } from "@nudojs/cli/evaluator";
63
63
 
64
64
  // src/mock-abs.ts
65
65
  import { parseTypeValueExpr } from "@nudojs/parser";
@@ -268,17 +268,44 @@ function mockDirectivesToAbsSeeds(functions) {
268
268
  return { seedVars, seedFns };
269
269
  }
270
270
 
271
+ // src/load-module.ts
272
+ import { existsSync, statSync, readFileSync } from "fs";
273
+ import { dirname, resolve, join } from "path";
274
+ function defaultLoadModule(spec, fromFile) {
275
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
276
+ try {
277
+ const base = dirname(resolve(fromFile));
278
+ const p = resolve(base, spec);
279
+ for (const cand of [
280
+ p,
281
+ `${p}.js`,
282
+ `${p}.mjs`,
283
+ `${p}.ts`,
284
+ join(p, "index.js"),
285
+ join(p, "index.mjs"),
286
+ join(p, "index.ts")
287
+ ]) {
288
+ if (existsSync(cand) && !statSync(cand).isDirectory()) {
289
+ return readFileSync(cand, "utf-8");
290
+ }
291
+ }
292
+ return void 0;
293
+ } catch {
294
+ return void 0;
295
+ }
296
+ }
297
+
271
298
  // src/harvest-auto.ts
272
299
  import { parse as parse2 } from "@nudojs/parser";
273
300
 
274
301
  // src/static-imports.ts
275
302
  import { parse } from "@nudojs/parser";
276
- import { readFileSync, existsSync } from "fs";
277
- import { resolve, dirname, join } from "path";
303
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
304
+ import { resolve as resolve2, dirname as dirname2, join as join2 } from "path";
278
305
  import { generalizeFromAst } from "@nudojs/core";
279
306
  function resolveRelative(fromFile, spec) {
280
307
  if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
281
- const base = resolve(dirname(fromFile), spec);
308
+ const base = resolve2(dirname2(fromFile), spec);
282
309
  for (const cand of [
283
310
  base,
284
311
  base + ".js",
@@ -287,12 +314,12 @@ function resolveRelative(fromFile, spec) {
287
314
  base + ".ts",
288
315
  base + ".mts",
289
316
  base + ".cts",
290
- join(base, "index.js"),
291
- join(base, "index.cjs"),
292
- join(base, "index.mjs"),
293
- join(base, "index.ts")
317
+ join2(base, "index.js"),
318
+ join2(base, "index.cjs"),
319
+ join2(base, "index.mjs"),
320
+ join2(base, "index.ts")
294
321
  ]) {
295
- if (existsSync(cand)) return cand;
322
+ if (existsSync2(cand)) return cand;
296
323
  }
297
324
  return void 0;
298
325
  }
@@ -411,15 +438,15 @@ function collectCjsExports(stmt, named, setDefault) {
411
438
  function collectStaticImports(entryFile, maxDepth = 8) {
412
439
  const graph = /* @__PURE__ */ new Map();
413
440
  const queue = [
414
- { file: resolve(entryFile), depth: 0 }
441
+ { file: resolve2(entryFile), depth: 0 }
415
442
  ];
416
443
  const seen = /* @__PURE__ */ new Set();
417
444
  while (queue.length > 0) {
418
445
  const { file, depth } = queue.shift();
419
446
  if (seen.has(file) || depth > maxDepth) continue;
420
447
  seen.add(file);
421
- if (!existsSync(file)) continue;
422
- const source = readFileSync(file, "utf8");
448
+ if (!existsSync2(file)) continue;
449
+ const source = readFileSync2(file, "utf8");
423
450
  const mod = analyzeExportsFromSource(file, source);
424
451
  graph.set(file, mod);
425
452
  const ast = parse(source);
@@ -432,19 +459,19 @@ function collectStaticImports(entryFile, maxDepth = 8) {
432
459
  }
433
460
 
434
461
  // src/harvest-package.ts
435
- import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
436
- import { join as join2, resolve as resolve2, dirname as dirname2, basename } from "path";
462
+ import { existsSync as existsSync3, readdirSync, statSync as statSync2, readFileSync as readFileSync3 } from "fs";
463
+ import { join as join3, resolve as resolve3, dirname as dirname3, basename } from "path";
437
464
  import { harvestDts } from "@nudojs/harvester";
438
465
  import { typeValueToString as typeValueToString2 } from "@nudojs/core";
439
466
  function resolvePackageRoot(pkg, fromDir = process.cwd()) {
440
- let dir = resolve2(fromDir);
467
+ let dir = resolve3(fromDir);
441
468
  const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
442
469
  for (let i = 0; i < 8; i++) {
443
- const typesPath = join2(dir, "node_modules", "@types", bare);
444
- if (existsSync2(typesPath)) return typesPath;
445
- const pkgPath = join2(dir, "node_modules", pkg);
446
- if (existsSync2(pkgPath)) return pkgPath;
447
- const parent = resolve2(dir, "..");
470
+ const typesPath = join3(dir, "node_modules", "@types", bare);
471
+ if (existsSync3(typesPath)) return typesPath;
472
+ const pkgPath = join3(dir, "node_modules", pkg);
473
+ if (existsSync3(pkgPath)) return pkgPath;
474
+ const parent = resolve3(dir, "..");
448
475
  if (parent === dir) break;
449
476
  dir = parent;
450
477
  }
@@ -474,10 +501,10 @@ function collectDtsFiles(root, maxFiles = 8) {
474
501
  for (const name of entries) {
475
502
  if (out.length >= maxFiles * 3) return;
476
503
  if (SKIP_DIRS.has(name)) continue;
477
- const p = join2(dir, name);
504
+ const p = join3(dir, name);
478
505
  let st;
479
506
  try {
480
- st = statSync(p);
507
+ st = statSync2(p);
481
508
  } catch {
482
509
  continue;
483
510
  }
@@ -493,15 +520,15 @@ function collectDtsFiles(root, maxFiles = 8) {
493
520
  return out.slice(0, maxFiles);
494
521
  }
495
522
  function entryDtsFromPackageJson(root) {
496
- const pj = join2(root, "package.json");
497
- if (!existsSync2(pj)) return void 0;
523
+ const pj = join3(root, "package.json");
524
+ if (!existsSync3(pj)) return void 0;
498
525
  try {
499
- const raw = JSON.parse(readFileSync2(pj, "utf8"));
526
+ const raw = JSON.parse(readFileSync3(pj, "utf8"));
500
527
  const entry = raw.types ?? raw.typings;
501
528
  if (typeof entry === "string" && entry.endsWith(".d.ts")) {
502
- const p = resolve2(root, entry);
503
- if (existsSync2(p)) {
504
- const st = statSync(p);
529
+ const p = resolve3(root, entry);
530
+ if (existsSync3(p)) {
531
+ const st = statSync2(p);
505
532
  if (st.size <= MAX_DTS_BYTES) return p;
506
533
  }
507
534
  }
@@ -517,24 +544,24 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
517
544
  const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
518
545
  while (queue.length > 0 && files.length < maxFiles) {
519
546
  const current = queue.shift();
520
- if (seen.has(current) || !existsSync2(current)) continue;
547
+ if (seen.has(current) || !existsSync3(current)) continue;
521
548
  seen.add(current);
522
549
  if (!current.endsWith(".d.ts")) continue;
523
550
  files.push(current);
524
551
  let text;
525
552
  try {
526
- text = readFileSync2(current, "utf-8");
553
+ text = readFileSync3(current, "utf-8");
527
554
  } catch {
528
555
  continue;
529
556
  }
530
- const dir = dirname2(current);
557
+ const dir = dirname3(current);
531
558
  for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
532
- queue.push(resolve2(dir, match[1]));
559
+ queue.push(resolve3(dir, match[1]));
533
560
  }
534
561
  for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
535
- const base = resolve2(dir, match[1]);
536
- for (const candidate of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
537
- if (existsSync2(candidate)) {
562
+ const base = resolve3(dir, match[1]);
563
+ for (const candidate of [`${base}.d.ts`, join3(base, "index.d.ts")]) {
564
+ if (existsSync3(candidate)) {
538
565
  queue.push(candidate);
539
566
  break;
540
567
  }
@@ -546,11 +573,11 @@ function collectDtsFromEntry(entry, maxFiles = 200) {
546
573
  function harvestPackage(pkg, fromDir, maxFiles = 8) {
547
574
  const root = resolvePackageRoot(pkg, fromDir);
548
575
  if (!root) return { error: `package not found: ${pkg}` };
549
- const entry = entryDtsFromPackageJson(root) ?? join2(root, "index.d.ts");
550
- let dtsFiles = existsSync2(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
576
+ const entry = entryDtsFromPackageJson(root) ?? join3(root, "index.d.ts");
577
+ let dtsFiles = existsSync3(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
551
578
  if (dtsFiles.length === 0) {
552
579
  const collected = collectDtsFiles(root, maxFiles);
553
- dtsFiles = entry && existsSync2(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
580
+ dtsFiles = entry && existsSync3(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
554
581
  }
555
582
  if (dtsFiles.length === 0) {
556
583
  return { error: `no .d.ts under ${root}` };
@@ -639,8 +666,8 @@ function autoHarvestModules(source, fromDir) {
639
666
  }
640
667
 
641
668
  // src/abs-modules-graph.ts
642
- import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
643
- import { dirname as dirname4, resolve as resolve3 } from "path";
669
+ import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
670
+ import { dirname as dirname5, resolve as resolve4 } from "path";
644
671
  import { parse as parse3 } from "@nudojs/parser";
645
672
  import {
646
673
  evalProgramAbs,
@@ -656,7 +683,7 @@ import {
656
683
  getFnSig,
657
684
  absFunction as absFunction2
658
685
  } from "@nudojs/core";
659
- import { dirname as dirname3 } from "path";
686
+ import { dirname as dirname4 } from "path";
660
687
  function markMockConf2(a) {
661
688
  a.conf = confJoin2(a.conf, "mock");
662
689
  return a;
@@ -728,7 +755,7 @@ function bareSpecToAbsModules(spec, fromFile) {
728
755
  const parts = spec.split("/");
729
756
  const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
730
757
  if (!pkg) return void 0;
731
- const table = harvestToAbsModules(pkg, dirname3(fromFile));
758
+ const table = harvestToAbsModules(pkg, dirname4(fromFile));
732
759
  return table[spec] ?? table[pkg];
733
760
  }
734
761
 
@@ -736,10 +763,10 @@ function bareSpecToAbsModules(spec, fromFile) {
736
763
  function defaultAbsLoadModule(spec, fromFile) {
737
764
  if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
738
765
  try {
739
- const p = resolve3(dirname4(resolve3(fromFile)), spec);
740
- for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve3(p, "index.js")]) {
766
+ const p = resolve4(dirname5(resolve4(fromFile)), spec);
767
+ for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
741
768
  try {
742
- return readFileSync3(cand, "utf-8");
769
+ return readFileSync4(cand, "utf-8");
743
770
  } catch {
744
771
  }
745
772
  }
@@ -749,14 +776,14 @@ function defaultAbsLoadModule(spec, fromFile) {
749
776
  }
750
777
  }
751
778
  function relCandidates(spec, fromFile) {
752
- const p = resolve3(dirname4(resolve3(fromFile)), spec);
753
- return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve3(p, "index.js")];
779
+ const p = resolve4(dirname5(resolve4(fromFile)), spec);
780
+ return [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")];
754
781
  }
755
782
  function resolveRel(spec, fromFile) {
756
783
  if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
757
784
  for (const cand of relCandidates(spec, fromFile)) {
758
785
  try {
759
- readFileSync3(cand, "utf-8");
786
+ readFileSync4(cand, "utf-8");
760
787
  return cand;
761
788
  } catch {
762
789
  }
@@ -854,7 +881,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
854
881
  const shared = absModuleCache.get(absPath);
855
882
  if (shared) {
856
883
  try {
857
- const st = statSync2(absPath);
884
+ const st = statSync3(absPath);
858
885
  if (st.mtimeMs === shared.mtimeMs && st.size === shared.size) {
859
886
  for (const iss of shared.issues) pushIssue(iss.kind, iss.label, iss.reason);
860
887
  cache.set(absPath, shared.exports);
@@ -919,7 +946,7 @@ function evalAbsModuleGraph(entrySource, entryFile, opts = {}) {
919
946
  cache.set(absPath, exports);
920
947
  let fingerprint;
921
948
  try {
922
- const st = statSync2(absPath);
949
+ const st = statSync3(absPath);
923
950
  fingerprint = { mtimeMs: st.mtimeMs, size: st.size };
924
951
  } catch {
925
952
  }
@@ -994,7 +1021,6 @@ import {
994
1021
  getFnImpl as getFnImpl2
995
1022
  } from "@nudojs/core";
996
1023
  import { parse as parse4, extractInlineDirectives } from "@nudojs/parser";
997
- import { loadEnvs } from "@nudojs/cli/evaluator";
998
1024
 
999
1025
  // src/env-to-abs.ts
1000
1026
  import {
@@ -1114,10 +1140,10 @@ function evictFnAnalysisCacheForFiles(files) {
1114
1140
  }
1115
1141
  return n;
1116
1142
  }
1117
- function caseDirectiveKey(directives, typeValueToString5) {
1143
+ function caseDirectiveKey(directives, typeValueToString7) {
1118
1144
  return directives.map((d) => {
1119
1145
  if (d.kind === "case") {
1120
- const args = (d.args ?? []).map((a) => typeValueToString5(a)).join(",");
1146
+ const args = (d.args ?? []).map((a) => typeValueToString7(a)).join(",");
1121
1147
  return `c:${d.name ?? ""}:${args}`;
1122
1148
  }
1123
1149
  return d.kind;
@@ -1576,23 +1602,23 @@ function resolveModule(source, fromDir) {
1576
1602
  const extensions = [".js", ".ts", ".mjs"];
1577
1603
  const nudoPath = resolveNpmNudo(source, fromDir);
1578
1604
  if (nudoPath) {
1579
- const src = readFileSync4(nudoPath, "utf-8");
1605
+ const src = readFileSync5(nudoPath, "utf-8");
1580
1606
  return { ast: parse5(src), filePath: nudoPath };
1581
1607
  }
1582
- const basePath = resolve4(fromDir, source);
1608
+ const basePath = resolve5(fromDir, source);
1583
1609
  for (const ext of ["", ...extensions]) {
1584
1610
  const candidate = basePath + ext;
1585
- if (!existsSync3(candidate)) continue;
1586
- if (statSync3(candidate).isDirectory()) {
1611
+ if (!existsSync4(candidate)) continue;
1612
+ if (statSync4(candidate).isDirectory()) {
1587
1613
  let entry = null;
1588
- const pkgPath = resolve4(candidate, "package.json");
1589
- if (existsSync3(pkgPath)) {
1614
+ const pkgPath = resolve5(candidate, "package.json");
1615
+ if (existsSync4(pkgPath)) {
1590
1616
  try {
1591
- const main = JSON.parse(readFileSync4(pkgPath, "utf-8")).main;
1617
+ const main = JSON.parse(readFileSync5(pkgPath, "utf-8")).main;
1592
1618
  if (typeof main === "string") {
1593
1619
  for (const e of ["", ...extensions]) {
1594
- const p = resolve4(candidate, main + e);
1595
- if (existsSync3(p) && statSync3(p).isFile()) {
1620
+ const p = resolve5(candidate, main + e);
1621
+ if (existsSync4(p) && statSync4(p).isFile()) {
1596
1622
  entry = p;
1597
1623
  break;
1598
1624
  }
@@ -1603,34 +1629,34 @@ function resolveModule(source, fromDir) {
1603
1629
  }
1604
1630
  if (!entry) {
1605
1631
  for (const e of ["", ...extensions]) {
1606
- const p = resolve4(candidate, "index" + e);
1607
- if (existsSync3(p) && statSync3(p).isFile()) {
1632
+ const p = resolve5(candidate, "index" + e);
1633
+ if (existsSync4(p) && statSync4(p).isFile()) {
1608
1634
  entry = p;
1609
1635
  break;
1610
1636
  }
1611
1637
  }
1612
1638
  }
1613
1639
  if (!entry) return null;
1614
- const src2 = readFileSync4(entry, "utf-8");
1640
+ const src2 = readFileSync5(entry, "utf-8");
1615
1641
  return { ast: parse5(src2), filePath: entry };
1616
1642
  }
1617
1643
  if (candidate.endsWith(".json")) {
1618
1644
  try {
1619
- return { ast: parse5("module.exports = undefined;"), filePath: candidate, json: JSON.parse(readFileSync4(candidate, "utf-8")) };
1645
+ return { ast: parse5("module.exports = undefined;"), filePath: candidate, json: JSON.parse(readFileSync5(candidate, "utf-8")) };
1620
1646
  } catch {
1621
1647
  return null;
1622
1648
  }
1623
1649
  }
1624
- const src = readFileSync4(candidate, "utf-8");
1650
+ const src = readFileSync5(candidate, "utf-8");
1625
1651
  return { ast: parse5(src), filePath: candidate };
1626
1652
  }
1627
1653
  return null;
1628
1654
  }
1629
1655
  function resolveImportPath(specifier, fromDir) {
1630
- const basePath = resolve4(fromDir, specifier);
1656
+ const basePath = resolve5(fromDir, specifier);
1631
1657
  for (const ext of ["", ".js", ".ts", ".mjs"]) {
1632
1658
  const candidate = basePath + ext;
1633
- if (existsSync3(candidate)) return candidate;
1659
+ if (existsSync4(candidate)) return candidate;
1634
1660
  }
1635
1661
  return null;
1636
1662
  }
@@ -1650,7 +1676,7 @@ function buildModuleGraph(files, cache) {
1650
1676
  if (cache) {
1651
1677
  let stat = null;
1652
1678
  try {
1653
- stat = statSync3(file);
1679
+ stat = statSync4(file);
1654
1680
  } catch {
1655
1681
  }
1656
1682
  const cached = stat ? cache.get(file) : void 0;
@@ -1671,7 +1697,7 @@ function extractImportEdges(file) {
1671
1697
  const edges = [];
1672
1698
  let ast;
1673
1699
  try {
1674
- ast = parse5(readFileSync4(file, "utf-8"));
1700
+ ast = parse5(readFileSync5(file, "utf-8"));
1675
1701
  } catch {
1676
1702
  return edges;
1677
1703
  }
@@ -1679,7 +1705,7 @@ function extractImportEdges(file) {
1679
1705
  if (stmt.type !== "ImportDeclaration") continue;
1680
1706
  const specifier = stmt.source.value;
1681
1707
  if (!specifier.startsWith(".") && !specifier.startsWith("/")) continue;
1682
- const resolved = resolveImportPath(specifier, dirname5(file));
1708
+ const resolved = resolveImportPath(specifier, dirname6(file));
1683
1709
  if (resolved) edges.push(resolved);
1684
1710
  }
1685
1711
  return edges;
@@ -1757,8 +1783,8 @@ function applyMocks(directives, env, filePath, diagnostics) {
1757
1783
  }
1758
1784
  env.bind(d.name, parseTypeValueExpr2(d.expression));
1759
1785
  } else if (d.fromPath) {
1760
- const mockPath = resolve4(dirname5(filePath), d.fromPath);
1761
- const mockSource = readFileSync4(mockPath, "utf-8");
1786
+ const mockPath = resolve5(dirname6(filePath), d.fromPath);
1787
+ const mockSource = readFileSync5(mockPath, "utf-8");
1762
1788
  const mockAst = parse5(mockSource);
1763
1789
  const mockEnv = createEnvironment2();
1764
1790
  evaluateProgram(mockAst, mockEnv);
@@ -1822,6 +1848,16 @@ function resolveFunctionNode(node) {
1822
1848
  }
1823
1849
  return node;
1824
1850
  }
1851
+ function fnNameLoc(node, fallback) {
1852
+ const unwrap = (n) => n.type === "ExportNamedDeclaration" && n.declaration ? n.declaration : n.type === "ExportDefaultDeclaration" ? n.declaration : n;
1853
+ const d = unwrap(node);
1854
+ if (d.type === "VariableDeclaration") {
1855
+ const id = d.declarations[0]?.id;
1856
+ if (id?.type === "Identifier" && id.loc) return id.loc;
1857
+ }
1858
+ if (d.id?.loc) return d.id.loc;
1859
+ return fallback;
1860
+ }
1825
1861
  function isFnExprValue(node) {
1826
1862
  return !!node && (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression");
1827
1863
  }
@@ -2180,14 +2216,14 @@ function collectEnvNames(filePath, source, includeProject) {
2180
2216
  const fileDirectives = extractFileDirectives(ast);
2181
2217
  const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
2182
2218
  if (!includeProject) return fileEnvNames;
2183
- const projectConfig = findProjectConfig(dirname5(filePath));
2219
+ const projectConfig = findProjectConfig(dirname6(filePath));
2184
2220
  const projectEnvNames = projectConfig?.config.env ?? [];
2185
2221
  return [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
2186
2222
  }
2187
2223
  async function analyzeFileAsync(filePath, source, activeCases, externalCallRecords) {
2188
2224
  const envNames = collectEnvNames(filePath, source, true);
2189
2225
  if (envNames.length > 0) {
2190
- await preloadPathEnvs(envNames, dirname5(filePath));
2226
+ await preloadPathEnvs(envNames, dirname6(filePath));
2191
2227
  }
2192
2228
  return analyzeFile(filePath, source, activeCases, externalCallRecords);
2193
2229
  }
@@ -2199,7 +2235,7 @@ function collectCallRecords(filePath, source) {
2199
2235
  resetEnvModules();
2200
2236
  resetMockModules();
2201
2237
  setModuleResolver(resolveModule);
2202
- setCurrentFileDir(dirname5(filePath));
2238
+ setCurrentFileDir(dirname6(filePath));
2203
2239
  setCurrentSource(source);
2204
2240
  setCallCollector((record) => records.push(record));
2205
2241
  try {
@@ -2330,11 +2366,11 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
2330
2366
  resetEnvModules();
2331
2367
  resetMockModules();
2332
2368
  setModuleResolver(resolveModule);
2333
- setCurrentFileDir(dirname5(filePath));
2369
+ setCurrentFileDir(dirname6(filePath));
2334
2370
  setCurrentSource(source);
2335
2371
  const fileDirectives = extractFileDirectives(ast);
2336
2372
  const fileEnvNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
2337
- const projectConfig = findProjectConfig(dirname5(filePath));
2373
+ const projectConfig = findProjectConfig(dirname6(filePath));
2338
2374
  const projectEnvNames = projectConfig?.config.env ?? [];
2339
2375
  const envNames = [.../* @__PURE__ */ new Set([...projectEnvNames, ...fileEnvNames])];
2340
2376
  setUnknownBuiltinHandler((name, loc) => {
@@ -2352,15 +2388,15 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
2352
2388
  });
2353
2389
  const globalEnv = createEnvironment2();
2354
2390
  {
2355
- const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
2356
- const auto = autoHarvestModules(source, dirname5(filePath));
2391
+ const loaded = envNames.length > 0 ? loadEnvs(envNames, globalEnv) : { modules: {} };
2392
+ const auto = autoHarvestModules(source, dirname6(filePath));
2357
2393
  const modules = { ...loaded.modules, ...auto };
2358
2394
  if (Object.keys(modules).length > 0) setEnvModules(modules);
2359
2395
  }
2360
2396
  const mocks = /* @__PURE__ */ new Map();
2361
2397
  if (projectConfig?.config.mocks) {
2362
2398
  for (const [source2, mockPath] of Object.entries(projectConfig.config.mocks)) {
2363
- mocks.set(source2, { fromPath: resolve4(projectConfig.projectDir, mockPath) });
2399
+ mocks.set(source2, { fromPath: resolve5(projectConfig.projectDir, mockPath) });
2364
2400
  }
2365
2401
  }
2366
2402
  const mockModuleDirectives = fileDirectives.filter((d) => d.kind === "mock-module");
@@ -2838,8 +2874,47 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
2838
2874
  }
2839
2875
  return n;
2840
2876
  };
2841
- const currentModulePath = normalizeModulePath(resolve4(filePath));
2877
+ const currentModulePath = normalizeModulePath(resolve5(filePath));
2842
2878
  const singleExportFn = findSingleModuleExportsFunction(ast);
2879
+ const reportInjectedDomainExceeds = (name, node, fallbackLoc) => {
2880
+ if (!externalCallRecords || externalCallRecords.length === 0) return;
2881
+ const singleExportHit = singleExportFn !== null && node === singleExportFn;
2882
+ const nameRoutes = (r) => r.fnName === name || r.targetExport === name || (r.targetAliases?.includes(name) ?? false) || singleExportHit && (r.fnModule !== void 0 || r.targetModule !== void 0);
2883
+ const matchingExternal = (r) => {
2884
+ const attributed = r.fnModule !== void 0 && normalizeModulePath(r.fnModule) === currentModulePath || r.targetModule !== void 0 && normalizeModulePath(r.targetModule) === currentModulePath;
2885
+ if (!attributed) return false;
2886
+ return nameRoutes(r);
2887
+ };
2888
+ const injected = externalCallRecords.filter(matchingExternal);
2889
+ if (injected.length === 0) return;
2890
+ const nameLoc = fnNameLoc(node, fallbackLoc);
2891
+ const fnNode = resolveFunctionNode(node);
2892
+ const autoBind = interfaceConfig(findProjectConfig(dirname6(filePath))?.config).autoBind;
2893
+ const domainIssues = checkInjectedDomainEvidence(name, source, injected, {
2894
+ paramNames: extractParamNames(fnNode),
2895
+ loadModule: defaultLoadModule,
2896
+ fromFile: filePath,
2897
+ loc: { line: nameLoc.start.line, column: nameLoc.start.column },
2898
+ ...autoBind === false ? { autoBind: false } : {}
2899
+ });
2900
+ for (const issue of domainIssues) {
2901
+ const line = issue.line ?? nameLoc.start.line;
2902
+ const column = issue.column ?? nameLoc.start.column;
2903
+ diagnostics.push({
2904
+ range: { start: { line, column }, end: { line, column: column + name.length } },
2905
+ severity: issue.severity,
2906
+ message: issue.message,
2907
+ code: issue.code,
2908
+ suggestions: issue.suggestion ? [issue.suggestion] : void 0,
2909
+ data: { actual: issue.actual, expected: issue.expected }
2910
+ });
2911
+ }
2912
+ };
2913
+ for (const fn of functions) {
2914
+ if (fn.directives.some((d) => d.kind === "case")) {
2915
+ reportInjectedDomainExceeds(fn.name, fn.node, locFromNode(fn.node));
2916
+ }
2917
+ }
2843
2918
  for (const candidate of synthCandidates) {
2844
2919
  const singleExportHit = singleExportFn !== null && candidate.node === singleExportFn;
2845
2920
  const nameRoutes = (r) => r.fnName === candidate.name || r.targetExport === candidate.name || (r.targetAliases?.includes(candidate.name) ?? false) || singleExportHit && (r.fnModule !== void 0 || r.targetModule !== void 0);
@@ -2859,6 +2934,7 @@ function analyzeFileUncached(filePath, source, activeCases, externalCallRecords)
2859
2934
  ...(externalCallRecords ?? []).filter(matchingExternal)
2860
2935
  ].filter((r) => !(r.resultType.kind === "never" && r.throws.kind === "never"))
2861
2936
  );
2937
+ reportInjectedDomainExceeds(candidate.name, candidate.node, candidate.analysis.loc);
2862
2938
  if (records.length > 0) {
2863
2939
  const informativeness = (r) => {
2864
2940
  if (r.resultType.kind === "unknown") return 2;
@@ -3101,136 +3177,338 @@ function buildNodeTypeMap(ast, env, nodeTypeMap) {
3101
3177
  } catch {
3102
3178
  }
3103
3179
  }
3104
- function getCasesForFile(filePath, source) {
3105
- const ast = parse5(source);
3106
- const functions = extractDirectives(ast);
3107
- return functions.map((fn) => {
3108
- const cases = fn.directives.filter((d) => d.kind === "case").map((d, i) => ({ name: d.name, index: i }));
3109
- return { functionName: fn.name, cases, loc: locFromNode(fn.node) };
3110
- });
3111
- }
3112
- async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
3113
- const envNames = collectEnvNames(filePath, source, false);
3114
- if (envNames.length > 0) {
3115
- await preloadPathEnvs(envNames, dirname5(filePath));
3116
- }
3117
- return getTypeAtPosition(filePath, source, line, column, activeCases);
3118
- }
3119
- function positionInsideCaseFunction(source, ast, line) {
3120
- try {
3121
- const enclosing = findEnclosingFunction(extractDirectives(ast), line);
3122
- return !!enclosing && enclosing.directives.some((d) => d.kind === "case");
3123
- } catch {
3180
+ function absIsBetter(absTv, prev) {
3181
+ if (prev.kind === "unknown") return absTv.kind !== "unknown";
3182
+ if (absTv.kind === "unknown" || absTv.kind === "never") return false;
3183
+ 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") {
3124
3184
  return false;
3125
3185
  }
3186
+ if (absTv.kind === "refined" && prev.kind === "primitive") return true;
3187
+ return false;
3126
3188
  }
3127
- function getTypeAtPosition(filePath, source, line, column, activeCases) {
3128
- const ast = parse5(source);
3129
- const fileDirectives = extractFileDirectives(ast);
3130
- const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
3131
- if (isBPathCapable(source, envNames) && !positionInsideCaseFunction(source, ast, line)) {
3132
- try {
3133
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(ast));
3134
- const { modules } = evalAbsModuleGraph(source, filePath);
3135
- const absNodes = collectAbsNodeTypes(source, {
3136
- ...seeds,
3137
- modules,
3138
- file: ast
3139
- });
3140
- const absAt = findAbsAtPosition(absNodes, line, column);
3141
- if (absAt) return absToTypeValue2(absAt);
3142
- const ident = findIdentNameAtPosition(source, line, column, ast);
3143
- if (ident) {
3144
- const binds = collectAbsBindingsFromGraph(source, filePath, {
3145
- seedVars: seeds.seedVars,
3146
- seedFns: seeds.seedFns
3189
+ function isSelfContainedSource(source, envNames) {
3190
+ if (envNames.length > 0) return false;
3191
+ return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
3192
+ }
3193
+ function absModulesOk(source, envNames) {
3194
+ void source;
3195
+ void envNames;
3196
+ return true;
3197
+ }
3198
+ function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
3199
+ const absCalls = [];
3200
+ let modules = precomputedModules;
3201
+ let importLocals = /* @__PURE__ */ new Map();
3202
+ if (filePath && absModulesOk(source, envNames)) {
3203
+ if (!modules) {
3204
+ try {
3205
+ const graph = evalAbsModuleGraph(source, filePath, {
3206
+ seedVars: seeds?.seedVars,
3207
+ seedFns: seeds?.seedFns
3147
3208
  });
3148
- const bound = binds.get(ident);
3149
- if (bound) return absToTypeValue2(bound);
3209
+ modules = graph.modules;
3210
+ } catch {
3211
+ modules = void 0;
3150
3212
  }
3213
+ }
3214
+ if (envNames.length > 0) {
3215
+ modules = { ...collectEnvModules(envNames), ...modules ?? {} };
3216
+ }
3217
+ try {
3218
+ importLocals = buildAbsImportLocalMap(source, filePath);
3151
3219
  } catch {
3220
+ importLocals = /* @__PURE__ */ new Map();
3152
3221
  }
3153
3222
  }
3154
- resetMemo();
3155
- resetEnvModules();
3156
- setModuleResolver(resolveModule);
3157
- setCurrentFileDir(dirname5(filePath));
3158
- setCurrentSource(source);
3159
- const globalEnv = createEnvironment2();
3160
- {
3161
- const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
3162
- const auto = autoHarvestModules(source, dirname5(filePath));
3163
- const modules = { ...loaded.modules, ...auto };
3164
- if (Object.keys(modules).length > 0) setEnvModules(modules);
3223
+ setAbsCallCollector((r) => absCalls.push(r));
3224
+ try {
3225
+ evalProgramAbs2(source, { ...seeds, modules });
3226
+ } catch {
3227
+ } finally {
3228
+ setAbsCallCollector(null);
3165
3229
  }
3166
- evaluateProgram(ast, globalEnv);
3167
- const nodeTypeMap = /* @__PURE__ */ new Map();
3168
- buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
3169
- const functions = extractDirectives(ast);
3170
- const enclosingFn = findEnclosingFunction(functions, line);
3171
- if (enclosingFn) {
3172
- const caseDirectives = enclosingFn.directives.filter((d) => d.kind === "case");
3173
- if (caseDirectives.length > 0) {
3174
- const caseIndex = activeCases?.get(enclosingFn.name) ?? 0;
3175
- const directive = caseDirectives[Math.min(caseIndex, caseDirectives.length - 1)];
3176
- const fnNodeTypeMap = /* @__PURE__ */ new Map();
3177
- setNodeTypeCollector((node, tv) => fnNodeTypeMap.set(node, tv));
3178
- evaluateFunctionFull(enclosingFn.node, directive.args, globalEnv);
3179
- setNodeTypeCollector(null);
3180
- for (const [node, tv] of fnNodeTypeMap) {
3181
- nodeTypeMap.set(node, tv);
3182
- }
3230
+ return absCalls.map((r) => {
3231
+ const rec = {
3232
+ fnName: r.fnName,
3233
+ argTypes: r.args.map((a) => absToTypeValue2(a)),
3234
+ resultType: r.threw ? T2.never : absToTypeValue2(r.result),
3235
+ throws: r.threw ? absToTypeValue2(r.result) : T2.never,
3236
+ callLoc: r.callLoc
3237
+ };
3238
+ const imp = importLocals.get(r.fnName);
3239
+ if (imp) {
3240
+ rec.targetModule = imp.modulePath;
3241
+ rec.targetExport = imp.exportName;
3183
3242
  }
3184
- }
3185
- setModuleResolver(null);
3186
- resetEnvModules();
3187
- resetMockModules();
3188
- return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
3243
+ return rec;
3244
+ });
3189
3245
  }
3190
- function getHoverAtPosition(filePath, source, line, column, activeCases) {
3191
- let file;
3246
+ function collectAbsBindsAndNodes(source, seeds, modules) {
3247
+ const binds = /* @__PURE__ */ new Map();
3248
+ const nodes = /* @__PURE__ */ new Map();
3249
+ setAbsNodeCollector((node, value) => {
3250
+ nodes.set(node, value);
3251
+ });
3192
3252
  try {
3193
- file = parse5(source);
3194
- } catch {
3195
- file = void 0;
3196
- }
3197
- const envNames = collectEnvNames(filePath, source, false);
3198
- const fnName = findFunctionNameAtPosition(source, line, column, file);
3199
- let gDisplay;
3200
- let gAbs;
3201
- let gMulti;
3202
- if (fnName) {
3203
- try {
3204
- const g = generalizeFromAst2(fnName, source, file ? { file } : {});
3205
- if (g) {
3206
- gDisplay = g.display;
3207
- gAbs = formatAbs3(g.symbolic);
3208
- gMulti = formatAbsMultiline(g.symbolic, fnName);
3253
+ const { env } = evalProgramAbs2(source, {
3254
+ ...seeds,
3255
+ modules
3256
+ });
3257
+ for (const [k, v] of env.vars) binds.set(k, v);
3258
+ for (const [name, impl] of env.fns) {
3259
+ if (!binds.has(name)) {
3260
+ binds.set(
3261
+ name,
3262
+ absFunction5(impl.params, { body: impl.body, async: impl.async, env })
3263
+ );
3209
3264
  }
3210
- } catch {
3211
3265
  }
3266
+ } catch {
3267
+ } finally {
3268
+ setAbsNodeCollector(null);
3212
3269
  }
3213
- const attachIntension = (info2) => {
3214
- if (!gDisplay) return info2;
3215
- if (!info2) {
3216
- return { typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti };
3217
- }
3218
- return {
3219
- ...info2,
3220
- intension: gDisplay,
3221
- // 外延侧已有更准 Abs 时保留;否则用 symbolic 兜底
3222
- abs: info2.abs ?? gAbs,
3270
+ return { binds, nodes };
3271
+ }
3272
+ function buildAbsImportLocalMap(source, fromFile) {
3273
+ const out = /* @__PURE__ */ new Map();
3274
+ try {
3275
+ const file = parse5(source);
3276
+ for (const stmt of file.program.body) {
3277
+ if (stmt.type !== "ImportDeclaration") continue;
3278
+ const spec = stmt.source.value;
3279
+ let modulePath = null;
3280
+ if (spec.startsWith(".") || spec.startsWith("/")) {
3281
+ modulePath = resolveImportAbs(spec, fromFile);
3282
+ } else if (!spec.startsWith("node:")) {
3283
+ modulePath = spec;
3284
+ }
3285
+ if (!modulePath) continue;
3286
+ for (const s of stmt.specifiers) {
3287
+ if (s.type === "ImportSpecifier") {
3288
+ const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
3289
+ out.set(s.local.name, { modulePath, exportName: imported });
3290
+ } else if (s.type === "ImportDefaultSpecifier") {
3291
+ out.set(s.local.name, { modulePath, exportName: "default" });
3292
+ }
3293
+ }
3294
+ }
3295
+ } catch {
3296
+ }
3297
+ return out;
3298
+ }
3299
+ function resolveImportAbs(spec, fromFile) {
3300
+ const base = dirname6(resolve5(fromFile));
3301
+ const p = resolve5(base, spec);
3302
+ for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve5(p, "index.js")]) {
3303
+ if (existsSync4(cand) && !statSync4(cand).isDirectory()) return cand;
3304
+ }
3305
+ return null;
3306
+ }
3307
+ function tryEvalAbs(source, fnName, args, filePath, mocks) {
3308
+ const raw = tryEvalAbsRaw(source, fnName, args, filePath, mocks);
3309
+ return raw ? absToTypeValue2(raw) : void 0;
3310
+ }
3311
+ function tryEvalAbsRaw(source, fnName, args, filePath, mocks) {
3312
+ if (/\brequire\s*\(/.test(source)) return void 0;
3313
+ try {
3314
+ const absArgs = args.map((a) => typeValueToAbs5(a));
3315
+ if (filePath) {
3316
+ const viaB = tryBPathCall(source, filePath, fnName, absArgs, { mocks });
3317
+ if (viaB) return viaB;
3318
+ }
3319
+ let modules;
3320
+ if (filePath && /\bimport\s*[{'"*]/.test(source)) {
3321
+ modules = evalAbsModuleGraph(source, filePath).modules;
3322
+ }
3323
+ const result = analyzeFn(source, fnName, absArgs, void 0, void 0, void 0, modules);
3324
+ if (result.shape.k === "unknown" && !result.term) {
3325
+ return void 0;
3326
+ }
3327
+ return result;
3328
+ } catch {
3329
+ return void 0;
3330
+ }
3331
+ }
3332
+ function tryEvalEntryAbs(source, fnName, args, filePath, mocks) {
3333
+ return tryEvalAbs(source, fnName, args, filePath, mocks);
3334
+ }
3335
+ function tryAttachIntension(caseResult, source, fnName) {
3336
+ try {
3337
+ const g = generalizeFromAst2(fnName, source);
3338
+ if (!g) return;
3339
+ const sym = g.symbolic;
3340
+ const intension = {
3341
+ display: g.display,
3342
+ abs: formatAbs3(sym),
3343
+ absMultiline: formatAbsMultiline(sym, fnName)
3344
+ };
3345
+ if (sym.term && sym.term.op !== "lit") {
3346
+ intension.term = termToString(sym.term);
3347
+ }
3348
+ if (sym.pred && sym.pred.op !== "true") {
3349
+ intension.pred = predToString(sym.pred);
3350
+ }
3351
+ intension.conf = sym.conf;
3352
+ caseResult.intension = intension;
3353
+ } catch {
3354
+ }
3355
+ }
3356
+ function attachAbsToIntension(caseResult, absVal, label) {
3357
+ const prev = caseResult.intension ?? {};
3358
+ caseResult.abs = absVal;
3359
+ caseResult.intension = {
3360
+ ...prev,
3361
+ abs: formatAbs3(absVal),
3362
+ absMultiline: formatAbsMultiline(absVal, label ?? caseResult.name),
3363
+ conf: absVal.conf
3364
+ };
3365
+ }
3366
+
3367
+ // src/lsp-surface.ts
3368
+ import { dirname as dirname7 } from "path";
3369
+ import traverse2 from "@babel/traverse";
3370
+ import {
3371
+ typeValueToString as typeValueToString4,
3372
+ createEnvironment as createEnvironment3,
3373
+ generalizeFromAst as generalizeFromAst3,
3374
+ formatAbs as formatAbs4,
3375
+ formatAbsMultiline as formatAbsMultiline2,
3376
+ getFnSig as getFnSig3,
3377
+ collectAbsNodeTypes,
3378
+ findAbsAtPosition,
3379
+ absToTypeValue as absToTypeValue3
3380
+ } from "@nudojs/core";
3381
+ import { parse as parse6, extractDirectives as extractDirectives2, extractFileDirectives as extractFileDirectives2 } from "@nudojs/parser";
3382
+ function getCasesForFile(filePath, source) {
3383
+ const ast = parse6(source);
3384
+ const functions = extractDirectives2(ast);
3385
+ return functions.map((fn) => {
3386
+ const cases = fn.directives.filter((d) => d.kind === "case").map((d, i) => ({ name: d.name, index: i }));
3387
+ return { functionName: fn.name, cases, loc: locFromNode(fn.node) };
3388
+ });
3389
+ }
3390
+ async function getTypeAtPositionAsync(filePath, source, line, column, activeCases) {
3391
+ const envNames = collectEnvNames(filePath, source, false);
3392
+ if (envNames.length > 0) {
3393
+ await preloadPathEnvs(envNames, dirname7(filePath));
3394
+ }
3395
+ return getTypeAtPosition(filePath, source, line, column, activeCases);
3396
+ }
3397
+ function positionInsideCaseFunction(source, ast, line) {
3398
+ try {
3399
+ const enclosing = findEnclosingFunction(extractDirectives2(ast), line);
3400
+ return !!enclosing && enclosing.directives.some((d) => d.kind === "case");
3401
+ } catch {
3402
+ return false;
3403
+ }
3404
+ }
3405
+ function getTypeAtPosition(filePath, source, line, column, activeCases) {
3406
+ const ast = parse6(source);
3407
+ const fileDirectives = extractFileDirectives2(ast);
3408
+ const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
3409
+ if (isBPathCapable(source, envNames) && !positionInsideCaseFunction(source, ast, line)) {
3410
+ try {
3411
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives2(ast));
3412
+ const { modules } = evalAbsModuleGraph(source, filePath);
3413
+ const absNodes = collectAbsNodeTypes(source, {
3414
+ ...seeds,
3415
+ modules,
3416
+ file: ast
3417
+ });
3418
+ const absAt = findAbsAtPosition(absNodes, line, column);
3419
+ if (absAt) return absToTypeValue3(absAt);
3420
+ const ident = findIdentNameAtPosition(source, line, column, ast);
3421
+ if (ident) {
3422
+ const binds = collectAbsBindingsFromGraph(source, filePath, {
3423
+ seedVars: seeds.seedVars,
3424
+ seedFns: seeds.seedFns
3425
+ });
3426
+ const bound = binds.get(ident);
3427
+ if (bound) return absToTypeValue3(bound);
3428
+ }
3429
+ } catch {
3430
+ }
3431
+ }
3432
+ resetMemo();
3433
+ resetEnvModules();
3434
+ setModuleResolver(resolveModule);
3435
+ setCurrentFileDir(dirname7(filePath));
3436
+ setCurrentSource(source);
3437
+ const globalEnv = createEnvironment3();
3438
+ {
3439
+ const loaded = envNames.length > 0 ? loadEnvs(envNames, globalEnv) : { modules: {} };
3440
+ const auto = autoHarvestModules(source, dirname7(filePath));
3441
+ const modules = { ...loaded.modules, ...auto };
3442
+ if (Object.keys(modules).length > 0) setEnvModules(modules);
3443
+ }
3444
+ evaluateProgram(ast, globalEnv);
3445
+ const nodeTypeMap = /* @__PURE__ */ new Map();
3446
+ buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
3447
+ const functions = extractDirectives2(ast);
3448
+ const enclosingFn = findEnclosingFunction(functions, line);
3449
+ if (enclosingFn) {
3450
+ const caseDirectives = enclosingFn.directives.filter((d) => d.kind === "case");
3451
+ if (caseDirectives.length > 0) {
3452
+ const caseIndex = activeCases?.get(enclosingFn.name) ?? 0;
3453
+ const directive = caseDirectives[Math.min(caseIndex, caseDirectives.length - 1)];
3454
+ const fnNodeTypeMap = /* @__PURE__ */ new Map();
3455
+ setNodeTypeCollector((node, tv) => fnNodeTypeMap.set(node, tv));
3456
+ evaluateFunctionFull(enclosingFn.node, directive.args, globalEnv);
3457
+ setNodeTypeCollector(null);
3458
+ for (const [node, tv] of fnNodeTypeMap) {
3459
+ nodeTypeMap.set(node, tv);
3460
+ }
3461
+ }
3462
+ }
3463
+ setModuleResolver(null);
3464
+ resetEnvModules();
3465
+ resetMockModules();
3466
+ return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
3467
+ }
3468
+ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3469
+ let file;
3470
+ try {
3471
+ file = parse6(source);
3472
+ } catch {
3473
+ file = void 0;
3474
+ }
3475
+ const envNames = collectEnvNames(filePath, source, false);
3476
+ const fnName = findFunctionNameAtPosition(source, line, column, file);
3477
+ let gDisplay;
3478
+ let gAbs;
3479
+ let gMulti;
3480
+ if (fnName) {
3481
+ try {
3482
+ const g = generalizeFromAst3(fnName, source, file ? { file } : {});
3483
+ if (g) {
3484
+ gDisplay = g.display;
3485
+ gAbs = formatAbs4(g.symbolic);
3486
+ gMulti = formatAbsMultiline2(g.symbolic, fnName);
3487
+ }
3488
+ } catch {
3489
+ }
3490
+ }
3491
+ const attachIntension = (info2) => {
3492
+ if (!gDisplay) return info2;
3493
+ if (!info2) {
3494
+ return { typeText: gDisplay, intension: gDisplay, abs: gAbs, absMultiline: gMulti };
3495
+ }
3496
+ return {
3497
+ ...info2,
3498
+ intension: gDisplay,
3499
+ // 外延侧已有更准 Abs 时保留;否则用 symbolic 兜底
3500
+ abs: info2.abs ?? gAbs,
3223
3501
  absMultiline: info2.absMultiline ?? gMulti
3224
3502
  };
3225
3503
  };
3226
3504
  const insideCaseFn = positionInsideCaseFunction(
3227
3505
  source,
3228
- file ?? parse5(source),
3506
+ file ?? parse6(source),
3229
3507
  line
3230
3508
  );
3231
3509
  if (isBPathCapable(source, envNames) && !insideCaseFn) {
3232
3510
  try {
3233
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3511
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
3234
3512
  const { modules } = evalAbsModuleGraph(source, filePath);
3235
3513
  const absNodes = collectAbsNodeTypes(source, {
3236
3514
  ...seeds,
@@ -3246,34 +3524,34 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3246
3524
  });
3247
3525
  const bound = binds.get(ident2);
3248
3526
  if (bound) {
3249
- const absLine = formatAbs3(bound);
3250
- const absMulti = formatAbsMultiline(bound, ident2);
3527
+ const absLine = formatAbs4(bound);
3528
+ const absMulti = formatAbsMultiline2(bound, ident2);
3251
3529
  return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
3252
3530
  }
3253
3531
  }
3254
3532
  if (absAt) {
3255
- const absLine = formatAbs3(absAt);
3256
- const absMulti = formatAbsMultiline(absAt, void 0);
3533
+ const absLine = formatAbs4(absAt);
3534
+ const absMulti = formatAbsMultiline2(absAt, void 0);
3257
3535
  return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
3258
3536
  }
3259
3537
  } catch {
3260
3538
  }
3261
3539
  }
3262
3540
  const tv = getTypeAtPosition(filePath, source, line, column, activeCases);
3263
- const info = tv ? { typeText: typeValueToString3(tv) } : null;
3541
+ const info = tv ? { typeText: typeValueToString4(tv) } : null;
3264
3542
  const ident = findIdentNameAtPosition(source, line, column, file);
3265
3543
  if (ident && !fnName && !insideCaseFn) {
3266
3544
  try {
3267
3545
  if (isBPathCapable(source, []) || !/\brequire\s*\(/.test(source)) {
3268
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3546
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
3269
3547
  const absBinds = collectAbsBindingsFromGraph(source, filePath, {
3270
3548
  seedVars: seeds.seedVars,
3271
3549
  seedFns: seeds.seedFns
3272
3550
  });
3273
3551
  const absBound = absBinds.get(ident);
3274
3552
  if (absBound) {
3275
- const absLine = formatAbs3(absBound);
3276
- const absMulti = formatAbsMultiline(absBound, ident);
3553
+ const absLine = formatAbs4(absBound);
3554
+ const absMulti = formatAbsMultiline2(absBound, ident);
3277
3555
  if (info) {
3278
3556
  info.abs = absLine;
3279
3557
  info.absMultiline = absMulti;
@@ -3288,7 +3566,7 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3288
3566
  try {
3289
3567
  if (!insideCaseFn && (isBPathCapable(source, []) || !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source))) {
3290
3568
  const seeds = mockDirectivesToAbsSeeds(
3291
- extractDirectives(file ?? parse5(source))
3569
+ extractDirectives2(file ?? parse6(source))
3292
3570
  );
3293
3571
  const { modules } = evalAbsModuleGraph(source, filePath);
3294
3572
  const nodeTypes = collectAbsNodeTypes(source, {
@@ -3298,8 +3576,8 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3298
3576
  });
3299
3577
  const absAt = findAbsAtPosition(nodeTypes, line, column);
3300
3578
  if (absAt) {
3301
- const absLine = formatAbs3(absAt);
3302
- const absMulti = formatAbsMultiline(absAt, void 0);
3579
+ const absLine = formatAbs4(absAt);
3580
+ const absMulti = formatAbsMultiline2(absAt, void 0);
3303
3581
  if (info) {
3304
3582
  info.abs = absLine;
3305
3583
  info.absMultiline = absMulti;
@@ -3314,9 +3592,9 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3314
3592
  }
3315
3593
  function findIdentNameAtPosition(source, line, column, fileAst) {
3316
3594
  try {
3317
- const ast = fileAst ?? parse5(source);
3595
+ const ast = fileAst ?? parse6(source);
3318
3596
  let found;
3319
- traverse(ast, {
3597
+ traverse2(ast, {
3320
3598
  Identifier(path) {
3321
3599
  const loc = path.node.loc;
3322
3600
  if (!loc) return;
@@ -3332,9 +3610,9 @@ function findIdentNameAtPosition(source, line, column, fileAst) {
3332
3610
  }
3333
3611
  function findFunctionNameAtPosition(source, line, column, fileAst) {
3334
3612
  try {
3335
- const ast = fileAst ?? parse5(source);
3613
+ const ast = fileAst ?? parse6(source);
3336
3614
  let found;
3337
- traverse(ast, {
3615
+ traverse2(ast, {
3338
3616
  Identifier(path) {
3339
3617
  const loc = path.node.loc;
3340
3618
  if (!loc) return;
@@ -3387,7 +3665,7 @@ function findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column) {
3387
3665
  }
3388
3666
  function findIdentifierAtPosition(ast, line, column) {
3389
3667
  let found = null;
3390
- const traverseFn = typeof traverse === "function" ? traverse : traverse.default;
3668
+ const traverseFn = typeof traverse2 === "function" ? traverse2 : traverse2.default;
3391
3669
  try {
3392
3670
  traverseFn(ast, {
3393
3671
  Identifier(path) {
@@ -3411,18 +3689,18 @@ function getCompletionsAtPosition(filePath, source, line, column) {
3411
3689
  const safeSource = sanitizeSourceForParsing(source);
3412
3690
  let ast;
3413
3691
  try {
3414
- ast = parse5(safeSource);
3692
+ ast = parse6(safeSource);
3415
3693
  } catch {
3416
3694
  try {
3417
- ast = parse5(source);
3695
+ ast = parse6(source);
3418
3696
  } catch {
3419
3697
  return [];
3420
3698
  }
3421
3699
  }
3422
3700
  resetMemo();
3423
3701
  setModuleResolver(resolveModule);
3424
- setCurrentFileDir(dirname5(filePath));
3425
- const globalEnv = createEnvironment2();
3702
+ setCurrentFileDir(dirname7(filePath));
3703
+ const globalEnv = createEnvironment3();
3426
3704
  evaluateProgram(ast, globalEnv);
3427
3705
  if (!globalEnv.has(objName)) {
3428
3706
  setModuleResolver(null);
@@ -3442,11 +3720,11 @@ function getTextBeforePosition(source, line, column) {
3442
3720
  return lines[line - 1].slice(0, column);
3443
3721
  }
3444
3722
  function getVariableCompletions(filePath, source) {
3445
- const ast = parse5(source);
3723
+ const ast = parse6(source);
3446
3724
  resetMemo();
3447
3725
  setModuleResolver(resolveModule);
3448
- setCurrentFileDir(dirname5(filePath));
3449
- const globalEnv = createEnvironment2();
3726
+ setCurrentFileDir(dirname7(filePath));
3727
+ const globalEnv = createEnvironment3();
3450
3728
  evaluateProgram(ast, globalEnv);
3451
3729
  const ownBindings = globalEnv.getOwnBindings();
3452
3730
  const completions = [];
@@ -3455,7 +3733,7 @@ function getVariableCompletions(filePath, source) {
3455
3733
  completions.push({
3456
3734
  label: name,
3457
3735
  kind: tv.kind === "function" ? "method" : "variable",
3458
- detail: typeValueToString3(tv)
3736
+ detail: typeValueToString4(tv)
3459
3737
  });
3460
3738
  }
3461
3739
  setModuleResolver(null);
@@ -3463,7 +3741,7 @@ function getVariableCompletions(filePath, source) {
3463
3741
  }
3464
3742
  function builtinMemberType(memberExpr) {
3465
3743
  try {
3466
- const result = evaluate(parse5(`${memberExpr};`), createEnvironment2());
3744
+ const result = evaluate(parse6(`${memberExpr};`), createEnvironment3());
3467
3745
  if (!result || typeof result !== "object" || !("kind" in result)) return null;
3468
3746
  return result;
3469
3747
  } catch {
@@ -3479,10 +3757,10 @@ function describeMember(label, tv, fallbackClass) {
3479
3757
  const sig = getFnSig3(tv);
3480
3758
  if (sig) {
3481
3759
  const paramNames = tv.kind === "function" ? tv.params : [];
3482
- const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${typeValueToString3(p)}`).join(", ");
3483
- return `(${params}) => ${typeValueToString3(sig.returnType)}`;
3760
+ const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${typeValueToString4(p)}`).join(", ");
3761
+ return `(${params}) => ${typeValueToString4(sig.returnType)}`;
3484
3762
  }
3485
- if (tv.kind !== "function") return typeValueToString3(tv);
3763
+ if (tv.kind !== "function") return typeValueToString4(tv);
3486
3764
  }
3487
3765
  return `${label}(\u2026)@${fallbackClass}`;
3488
3766
  }
@@ -3505,281 +3783,95 @@ function getPromiseCompletions() {
3505
3783
  label: m,
3506
3784
  kind: "method",
3507
3785
  detail: describeMember(m, builtinMemberType(`Promise.prototype.${m}`), "Promise")
3508
- }));
3509
- }
3510
- function getStringCompletions() {
3511
- const completions = [];
3512
- for (const m of builtinProtoMembers("String")) {
3513
- completions.push({
3514
- label: m,
3515
- kind: "method",
3516
- detail: describeMember(m, builtinMemberType(`"s".${m}`), "String")
3517
- });
3518
- }
3519
- completions.push({ label: "length", kind: "property", detail: "number" });
3520
- return completions;
3521
- }
3522
- function getUnionCompletions(tv) {
3523
- const members = tv.members;
3524
- if (members.length === 0) return [];
3525
- const labelsByMember = members.map((m) => getCompletionsForType(m));
3526
- const baseIdx = labelsByMember.findIndex((labels) => labels.length > 0);
3527
- if (baseIdx === -1) return [];
3528
- const common = [];
3529
- for (const base of labelsByMember[baseIdx]) {
3530
- let allPresent = true;
3531
- const memberTypes = [base.detail ?? base.label];
3532
- for (let i = 0; i < members.length; i++) {
3533
- if (i === baseIdx) continue;
3534
- const labels = labelsByMember[i];
3535
- if (labels.length === 0) continue;
3536
- const hit = labels.find((l) => l.label === base.label);
3537
- if (!hit) {
3538
- allPresent = false;
3539
- break;
3540
- }
3541
- memberTypes.push(hit.detail ?? hit.label);
3542
- }
3543
- if (allPresent) {
3544
- common.push({ ...base, detail: memberTypes.join(" | ") });
3545
- }
3546
- }
3547
- return common;
3548
- }
3549
- function getCompletionsForType(tv) {
3550
- const completions = [];
3551
- if (tv.kind === "object") {
3552
- for (const [key, val] of Object.entries(tv.properties)) {
3553
- completions.push({
3554
- label: key,
3555
- kind: val.kind === "function" ? "method" : "property",
3556
- detail: typeValueToString3(val)
3557
- });
3558
- }
3559
- return completions;
3560
- }
3561
- if (tv.kind === "instance") {
3562
- for (const [key, val] of Object.entries(tv.properties)) {
3563
- completions.push({
3564
- label: key,
3565
- kind: val.kind === "function" ? "method" : "property",
3566
- detail: typeValueToString3(val)
3567
- });
3568
- }
3569
- return completions;
3570
- }
3571
- if (tv.kind === "union") {
3572
- return getUnionCompletions(tv);
3573
- }
3574
- if (tv.kind === "array" || tv.kind === "tuple") {
3575
- return getArrayCompletions(tv);
3576
- }
3577
- if (tv.kind === "promise") {
3578
- return getPromiseCompletions();
3579
- }
3580
- if (tv.kind === "primitive" && tv.type === "string") {
3581
- return getStringCompletions();
3582
- }
3583
- return completions;
3584
- }
3585
- function absIsBetter(absTv, prev) {
3586
- if (prev.kind === "unknown") return absTv.kind !== "unknown";
3587
- if (absTv.kind === "unknown" || absTv.kind === "never") return false;
3588
- 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") {
3589
- return false;
3590
- }
3591
- if (absTv.kind === "refined" && prev.kind === "primitive") return true;
3592
- return false;
3593
- }
3594
- function isSelfContainedSource(source, envNames) {
3595
- if (envNames.length > 0) return false;
3596
- return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
3597
- }
3598
- function absModulesOk(source, envNames) {
3599
- void source;
3600
- void envNames;
3601
- return true;
3602
- }
3603
- function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
3604
- const absCalls = [];
3605
- let modules = precomputedModules;
3606
- let importLocals = /* @__PURE__ */ new Map();
3607
- if (filePath && absModulesOk(source, envNames)) {
3608
- if (!modules) {
3609
- try {
3610
- const graph = evalAbsModuleGraph(source, filePath, {
3611
- seedVars: seeds?.seedVars,
3612
- seedFns: seeds?.seedFns
3613
- });
3614
- modules = graph.modules;
3615
- } catch {
3616
- modules = void 0;
3617
- }
3618
- }
3619
- if (envNames.length > 0) {
3620
- modules = { ...collectEnvModules(envNames), ...modules ?? {} };
3621
- }
3622
- try {
3623
- importLocals = buildAbsImportLocalMap(source, filePath);
3624
- } catch {
3625
- importLocals = /* @__PURE__ */ new Map();
3626
- }
3627
- }
3628
- setAbsCallCollector((r) => absCalls.push(r));
3629
- try {
3630
- evalProgramAbs2(source, { ...seeds, modules });
3631
- } catch {
3632
- } finally {
3633
- setAbsCallCollector(null);
3634
- }
3635
- return absCalls.map((r) => {
3636
- const rec = {
3637
- fnName: r.fnName,
3638
- argTypes: r.args.map((a) => absToTypeValue2(a)),
3639
- resultType: r.threw ? T2.never : absToTypeValue2(r.result),
3640
- throws: r.threw ? absToTypeValue2(r.result) : T2.never,
3641
- callLoc: r.callLoc
3642
- };
3643
- const imp = importLocals.get(r.fnName);
3644
- if (imp) {
3645
- rec.targetModule = imp.modulePath;
3646
- rec.targetExport = imp.exportName;
3647
- }
3648
- return rec;
3649
- });
3650
- }
3651
- function collectAbsBindsAndNodes(source, seeds, modules) {
3652
- const binds = /* @__PURE__ */ new Map();
3653
- const nodes = /* @__PURE__ */ new Map();
3654
- setAbsNodeCollector((node, value) => {
3655
- nodes.set(node, value);
3656
- });
3657
- try {
3658
- const { env } = evalProgramAbs2(source, {
3659
- ...seeds,
3660
- modules
3661
- });
3662
- for (const [k, v] of env.vars) binds.set(k, v);
3663
- for (const [name, impl] of env.fns) {
3664
- if (!binds.has(name)) {
3665
- binds.set(
3666
- name,
3667
- absFunction5(impl.params, { body: impl.body, async: impl.async, env })
3668
- );
3669
- }
3670
- }
3671
- } catch {
3672
- } finally {
3673
- setAbsNodeCollector(null);
3674
- }
3675
- return { binds, nodes };
3676
- }
3677
- function buildAbsImportLocalMap(source, fromFile) {
3678
- const out = /* @__PURE__ */ new Map();
3679
- try {
3680
- const file = parse5(source);
3681
- for (const stmt of file.program.body) {
3682
- if (stmt.type !== "ImportDeclaration") continue;
3683
- const spec = stmt.source.value;
3684
- let modulePath = null;
3685
- if (spec.startsWith(".") || spec.startsWith("/")) {
3686
- modulePath = resolveImportAbs(spec, fromFile);
3687
- } else if (!spec.startsWith("node:")) {
3688
- modulePath = spec;
3689
- }
3690
- if (!modulePath) continue;
3691
- for (const s of stmt.specifiers) {
3692
- if (s.type === "ImportSpecifier") {
3693
- const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
3694
- out.set(s.local.name, { modulePath, exportName: imported });
3695
- } else if (s.type === "ImportDefaultSpecifier") {
3696
- out.set(s.local.name, { modulePath, exportName: "default" });
3697
- }
3698
- }
3699
- }
3700
- } catch {
3701
- }
3702
- return out;
3703
- }
3704
- function resolveImportAbs(spec, fromFile) {
3705
- const base = dirname5(resolve4(fromFile));
3706
- const p = resolve4(base, spec);
3707
- for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
3708
- if (existsSync3(cand) && !statSync3(cand).isDirectory()) return cand;
3709
- }
3710
- return null;
3786
+ }));
3711
3787
  }
3712
- function tryEvalAbs(source, fnName, args, filePath, mocks) {
3713
- const raw = tryEvalAbsRaw(source, fnName, args, filePath, mocks);
3714
- return raw ? absToTypeValue2(raw) : void 0;
3788
+ function getStringCompletions() {
3789
+ const completions = [];
3790
+ for (const m of builtinProtoMembers("String")) {
3791
+ completions.push({
3792
+ label: m,
3793
+ kind: "method",
3794
+ detail: describeMember(m, builtinMemberType(`"s".${m}`), "String")
3795
+ });
3796
+ }
3797
+ completions.push({ label: "length", kind: "property", detail: "number" });
3798
+ return completions;
3715
3799
  }
3716
- function tryEvalAbsRaw(source, fnName, args, filePath, mocks) {
3717
- if (/\brequire\s*\(/.test(source)) return void 0;
3718
- try {
3719
- const absArgs = args.map((a) => typeValueToAbs5(a));
3720
- if (filePath) {
3721
- const viaB = tryBPathCall(source, filePath, fnName, absArgs, { mocks });
3722
- if (viaB) return viaB;
3723
- }
3724
- let modules;
3725
- if (filePath && /\bimport\s*[{'"*]/.test(source)) {
3726
- modules = evalAbsModuleGraph(source, filePath).modules;
3800
+ function getUnionCompletions(tv) {
3801
+ const members = tv.members;
3802
+ if (members.length === 0) return [];
3803
+ const labelsByMember = members.map((m) => getCompletionsForType(m));
3804
+ const baseIdx = labelsByMember.findIndex((labels) => labels.length > 0);
3805
+ if (baseIdx === -1) return [];
3806
+ const common = [];
3807
+ for (const base of labelsByMember[baseIdx]) {
3808
+ let allPresent = true;
3809
+ const memberTypes = [base.detail ?? base.label];
3810
+ for (let i = 0; i < members.length; i++) {
3811
+ if (i === baseIdx) continue;
3812
+ const labels = labelsByMember[i];
3813
+ if (labels.length === 0) continue;
3814
+ const hit = labels.find((l) => l.label === base.label);
3815
+ if (!hit) {
3816
+ allPresent = false;
3817
+ break;
3818
+ }
3819
+ memberTypes.push(hit.detail ?? hit.label);
3727
3820
  }
3728
- const result = analyzeFn(source, fnName, absArgs, void 0, void 0, void 0, modules);
3729
- if (result.shape.k === "unknown" && !result.term) {
3730
- return void 0;
3821
+ if (allPresent) {
3822
+ common.push({ ...base, detail: memberTypes.join(" | ") });
3731
3823
  }
3732
- return result;
3733
- } catch {
3734
- return void 0;
3735
3824
  }
3825
+ return common;
3736
3826
  }
3737
- function tryEvalEntryAbs(source, fnName, args, filePath, mocks) {
3738
- return tryEvalAbs(source, fnName, args, filePath, mocks);
3739
- }
3740
- function tryAttachIntension(caseResult, source, fnName) {
3741
- try {
3742
- const g = generalizeFromAst2(fnName, source);
3743
- if (!g) return;
3744
- const sym = g.symbolic;
3745
- const intension = {
3746
- display: g.display,
3747
- abs: formatAbs3(sym),
3748
- absMultiline: formatAbsMultiline(sym, fnName)
3749
- };
3750
- if (sym.term && sym.term.op !== "lit") {
3751
- intension.term = termToString(sym.term);
3827
+ function getCompletionsForType(tv) {
3828
+ const completions = [];
3829
+ if (tv.kind === "object") {
3830
+ for (const [key, val] of Object.entries(tv.properties)) {
3831
+ completions.push({
3832
+ label: key,
3833
+ kind: val.kind === "function" ? "method" : "property",
3834
+ detail: typeValueToString4(val)
3835
+ });
3752
3836
  }
3753
- if (sym.pred && sym.pred.op !== "true") {
3754
- intension.pred = predToString(sym.pred);
3837
+ return completions;
3838
+ }
3839
+ if (tv.kind === "instance") {
3840
+ for (const [key, val] of Object.entries(tv.properties)) {
3841
+ completions.push({
3842
+ label: key,
3843
+ kind: val.kind === "function" ? "method" : "property",
3844
+ detail: typeValueToString4(val)
3845
+ });
3755
3846
  }
3756
- intension.conf = sym.conf;
3757
- caseResult.intension = intension;
3758
- } catch {
3847
+ return completions;
3759
3848
  }
3760
- }
3761
- function attachAbsToIntension(caseResult, absVal, label) {
3762
- const prev = caseResult.intension ?? {};
3763
- caseResult.abs = absVal;
3764
- caseResult.intension = {
3765
- ...prev,
3766
- abs: formatAbs3(absVal),
3767
- absMultiline: formatAbsMultiline(absVal, label ?? caseResult.name),
3768
- conf: absVal.conf
3769
- };
3849
+ if (tv.kind === "union") {
3850
+ return getUnionCompletions(tv);
3851
+ }
3852
+ if (tv.kind === "array" || tv.kind === "tuple") {
3853
+ return getArrayCompletions(tv);
3854
+ }
3855
+ if (tv.kind === "promise") {
3856
+ return getPromiseCompletions();
3857
+ }
3858
+ if (tv.kind === "primitive" && tv.type === "string") {
3859
+ return getStringCompletions();
3860
+ }
3861
+ return completions;
3770
3862
  }
3771
3863
 
3772
3864
  // src/index.ts
3773
3865
  import { collectAbsInlays } from "@nudojs/core";
3774
3866
 
3775
3867
  // src/infer-json.ts
3776
- import { typeValueToString as typeValueToString4 } from "@nudojs/core";
3868
+ import { typeValueToString as typeValueToString5 } from "@nudojs/core";
3777
3869
  function mapCase(c) {
3778
3870
  const out = {
3779
3871
  name: c.name,
3780
- args: c.args.map((a) => typeValueToString4(a)),
3781
- result: typeValueToString4(c.result),
3782
- throws: c.throws.kind !== "never" ? typeValueToString4(c.throws) : null,
3872
+ args: c.args.map((a) => typeValueToString5(a)),
3873
+ result: typeValueToString5(c.result),
3874
+ throws: c.throws.kind !== "never" ? typeValueToString5(c.throws) : null,
3783
3875
  source: c.source ?? null
3784
3876
  };
3785
3877
  if (c.aggregatedFrom !== void 0) out.aggregatedFrom = c.aggregatedFrom;
@@ -3804,7 +3896,7 @@ function mapFunction(f) {
3804
3896
  cases: f.cases.map(mapCase)
3805
3897
  };
3806
3898
  if (f.noDeclaration) out.noDeclaration = true;
3807
- if (f.combined) out.combined = typeValueToString4(f.combined);
3899
+ if (f.combined) out.combined = typeValueToString5(f.combined);
3808
3900
  return out;
3809
3901
  }
3810
3902
  function serializeInferJson(result, file) {
@@ -3843,36 +3935,10 @@ function serializeInferJson(result, file) {
3843
3935
  function isNudoTargetPath(path) {
3844
3936
  const lower = path.toLowerCase();
3845
3937
  if (lower.endsWith(".d.ts")) return false;
3938
+ if (lower.endsWith(".nudo.js") || lower.endsWith(".nudo.ts")) return false;
3846
3939
  return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".ts");
3847
3940
  }
3848
3941
 
3849
- // src/load-module.ts
3850
- import { existsSync as existsSync4, statSync as statSync4, readFileSync as readFileSync5 } from "fs";
3851
- import { dirname as dirname6, resolve as resolve5, join as join3 } from "path";
3852
- function defaultLoadModule(spec, fromFile) {
3853
- if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
3854
- try {
3855
- const base = dirname6(resolve5(fromFile));
3856
- const p = resolve5(base, spec);
3857
- for (const cand of [
3858
- p,
3859
- `${p}.js`,
3860
- `${p}.mjs`,
3861
- `${p}.ts`,
3862
- join3(p, "index.js"),
3863
- join3(p, "index.mjs"),
3864
- join3(p, "index.ts")
3865
- ]) {
3866
- if (existsSync4(cand) && !statSync4(cand).isDirectory()) {
3867
- return readFileSync5(cand, "utf-8");
3868
- }
3869
- }
3870
- return void 0;
3871
- } catch {
3872
- return void 0;
3873
- }
3874
- }
3875
-
3876
3942
  // src/session-cache.ts
3877
3943
  import {
3878
3944
  resetGeneralizeMemo,
@@ -3923,15 +3989,9 @@ function summarizeNodeEnv(env) {
3923
3989
  }
3924
3990
 
3925
3991
  // src/semantic-tokens.ts
3926
- import { dirname as dirname7 } from "path";
3927
- import { createEnvironment as createEnvironment3 } from "@nudojs/core";
3928
- import { parse as parse6 } from "@nudojs/parser";
3929
- import {
3930
- evaluateProgram as evaluateProgram2,
3931
- setModuleResolver as setModuleResolver2,
3932
- setCurrentFileDir as setCurrentFileDir2,
3933
- resetMemo as resetMemo2
3934
- } from "@nudojs/cli/evaluator";
3992
+ import { dirname as dirname8 } from "path";
3993
+ import { createEnvironment as createEnvironment4 } from "@nudojs/core";
3994
+ import { parse as parse7 } from "@nudojs/parser";
3935
3995
  var SEMANTIC_TOKEN_TYPES = [
3936
3996
  "function",
3937
3997
  "variable",
@@ -3973,19 +4033,19 @@ var MOD_DECLARATION = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration");
3973
4033
  function buildSemanticTokens(filePath, source) {
3974
4034
  let ast;
3975
4035
  try {
3976
- ast = parse6(source);
4036
+ ast = parse7(source);
3977
4037
  } catch {
3978
4038
  return [];
3979
4039
  }
3980
- const env = createEnvironment3();
3981
- resetMemo2();
3982
- setModuleResolver2(resolveModule);
3983
- setCurrentFileDir2(dirname7(filePath));
4040
+ const env = createEnvironment4();
4041
+ resetMemo();
4042
+ setModuleResolver(resolveModule);
4043
+ setCurrentFileDir(dirname8(filePath));
3984
4044
  try {
3985
- evaluateProgram2(ast, env);
4045
+ evaluateProgram(ast, env);
3986
4046
  } catch {
3987
4047
  } finally {
3988
- setModuleResolver2(null);
4048
+ setModuleResolver(null);
3989
4049
  }
3990
4050
  const program = ast.program ?? ast;
3991
4051
  const ownBindings = env.getOwnBindings();
@@ -4377,7 +4437,7 @@ function generateGuardBody(tv, varName) {
4377
4437
  }
4378
4438
 
4379
4439
  // src/case-emitter.ts
4380
- import { parse as parse7, extractDirectives as extractDirectives2 } from "@nudojs/parser";
4440
+ import { parse as parse8, extractDirectives as extractDirectives3 } from "@nudojs/parser";
4381
4441
  var GENERATED_PREFIX = "call@";
4382
4442
  var GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
4383
4443
  var IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
@@ -4520,11 +4580,11 @@ function collectExistingCases(source) {
4520
4580
  const byName = /* @__PURE__ */ new Map();
4521
4581
  let ast;
4522
4582
  try {
4523
- ast = parse7(source, { errorRecovery: true });
4583
+ ast = parse8(source, { errorRecovery: true });
4524
4584
  } catch {
4525
4585
  return byName;
4526
4586
  }
4527
- for (const { name, directives } of extractDirectives2(ast)) {
4587
+ for (const { name, directives } of extractDirectives3(ast)) {
4528
4588
  for (const d of directives) {
4529
4589
  if (d.kind !== "case") continue;
4530
4590
  const list = byName.get(name);
@@ -4729,6 +4789,418 @@ function unifiedDiff(a, b, path) {
4729
4789
  }
4730
4790
  return out.join("\n") + "\n";
4731
4791
  }
4792
+
4793
+ // src/interface-surface.ts
4794
+ import { readFileSync as readFileSync6 } from "fs";
4795
+ import { dirname as dirname9, resolve as resolve6 } from "path";
4796
+ import {
4797
+ effectiveInterface,
4798
+ formatConstraint,
4799
+ interfaceDiagCount,
4800
+ localNamedExports,
4801
+ refineDiagCount,
4802
+ takeInterfaceDiagsSince,
4803
+ takeRefineDiagsSince,
4804
+ typeValueToString as typeValueToString6
4805
+ } from "@nudojs/core";
4806
+ function formatInterfaceSurfaceLine(e) {
4807
+ const params = `(${e.params.map((p) => `${p.name}: ${p.display}`).join(", ")})`;
4808
+ let line = ` ${e.fn} [${e.source}] ${params}`;
4809
+ if (e.returns !== void 0) line += ` \u2192 ${e.returns}`;
4810
+ if (e.kind === "local") line += " (local)";
4811
+ return line;
4812
+ }
4813
+ async function interfaceSurface(filePath, opts = {}) {
4814
+ const abs = resolve6(filePath);
4815
+ const source = readFileSync6(abs, "utf-8");
4816
+ const ifaceSince = interfaceDiagCount();
4817
+ const refineSince = refineDiagCount();
4818
+ const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(dirname9(abs))?.config).autoBind;
4819
+ const loadModule = opts.loadModule ?? defaultLoadModule;
4820
+ const exported = localNamedExports(source);
4821
+ const kindOf = (fnName) => exported.has(fnName) ? "export" : "local";
4822
+ const analysis = await analyzeFileAsync(abs, source, void 0, opts.records);
4823
+ const entries = [];
4824
+ for (const fn of analysis.functions) {
4825
+ const eff = effectiveInterface(source, fn.name, { loadModule, fromFile: abs, autoBind });
4826
+ if (eff) {
4827
+ entries.push({
4828
+ fn: fn.name,
4829
+ kind: kindOf(fn.name),
4830
+ source: eff.source,
4831
+ params: eff.params.map((p) => ({ name: p.param, display: formatConstraint(p.constraint) })),
4832
+ returns: eff.returns ? formatConstraint(eff.returns.constraint) : void 0
4833
+ });
4834
+ continue;
4835
+ }
4836
+ const params = fn.paramNames.map((name, i) => {
4837
+ const seen = [];
4838
+ for (const c of fn.cases) {
4839
+ const arg = c.args[i];
4840
+ if (!arg) continue;
4841
+ const s = typeValueToString6(arg);
4842
+ if (!seen.includes(s)) seen.push(s);
4843
+ }
4844
+ return { name, display: seen.length > 0 ? seen.join(" | ") : "unknown" };
4845
+ });
4846
+ const ret = fn.combined !== void 0 ? typeValueToString6(fn.combined) : fn.cases.length > 0 ? typeValueToString6(fn.cases[fn.cases.length - 1].result) : void 0;
4847
+ entries.push({ fn: fn.name, kind: kindOf(fn.name), source: "implicit", params, returns: ret });
4848
+ }
4849
+ takeInterfaceDiagsSince(ifaceSince);
4850
+ takeRefineDiagsSince(refineSince);
4851
+ return entries;
4852
+ }
4853
+
4854
+ // src/interface-emitter.ts
4855
+ import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync, renameSync, unlinkSync } from "fs";
4856
+ import { basename as basename2, dirname as dirname10, relative, resolve as resolve7 } from "path";
4857
+ import {
4858
+ execNudoModule,
4859
+ formatConstraint as formatConstraint2,
4860
+ interfaceDiagCount as interfaceDiagCount2,
4861
+ isNodeModulesPath,
4862
+ isNudoConstraint,
4863
+ joinThenProject,
4864
+ localNamedExports as localNamedExports2,
4865
+ parseSource as parseSource2,
4866
+ refineDiagCount as refineDiagCount2,
4867
+ sidecarPathOf,
4868
+ takeInterfaceDiagsSince as takeInterfaceDiagsSince2,
4869
+ takeRefineDiagsSince as takeRefineDiagsSince2,
4870
+ typeValueToAbs as typeValueToAbs6
4871
+ } from "@nudojs/core";
4872
+ import { randomBytes } from "crypto";
4873
+ var GENERATED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo interface --emit`";
4874
+ async function emitInterface(filePath, opts) {
4875
+ const abs = resolve7(filePath);
4876
+ if (isNodeModulesPath(abs) || isNodeModulesPath(sidecarPathOf(abs))) {
4877
+ throw new Error(
4878
+ `emit target '${abs}' is inside node_modules; contract sidecars are never written there`
4879
+ );
4880
+ }
4881
+ const source = readFileSync7(abs, "utf-8");
4882
+ const ifaceSince = interfaceDiagCount2();
4883
+ const refineSince = refineDiagCount2();
4884
+ const sidecarPath = sidecarPathOf(abs);
4885
+ const sidecarSrc = existsSync6(sidecarPath) ? readFileSync7(sidecarPath, "utf-8") : "";
4886
+ const srcRel = relative(dirname10(sidecarPath), abs) || basename2(abs);
4887
+ const sections = collectGeneratedSections(sidecarSrc);
4888
+ const generatedNames = new Set(sections.flatMap((s) => s.names));
4889
+ const declared = topLevelDeclaredNames(sidecarSrc);
4890
+ if (declared === void 0) {
4891
+ throw new Error(
4892
+ `sidecar '${relative(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
4893
+ );
4894
+ }
4895
+ for (const n of generatedNames) declared.delete(n);
4896
+ const analysis = await analyzeFileAsync(abs, source, void 0, opts.records);
4897
+ const exported = localNamedExports2(source);
4898
+ const fnByName = /* @__PURE__ */ new Map();
4899
+ for (const f of analysis.functions) {
4900
+ if (!fnByName.has(f.name)) fnByName.set(f.name, f);
4901
+ }
4902
+ const fileExportOrder = analysis.functions.map((f) => f.name).filter((n) => exported.has(n));
4903
+ let targetNames;
4904
+ if (opts.fnNames && opts.fnNames.length > 0) {
4905
+ targetNames = [...new Set(opts.fnNames)];
4906
+ } else if (opts.all) {
4907
+ targetNames = fileExportOrder;
4908
+ } else {
4909
+ targetNames = fileExportOrder.filter((n) => generatedNames.has(n));
4910
+ }
4911
+ const targetSet = new Set(targetNames);
4912
+ const written = [];
4913
+ const skipped = [];
4914
+ const issues = [];
4915
+ const accepted = [];
4916
+ const planCache = /* @__PURE__ */ new Map();
4917
+ const planFor = (name) => {
4918
+ const hit = planCache.get(name);
4919
+ if (hit) return hit;
4920
+ const fn = fnByName.get(name);
4921
+ const dsl = fn === void 0 ? void 0 : projectFunctionDsl(fn);
4922
+ const roundTrip = dsl !== void 0 && roundTrips(sectionText(name, dsl, srcRel), name);
4923
+ const rec = { dsl, roundTrip };
4924
+ planCache.set(name, rec);
4925
+ return rec;
4926
+ };
4927
+ const atomicSections = /* @__PURE__ */ new Set();
4928
+ for (const s of sections) {
4929
+ if (s.names.length <= 1) continue;
4930
+ const everyRewritable = s.names.every(
4931
+ (n) => targetSet.has(n) && exported.has(n) && !declared.has(n) && planFor(n).dsl !== void 0 && planFor(n).roundTrip
4932
+ );
4933
+ if (!everyRewritable) atomicSections.add(s);
4934
+ }
4935
+ const acceptedAtomic = /* @__PURE__ */ new Set();
4936
+ for (const name of targetNames) {
4937
+ if (!exported.has(name) || !fnByName.has(name)) {
4938
+ skipped.push({
4939
+ fn: name,
4940
+ reason: "not-an-export"
4941
+ });
4942
+ continue;
4943
+ }
4944
+ if (declared.has(name)) {
4945
+ skipped.push({ fn: name, reason: "name-clash" });
4946
+ issues.push({
4947
+ code: "nudo:interface-name-clash",
4948
+ severity: "error",
4949
+ message: `sidecar already has a handwritten binding '${name}' (${srcRel}); handwritten wins \u2014 skipping emit for it`
4950
+ });
4951
+ continue;
4952
+ }
4953
+ const prev = sections.find((s) => s.names.includes(name));
4954
+ if (prev !== void 0 && atomicSections.has(prev)) {
4955
+ if (!acceptedAtomic.has(prev)) {
4956
+ acceptedAtomic.add(prev);
4957
+ const keptText = normalizeSection(prev.text);
4958
+ accepted.push({ fn: prev.names.join("+"), text: keptText, prevText: keptText });
4959
+ issues.push({
4960
+ code: "nudo:interface-multi-declarator",
4961
+ severity: "warning",
4962
+ message: `generated section '${prev.names.join(", ")}' is a hand-merged multi-declarator form; kept verbatim (split it into one export per section to re-emit)`
4963
+ });
4964
+ }
4965
+ skipped.push({ fn: name, reason: "multi-declarator" });
4966
+ continue;
4967
+ }
4968
+ const prevText = prev === void 0 ? void 0 : normalizeSection(prev.text);
4969
+ const plan = planFor(name);
4970
+ if (plan.dsl === void 0 || !plan.roundTrip) {
4971
+ skipped.push({ fn: name, reason: "not-projectable" });
4972
+ if (prevText !== void 0) {
4973
+ accepted.push({ fn: name, text: prevText, prevText });
4974
+ }
4975
+ continue;
4976
+ }
4977
+ const text = sectionText(name, plan.dsl, srcRel);
4978
+ if (opts.mode === "add" && prev !== void 0) {
4979
+ skipped.push({ fn: name, reason: "no-change" });
4980
+ continue;
4981
+ }
4982
+ if (prevText !== void 0 && prevText === normalizeSection(text)) {
4983
+ skipped.push({ fn: name, reason: "no-change" });
4984
+ accepted.push({ fn: name, text: prevText, prevText });
4985
+ continue;
4986
+ }
4987
+ written.push(name);
4988
+ accepted.push({ fn: name, text, prevText });
4989
+ }
4990
+ let finalContent;
4991
+ if (opts.mode === "add") {
4992
+ finalContent = accepted.length === 0 ? sidecarSrc : joinSections(sidecarSrc, accepted.map((a) => a.text));
4993
+ } else {
4994
+ const base = removeSections(sidecarSrc, sections);
4995
+ const preserved = sections.filter((s) => !s.names.some((n) => targetSet.has(n))).map((s) => normalizeSection(s.text));
4996
+ finalContent = joinSections(base, [...preserved, ...accepted.map((a) => a.text)]);
4997
+ }
4998
+ const changed = finalContent !== sidecarSrc;
4999
+ const diff = changed ? unifiedDiff(sidecarSrc, finalContent, relative(process.cwd(), sidecarPath) || sidecarPath) : void 0;
5000
+ if (changed && !opts.dryRun) {
5001
+ const tmp = `${sidecarPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
5002
+ try {
5003
+ writeFileSync(tmp, finalContent, "utf-8");
5004
+ renameSync(tmp, sidecarPath);
5005
+ } catch (e) {
5006
+ try {
5007
+ if (existsSync6(tmp)) unlinkSync(tmp);
5008
+ } catch {
5009
+ }
5010
+ throw e;
5011
+ }
5012
+ }
5013
+ takeRefineDiagsSince2(refineSince);
5014
+ takeInterfaceDiagsSince2(ifaceSince);
5015
+ return {
5016
+ written,
5017
+ skipped,
5018
+ changed,
5019
+ ...diff !== void 0 ? { diff } : {},
5020
+ issues,
5021
+ sidecarPath,
5022
+ ...opts.fnNames && opts.fnNames.length > 0 ? {} : opts.all ? {} : targetNames.length === 0 ? { emptyDefaultTargets: true } : {}
5023
+ };
5024
+ }
5025
+ function formatEmitSummary(sourcePath, sidecarRel, result) {
5026
+ const lines = [];
5027
+ if (result.changed) {
5028
+ lines.push(`Updated ${sourcePath} \u2192 ${sidecarRel}`);
5029
+ lines.push(` written: ${result.written.join(", ") || "(none)"}`);
5030
+ } else {
5031
+ lines.push(`${sourcePath}: no interface changes`);
5032
+ if (result.emptyDefaultTargets) {
5033
+ lines.push(
5034
+ ` tip: default --emit only refreshes existing @generated segments; pass --fn <name> or --all to create new ones`
5035
+ );
5036
+ }
5037
+ }
5038
+ for (const s of result.skipped.filter((x) => x.reason !== "no-change")) {
5039
+ lines.push(` skipped ${s.fn} (${s.reason})`);
5040
+ }
5041
+ for (const i of result.issues) {
5042
+ lines.push(` [${i.severity}] ${i.code}: ${i.message}`);
5043
+ }
5044
+ return lines;
5045
+ }
5046
+ function projectFunctionDsl(fn) {
5047
+ const callsite = fn.cases.filter((c) => c.source === "callsite");
5048
+ const directive = fn.cases.filter((c) => c.source === "directive");
5049
+ const paramCases = callsite.length > 0 ? callsite : directive;
5050
+ const returnCases = callsite.length > 0 ? callsite : directive.length > 0 ? directive : fn.cases;
5051
+ const paramParts = [];
5052
+ for (let i = 0; i < fn.paramNames.length; i++) {
5053
+ const argAbs = [];
5054
+ for (const c of paramCases) {
5055
+ const a = c.args[i];
5056
+ if (a === void 0) continue;
5057
+ argAbs.push(safeTypeValueToAbs(a));
5058
+ }
5059
+ if (argAbs.length === 0) continue;
5060
+ const constraint = joinThenProject(argAbs);
5061
+ if (constraint === void 0) return void 0;
5062
+ paramParts.push(`${fn.paramNames[i]}: ${formatConstraint2(constraint)}`);
5063
+ }
5064
+ const retAbs = [];
5065
+ for (const c of returnCases) {
5066
+ if (c.throws !== void 0 && c.throws.kind !== "never") continue;
5067
+ retAbs.push(c.abs ?? safeTypeValueToAbs(c.result));
5068
+ }
5069
+ const retConstraint = retAbs.length > 0 ? joinThenProject(retAbs) : void 0;
5070
+ if (retConstraint === void 0 && paramParts.length === 0) return void 0;
5071
+ if (retAbs.length > 0 && retConstraint === void 0) return void 0;
5072
+ const ret = retConstraint === void 0 ? "" : `, ${formatConstraint2(retConstraint)}`;
5073
+ return `fn({ ${paramParts.join(", ")} }${ret})`;
5074
+ }
5075
+ function safeTypeValueToAbs(v) {
5076
+ try {
5077
+ return typeValueToAbs6(v);
5078
+ } catch {
5079
+ return { shape: { k: "unknown" }, conf: "opaque" };
5080
+ }
5081
+ }
5082
+ function sectionText(fn, dsl, srcRel) {
5083
+ return `${GENERATED_HEADER}
5084
+ // source: ${srcRel}:${fn}
5085
+ export const ${fn} = ${dsl};
5086
+ `;
5087
+ }
5088
+ function roundTrips(text, fn) {
5089
+ const since = refineDiagCount2();
5090
+ try {
5091
+ const exports = execNudoModule(text);
5092
+ const v = exports[fn];
5093
+ return isNudoConstraint(v) && v.fn !== void 0;
5094
+ } catch {
5095
+ return false;
5096
+ } finally {
5097
+ takeRefineDiagsSince2(since);
5098
+ }
5099
+ }
5100
+ function collectGeneratedSections(sidecarSrc) {
5101
+ if (sidecarSrc.trim() === "") return [];
5102
+ let ast;
5103
+ try {
5104
+ ast = parseSource2(sidecarSrc);
5105
+ } catch {
5106
+ throw new Error("sidecar is not parseable");
5107
+ }
5108
+ const out = [];
5109
+ for (const stmt of ast.program.body) {
5110
+ if (stmt.type !== "ExportNamedDeclaration" || stmt.source) continue;
5111
+ const d = stmt.declaration;
5112
+ if (!d || stmt.start == null || stmt.end == null) continue;
5113
+ const names = [];
5114
+ if (d.type === "VariableDeclaration") {
5115
+ for (const decl of d.declarations) {
5116
+ if (decl.id.type === "Identifier") names.push(decl.id.name);
5117
+ }
5118
+ } else if (d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") {
5119
+ if (d.id) names.push(d.id.name);
5120
+ }
5121
+ if (names.length === 0) continue;
5122
+ const blockStart = generatedCommentBlockStart(sidecarSrc, stmt.start);
5123
+ if (blockStart === void 0) continue;
5124
+ out.push({ names, start: blockStart, end: stmt.end, text: sidecarSrc.slice(blockStart, stmt.end) });
5125
+ }
5126
+ return out;
5127
+ }
5128
+ function generatedCommentBlockStart(src, pos) {
5129
+ const lines = src.slice(0, pos).split("\n");
5130
+ let blockStartLine = -1;
5131
+ let sawGenerated = false;
5132
+ for (let i = lines.length - 1; i >= 0; i--) {
5133
+ const line = lines[i].trim();
5134
+ if (line === "" || line === "*/") continue;
5135
+ if (line.startsWith("//") || line.startsWith("/*") || line.startsWith("*")) {
5136
+ blockStartLine = i;
5137
+ if (/@generated/.test(line)) sawGenerated = true;
5138
+ continue;
5139
+ }
5140
+ break;
5141
+ }
5142
+ if (!sawGenerated || blockStartLine < 0) return void 0;
5143
+ const prefix = lines.slice(0, blockStartLine);
5144
+ return prefix.length === 0 ? 0 : prefix.join("\n").length + 1;
5145
+ }
5146
+ function topLevelDeclaredNames(sidecarSrc) {
5147
+ if (sidecarSrc.trim() === "") return /* @__PURE__ */ new Set();
5148
+ let ast;
5149
+ try {
5150
+ ast = parseSource2(sidecarSrc);
5151
+ } catch {
5152
+ return void 0;
5153
+ }
5154
+ const names = /* @__PURE__ */ new Set();
5155
+ const collect = (stmt) => {
5156
+ if (stmt.type === "VariableDeclaration") {
5157
+ for (const d of stmt.declarations) {
5158
+ if (d.id.type === "Identifier") names.add(d.id.name);
5159
+ }
5160
+ } else if (stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration") {
5161
+ if (stmt.id) names.add(stmt.id.name);
5162
+ }
5163
+ };
5164
+ for (const stmt of ast.program.body) {
5165
+ if (stmt.type === "ExportNamedDeclaration") {
5166
+ if (stmt.declaration) collect(stmt.declaration);
5167
+ if (!stmt.source) {
5168
+ for (const spec of stmt.specifiers) {
5169
+ names.add(spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
5170
+ }
5171
+ }
5172
+ } else {
5173
+ collect(stmt);
5174
+ }
5175
+ }
5176
+ return names;
5177
+ }
5178
+ function removeSections(src, sections) {
5179
+ if (sections.length === 0) return src;
5180
+ const sorted = [...sections].sort((a, b) => a.start - b.start);
5181
+ let out = "";
5182
+ let pos = 0;
5183
+ for (const s of sorted) {
5184
+ out += src.slice(pos, s.start);
5185
+ pos = Math.max(pos, s.end);
5186
+ }
5187
+ out += src.slice(pos);
5188
+ return out;
5189
+ }
5190
+ function normalizeSection(text) {
5191
+ return text.replace(/\s*$/, "") + "\n";
5192
+ }
5193
+ function joinSections(base, sectionTexts) {
5194
+ const normalized = sectionTexts.map(normalizeSection).filter((t) => t.trim() !== "");
5195
+ const tailTrimmed = base.replace(/\s+$/, "");
5196
+ if (normalized.length === 0) return tailTrimmed === "" ? "" : `${tailTrimmed}
5197
+ `;
5198
+ const trimmed = tailTrimmed.replace(/^\s+/, "");
5199
+ const lead = trimmed === "" ? "" : `${trimmed}
5200
+
5201
+ `;
5202
+ return lead + normalized.join("\n");
5203
+ }
4732
5204
  export {
4733
5205
  SEMANTIC_TOKEN_MODIFIERS,
4734
5206
  SEMANTIC_TOKEN_TYPES,
@@ -4761,6 +5233,7 @@ export {
4761
5233
  computeDirtySet,
4762
5234
  defaultAbsLoadModule,
4763
5235
  defaultLoadModule,
5236
+ emitInterface,
4764
5237
  encodeSemanticTokens,
4765
5238
  envValueToAbs,
4766
5239
  evalAbsModuleGraph,
@@ -4770,7 +5243,10 @@ export {
4770
5243
  evictAnalysisFileCacheForFiles,
4771
5244
  evictBPathCacheForFiles,
4772
5245
  evictFnAnalysisCacheForFiles,
5246
+ findProjectConfig,
5247
+ formatEmitSummary,
4773
5248
  formatHarvestSummary,
5249
+ formatInterfaceSurfaceLine,
4774
5250
  generateDts,
4775
5251
  generateFunctionDtsLines,
4776
5252
  generateGuardFunction,
@@ -4787,6 +5263,8 @@ export {
4787
5263
  harvestToAbsModules,
4788
5264
  harvestedValueToAbs,
4789
5265
  insertGeneratedCaseDirectives,
5266
+ interfaceConfig,
5267
+ interfaceSurface,
4790
5268
  isBPathCapable,
4791
5269
  isNudoTargetPath,
4792
5270
  lookupHarvested,