@nudojs/service 1.0.1 → 1.1.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.
@@ -235,6 +235,75 @@ import { tmpdir } from "os";
235
235
  import { defineEnv as defineEsEnv } from "@nudojs/env/es";
236
236
  import { defineEnv as defineWebEnv } from "@nudojs/env/web";
237
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
238
307
  var envFactories = {
239
308
  es: defineEsEnv,
240
309
  web: defineWebEnv,
@@ -255,9 +324,28 @@ function resolveEnvNames(names) {
255
324
  names.forEach(visit);
256
325
  return [...resolved];
257
326
  }
258
- var pathEnvCache = /* @__PURE__ */ new Map();
259
- var pathEnvByPath = /* @__PURE__ */ new Map();
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);
260
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
+ }
261
349
  function isPathEnvName(name, baseDir) {
262
350
  if (name in envFactories) return false;
263
351
  if (name.includes("/") || name.startsWith("./") || name.startsWith("../")) return true;
@@ -281,7 +369,7 @@ function rewriteBareImports(text) {
281
369
  }
282
370
  async function importPathEnv(resolvedPath, mtimeMs) {
283
371
  const cacheKey = `${resolvedPath}:${mtimeMs}`;
284
- if (pathEnvCache.has(cacheKey)) return;
372
+ if (pathEnvCache.get(cacheKey)) return;
285
373
  let mod = null;
286
374
  try {
287
375
  const url = pathToFileURL(resolvedPath).href + `?mtime=${mtimeMs}`;
@@ -306,7 +394,10 @@ async function importPathEnv(resolvedPath, mtimeMs) {
306
394
  }
307
395
  if (mod && typeof mod.defineEnv === "function") {
308
396
  pathEnvCache.set(cacheKey, mod.defineEnv);
309
- pathEnvByPath.set(resolvedPath, mod.defineEnv);
397
+ pathEnvByPath.set(resolvedPath, {
398
+ factory: mod.defineEnv,
399
+ mtimeMs
400
+ });
310
401
  }
311
402
  }
312
403
  function lookupPathEnv(name) {
@@ -314,12 +405,28 @@ function lookupPathEnv(name) {
314
405
  const candidates = name.startsWith("/") ? [name] : [name, ...[...pathEnvBaseDirs].map((d) => resolvePath(d, name))];
315
406
  for (const candidate of candidates) {
316
407
  const hit = pathEnvByPath.get(candidate);
317
- if (hit) return hit;
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;
318
420
  }
319
421
  return void 0;
320
422
  }
423
+ function clearPathEnvCaches() {
424
+ pathEnvCache.clear();
425
+ pathEnvByPath.clear();
426
+ pathEnvBaseDirs.clear();
427
+ }
321
428
  async function preloadPathEnvs(envNames, baseDir) {
322
- pathEnvBaseDirs.add(baseDir);
429
+ addPathEnvBaseDir(baseDir);
323
430
  for (const name of envNames) {
324
431
  if (!isPathEnvName(name, baseDir)) continue;
325
432
  const resolved = resolvePath(baseDir, name);
@@ -359,11 +466,146 @@ function loadEnvs(envNames, globalEnv) {
359
466
  // src/evaluator/config.ts
360
467
  import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
361
468
  import { resolve, dirname, relative, sep } from "path";
469
+
470
+ // src/session-cache-limits.ts
471
+ var DEFAULT_SESSION_CACHE_LIMITS = {
472
+ maxFiles: 64,
473
+ maxFns: 1024,
474
+ maxBRuns: 32
475
+ };
476
+ var HARD_CAP = 65536;
477
+ function clampEntries(n, fallback) {
478
+ if (typeof n !== "number" || !Number.isFinite(n)) return fallback;
479
+ return Math.max(0, Math.min(HARD_CAP, Math.floor(n)));
480
+ }
481
+ function parseLimit(raw, fallback) {
482
+ if (raw === void 0 || raw === "") return fallback;
483
+ if (raw === "off" || raw === "0") return 0;
484
+ const n = Number(raw);
485
+ return clampEntries(n, fallback);
486
+ }
487
+ var explicit = {};
488
+ var fromProject = {};
489
+ var envCache = null;
490
+ function envLimits(env) {
491
+ return {
492
+ maxFiles: parseLimit(env.NUDO_CACHE_MAX_FILES, NaN),
493
+ maxFns: parseLimit(env.NUDO_CACHE_MAX_FNS, NaN),
494
+ maxBRuns: parseLimit(env.NUDO_CACHE_MAX_BRUNS, NaN)
495
+ };
496
+ }
497
+ function pick(key, env, fallback) {
498
+ const e = env[key];
499
+ if (typeof e === "number" && Number.isFinite(e)) return e;
500
+ const x = explicit[key];
501
+ if (typeof x === "number" && Number.isFinite(x)) return clampEntries(x, fallback);
502
+ const p = fromProject[key];
503
+ if (typeof p === "number" && Number.isFinite(p)) return clampEntries(p, fallback);
504
+ return fallback;
505
+ }
506
+ function getSessionCacheLimits(env = process.env) {
507
+ if (envCache === null) envCache = envLimits(env);
508
+ return {
509
+ maxFiles: pick("maxFiles", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxFiles),
510
+ maxFns: pick("maxFns", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxFns),
511
+ maxBRuns: pick("maxBRuns", envCache, DEFAULT_SESSION_CACHE_LIMITS.maxBRuns)
512
+ };
513
+ }
514
+ function setSessionCacheLimits(partial) {
515
+ explicit = partial ? { ...partial } : {};
516
+ return getSessionCacheLimits();
517
+ }
518
+ function setSessionCacheFromProject(partial) {
519
+ fromProject = partial ? { ...partial } : {};
520
+ }
521
+ function resetSessionCacheLimitState() {
522
+ explicit = {};
523
+ fromProject = {};
524
+ envCache = null;
525
+ }
526
+
527
+ // src/evaluator/config.ts
528
+ import { setBForkBudgetLimit, getBForkBudgetLimit, MAX_B_TOTAL_FORKS } from "@nudojs/core/internal";
529
+ function checkConfig(config) {
530
+ const raw = config?.check;
531
+ const rawEntry = raw?.entryThrows;
532
+ let entryThrows = "error";
533
+ if (rawEntry === "off" || rawEntry === "warning" || rawEntry === "error") {
534
+ entryThrows = rawEntry;
535
+ } else if (rawEntry !== void 0) {
536
+ process.stderr?.write?.(
537
+ `nudo.check.entryThrows: invalid value ${JSON.stringify(rawEntry)} (expected error|warning|off); using error
538
+ `
539
+ );
540
+ }
541
+ const ignore = Array.isArray(raw?.ignoreThrows) ? raw.ignoreThrows.filter((s) => typeof s === "string" && s.length > 0) : [];
542
+ return { entryThrows, ignoreThrows: ignore };
543
+ }
544
+ var DEFAULT_ANALYSIS_EXCLUDE = [
545
+ "**/node_modules/**",
546
+ "**/dist/**",
547
+ "**/coverage/**"
548
+ ];
549
+ var DEFAULT_ANALYSIS_MODE = "exports";
550
+ function toStringArray(raw, fallback) {
551
+ if (raw === void 0) return fallback;
552
+ const arr = Array.isArray(raw) ? raw : [raw];
553
+ const out = arr.filter((s) => typeof s === "string" && s.length > 0);
554
+ return out.length > 0 ? out : fallback;
555
+ }
556
+ function analysisConfig(config) {
557
+ const raw = config?.analysis;
558
+ const modeRaw = raw?.mode;
559
+ const mode = modeRaw === "exports" || modeRaw === "all" || modeRaw === "directives" ? modeRaw : DEFAULT_ANALYSIS_MODE;
560
+ const diagRaw = raw?.diagnostics;
561
+ const diagnostics = diagRaw === "off" || diagRaw === "errors" || diagRaw === "default" || diagRaw === "verbose" ? diagRaw : mode === "directives" ? "errors" : "default";
562
+ const budgetRaw = raw?.callSiteBudget;
563
+ const callSiteBudget = typeof budgetRaw === "number" && Number.isFinite(budgetRaw) && budgetRaw >= 1 ? Math.min(Math.floor(budgetRaw), 64) : 3;
564
+ return {
565
+ // include 空数组 = 不过滤(与「省略」同义)
566
+ include: toStringArray(raw?.include, []),
567
+ // exclude 空数组回落默认安全列表,避免误关 node_modules 保护
568
+ exclude: toStringArray(raw?.exclude, DEFAULT_ANALYSIS_EXCLUDE),
569
+ mode,
570
+ diagnostics,
571
+ callSiteBudget,
572
+ evalMissingSlot: raw?.evalMissingSlot === "warning" ? "warning" : "off",
573
+ maxForks: parseMaxForks(raw?.maxForks) ?? MAX_B_TOTAL_FORKS
574
+ };
575
+ }
576
+ function parseMaxForks(raw) {
577
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 1) return void 0;
578
+ return Math.floor(raw);
579
+ }
580
+ function applyBForkBudgetFromConfig(config, env = process.env) {
581
+ const fromEnv = parseMaxForks(
582
+ env.NUDO_MAX_FORKS === void 0 ? void 0 : Number(env.NUDO_MAX_FORKS)
583
+ );
584
+ const fromCfg = parseMaxForks(config?.analysis?.maxForks);
585
+ return setBForkBudgetLimit(fromEnv ?? fromCfg ?? MAX_B_TOTAL_FORKS);
586
+ }
587
+ function currentBForkBudgetLimit() {
588
+ return getBForkBudgetLimit();
589
+ }
590
+ function diskCacheRoot(config, projectDir) {
591
+ const raw = config?.cache;
592
+ if (raw === false) return void 0;
593
+ if (typeof raw === "string" && raw.length > 0) {
594
+ return projectDir ? resolve(projectDir, raw) : raw;
595
+ }
596
+ if (raw === true) {
597
+ return projectDir ? resolve(projectDir, ".nudo/cache") : void 0;
598
+ }
599
+ const env = process.env.NUDO_CACHE_DIR;
600
+ if (env === "off" || env === "0") return void 0;
601
+ if (env && env.length > 0) return env;
602
+ return void 0;
603
+ }
362
604
  function interfaceConfig(config) {
363
- const raw = config?.interface?.emit;
605
+ const raw = config?.contract?.emit;
364
606
  const emit = raw === void 0 ? [] : Array.isArray(raw) ? raw.filter((s) => typeof s === "string" && s.length > 0) : typeof raw === "string" && raw.length > 0 ? [raw] : [];
365
607
  return {
366
- autoBind: config?.interface?.autoBind ?? true,
608
+ autoBind: config?.contract?.autoBind ?? true,
367
609
  emit
368
610
  };
369
611
  }
@@ -414,7 +656,10 @@ function findProjectConfig(startDir) {
414
656
  try {
415
657
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
416
658
  if (pkg.nudo) {
417
- return { config: pkg.nudo, projectDir: dir };
659
+ const nudo = pkg.nudo;
660
+ setSessionCacheFromProject(nudo.sessionCache);
661
+ applyBForkBudgetFromConfig(nudo);
662
+ return { config: nudo, projectDir: dir };
418
663
  }
419
664
  } catch {
420
665
  }
@@ -423,11 +668,12 @@ function findProjectConfig(startDir) {
423
668
  if (parent === dir) break;
424
669
  dir = parent;
425
670
  }
671
+ applyBForkBudgetFromConfig(null);
426
672
  return null;
427
673
  }
428
674
 
429
675
  // src/evaluator/resolve-npm.ts
430
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
676
+ import { readFileSync as readFileSync3, existsSync as existsSync3, statSync as statSync2 } from "fs";
431
677
  import { resolve as resolve2, join, dirname as dirname2 } from "path";
432
678
  function findNodeModules(startDir) {
433
679
  let dir = resolve2(startDir);
@@ -441,6 +687,72 @@ function findNodeModules(startDir) {
441
687
  }
442
688
  return null;
443
689
  }
690
+ function resolveNpmJsEntry(source, fromDir) {
691
+ if (!source || source.startsWith(".") || source.startsWith("/") || source.startsWith("node:")) {
692
+ return null;
693
+ }
694
+ const parts = source.startsWith("@") ? source.split("/").slice(0, 2) : source.split("/").slice(0, 1);
695
+ const pkgName = parts.join("/");
696
+ const subpath = source.slice(pkgName.length).replace(/^\//, "") || ".";
697
+ const nodeModules = findNodeModules(fromDir);
698
+ if (!nodeModules) return null;
699
+ const pkgDir = join(nodeModules, pkgName);
700
+ const pkgJsonPath = join(pkgDir, "package.json");
701
+ if (!existsSync3(pkgJsonPath)) return null;
702
+ const tryFile = (p) => {
703
+ for (const c of [p, `${p}.js`, `${p}.cjs`, `${p}.mjs`, join(p, "index.js"), join(p, "index.cjs")]) {
704
+ try {
705
+ if (existsSync3(c) && statSync2(c).isFile()) return c;
706
+ } catch {
707
+ }
708
+ }
709
+ return null;
710
+ };
711
+ let pkg = {};
712
+ try {
713
+ pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
714
+ } catch {
715
+ return null;
716
+ }
717
+ const exportsField = pkg.exports;
718
+ const pickExport = (entry) => {
719
+ if (typeof entry === "string") return entry;
720
+ if (entry && typeof entry === "object") {
721
+ const o = entry;
722
+ for (const k of ["nudo", "node", "require", "import", "default"]) {
723
+ const v = o[k];
724
+ const r = pickExport(v);
725
+ if (r) return r;
726
+ }
727
+ }
728
+ return null;
729
+ };
730
+ if (exportsField) {
731
+ let rel = null;
732
+ if (typeof exportsField === "string" && subpath === ".") rel = exportsField;
733
+ else if (exportsField && typeof exportsField === "object") {
734
+ const map = exportsField;
735
+ const key = subpath === "." ? "." : `./${subpath}`;
736
+ rel = pickExport(map[key] ?? (subpath === "." ? map : void 0));
737
+ }
738
+ if (rel) {
739
+ const hit = tryFile(resolve2(pkgDir, rel));
740
+ if (hit) return hit;
741
+ }
742
+ }
743
+ if (subpath === ".") {
744
+ const main = typeof pkg.main === "string" ? pkg.main : void 0;
745
+ const hit = tryFile(resolve2(pkgDir, main ?? "."));
746
+ if (hit) return hit;
747
+ const mod = typeof pkg.module === "string" ? pkg.module : void 0;
748
+ if (mod) {
749
+ const hitMod = tryFile(resolve2(pkgDir, mod));
750
+ if (hitMod) return hitMod;
751
+ }
752
+ return null;
753
+ }
754
+ return tryFile(resolve2(pkgDir, subpath));
755
+ }
444
756
  function resolveExportsNudo(exports, subpath) {
445
757
  if (!exports || typeof exports !== "object") return null;
446
758
  const entry = exports[subpath];
@@ -474,15 +786,30 @@ function resolveNpmNudo(source, fromDir) {
474
786
  }
475
787
 
476
788
  export {
789
+ resolveNpmJsEntry,
790
+ resolveNpmNudo,
791
+ BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS,
792
+ describeAbsMember,
793
+ builtinProtoMemberNames,
794
+ builtinProtoMember,
795
+ BoundedLruMap,
796
+ getPathEnvCacheSizes,
797
+ clearPathEnvCaches,
477
798
  preloadPathEnvs,
478
799
  loadEnvsAsync,
479
800
  loadEnvs,
801
+ DEFAULT_SESSION_CACHE_LIMITS,
802
+ getSessionCacheLimits,
803
+ setSessionCacheLimits,
804
+ setSessionCacheFromProject,
805
+ resetSessionCacheLimitState,
806
+ checkConfig,
807
+ DEFAULT_ANALYSIS_MODE,
808
+ analysisConfig,
809
+ applyBForkBudgetFromConfig,
810
+ currentBForkBudgetLimit,
811
+ diskCacheRoot,
480
812
  interfaceConfig,
481
813
  matchesEmitAllowlist,
482
- findProjectConfig,
483
- resolveNpmNudo,
484
- BUILTIN_PROTOTYPE_METHOD_APPROXIMATIONS,
485
- describeAbsMember,
486
- builtinProtoMemberNames,
487
- builtinProtoMember
814
+ findProjectConfig
488
815
  };
@@ -0,0 +1,114 @@
1
+ type NudoConfig = {
2
+ env?: string[];
3
+ mocks?: Record<string, string>;
4
+ contract?: {
5
+ /** 侧车 ambient 绑定总开关(check/LSP 执法与 contract 打印共用) */
6
+ autoBind?: boolean;
7
+ /**
8
+ * emit 白名单(Phase 3,§7.3):glob 数组,相对 projectDir。
9
+ * 省略/空 = 不按路径过滤(仍受 --fn/--all 与「默认只刷已有生成段」约束)。
10
+ */
11
+ emit?: string[] | string;
12
+ };
13
+ /** 分析范围与噪声档(design-cli-semantics.md §7) */
14
+ analysis?: {
15
+ include?: string[] | string;
16
+ exclude?: string[] | string;
17
+ /** directives | exports(默认)| all */
18
+ mode?: string;
19
+ /** off | errors | default | verbose */
20
+ diagnostics?: string;
21
+ /** polyvariant:保留的精确调用点 case 上限(默认 3);超出进 symbolic #widened */
22
+ callSiteBudget?: number;
23
+ /** C0.5:求值命中闭对象缺字段 → nudo:missing-slot;默认 off */
24
+ evalMissingSlot?: "off" | "warning";
25
+ /**
26
+ * B $fork 总次数上限(默认 5000)。env `NUDO_MAX_FORKS` 优先。
27
+ * n≥1 有限整数;非法值回默认。启动时 set 进 core(setBForkBudgetLimit)。
28
+ */
29
+ maxForks?: number;
30
+ };
31
+ /** 磁盘缓存(B3):true → `.nudo/cache`;字符串 → 自定义根;false/省略 → 关 */
32
+ cache?: boolean | string;
33
+ /**
34
+ * 进程内会话 LRU 上限(内存/速度权衡)。多项目开 IDE 时调低封顶;
35
+ * 单大仓 warm 命中可调高。0 = 关闭该层。env `NUDO_CACHE_MAX_*` 优先。
36
+ */
37
+ sessionCache?: {
38
+ maxFiles?: number;
39
+ maxFns?: number;
40
+ maxBRuns?: number;
41
+ };
42
+ /** check 门禁(design-cli-semantics §3) */
43
+ check?: {
44
+ /** L2 入口 may-throw:error | warning | off(默认 error) */
45
+ entryThrows?: "error" | "warning" | "off";
46
+ /** L2 --ignore-throws 类型名列表 */
47
+ ignoreThrows?: string[];
48
+ };
49
+ };
50
+ type InterfaceConfig = {
51
+ autoBind: boolean;
52
+ /** emit 路径白名单(已归一化;空数组 = 不限制) */
53
+ emit: string[];
54
+ };
55
+ type AnalysisMode = "directives" | "exports" | "all";
56
+ type DiagnosticsLevel = "off" | "errors" | "default" | "verbose";
57
+ type AnalysisConfig = {
58
+ include: string[];
59
+ exclude: string[];
60
+ mode: AnalysisMode;
61
+ diagnostics: DiagnosticsLevel;
62
+ /** polyvariant 精确调用点上限(B4) */
63
+ callSiteBudget: number;
64
+ /** C0.5 evaluation-driven missing-slot;默认 off */
65
+ evalMissingSlot: "off" | "warning";
66
+ /** B $fork 总次数上限(已归一化;非法值回 core 默认) */
67
+ maxForks: number;
68
+ };
69
+ type CheckConfig = {
70
+ /** L2 入口 may-throw 执法档;默认 error */
71
+ entryThrows: "error" | "warning" | "off";
72
+ /** L2 ignoreThrows 类型名;默认空 */
73
+ ignoreThrows: string[];
74
+ };
75
+ /** package.json#nudo.check → 执法选项 */
76
+ declare function checkConfig(config: NudoConfig | null | undefined): CheckConfig;
77
+ /** A1 产品默认:exports — 普通带导出的 .js 进 IDE;directives/all 需显式 */
78
+ declare const DEFAULT_ANALYSIS_MODE: AnalysisMode;
79
+ /**
80
+ * 归一化 `nudo.analysis`。默认 mode=exports(A1:无指令但有 export/侧车的文件
81
+ * 进 IDE 分析;`all` / `directives` 需显式配置)。
82
+ * diagnostics:directives→errors,exports/all→default。
83
+ * include 空 = 不按路径过滤(isNudoTargetPath 已管扩展名)。
84
+ */
85
+ declare function analysisConfig(config: NudoConfig | null | undefined): AnalysisConfig;
86
+ /**
87
+ * 把 fork 预算写进 core(core 保持无 IO)。
88
+ * 优先级:env `NUDO_MAX_FORKS` > `package.json#nudo.analysis.maxForks` > 默认 5000。
89
+ * 约定:n≥1 有限整数;非法值回默认。返回实际生效值。
90
+ * 由 findProjectConfig / 宿主启动时调用(与 setSessionCacheFromProject 同时机)。
91
+ */
92
+ declare function applyBForkBudgetFromConfig(config: NudoConfig | null | undefined, env?: NodeJS.ProcessEnv): number;
93
+ /** 当前生效 fork 上限(调试/测试;与 core getBForkBudgetLimit 同源) */
94
+ declare function currentBForkBudgetLimit(): number;
95
+ /** 磁盘缓存根(B3):config.cache / NUDO_CACHE_DIR / 默认关 */
96
+ declare function diskCacheRoot(config: NudoConfig | null | undefined, projectDir: string | undefined): string | undefined;
97
+ /**
98
+ * 归一化 `nudo.contract` 配置段。
99
+ * - autoBind 默认 true
100
+ * - emit:string | string[] → string[](空 = 不限制路径)
101
+ */
102
+ declare function interfaceConfig(config: NudoConfig | null | undefined): InterfaceConfig;
103
+ /**
104
+ * 极简 glob(`**` / `*` / `?`):相对 projectDir 匹配**源文件**绝对路径
105
+ * (不是侧车路径;侧车随源文件同目录写出)。无白名单 → true。
106
+ * 路径分隔符归一为 `/`。
107
+ */
108
+ declare function matchesEmitAllowlist(absPath: string, projectDir: string | undefined, patterns: string[]): boolean;
109
+ declare function findProjectConfig(startDir: string): {
110
+ config: NudoConfig;
111
+ projectDir: string;
112
+ } | null;
113
+
114
+ export { type AnalysisConfig as A, type CheckConfig as C, type DiagnosticsLevel as D, type InterfaceConfig as I, type NudoConfig as N, type AnalysisMode as a, analysisConfig as b, checkConfig as c, diskCacheRoot as d, DEFAULT_ANALYSIS_MODE as e, findProjectConfig as f, applyBForkBudgetFromConfig as g, currentBForkBudgetLimit as h, interfaceConfig as i, matchesEmitAllowlist as m };