@nudojs/service 0.3.1 → 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,103 +3177,313 @@ 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));
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") {
3184
+ return false;
3116
3185
  }
3117
- return getTypeAtPosition(filePath, source, line, column, activeCases);
3186
+ if (absTv.kind === "refined" && prev.kind === "primitive") return true;
3187
+ return false;
3118
3188
  }
3119
- function getTypeAtPosition(filePath, source, line, column, activeCases) {
3120
- const ast = parse5(source);
3121
- const fileDirectives = extractFileDirectives(ast);
3122
- const envNames = fileDirectives.filter((d) => d.kind === "env").flatMap((d) => d.envs);
3123
- if (isBPathCapable(source, envNames)) {
3124
- try {
3125
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(ast));
3126
- const { modules } = evalAbsModuleGraph(source, filePath);
3127
- const absNodes = collectAbsNodeTypes(source, {
3128
- ...seeds,
3129
- modules,
3130
- file: ast
3131
- });
3132
- const absAt = findAbsAtPosition(absNodes, line, column);
3133
- if (absAt) return absToTypeValue2(absAt);
3134
- const ident = findIdentNameAtPosition(source, line, column, ast);
3135
- if (ident) {
3136
- const binds = collectAbsBindingsFromGraph(source, filePath, {
3137
- seedVars: seeds.seedVars,
3138
- 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
3139
3208
  });
3140
- const bound = binds.get(ident);
3141
- if (bound) return absToTypeValue2(bound);
3209
+ modules = graph.modules;
3210
+ } catch {
3211
+ modules = void 0;
3142
3212
  }
3213
+ }
3214
+ if (envNames.length > 0) {
3215
+ modules = { ...collectEnvModules(envNames), ...modules ?? {} };
3216
+ }
3217
+ try {
3218
+ importLocals = buildAbsImportLocalMap(source, filePath);
3143
3219
  } catch {
3220
+ importLocals = /* @__PURE__ */ new Map();
3144
3221
  }
3145
3222
  }
3146
- resetMemo();
3147
- resetEnvModules();
3148
- setModuleResolver(resolveModule);
3149
- setCurrentFileDir(dirname5(filePath));
3150
- setCurrentSource(source);
3151
- const globalEnv = createEnvironment2();
3152
- {
3153
- const loaded = envNames.length > 0 ? loadEnvs2(envNames, globalEnv) : { modules: {} };
3154
- const auto = autoHarvestModules(source, dirname5(filePath));
3155
- const modules = { ...loaded.modules, ...auto };
3156
- 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);
3157
3229
  }
3158
- evaluateProgram(ast, globalEnv);
3159
- const nodeTypeMap = /* @__PURE__ */ new Map();
3160
- buildNodeTypeMap(ast, globalEnv, nodeTypeMap);
3161
- const functions = extractDirectives(ast);
3162
- const enclosingFn = findEnclosingFunction(functions, line);
3163
- if (enclosingFn) {
3164
- const caseDirectives = enclosingFn.directives.filter((d) => d.kind === "case");
3165
- if (caseDirectives.length > 0) {
3166
- const caseIndex = activeCases?.get(enclosingFn.name) ?? 0;
3167
- const directive = caseDirectives[Math.min(caseIndex, caseDirectives.length - 1)];
3168
- const fnNodeTypeMap = /* @__PURE__ */ new Map();
3169
- setNodeTypeCollector((node, tv) => fnNodeTypeMap.set(node, tv));
3170
- evaluateFunctionFull(enclosingFn.node, directive.args, globalEnv);
3171
- setNodeTypeCollector(null);
3172
- for (const [node, tv] of fnNodeTypeMap) {
3173
- nodeTypeMap.set(node, tv);
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;
3242
+ }
3243
+ return rec;
3244
+ });
3245
+ }
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
+ });
3252
+ try {
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
+ );
3174
3264
  }
3175
3265
  }
3266
+ } catch {
3267
+ } finally {
3268
+ setAbsNodeCollector(null);
3176
3269
  }
3177
- setModuleResolver(null);
3178
- resetEnvModules();
3179
- resetMockModules();
3180
- return findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column);
3270
+ return { binds, nodes };
3181
3271
  }
3182
- function getHoverAtPosition(filePath, source, line, column, activeCases) {
3183
- let file;
3272
+ function buildAbsImportLocalMap(source, fromFile) {
3273
+ const out = /* @__PURE__ */ new Map();
3184
3274
  try {
3185
- file = parse5(source);
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
+ }
3186
3295
  } catch {
3187
- file = void 0;
3188
3296
  }
3189
- const envNames = collectEnvNames(filePath, source, false);
3190
- const fnName = findFunctionNameAtPosition(source, line, column, file);
3191
- let gDisplay;
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;
3192
3478
  let gAbs;
3193
3479
  let gMulti;
3194
3480
  if (fnName) {
3195
3481
  try {
3196
- const g = generalizeFromAst2(fnName, source, file ? { file } : {});
3482
+ const g = generalizeFromAst3(fnName, source, file ? { file } : {});
3197
3483
  if (g) {
3198
3484
  gDisplay = g.display;
3199
- gAbs = formatAbs3(g.symbolic);
3200
- gMulti = formatAbsMultiline(g.symbolic, fnName);
3485
+ gAbs = formatAbs4(g.symbolic);
3486
+ gMulti = formatAbsMultiline2(g.symbolic, fnName);
3201
3487
  }
3202
3488
  } catch {
3203
3489
  }
@@ -3215,9 +3501,14 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3215
3501
  absMultiline: info2.absMultiline ?? gMulti
3216
3502
  };
3217
3503
  };
3218
- if (isBPathCapable(source, envNames)) {
3504
+ const insideCaseFn = positionInsideCaseFunction(
3505
+ source,
3506
+ file ?? parse6(source),
3507
+ line
3508
+ );
3509
+ if (isBPathCapable(source, envNames) && !insideCaseFn) {
3219
3510
  try {
3220
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3511
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
3221
3512
  const { modules } = evalAbsModuleGraph(source, filePath);
3222
3513
  const absNodes = collectAbsNodeTypes(source, {
3223
3514
  ...seeds,
@@ -3233,34 +3524,34 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3233
3524
  });
3234
3525
  const bound = binds.get(ident2);
3235
3526
  if (bound) {
3236
- const absLine = formatAbs3(bound);
3237
- const absMulti = formatAbsMultiline(bound, ident2);
3527
+ const absLine = formatAbs4(bound);
3528
+ const absMulti = formatAbsMultiline2(bound, ident2);
3238
3529
  return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
3239
3530
  }
3240
3531
  }
3241
3532
  if (absAt) {
3242
- const absLine = formatAbs3(absAt);
3243
- const absMulti = formatAbsMultiline(absAt, void 0);
3533
+ const absLine = formatAbs4(absAt);
3534
+ const absMulti = formatAbsMultiline2(absAt, void 0);
3244
3535
  return attachIntension({ typeText: absLine, abs: absLine, absMultiline: absMulti });
3245
3536
  }
3246
3537
  } catch {
3247
3538
  }
3248
3539
  }
3249
3540
  const tv = getTypeAtPosition(filePath, source, line, column, activeCases);
3250
- const info = tv ? { typeText: typeValueToString3(tv) } : null;
3541
+ const info = tv ? { typeText: typeValueToString4(tv) } : null;
3251
3542
  const ident = findIdentNameAtPosition(source, line, column, file);
3252
- if (ident && !fnName) {
3543
+ if (ident && !fnName && !insideCaseFn) {
3253
3544
  try {
3254
3545
  if (isBPathCapable(source, []) || !/\brequire\s*\(/.test(source)) {
3255
- const seeds = mockDirectivesToAbsSeeds(extractDirectives(file ?? parse5(source)));
3546
+ const seeds = mockDirectivesToAbsSeeds(extractDirectives2(file ?? parse6(source)));
3256
3547
  const absBinds = collectAbsBindingsFromGraph(source, filePath, {
3257
3548
  seedVars: seeds.seedVars,
3258
3549
  seedFns: seeds.seedFns
3259
3550
  });
3260
3551
  const absBound = absBinds.get(ident);
3261
3552
  if (absBound) {
3262
- const absLine = formatAbs3(absBound);
3263
- const absMulti = formatAbsMultiline(absBound, ident);
3553
+ const absLine = formatAbs4(absBound);
3554
+ const absMulti = formatAbsMultiline2(absBound, ident);
3264
3555
  if (info) {
3265
3556
  info.abs = absLine;
3266
3557
  info.absMultiline = absMulti;
@@ -3273,9 +3564,9 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3273
3564
  }
3274
3565
  }
3275
3566
  try {
3276
- if (isBPathCapable(source, []) || !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source)) {
3567
+ if (!insideCaseFn && (isBPathCapable(source, []) || !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source))) {
3277
3568
  const seeds = mockDirectivesToAbsSeeds(
3278
- extractDirectives(file ?? parse5(source))
3569
+ extractDirectives2(file ?? parse6(source))
3279
3570
  );
3280
3571
  const { modules } = evalAbsModuleGraph(source, filePath);
3281
3572
  const nodeTypes = collectAbsNodeTypes(source, {
@@ -3285,8 +3576,8 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3285
3576
  });
3286
3577
  const absAt = findAbsAtPosition(nodeTypes, line, column);
3287
3578
  if (absAt) {
3288
- const absLine = formatAbs3(absAt);
3289
- const absMulti = formatAbsMultiline(absAt, void 0);
3579
+ const absLine = formatAbs4(absAt);
3580
+ const absMulti = formatAbsMultiline2(absAt, void 0);
3290
3581
  if (info) {
3291
3582
  info.abs = absLine;
3292
3583
  info.absMultiline = absMulti;
@@ -3301,9 +3592,9 @@ function getHoverAtPosition(filePath, source, line, column, activeCases) {
3301
3592
  }
3302
3593
  function findIdentNameAtPosition(source, line, column, fileAst) {
3303
3594
  try {
3304
- const ast = fileAst ?? parse5(source);
3595
+ const ast = fileAst ?? parse6(source);
3305
3596
  let found;
3306
- traverse(ast, {
3597
+ traverse2(ast, {
3307
3598
  Identifier(path) {
3308
3599
  const loc = path.node.loc;
3309
3600
  if (!loc) return;
@@ -3319,9 +3610,9 @@ function findIdentNameAtPosition(source, line, column, fileAst) {
3319
3610
  }
3320
3611
  function findFunctionNameAtPosition(source, line, column, fileAst) {
3321
3612
  try {
3322
- const ast = fileAst ?? parse5(source);
3613
+ const ast = fileAst ?? parse6(source);
3323
3614
  let found;
3324
- traverse(ast, {
3615
+ traverse2(ast, {
3325
3616
  Identifier(path) {
3326
3617
  const loc = path.node.loc;
3327
3618
  if (!loc) return;
@@ -3374,7 +3665,7 @@ function findBestTypeAtPosition(nodeTypeMap, globalEnv, ast, line, column) {
3374
3665
  }
3375
3666
  function findIdentifierAtPosition(ast, line, column) {
3376
3667
  let found = null;
3377
- const traverseFn = typeof traverse === "function" ? traverse : traverse.default;
3668
+ const traverseFn = typeof traverse2 === "function" ? traverse2 : traverse2.default;
3378
3669
  try {
3379
3670
  traverseFn(ast, {
3380
3671
  Identifier(path) {
@@ -3398,18 +3689,18 @@ function getCompletionsAtPosition(filePath, source, line, column) {
3398
3689
  const safeSource = sanitizeSourceForParsing(source);
3399
3690
  let ast;
3400
3691
  try {
3401
- ast = parse5(safeSource);
3692
+ ast = parse6(safeSource);
3402
3693
  } catch {
3403
3694
  try {
3404
- ast = parse5(source);
3695
+ ast = parse6(source);
3405
3696
  } catch {
3406
3697
  return [];
3407
3698
  }
3408
3699
  }
3409
3700
  resetMemo();
3410
3701
  setModuleResolver(resolveModule);
3411
- setCurrentFileDir(dirname5(filePath));
3412
- const globalEnv = createEnvironment2();
3702
+ setCurrentFileDir(dirname7(filePath));
3703
+ const globalEnv = createEnvironment3();
3413
3704
  evaluateProgram(ast, globalEnv);
3414
3705
  if (!globalEnv.has(objName)) {
3415
3706
  setModuleResolver(null);
@@ -3429,11 +3720,11 @@ function getTextBeforePosition(source, line, column) {
3429
3720
  return lines[line - 1].slice(0, column);
3430
3721
  }
3431
3722
  function getVariableCompletions(filePath, source) {
3432
- const ast = parse5(source);
3723
+ const ast = parse6(source);
3433
3724
  resetMemo();
3434
3725
  setModuleResolver(resolveModule);
3435
- setCurrentFileDir(dirname5(filePath));
3436
- const globalEnv = createEnvironment2();
3726
+ setCurrentFileDir(dirname7(filePath));
3727
+ const globalEnv = createEnvironment3();
3437
3728
  evaluateProgram(ast, globalEnv);
3438
3729
  const ownBindings = globalEnv.getOwnBindings();
3439
3730
  const completions = [];
@@ -3442,7 +3733,7 @@ function getVariableCompletions(filePath, source) {
3442
3733
  completions.push({
3443
3734
  label: name,
3444
3735
  kind: tv.kind === "function" ? "method" : "variable",
3445
- detail: typeValueToString3(tv)
3736
+ detail: typeValueToString4(tv)
3446
3737
  });
3447
3738
  }
3448
3739
  setModuleResolver(null);
@@ -3450,7 +3741,7 @@ function getVariableCompletions(filePath, source) {
3450
3741
  }
3451
3742
  function builtinMemberType(memberExpr) {
3452
3743
  try {
3453
- const result = evaluate(parse5(`${memberExpr};`), createEnvironment2());
3744
+ const result = evaluate(parse6(`${memberExpr};`), createEnvironment3());
3454
3745
  if (!result || typeof result !== "object" || !("kind" in result)) return null;
3455
3746
  return result;
3456
3747
  } catch {
@@ -3466,10 +3757,10 @@ function describeMember(label, tv, fallbackClass) {
3466
3757
  const sig = getFnSig3(tv);
3467
3758
  if (sig) {
3468
3759
  const paramNames = tv.kind === "function" ? tv.params : [];
3469
- const params = sig.paramTypes.map((p, i) => `${paramNames[i] ?? `arg${i}`}: ${typeValueToString3(p)}`).join(", ");
3470
- 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)}`;
3471
3762
  }
3472
- if (tv.kind !== "function") return typeValueToString3(tv);
3763
+ if (tv.kind !== "function") return typeValueToString4(tv);
3473
3764
  }
3474
3765
  return `${label}(\u2026)@${fallbackClass}`;
3475
3766
  }
@@ -3540,7 +3831,7 @@ function getCompletionsForType(tv) {
3540
3831
  completions.push({
3541
3832
  label: key,
3542
3833
  kind: val.kind === "function" ? "method" : "property",
3543
- detail: typeValueToString3(val)
3834
+ detail: typeValueToString4(val)
3544
3835
  });
3545
3836
  }
3546
3837
  return completions;
@@ -3550,7 +3841,7 @@ function getCompletionsForType(tv) {
3550
3841
  completions.push({
3551
3842
  label: key,
3552
3843
  kind: val.kind === "function" ? "method" : "property",
3553
- detail: typeValueToString3(val)
3844
+ detail: typeValueToString4(val)
3554
3845
  });
3555
3846
  }
3556
3847
  return completions;
@@ -3569,204 +3860,18 @@ function getCompletionsForType(tv) {
3569
3860
  }
3570
3861
  return completions;
3571
3862
  }
3572
- function absIsBetter(absTv, prev) {
3573
- if (prev.kind === "unknown") return absTv.kind !== "unknown";
3574
- if (absTv.kind === "unknown" || absTv.kind === "never") return false;
3575
- 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") {
3576
- return false;
3577
- }
3578
- if (absTv.kind === "refined" && prev.kind === "primitive") return true;
3579
- return false;
3580
- }
3581
- function isSelfContainedSource(source, envNames) {
3582
- if (envNames.length > 0) return false;
3583
- return !/\brequire\s*\(|\bimport\s*[{'"*]/.test(source);
3584
- }
3585
- function absModulesOk(source, envNames) {
3586
- void source;
3587
- void envNames;
3588
- return true;
3589
- }
3590
- function collectAbsCallRecords(source, seeds, filePath, precomputedModules, envNames = []) {
3591
- const absCalls = [];
3592
- let modules = precomputedModules;
3593
- let importLocals = /* @__PURE__ */ new Map();
3594
- if (filePath && absModulesOk(source, envNames)) {
3595
- if (!modules) {
3596
- try {
3597
- const graph = evalAbsModuleGraph(source, filePath, {
3598
- seedVars: seeds?.seedVars,
3599
- seedFns: seeds?.seedFns
3600
- });
3601
- modules = graph.modules;
3602
- } catch {
3603
- modules = void 0;
3604
- }
3605
- }
3606
- if (envNames.length > 0) {
3607
- modules = { ...collectEnvModules(envNames), ...modules ?? {} };
3608
- }
3609
- try {
3610
- importLocals = buildAbsImportLocalMap(source, filePath);
3611
- } catch {
3612
- importLocals = /* @__PURE__ */ new Map();
3613
- }
3614
- }
3615
- setAbsCallCollector((r) => absCalls.push(r));
3616
- try {
3617
- evalProgramAbs2(source, { ...seeds, modules });
3618
- } catch {
3619
- } finally {
3620
- setAbsCallCollector(null);
3621
- }
3622
- return absCalls.map((r) => {
3623
- const rec = {
3624
- fnName: r.fnName,
3625
- argTypes: r.args.map((a) => absToTypeValue2(a)),
3626
- resultType: r.threw ? T2.never : absToTypeValue2(r.result),
3627
- throws: r.threw ? absToTypeValue2(r.result) : T2.never,
3628
- callLoc: r.callLoc
3629
- };
3630
- const imp = importLocals.get(r.fnName);
3631
- if (imp) {
3632
- rec.targetModule = imp.modulePath;
3633
- rec.targetExport = imp.exportName;
3634
- }
3635
- return rec;
3636
- });
3637
- }
3638
- function collectAbsBindsAndNodes(source, seeds, modules) {
3639
- const binds = /* @__PURE__ */ new Map();
3640
- const nodes = /* @__PURE__ */ new Map();
3641
- setAbsNodeCollector((node, value) => {
3642
- nodes.set(node, value);
3643
- });
3644
- try {
3645
- const { env } = evalProgramAbs2(source, {
3646
- ...seeds,
3647
- modules
3648
- });
3649
- for (const [k, v] of env.vars) binds.set(k, v);
3650
- for (const [name, impl] of env.fns) {
3651
- if (!binds.has(name)) {
3652
- binds.set(
3653
- name,
3654
- absFunction5(impl.params, { body: impl.body, async: impl.async, env })
3655
- );
3656
- }
3657
- }
3658
- } catch {
3659
- } finally {
3660
- setAbsNodeCollector(null);
3661
- }
3662
- return { binds, nodes };
3663
- }
3664
- function buildAbsImportLocalMap(source, fromFile) {
3665
- const out = /* @__PURE__ */ new Map();
3666
- try {
3667
- const file = parse5(source);
3668
- for (const stmt of file.program.body) {
3669
- if (stmt.type !== "ImportDeclaration") continue;
3670
- const spec = stmt.source.value;
3671
- let modulePath = null;
3672
- if (spec.startsWith(".") || spec.startsWith("/")) {
3673
- modulePath = resolveImportAbs(spec, fromFile);
3674
- } else if (!spec.startsWith("node:")) {
3675
- modulePath = spec;
3676
- }
3677
- if (!modulePath) continue;
3678
- for (const s of stmt.specifiers) {
3679
- if (s.type === "ImportSpecifier") {
3680
- const imported = s.imported.type === "Identifier" ? s.imported.name : String(s.imported);
3681
- out.set(s.local.name, { modulePath, exportName: imported });
3682
- } else if (s.type === "ImportDefaultSpecifier") {
3683
- out.set(s.local.name, { modulePath, exportName: "default" });
3684
- }
3685
- }
3686
- }
3687
- } catch {
3688
- }
3689
- return out;
3690
- }
3691
- function resolveImportAbs(spec, fromFile) {
3692
- const base = dirname5(resolve4(fromFile));
3693
- const p = resolve4(base, spec);
3694
- for (const cand of [p, `${p}.js`, `${p}.mjs`, `${p}.ts`, resolve4(p, "index.js")]) {
3695
- if (existsSync3(cand) && !statSync3(cand).isDirectory()) return cand;
3696
- }
3697
- return null;
3698
- }
3699
- function tryEvalAbs(source, fnName, args, filePath, mocks) {
3700
- const raw = tryEvalAbsRaw(source, fnName, args, filePath, mocks);
3701
- return raw ? absToTypeValue2(raw) : void 0;
3702
- }
3703
- function tryEvalAbsRaw(source, fnName, args, filePath, mocks) {
3704
- if (/\brequire\s*\(/.test(source)) return void 0;
3705
- try {
3706
- const absArgs = args.map((a) => typeValueToAbs5(a));
3707
- if (filePath) {
3708
- const viaB = tryBPathCall(source, filePath, fnName, absArgs, { mocks });
3709
- if (viaB) return viaB;
3710
- }
3711
- let modules;
3712
- if (filePath && /\bimport\s*[{'"*]/.test(source)) {
3713
- modules = evalAbsModuleGraph(source, filePath).modules;
3714
- }
3715
- const result = analyzeFn(source, fnName, absArgs, void 0, void 0, void 0, modules);
3716
- if (result.shape.k === "unknown" && !result.term) {
3717
- return void 0;
3718
- }
3719
- return result;
3720
- } catch {
3721
- return void 0;
3722
- }
3723
- }
3724
- function tryEvalEntryAbs(source, fnName, args, filePath, mocks) {
3725
- return tryEvalAbs(source, fnName, args, filePath, mocks);
3726
- }
3727
- function tryAttachIntension(caseResult, source, fnName) {
3728
- try {
3729
- const g = generalizeFromAst2(fnName, source);
3730
- if (!g) return;
3731
- const sym = g.symbolic;
3732
- const intension = {
3733
- display: g.display,
3734
- abs: formatAbs3(sym),
3735
- absMultiline: formatAbsMultiline(sym, fnName)
3736
- };
3737
- if (sym.term && sym.term.op !== "lit") {
3738
- intension.term = termToString(sym.term);
3739
- }
3740
- if (sym.pred && sym.pred.op !== "true") {
3741
- intension.pred = predToString(sym.pred);
3742
- }
3743
- intension.conf = sym.conf;
3744
- caseResult.intension = intension;
3745
- } catch {
3746
- }
3747
- }
3748
- function attachAbsToIntension(caseResult, absVal, label) {
3749
- const prev = caseResult.intension ?? {};
3750
- caseResult.abs = absVal;
3751
- caseResult.intension = {
3752
- ...prev,
3753
- abs: formatAbs3(absVal),
3754
- absMultiline: formatAbsMultiline(absVal, label ?? caseResult.name),
3755
- conf: absVal.conf
3756
- };
3757
- }
3758
3863
 
3759
3864
  // src/index.ts
3760
3865
  import { collectAbsInlays } from "@nudojs/core";
3761
3866
 
3762
3867
  // src/infer-json.ts
3763
- import { typeValueToString as typeValueToString4 } from "@nudojs/core";
3868
+ import { typeValueToString as typeValueToString5 } from "@nudojs/core";
3764
3869
  function mapCase(c) {
3765
3870
  const out = {
3766
3871
  name: c.name,
3767
- args: c.args.map((a) => typeValueToString4(a)),
3768
- result: typeValueToString4(c.result),
3769
- 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,
3770
3875
  source: c.source ?? null
3771
3876
  };
3772
3877
  if (c.aggregatedFrom !== void 0) out.aggregatedFrom = c.aggregatedFrom;
@@ -3791,7 +3896,7 @@ function mapFunction(f) {
3791
3896
  cases: f.cases.map(mapCase)
3792
3897
  };
3793
3898
  if (f.noDeclaration) out.noDeclaration = true;
3794
- if (f.combined) out.combined = typeValueToString4(f.combined);
3899
+ if (f.combined) out.combined = typeValueToString5(f.combined);
3795
3900
  return out;
3796
3901
  }
3797
3902
  function serializeInferJson(result, file) {
@@ -3830,36 +3935,10 @@ function serializeInferJson(result, file) {
3830
3935
  function isNudoTargetPath(path) {
3831
3936
  const lower = path.toLowerCase();
3832
3937
  if (lower.endsWith(".d.ts")) return false;
3938
+ if (lower.endsWith(".nudo.js") || lower.endsWith(".nudo.ts")) return false;
3833
3939
  return lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".ts");
3834
3940
  }
3835
3941
 
3836
- // src/load-module.ts
3837
- import { existsSync as existsSync4, statSync as statSync4, readFileSync as readFileSync5 } from "fs";
3838
- import { dirname as dirname6, resolve as resolve5, join as join3 } from "path";
3839
- function defaultLoadModule(spec, fromFile) {
3840
- if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
3841
- try {
3842
- const base = dirname6(resolve5(fromFile));
3843
- const p = resolve5(base, spec);
3844
- for (const cand of [
3845
- p,
3846
- `${p}.js`,
3847
- `${p}.mjs`,
3848
- `${p}.ts`,
3849
- join3(p, "index.js"),
3850
- join3(p, "index.mjs"),
3851
- join3(p, "index.ts")
3852
- ]) {
3853
- if (existsSync4(cand) && !statSync4(cand).isDirectory()) {
3854
- return readFileSync5(cand, "utf-8");
3855
- }
3856
- }
3857
- return void 0;
3858
- } catch {
3859
- return void 0;
3860
- }
3861
- }
3862
-
3863
3942
  // src/session-cache.ts
3864
3943
  import {
3865
3944
  resetGeneralizeMemo,
@@ -3910,15 +3989,9 @@ function summarizeNodeEnv(env) {
3910
3989
  }
3911
3990
 
3912
3991
  // src/semantic-tokens.ts
3913
- import { dirname as dirname7 } from "path";
3914
- import { createEnvironment as createEnvironment3 } from "@nudojs/core";
3915
- import { parse as parse6 } from "@nudojs/parser";
3916
- import {
3917
- evaluateProgram as evaluateProgram2,
3918
- setModuleResolver as setModuleResolver2,
3919
- setCurrentFileDir as setCurrentFileDir2,
3920
- resetMemo as resetMemo2
3921
- } 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";
3922
3995
  var SEMANTIC_TOKEN_TYPES = [
3923
3996
  "function",
3924
3997
  "variable",
@@ -3960,19 +4033,19 @@ var MOD_DECLARATION = 1 << SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration");
3960
4033
  function buildSemanticTokens(filePath, source) {
3961
4034
  let ast;
3962
4035
  try {
3963
- ast = parse6(source);
4036
+ ast = parse7(source);
3964
4037
  } catch {
3965
4038
  return [];
3966
4039
  }
3967
- const env = createEnvironment3();
3968
- resetMemo2();
3969
- setModuleResolver2(resolveModule);
3970
- setCurrentFileDir2(dirname7(filePath));
4040
+ const env = createEnvironment4();
4041
+ resetMemo();
4042
+ setModuleResolver(resolveModule);
4043
+ setCurrentFileDir(dirname8(filePath));
3971
4044
  try {
3972
- evaluateProgram2(ast, env);
4045
+ evaluateProgram(ast, env);
3973
4046
  } catch {
3974
4047
  } finally {
3975
- setModuleResolver2(null);
4048
+ setModuleResolver(null);
3976
4049
  }
3977
4050
  const program = ast.program ?? ast;
3978
4051
  const ownBindings = env.getOwnBindings();
@@ -4364,7 +4437,7 @@ function generateGuardBody(tv, varName) {
4364
4437
  }
4365
4438
 
4366
4439
  // src/case-emitter.ts
4367
- import { parse as parse7, extractDirectives as extractDirectives2 } from "@nudojs/parser";
4440
+ import { parse as parse8, extractDirectives as extractDirectives3 } from "@nudojs/parser";
4368
4441
  var GENERATED_PREFIX = "call@";
4369
4442
  var GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
4370
4443
  var IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
@@ -4507,11 +4580,11 @@ function collectExistingCases(source) {
4507
4580
  const byName = /* @__PURE__ */ new Map();
4508
4581
  let ast;
4509
4582
  try {
4510
- ast = parse7(source, { errorRecovery: true });
4583
+ ast = parse8(source, { errorRecovery: true });
4511
4584
  } catch {
4512
4585
  return byName;
4513
4586
  }
4514
- for (const { name, directives } of extractDirectives2(ast)) {
4587
+ for (const { name, directives } of extractDirectives3(ast)) {
4515
4588
  for (const d of directives) {
4516
4589
  if (d.kind !== "case") continue;
4517
4590
  const list = byName.get(name);
@@ -4716,6 +4789,418 @@ function unifiedDiff(a, b, path) {
4716
4789
  }
4717
4790
  return out.join("\n") + "\n";
4718
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
+ }
4719
5204
  export {
4720
5205
  SEMANTIC_TOKEN_MODIFIERS,
4721
5206
  SEMANTIC_TOKEN_TYPES,
@@ -4748,6 +5233,7 @@ export {
4748
5233
  computeDirtySet,
4749
5234
  defaultAbsLoadModule,
4750
5235
  defaultLoadModule,
5236
+ emitInterface,
4751
5237
  encodeSemanticTokens,
4752
5238
  envValueToAbs,
4753
5239
  evalAbsModuleGraph,
@@ -4757,7 +5243,10 @@ export {
4757
5243
  evictAnalysisFileCacheForFiles,
4758
5244
  evictBPathCacheForFiles,
4759
5245
  evictFnAnalysisCacheForFiles,
5246
+ findProjectConfig,
5247
+ formatEmitSummary,
4760
5248
  formatHarvestSummary,
5249
+ formatInterfaceSurfaceLine,
4761
5250
  generateDts,
4762
5251
  generateFunctionDtsLines,
4763
5252
  generateGuardFunction,
@@ -4774,6 +5263,8 @@ export {
4774
5263
  harvestToAbsModules,
4775
5264
  harvestedValueToAbs,
4776
5265
  insertGeneratedCaseDirectives,
5266
+ interfaceConfig,
5267
+ interfaceSurface,
4777
5268
  isBPathCapable,
4778
5269
  isNudoTargetPath,
4779
5270
  lookupHarvested,