@nudojs/service 5.0.0-beta.0 → 5.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -12
- package/dist/{analysis-CSXrOVlg.d.ts → analysis-_DlXLC-L.d.ts} +17 -3
- package/dist/analysis.d.ts +3 -2
- package/dist/analysis.js +9 -5
- package/dist/{analyzer-types-JyJiCt8w.d.ts → analyzer-types-CfBrPK9L.d.ts} +1 -1
- package/dist/{chunk-LFZRUXGK.js → chunk-EG6FYQLI.js} +1 -1
- package/dist/chunk-J2PUE4NV.js +235 -0
- package/dist/{chunk-QAFMPM2E.js → chunk-NQXAF4LJ.js} +2277 -2225
- package/dist/{chunk-YLALZXTZ.js → chunk-ZXVAX7DE.js} +255 -11
- package/dist/{interface-BGlP7aIk.d.ts → emit/index.d.ts} +258 -18
- package/dist/{chunk-HEU4WON5.js → emit/index.js} +1627 -299
- package/dist/evaluator/evaluator-api.d.ts +1 -1
- package/dist/evaluator/evaluator-api.js +5 -5
- package/dist/index.d.ts +17 -19
- package/dist/index.js +188 -197
- package/dist/what-if-mIo7I5Ii.d.ts +21 -0
- package/package.json +9 -25
- package/dist/case.d.ts +0 -124
- package/dist/case.js +0 -18
- package/dist/chunk-6IZR4ZYJ.js +0 -1107
- package/dist/chunk-E4N4A4JV.js +0 -651
- package/dist/chunk-K3ZPOTP2.js +0 -199
- package/dist/chunk-PVJIMQRI.js +0 -246
- package/dist/chunk-QYRH7Z2Q.js +0 -89
- package/dist/chunk-U7JYTRIB.js +0 -888
- package/dist/chunk-YLVZMHWC.js +0 -364
- package/dist/dts.d.ts +0 -142
- package/dist/dts.js +0 -30
- package/dist/harvest.d.ts +0 -254
- package/dist/harvest.js +0 -75
- package/dist/interface.d.ts +0 -3
- package/dist/interface.js +0 -43
- package/dist/lsp.d.ts +0 -103
- package/dist/lsp.js +0 -36
|
@@ -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 (
|
|
655
|
+
if (existsSync2(pkgPath)) {
|
|
418
656
|
try {
|
|
419
|
-
const pkg = JSON.parse(
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
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(
|
|
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 (!
|
|
775
|
+
if (!existsSync3(pkgJsonPath)) return null;
|
|
538
776
|
try {
|
|
539
|
-
const pkg = JSON.parse(
|
|
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 (
|
|
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 '
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
419
|
+
} | {
|
|
420
|
+
k: "never";
|
|
421
|
+
} | {
|
|
422
|
+
k: "unknown";
|
|
423
|
+
};
|
|
424
|
+
type SchemaProjection = {
|
|
425
|
+
source: string;
|
|
426
|
+
dialect: SchemaDialect;
|
|
427
|
+
dropped: string[];
|
|
358
428
|
};
|
|
359
|
-
/**
|
|
360
|
-
declare function
|
|
361
|
-
|
|
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
|
-
|
|
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 {
|
|
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 };
|