@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.
@@ -225,8 +225,246 @@ function builtinProtoMember(className, member) {
225
225
  return table[member] ?? null;
226
226
  }
227
227
 
228
+ // src/evaluator/env-loader.ts
229
+ import { readFileSync, existsSync, statSync, mkdirSync, writeFileSync } from "fs";
230
+ import { resolve as resolvePath, join as joinPath } from "path";
231
+ import { pathToFileURL } from "url";
232
+ import { createRequire } from "module";
233
+ import { createHash } from "crypto";
234
+ import { tmpdir } from "os";
235
+ import { defineEnv as defineEsEnv } from "@nudojs/env/es";
236
+ import { defineEnv as defineWebEnv } from "@nudojs/env/web";
237
+ import { defineEnv as defineNodeEnv } from "@nudojs/env/node";
238
+
239
+ // src/lru-map.ts
240
+ var BoundedLruMap = class {
241
+ map = /* @__PURE__ */ new Map();
242
+ max;
243
+ constructor(max) {
244
+ this.max = max;
245
+ }
246
+ get size() {
247
+ return this.map.size;
248
+ }
249
+ /** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
250
+ getMax() {
251
+ return this.max;
252
+ }
253
+ setMax(max) {
254
+ this.max = max;
255
+ this.trim();
256
+ }
257
+ has(key) {
258
+ return this.map.has(key);
259
+ }
260
+ get(key) {
261
+ const hit = this.map.get(key);
262
+ if (hit === void 0) return void 0;
263
+ this.map.delete(key);
264
+ this.map.set(key, hit);
265
+ return hit;
266
+ }
267
+ /** 命中不改变 LRU 序(只读探测) */
268
+ peek(key) {
269
+ return this.map.get(key);
270
+ }
271
+ set(key, value) {
272
+ if (this.max <= 0) return;
273
+ while (this.map.size >= this.max && !this.map.has(key)) {
274
+ const oldest = this.map.keys().next().value;
275
+ if (oldest === void 0) break;
276
+ this.map.delete(oldest);
277
+ }
278
+ this.map.delete(key);
279
+ this.map.set(key, value);
280
+ }
281
+ delete(key) {
282
+ return this.map.delete(key);
283
+ }
284
+ clear() {
285
+ this.map.clear();
286
+ }
287
+ keys() {
288
+ return this.map.keys();
289
+ }
290
+ values() {
291
+ return this.map.values();
292
+ }
293
+ entries() {
294
+ return this.map.entries();
295
+ }
296
+ /** 压到当前 max(调低上限时收内存) */
297
+ trim() {
298
+ while (this.map.size > this.max) {
299
+ const oldest = this.map.keys().next().value;
300
+ if (oldest === void 0) break;
301
+ this.map.delete(oldest);
302
+ }
303
+ }
304
+ };
305
+
306
+ // src/evaluator/env-loader.ts
307
+ var envFactories = {
308
+ es: defineEsEnv,
309
+ web: defineWebEnv,
310
+ node: defineNodeEnv
311
+ };
312
+ var impliedDeps = {
313
+ web: ["es"],
314
+ node: ["es"]
315
+ };
316
+ function resolveEnvNames(names) {
317
+ const resolved = /* @__PURE__ */ new Set();
318
+ const visit = (name) => {
319
+ if (resolved.has(name)) return;
320
+ const deps = impliedDeps[name];
321
+ if (deps) deps.forEach(visit);
322
+ resolved.add(name);
323
+ };
324
+ names.forEach(visit);
325
+ return [...resolved];
326
+ }
327
+ var PATH_ENV_CACHE_MAX = 64;
328
+ var PATH_ENV_BY_PATH_MAX = 64;
329
+ var PATH_ENV_BASE_DIRS_MAX = 64;
330
+ var pathEnvCache = new BoundedLruMap(PATH_ENV_CACHE_MAX);
331
+ var pathEnvByPath = new BoundedLruMap(PATH_ENV_BY_PATH_MAX);
332
+ var pathEnvBaseDirs = /* @__PURE__ */ new Set();
333
+ function addPathEnvBaseDir(baseDir) {
334
+ pathEnvBaseDirs.delete(baseDir);
335
+ pathEnvBaseDirs.add(baseDir);
336
+ while (pathEnvBaseDirs.size > PATH_ENV_BASE_DIRS_MAX) {
337
+ const oldest = pathEnvBaseDirs.values().next().value;
338
+ if (oldest === void 0) break;
339
+ pathEnvBaseDirs.delete(oldest);
340
+ }
341
+ }
342
+ function getPathEnvCacheSizes() {
343
+ return {
344
+ byKey: pathEnvCache.size,
345
+ byPath: pathEnvByPath.size,
346
+ baseDirs: pathEnvBaseDirs.size
347
+ };
348
+ }
349
+ function isPathEnvName(name, baseDir) {
350
+ if (name in envFactories) return false;
351
+ if (name.includes("/") || name.startsWith("./") || name.startsWith("../")) return true;
352
+ const resolved = resolvePath(baseDir, name);
353
+ return resolved.endsWith(".ts") && existsSync(resolved);
354
+ }
355
+ function rewriteBareImports(text) {
356
+ const require2 = createRequire(import.meta.url);
357
+ let rewrote = false;
358
+ const out = text.replace(/(["'])(@nudojs\/[a-z0-9-]+)\1/g, (_m, quote, spec) => {
359
+ try {
360
+ const entry = require2.resolve(spec);
361
+ const url = pathToFileURL(entry).href;
362
+ rewrote = true;
363
+ return `${quote}${url}${quote}`;
364
+ } catch {
365
+ return `${quote}${spec}${quote}`;
366
+ }
367
+ });
368
+ return rewrote ? out : null;
369
+ }
370
+ async function importPathEnv(resolvedPath, mtimeMs) {
371
+ const cacheKey = `${resolvedPath}:${mtimeMs}`;
372
+ if (pathEnvCache.get(cacheKey)) return;
373
+ let mod = null;
374
+ try {
375
+ const url = pathToFileURL(resolvedPath).href + `?mtime=${mtimeMs}`;
376
+ mod = await import(url);
377
+ } catch {
378
+ mod = null;
379
+ }
380
+ if (!mod) {
381
+ try {
382
+ const text = readFileSync(resolvedPath, "utf-8");
383
+ const rewritten = rewriteBareImports(text);
384
+ if (rewritten === null) return;
385
+ const cacheDir = joinPath(tmpdir(), "nudo-env");
386
+ mkdirSync(cacheDir, { recursive: true });
387
+ const hash = createHash("md5").update(`${resolvedPath}:${mtimeMs}`).digest("hex").slice(0, 16);
388
+ const copyPath = joinPath(cacheDir, `${hash}.ts`);
389
+ writeFileSync(copyPath, rewritten, "utf-8");
390
+ mod = await import(pathToFileURL(copyPath).href);
391
+ } catch {
392
+ return;
393
+ }
394
+ }
395
+ if (mod && typeof mod.defineEnv === "function") {
396
+ pathEnvCache.set(cacheKey, mod.defineEnv);
397
+ pathEnvByPath.set(resolvedPath, {
398
+ factory: mod.defineEnv,
399
+ mtimeMs
400
+ });
401
+ }
402
+ }
403
+ function lookupPathEnv(name) {
404
+ if (pathEnvByPath.size === 0) return void 0;
405
+ const candidates = name.startsWith("/") ? [name] : [name, ...[...pathEnvBaseDirs].map((d) => resolvePath(d, name))];
406
+ for (const candidate of candidates) {
407
+ const hit = pathEnvByPath.get(candidate);
408
+ if (!hit) continue;
409
+ try {
410
+ const { mtimeMs } = statSync(candidate);
411
+ if (mtimeMs !== hit.mtimeMs) {
412
+ pathEnvByPath.delete(candidate);
413
+ return void 0;
414
+ }
415
+ } catch {
416
+ pathEnvByPath.delete(candidate);
417
+ return void 0;
418
+ }
419
+ return hit.factory;
420
+ }
421
+ return void 0;
422
+ }
423
+ function clearPathEnvCaches() {
424
+ pathEnvCache.clear();
425
+ pathEnvByPath.clear();
426
+ pathEnvBaseDirs.clear();
427
+ }
428
+ async function preloadPathEnvs(envNames, baseDir) {
429
+ addPathEnvBaseDir(baseDir);
430
+ for (const name of envNames) {
431
+ if (!isPathEnvName(name, baseDir)) continue;
432
+ const resolved = resolvePath(baseDir, name);
433
+ if (!existsSync(resolved)) continue;
434
+ try {
435
+ const { mtimeMs } = statSync(resolved);
436
+ await importPathEnv(resolved, mtimeMs);
437
+ } catch {
438
+ }
439
+ }
440
+ }
441
+ async function loadEnvsAsync(envNames, globalEnv, baseDir = process.cwd()) {
442
+ await preloadPathEnvs(envNames, baseDir);
443
+ return loadEnvs(envNames, globalEnv);
444
+ }
445
+ function loadEnvs(envNames, globalEnv) {
446
+ const allModules = {};
447
+ const allGlobals = {};
448
+ const resolved = resolveEnvNames(envNames);
449
+ for (const name of resolved) {
450
+ const factory = envFactories[name] ?? lookupPathEnv(name);
451
+ if (!factory) continue;
452
+ const def = factory();
453
+ for (const [key, value] of Object.entries(def.globals)) {
454
+ allGlobals[key] = value;
455
+ globalEnv.bind(key, value);
456
+ }
457
+ if (def.modules) {
458
+ for (const [modName, exports] of Object.entries(def.modules)) {
459
+ allModules[modName] = { ...allModules[modName], ...exports };
460
+ }
461
+ }
462
+ }
463
+ return { modules: allModules, globals: allGlobals };
464
+ }
465
+
228
466
  // src/evaluator/config.ts
229
- import { readFileSync, existsSync } from "fs";
467
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
230
468
  import { resolve, dirname, relative, sep } from "path";
231
469
 
232
470
  // src/session-cache-limits.ts
@@ -414,9 +652,9 @@ function findProjectConfig(startDir) {
414
652
  const root = resolve("/");
415
653
  while (dir !== root) {
416
654
  const pkgPath = resolve(dir, "package.json");
417
- if (existsSync(pkgPath)) {
655
+ if (existsSync2(pkgPath)) {
418
656
  try {
419
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
657
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
420
658
  if (pkg.nudo) {
421
659
  const nudo = pkg.nudo;
422
660
  setSessionCacheFromProject(nudo.sessionCache);
@@ -435,14 +673,14 @@ function findProjectConfig(startDir) {
435
673
  }
436
674
 
437
675
  // src/evaluator/resolve-npm.ts
438
- import { readFileSync as readFileSync2, existsSync as existsSync2, statSync } from "fs";
676
+ import { readFileSync as readFileSync3, existsSync as existsSync3, statSync as statSync2 } from "fs";
439
677
  import { resolve as resolve2, join, dirname as dirname2 } from "path";
440
678
  function findNodeModules(startDir) {
441
679
  let dir = resolve2(startDir);
442
680
  const root = resolve2("/");
443
681
  while (dir !== root) {
444
682
  const nmPath = join(dir, "node_modules");
445
- if (existsSync2(nmPath)) return nmPath;
683
+ if (existsSync3(nmPath)) return nmPath;
446
684
  const parent = dirname2(dir);
447
685
  if (parent === dir) break;
448
686
  dir = parent;
@@ -460,11 +698,11 @@ function resolveNpmJsEntry(source, fromDir) {
460
698
  if (!nodeModules) return null;
461
699
  const pkgDir = join(nodeModules, pkgName);
462
700
  const pkgJsonPath = join(pkgDir, "package.json");
463
- if (!existsSync2(pkgJsonPath)) return null;
701
+ if (!existsSync3(pkgJsonPath)) return null;
464
702
  const tryFile = (p) => {
465
703
  for (const c of [p, `${p}.js`, `${p}.cjs`, `${p}.mjs`, join(p, "index.js"), join(p, "index.cjs")]) {
466
704
  try {
467
- if (existsSync2(c) && statSync(c).isFile()) return c;
705
+ if (existsSync3(c) && statSync2(c).isFile()) return c;
468
706
  } catch {
469
707
  }
470
708
  }
@@ -472,7 +710,7 @@ function resolveNpmJsEntry(source, fromDir) {
472
710
  };
473
711
  let pkg = {};
474
712
  try {
475
- pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf-8"));
713
+ pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
476
714
  } catch {
477
715
  return null;
478
716
  }
@@ -534,13 +772,13 @@ function resolveNpmNudo(source, fromDir) {
534
772
  const nodeModules = findNodeModules(fromDir);
535
773
  if (!nodeModules) return null;
536
774
  const pkgJsonPath = join(nodeModules, pkgName, "package.json");
537
- if (!existsSync2(pkgJsonPath)) return null;
775
+ if (!existsSync3(pkgJsonPath)) return null;
538
776
  try {
539
- const pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf-8"));
777
+ const pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
540
778
  const nudoEntry = resolveExportsNudo(pkg.exports, subpath);
541
779
  if (nudoEntry) {
542
780
  const resolved = resolve2(dirname2(pkgJsonPath), nudoEntry);
543
- if (existsSync2(resolved)) return resolved;
781
+ if (existsSync3(resolved)) return resolved;
544
782
  }
545
783
  } catch {
546
784
  }
@@ -554,6 +792,12 @@ export {
554
792
  describeAbsMember,
555
793
  builtinProtoMemberNames,
556
794
  builtinProtoMember,
795
+ BoundedLruMap,
796
+ getPathEnvCacheSizes,
797
+ clearPathEnvCaches,
798
+ preloadPathEnvs,
799
+ loadEnvsAsync,
800
+ loadEnvs,
557
801
  DEFAULT_SESSION_CACHE_LIMITS,
558
802
  getSessionCacheLimits,
559
803
  setSessionCacheLimits,
@@ -1,13 +1,9 @@
1
- import { C as CallRecord } from './call-record-CkyCFkT9.js';
2
- import { NudoConstraint } from '@nudojs/core';
3
-
4
- /**
5
- * 相对/绝对 specifier → 模块源码。
6
- * CLI / LSP / vite / check 共用同一扩展名表,避免门禁结果分叉。
7
- */
8
- type LoadModule = (spec: string, fromFile: string) => string | undefined;
9
- /** 默认 loadModule:支持 .js/.mjs/.ts 与 index 入口 */
10
- declare function defaultLoadModule(spec: string, fromFile: string): string | undefined;
1
+ import { C as CallRecord } from '../call-record-CkyCFkT9.js';
2
+ import { L as LoadModule } from '../what-if-mIo7I5Ii.js';
3
+ export { T as TypeBinding, i as injectBindings, t as typeExprToDirective } from '../what-if-mIo7I5Ii.js';
4
+ import { NudoConstraint, Abs } from '@nudojs/core';
5
+ import { A as AnalysisResult, F as FunctionAnalysis, S as SourceLocation } from '../analyzer-types-CfBrPK9L.js';
6
+ import '@babel/types';
11
7
 
12
8
  /**
13
9
  * `nudo contract` 的打印数据源(design-refine-derivation §11 第 0 步):
@@ -352,16 +348,260 @@ declare function emitDerivedFromRoot(rootFile: string, opts: {
352
348
  refreshExistingOnly?: boolean;
353
349
  }): EmitDerivedResult;
354
350
 
355
- type TypeBinding = {
351
+ /**
352
+ * Abs → TS 类型串。有损:pred / 非 lit term 落到 shape 基类型。
353
+ * `typeVars`:term var id → TS 类型参数名(HOF 泛型投影时传入;
354
+ * `any`+var 在此映射下渲染为该参数名,否则 `unknown`)。
355
+ */
356
+ declare function absToTSType(a: Abs, typeVars?: Map<string, string>): string;
357
+ /**
358
+ * 为单个函数生成 .d.ts 声明行(JSDoc + 单一 widen 主签名)。
359
+ * 有 hof 关系时优先泛型投影(C3.3);否则 case-widen。
360
+ * service 的 generateDts 与 CLI `--dts`(infer/watch)共用本函数,
361
+ * 两条路径输出保持一致。
362
+ */
363
+ declare function generateFunctionDtsLines(fn: FunctionAnalysis): string[];
364
+ declare function generateDts(result: AnalysisResult): string;
365
+
366
+ /**
367
+ * Abs → 生态 schema 投影(单向有损)。
368
+ *
369
+ * 优先走 core `absToConstraint`(与 interface/check 同一投影语义),
370
+ * 再转成中间层 SchemaNode;投影失败时退回 shape 尽力提取并记 dropped。
371
+ * Dialect 只负责 Node → 源码字符串;Abs 才是真理源。
372
+ */
373
+
374
+ type SchemaDialect = "zod";
375
+ type SchemaRefinement = {
376
+ kind: "numBound";
377
+ op: "gt" | "ge" | "lt" | "le";
378
+ n: number;
379
+ } | {
380
+ kind: "int";
381
+ } | {
382
+ kind: "strMin";
383
+ n: number;
384
+ } | {
385
+ kind: "strMax";
386
+ n: number;
387
+ };
388
+ type SchemaNode = {
389
+ k: "lit";
390
+ value: string | number | boolean | null | undefined;
391
+ } | {
392
+ k: "prim";
393
+ type: "number" | "string" | "boolean" | "bigint" | "symbol";
394
+ refinements: SchemaRefinement[];
395
+ } | {
396
+ k: "obj";
397
+ slots: Array<{
398
+ key: string;
399
+ node: SchemaNode;
400
+ optional?: boolean;
401
+ }>;
402
+ } | {
403
+ k: "arr";
404
+ element: SchemaNode;
405
+ } | {
406
+ k: "tuple";
407
+ elements: SchemaNode[];
408
+ } | {
409
+ k: "union";
410
+ members: SchemaNode[];
411
+ } | {
412
+ k: "fn";
413
+ } | {
414
+ k: "promise";
415
+ inner: SchemaNode;
416
+ } | {
417
+ k: "brand";
356
418
  name: string;
357
- type: string;
419
+ } | {
420
+ k: "never";
421
+ } | {
422
+ k: "unknown";
423
+ };
424
+ type SchemaProjection = {
425
+ source: string;
426
+ dialect: SchemaDialect;
427
+ dropped: string[];
358
428
  };
359
- /** agent 面类型表达式 → `@nudo:as` 文法 */
360
- declare function typeExprToDirective(expr: string): string;
361
- declare function injectBindings(source: string, bindings: TypeBinding[]): {
429
+ /** NudoConstraint → SchemaNode(与 absToConstraint 投影语义对齐) */
430
+ declare function constraintToSchemaNode(c: NudoConstraint): SchemaNode;
431
+ /** Abs → SchemaNode + dropped(优先 core absToConstraint;失败则 shape 尽力) */
432
+ declare function absToSchemaNode(a: Abs): {
433
+ node: SchemaNode;
434
+ dropped: string[];
435
+ };
436
+ declare function schemaNodeToZod(node: SchemaNode): string;
437
+ declare function projectAbsToSchema(a: Abs, opts?: {
438
+ dialect?: SchemaDialect;
439
+ }): SchemaProjection;
440
+ declare function absToSchemaSource(a: Abs, opts?: {
441
+ dialect?: SchemaDialect;
442
+ }): string;
443
+
444
+ /**
445
+ * Abs / SchemaNode → Standard Schema v1 运行时模块(单向有损投影)。
446
+ *
447
+ * 产物是零第三方依赖的 TS/JS 模块:每个导出实现 `~standard` Props
448
+ * (version=1, vendor="nudo", validate)。validate 对 SchemaNode 做
449
+ * 结构 + 可表达 refinement 执法;落不了的 pred 不在此假装精确。
450
+ *
451
+ * 协议:https://standardschema.dev —— 运行时互操作出口,不替代 nudo check。
452
+ */
453
+
454
+ type StandardSchemaIssue = {
455
+ message: string;
456
+ path?: ReadonlyArray<PropertyKey>;
457
+ };
458
+ type StandardSchemaResult = {
459
+ value: unknown;
460
+ issues?: undefined;
461
+ } | {
462
+ issues: ReadonlyArray<StandardSchemaIssue>;
463
+ value?: undefined;
464
+ };
465
+ /** SchemaNode 同步校验(生成模块与测试共用语义)。 */
466
+ declare function validateSchemaNode(node: SchemaNode, value: unknown): StandardSchemaResult;
467
+ type StandardSchemaModuleProjection = {
362
468
  source: string;
363
- applied: string[];
364
- unapplied: string[];
469
+ dropped: string[];
365
470
  };
471
+ /**
472
+ * Abs → Standard Schema v1 模块源码。
473
+ * `exports`:导出名 → Abs(通常是 output / 各参数)。
474
+ */
475
+ declare function absToStandardSchemaModule(exports: Record<string, Abs>, opts?: {
476
+ banner?: string;
477
+ }): StandardSchemaModuleProjection;
478
+ /** 便捷:单个 Abs → 单导出模块(默认导出名 `schema`)。 */
479
+ declare function absToStandardSchema(a: Abs, opts?: {
480
+ name?: string;
481
+ }): StandardSchemaModuleProjection;
482
+
483
+ /** Abs 指称守卫(设计 §2.7):保留 pred */
484
+ declare function generateGuardFunctionFromAbs(name: string, abs: Abs): string;
485
+ /** 兼容别名:Abs 路径唯一 */
486
+ declare function generateGuardFunction(name: string, abs: Abs): string;
487
+
488
+ /**
489
+ * Case facts JSON(`nudo test --json` 序列化)。
490
+ *
491
+ * - args / result / throws / combined:formatShape 外延投影(有损)
492
+ * 入口无约束参数展示为 any;unknown 仅表示推导失败
493
+ * - intension.abs*:无损 Abs 展示(主线)
494
+ * - argsAbs / resultAbs:CaseResult 上的无损 Abs(formatAbs);有则补齐
495
+ */
496
+
497
+ type CaseJsonCase = {
498
+ name: string;
499
+ /** formatShape 外延投影(有损) */
500
+ args: string[];
501
+ result: string;
502
+ throws: string | null;
503
+ source: string | null;
504
+ aggregatedFrom?: number;
505
+ /** 无损参数 Abs 展示(与 args 对齐;缺失位省略) */
506
+ argsAbs?: string[];
507
+ /** 无损结果 Abs 展示 */
508
+ resultAbs?: string;
509
+ /** 无损内涵(Abs);无则省略 */
510
+ intension?: {
511
+ display?: string;
512
+ abs?: string;
513
+ absMultiline?: string;
514
+ term?: string;
515
+ pred?: string;
516
+ conf?: string;
517
+ };
518
+ };
519
+ type CaseJsonFunction = {
520
+ name: string;
521
+ loc: SourceLocation;
522
+ entryOnly: boolean;
523
+ noDeclaration?: boolean;
524
+ cases: CaseJsonCase[];
525
+ combined?: string;
526
+ /** 无损 combined Abs 展示 */
527
+ combinedAbs?: string;
528
+ };
529
+ type CaseJson = {
530
+ version: 1;
531
+ file: string;
532
+ summary: {
533
+ functions: number;
534
+ externalFunctions: number;
535
+ cases: number;
536
+ diagnostics: number;
537
+ };
538
+ functions: CaseJsonFunction[];
539
+ externalFunctions?: Array<{
540
+ name: string;
541
+ fromModule?: string;
542
+ cases: CaseJsonCase[];
543
+ }>;
544
+ diagnostics: Array<{
545
+ range: SourceLocation;
546
+ severity: string;
547
+ message: string;
548
+ code?: string;
549
+ suggestions?: string[];
550
+ tags?: string[];
551
+ origin?: {
552
+ line: number;
553
+ column: number;
554
+ };
555
+ }>;
556
+ };
557
+ declare function serializeCaseJson(result: AnalysisResult, file: string): CaseJson;
558
+
559
+ /** 单个 Abs → parseCaseArgExpr 可解析回去的表达式文本;不可表达返回 null */
560
+ declare function serializeCaseArg(a: Abs): string | null;
561
+ /**
562
+ * 组装单行 ` * @nudo:case "name" (a, b)` 指令文本(无尾换行)。
563
+ * 任一实参不可序列化、或名字含双引号/换行(名字正则 `"([^"]+)"` 承载不了)→ 整体 null。
564
+ */
565
+ declare function buildCaseDirective(name: string, argsAbs: Abs[]): string | null;
566
+ /**
567
+ * 从源码剥离所有本模块生成的 @nudo:case 指令(名字以 call@ 开头,整行删除)。
568
+ * 若所属 JSDoc 块因此只剩空 ` *` 行(无其他 @nudo:* 指令、无文字内容),
569
+ * 连块首 `/**` 行与块尾行整块删除。绝不碰非 case 指令与普通注释。
570
+ * 注意:手写但以 call@ 命名的 case 同样会被删——call@ 前缀保留为生成物标记。
571
+ * removed 返回被删指令名列表(按出现顺序)。
572
+ */
573
+ declare function stripGeneratedCaseDirectives(source: string): {
574
+ source: string;
575
+ removed: string[];
576
+ };
577
+ type EmitSkipReason = "hand-written" | "already-generated" | "entry-only" | "no-serializable-cases" | "no-declaration" | "skipped";
578
+ type EmitResult = {
579
+ source: string;
580
+ changed: boolean;
581
+ written: Array<{
582
+ fn: string;
583
+ cases: string[];
584
+ }>;
585
+ skipped: Array<{
586
+ fn: string;
587
+ reason: EmitSkipReason;
588
+ detail?: string;
589
+ }>;
590
+ };
591
+ /**
592
+ * 把 analysis 中的合成 case(source === "callsite")固化为源码指令:
593
+ *
594
+ * 1. 源码已有任何非 call@ 命名的 case → skip "hand-written";
595
+ * 已有 call@ case → skip "already-generated"。
596
+ * 2. fn.skipped / fn.noDeclaration / fn.entryOnly → 对应 skip。
597
+ * 3. 逐 case 序列化;个别不可序列化的丢弃并在 skipped 记
598
+ * no-serializable-cases(函数整体仍写可序列化子集,全不可序列化才整函数跳过)。
599
+ * 4. 写入位置:函数声明行正上方——已有 JSDoc 块则插到 `/**` 行后,
600
+ * 无块则新建三行块。缩进取声明行的 loc.start.column(babel 0 基列)。
601
+ * 多函数编辑按行号从下往上应用,避免行号漂移。
602
+ */
603
+ declare function insertGeneratedCaseDirectives(source: string, analysis: AnalysisResult): EmitResult;
604
+ /** 行级 unified diff:`--- a/path` 头 + `@@` hunk + 上下文 3 行;相同返回 "" */
605
+ declare function unifiedDiff(a: string, b: string, path: string): string;
366
606
 
367
- export { typeExprToDirective as A, writeInterfaceDraft as B, type ConstraintSourceExpr as C, type DerivedExport as D, type EmitDerivedResult as E, type InterfaceDraftEntry as I, type LoadModule as L, type RootDeriveOpts as R, type TypeBinding as T, type WriteDraftResult as W, type DerivedParam as a, type DraftEvidence as b, type EmitInterfaceOpts as c, type EmitInterfaceResult as d, type EmitInterfaceSkipReason as e, type InterfaceDraftOpts as f, type InterfaceDraftResult as g, type InterfaceSurfaceEntry as h, type InterfaceSurfaceOpts as i, type RootDeriveResult as j, collectParamBodyAccesses as k, defaultLoadModule as l, deriveFromRoot as m, draftInterface as n, emitDerivedFromRoot as o, emitInterface as p, extractFnConstraintSources as q, formatDerivedSection as r, formatDraftModule as s, formatDraftSummary as t, formatEmitSummary as u, formatInterfaceSurfaceLine as v, injectBindings as w, interfaceSurface as x, isDraftableEntry as y, sidecarDraftPath as z };
607
+ export { type CaseJson, type CaseJsonCase, type CaseJsonFunction, type ConstraintSourceExpr, type DerivedExport, type DerivedParam, type DraftEvidence, type EmitDerivedResult, type EmitInterfaceOpts, type EmitInterfaceResult, type EmitInterfaceSkipReason, type EmitResult, type EmitSkipReason, type InterfaceDraftEntry, type InterfaceDraftOpts, type InterfaceDraftResult, type InterfaceSurfaceEntry, type InterfaceSurfaceOpts, type RootDeriveOpts, type RootDeriveResult, type SchemaDialect, type SchemaNode, type SchemaProjection, type SchemaRefinement, type StandardSchemaIssue, type StandardSchemaModuleProjection, type StandardSchemaResult, type WriteDraftResult, absToSchemaNode, absToSchemaSource, absToStandardSchema, absToStandardSchemaModule, absToTSType, buildCaseDirective, collectParamBodyAccesses, constraintToSchemaNode, deriveFromRoot, draftInterface, emitDerivedFromRoot, emitInterface, extractFnConstraintSources, formatDerivedSection, formatDraftModule, formatDraftSummary, formatEmitSummary, formatInterfaceSurfaceLine, generateDts, generateFunctionDtsLines, generateGuardFunction, generateGuardFunctionFromAbs, insertGeneratedCaseDirectives, interfaceSurface, isDraftableEntry, projectAbsToSchema, schemaNodeToZod, serializeCaseArg, serializeCaseJson, sidecarDraftPath, stripGeneratedCaseDirectives, unifiedDiff, validateSchemaNode, writeInterfaceDraft };