@nudojs/service 4.0.0 → 5.0.0-beta.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.d.ts CHANGED
@@ -1,200 +1,22 @@
1
- import { Node, File } from '@babel/types';
2
- import { FormalParam, Abs, InterfaceSource, InterfaceTierOpts, PolyFn, AbsModuleExports, AstEnv, Phi, BMemberDiag, BCallRecord, TranspiledCallResult, NudoConstraint } from '@nudojs/core';
3
- export { AbsInlay, BMemberDiag, collectAbsInlays } from '@nudojs/core';
4
- import { C as CallRecord, D as DiagnosticsLevel, A as AnalysisConfig } from './config-Cqj8zeZH.js';
5
- export { a as AnalysisMode, b as DEFAULT_ANALYSIS_MODE, I as InterfaceConfig, N as NudoConfig, c as analysisConfig, d as clearPathEnvCaches, e as diskCacheRoot, f as findProjectConfig, i as interfaceConfig, m as matchesEmitAllowlist } from './config-Cqj8zeZH.js';
6
- import { HarvestedEnv } from '@nudojs/harvester';
7
- import { FunctionWithDirectives } from '@nudojs/parser';
1
+ import { A as AnalyzeLoadModule, D as DirectiveCaseMode, a as AnalysisResult } from './analyzer-types-JyJiCt8w.js';
2
+ export { B as BindingInfo, C as CaseHint, b as CaseResult, c as CompletionItem, d as Diagnostic, e as DiagnosticSeverity, f as DiagnosticTag, F as FunctionAnalysis, R as ReferenceInfo, S as SourceLocation, g as SymbolInfo, h as SymbolTable } from './analyzer-types-JyJiCt8w.js';
3
+ export { A as AbsGraphOptions, a as AbsModuleCacheEntry, b as AbsModuleGraphResult, c as AbsModuleLoadIssue, B as BPathRunResult, E as EnvHarvestConflict, M as MergeHarvestOptions, d as ModuleGraphCache, e as analyzeFile, f as analyzeFileAsync, g as buildModuleGraph, h as clearAbsModuleCache, i as clearBPathCache, j as collectAbsBindingsFromGraph, k as collectBPathReplacements, l as collectCallRecords, m as collectEnvGlobals, n as collectEnvModules, 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, x as getEnvHarvestConflictCollector, y as isBPathCapable, z as mergeHarvestUnderEnv, C as setEnvHarvestConflictCollector, 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';
4
+ import { C as CallRecord } from './call-record-CkyCFkT9.js';
5
+ export { c as clearPathEnvCaches, g as getPathEnvCacheSizes } from './env-loader-fj0TSCcA.js';
6
+ import { N as NudoConfig } from './config-DDB588oA.js';
7
+ export { A as AnalysisConfig, a as AnalysisMode, C as CheckConfig, e as DEFAULT_ANALYSIS_MODE, D as DiagnosticsLevel, I as InterfaceConfig, b as analysisConfig, g as applyBForkBudgetFromConfig, c as checkConfig, h as currentBForkBudgetLimit, d as diskCacheRoot, f as findProjectConfig, i as interfaceConfig, m as matchesEmitAllowlist } from './config-DDB588oA.js';
8
+ import { File, Node } from '@babel/types';
9
+ import { PolyFn, Abs, AbsModuleExports } from '@nudojs/core';
10
+ import { L as LoadModule } from './interface-BGlP7aIk.js';
11
+ export { C as ConstraintSourceExpr, D as DerivedExport, a as DerivedParam, b as DraftEvidence, E as EmitDerivedResult, c as EmitInterfaceOpts, d as EmitInterfaceResult, e as EmitInterfaceSkipReason, I as InterfaceDraftEntry, f as InterfaceDraftOpts, g as InterfaceDraftResult, h as InterfaceSurfaceEntry, i as InterfaceSurfaceOpts, R as RootDeriveOpts, j as RootDeriveResult, T as TypeBinding, W as WriteDraftResult, k as collectParamBodyAccesses, l as defaultLoadModule, m as deriveFromRoot, n as draftInterface, o as emitDerivedFromRoot, p as emitInterface, q as extractFnConstraintSources, r as formatDerivedSection, s as formatDraftModule, t as formatDraftSummary, u as formatEmitSummary, v as formatInterfaceSurfaceLine, w as injectBindings, x as interfaceSurface, y as isDraftableEntry, z as sidecarDraftPath, A as typeExprToDirective, B as writeInterfaceDraft } from './interface-BGlP7aIk.js';
12
+ import { FunctionWithDirectives, FileDirective } from '@nudojs/parser';
13
+ export { SchemaDialect, SchemaNode, SchemaProjection, SchemaRefinement, StandardSchemaIssue, StandardSchemaModuleProjection, StandardSchemaResult, absToSchemaNode, absToSchemaSource, absToStandardSchema, absToStandardSchemaModule, absToTSType, constraintToSchemaNode, generateDts, generateFunctionDtsLines, generateGuardFunction, generateGuardFunctionFromAbs, projectAbsToSchema, schemaNodeToZod, validateSchemaNode } from './dts.js';
14
+ export { CaseJson, CaseJsonCase, CaseJsonFunction, EmitResult, EmitSkipReason, buildCaseDirective, insertGeneratedCaseDirectives, serializeCaseArg, serializeCaseJson, stripGeneratedCaseDirectives, unifiedDiff } from './case.js';
15
+ export { BuildSemanticTokensOpts, CaseInfo, HoverInfo, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES, SemanticToken, buildSemanticTokens, encodeSemanticTokens, getAbsAtPosition, getAbsAtPositionAsync, getCasesForFile, getCompletionsAtPosition, getHoverAtPosition, getTypeAtPosition, getTypeAtPositionAsync, interfaceTierModifierBit } from './lsp.js';
16
+ export { AbsInlay, BMemberDiag, collectAbsInlays } from '@nudojs/core/internal';
17
+ export { HARVEST_NODE_DEFAULT_MAX_FILES, HARVEST_NODE_DEFAULT_MAX_MS, HarvestJson, HarvestNodeStats, HarvestSig, NodeEnvResult, PackageHarvest, absToHarvestSig, autoHarvestModules, barePackageName, bareSpecToAbsModules, clearHarvestCache, clearNodeHarvestCache, collectBarePackages, collectDtsFromEntry, depsCacheRoot, dtsClosureHash, formatHarvestSummary, getHarvestCacheSize, getNodeHarvestCacheSize, handwrittenNodeEnv, harvestCacheKey, harvestNodeTypes, harvestPackage, harvestPackageCached, harvestPackageWithDisk, harvestSigToAbs, harvestToAbsModules, harvestedValueToAbs, isHarvestNodeDisabled, loadHarvestEnvFromDisk, lookupHarvested, materializeHarvestJson, packageHarvestToAbsModules, readHarvestDisk, resolvePackageRoot, serializeHarvestJson, summarizeNodeEnv, writeHarvestDisk } from './harvest.js';
18
+ import '@nudojs/harvester';
8
19
 
9
- /**
10
- * Per-function FunctionAnalysis cache (body-edit: recompute only dirty fns).
11
- * Cleared together with B-path / whole-file analysis caches.
12
- */
13
-
14
- declare function clearFnAnalysisCache(): void;
15
- /**
16
- * Dependency content changed: drop every per-fn entry for these entry files.
17
- * Keys are `filePath\0...`, so a prefix scan is sound and cheap at LRU size.
18
- */
19
- declare function evictFnAnalysisCacheForFiles(files: string[]): number;
20
-
21
- type SourceLocation = {
22
- start: {
23
- line: number;
24
- column: number;
25
- };
26
- end: {
27
- line: number;
28
- column: number;
29
- };
30
- };
31
- type DiagnosticSeverity = "error" | "warning" | "info";
32
- type DiagnosticTag = "unnecessary";
33
- type Diagnostic = {
34
- range: SourceLocation;
35
- severity: DiagnosticSeverity;
36
- message: string;
37
- tags?: DiagnosticTag[];
38
- code?: string;
39
- suggestions?: string[];
40
- data?: unknown;
41
- /** provenance of the receiver value (callsite argument that flowed into the error) */
42
- origin?: {
43
- line: number;
44
- column: number;
45
- };
46
- };
47
- type CaseResult = {
48
- name: string;
49
- /** 无损参数 Abs */
50
- argAbs: Abs[];
51
- /** 无损结果 Abs */
52
- abs: Abs;
53
- /** 无损抛出 Abs(未抛为 never) */
54
- throwsAbs: Abs;
55
- throwLoc?: SourceLocation;
56
- source?: "directive" | "callsite";
57
- /** `@nudo:case "name" (…) => expected` — presence means the case is a test assertion */
58
- expected?: Abs;
59
- /** number of additional call sites folded into a symbolic case */
60
- aggregatedFrom?: number;
61
- /**
62
- * 内涵摘要(代数 generalize):无损 Abs + term/pred/conf。
63
- */
64
- intension?: {
65
- display?: string;
66
- term?: string;
67
- pred?: string;
68
- conf?: string;
69
- /** 无损 Abs 单行(formatAbs) */
70
- abs?: string;
71
- /** 无损 Abs 多行(formatAbsMultiline) */
72
- absMultiline?: string;
73
- };
74
- };
75
- type FunctionAnalysis = {
76
- name: string;
77
- loc: SourceLocation;
78
- paramNames: string[];
79
- /** C4.1 形参表面:draft/契约对齐(解构 placeholder + bound 名) */
80
- formals?: FormalParam[];
81
- cases: CaseResult[];
82
- /** cases 结果 Abs 的 join;dts 返回位源 */
83
- combinedAbs?: Abs;
84
- entryOnly?: boolean;
85
- skipped?: boolean;
86
- /**
87
- * True for functions collected from CJS-style bindings/assignments
88
- * (`exports.X = fn`, `module.exports = fn`, `const f = fn`): their name has
89
- * no declaration-level stability, so .d.ts generation skips them while
90
- * infer/JSON output still reports them.
91
- */
92
- noDeclaration?: boolean;
93
- /** absolute path of the module this function is imported from (externalFunctions only) */
94
- fromModule?: string;
95
- /**
96
- * HOF / entry shape 关系快照(generalizeFromAst),供 dts 泛型投影(C3.3)。
97
- * 只存投影所需的 Abs,不挂 PolyFn 本体(缓存可克隆)。
98
- */
99
- hof?: {
100
- /** 函数形参:外延关系 Abs(fn 形状,paramTypes/returnType) */
101
- fnRels?: Array<{
102
- param: string;
103
- abs: Abs;
104
- }>;
105
- /** 值形参提升快照(如 items → arr(A1)) */
106
- entryShapes?: Array<{
107
- param: string;
108
- abs: Abs;
109
- }>;
110
- /** 符号返回 Abs(含自由 α / B:param) */
111
- symbolic?: Abs;
112
- };
113
- };
114
- type BindingInfo = {
115
- /** 无损 Abs */
116
- abs: Abs;
117
- loc?: SourceLocation;
118
- };
119
- type CaseHint = {
120
- line: number;
121
- label: string;
122
- ok: boolean;
123
- };
124
- type AnalysisResult = {
125
- functions: FunctionAnalysis[];
126
- diagnostics: Diagnostic[];
127
- bindings: Map<string, BindingInfo>;
128
- /**
129
- * Node identity is from the analysis-time parse. A later re-parse of the
130
- * same source returns a *different* File (new Nodes) if the AST LRU evicted
131
- * the original — lookups must use the File that produced this result, not a
132
- * freshly parsed one. getTypeAtPosition rebuilds its own map and is unaffected.
133
- */
134
- /** 无损节点 Abs */
135
- nodeAbsMap: Map<Node, Abs>;
136
- caseHints: CaseHint[];
137
- /** functions imported from other modules, synthesized from cross-file call sites observed while analyzing this file */
138
- externalFunctions?: FunctionAnalysis[];
139
- };
140
- type CompletionItem = {
141
- label: string;
142
- kind: "property" | "method" | "variable";
143
- detail?: string;
144
- };
145
- type SymbolInfo = {
146
- name: string;
147
- kind: "function" | "variable" | "class" | "parameter";
148
- loc: SourceLocation;
149
- uri?: string;
150
- };
151
- type ReferenceInfo = {
152
- name: string;
153
- loc: SourceLocation;
154
- uri?: string;
155
- };
156
- type SymbolTable = {
157
- definitions: Map<string, SymbolInfo>;
158
- references: ReferenceInfo[];
159
- };
160
- /** mtime 边缓存:key 为文件路径,edges 为已抽取的相对 import 边(与 buildModuleGraph 返回语义一致)。 */
161
- type ModuleGraphCache = Map<string, {
162
- mtimeMs: number;
163
- size: number;
164
- edges: string[];
165
- }>;
166
- /** Statically extract each file's relative import edges (extension resolution identical to CLI resolveModule: ''/'.js'/'.ts'/'.mjs'; bare npm specifiers skipped). */
167
- declare function buildModuleGraph(files: string[], cache?: ModuleGraphCache): {
168
- imports: Map<string, Set<string>>;
169
- dependents: Map<string, Set<string>>;
170
- };
171
- /** changed plus its transitive dependents (reverse-edge BFS); cycle-safe via visited. */
172
- declare function computeDirtySet(dependents: Map<string, Set<string>>, changedFile: string): string[];
173
- /** Topological order with dependencies before dependents (only imports edges internal to dirty; cycles tolerated — remaining files appended in arbitrary order). */
174
- declare function topoSortDirty(imports: Map<string, Set<string>>, dirty: string[]): string[];
175
- /** Optional module loader for analysis (sidecar / relative imports). */
176
- type AnalyzeLoadModule = (spec: string, fromFile: string) => string | undefined;
177
- /**
178
- * Async entry to analyzeFile: preloads path-based env files
179
- * (`/// @nudo:env ./nudo-harvest-node.ts`) via dynamic import — impossible
180
- * synchronously in ESM — then runs the sync analysis, which picks the
181
- * preloaded factories up from the env-loader cache.
182
- * `loadModule` is optional; when provided it is used for relative imports /
183
- * sidecar ambient bindings (LSP buffer-aware path). Default remains
184
- * `defaultLoadModule` (disk).
185
- */
186
- declare function analyzeFileAsync(filePath: string, source: string, activeCases?: Map<string, number>, externalCallRecords?: CallRecord[], loadModule?: AnalyzeLoadModule): Promise<AnalysisResult>;
187
- /**
188
- * 调用点发现(阶段一):在"使用现场"文件(测试 / 上层应用)中求值
189
- * 顶层代码,收集它对(外部模块导出的)函数的调用记录。每条记录带
190
- * 真实的实参类型与结果类型——后续 analyzeFile 将其注入合成 case,
191
- * 使被使用方从 entry-only(参数全 unknown)升级为真实调用形态。
192
- *
193
- * Abs 路径(TypeValue evaluateProgram 已删):evalProgramAbs + AbsCallRecord。
194
- * 只做求值与记录,不产出诊断;求值异常不抛出(使用现场文件可能
195
- * 依赖未 mock 的全局,收集不到就收集不到,不能拖垮主分析)。
196
- */
197
- declare function collectCallRecords(filePath: string, source: string): CallRecord[];
198
20
  declare function analysisFileCacheKey(filePath: string, source: string, activeCases?: Map<string, number>, externalCallRecords?: CallRecord[], analysisCfg?: {
199
21
  mode: string;
200
22
  evalMissingSlot: string;
@@ -202,222 +24,28 @@ declare function analysisFileCacheKey(filePath: string, source: string, activeCa
202
24
  diagnostics: string;
203
25
  }, loadModule?: AnalyzeLoadModule, projectEnvNames?: string[],
204
26
  /** ambient 侧车绑定:变更必须 miss(dep 指纹故意不编码 autoBind) */
205
- autoBind?: boolean): {
27
+ autoBind?: boolean, caseMode?: DirectiveCaseMode): {
206
28
  filePath: string;
207
29
  source: string;
208
30
  auxKey: string;
209
31
  noCache?: boolean;
210
32
  };
211
- /**
212
- * 整文件分析。同 (path, source, cases, external) 命中 memo → O(1)。
213
- * 不再每次 clearBPathCache:B 路径按本文件 source 键控。
214
- *
215
- * `loadModule`:可选;提供时用于相对 import / 侧车 ambient(LSP
216
- * buffer-aware)。未提供时走 defaultLoadModule(磁盘)。
217
- *
218
- * 宿主契约:入口 source 未变但依赖模块内容变了时,必须调用
219
- * `evictBPathCacheForFiles` / `evictAnalysisFileCacheForFiles` /
220
- * `evictFnAnalysisCacheForFiles`(LSP 已接好)。非 LSP 宿主
221
- * (CLI watch / vite-plugin)在 dep 变更时应 `clearBPathCache()` 或上述逐出。
222
- */
223
- declare function analyzeFile(filePath: string, source: string, activeCases?: Map<string, number>, externalCallRecords?: CallRecord[], loadModule?: AnalyzeLoadModule): AnalysisResult;
224
-
225
- type CaseInfo = {
226
- functionName: string;
227
- caseName: string;
228
- caseIndex: number;
229
- };
230
- declare function getCasesForFile(filePath: string, source: string): {
231
- functionName: string;
232
- cases: {
233
- name: string;
234
- index: number;
235
- }[];
236
- loc: SourceLocation;
237
- }[];
238
- /** Async entry to getTypeAtPosition with path-env preloading (see analyzeFileAsync). */
239
- declare function getTypeAtPositionAsync(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>): Promise<Abs | null>;
240
- /** Async entry to getAbsAtPosition(与 getTypeAtPositionAsync 同预加载口径) */
241
- declare function getAbsAtPositionAsync(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>): Promise<Abs | null>;
242
- /**
243
- * 光标处无损 Abs。B-path 节点表优先;用例函数体走 Abs 重放。
244
- */
245
- declare function getAbsAtPosition(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>): Abs | null;
246
- /** 光标处类型(Abs)。B-path 节点表优先;用例函数体走 Abs 重放。 */
247
- declare function getTypeAtPosition(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>): Abs | null;
248
- type HoverInfo = {
249
- /** 外延展示(formatShape / formatAbs) */
250
- typeText: string;
251
- /** 内涵签名(代数 generalize) */
252
- intension?: string;
253
- /** 无损 Abs 单行展示(shape / term / pred / conf) */
254
- abs?: string;
255
- /** 无损 Abs 多行展示 */
256
- absMultiline?: string;
257
- /** CodeLens `● interface` 同源档位(A7);仅本地 named export */
258
- interfaceSource?: InterfaceSource;
259
- /** 有效契约展示(handwritten/generated);implicit 为 undefined */
260
- interfaceDisplay?: string;
261
- };
262
- /** A7:hover/inlay 与 CodeLens interface 档同源(design-refine-derivation §8) */
263
- type HoverInterfaceOpts = InterfaceTierOpts;
264
- /**
265
- * LSP hover:优先无损 Abs(类型即计算本体)。
266
- * 节点表也是 Abs(collectAbsNodeTypes),不经 bridge。
267
- *
268
- * 函数名/调用 callee 位置(design-hof-relations §7):
269
- * intension 一律走 generalize/formatPoly(HOF fnRels 在这里);
270
- * typeText 仍落 B-path Abs(调用点显示结果类型,不是函数签名)。
271
- * 禁止用 B-path 的 arity-only fn Abs 冒充权威关系源。
272
- *
273
- * A7 default 档:函数名 hover 附带 interfaceTierOf 来源 + 契约展示,
274
- * 与 CodeLens `● interface / <source>` 同源;选 case 时 body 仍走
275
- * activeCases 重放,interface 档标注不变。
276
- */
277
- declare function getHoverAtPosition(filePath: string, source: string, line: number, column: number, activeCases?: Map<string, number>, opts?: HoverInterfaceOpts): HoverInfo | null;
278
- declare function getCompletionsAtPosition(filePath: string, source: string, line: number, column: number): CompletionItem[];
279
-
280
- /**
281
- * `nudo infer --json` 稳定契约 v1(CI / Agent)。
282
- *
283
- * 字段只增不改语义:
284
- * - args / result / throws / combined:formatShape 外延投影(有损,兼容)
285
- * - intension.abs*:无损 Abs 展示(主线)
286
- * - argsAbs / resultAbs:CaseResult 上的无损 Abs(formatAbs);有则补齐
287
- */
288
-
289
- type InferJsonCase = {
290
- name: string;
291
- /** TypeValue 投影(有损外延) */
292
- args: string[];
293
- result: string;
294
- throws: string | null;
295
- source: string | null;
296
- aggregatedFrom?: number;
297
- /** 无损参数 Abs 展示(与 args 对齐;缺失位省略) */
298
- argsAbs?: string[];
299
- /** 无损结果 Abs 展示 */
300
- resultAbs?: string;
301
- /** 无损内涵(Abs);无则省略 */
302
- intension?: {
303
- display?: string;
304
- abs?: string;
305
- absMultiline?: string;
306
- term?: string;
307
- pred?: string;
308
- conf?: string;
309
- };
310
- };
311
- type InferJsonFunction = {
312
- name: string;
313
- loc: SourceLocation;
314
- entryOnly: boolean;
315
- noDeclaration?: boolean;
316
- cases: InferJsonCase[];
317
- combined?: string;
318
- /** 无损 combined Abs 展示 */
319
- combinedAbs?: string;
320
- };
321
- type InferJson = {
322
- version: 1;
323
- file: string;
324
- summary: {
325
- functions: number;
326
- externalFunctions: number;
327
- cases: number;
328
- diagnostics: number;
329
- };
330
- functions: InferJsonFunction[];
331
- externalFunctions?: Array<{
332
- name: string;
333
- fromModule?: string;
334
- cases: InferJsonCase[];
335
- }>;
336
- diagnostics: Array<{
337
- range: SourceLocation;
338
- severity: string;
339
- message: string;
340
- code?: string;
341
- suggestions?: string[];
342
- tags?: string[];
343
- origin?: {
344
- line: number;
345
- column: number;
346
- };
347
- }>;
348
- };
349
- declare function serializeInferJson(result: AnalysisResult, file: string): InferJson;
350
-
351
- /**
352
- * nudo 推断目标文件判定(纯扩展名规则,路径无需存在)。
353
- *
354
- * 规则:.js / .mjs / .ts 可作为推断目标;其余扩展(.cjs/.tsx/.jsx/.mts/.d.ts…)
355
- * 一律 false。显式排除:
356
- * - .d.ts —— 类型声明文件(harvester 的输入),不是可求值的实现源码;
357
- * - .tsx / .jsx —— JSX 构造的求值超出 nudo 推断器范围;
358
- * - *.nudo.js / *.nudo.mjs / *.nudo.ts —— 侧车契约模块(interface 绑定源),
359
- * 不是源码推断目标;目录级 check/infer/doctor 展开不得把它们当实现分析;
360
- * - *.nudo.draft.js / *.nudo.draft.mjs / *.nudo.draft.ts —— draft 产物,
361
- * 明确不被 ambient 绑定,也不得当实现源码分析。
362
- *
363
- * 消费方:CLI 的 collectNudoFiles/watch 过滤/doctor 目录展开(.ts 放开后统一
364
- * 走本函数,避免各处手写 endsWith 漂移),以及 LSP 的 isNudoFile(接线由
365
- * LSP 侧负责)。
366
- */
367
- declare function isNudoTargetPath(path: string): boolean;
368
-
369
- /** Formal sidecar contracts only — drafts never ambient-bind and need not reanalyze */
370
- declare function isSidecarPath(path: string): boolean;
371
- declare function isProjectConfigPath(path: string): boolean;
372
- /** Watch accept gate: analysis targets + sidecar/config/env-template invalidators */
373
- declare function isWatchRelevantPath(path: string): boolean;
374
- /** `lib.nudo.js|ts` → candidate ambient sources next to it */
375
- declare function ambientSourcesOfSidecar(sidecarPath: string): string[];
376
-
377
- /** 源码里的 path-based load specs 解析为绝对路径后登记反向边 */
378
- declare function noteEnvPathDeps(sourcePath: string, source: string): void;
379
- /** 依赖该 env 模板的源文件列表 */
380
- declare function envPathDependents(envPath: string): string[];
381
- declare function clearEnvPathDeps(): void;
382
- /** watch 门禁:env 模板变更必须可被接收(即便扩展名不进 isNudoTargetPath) */
383
- declare function isEnvTemplatePath(path: string): boolean;
384
-
385
- /**
386
- * 分析范围判定(A1/A2,design-analysis-scope.md)。
387
- * CLI 显式路径不受 mode 限制;本模块供 LSP/watch 自动验证使用。
388
- */
389
33
 
390
34
  /**
391
- * 按 analysis.diagnostics 档过滤 evaluator/check **显示路径**诊断。
392
- * - off:显示层全静音(与 errors 档区分)。check 门禁(CLI `nudo check` /
393
- * checkSource)独立于本过滤,不受 off 影响。
394
- * - errors:只发 severity=error
395
- * - default:error + warning(静音 NOISY_WARNING_CODES)
396
- * - verbose:全量
397
- */
398
- declare function filterDiagnosticsByLevel<T extends {
399
- severity: string;
400
- code?: string;
401
- }>(diags: T[], level: DiagnosticsLevel): T[];
402
- declare function diagnosticsLevelForFile(filePath: string): DiagnosticsLevel;
403
- declare function hasNudoDirectives(source: string): boolean;
404
- /**
405
- * 是否应对该文件跑分析(自动路径,如 LSP validate)。
406
- * - 非 target 路径 → false
407
- * - exclude 命中 / include 未命中 → false
408
- * - mode=directives → 仅有指令
409
- * - mode=exports → 指令 | export | 同名侧车
410
- * - mode=all → true
35
+ * Per-function FunctionAnalysis cache (body-edit: recompute only dirty fns).
36
+ * Cleared together with B-path / whole-file analysis caches.
411
37
  */
412
- declare function shouldAnalyzeFile(filePath: string, source: string | undefined, config?: AnalysisConfig): boolean;
413
38
 
39
+ /** 测试/诊断:当前条目数(≤ getSessionCacheLimits().maxFns) */
40
+ declare function getFnAnalysisCacheSize(): number;
41
+ declare function clearFnAnalysisCache(): void;
42
+ /** 立刻压到当前 maxFns(调低上限时收内存) */
43
+ declare function trimFnAnalysisCache(): void;
414
44
  /**
415
- * 相对/绝对 specifier → 模块源码。
416
- * CLI / LSP / vite / check 共用同一扩展名表,避免门禁结果分叉。
45
+ * Dependency content changed: drop every per-fn entry for these entry files.
46
+ * Keys are `filePath\0...`, so a prefix scan is sound and cheap at LRU size.
417
47
  */
418
- type LoadModule = (spec: string, fromFile: string) => string | undefined;
419
- /** 默认 loadModule:支持 .js/.mjs/.ts 与 index 入口 */
420
- declare function defaultLoadModule(spec: string, fromFile: string): string | undefined;
48
+ declare function evictFnAnalysisCacheForFiles(files: string[]): number;
421
49
 
422
50
  /**
423
51
  * Host 侧多文件解析(非代数层)。
@@ -448,247 +76,205 @@ declare function analyzeExportsFromSource(filePath: string, source: string): Mod
448
76
  declare function collectStaticImports(entryFile: string, maxDepth?: number): Map<string, ModuleExports>;
449
77
 
450
78
  /**
451
- * Host:从 npm 包 / @types 收集 .d.ts → harvestDts。
452
- * 代数不碰 fs;这里只做「找到 dts 路径」这一层宿主职责。
453
- */
454
-
455
- /** 在 node_modules 中解析包根(含 @types/*) */
456
- declare function resolvePackageRoot(pkg: string, fromDir?: string): string | undefined;
457
- type PackageHarvest = {
458
- pkg: string;
459
- root: string;
460
- dtsFiles: string[];
461
- env: HarvestedEnv;
79
+ * Dual-entry (browser / node) observation signal.
80
+ *
81
+ * Call-site records are file-scoped: when a package ships separate browser and
82
+ * node entrypoints, records collected against one variant do **not** inject
83
+ * into analysis of the other (design limitation, not a bug). This module makes
84
+ * that ceiling visible: when analysis runs on one entry variant of a dual-entry
85
+ * package, emit `nudo:dual-entry` once so the user is not left thinking both
86
+ * entries were covered.
87
+ *
88
+ * Zero-FP discipline: fires only when package.json really declares two faces
89
+ * (browser + node/default) that resolve to **different** files, and the file
90
+ * being analyzed is one of those entry targets. Single-entry packages never
91
+ * fire.
92
+ */
93
+ type DualEntryInfo = {
94
+ pkgPath: string;
95
+ pkgDir: string;
96
+ pkgName?: string;
97
+ kind: "exports-conditions" | "browser-field";
98
+ /** resolved absolute paths of the browser face */
99
+ browserPaths: string[];
100
+ /** resolved absolute paths of the node/default face */
101
+ nodePaths: string[];
102
+ /** which face the analyzed file belongs to */
103
+ role: "browser" | "node";
104
+ /** display targets (as declared, relative) for the message */
105
+ browserTargets: string[];
106
+ nodeTargets: string[];
107
+ };
108
+ type DualFaces = {
109
+ kind: "exports-conditions" | "browser-field";
110
+ browser: string[];
111
+ node: string[];
112
+ };
113
+ /**
114
+ * Detect browser/node dual faces in a parsed package.json.
115
+ * Returns null unless both faces exist and differ (zero-FP on single-entry).
116
+ */
117
+ declare function detectDualEntryFromPackageJson(pkg: unknown): DualFaces | null;
118
+ /** Nearest package.json walking up from the file's directory. */
119
+ declare function findOwningPackage(fromFile: string): {
120
+ path: string;
121
+ dir: string;
122
+ pkg: Record<string, unknown>;
123
+ } | null;
124
+ /**
125
+ * Dual-entry info for an analyzed file: the owning package must declare two
126
+ * differing faces **and** this file must be one of the entry targets.
127
+ * Returns null otherwise (single-entry packages, shared helpers, …).
128
+ */
129
+ declare function dualEntryForFile(filePath: string): DualEntryInfo | null;
130
+ /** Host-facing info issue (CLI check / JSON) for one analyzed entry variant. */
131
+ type DualEntryIssue = {
132
+ severity: "info";
133
+ code: "nudo:dual-entry";
134
+ message: string;
135
+ suggestion: string;
136
+ line: number;
137
+ column: number;
462
138
  };
139
+ declare function dualEntryIssueForFile(filePath: string): DualEntryIssue | null;
140
+
463
141
  /**
464
- * 从入口 .d.ts 沿 reference path / 相对 import 做 BFS 收集。
465
- * 适合 @types 包(三斜线引用图);比整树 walk 更贴声明入口。
142
+ * `@nudo:skip [returnsExpr]` 采集(host 侧)。
143
+ *
144
+ * core 的 `checkSource` 不依赖 parser,所以 skip 指令由 host(CLI / LSP /
145
+ * vite-plugin)解析后经 `CheckOptions.skips` 下传——保证 check 面与 test 面
146
+ * 对 skip 的口径一致:不评估 body、退出 L1/L2 门禁。
466
147
  */
467
- declare function collectDtsFromEntry(entry: string, maxFiles?: number): string[];
468
- /** harvest 一个 npm 包(或 @types 包) */
469
- declare function harvestPackage(pkg: string, fromDir?: string, maxFiles?: number): PackageHarvest | {
470
- error: string;
471
- };
472
- /** harvest 结果 → 人类可读导出表 */
473
- declare function formatHarvestSummary(h: PackageHarvest): string;
474
- /** 取某个导出的展示串(formatShape) */
475
- declare function lookupHarvested(h: PackageHarvest, moduleName: string, exportName: string): string | undefined;
476
148
 
477
149
  /**
478
- * harvest 自动化:源码裸 import → @types/package → env modules。
479
- * 手动 `nudo harvest` 仍保留;这里是分析路径上的按需注入。
150
+ * 每个带 `@nudo:skip` 的顶层函数 → 声明的返回 Abs;`null` = 未声明返回类型。
151
+ * 解析失败(语法错误等)→ 空表(check 退回默认路径,不静默吞函数)。
480
152
  */
153
+ declare function collectSkipReturns(source: string): Map<string, Abs | null>;
481
154
 
482
- /** 裸说明符 → 包名(含 scope);相对/绝对/node: 与裸 Node 内建返回 undefined */
483
- declare function barePackageName(spec: string): string | undefined;
484
- declare function collectBarePackages(source: string): string[];
485
- declare function harvestPackageCached(pkg: string, fromDir: string): PackageHarvest | null;
486
- declare function clearHarvestCache(): void;
487
155
  /**
488
- * 自动 harvest 源码中全部裸包(有 @types 或包内 dts 时)。
489
- * 返回可注入 setEnvModules 的 modules 表;无包可 harvest 时为空对象。
156
+ * check/iface 磁盘缓存键的依赖内容采集。
157
+ * 走 core `loadModuleDepsFingerprint`:覆盖 ESM from / require / dynamic import /
158
+ * `@nudo:import` / ambient 侧车闭包——不只 `@nudo:import` 注释。
159
+ *
160
+ * 内容必须优先取 loadModule 解析结果(fingerprint.contents),不得回读磁盘:
161
+ * LSP buffer-aware loadModule 与自定义 loader 的内容可能与磁盘不一致,
162
+ * 回读会让缓存键分叉(buffer 编辑后仍命中磁盘旧内容)。
490
163
  */
491
- declare function autoHarvestModules(source: string, fromDir: string): Record<string, Record<string, Abs>>;
164
+ type DepContent = {
165
+ path: string;
166
+ content: string | null;
167
+ };
168
+ declare function collectLoadDepContents(filePath: string, source: string, loadModule: (spec: string, fromFile: string) => string | undefined): {
169
+ depContents: DepContent[];
170
+ truncated: boolean;
171
+ };
492
172
 
493
173
  /**
494
- * Host:import 图(相对 + 裸包 harvest)→ Abs 导出表 → 注入入口。
495
- * 读 fs / 解析路径 / harvest 在这里;core 只收 modules 表。
174
+ * B 路径静态诊断(AST,不执行):
175
+ * - unreachable:同块 return/throw 之后的语句
176
+ * - builtin-unknown:未声明的全局调用(非 import / 局部 / $runtime)
496
177
  */
497
-
498
- type AbsLoadModule = (spec: string, fromFile: string) => string | undefined;
499
- /** 相对说明符 → 源码 */
500
- declare function defaultAbsLoadModule(spec: string, fromFile: string): string | undefined;
501
- /** 模块加载守卫:与 TypeValue loadModuleEnv 口径对齐,供 analyzer 映射诊断 */
502
- type AbsModuleLoadIssue = {
503
- kind: "cycle" | "depth" | "missing";
504
- /** 诊断定位用标签(文件 basename 或 require/import 说明符) */
505
- label: string;
506
- reason: string;
507
- };
508
- type AbsModuleGraphResult = {
509
- /** 入口 import 说明符 → 依赖导出表 */
510
- modules: Record<string, AbsModuleExports>;
511
- /** 绝对路径 → 导出表(含依赖;循环时占位为空) */
512
- byPath: Map<string, AbsModuleExports>;
513
- /** cycle / depth / missing(B 路径权威,避免 TypeValue 叠报) */
514
- issues: AbsModuleLoadIssue[];
178
+ type BPathLoc = {
179
+ start: {
180
+ line: number;
181
+ column: number;
182
+ };
183
+ end: {
184
+ line: number;
185
+ column: number;
186
+ };
515
187
  };
516
- type AbsGraphOptions = {
517
- loadModule?: AbsLoadModule;
518
- seedVars?: Record<string, Abs>;
519
- seedFns?: Record<string, {
520
- params: string[];
521
- body: Node;
522
- async?: boolean;
523
- }>;
524
- maxDepth?: number;
188
+ type BPathUnreachable = {
189
+ range: BPathLoc;
525
190
  };
526
- /** 会话级依赖模块缓存条目:stat 指纹 + 导出 + 子树装载 issue。 */
527
- type AbsModuleCacheEntry = {
528
- mtimeMs: number;
529
- size: number;
530
- exports: AbsModuleExports;
531
- /** 该模块子树首次求值时记录的 cycle/depth/missing;命中时重放。 */
532
- issues: AbsModuleLoadIssue[];
191
+ type BPathBuiltinUnknown = {
192
+ name: string;
193
+ range: BPathLoc;
533
194
  };
534
- declare function clearAbsModuleCache(): void;
535
- declare function evictAbsModuleCacheFiles(paths: string[]): void;
536
- /**
537
- * 递归求值相对依赖 + 裸包 harvest,产出入口可用的 modules 表。
538
- * 循环依赖:先放空表再回填(与 TypeValue 路径 partial 口径一致),
539
- * 并记录 cycle/depth/missing 供诊断。
540
- */
541
- declare function evalAbsModuleGraph(entrySource: string, entryFile: string, opts?: AbsGraphOptions): AbsModuleGraphResult;
542
- /** 便捷:入口求值 + 依赖 Abs 注入 */
543
- declare function evalProgramAbsWithModules(source: string, entryFile: string, opts?: AbsGraphOptions & {
544
- file?: unknown;
545
- }): {
546
- env: AstEnv;
547
- last: Abs;
548
- phi: Phi;
195
+ type BPathDiagnostics = {
196
+ unreachable: BPathUnreachable[];
197
+ builtinUnknown: BPathBuiltinUnknown[];
549
198
  };
550
199
  /**
551
- * 收集顶层绑定名 → Abs(含相对 import / 裸包 harvest 注入)。
552
- * 供 bindings / hover 从 Abs 投影,不必走 TypeValue evaluator。
553
- */
554
- declare function collectAbsBindingsFromGraph(source: string, filePath: string, opts?: AbsGraphOptions): Map<string, Abs>;
555
-
556
- /**
557
- * Harvest Abs 导出 → AbsModuleExports(供 Abs 模块图注入)。
558
- * harvest 产物已是 Abs;fn 槽 apply 直接返回声明返回类型(conf=mock)。
200
+ * 静态收集 B 路径诊断。
201
+ * extraKnown:@nudo:mock / @nudo:env 已覆盖的全局名(B 注入后不再是裸原生
202
+ * 调用,不得误报 builtin-unknown——收集器只吃 AST,看不到指令)。
559
203
  */
204
+ declare function collectBPathDiagnostics(source: string, extraKnown?: Iterable<string>): BPathDiagnostics;
560
205
 
561
- /** 单个 Abs 导出 → 可调用 mock(fn 槽 apply 返回声明返回类型) */
562
- declare function harvestedValueToAbs(a: Abs): Abs;
563
206
  /**
564
- * 把 harvest 的 modules/globals 合成 AbsModuleExports。
565
- * 键同时登记:原始模块路径、包名、basename(path / @types/path)。
207
+ * @nudo:mock / sinon 指令 → Abs seed(供 B 路径注入:runTranspiled
208
+ * envGlobals / mockSeedsToAbsMocks)。
209
+ * host 层:依赖 parser 指令形态;core 只吃 Abs 绑定。
566
210
  */
567
- declare function harvestToAbsModules(pkg: string, fromDir: string): Record<string, AbsModuleExports>;
568
- declare function packageHarvestToAbsModules(pkg: string, h: PackageHarvest): Record<string, AbsModuleExports>;
569
- /** 裸说明符 → Abs 导出表(走 harvest 缓存) */
570
- declare function bareSpecToAbsModules(spec: string, fromFile: string): AbsModuleExports | undefined;
571
211
 
572
- /** @nudo:env → Abs 全局表(env 模块 Abs 原生) */
573
- declare function collectEnvGlobals(envNames: string[]): Record<string, Abs>;
574
- /** @nudo:env modules(path / node:path / fs…)→ AbsModuleExports */
575
- declare function collectEnvModules(envNames: string[]): Record<string, AbsModuleExports>;
576
- /** Conflict when handwritten env overwrote a harvest module/export (B8). */
577
- type EnvHarvestConflict = {
578
- module: string;
579
- /** export names where env replaced a harvest binding (empty + defaulted = default only) */
580
- exports: string[];
581
- defaultOverwritten: boolean;
582
- };
583
- /**
584
- * Install conflict collector; returns the previous one so nested/concurrent
585
- * analyzeFile callers can save/restore (module-global is not re-entrant).
586
- */
587
- declare function setEnvHarvestConflictCollector(collector: ((c: EnvHarvestConflict) => void) | null): ((c: EnvHarvestConflict) => void) | null;
588
- /** Read-only peek for tests / nested restore. */
589
- declare function getEnvHarvestConflictCollector(): ((c: EnvHarvestConflict) => void) | null;
590
- type MergeHarvestOptions = {
591
- /**
592
- * Per-call conflict sink. Takes precedence over the module-global collector
593
- * installed via `setEnvHarvestConflictCollector`.
594
- */
595
- onConflict?: (c: EnvHarvestConflict) => void;
596
- };
597
- /**
598
- * Handwritten `@nudojs/env` wins over harvest / graph modules on overlapping
599
- * module keys and overlapping export names (docs/versioning.md B8 + website
600
- * harvester API). Harvest-only modules/exports are kept as fill-in.
601
- * Overwrites notify `opts.onConflict` or the global collector.
602
- */
603
- declare function mergeHarvestUnderEnv(harvestModules: Record<string, AbsModuleExports>, envModules: Record<string, AbsModuleExports>, opts?: MergeHarvestOptions): Record<string, AbsModuleExports>;
604
- /** 收集 @nudo:replace + @nudo:as → transpile 注入表 */
605
- declare function collectBPathReplacements(source: string): {
606
- targets: Array<{
607
- target: string;
608
- varName: string;
609
- stmtStart?: number;
610
- stmtEnd?: number;
611
- }>;
612
- values: Record<string, Abs>;
613
- asTargets: Array<{
614
- varName: string;
615
- stmtStart: number;
616
- stmtEnd: number;
212
+ type AbsMockSeeds = {
213
+ seedVars: Record<string, Abs>;
214
+ seedFns: Record<string, {
215
+ params: string[];
216
+ body: Node;
217
+ async?: boolean;
218
+ fingerprint?: string;
617
219
  }>;
618
- asValues: Record<string, Abs>;
220
+ /** `@nudo:mock name from "path"` 解析失败(缺文件 / 缺绑定 / 求值失败)——fail-closed 诊断用 */
221
+ fromErrors?: FromMockError[];
619
222
  };
620
- /** 可走 transpile+exec:env 经 loadEnvs(内置 + 已 preload 的路径型) */
621
- declare function isBPathCapable(source: string, envNames?: string[]): boolean;
622
- type BPathRunResult = {
623
- exports: Record<string, unknown>;
624
- modules: Record<string, AbsModuleExports>;
625
- /** 顶层执行期 method-missing */
626
- memberDiags?: BMemberDiag[];
627
- /** 模块图 cycle/depth/missing(B 权威) */
628
- moduleIssues?: AbsModuleLoadIssue[];
629
- /** Abs 求值递归截断的函数标签 */
630
- truncatedFns?: string[];
631
- /** 顶层 $callNamed 调用点(call@ 合成;不经 TypeValue collector) */
632
- calls?: BCallRecord[];
223
+ type FromMockError = {
224
+ name: string;
225
+ fromPath: string;
226
+ message: string;
633
227
  };
634
- declare function clearBPathCache(): void;
635
- /** 依赖文件变更后:逐出以这些文件为入口的 B-path 缓存 */
636
- declare function evictBPathCacheForFiles(files: string[]): number;
637
- /** 模块图 + runTranspiled(默认 analyze 模式) */
638
- declare function tryRunBPath(source: string, filePath: string, opts?: {
639
- maxLoopIters?: number;
640
- mode?: "exec" | "analyze";
641
- envNames?: string[];
642
- /** @nudo:mock → Abs,注入为全局绑定(防止顶层调用真 fetch 等) */
643
- mocks?: Record<string, Abs>;
644
- }): BPathRunResult | undefined;
645
228
  /**
646
- * B 路径求值具名导出(结果 + throws);opts.collectCalls 时附带调用点记录。
647
- *
648
- * `calls` 只含**本次具名调用期间**(callTranspiledExportFull 内)发生的
649
- * 调用点——不含模块级顶层调用(run.calls)。模块级记录由主分析流程
650
- * 另行收集(analyzer 的 bTopCallRecords / absCallRecords);在此重复返回
651
- * 会把 B 路径结果(常为 unknown)当第二组记录推给 analyzer,dedupe 因
652
- * 结果形态不同而保留,污染被调函数的 case 列表(多余 call@L 重复 +
653
- * call@symbolic 聚合 case,见 analyzer directive-case 分支)。
229
+ * B 路径注入用:seedVars + seedFns 统一为 Abs 函数绑定。
230
+ * arrowFn mock 落在 seedFns(AST body,供 ast-eval),B 路径的
231
+ * envGlobals 注入只吃 Abs——不合并会把 mock 丢掉,函数体内的调用
232
+ * 会落到真实原生函数(如 fetch 拿 Abs 当 URL,直接崩)。
654
233
  */
655
- declare function tryBPathCallFull(source: string, filePath: string, fnName: string, args: Abs[], opts?: {
656
- collectCalls?: boolean;
657
- collectMemberDiags?: boolean;
658
- envNames?: string[];
659
- mocks?: Record<string, Abs>;
660
- }): (TranspiledCallResult & {
661
- calls?: BCallRecord[];
662
- memberDiags?: BMemberDiag[];
663
- moduleIssues?: AbsModuleLoadIssue[];
664
- truncatedFns?: string[];
665
- }) | undefined;
666
- /** B 路径求值具名导出(仅成功结果) */
667
- declare function tryBPathCall(source: string, filePath: string, fnName: string, args: Abs[], opts?: {
668
- envNames?: string[];
669
- mocks?: Record<string, Abs>;
670
- }): Abs | undefined;
671
-
672
- declare function clearAnalysisFileCache(): void;
673
- declare function getAnalysisFileCacheSize(): number;
674
- /** 依赖变更后:按入口文件逐出 */
675
- declare function evictAnalysisFileCacheForFiles(files: string[]): number;
234
+ declare function mockSeedsToAbsMocks(seeds: AbsMockSeeds): Record<string, Abs>;
235
+ /** 从函数上的 @nudo:mock 指令收集 Abs seed */
236
+ declare function mockDirectivesToAbsSeeds(functions: Array<{
237
+ directives: FunctionWithDirectives["directives"];
238
+ }>, opts?: {
239
+ /** `@nudo:mock name from "path"` 相对解析基准(被分析文件) */
240
+ fromFile?: string;
241
+ /** 自定义装载器(LSP buffer-aware);缺省 defaultLoadModule */
242
+ loadModule?: LoadModule;
243
+ }): AbsMockSeeds;
244
+ /** 便捷入口:源码 → @nudo:mock 的 B 注入 Abs 绑定(checkSource 注入管线用) */
245
+ declare function mockSeedsForSource(source: string, opts?: {
246
+ fromFile?: string;
247
+ loadModule?: LoadModule;
248
+ }): Record<string, Abs>;
676
249
 
677
250
  /**
678
- * 依赖内容变更后:按入口文件定向逐出 service 层缓存。
679
- * 调用方应传「以这些文件为入口」的路径(脏集里的 dependents),
680
- * 而不是变更的 dep 文件本身——dep 自己 source 变了会自然 miss。
251
+ * `@nudo:mock-module "spec" from "./mock.js"` — file-level module replacement.
252
+ * Full replace swaps the specifier's exports table; partial `{ a, b }` only
253
+ * overlays those named exports and lets the rest fall through.
254
+ *
255
+ * Fail-closed: missing mock file / eval failure / empty binding → error, not
256
+ * a silent drop and not invented types.
681
257
  */
682
- declare function evictAnalysisCachesForFiles(files: string[]): void;
258
+
259
+ type MockModuleApplyResult = {
260
+ modules: Record<string, AbsModuleExports>;
261
+ errors: FromMockError[];
262
+ /** true when at least one mock-module directive was applied */
263
+ applied: boolean;
264
+ };
683
265
  /**
684
- * 清空全部会话级分析缓存(service + core)。
685
- * 适用于:CLI watch 增量批前、vite buildStart / watchChange、测试隔离。
686
- * 比定向逐出重,但保证无陈旧命中;单次 analyze 内部的 per-fn / generalize
687
- * memo 不受影响(它们在同一轮里先写后读)。
266
+ * Overlay `@nudo:mock-module` directives onto a modules map.
267
+ * `base` is not mutated; a new map is returned.
688
268
  */
689
- declare function clearAnalysisSessionCaches(): void;
690
- /** 比 clearAnalysisSessionCaches 更彻底:再丢 AST LRU(测试 / 进程复用场景) */
691
- declare function resetAllAnalysisCaches(): void;
269
+ declare function applyMockModuleDirectives(base: Record<string, AbsModuleExports>, fileDirectives: FileDirective[], opts: {
270
+ fromFile: string;
271
+ loadModule?: LoadModule;
272
+ }): MockModuleApplyResult;
273
+ /** Source string → apply mock-module (CLI / one-shot hosts). */
274
+ declare function applyMockModuleDirectivesFromSource(source: string, base: Record<string, AbsModuleExports>, opts: {
275
+ fromFile: string;
276
+ loadModule?: LoadModule;
277
+ }): MockModuleApplyResult;
692
278
 
693
279
  /**
694
280
  * B5:workspace 级 AnalysisSession——LSP 与 CLI(同进程)共享的分析 memo 面。
@@ -721,12 +307,77 @@ declare function getAnalysisSession(): AnalysisSession;
721
307
  /** 测试:替换默认 session(返回旧值以便恢复) */
722
308
  declare function setAnalysisSession(session: AnalysisSession | undefined): AnalysisSession | undefined;
723
309
 
310
+ declare function clearAnalysisFileCache(): void;
311
+ declare function getAnalysisFileCacheSize(): number;
312
+ /** 立刻压到当前 maxFiles(调低上限时收内存) */
313
+ declare function trimAnalysisFileCache(): void;
314
+ /** 依赖变更后:按入口文件逐出 */
315
+ declare function evictAnalysisFileCacheForFiles(files: string[]): number;
316
+
317
+ /**
318
+ * 会话级内存 LRU 上限(进程内,非磁盘 cache)。
319
+ * 多项目开 LSP 时用 env 封顶内存;单大仓可调高换 warm 命中。
320
+ *
321
+ * 优先级:setSessionCacheLimits(显式)> env > package.json#nudo.sessionCache > 默认
322
+ *
323
+ * 覆盖面:analysis-file-cache(maxFiles)/ fn-analysis-cache(maxFns)/
324
+ * bpath-run(maxBRuns)。其余驻留结构(harvest-auto / harvest-node /
325
+ * abs-modules-graph / env-loader path-env / env-path-deps)用各自的硬上限常量,
326
+ * 见各文件与 lru-map.ts。所有上限加起来给出大仓分析后 retained 内存的上界。
327
+ */
328
+ type SessionCacheLimits = {
329
+ /** 整文件 AnalysisResult LRU;0 = 关闭 */
330
+ maxFiles: number;
331
+ /** per-fn FunctionAnalysis LRU;0 = 关闭 */
332
+ maxFns: number;
333
+ /** B-path run LRU;0 = 关闭 */
334
+ maxBRuns: number;
335
+ };
336
+ /** 保守默认:多项目共存时不悄悄吃内存(大仓请显式调高) */
337
+ declare const DEFAULT_SESSION_CACHE_LIMITS: SessionCacheLimits;
338
+ type PartialLimits = Partial<SessionCacheLimits>;
339
+ declare function getSessionCacheLimits(env?: NodeJS.ProcessEnv): SessionCacheLimits;
340
+ /** 显式覆盖(宿主 / 测试)。传 null 清除显式层 */
341
+ declare function setSessionCacheLimits(partial: PartialLimits | null): SessionCacheLimits;
342
+ /** package.json#nudo.sessionCache 层(findProjectConfig / 宿主接线) */
343
+ declare function setSessionCacheFromProject(partial: PartialLimits | null | undefined): void;
344
+ /** 测试:丢弃 env 惰性缓存,重新读 process.env */
345
+ declare function resetSessionCacheLimitState(): void;
346
+
347
+ /**
348
+ * 依赖内容变更后:按入口文件定向逐出 service 层缓存。
349
+ * 调用方应传「以这些文件为入口」的路径(脏集里的 dependents),
350
+ * 而不是变更的 dep 文件本身——dep 自己 source 变了会自然 miss。
351
+ * 残余缺口:absModuleCache 按 dep 路径 + mtime/size 键控;「同 size + 同 mtime」
352
+ * 编辑需再 evictAbsModuleCacheFiles([depPath]) 或 clearAnalysisSessionCaches。
353
+ */
354
+ declare function evictAnalysisCachesForFiles(files: string[]): void;
355
+ /**
356
+ * 清空全部会话级分析缓存(service + core)。
357
+ * 适用于:CLI watch 增量批前、vite buildStart / watchChange、测试隔离。
358
+ * 比定向逐出重,但保证无陈旧命中;单次 analyze 内部的 per-fn / generalize
359
+ * memo 不受影响(它们在同一轮里先写后读)。
360
+ */
361
+ declare function clearAnalysisSessionCaches(): void;
362
+ /**
363
+ * 接线 package.json#nudo.sessionCache(进程内 LRU 上限)并立刻 trim。
364
+ * env `NUDO_CACHE_MAX_FILES|FNS|BRUNS` 仍优先(多项目内存封顶)。
365
+ */
366
+ declare function applySessionCacheConfig(config: NudoConfig | null | undefined): SessionCacheLimits;
367
+ /** 比 clearAnalysisSessionCaches 更彻底:再丢 AST LRU(测试 / 进程复用场景) */
368
+ declare function resetAllAnalysisCaches(): void;
369
+
724
370
  /**
725
371
  * 磁盘内容寻址缓存(B3,design-persistent-cache.md L1 骨架)。
726
372
  * - 键:sha256(analysisAbi + relative paths + content hashes + import deps)
727
373
  * - fail-open:读写失败/版本不符 → miss,绝不 throw
728
374
  * - 不存 Abs;只存可 JSON 再执行投影(如 CheckJson)
729
375
  * - 缓存根解析见 `evaluator/config.ts` 的 `diskCacheRoot`(唯一入口)
376
+ *
377
+ * 内存上界:本层不持有进程内 map——每次 get/set 直读/直写磁盘,retained
378
+ * heap O(1)。磁盘容量不在本层封顶(由宿主/CI 清理 `diskCacheRoot`)。
379
+ * 进程内 LRU 上界见 session-cache-limits / lru-map.ts(analysis/fn/bpath/
380
+ * harvest/abs-module/path-env)。
730
381
  */
731
382
  /**
732
383
  * 带包版本:升级 @nudojs/* 后旧 CheckJson 不得继续命中。
@@ -773,6 +424,8 @@ declare function checkCacheKey(filePath: string, source: string, opts: {
773
424
  mode?: string;
774
425
  evalMissingSlot?: string;
775
426
  callSiteBudget?: number;
427
+ entryThrows?: string;
428
+ ignoreThrows?: string;
776
429
  };
777
430
  }): string;
778
431
  /**
@@ -796,596 +449,39 @@ declare function ifaceCacheKey(filePath: string, source: string, opts: {
796
449
  declare function extractNudoImportSpecs(source: string): string[];
797
450
 
798
451
  /**
799
- * check/iface 磁盘缓存键的依赖内容采集。
800
- * 走 core `loadModuleDepsFingerprint`:覆盖 ESM from / require / dynamic import /
801
- * `@nudo:import` / ambient 侧车闭包——不只 `@nudo:import` 注释。
802
- *
803
- * 内容必须优先取 loadModule 解析结果(fingerprint.contents),不得回读磁盘:
804
- * LSP buffer-aware loadModule 与自定义 loader 的内容可能与磁盘不一致,
805
- * 回读会让缓存键分叉(buffer 编辑后仍命中磁盘旧内容)。
806
- */
807
- type DepContent = {
808
- path: string;
809
- content: string | null;
810
- };
811
- declare function collectLoadDepContents(filePath: string, source: string, loadModule: (spec: string, fromFile: string) => string | undefined): {
812
- depContents: DepContent[];
813
- truncated: boolean;
814
- };
815
-
816
- /**
817
- * B 路径静态诊断(AST,不执行):
818
- * - unreachable:同块 return/throw 之后的语句
819
- * - builtin-unknown:未声明的全局调用(非 import / 局部 / $runtime)
820
- */
821
- type BPathLoc = {
822
- start: {
823
- line: number;
824
- column: number;
825
- };
826
- end: {
827
- line: number;
828
- column: number;
829
- };
830
- };
831
- type BPathUnreachable = {
832
- range: BPathLoc;
833
- };
834
- type BPathBuiltinUnknown = {
835
- name: string;
836
- range: BPathLoc;
837
- };
838
- type BPathDiagnostics = {
839
- unreachable: BPathUnreachable[];
840
- builtinUnknown: BPathBuiltinUnknown[];
841
- };
842
- /**
843
- * 静态收集 B 路径诊断。
844
- * extraKnown:@nudo:mock / @nudo:env 已覆盖的全局名(B 注入后不再是裸原生
845
- * 调用,不得误报 builtin-unknown——收集器只吃 AST,看不到指令)。
846
- */
847
- declare function collectBPathDiagnostics(source: string, extraKnown?: Iterable<string>): BPathDiagnostics;
848
-
849
- /**
850
- * 预置 Node API env:从 @types/node 的 .d.ts harvest。
851
- * 与 packages/env 的手写 env 并行;此路径自动、可刷新。
852
- *
853
- * Productization (P0-B B2/B7):
854
- * - In-process cache keyed by package root + package.json mtime/size.
855
- * - Terminal failures (`not-found`/`no-dts`/`failed`) also cache — no retry storm.
856
- * `disabled` is never cached (env var can flip mid-process).
857
- * - `clearNodeHarvestCache()` for tests / watch invalidation.
858
- * - `NUDO_HARVEST_NODE=off` disables harvest (explicit skip, not silent).
859
- * - Defaults stay IDE-budgeted: maxFiles=12, maxMs=2500.
860
- */
861
-
862
- /** IDE-startup budgets for @types/node harvest. Do not raise casually. */
863
- declare const HARVEST_NODE_DEFAULT_MAX_FILES = 12;
864
- declare const HARVEST_NODE_DEFAULT_MAX_MS = 2500;
865
- type HarvestNodeStats = {
866
- files: number;
867
- symbols: number;
868
- skipped: number;
869
- };
870
- type NodeEnvResult = {
871
- ok: true;
872
- env: HarvestedEnv;
873
- root: string;
874
- files: number;
875
- stats: HarvestNodeStats;
876
- /** true when this result came from the in-process cache */
877
- cached: boolean;
878
- } | {
879
- ok: false;
880
- error: string;
881
- reason: "disabled" | "not-found" | "no-dts" | "failed";
882
- /** true when this failure came from the in-process cache */
883
- cached?: boolean;
884
- };
885
- /**
886
- * Clear the in-process @types/node harvest cache.
887
- * Drops **success and terminal-failure** entries (including `not-found`), so
888
- * a mid-session `@types/node` install becomes visible without process restart.
889
- * Tests, watch-mode dep-hash invalidation, and package-manager hooks should
890
- * call this when `node_modules/@types/node` changes underneath the process.
891
- */
892
- declare function clearNodeHarvestCache(): void;
893
- /** Number of cached harvest results (diagnostics / tests). */
894
- declare function getNodeHarvestCacheSize(): number;
895
- declare function isHarvestNodeDisabled(env?: NodeJS.ProcessEnv): boolean;
896
- /**
897
- * harvest @types/node(限制文件数 + 时间预算,避免拖垮启动)。
898
- * Success **and** terminal failures (`not-found` / `no-dts` / `failed`) are
899
- * cached in-process so IDE analysis does not re-walk / re-timeout every file.
900
- * `disabled` is never cached — the env var can flip mid-process.
901
- * Call `clearNodeHarvestCache()` after `@types/node` changes.
902
- */
903
- declare function harvestNodeTypes(fromDir?: string, maxFiles?: number, maxMs?: number): NodeEnvResult;
904
- /** 把 harvest 结果压成「模块名 → 导出名列表」摘要,便于日志/测试 */
905
- declare function summarizeNodeEnv(env: HarvestedEnv): {
906
- modules: string[];
907
- globals: string[];
908
- symbolCount: number;
909
- };
910
-
911
- /**
912
- * Semantic tokens 图例(tokenTypes 下标即 LSP 编码里的 tokenType 值)。
913
- * 与 LSP server capabilities 里声明的 legend 必须逐字对齐——server.ts 直接
914
- * 导入本常量注册,保证「提取端索引」与「客户端图例」单一来源。
915
- * 顺序沿用 lsp 包原 legend(function/variable/parameter/property 在前),
916
- * 末尾追加 method(对象字面量方法键)与 interface 档 modifier(A7),
917
- * 只追加不重排,客户端索引稳定。
918
- */
919
- declare const SEMANTIC_TOKEN_TYPES: readonly ["function", "variable", "parameter", "property", "type", "keyword", "string", "number", "comment", "decorator", "method"];
920
- declare const SEMANTIC_TOKEN_MODIFIERS: readonly ["declaration", "readonly", "deprecated", "unreachable", "contract", "generated", "derived"];
921
- type SemanticToken = {
922
- line: number;
923
- char: number;
924
- length: number;
925
- typeIndex: number;
926
- modifierBitmask: number;
927
- };
928
- /** LSP 标准相对五元组编码:deltaLine/deltaStartChar/length/tokenType/tokenModifiers。 */
929
- declare function encodeSemanticTokens(tokens: SemanticToken[]): number[];
930
- /** A7:interface 档 → semantic token modifier(与 CodeLens 同源) */
931
- declare function interfaceTierModifierBit(src: InterfaceSource): number;
932
- type BuildSemanticTokensOpts = InterfaceTierOpts & {
933
- loadModule?: (spec: string, fromFile: string) => string | undefined;
934
- };
935
- /**
936
- * 从源码提取 semantic tokens 并按 LSP 相对编码返回扁平 number[]。
937
- *
938
- * 上色范围(声明位优先,未解析的 token 一律不上色):
939
- * - 顶层 const/let/var 声明的绑定名:推断为函数绑定 → function,否则 variable
940
- * (函数体内部的声明不做 env 反查——外层同名绑定会串味,统一 variable);
941
- * - 函数声明/函数表达式的名字 → function;所有函数的参数 → parameter;
942
- * - 对象字面量的键:值为函数 → method,否则 property。
943
- *
944
- * A7:本地 named export 的函数绑定额外带 interface 档 modifier
945
- * (contract / generated / derived),与 CodeLens `● interface` 同源。
946
- * 非导出绑定只带 declaration,不假装进档。
947
- *
948
- * 推断优先 Abs 模块图绑定(TypeValue 退出主路径);失败时复用
949
- * evaluateProgram。解析失败返回 []。
950
- */
951
- declare function buildSemanticTokens(filePath: string, source: string, opts?: BuildSemanticTokensOpts): number[];
952
-
953
- /**
954
- * Abs → TS 类型串。有损:pred / 非 lit term 落到 shape 基类型。
955
- * `typeVars`:term var id → TS 类型参数名(HOF 泛型投影时传入;
956
- * `any`+var 在此映射下渲染为该参数名,否则 `unknown`)。
957
- */
958
- declare function absToTSType(a: Abs, typeVars?: Map<string, string>): string;
959
- /**
960
- * 为单个函数生成 .d.ts 声明行(JSDoc + 单一 widen 主签名)。
961
- * 有 hof 关系时优先泛型投影(C3.3);否则 case-widen。
962
- * service 的 generateDts 与 CLI `--dts`(infer/watch)共用本函数,
963
- * 两条路径输出保持一致。
964
- */
965
- declare function generateFunctionDtsLines(fn: FunctionAnalysis): string[];
966
- declare function generateDts(result: AnalysisResult): string;
967
-
968
- /** Abs → zod schema 源码。有损:pred / 非 lit term 落到 shape 基类型。 */
969
- declare function absToZodSchema(a: Abs): string;
970
-
971
- /** Abs 指称守卫(设计 §2.7):保留 pred */
972
- declare function generateGuardFunctionFromAbs(name: string, abs: Abs): string;
973
- /** 兼容别名:Abs 路径唯一 */
974
- declare function generateGuardFunction(name: string, abs: Abs): string;
975
-
976
- /** 单个 Abs → parseCaseArgExpr 可解析回去的表达式文本;不可表达返回 null */
977
- declare function serializeCaseArg(a: Abs): string | null;
978
- /**
979
- * 组装单行 ` * @nudo:case "name" (a, b)` 指令文本(无尾换行)。
980
- * 任一实参不可序列化、或名字含双引号/换行(名字正则 `"([^"]+)"` 承载不了)→ 整体 null。
981
- */
982
- declare function buildCaseDirective(name: string, argsAbs: Abs[]): string | null;
983
- /**
984
- * 从源码剥离所有本模块生成的 @nudo:case 指令(名字以 call@ 开头,整行删除)。
985
- * 若所属 JSDoc 块因此只剩空 ` *` 行(无其他 @nudo:* 指令、无文字内容),
986
- * 连块首 `/**` 行与块尾行整块删除。绝不碰非 case 指令与普通注释。
987
- * 注意:手写但以 call@ 命名的 case 同样会被删——call@ 前缀保留为生成物标记。
988
- * removed 返回被删指令名列表(按出现顺序)。
989
- */
990
- declare function stripGeneratedCaseDirectives(source: string): {
991
- source: string;
992
- removed: string[];
993
- };
994
- type EmitSkipReason = "hand-written" | "already-generated" | "entry-only" | "no-serializable-cases" | "no-declaration" | "skipped";
995
- type EmitResult = {
996
- source: string;
997
- changed: boolean;
998
- written: Array<{
999
- fn: string;
1000
- cases: string[];
1001
- }>;
1002
- skipped: Array<{
1003
- fn: string;
1004
- reason: EmitSkipReason;
1005
- detail?: string;
1006
- }>;
1007
- };
1008
- /**
1009
- * 把 analysis 中的合成 case(source === "callsite")固化为源码指令:
1010
- *
1011
- * 1. 源码已有任何非 call@ 命名的 case → skip "hand-written";
1012
- * 已有 call@ case → skip "already-generated"。
1013
- * 2. fn.skipped / fn.noDeclaration / fn.entryOnly → 对应 skip。
1014
- * 3. 逐 case 序列化;个别不可序列化的丢弃并在 skipped 记
1015
- * no-serializable-cases(函数整体仍写可序列化子集,全不可序列化才整函数跳过)。
1016
- * 4. 写入位置:函数声明行正上方——已有 JSDoc 块则插到 `/**` 行后,
1017
- * 无块则新建三行块。缩进取声明行的 loc.start.column(babel 0 基列)。
1018
- * 多函数编辑按行号从下往上应用,避免行号漂移。
1019
- */
1020
- declare function insertGeneratedCaseDirectives(source: string, analysis: AnalysisResult): EmitResult;
1021
- /** 行级 unified diff:`--- a/path` 头 + `@@` hunk + 上下文 3 行;相同返回 "" */
1022
- declare function unifiedDiff(a: string, b: string, path: string): string;
1023
-
1024
- /**
1025
- * @nudo:mock / sinon 指令 → Abs seed(供 evalProgramAbs)。
1026
- * host 层:依赖 parser 指令形态;core 只吃 seedVars/seedFns。
1027
- */
1028
-
1029
- type AbsMockSeeds = {
1030
- seedVars: Record<string, Abs>;
1031
- seedFns: Record<string, {
1032
- params: string[];
1033
- body: Node;
1034
- async?: boolean;
1035
- fingerprint?: string;
1036
- }>;
1037
- };
1038
- /** 从函数上的 @nudo:mock 指令收集 Abs seed */
1039
- declare function mockDirectivesToAbsSeeds(functions: Array<{
1040
- directives: FunctionWithDirectives["directives"];
1041
- }>): AbsMockSeeds;
1042
-
1043
- /**
1044
- * `nudo interface` 的打印数据源(design-refine-derivation §11 第 0 步):
1045
- * 逐顶层函数展示有效契约分层——effectiveInterface 命中(手写契约 / 侧车
1046
- * 生成段)时 params/returns 用 formatConstraint 组合式显示;未命中走
1047
- * implicit:优先 CaseResult.argAbs / FunctionAnalysis.combinedAbs(无损
1048
- * Abs 源),展示用 formatShape(外延口径,不强求约束式、不带 conf)。
1049
- *
1050
- * autoBind 沿 package.json#nudo.interface(findProjectConfig → interfaceConfig)
1051
- * 下传,可用 opts 覆盖(测试 / CLI 显式开关);读盘用 defaultLoadModule
1052
- * (与 check 的 refine 解析同一扩展名表)。
1053
- *
1054
- * B3 Phase B:`nudo.cache` / NUDO_CACHE_DIR 打开时,整文件 effectiveInterface
1055
- * 表(含 implicit 负缓存 null)落盘;二次冷启动跳过侧车 exec / 契约合并。
1056
- * 缓存只服务打印/表面,不加速 B-path 分析(design-persistent-cache §0)。
1057
- */
1058
-
1059
- type InterfaceSurfaceEntry = {
1060
- fn: string;
1061
- /** 本文件 named export(自动绑定边界口径:localNamedExports) */
1062
- kind: "export" | "local";
1063
- source: "handwritten" | "generated" | "implicit";
1064
- params: Array<{
1065
- name: string;
1066
- display: string;
1067
- }>;
1068
- returns?: string;
1069
- };
1070
- type InterfaceSurfaceOpts = {
1071
- /** 覆盖项目配置的 autoBind(默认 findProjectConfig → interfaceConfig) */
1072
- autoBind?: boolean;
1073
- /** 模块源码装载器(测试注入);默认 defaultLoadModule 真实读盘 */
1074
- loadModule?: LoadModule;
1075
- /** 跨文件调用记录(--callsites 采集):implicit 展示的实参域原料 */
1076
- records?: CallRecord[];
1077
- /** 打开 buffer 覆盖磁盘源码(E5:agent/LSP 与 hover 同口径) */
1078
- source?: string;
1079
- };
1080
- /** 单条 interface 打印行(CLI runInterface 与 LSP agent 面共用) */
1081
- declare function formatInterfaceSurfaceLine(e: InterfaceSurfaceEntry): string;
1082
- /**
1083
- * 单文件 interface 表面:analyzer 推断结果给出函数清单与 implicit 展示,
1084
- * effectiveInterface 给出契约命中(手写 > 生成段)。诊断 side-channel
1085
- * 在收尾时取走丢弃——打印命令不执法,interface-load 等错误留给 check 路径。
1086
- */
1087
- declare function interfaceSurface(filePath: string, opts?: InterfaceSurfaceOpts): Promise<InterfaceSurfaceEntry[]>;
1088
-
1089
- /**
1090
- * `nudo interface --emit` 的写盘器(design-refine-derivation §5.4 / §7.3 / §11):
1091
- * 把分析产出的调用点域投影(joinThenProject,先 Abs join 再投影)固化为侧车
1092
- * `*.nudo.js` / `*.nudo.ts` 的 `@generated` 段——`export const <fn> = fn({…}, …)`。
1093
- *
1094
- * - 生成段 = 三行:`@generated` 头注释 + `// source: <相对file>:<fn>` + export 行。
1095
- * DSL 由 formatConstraint 产出,**写入前 round-trip 自检**(生成文本经
1096
- * execNudoModule 求值、导出须 isNudoConstraint 且 fn 槽存在):展示兜底形态
1097
- * (`x?:` 可选字段、`.eq()` 伪链、`{谓词原文}`)不是合法 DSL,自检拦下 →
1098
- * not-projectable 跳过,不写垃圾。
1099
- * - 手写导出永不覆盖:目标名已是侧车手写绑定 → 跳过写入并报
1100
- * `nudo:interface-name-clash`(error,手写优先)。
1101
- * - mode=update:剥离全部生成段再重排(幂等;非目标的既有生成段原样保留,
1102
- * 不随 --fn 丢失);mode=add:仅追加缺失段。
1103
- * - 目标过滤(§7.3):--fn 白名单 > --all 全量 > 默认只刷新已有生成段
1104
- * (「无根只处理已存在生成段」;Phase 1 无 root 闭包下行推导)。
1105
- * - autoBind=false 不影响 emit(显式动作,非 ambient 加载)。
1106
- */
1107
-
1108
- type EmitInterfaceSkipReason = "name-clash" | "not-projectable" | "not-an-export" | "no-change"
1109
- /** package.json#nudo.interface.emit 白名单拒绝 */
1110
- | "emit-denied"
1111
- /** 手工合并的多声明符 @generated 段:按段原子保留,不拆不重写 */
1112
- | "multi-declarator";
1113
- type EmitInterfaceOpts = {
1114
- /** 目标导出名白名单(Phase 1:只过滤目标文件自身的导出;跨文件 root 闭包 emit 是 Phase 2 下行推导能力) */
1115
- fnNames?: string[];
1116
- /** update=剥离全部生成段再重排(幂等);add=仅追加缺失段 */
1117
- mode: "add" | "update";
1118
- /** 全部顶层导出(显式 opt-in,CLI 侧文档警告勿默认) */
1119
- all?: boolean;
1120
- /** 不写盘,返回 unifiedDiff */
1121
- dryRun?: boolean;
1122
- /**
1123
- * 跨文件调用记录(--callsites 采集):域根导出(本文件无调用点、仅有
1124
- * 外部使用现场记录)的投影原料;缺省时仅用本文件调用点证据。
1125
- */
1126
- records?: CallRecord[];
1127
- /** 缓冲源(E5:agent/LSP 与 validate 同源);缺省读盘 */
1128
- source?: string;
1129
- /** 模块装载器(buffer-aware 侧车可见);缺省 defaultLoadModule */
1130
- loadModule?: (spec: string, fromFile: string) => string | undefined;
1131
- };
1132
- type EmitInterfaceResult = {
1133
- written: string[];
1134
- skipped: Array<{
1135
- fn: string;
1136
- reason: EmitInterfaceSkipReason;
1137
- }>;
1138
- changed: boolean;
1139
- diff?: string;
1140
- issues: Array<{
1141
- code: string;
1142
- severity: "error" | "warning";
1143
- message: string;
1144
- }>;
1145
- /** 侧车绝对路径(新建或已有) */
1146
- sidecarPath: string;
1147
- /**
1148
- * 默认过滤(无 --fn/--all)且侧车无既有 @generated 段可刷 —— 调用方
1149
- * 用它把「no interface changes」解释成首次 emit 的用法提示,而非漂移。
1150
- */
1151
- emptyDefaultTargets?: boolean;
1152
- };
1153
- /**
1154
- * 单文件 emit:分析 → 逐目标投影/自检 → 组装侧车内容 →(非 dryRun)写盘。
1155
- * 诊断 side-channel(round-trip 自检触发的 refine/interface diags)收尾取走丢弃。
1156
- */
1157
- declare function emitInterface(filePath: string, opts: EmitInterfaceOpts): Promise<EmitInterfaceResult>;
1158
- /**
1159
- * emit 摘要行(CLI runInterfaceEmit 与 LSP agent 面共用;路径由调用方按
1160
- * 展示口径传入——CLI 传 cwd 相对、agent 传绝对路径)。
1161
- */
1162
- declare function formatEmitSummary(sourcePath: string, sidecarRel: string, result: EmitInterfaceResult): string[];
1163
-
1164
- /**
1165
- * `nudo interface --draft`:从已有 JS **逻辑** 生成 interface 草稿。
1166
- *
1167
- * 产品位(代码优先 / 迁移):先写实现,再反推可审阅的 `fn({…}, …)` 草稿;
1168
- * 人审后迁入 `*.nudo.js` 才成为契约。
1169
- *
1170
- * 证据分层(与 C0 一致:草稿 ≠ ambient 义务):
1171
- * - callsite / directive case → joinThenProject 值域(迁移最可信)
1172
- * - body 触达(**仅草稿展示**)→ 参数上被读到的字段名建议;不进 check
1173
- * - generalize symbolic → 返回位兜底
1174
- * - 无证据 → 参数槽省略 + 注释 TODO(不发明义务)
1175
- * - 已有 handwritten 契约 → **跳过**(手写优先,不覆盖)
452
+ * nudo 推断目标文件判定(纯扩展名规则,路径无需存在)。
1176
453
  *
1177
- * 写盘:默认只打印;`--write` 写入 `<file>.nudo.draft.js`——**不会**被
1178
- * sidecar 自动绑定(loadModule 只认 `*.nudo.js`)。审阅后复制进正式侧车。
1179
- */
1180
-
1181
- /**
1182
- * DraftEvidence `body` = 仅来自函数体对形参的成员读取(草稿建议,非义务)。
1183
- * check / effectiveInterface **永不**消费该档。
1184
- */
1185
- type DraftEvidence = "callsite" | "directive" | "symbolic" | "body" | "none";
1186
- type InterfaceDraftEntry = {
1187
- fn: string;
1188
- /** 全部形参(含无证据槽,便于人读) */
1189
- params: Array<{
1190
- name: string;
1191
- constraint?: NudoConstraint;
1192
- display: string;
1193
- /** false = 无证据,DSL 对象里省略该槽 */
1194
- projected: boolean;
1195
- /** 函数体读到的字段名(草稿建议;与 projected 无关) */
1196
- bodyAccesses?: string[];
1197
- }>;
1198
- returns?: {
1199
- constraint?: NudoConstraint;
1200
- display: string;
1201
- projected: boolean;
1202
- };
1203
- paramEvidence: DraftEvidence;
1204
- returnEvidence: DraftEvidence;
1205
- skipped?: "handwritten" | "not-an-export";
1206
- /** 草稿 DSL:`fn({ … }, …)`;handwritten / 非导出时 undefined */
1207
- dsl?: string;
1208
- };
1209
- type InterfaceDraftOpts = {
1210
- fnNames?: string[];
1211
- records?: CallRecord[];
1212
- loadModule?: LoadModule;
1213
- /**
1214
- * Accepted for API compatibility; **ignored**. The handwritten-contract probe
1215
- * always uses `effectiveInterface(..., { autoBind: true })` so draft skips
1216
- * disk sidecars even when ambient autoBind is off (product: draft asks
1217
- * "does a contract already exist?", not "is ambient binding on?").
1218
- */
1219
- autoBind?: boolean;
1220
- /** 默认 true:收集 body 成员读取作草稿建议(永不进 check) */
1221
- bodyAccesses?: boolean;
1222
- /** 打开 buffer 源(E5:与 hover/interface 同口径) */
1223
- source?: string;
1224
- };
1225
- type InterfaceDraftResult = {
1226
- file: string;
1227
- entries: InterfaceDraftEntry[];
1228
- draftSource: string;
1229
- sidecarPath: string;
1230
- };
1231
- /**
1232
- * Draft-only:收集每个顶层函数形参上的成员读取键(`user.name` → name)。
1233
- * **不是** C0 禁止的 body→义务通道——只进草稿注释/建议,check 不读此表。
1234
- */
1235
- declare function collectParamBodyAccesses(source: string): Map<string, Map<string, Set<string>>>;
1236
- /**
1237
- * 为单文件顶层导出生成 interface 草稿(不写盘)。
1238
- * handwritten 跳过;已有 @generated 仍出草稿(便于对照)。
1239
- */
1240
- declare function draftInterface(filePath: string, opts?: InterfaceDraftOpts): Promise<InterfaceDraftResult>;
1241
- /** 草稿模块文本(人读 + 可复制到 *.nudo.js / *.nudo.ts) */
1242
- declare function formatDraftModule(filePath: string, entries: InterfaceDraftEntry[], sidecarPath?: string): string;
1243
- /** `lib.js|ts` → `lib.nudo.draft.js|ts`(不进 ambient sidecar 表) */
1244
- declare function sidecarDraftPath(filePath: string): string;
1245
- type WriteDraftResult = {
1246
- draftPath: string;
1247
- written: boolean;
1248
- changed: boolean;
1249
- draftable: boolean;
1250
- draftSource: string;
1251
- };
1252
- /** Parse-layer draftable: at least one entry has generated DSL and was not skipped */
1253
- declare function isDraftableEntry(entries: ReadonlyArray<Pick<InterfaceDraftEntry, "dsl" | "skipped">>): boolean;
1254
- /**
1255
- * 写入 `*.nudo.draft.js`(覆盖草稿文件本身;不碰正式 `*.nudo.js`)。
1256
- * 基础路径防护:拒绝 node_modules;拒绝 draft 路径与正式侧车重合;
1257
- * 拒绝 draft 路径落在源文件目录之外的穿越。
454
+ * 规则:.js / .mjs / .ts 可作为推断目标;其余扩展(.cjs/.tsx/.jsx/.mts/.d.ts…)
455
+ * 一律 false。显式排除:
456
+ * - .d.ts —— 类型声明文件(harvester 的输入),不是可求值的实现源码;
457
+ * - .tsx / .jsx —— JSX 构造的求值超出 nudo 推断器范围;
458
+ * - *.nudo.js / *.nudo.mjs / *.nudo.ts —— 侧车契约模块(interface 绑定源),
459
+ * 不是源码推断目标;目录级 check/infer/doctor 展开不得把它们当实现分析;
460
+ * - *.nudo.draft.js / *.nudo.draft.mjs / *.nudo.draft.ts —— draft 产物,
461
+ * 明确不被 ambient 绑定,也不得当实现源码分析。
1258
462
  *
1259
- * `opts.entries` / `opts.draftable`:解析层判定(优先)。缺省时用 Unicode
1260
- * 感知 export 正则兜底——`\w` 会静默丢掉 `计算` 这类标识符。
463
+ * 消费方:CLI 的 collectNudoFiles/watch 过滤/doctor 目录展开(.ts 放开后统一
464
+ * 走本函数,避免各处手写 endsWith 漂移),以及 LSP 的 isNudoFile(接线由
465
+ * LSP 侧负责)。
1261
466
  */
1262
- declare function writeInterfaceDraft(filePath: string, draftSource: string, opts?: {
1263
- dryRun?: boolean;
1264
- projectDir?: string;
1265
- entries?: ReadonlyArray<Pick<InterfaceDraftEntry, "dsl" | "skipped">>;
1266
- draftable?: boolean;
1267
- }): WriteDraftResult;
1268
- declare function formatDraftSummary(sourceRel: string, draftRel: string, result: InterfaceDraftResult, write?: WriteDraftResult): string[];
467
+ declare function isNudoTargetPath(path: string): boolean;
1269
468
 
1270
- /**
1271
- * Root 驱动的契约下行(design-refine-derivation §4.2 / §11 Phase 2)。
1272
- *
1273
- * 对含手写契约根的文件(lib.nudo.js 绑定 add4):
1274
- * 1. 从侧车 AST 抽出参数约束的源表达式(`positive` ← `./std.nudo.js`);
1275
- * 2. constraintToEntryAbs + tagDerivationRoot;
1276
- * 3. analyzeFn 求值 body(模块图注入下游),收集调用记录 + 推导打点;
1277
- * 4. 闭包内下游导出投影为组合式 DSL,禁止事后从 Abs 反编译链。
1278
- *
1279
- * check 分轨:每条 root 链独立;join 只用于工件聚合(多调用者)。
1280
- */
469
+ /** Formal sidecar contracts only — drafts never ambient-bind and need not reanalyze */
470
+ declare function isSidecarPath(path: string): boolean;
471
+ declare function isProjectConfigPath(path: string): boolean;
472
+ /** Watch accept gate: analysis targets + sidecar/config/env-template invalidators */
473
+ declare function isWatchRelevantPath(path: string): boolean;
474
+ /** `lib.nudo.js|ts` → candidate ambient sources next to it */
475
+ declare function ambientSourcesOfSidecar(sidecarPath: string): string[];
1281
476
 
1282
- type ConstraintSourceExpr = {
1283
- expr: string;
1284
- importFrom?: string;
1285
- importName?: string;
1286
- };
1287
- type DerivedParam = {
1288
- name: string;
1289
- constraint: NudoConstraint;
1290
- dsl: string;
1291
- prelude: string[];
1292
- imports: Array<{
1293
- name: string;
1294
- from: string;
1295
- }>;
1296
- /** 单链组合式投影时的推导图 root 节点 id(返回位相对锚定用) */
1297
- rootNodeId?: number;
1298
- /** 该参数位自 root 起消耗的 shift 步数(与 prelude 行数无关,读图) */
1299
- shiftCount?: number;
1300
- };
1301
- type DerivedExport = {
1302
- file: string;
1303
- fn: string;
1304
- paramNames: string[];
1305
- params: DerivedParam[];
1306
- returns?: {
1307
- constraint: NudoConstraint;
1308
- dsl: string;
1309
- prelude: string[];
1310
- imports: Array<{
1311
- name: string;
1312
- from: string;
1313
- }>;
1314
- };
1315
- /** `lib.js:add4` */
1316
- derivedFrom: string;
1317
- compositional: boolean;
1318
- underivable?: boolean;
1319
- };
1320
- type RootDeriveOpts = {
1321
- loadModule?: LoadModule;
1322
- autoBind?: boolean;
1323
- fnNames?: string[];
1324
- /**
1325
- * true:只刷新目标侧车里**已存在**的 @generated 段(CLI 无 --fn/--all 时
1326
- * 的默认行为——不发明新下游契约)。false/省略:闭包内全部可推导导出。
1327
- */
1328
- refreshExistingOnly?: boolean;
1329
- };
1330
- type RootDeriveResult = {
1331
- roots: string[];
1332
- derived: DerivedExport[];
1333
- hasRoot: boolean;
1334
- };
1335
- /** 从侧车源码抽出 `export const f = fn({ x: positive }, ret)` 的约束源表达式 */
1336
- declare function extractFnConstraintSources(sidecarSrc: string, fnName: string): {
1337
- params: Record<string, ConstraintSourceExpr>;
1338
- returns?: ConstraintSourceExpr;
1339
- };
1340
- /**
1341
- * 入口:对 filePath 做 root 驱动下行推导。
1342
- */
1343
- declare function deriveFromRoot(filePath: string, opts?: RootDeriveOpts): RootDeriveResult;
1344
- /**
1345
- * 组装生成段(组合式 + import + prelude)。
1346
- * importFrom 相对 root 侧车解析,再相对 target 侧车写出。
1347
- *
1348
- * `takenNames`:侧车顶层已占用标识符(手写 / 既有生成段 / 本批先前段)。
1349
- * 同文件多导出时对 import local 与 prelude local 做避让改写,
1350
- * 保证拼出的侧车在模块作用域内无重复声明。
1351
- */
1352
- declare function formatDerivedSection(row: DerivedExport, opts: {
1353
- rootSidecarDir: string;
1354
- targetSidecarDir: string;
1355
- takenNames?: Iterable<string>;
1356
- }): {
1357
- text: string;
1358
- usedNames: string[];
1359
- } | undefined;
1360
- type EmitDerivedResult = {
1361
- /** 被写/将写的下游侧车(绝对路径) */
1362
- sidecars: Array<{
1363
- file: string;
1364
- sidecarPath: string;
1365
- fn: string;
1366
- written: boolean;
1367
- changed: boolean;
1368
- skipped?: "name-clash" | "not-projectable" | "underivable" | "no-change";
1369
- diff?: string;
1370
- issues: Array<{
1371
- code: string;
1372
- severity: "error" | "warning" | "info";
1373
- message: string;
1374
- }>;
1375
- }>;
1376
- hasRoot: boolean;
1377
- roots: string[];
1378
- /** 根在别处 / 无契约根且无已存在生成段 → info */
1379
- entryOnly?: boolean;
1380
- };
1381
- declare function emitDerivedFromRoot(rootFile: string, opts: {
1382
- fnNames?: string[];
1383
- mode: "add" | "update";
1384
- dryRun?: boolean;
1385
- loadModule?: LoadModule;
1386
- autoBind?: boolean;
1387
- /** 只刷新目标侧车里已存在的 @generated 段(不发明新下游契约) */
1388
- refreshExistingOnly?: boolean;
1389
- }): EmitDerivedResult;
477
+ /** 测试/诊断:反向依赖驻留规模 */
478
+ declare function getEnvPathDepsSize(): number;
479
+ /** 源码里的 path-based load specs 解析为绝对路径后登记反向边 */
480
+ declare function noteEnvPathDeps(sourcePath: string, source: string): void;
481
+ /** 依赖该 env 模板的源文件列表 */
482
+ declare function envPathDependents(envPath: string): string[];
483
+ declare function clearEnvPathDeps(): void;
484
+ /** watch 门禁:env 模板变更必须可被接收(即便扩展名不进 isNudoTargetPath) */
485
+ declare function isEnvTemplatePath(path: string): boolean;
1390
486
 
1391
- export { ANALYSIS_ABI, type AbsGraphOptions, type AbsMockSeeds, type AbsModuleCacheEntry, type AbsModuleGraphResult, type AbsModuleLoadIssue, AnalysisConfig, type AnalysisResult, type AnalysisSession, type BPathBuiltinUnknown, type BPathDiagnostics, type BPathRunResult, type BPathUnreachable, type BindingInfo, type BuildSemanticTokensOpts, CallRecord, type CaseHint, type CaseInfo, type CaseResult, type CompletionItem, type ConstraintSourceExpr, type DepContent, type DerivedExport, type DerivedParam, type Diagnostic, type DiagnosticSeverity, type DiagnosticTag, DiagnosticsLevel, DiskCache, type DiskCacheOptions, type DraftEvidence, type EmitDerivedResult, type EmitInterfaceOpts, type EmitInterfaceResult, type EmitInterfaceSkipReason, type EmitResult, type EmitSkipReason, type EnvHarvestConflict, type FunctionAnalysis, HARVEST_NODE_DEFAULT_MAX_FILES, HARVEST_NODE_DEFAULT_MAX_MS, type HarvestNodeStats, type HoverInfo, type InferJson, type InferJsonCase, type InferJsonFunction, type InterfaceDraftEntry, type InterfaceDraftOpts, type InterfaceDraftResult, type InterfaceSurfaceEntry, type InterfaceSurfaceOpts, type LoadModule, type MergeHarvestOptions, type ModuleExports, type ModuleGraphCache, type NodeEnvResult, type PackageHarvest, type ReferenceInfo, type RootDeriveOpts, type RootDeriveResult, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES, type SemanticToken, type SourceLocation, type SymbolInfo, type SymbolTable, type WriteDraftResult, absToTSType, absToZodSchema, ambientSourcesOfSidecar, analysisFileCacheKey, analyzeExportsFromSource, analyzeFile, analyzeFileAsync, autoHarvestModules, barePackageName, bareSpecToAbsModules, buildCaseDirective, buildModuleGraph, buildSemanticTokens, checkCacheKey, clearAbsModuleCache, clearAnalysisFileCache, clearAnalysisSessionCaches, clearBPathCache, clearEnvPathDeps, clearFnAnalysisCache, clearHarvestCache, clearNodeHarvestCache, collectAbsBindingsFromGraph, collectBPathDiagnostics, collectBPathReplacements, collectBarePackages, collectCallRecords, collectDependencySpecs, collectDtsFromEntry, collectEnvGlobals, collectEnvModules, collectLoadDepContents, collectParamBodyAccesses, collectStaticImports, computeDirtySet, defaultAbsLoadModule, defaultLoadModule, deriveFromRoot, diagnosticsLevelForFile, draftInterface, emitDerivedFromRoot, emitInterface, encodeSemanticTokens, envPathDependents, evalAbsModuleGraph, evalProgramAbsWithModules, evictAbsModuleCacheFiles, evictAnalysisCachesForFiles, evictAnalysisFileCacheForFiles, evictBPathCacheForFiles, evictFnAnalysisCacheForFiles, extractFnConstraintSources, extractNudoImportSpecs, filterDiagnosticsByLevel, formatDerivedSection, formatDraftModule, formatDraftSummary, formatEmitSummary, formatHarvestSummary, formatInterfaceSurfaceLine, generateDts, generateFunctionDtsLines, generateGuardFunction, generateGuardFunctionFromAbs, getAbsAtPosition, getAbsAtPositionAsync, getAnalysisFileCacheSize, getAnalysisSession, getCasesForFile, getCompletionsAtPosition, getEnvHarvestConflictCollector, getHoverAtPosition, getNodeHarvestCacheSize, getTypeAtPosition, getTypeAtPositionAsync, harvestNodeTypes, harvestPackage, harvestPackageCached, harvestToAbsModules, harvestedValueToAbs, ifaceCacheKey, insertGeneratedCaseDirectives, interfaceSurface, interfaceTierModifierBit, isBPathCapable, isDraftableEntry, isEnvTemplatePath, isHarvestNodeDisabled, isNudoTargetPath, isProjectConfigPath, isSidecarPath, isWatchRelevantPath, lookupHarvested, mergeHarvestUnderEnv, mockDirectivesToAbsSeeds, noteEnvPathDeps, packageHarvestToAbsModules, relativizePath, resetAllAnalysisCaches, resolvePackageRoot, serializeCaseArg, serializeInferJson, setAnalysisSession, setEnvHarvestConflictCollector, sha256Hex, shouldAnalyzeFile, sidecarDraftPath, hasNudoDirectives as sourceHasNudoDirectives, stripGeneratedCaseDirectives, summarizeNodeEnv, topoSortDirty, tryBPathCall, tryBPathCallFull, tryRunBPath, unifiedDiff, writeInterfaceDraft };
487
+ export { ANALYSIS_ABI, type AbsMockSeeds, AnalysisResult, type AnalysisSession, type BPathBuiltinUnknown, type BPathDiagnostics, type BPathUnreachable, CallRecord, DEFAULT_SESSION_CACHE_LIMITS, type DepContent, DirectiveCaseMode, DiskCache, type DiskCacheOptions, type DualEntryInfo, type DualEntryIssue, LoadModule, type MockModuleApplyResult, type ModuleExports, NudoConfig, type SessionCacheLimits, ambientSourcesOfSidecar, analysisFileCacheKey, analyzeExportsFromSource, applyMockModuleDirectives, applyMockModuleDirectivesFromSource, applySessionCacheConfig, checkCacheKey, clearAnalysisFileCache, clearAnalysisSessionCaches, clearEnvPathDeps, clearFnAnalysisCache, collectBPathDiagnostics, collectDependencySpecs, collectLoadDepContents, collectSkipReturns, collectStaticImports, detectDualEntryFromPackageJson, dualEntryForFile, dualEntryIssueForFile, envPathDependents, evictAnalysisCachesForFiles, evictAnalysisFileCacheForFiles, evictFnAnalysisCacheForFiles, extractNudoImportSpecs, findOwningPackage, getAnalysisFileCacheSize, getAnalysisSession, getEnvPathDepsSize, getFnAnalysisCacheSize, getSessionCacheLimits, ifaceCacheKey, isEnvTemplatePath, isNudoTargetPath, isProjectConfigPath, isSidecarPath, isWatchRelevantPath, mockDirectivesToAbsSeeds, mockSeedsForSource, mockSeedsToAbsMocks, noteEnvPathDeps, relativizePath, resetAllAnalysisCaches, resetSessionCacheLimitState, setAnalysisSession, setSessionCacheFromProject, setSessionCacheLimits, sha256Hex, trimAnalysisFileCache, trimFnAnalysisCache };