@nudojs/service 5.0.0-beta.0 → 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @nudojs/service
2
2
 
3
- Shared inference service for [Nudo](https://github.com/nudojs/nudo) IDE integrations.
3
+ Nudo analysis core and emit products: Abs-native file analysis, evaluator host API, interface/dts/schema/guard projections, and session caches.
4
4
 
5
5
  ## What is Nudo?
6
6
 
@@ -8,28 +8,23 @@ Nudo is a type inference engine for JavaScript. The type system is Abs (`shape
8
8
 
9
9
  ## This package
10
10
 
11
- `@nudojs/service` provides the high-level analysis API used by editor extensions and build tools.
11
+ `@nudojs/service` provides the analysis core used by CLI, editor extensions, and build tools, plus the emit faces that project Abs to dts/schema/guard/interface.
12
12
 
13
13
  Production evaluation is **Abs-native B-path** (`evalAbsModuleGraph` + `runTranspiled`); the TypeValue AST interpreter is gone. Prefer a focused subpath over the full barrel:
14
14
 
15
15
  | Subpath | Face |
16
16
  |---|---|
17
17
  | `@nudojs/service/analysis` | File analysis, diagnostics, call records, Abs module-graph eval |
18
- | `@nudojs/service/interface` | Interface / contract product (surface, emit, draft, derive) |
19
- | `@nudojs/service/dts` | Extensional projections: dts / schema / standard / guard |
20
- | `@nudojs/service/case` | Debug case reports + case directive emit (`nudo test`) |
21
- | `@nudojs/service/lsp` | IDE surface (hover, completions, semantic tokens, inlays) |
22
- | `@nudojs/service/harvest` | `@types` → Abs env harvesting |
23
18
  | `@nudojs/service/evaluator` | Host API surface (env/config/CallRecord) — not the eval engine |
24
- | `@nudojs/service` | Full barrel (stable; all of the above) |
19
+ | `@nudojs/service` | Full barrel (stable; analysis face) |
20
+
21
+ Emit products live in `@nudojs/service/emit` (interface/contract, dts, schema, guard, case reports). IDE surface lives in `@nudojs/lsp`; `@types` harvest in `@nudojs/harvester`.
25
22
 
26
23
  Highlights:
27
24
 
28
25
  - **File analysis** — `analyzeFile` returns diagnostics, function analyses, and case results
29
- - **IDE features** — `getTypeAtPosition`, `getCompletionsAtPosition`, `getCasesForFile`
30
- - **DTS generation** — `generateDts` / `absToTSType` for producing `.d.ts` output from Abs
31
- - **Schema projection** — `absToSchemaSource` / `projectAbsToSchema` produce dialect schema source
32
- - **Guard generation** — `generateGuardFunction` produces zero-dependency runtime type guards
26
+ - **Module graph** — `evalAbsModuleGraph` evaluates a file's import closure as Abs
27
+ - **Session caches** — analysis / fn / B-path caches with eviction and project-config limits
33
28
 
34
29
  ## Install
35
30
 
@@ -1,10 +1,16 @@
1
- import { A as AnalyzeLoadModule, D as DirectiveCaseMode, a as AnalysisResult } from './analyzer-types-JyJiCt8w.js';
1
+ import { S as SourceLocation, a as AnalyzeLoadModule, D as DirectiveCaseMode, A as AnalysisResult } from './analyzer-types-CfBrPK9L.js';
2
+ import { parse } from '@nudojs/parser';
3
+ import { Node } from '@babel/types';
2
4
  import { C as CallRecord } from './call-record-CkyCFkT9.js';
3
5
  import { Abs, AbsModuleExports, BCallRecord, Phi, TranspiledCallResult } from '@nudojs/core';
4
- import { Node } from '@babel/types';
5
6
  import { BMemberDiag } from '@nudojs/core/internal';
6
7
  import { D as DiagnosticsLevel, A as AnalysisConfig } from './config-DDB588oA.js';
7
8
 
9
+ declare function resolveModule(source: string, fromDir: string): {
10
+ ast: ReturnType<typeof parse>;
11
+ filePath: string;
12
+ json?: unknown;
13
+ } | null;
8
14
  /** mtime 边缓存:key 为文件路径,edges 为已抽取的相对 import 边(与 buildModuleGraph 返回语义一致)。 */
9
15
  type ModuleGraphCache = Map<string, {
10
16
  mtimeMs: number;
@@ -21,6 +27,14 @@ declare function computeDirtySet(dependents: Map<string, Set<string>>, changedFi
21
27
  /** Topological order with dependencies before dependents (only imports edges internal to dirty; cycles tolerated — remaining files appended in arbitrary order). */
22
28
  declare function topoSortDirty(imports: Map<string, Set<string>>, dirty: string[]): string[];
23
29
 
30
+ /**
31
+ * AST 辅助:函数收集 / 形参 / 定位 / 节点 Abs 表。
32
+ * 自 analyzer.ts 机械拆出;语义未改。
33
+ */
34
+
35
+ declare function locFromNode(node: Node): SourceLocation;
36
+
37
+ declare function collectEnvNames(filePath: string, source: string, includeProject: boolean): string[];
24
38
  /**
25
39
  * 调用点发现(阶段一):在"使用现场"文件(测试 / 上层应用)中求值
26
40
  * 顶层代码,收集它对(外部模块导出的)函数的调用记录。每条记录带
@@ -264,4 +278,4 @@ declare function hasNudoDirectives(source: string): boolean;
264
278
  */
265
279
  declare function shouldAnalyzeFile(filePath: string, source: string | undefined, config?: AnalysisConfig): boolean;
266
280
 
267
- export { type AbsGraphOptions as A, type BPathRunResult as B, setEnvHarvestConflictCollector as C, shouldAnalyzeFile as D, type EnvHarvestConflict as E, hasNudoDirectives as F, topoSortDirty as G, trimBPathCache as H, tryBPathCall as I, tryBPathCallFull as J, tryRunBPath as K, type MergeHarvestOptions as M, type AbsModuleCacheEntry as a, type AbsModuleGraphResult as b, type AbsModuleLoadIssue as c, type ModuleGraphCache as d, analyzeFile as e, analyzeFileAsync as f, buildModuleGraph as g, clearAbsModuleCache as h, clearBPathCache as i, collectAbsBindingsFromGraph as j, collectBPathReplacements as k, collectCallRecords as l, collectEnvGlobals as m, collectEnvModules as n, computeDirtySet as o, defaultAbsLoadModule as p, diagnosticsLevelForFile as q, evalAbsModuleGraph as r, evictAbsModuleCacheFiles as s, evictBPathCacheForFiles as t, filterDiagnosticsByLevel as u, getAbsModuleCacheSize as v, getBPathCacheSize as w, getEnvHarvestConflictCollector as x, isBPathCapable as y, mergeHarvestUnderEnv as z };
281
+ export { type AbsGraphOptions as A, type BPathRunResult as B, locFromNode as C, mergeHarvestUnderEnv as D, type EnvHarvestConflict as E, resolveModule as F, setEnvHarvestConflictCollector as G, shouldAnalyzeFile as H, hasNudoDirectives as I, topoSortDirty as J, trimBPathCache as K, tryBPathCall as L, type MergeHarvestOptions as M, tryBPathCallFull as N, tryRunBPath as O, type AbsModuleCacheEntry as a, type AbsModuleGraphResult as b, type AbsModuleLoadIssue as c, type ModuleGraphCache as d, analyzeFile as e, analyzeFileAsync as f, buildModuleGraph as g, clearAbsModuleCache as h, clearBPathCache as i, collectAbsBindingsFromGraph as j, collectBPathReplacements as k, collectCallRecords as l, collectEnvGlobals as m, collectEnvModules as n, collectEnvNames as o, computeDirtySet as p, defaultAbsLoadModule as q, diagnosticsLevelForFile as r, evalAbsModuleGraph as s, evictAbsModuleCacheFiles as t, evictBPathCacheForFiles as u, filterDiagnosticsByLevel as v, getAbsModuleCacheSize as w, getBPathCacheSize as x, getEnvHarvestConflictCollector as y, isBPathCapable as z };
@@ -1,7 +1,8 @@
1
- export { a as AnalysisResult, B as BindingInfo, C as CaseHint, b as CaseResult, c as CompletionItem, d as Diagnostic, e as DiagnosticSeverity, f as DiagnosticTag, D as DirectiveCaseMode, F as FunctionAnalysis, R as ReferenceInfo, S as SourceLocation, g as SymbolInfo, h as SymbolTable } from './analyzer-types-JyJiCt8w.js';
2
- export { A as AbsGraphOptions, a as AbsModuleCacheEntry, b as AbsModuleGraphResult, c as AbsModuleLoadIssue, B as BPathRunResult, d as ModuleGraphCache, e as analyzeFile, f as analyzeFileAsync, g as buildModuleGraph, h as clearAbsModuleCache, i as clearBPathCache, j as collectAbsBindingsFromGraph, l as collectCallRecords, o as computeDirtySet, p as defaultAbsLoadModule, q as diagnosticsLevelForFile, r as evalAbsModuleGraph, s as evictAbsModuleCacheFiles, t as evictBPathCacheForFiles, u as filterDiagnosticsByLevel, v as getAbsModuleCacheSize, w as getBPathCacheSize, y as isBPathCapable, D as shouldAnalyzeFile, F as sourceHasNudoDirectives, G as topoSortDirty, H as trimBPathCache, I as tryBPathCall, J as tryBPathCallFull, K as tryRunBPath } from './analysis-CSXrOVlg.js';
1
+ export { A as AnalysisResult, B as BindingInfo, C as CaseHint, b as CaseResult, c as CompletionItem, d as Diagnostic, e as DiagnosticSeverity, f as DiagnosticTag, D as DirectiveCaseMode, F as FunctionAnalysis, R as ReferenceInfo, S as SourceLocation, g as SymbolInfo, h as SymbolTable } from './analyzer-types-CfBrPK9L.js';
2
+ export { A as AbsGraphOptions, a as AbsModuleCacheEntry, b as AbsModuleGraphResult, c as AbsModuleLoadIssue, B as BPathRunResult, d as ModuleGraphCache, e as analyzeFile, f as analyzeFileAsync, g as buildModuleGraph, h as clearAbsModuleCache, i as clearBPathCache, j as collectAbsBindingsFromGraph, l as collectCallRecords, o as collectEnvNames, p as computeDirtySet, q as defaultAbsLoadModule, r as diagnosticsLevelForFile, s as evalAbsModuleGraph, t as evictAbsModuleCacheFiles, u as evictBPathCacheForFiles, v as filterDiagnosticsByLevel, w as getAbsModuleCacheSize, x as getBPathCacheSize, z as isBPathCapable, C as locFromNode, F as resolveModule, H as shouldAnalyzeFile, I as sourceHasNudoDirectives, J as topoSortDirty, K as trimBPathCache, L as tryBPathCall, N as tryBPathCallFull, O as tryRunBPath } from './analysis-_DlXLC-L.js';
3
3
  export { C as CallRecord } from './call-record-CkyCFkT9.js';
4
4
  import '@babel/types';
5
5
  import '@nudojs/core';
6
+ import '@nudojs/parser';
6
7
  import '@nudojs/core/internal';
7
8
  import './config-DDB588oA.js';
package/dist/analysis.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  filterDiagnosticsByLevel,
4
4
  hasNudoDirectives,
5
5
  shouldAnalyzeFile
6
- } from "./chunk-LFZRUXGK.js";
6
+ } from "./chunk-EG6FYQLI.js";
7
7
  import {
8
8
  analyzeFile,
9
9
  analyzeFileAsync,
@@ -12,6 +12,7 @@ import {
12
12
  clearBPathCache,
13
13
  collectAbsBindingsFromGraph,
14
14
  collectCallRecords,
15
+ collectEnvNames,
15
16
  computeDirtySet,
16
17
  defaultAbsLoadModule,
17
18
  evalAbsModuleGraph,
@@ -20,15 +21,15 @@ import {
20
21
  getAbsModuleCacheSize,
21
22
  getBPathCacheSize,
22
23
  isBPathCapable,
24
+ locFromNode,
25
+ resolveModule,
23
26
  topoSortDirty,
24
27
  trimBPathCache,
25
28
  tryBPathCall,
26
29
  tryBPathCallFull,
27
30
  tryRunBPath
28
- } from "./chunk-QAFMPM2E.js";
29
- import "./chunk-U7JYTRIB.js";
30
- import "./chunk-YLALZXTZ.js";
31
- import "./chunk-PVJIMQRI.js";
31
+ } from "./chunk-NQXAF4LJ.js";
32
+ import "./chunk-ZXVAX7DE.js";
32
33
  export {
33
34
  analyzeFile,
34
35
  analyzeFileAsync,
@@ -37,6 +38,7 @@ export {
37
38
  clearBPathCache,
38
39
  collectAbsBindingsFromGraph,
39
40
  collectCallRecords,
41
+ collectEnvNames,
40
42
  computeDirtySet,
41
43
  defaultAbsLoadModule,
42
44
  diagnosticsLevelForFile,
@@ -47,6 +49,8 @@ export {
47
49
  getAbsModuleCacheSize,
48
50
  getBPathCacheSize,
49
51
  isBPathCapable,
52
+ locFromNode,
53
+ resolveModule,
50
54
  shouldAnalyzeFile,
51
55
  hasNudoDirectives as sourceHasNudoDirectives,
52
56
  topoSortDirty,
@@ -155,4 +155,4 @@ type AnalyzeLoadModule = (spec: string, fromFile: string) => string | undefined;
155
155
  */
156
156
  type DirectiveCaseMode = "none" | "all" | "selected";
157
157
 
158
- export type { AnalyzeLoadModule as A, BindingInfo as B, CaseHint as C, DirectiveCaseMode as D, FunctionAnalysis as F, ReferenceInfo as R, SourceLocation as S, AnalysisResult as a, CaseResult as b, CompletionItem as c, Diagnostic as d, DiagnosticSeverity as e, DiagnosticTag as f, SymbolInfo as g, SymbolTable as h };
158
+ export type { AnalysisResult as A, BindingInfo as B, CaseHint as C, DirectiveCaseMode as D, FunctionAnalysis as F, ReferenceInfo as R, SourceLocation as S, AnalyzeLoadModule as a, CaseResult as b, CompletionItem as c, Diagnostic as d, DiagnosticSeverity as e, DiagnosticTag as f, SymbolInfo as g, SymbolTable as h };
@@ -2,7 +2,7 @@ import {
2
2
  analysisConfig,
3
3
  findProjectConfig,
4
4
  matchesEmitAllowlist
5
- } from "./chunk-YLALZXTZ.js";
5
+ } from "./chunk-ZXVAX7DE.js";
6
6
 
7
7
  // src/target-path.ts
8
8
  function isNudoTargetPath(path) {
@@ -0,0 +1,235 @@
1
+ // src/what-if.ts
2
+ import { parse } from "@nudojs/parser";
3
+ function splitTopLevelUnion(expr) {
4
+ const members = [];
5
+ let start = 0;
6
+ let depth = 0;
7
+ for (let i = 0; i < expr.length; i++) {
8
+ const c = expr[i];
9
+ if (c === "(" || c === "[" || c === "{") depth++;
10
+ else if (c === ")" || c === "]" || c === "}") depth--;
11
+ else if (c === "|" && depth === 0) {
12
+ members.push(expr.slice(start, i));
13
+ start = i + 1;
14
+ }
15
+ }
16
+ members.push(expr.slice(start));
17
+ return members.map((m) => m.trim()).filter(Boolean);
18
+ }
19
+ function typeExprToDirective(expr) {
20
+ const members = splitTopLevelUnion(expr);
21
+ if (members.length === 0) return "any()";
22
+ const mapped = members.map((m) => {
23
+ if (m.startsWith("T.")) return "any()";
24
+ if (m === "number" || m === "string" || m === "boolean") return `${m}()`;
25
+ if (m === "unknown" || m === "any") return "any()";
26
+ if (m === "null" || m === "undefined" || m === "true" || m === "false") return m;
27
+ if (/^-?\d+(\.\d+)?$/.test(m)) return m;
28
+ if (/^["']/.test(m)) return m;
29
+ if (/[([{]|=>/.test(m) && !m.startsWith("T.")) return m;
30
+ if (/^(number|string|boolean|any|array|shape|lit|union|fn)\s*\(/.test(m)) return m;
31
+ return "any()";
32
+ });
33
+ return mapped.length === 1 ? mapped[0] : `union(${mapped.join(", ")})`;
34
+ }
35
+ function declaredNames(stmt, out) {
36
+ if (stmt.type === "FunctionDeclaration" && stmt.id) out.add(stmt.id.name);
37
+ else if (stmt.type === "ClassDeclaration" && stmt.id) out.add(stmt.id.name);
38
+ else if (stmt.type === "VariableDeclaration") {
39
+ for (const decl of stmt.declarations) {
40
+ if (decl.id?.type === "Identifier") out.add(decl.id.name);
41
+ }
42
+ } else if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
43
+ declaredNames(stmt.declaration, out);
44
+ }
45
+ }
46
+ function injectBindings(source, bindings) {
47
+ const applied = [];
48
+ const unapplied = [];
49
+ if (bindings.length === 0) return { source, applied, unapplied };
50
+ const ast = parse(source);
51
+ const declLines = /* @__PURE__ */ new Map();
52
+ for (const stmt of ast.program.body) {
53
+ const names = /* @__PURE__ */ new Set();
54
+ declaredNames(stmt, names);
55
+ if (names.size === 0 || !stmt.loc) continue;
56
+ const anchorLine = stmt.leadingComments?.[0]?.loc?.start.line ?? stmt.loc.start.line;
57
+ for (const name of names) {
58
+ if (!declLines.has(name)) declLines.set(name, anchorLine);
59
+ }
60
+ }
61
+ const byLine = /* @__PURE__ */ new Map();
62
+ for (const binding of bindings) {
63
+ const line = declLines.get(binding.name);
64
+ if (line === void 0) {
65
+ unapplied.push(binding.name);
66
+ continue;
67
+ }
68
+ const group = byLine.get(line) ?? [];
69
+ group.push(binding);
70
+ byLine.set(line, group);
71
+ applied.push(binding.name);
72
+ }
73
+ const insertions = [];
74
+ for (const [line, group] of byLine) {
75
+ insertions.push({
76
+ index: line - 1,
77
+ text: `// @nudo:as ${typeExprToDirective(group[0].type)}`
78
+ });
79
+ }
80
+ insertions.sort((a, b) => b.index - a.index);
81
+ const lines = source.split("\n");
82
+ for (const ins of insertions) lines.splice(ins.index, 0, ins.text);
83
+ return { source: lines.join("\n"), applied, unapplied };
84
+ }
85
+
86
+ // src/dep-contents.ts
87
+ import { loadModuleDepsFingerprint } from "@nudojs/core/internal";
88
+ function collectLoadDepContents(filePath, source, loadModule) {
89
+ const fp = loadModuleDepsFingerprint(source, loadModule, filePath);
90
+ const depContents = fp.contents.length > 0 ? fp.contents : fp.paths.map((path) => ({ path, content: null }));
91
+ return { depContents, truncated: fp.truncated };
92
+ }
93
+
94
+ // src/disk-cache.ts
95
+ import { createHash } from "crypto";
96
+ import { createRequire } from "module";
97
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, rmSync } from "fs";
98
+ import { join, dirname, relative, sep, isAbsolute } from "path";
99
+ function readServiceVersion() {
100
+ try {
101
+ const require2 = createRequire(import.meta.url);
102
+ for (const p of ["../package.json", "./package.json", "../../package.json"]) {
103
+ try {
104
+ const pkg = require2(p);
105
+ if (pkg?.name === "@nudojs/service" && pkg.version) return pkg.version;
106
+ if (pkg?.version && p.includes("service")) return pkg.version;
107
+ } catch {
108
+ }
109
+ }
110
+ } catch {
111
+ }
112
+ return "0";
113
+ }
114
+ var ANALYSIS_ABI = `nudo-check-cache-v3+${readServiceVersion()}`;
115
+ function sha256Hex(data) {
116
+ return createHash("sha256").update(data).digest("hex");
117
+ }
118
+ function relativizePath(p, root) {
119
+ const norm = p.split(sep).join("/");
120
+ if (!root) return norm;
121
+ const r = relative(root, p).split(sep).join("/");
122
+ if (!r.startsWith("..") && !isAbsolute(r)) return r;
123
+ return `ext:${createHash("sha256").update(norm).digest("hex").slice(0, 16)}`;
124
+ }
125
+ function sanitizeCacheNamespace(ns) {
126
+ return ns.replace(/[^a-z0-9_-]/gi, "_").replace(/^_+|_+$/g, "") || "cache";
127
+ }
128
+ var DiskCache = class {
129
+ root;
130
+ ns;
131
+ enabled = false;
132
+ constructor(opts) {
133
+ this.root = opts.root;
134
+ this.ns = sanitizeCacheNamespace(opts.namespace);
135
+ this.enabled = !!opts.root;
136
+ }
137
+ pathFor(key) {
138
+ if (!/^[a-f0-9]{16,128}$/i.test(key)) {
139
+ throw new Error("DiskCache key must be a hex digest");
140
+ }
141
+ return join(this.root, this.ns, key.slice(0, 2), `${key}.json`);
142
+ }
143
+ get(key) {
144
+ if (!this.enabled || !this.root) return void 0;
145
+ try {
146
+ const p = this.pathFor(key);
147
+ if (!existsSync(p)) return void 0;
148
+ const raw = readFileSync(p, "utf8");
149
+ const parsed = JSON.parse(raw);
150
+ if (parsed?.abi !== ANALYSIS_ABI) return void 0;
151
+ return parsed.value;
152
+ } catch {
153
+ return void 0;
154
+ }
155
+ }
156
+ set(key, value) {
157
+ if (!this.enabled || !this.root) return;
158
+ try {
159
+ const p = this.pathFor(key);
160
+ mkdirSync(dirname(p), { recursive: true });
161
+ writeFileSync(p, JSON.stringify({ abi: ANALYSIS_ABI, value }), "utf8");
162
+ } catch {
163
+ }
164
+ }
165
+ clearNamespace() {
166
+ if (!this.enabled || !this.root) return;
167
+ try {
168
+ rmSync(join(this.root, this.ns), { recursive: true, force: true });
169
+ } catch {
170
+ }
171
+ }
172
+ };
173
+ function checkCacheKey(filePath, source, opts) {
174
+ const rel = relativizePath(filePath, opts.projectDir);
175
+ const sidecarSha = opts.sidecarContent != null ? sha256Hex(opts.sidecarContent) : "nosidecar";
176
+ const depSeg = (opts.depContents ?? []).map(
177
+ (d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
178
+ ).join("\n");
179
+ const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
180
+ const cfgSeg = opts.analysisCfg ? `${opts.analysisCfg.mode ?? "-"}|${opts.analysisCfg.evalMissingSlot ?? "-"}|${opts.analysisCfg.callSiteBudget ?? "-"}|${opts.analysisCfg.entryThrows ?? "-"}|${opts.analysisCfg.ignoreThrows ?? "-"}` : "-";
181
+ return sha256Hex(
182
+ [
183
+ ANALYSIS_ABI,
184
+ rel,
185
+ opts.autoBind ? "ab1" : "ab0",
186
+ sha256Hex(source),
187
+ sidecarSha,
188
+ depSeg,
189
+ envSeg,
190
+ cfgSeg
191
+ ].join("\0")
192
+ );
193
+ }
194
+ function ifaceCacheKey(filePath, source, opts) {
195
+ const rel = relativizePath(filePath, opts.projectDir);
196
+ const sidecarSeg = opts.autoBind && opts.sidecarSource !== void 0 ? `sc:${sha256Hex(opts.sidecarSource)}` : "sc0";
197
+ const depSeg = (opts.depContents ?? []).map(
198
+ (d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
199
+ ).join("\n");
200
+ const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
201
+ return sha256Hex(
202
+ [
203
+ ANALYSIS_ABI,
204
+ "iface",
205
+ rel,
206
+ opts.autoBind ? "ab1" : "ab0",
207
+ sha256Hex(source),
208
+ sidecarSeg,
209
+ depSeg,
210
+ envSeg
211
+ ].join("\0")
212
+ );
213
+ }
214
+ function extractNudoImportSpecs(source) {
215
+ const specs = /* @__PURE__ */ new Set();
216
+ const named = /@nudo:import\s*\{[^}]*\}\s*from\s*["']([^"']+)["']/g;
217
+ const ns = /@nudo:import\s+\*\s+as\s+\w+\s+from\s*["']([^"']+)["']/g;
218
+ let m;
219
+ while (m = named.exec(source)) specs.add(m[1]);
220
+ while (m = ns.exec(source)) specs.add(m[1]);
221
+ return [...specs];
222
+ }
223
+
224
+ export {
225
+ typeExprToDirective,
226
+ injectBindings,
227
+ collectLoadDepContents,
228
+ ANALYSIS_ABI,
229
+ sha256Hex,
230
+ relativizePath,
231
+ DiskCache,
232
+ checkCacheKey,
233
+ ifaceCacheKey,
234
+ extractNudoImportSpecs
235
+ };