@nudojs/harvester 0.2.7 → 0.2.8

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.js CHANGED
@@ -1,14 +1,18 @@
1
- // src/index.ts
1
+ // src/harvest-dts.ts
2
2
  import { readFileSync, statSync } from "fs";
3
3
  import ts from "typescript";
4
4
  import {
5
5
  abs as makeAbs,
6
6
  lit as termLit,
7
+ v as termVar,
7
8
  numLit,
8
9
  strLit,
9
10
  boolLit,
10
11
  objOf,
11
- joinAbs
12
+ joinAbs,
13
+ relationFn,
14
+ getFnImpl,
15
+ substAbs
12
16
  } from "@nudojs/core";
13
17
  var MAX_ALIAS_DEPTH = 8;
14
18
  function scopeKey(scope) {
@@ -46,6 +50,25 @@ function absArr(element) {
46
50
  function absPromise(inner) {
47
51
  return absExact({ k: "eff", eff: "promise", inner });
48
52
  }
53
+ function absMapType(name, key, value) {
54
+ return absExact({
55
+ k: "brand",
56
+ name,
57
+ shape: absObj({
58
+ __key: key,
59
+ __value: value
60
+ })
61
+ });
62
+ }
63
+ function absSetType(name, elem) {
64
+ return absExact({
65
+ k: "brand",
66
+ name,
67
+ shape: absObj({
68
+ __elem: elem
69
+ })
70
+ });
71
+ }
49
72
  function absObj(properties) {
50
73
  const slots = {};
51
74
  for (const [k, v] of Object.entries(properties)) {
@@ -56,14 +79,16 @@ function absObj(properties) {
56
79
  function absBrand(name, properties) {
57
80
  return absExact({ k: "brand", name, shape: absObj(properties) });
58
81
  }
59
- function absFnSig(paramTypes, returnType) {
60
- return absExact({
61
- k: "fn",
82
+ function absFnSig(paramTypes, returnType, extra) {
83
+ return relationFn(paramTypes, returnType, {
62
84
  params: paramTypes.map((_, i) => `_arg${i}`),
63
- paramTypes,
64
- returnType
85
+ conf: "exact",
86
+ ...extra ?? {}
65
87
  });
66
88
  }
89
+ function typeVarAbs(name) {
90
+ return makeAbs({ k: "any" }, termVar(name), void 0, "path");
91
+ }
67
92
  function absUnion(members) {
68
93
  if (members.length === 0) return absNever();
69
94
  return members.reduce((acc, m) => joinAbs(acc, m));
@@ -71,6 +96,19 @@ function absUnion(members) {
71
96
  function isPlainUnknown(a) {
72
97
  return a.shape.k === "unknown" && !(a.term?.op === "lit");
73
98
  }
99
+ function hasTypeVar(a, depth = 0) {
100
+ if (depth > 6) return false;
101
+ if (a.term?.op === "var") return true;
102
+ const s = a.shape;
103
+ if (s.k === "arr") return hasTypeVar(s.element, depth + 1);
104
+ if (s.k === "tuple") return s.elements.some((e) => hasTypeVar(e, depth + 1));
105
+ if (s.k === "sum") return s.members.some((m) => hasTypeVar(m, depth + 1));
106
+ if (s.k === "obj") return Object.values(s.slots).some((sl) => hasTypeVar(sl.value, depth + 1));
107
+ if (s.k === "fn") {
108
+ return (s.paramTypes ?? []).some((p) => hasTypeVar(p, depth + 1)) || (s.returnType ? hasTypeVar(s.returnType, depth + 1) : false);
109
+ }
110
+ return false;
111
+ }
74
112
  function harvestDts(files, opts) {
75
113
  const maxBytes = opts?.maxFileBytes ?? 15e5;
76
114
  const deadline = opts?.maxMs !== void 0 ? Date.now() + opts.maxMs : void 0;
@@ -84,6 +122,7 @@ function harvestDts(files, opts) {
84
122
  classes: /* @__PURE__ */ new Map(),
85
123
  instanceCache: /* @__PURE__ */ new Map(),
86
124
  expanding: /* @__PURE__ */ new Set(),
125
+ typeParams: /* @__PURE__ */ new Set(),
87
126
  moduleAliases: [],
88
127
  importEquals: /* @__PURE__ */ new Map(),
89
128
  declaredModules: /* @__PURE__ */ new Set(),
@@ -141,7 +180,7 @@ function emitEnvModule(env, pkgName) {
141
180
  constLines.push("");
142
181
  }
143
182
  const lines = [];
144
- lines.push("// Auto-generated by nudo harvest \u2014 DO NOT EDIT");
183
+ lines.push("// Auto-generated by @nudojs/harvester \u2014 DO NOT EDIT");
145
184
  lines.push(`// Source package: ${pkgName}`);
146
185
  lines.push(
147
186
  'import { num, str, bool, never as absNever, unknown as absUnknown, numLit, strLit, boolLit, objOf, relationFn, abs as makeAbs } from "@nudojs/core";'
@@ -222,9 +261,17 @@ function collectStatement(ctx, stmt, scope) {
222
261
  ctx.skipped++;
223
262
  continue;
224
263
  }
225
- const iface = decl.type ? interfaceDeclOf(ctx, decl.type) : void 0;
226
- if (iface) {
227
- expandInterfaceAsModuleMembers(ctx, scope, iface);
264
+ const ifaceName = decl.type && ts.isTypeReferenceNode(decl.type) ? typeNameString(decl.type.typeName) : void 0;
265
+ if (ifaceName && (ctx.interfaces.has(ifaceName) || ctx.qualifiedInterfaces.has(ifaceName) || true)) {
266
+ ctx.pending.push({
267
+ t: "iface-expand",
268
+ scope,
269
+ localName: decl.name.text,
270
+ typeRefName: ifaceName
271
+ });
272
+ ctx.symbols++;
273
+ ctx.pending.push({ t: "var", scope, name: decl.name.text, typeNode: decl.type });
274
+ ctx.symbols++;
228
275
  continue;
229
276
  }
230
277
  ctx.pending.push({ t: "var", scope, name: decl.name.text, typeNode: decl.type });
@@ -320,66 +367,122 @@ function collectStatement(ctx, stmt, scope) {
320
367
  ctx.skipped++;
321
368
  }
322
369
  function registerTypeDecl(ctx, scope, name, decl) {
323
- if (!ctx.interfaces.has(name)) ctx.interfaces.set(name, decl);
370
+ const push = (map, key) => {
371
+ const list = map.get(key);
372
+ if (list) {
373
+ if (!list.includes(decl)) list.push(decl);
374
+ } else {
375
+ map.set(key, [decl]);
376
+ }
377
+ };
378
+ push(ctx.interfaces, name);
324
379
  if (ts.isClassDeclaration(decl)) return;
325
380
  const qualified = [...scope.ns, name].join(".");
326
- if (qualified !== name && !ctx.qualifiedInterfaces.has(qualified)) {
327
- ctx.qualifiedInterfaces.set(qualified, decl);
328
- }
381
+ if (qualified !== name) push(ctx.qualifiedInterfaces, qualified);
329
382
  }
330
- function interfaceDeclOf(ctx, typeNode) {
331
- if (!ts.isTypeReferenceNode(typeNode)) return void 0;
332
- const name = typeNameString(typeNode.typeName);
333
- const d = ctx.interfaces.get(name) ?? ctx.qualifiedInterfaces.get(name);
334
- return d && ts.isInterfaceDeclaration(d) ? d : void 0;
383
+ function interfaceDeclsOf(ctx, typeNode) {
384
+ const name = typeof typeNode === "string" ? typeNode : ts.isTypeReferenceNode(typeNode) ? typeNameString(typeNode.typeName) : void 0;
385
+ if (!name) return [];
386
+ const short = name.includes(".") ? name.split(".").pop() : name;
387
+ const keys = name === short ? [name] : [name, short];
388
+ const seen = /* @__PURE__ */ new Set();
389
+ const out = [];
390
+ for (const key of keys) {
391
+ for (const d of [
392
+ ...ctx.interfaces.get(key) ?? [],
393
+ ...ctx.qualifiedInterfaces.get(key) ?? []
394
+ ]) {
395
+ if (ts.isInterfaceDeclaration(d) && !seen.has(d)) {
396
+ seen.add(d);
397
+ out.push(d);
398
+ }
399
+ }
400
+ }
401
+ return out;
335
402
  }
336
- function expandInterfaceAsModuleMembers(ctx, scope, iface) {
337
- for (const member of iface.members) {
338
- if (ts.isMethodSignature(member)) {
339
- const name = memberName(member);
340
- if (name === void 0) continue;
341
- const existing = ctx.pending.find(
342
- (p) => p.t === "fn" && p.name === name && scopeKey(p.scope) === scopeKey(scope)
343
- );
344
- if (existing && existing.t === "fn") {
345
- existing.decls.push(member);
403
+ function expandInterfaceAsModuleMembers(ctx, scope, ifaces) {
404
+ for (const iface of ifaces) {
405
+ for (const member of iface.members) {
406
+ if (ts.isMethodSignature(member)) {
407
+ const name = memberName(member);
408
+ if (name === void 0) continue;
409
+ const existing = ctx.pending.find(
410
+ (p) => p.t === "fn" && p.name === name && scopeKey(p.scope) === scopeKey(scope)
411
+ );
412
+ if (existing && existing.t === "fn") {
413
+ existing.decls.push(member);
414
+ continue;
415
+ }
416
+ ctx.pending.push({ t: "fn", scope, name, decls: [member] });
417
+ ctx.symbols++;
346
418
  continue;
347
419
  }
348
- ctx.pending.push({ t: "fn", scope, name, decls: [member] });
349
- ctx.symbols++;
350
- continue;
351
- }
352
- if (ts.isPropertySignature(member)) {
353
- const name = memberName(member);
354
- if (name === void 0) continue;
355
- ctx.pending.push({ t: "var", scope, name, typeNode: member.type });
356
- ctx.symbols++;
420
+ if (ts.isPropertySignature(member)) {
421
+ const name = memberName(member);
422
+ if (name === void 0) continue;
423
+ if (ctx.pending.some((p) => p.t === "var" && p.name === name && scopeKey(p.scope) === scopeKey(scope))) {
424
+ continue;
425
+ }
426
+ ctx.pending.push({ t: "var", scope, name, typeNode: member.type });
427
+ ctx.symbols++;
428
+ }
357
429
  }
358
430
  }
359
431
  }
360
432
  function registerAlias(ctx, scope, stmt) {
361
433
  const name = stmt.name.text;
362
- if (!ctx.aliases.has(name)) ctx.aliases.set(name, stmt);
434
+ const push = (map, key) => {
435
+ const list = map.get(key);
436
+ if (list) {
437
+ if (!list.includes(stmt)) list.push(stmt);
438
+ } else {
439
+ map.set(key, [stmt]);
440
+ }
441
+ };
442
+ push(ctx.aliases, name);
363
443
  const qualified = [...scope.ns, name].join(".");
364
- if (qualified !== name && !ctx.qualifiedAliases.has(qualified)) {
365
- ctx.qualifiedAliases.set(qualified, stmt);
366
- }
444
+ if (qualified !== name) push(ctx.qualifiedAliases, qualified);
367
445
  }
368
446
  function materialize(ctx) {
447
+ const expanded = [];
369
448
  for (const p of ctx.pending) {
449
+ if (p.t !== "iface-expand") {
450
+ expanded.push(p);
451
+ continue;
452
+ }
453
+ const ifaces = interfaceDeclsOf(ctx, p.typeRefName);
454
+ if (ifaces.length === 0) {
455
+ continue;
456
+ }
457
+ const before = ctx.pending.length;
458
+ expandInterfaceAsModuleMembers(ctx, p.scope, ifaces);
459
+ for (let i = before; i < ctx.pending.length; i++) {
460
+ const n = ctx.pending[i];
461
+ if (n.t === "fn" || n.t === "var") expanded.push(n);
462
+ }
463
+ }
464
+ ctx.pending = expanded;
465
+ for (const p of ctx.pending) {
466
+ if (p.t === "iface-expand") continue;
370
467
  const rec = recordFor(ctx, p.scope);
371
468
  if (p.t === "fn") {
372
469
  if (rec[p.name] !== void 0) continue;
373
- const value = signatureToFnSig(ctx, p.decls[0]);
374
- const sig = value.shape.k === "fn" ? value.shape : void 0;
375
- if (sig) {
470
+ const sigs = p.decls.map((d) => signatureToFnSig(ctx, d));
471
+ const value = sigs[0];
472
+ const shape0 = value.shape.k === "fn" ? value.shape : void 0;
473
+ if (shape0 && sigs.length > 1) {
376
474
  const returns = [];
377
- for (const decl of p.decls) {
378
- if (!decl.type) continue;
379
- const ret = mapType(ctx, decl.type, 0);
380
- if (!isPlainUnknown(ret)) returns.push(ret);
475
+ for (const s of sigs) {
476
+ if (s.shape.k !== "fn") continue;
477
+ const ret = s.shape.returnType;
478
+ if (ret && !isPlainUnknown(ret)) returns.push(ret);
381
479
  }
382
- sig.returnType = returns.length > 0 ? absUnion(returns) : absUnknown();
480
+ const withVars = returns.filter((r) => hasTypeVar(r));
481
+ const pool = withVars.length > 0 ? withVars : returns;
482
+ const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
483
+ shape0.returnType = mergedRet;
484
+ const rel = getFnImpl(value)?.relation;
485
+ if (rel) rel.returnType = mergedRet;
383
486
  }
384
487
  rec[p.name] = value;
385
488
  continue;
@@ -434,58 +537,91 @@ function memberName(node) {
434
537
  if (ts.isIdentifier(n) || ts.isStringLiteral(n) || ts.isNumericLiteral(n)) return n.text;
435
538
  return void 0;
436
539
  }
437
- function instanceFor(ctx, key) {
438
- const cached = ctx.instanceCache.get(key);
540
+ function instanceFor(ctx, key, typeArgs) {
541
+ const cacheKey = typeArgs && typeArgs.length > 0 ? `${key}::${typeArgs.length}` : key;
542
+ const cached = ctx.instanceCache.get(cacheKey);
439
543
  if (cached !== void 0) return cached;
440
- const decl = ctx.interfaces.get(key) ?? ctx.qualifiedInterfaces.get(key);
544
+ const decls = interfaceDeclsOf(ctx, key);
545
+ const decl = decls[0];
441
546
  if (!decl) return absUnknown();
442
547
  if (ctx.expanding.has(key)) {
443
548
  return absBrand(decl.name?.text ?? key, {});
444
549
  }
445
550
  ctx.expanding.add(key);
551
+ const savedTp = ctx.typeParams;
552
+ ctx.typeParams = new Set(savedTp);
553
+ for (const d of decls) {
554
+ for (const tp of d.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
555
+ }
446
556
  const properties = {};
447
557
  try {
448
- const members = decl.members;
449
- for (const member of members) {
450
- const modifiers = member.modifiers;
451
- if (modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) continue;
452
- if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
453
- const name = memberName(member);
454
- if (name === void 0) continue;
455
- let type = member.type ? mapType(ctx, member.type, 0) : absUnknown();
456
- if (member.questionToken) type = absUnion([type, absUndefLit()]);
457
- properties[name] = type;
458
- } else if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member)) {
459
- const name = memberName(member);
460
- if (name === void 0 || properties[name] !== void 0) continue;
461
- properties[name] = signatureToFnSig(ctx, member);
462
- } else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
463
- const name = memberName(member);
464
- if (name === void 0 || properties[name] !== void 0 || !member.type) continue;
465
- properties[name] = mapType(ctx, member.type, 0);
558
+ for (const d of decls) {
559
+ const members = d.members;
560
+ for (const member of members) {
561
+ const modifiers = member.modifiers;
562
+ if (modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) continue;
563
+ if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
564
+ const name = memberName(member);
565
+ if (name === void 0) continue;
566
+ let type = member.type ? mapType(ctx, member.type, 0) : absUnknown();
567
+ if (member.questionToken) type = absUnion([type, absUndefLit()]);
568
+ properties[name] = type;
569
+ } else if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member)) {
570
+ const name = memberName(member);
571
+ if (name === void 0 || properties[name] !== void 0) continue;
572
+ properties[name] = signatureToFnSig(ctx, member);
573
+ } else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
574
+ const name = memberName(member);
575
+ if (name === void 0 || properties[name] !== void 0 || !member.type) continue;
576
+ properties[name] = mapType(ctx, member.type, 0);
577
+ }
466
578
  }
467
579
  }
468
580
  } finally {
581
+ ctx.typeParams = savedTp;
469
582
  ctx.expanding.delete(key);
470
583
  }
471
- const value = absBrand(decl.name?.text ?? key, properties);
472
- ctx.instanceCache.set(key, value);
584
+ let value = absBrand(decl.name?.text ?? key, properties);
585
+ if (typeArgs && typeArgs.length > 0 && Object.keys(properties).length <= 40) {
586
+ const map = /* @__PURE__ */ new Map();
587
+ const tps = decls[0].typeParameters ?? [];
588
+ tps.forEach((tp, i) => {
589
+ const arg = typeArgs[i];
590
+ if (arg) map.set(tp.name.text, arg);
591
+ });
592
+ if (map.size > 0) value = substAbs(value, map);
593
+ }
594
+ ctx.instanceCache.set(cacheKey, value);
473
595
  return value;
474
596
  }
475
597
  function signatureToFnSig(ctx, sig) {
476
- const paramTypes = sig.parameters.map((param) => {
477
- let type;
478
- if (param.dotDotDotToken) {
479
- const inner = param.type && ts.isArrayTypeNode(param.type) ? param.type.elementType : param.type;
480
- type = inner ? absArr(mapType(ctx, inner, 0)) : absUnknown();
481
- } else {
482
- type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
483
- }
484
- if (param.questionToken && !param.dotDotDotToken) type = absUnion([type, absUndefLit()]);
485
- return type;
486
- });
487
- const returnType = sig.type ? mapType(ctx, sig.type, 0) : absUnknown();
488
- return absFnSig(paramTypes, returnType);
598
+ const savedTp = ctx.typeParams;
599
+ ctx.typeParams = new Set(savedTp);
600
+ for (const tp of sig.typeParameters ?? []) {
601
+ ctx.typeParams.add(tp.name.text);
602
+ }
603
+ try {
604
+ const paramTypes = sig.parameters.map((param) => {
605
+ let type;
606
+ if (param.dotDotDotToken) {
607
+ const inner = param.type && ts.isArrayTypeNode(param.type) ? param.type.elementType : param.type;
608
+ type = inner ? absArr(mapType(ctx, inner, 0)) : absUnknown();
609
+ } else {
610
+ type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
611
+ }
612
+ if (param.questionToken && !param.dotDotDotToken) type = absUnion([type, absUndefLit()]);
613
+ return type;
614
+ });
615
+ const returnType = sig.type ? mapType(ctx, sig.type, 0) : absUnknown();
616
+ const cond = ctx.pendingCond;
617
+ ctx.pendingCond = void 0;
618
+ return absFnSig(paramTypes, returnType, cond ? {
619
+ inferFrom: { fromVar: cond.fromVar, via: cond.via, inferVar: cond.inferVar },
620
+ condFallback: cond.fallback
621
+ } : void 0);
622
+ } finally {
623
+ ctx.typeParams = savedTp;
624
+ }
489
625
  }
490
626
  function mapTypeMembers(ctx, members, depth) {
491
627
  const properties = {};
@@ -507,22 +643,163 @@ function mapTypeMembers(ctx, members, depth) {
507
643
  function mapTypeRef(ctx, node, depth) {
508
644
  const name = typeNameString(node.typeName);
509
645
  const args = node.typeArguments ?? [];
646
+ if (ctx.typeParams.has(name) && args.length === 0) {
647
+ return typeVarAbs(name);
648
+ }
510
649
  if ((name === "Array" || name === "ReadonlyArray") && args.length >= 1) {
511
650
  return absArr(mapType(ctx, args[0], depth));
512
651
  }
652
+ if ((name === "List" || name === "ArrayLike" || name === "Collection" || name === "Many" || name === "NumericDictionary" || name === "Dictionary") && args.length >= 1) {
653
+ return absArr(mapType(ctx, args[0], depth));
654
+ }
513
655
  if (name === "Promise" && args.length >= 1) {
514
656
  return absPromise(mapType(ctx, args[0], depth));
515
657
  }
658
+ if ((name === "Map" || name === "ReadonlyMap" || name === "WeakMap") && args.length >= 2) {
659
+ return absMapType(name, mapType(ctx, args[0], depth), mapType(ctx, args[1], depth));
660
+ }
661
+ if ((name === "Set" || name === "ReadonlySet" || name === "WeakSet") && args.length >= 1) {
662
+ return absSetType(name, mapType(ctx, args[0], depth));
663
+ }
516
664
  if (name === "Record") {
517
665
  return absObj({});
518
666
  }
519
667
  if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) {
520
- return instanceFor(ctx, name);
668
+ return instanceFor(ctx, name, args.map((a) => mapType(ctx, a, depth)));
521
669
  }
522
- const alias = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name);
670
+ const aliases = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? [];
671
+ const alias = aliases[0];
523
672
  if (alias) return mapType(ctx, alias.type, depth + 1);
524
673
  return absUnknown();
525
674
  }
675
+ function collectInferNames(node, out = /* @__PURE__ */ new Set()) {
676
+ const visit = (n) => {
677
+ if (!n) return;
678
+ if (ts.isInferTypeNode(n)) {
679
+ out.add(n.typeParameter.name.text);
680
+ visit(n.typeParameter.constraint);
681
+ return;
682
+ }
683
+ n.forEachChild(visit);
684
+ };
685
+ visit(node);
686
+ return out;
687
+ }
688
+ function absExtends(check, ext) {
689
+ if (check.term?.op === "var") return "unknown";
690
+ if (ext.term?.op === "lit" && (ext.term.value === null || ext.term.value === void 0)) {
691
+ if (check.term?.op === "lit") {
692
+ return check.term.value === ext.term.value ? "true" : "false";
693
+ }
694
+ if (check.shape.k === "prim" || check.shape.k === "arr" || check.shape.k === "obj" || check.shape.k === "fn" || check.shape.k === "brand" || check.shape.k === "eff") {
695
+ return "false";
696
+ }
697
+ return "unknown";
698
+ }
699
+ if (ext.shape.k === "unknown") return "true";
700
+ if (ext.shape.k === "any") return "true";
701
+ const cs = check.shape;
702
+ const es = ext.shape;
703
+ if (es.k === "prim" && cs.k === "prim") {
704
+ return cs.type === es.type ? "true" : "false";
705
+ }
706
+ if (es.k === "sum") {
707
+ const flags = es.members.map((m) => absExtends(check, m));
708
+ if (flags.some((f) => f === "true")) return "true";
709
+ if (flags.every((f) => f === "false")) return "false";
710
+ return "unknown";
711
+ }
712
+ if (cs.k === "sum") {
713
+ const flags = cs.members.map((m) => absExtends(m, ext));
714
+ if (flags.every((f) => f === "true")) return "true";
715
+ if (flags.every((f) => f === "false")) return "false";
716
+ return "unknown";
717
+ }
718
+ if (cs.k === "arr" && es.k === "arr") {
719
+ return absExtends(cs.element, es.element);
720
+ }
721
+ if (cs.k === "eff" && es.k === "eff" && cs.eff === es.eff) {
722
+ return absExtends(cs.inner, es.inner);
723
+ }
724
+ if (cs.k === "brand" && es.k === "brand") {
725
+ return cs.name === es.name ? "true" : "false";
726
+ }
727
+ if (cs.k === "never") return "true";
728
+ return "unknown";
729
+ }
730
+ function matchExtendsInfer(pattern, actual, inferMap, depth = 0) {
731
+ if (depth > 6) return "unknown";
732
+ if (ts.isInferTypeNode(pattern)) {
733
+ const name = pattern.typeParameter.name.text;
734
+ if (!inferMap.has(name)) inferMap.set(name, actual);
735
+ return "true";
736
+ }
737
+ if (ts.isArrayTypeNode(pattern) && actual.shape.k === "arr") {
738
+ return matchExtendsInfer(pattern.elementType, actual.shape.element, inferMap, depth + 1);
739
+ }
740
+ if (ts.isTypeReferenceNode(pattern)) {
741
+ const name = typeNameString(pattern.typeName);
742
+ const args = pattern.typeArguments ?? [];
743
+ if ((name === "Array" || name === "ReadonlyArray" || name === "List" || name === "ArrayLike") && args[0] && actual.shape.k === "arr") {
744
+ return matchExtendsInfer(args[0], actual.shape.element, inferMap, depth + 1);
745
+ }
746
+ if (name === "Promise" && args[0] && actual.shape.k === "eff" && actual.shape.eff === "promise") {
747
+ return matchExtendsInfer(args[0], actual.shape.inner, inferMap, depth + 1);
748
+ }
749
+ return "unknown";
750
+ }
751
+ if (ts.isUnionTypeNode(pattern)) {
752
+ const flags = pattern.types.map((t) => matchExtendsInfer(t, actual, inferMap, depth + 1));
753
+ if (flags.some((f) => f === "true")) return "true";
754
+ if (flags.every((f) => f === "false")) return "false";
755
+ return "unknown";
756
+ }
757
+ return "unknown";
758
+ }
759
+ function mapConditionalType(ctx, node, depth) {
760
+ const inferMap = /* @__PURE__ */ new Map();
761
+ const inferNames = collectInferNames(node.extendsType);
762
+ const check = mapType(ctx, node.checkType, depth + 1);
763
+ const astMatch = matchExtendsInfer(node.extendsType, check, inferMap);
764
+ const extAbs = mapType(ctx, node.extendsType, depth + 1);
765
+ const absMatch = astMatch !== "unknown" ? astMatch : absExtends(check, extAbs);
766
+ const condMeta = analyzeCondPattern(ctx, node, check);
767
+ const savedTp = ctx.typeParams;
768
+ ctx.typeParams = new Set(savedTp);
769
+ for (const n of inferNames) ctx.typeParams.add(n);
770
+ try {
771
+ const trueAbs = inferMap.size > 0 ? substAbs(mapType(ctx, node.trueType, depth + 1), inferMap) : mapType(ctx, node.trueType, depth + 1);
772
+ const falseAbs = mapType(ctx, node.falseType, depth + 1);
773
+ if (absMatch === "true") return trueAbs;
774
+ if (absMatch === "false") return falseAbs;
775
+ if (condMeta && check.term?.op === "var") {
776
+ ctx.pendingCond = { ...condMeta, fallback: falseAbs };
777
+ return typeVarAbs(condMeta.inferVar);
778
+ }
779
+ return absUnion([trueAbs, falseAbs]);
780
+ } finally {
781
+ ctx.typeParams = savedTp;
782
+ }
783
+ }
784
+ function analyzeCondPattern(_ctx, node, check) {
785
+ if (check.term?.op !== "var") return void 0;
786
+ const fromVar = check.term.id;
787
+ const ext = node.extendsType;
788
+ const names = collectInferNames(ext);
789
+ const inferVar = [...names][0];
790
+ if (!inferVar) return void 0;
791
+ if (ts.isArrayTypeNode(ext)) {
792
+ return { fromVar, via: "arr", inferVar };
793
+ }
794
+ if (ts.isTypeReferenceNode(ext)) {
795
+ const n = typeNameString(ext.typeName);
796
+ if (n === "Promise" || n === "PromiseLike") return { fromVar, via: "promise", inferVar };
797
+ if (n === "Array" || n === "ReadonlyArray" || n === "List" || n === "ArrayLike") {
798
+ return { fromVar, via: "arr", inferVar };
799
+ }
800
+ }
801
+ return void 0;
802
+ }
526
803
  function mapType(ctx, node, depth) {
527
804
  if (!node || depth > MAX_ALIAS_DEPTH) return absUnknown();
528
805
  if (ts.isParenthesizedTypeNode(node)) return mapType(ctx, node.type, depth);
@@ -536,7 +813,12 @@ function mapType(ctx, node, depth) {
536
813
  return absUnknown();
537
814
  }
538
815
  if (ts.isUnionTypeNode(node)) {
539
- return absUnion(node.types.map((t) => mapType(ctx, t, depth)));
816
+ const members = node.types.map((t) => mapType(ctx, t, depth));
817
+ const hasLit = members.some((m) => m.term?.op === "lit");
818
+ if (hasLit && members.length > 1) {
819
+ return absExact({ k: "sum", members });
820
+ }
821
+ return absUnion(members);
540
822
  }
541
823
  if (ts.isIntersectionTypeNode(node)) {
542
824
  const members = node.types.map((t) => mapType(ctx, t, depth));
@@ -558,6 +840,7 @@ function mapType(ctx, node, depth) {
558
840
  if (ts.isNumericLiteral(lit)) return absLit(Number(lit.text));
559
841
  if (lit.kind === ts.SyntaxKind.TrueKeyword) return absLit(true);
560
842
  if (lit.kind === ts.SyntaxKind.FalseKeyword) return absLit(false);
843
+ if (lit.kind === ts.SyntaxKind.NullKeyword) return absNullLit();
561
844
  if (ts.isPrefixUnaryExpression(lit) && lit.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(lit.operand)) {
562
845
  return absLit(-Number(lit.operand.text));
563
846
  }
@@ -580,6 +863,12 @@ function mapType(ctx, node, depth) {
580
863
  if (ts.isTypeOperatorNode(node)) {
581
864
  return node.operator === ts.SyntaxKind.ReadonlyKeyword ? mapType(ctx, node.type, depth) : absUnknown();
582
865
  }
866
+ if (ts.isConditionalTypeNode(node)) {
867
+ return mapConditionalType(ctx, node, depth);
868
+ }
869
+ if (ts.isInferTypeNode(node)) {
870
+ return typeVarAbs(node.typeParameter.name.text);
871
+ }
583
872
  switch (node.kind) {
584
873
  case ts.SyntaxKind.StringKeyword:
585
874
  return absPrim("string");
@@ -684,7 +973,1372 @@ function emitAbs(a, expanding) {
684
973
  return "absUnknown";
685
974
  }
686
975
  }
976
+
977
+ // src/dts-contract-draft.ts
978
+ import { readFileSync as readFileSync2, existsSync, statSync as statSync2, readdirSync } from "fs";
979
+ import { basename, dirname, join } from "path";
980
+ import ts2 from "typescript";
981
+ var BUILDERS = [
982
+ "fn",
983
+ "number",
984
+ "string",
985
+ "boolean",
986
+ "any",
987
+ "shape",
988
+ "array",
989
+ "lit",
990
+ "union"
991
+ ];
992
+ function keywordDsl(node) {
993
+ switch (node.kind) {
994
+ case ts2.SyntaxKind.NumberKeyword:
995
+ return "number()";
996
+ case ts2.SyntaxKind.StringKeyword:
997
+ return "string()";
998
+ case ts2.SyntaxKind.BooleanKeyword:
999
+ return "boolean()";
1000
+ case ts2.SyntaxKind.AnyKeyword:
1001
+ case ts2.SyntaxKind.UnknownKeyword:
1002
+ case ts2.SyntaxKind.VoidKeyword:
1003
+ case ts2.SyntaxKind.UndefinedKeyword:
1004
+ case ts2.SyntaxKind.NullKeyword:
1005
+ case ts2.SyntaxKind.ObjectKeyword:
1006
+ case ts2.SyntaxKind.NeverKeyword:
1007
+ return "any()";
1008
+ case ts2.SyntaxKind.BigIntKeyword:
1009
+ case ts2.SyntaxKind.SymbolKeyword:
1010
+ return void 0;
1011
+ default:
1012
+ return void 0;
1013
+ }
1014
+ }
1015
+ function litDsl(node) {
1016
+ const lit = node.literal;
1017
+ if (ts2.isStringLiteral(lit)) return `lit(${JSON.stringify(lit.text)})`;
1018
+ if (ts2.isNumericLiteral(lit)) return `lit(${lit.text})`;
1019
+ if (lit.kind === ts2.SyntaxKind.TrueKeyword) return "lit(true)";
1020
+ if (lit.kind === ts2.SyntaxKind.FalseKeyword) return "lit(false)";
1021
+ return void 0;
1022
+ }
1023
+ function typeRefName(node) {
1024
+ const n = node.typeName;
1025
+ if (ts2.isIdentifier(n)) return n.text;
1026
+ if (ts2.isQualifiedName(n)) {
1027
+ const parts = [];
1028
+ let cur = n;
1029
+ while (ts2.isQualifiedName(cur)) {
1030
+ parts.unshift(cur.right.text);
1031
+ cur = cur.left;
1032
+ }
1033
+ if (ts2.isIdentifier(cur)) parts.unshift(cur.text);
1034
+ return parts.join(".");
1035
+ }
1036
+ return "?";
1037
+ }
1038
+ function typeToDsl(node, depth = 0) {
1039
+ if (!node || depth > 6) return "any()";
1040
+ const kw = keywordDsl(node);
1041
+ if (kw !== void 0) return kw;
1042
+ if (ts2.isLiteralTypeNode(node)) {
1043
+ return litDsl(node) ?? "any()";
1044
+ }
1045
+ if (ts2.isArrayTypeNode(node)) {
1046
+ const el = typeToDsl(node.elementType, depth + 1) ?? "any()";
1047
+ return `array(${el})`;
1048
+ }
1049
+ if (ts2.isUnionTypeNode(node)) {
1050
+ const parts = node.types.map((t) => typeToDsl(t, depth + 1) ?? "any()");
1051
+ if (parts.length === 0) return "any()";
1052
+ if (parts.length === 1) return parts[0];
1053
+ return `union(${parts.join(", ")})`;
1054
+ }
1055
+ if (ts2.isParenthesizedTypeNode(node)) {
1056
+ return typeToDsl(node.type, depth + 1);
1057
+ }
1058
+ if (ts2.isTypeLiteralNode(node)) {
1059
+ const fields = [];
1060
+ for (const m of node.members) {
1061
+ if (!ts2.isPropertySignature(m)) continue;
1062
+ const key = m.name && (ts2.isIdentifier(m.name) || ts2.isStringLiteral(m.name)) ? ts2.isIdentifier(m.name) ? m.name.text : m.name.text : void 0;
1063
+ if (!key) continue;
1064
+ const optional = m.questionToken ? true : false;
1065
+ const val = typeToDsl(m.type, depth + 1) ?? "any()";
1066
+ fields.push(optional ? `${key}: ${val}.optional()` : `${key}: ${val}`);
1067
+ }
1068
+ return fields.length === 0 ? "shape({})" : `shape({ ${fields.join(", ")} })`;
1069
+ }
1070
+ if (ts2.isFunctionTypeNode(node) || ts2.isConstructorTypeNode(node)) {
1071
+ return fnSignatureDsl(node.parameters, node.type, depth);
1072
+ }
1073
+ if (ts2.isTypeReferenceNode(node)) {
1074
+ const name = typeRefName(node);
1075
+ const args = node.typeArguments ?? [];
1076
+ if (name === "Array" || name === "ReadonlyArray" || name === "readonly") {
1077
+ const el = args[0] ? typeToDsl(args[0], depth + 1) : "any()";
1078
+ return `array(${el ?? "any()"})`;
1079
+ }
1080
+ if (name === "Promise") {
1081
+ return args[0] ? typeToDsl(args[0], depth + 1) : "any()";
1082
+ }
1083
+ if (name === "Record") {
1084
+ const val = args[1] ? typeToDsl(args[1], depth + 1) : "any()";
1085
+ return `shape({}) /* open Record \u2192 ${val ?? "any()"} */`;
1086
+ }
1087
+ if (name === "Partial" && args[0]) {
1088
+ const inner = typeToDsl(args[0], depth + 1);
1089
+ return inner ?? "any()";
1090
+ }
1091
+ return void 0;
1092
+ }
1093
+ if (ts2.isExpressionWithTypeArguments(node)) {
1094
+ return void 0;
1095
+ }
1096
+ return void 0;
1097
+ }
1098
+ function fnSignatureDsl(params, ret, depth) {
1099
+ const fields = [];
1100
+ const notes = [];
1101
+ params.forEach((p, i) => {
1102
+ let name;
1103
+ if (p.name && ts2.isIdentifier(p.name)) name = p.name.text;
1104
+ else name = `p${i}`;
1105
+ if (p.dotDotDotToken) {
1106
+ notes.push(`${name}: rest \u2014 array()`);
1107
+ fields.push(`${name}: array(any())`);
1108
+ return;
1109
+ }
1110
+ const optional = Boolean(p.questionToken) || p.initializer;
1111
+ let dsl = typeToDsl(p.type, depth + 1);
1112
+ if (dsl === void 0) {
1113
+ notes.push(`${name}: unmodeled type \u2192 any()`);
1114
+ dsl = "any()";
1115
+ }
1116
+ fields.push(optional ? `${name}: ${dsl}.optional()` : `${name}: ${dsl}`);
1117
+ });
1118
+ let retDsl = typeToDsl(ret, depth + 1);
1119
+ if (retDsl === void 0) {
1120
+ notes.push(`returns: unmodeled \u2192 any()`);
1121
+ retDsl = "any()";
1122
+ }
1123
+ const obj = fields.length === 0 ? "{}" : `{ ${fields.join(", ")} }`;
1124
+ return `fn(${obj}, ${retDsl})`;
1125
+ }
1126
+ function pickName(decl) {
1127
+ return decl.name && ts2.isIdentifier(decl.name) ? decl.name.text : void 0;
1128
+ }
1129
+ function collectExports(sourceFile) {
1130
+ const out = [];
1131
+ const push = (e) => {
1132
+ out.push(e);
1133
+ };
1134
+ for (const stmt of sourceFile.statements) {
1135
+ if ((ts2.isFunctionDeclaration(stmt) || ts2.isMethodSignature(stmt)) && stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword)) {
1136
+ const name = pickName(stmt);
1137
+ if (!name) continue;
1138
+ const fn = stmt;
1139
+ const dsl = fnSignatureDsl(fn.parameters, fn.type, 0);
1140
+ push({ name, dsl, kind: "function" });
1141
+ continue;
1142
+ }
1143
+ if (ts2.isFunctionDeclaration(stmt) && pickName(stmt)) {
1144
+ const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1145
+ if (!isExport) continue;
1146
+ const name = pickName(stmt);
1147
+ push({ name, dsl: fnSignatureDsl(stmt.parameters, stmt.type, 0), kind: "function" });
1148
+ continue;
1149
+ }
1150
+ if (ts2.isVariableStatement(stmt)) {
1151
+ const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1152
+ if (!isExport) continue;
1153
+ for (const d of stmt.declarationList.declarations) {
1154
+ const name = pickName(d);
1155
+ if (!name) continue;
1156
+ const dsl = typeToDsl(d.type, 0);
1157
+ if (dsl === void 0) {
1158
+ push({ name, note: "unmodeled type", kind: "value" });
1159
+ } else if (dsl.startsWith("fn(")) {
1160
+ push({ name, dsl, kind: "function" });
1161
+ } else {
1162
+ push({ name, dsl, kind: "value" });
1163
+ }
1164
+ }
1165
+ continue;
1166
+ }
1167
+ if (ts2.isClassDeclaration(stmt)) {
1168
+ const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1169
+ if (!isExport) continue;
1170
+ const name = pickName(stmt);
1171
+ if (!name) {
1172
+ push({ name: "default", note: "anonymous class", kind: "class" });
1173
+ continue;
1174
+ }
1175
+ let anyMethod = false;
1176
+ for (const m of stmt.members) {
1177
+ if (ts2.isMethodDeclaration(m) || ts2.isMethodSignature(m)) {
1178
+ const mname = pickName(m);
1179
+ if (!mname) continue;
1180
+ anyMethod = true;
1181
+ push({
1182
+ name: `${name}.${mname}`,
1183
+ dsl: fnSignatureDsl(m.parameters, m.type, 0),
1184
+ kind: "function"
1185
+ });
1186
+ }
1187
+ }
1188
+ if (!anyMethod) {
1189
+ push({ name, note: "class without public methods projected", kind: "class" });
1190
+ }
1191
+ continue;
1192
+ }
1193
+ if (ts2.isExportAssignment(stmt)) {
1194
+ push({ name: "default", note: "export assignment \u2014 bind via sidecar default", kind: "other" });
1195
+ continue;
1196
+ }
1197
+ if (ts2.isInterfaceDeclaration(stmt) || ts2.isTypeAliasDeclaration(stmt)) {
1198
+ continue;
1199
+ }
1200
+ }
1201
+ return out;
1202
+ }
1203
+ function draftExportName(name) {
1204
+ return name.includes(".") ? name.replace(/\./g, "_") : name;
1205
+ }
1206
+ function formatDtsContractDraft(label, files, exports) {
1207
+ const draftable = exports.filter((e) => e.dsl !== void 0);
1208
+ const used = /* @__PURE__ */ new Set(["fn"]);
1209
+ for (const e of draftable) {
1210
+ for (const b of BUILDERS) {
1211
+ if (new RegExp(`\\b${b}\\b`).test(e.dsl)) used.add(b);
1212
+ }
1213
+ }
1214
+ if (draftable.some((e) => /\bany\s*\(/.test(e.dsl))) used.add("any");
1215
+ const importList = BUILDERS.filter((b) => used.has(b)).join(", ");
1216
+ const lines = [
1217
+ "// @nudo:draft",
1218
+ `// Generated by \`nudo contract --from-dts\` from ${label}`,
1219
+ `// Sources: ${files.map((f) => basename(f)).join(", ")}`,
1220
+ "//",
1221
+ "// Reverse-engineered from TypeScript declarations \u2014 NOT a sidecar contract.",
1222
+ "// This file is never loaded for check. Review each export, then copy it",
1223
+ "// into <target>.nudo.js to accept (that is when obligations go live).",
1224
+ "//",
1225
+ "// After accept: strengthen with Pred builders (number().gt(0) \u2026) \u2014",
1226
+ "// dts cannot express algebraic implications (x>0 \u21D2 x+1>1).",
1227
+ "",
1228
+ `import { ${importList} } from "@nudojs/core";`,
1229
+ ""
1230
+ ];
1231
+ if (draftable.length === 0) {
1232
+ lines.push("// (no projectable exports)");
1233
+ lines.push("");
1234
+ }
1235
+ for (const e of exports) {
1236
+ if (e.dsl === void 0) {
1237
+ lines.push(`// ${e.name} \u2014 skipped (${e.note ?? "unmodeled"})`);
1238
+ continue;
1239
+ }
1240
+ if (e.note) lines.push(`// ${e.name} \u2014 ${e.note}`);
1241
+ else lines.push(`// ${e.name} \u2014 from TypeScript`);
1242
+ lines.push(`export const ${draftExportName(e.name)} = ${e.dsl};`);
1243
+ if (e.name.includes(".")) {
1244
+ lines.push(`// sidecar key may also be written as \`${e.name}\``);
1245
+ }
1246
+ lines.push("");
1247
+ }
1248
+ return lines.join("\n");
1249
+ }
1250
+ function readDtsFiles(paths, maxFiles = 16) {
1251
+ const out = [];
1252
+ const walk = (dir, depth) => {
1253
+ if (depth > 6 || out.length >= maxFiles * 3) return;
1254
+ let entries;
1255
+ try {
1256
+ entries = readdirSync(dir);
1257
+ } catch {
1258
+ return;
1259
+ }
1260
+ for (const name of entries) {
1261
+ if (name === "node_modules" || name === "test" || name === "__tests__") continue;
1262
+ const p = join(dir, name);
1263
+ let st;
1264
+ try {
1265
+ st = statSync2(p);
1266
+ } catch {
1267
+ continue;
1268
+ }
1269
+ if (st.isDirectory()) walk(p, depth + 1);
1270
+ else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) out.push(p);
1271
+ }
1272
+ };
1273
+ for (const p of paths) {
1274
+ if (!existsSync(p)) continue;
1275
+ const st = statSync2(p);
1276
+ if (st.isDirectory()) walk(p, 0);
1277
+ else if (/\.d\.ts$/.test(p) || /\.(m)?ts$/.test(p)) {
1278
+ if (!p.endsWith(".d.ts.map")) out.push(p);
1279
+ }
1280
+ }
1281
+ return out.slice(0, maxFiles);
1282
+ }
1283
+ function dtsToContractDraft(dtsFiles, label) {
1284
+ const files = dtsFiles.filter((f) => existsSync(f));
1285
+ const exports = [];
1286
+ let skippedFiles = 0;
1287
+ for (const file of files) {
1288
+ let content;
1289
+ try {
1290
+ content = readFileSync2(file, "utf8");
1291
+ } catch {
1292
+ skippedFiles++;
1293
+ continue;
1294
+ }
1295
+ const sf = ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true);
1296
+ for (const e of collectExports(sf)) {
1297
+ if (exports.some((x) => x.name === e.name && x.dsl)) continue;
1298
+ exports.push(e);
1299
+ }
1300
+ }
1301
+ const name = label ?? (files[0] ? basename(files[0]).replace(/\.d\.ts$/, "") : "dts");
1302
+ const draftSource = formatDtsContractDraft(name, files, exports);
1303
+ return {
1304
+ label: name,
1305
+ files,
1306
+ exports,
1307
+ draftSource,
1308
+ stats: {
1309
+ files: files.length - skippedFiles,
1310
+ exports: exports.filter((e) => e.dsl !== void 0).length,
1311
+ skipped: exports.filter((e) => e.dsl === void 0).length + skippedFiles
1312
+ }
1313
+ };
1314
+ }
1315
+ function dtsPathToContractDraft(path, label) {
1316
+ const files = readDtsFiles([path]);
1317
+ return dtsToContractDraft(files, label ?? basename(path).replace(/\.d\.ts$/, ""));
1318
+ }
1319
+
1320
+ // src/harvest-package.ts
1321
+ import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
1322
+ import { join as join2, resolve, dirname as dirname2, basename as basename2 } from "path";
1323
+ import { formatShape as formatShape2 } from "@nudojs/core";
1324
+ function resolvePackageRoot(pkg, fromDir = process.cwd()) {
1325
+ let dir = resolve(fromDir);
1326
+ const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
1327
+ for (let i = 0; i < 8; i++) {
1328
+ const typesPath = join2(dir, "node_modules", "@types", bare);
1329
+ if (existsSync2(typesPath)) return typesPath;
1330
+ const pkgPath = join2(dir, "node_modules", pkg);
1331
+ if (existsSync2(pkgPath)) return pkgPath;
1332
+ const parent = resolve(dir, "..");
1333
+ if (parent === dir) break;
1334
+ dir = parent;
1335
+ }
1336
+ return void 0;
1337
+ }
1338
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "test", "tests", "docs", "doc", "examples", "__tests__"]);
1339
+ var MAX_DTS_BYTES = 15e5;
1340
+ function dtsPriority(p, root) {
1341
+ const base = basename2(p);
1342
+ const rel = p.slice(root.length);
1343
+ if (base === "index.d.ts") return 0;
1344
+ if (base === "index.d.mts") return 1;
1345
+ if (rel.includes("/types/") && base === "index.d.ts") return 2;
1346
+ if (base.endsWith(".d.ts") && !base.includes(".")) return 3;
1347
+ return 10;
1348
+ }
1349
+ function collectDtsFiles(root, maxFiles = 8) {
1350
+ const out = [];
1351
+ const walk = (dir) => {
1352
+ if (out.length >= maxFiles * 3) return;
1353
+ let entries;
1354
+ try {
1355
+ entries = readdirSync2(dir);
1356
+ } catch {
1357
+ return;
1358
+ }
1359
+ for (const name of entries) {
1360
+ if (out.length >= maxFiles * 3) return;
1361
+ if (SKIP_DIRS.has(name)) continue;
1362
+ const p = join2(dir, name);
1363
+ let st;
1364
+ try {
1365
+ st = statSync3(p);
1366
+ } catch {
1367
+ continue;
1368
+ }
1369
+ if (st.isDirectory()) walk(p);
1370
+ else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) {
1371
+ if (st.size > MAX_DTS_BYTES) continue;
1372
+ out.push(p);
1373
+ }
1374
+ }
1375
+ };
1376
+ walk(root);
1377
+ out.sort((a, b) => dtsPriority(a, root) - dtsPriority(b, root) || a.length - b.length);
1378
+ return out.slice(0, maxFiles);
1379
+ }
1380
+ function entryDtsFromPackageJson(root) {
1381
+ const pj = join2(root, "package.json");
1382
+ if (!existsSync2(pj)) return void 0;
1383
+ try {
1384
+ const raw = JSON.parse(readFileSync3(pj, "utf8"));
1385
+ const entry = raw.types ?? raw.typings;
1386
+ if (typeof entry === "string" && entry.endsWith(".d.ts")) {
1387
+ const p = resolve(root, entry);
1388
+ if (existsSync2(p)) {
1389
+ const st = statSync3(p);
1390
+ if (st.size <= MAX_DTS_BYTES) return p;
1391
+ }
1392
+ }
1393
+ } catch {
1394
+ }
1395
+ return void 0;
1396
+ }
1397
+ function collectDtsFromEntry(entry, maxFiles = 200) {
1398
+ const files = [];
1399
+ const seen = /* @__PURE__ */ new Set();
1400
+ const queue = [entry];
1401
+ const REFERENCE_PATH_REGEX = /<reference\s+path=["']([^"']+)["']\s*\/>/g;
1402
+ const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
1403
+ while (queue.length > 0 && files.length < maxFiles) {
1404
+ const current = queue.shift();
1405
+ if (seen.has(current) || !existsSync2(current)) continue;
1406
+ seen.add(current);
1407
+ if (!current.endsWith(".d.ts")) continue;
1408
+ files.push(current);
1409
+ let text;
1410
+ try {
1411
+ text = readFileSync3(current, "utf-8");
1412
+ } catch {
1413
+ continue;
1414
+ }
1415
+ const dir = dirname2(current);
1416
+ for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
1417
+ queue.push(resolve(dir, match[1]));
1418
+ }
1419
+ for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
1420
+ const base = resolve(dir, match[1]);
1421
+ for (const candidate of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
1422
+ if (existsSync2(candidate)) {
1423
+ queue.push(candidate);
1424
+ break;
1425
+ }
1426
+ }
1427
+ }
1428
+ }
1429
+ return files;
1430
+ }
1431
+ function harvestPackage(pkg, fromDir, maxFiles = 24) {
1432
+ const root = resolvePackageRoot(pkg, fromDir);
1433
+ if (!root) return { error: `package not found: ${pkg}` };
1434
+ const entry = entryDtsFromPackageJson(root) ?? join2(root, "index.d.ts");
1435
+ let dtsFiles = existsSync2(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
1436
+ if (dtsFiles.length === 0) {
1437
+ const collected = collectDtsFiles(root, maxFiles);
1438
+ dtsFiles = entry && existsSync2(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
1439
+ }
1440
+ if (dtsFiles.length === 0) {
1441
+ return { error: `no .d.ts under ${root}` };
1442
+ }
1443
+ const env = harvestDts(dtsFiles);
1444
+ return { pkg, root, dtsFiles, env };
1445
+ }
1446
+ function formatHarvestSummary(h) {
1447
+ const lines = [];
1448
+ lines.push(`${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols`);
1449
+ const mods = Object.keys(h.env.modules);
1450
+ for (const m of mods.slice(0, 5)) {
1451
+ const exports = h.env.modules[m];
1452
+ const names = Object.keys(exports).slice(0, 12);
1453
+ lines.push(` module "${m}": ${names.join(", ")}${Object.keys(exports).length > 12 ? "\u2026" : ""}`);
1454
+ }
1455
+ if (mods.length > 5) lines.push(` \u2026 ${mods.length - 5} more modules`);
1456
+ return lines.join("\n");
1457
+ }
1458
+ function lookupHarvested(h, moduleName, exportName) {
1459
+ const mod = h.env.modules[moduleName];
1460
+ const a = mod?.[exportName] ?? h.env.globals[exportName];
1461
+ if (!a) return void 0;
1462
+ return formatShape2(a);
1463
+ }
1464
+
1465
+ // src/harvest-auto.ts
1466
+ import { parse } from "@nudojs/parser";
1467
+
1468
+ // src/collect-specs.ts
1469
+ import { foldStaticStringExpr } from "@nudojs/core";
1470
+ function collectDependencySpecs(ast) {
1471
+ const specs = [];
1472
+ for (const stmt of ast.program.body) {
1473
+ if (stmt.type === "ImportDeclaration") {
1474
+ specs.push(stmt.source.value);
1475
+ }
1476
+ if (stmt.type === "ExportNamedDeclaration" && stmt.source) {
1477
+ specs.push(stmt.source.value);
1478
+ }
1479
+ if (stmt.type === "ExportAllDeclaration" && stmt.source) {
1480
+ specs.push(stmt.source.value);
1481
+ }
1482
+ collectRequires(stmt, specs);
1483
+ }
1484
+ return specs;
1485
+ }
1486
+ function collectRequires(node, out) {
1487
+ const visit = (n) => {
1488
+ if (!n || typeof n !== "object") return;
1489
+ const obj = n;
1490
+ if (obj.type === "CallExpression" && obj.callee) {
1491
+ const c = obj.callee;
1492
+ let spec;
1493
+ if (c.type === "Identifier" && c.name === "require") {
1494
+ spec = foldStaticStringExpr(obj.arguments?.[0]);
1495
+ } else if (c.type === "MemberExpression" && !c.computed && c.object?.type === "Identifier" && c.object.name === "require" && c.property?.type === "Identifier" && c.property.name === "resolve") {
1496
+ spec = foldStaticStringExpr(obj.arguments?.[0]);
1497
+ }
1498
+ if (spec !== void 0) out.push(spec);
1499
+ }
1500
+ for (const key of Object.keys(obj)) {
1501
+ if (key === "loc" || key === "start" || key === "end") continue;
1502
+ const v = obj[key];
1503
+ if (Array.isArray(v)) v.forEach(visit);
1504
+ else if (v && typeof v === "object") visit(v);
1505
+ }
1506
+ };
1507
+ visit(node);
1508
+ }
1509
+
1510
+ // src/harvest-disk.ts
1511
+ import { createHash as createHash2 } from "crypto";
1512
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
1513
+ import { homedir } from "os";
1514
+ import { join as join3, dirname as dirname3 } from "path";
1515
+
1516
+ // src/harvest-json.ts
1517
+ import { createHash } from "crypto";
1518
+ import {
1519
+ abs,
1520
+ confJoin,
1521
+ relationFn as relationFn2,
1522
+ getFnImpl as getFnImpl2
1523
+ } from "@nudojs/core";
1524
+ var HARVEST_DISK_ABI = "nudo-harvest-disk-v7";
1525
+ function sha256Hex(data) {
1526
+ return createHash("sha256").update(data).digest("hex");
1527
+ }
1528
+ function absToHarvestSig(a) {
1529
+ if (a.term?.op === "var") {
1530
+ return { k: "tvar", name: a.term.id };
1531
+ }
1532
+ const s = a.shape;
1533
+ switch (s.k) {
1534
+ case "prim":
1535
+ return { k: "prim", type: s.type };
1536
+ case "unknown":
1537
+ return { k: "unknown" };
1538
+ case "any":
1539
+ return { k: "any" };
1540
+ case "never":
1541
+ return { k: "never" };
1542
+ case "arr":
1543
+ return { k: "arr", element: absToHarvestSig(s.element) };
1544
+ case "tuple":
1545
+ return { k: "tuple", elements: s.elements.map(absToHarvestSig) };
1546
+ case "obj": {
1547
+ const slots = {};
1548
+ for (const [key, slot] of Object.entries(s.slots)) {
1549
+ slots[key] = {
1550
+ v: absToHarvestSig(slot.value),
1551
+ ...slot.optional ? { opt: true } : {}
1552
+ };
1553
+ }
1554
+ return { k: "obj", slots };
1555
+ }
1556
+ case "fn": {
1557
+ const rel = getFnImpl2(a)?.relation;
1558
+ const pts = rel?.paramTypes ?? s.paramTypes ?? s.params.map(() => ({ shape: { k: "unknown" }, conf: "mock" }));
1559
+ const paramTypes = pts.map(absToHarvestSig);
1560
+ const retSrc = rel?.returnType ?? s.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
1561
+ return {
1562
+ k: "fn",
1563
+ params: s.params,
1564
+ paramTypes,
1565
+ returns: absToHarvestSig(retSrc)
1566
+ };
1567
+ }
1568
+ case "sum":
1569
+ return { k: "sum", members: s.members.map(absToHarvestSig) };
1570
+ case "eff":
1571
+ return {
1572
+ k: s.eff === "generator" ? "generator" : "promise",
1573
+ value: absToHarvestSig(s.inner)
1574
+ };
1575
+ case "brand":
1576
+ return { k: "brand", name: s.name, shape: absToHarvestSig(s.shape) };
1577
+ default:
1578
+ return { k: "unknown" };
1579
+ }
1580
+ }
1581
+ function harvestSigToAbs(sig) {
1582
+ const mark = (a) => {
1583
+ a.conf = confJoin(a.conf, "mock");
1584
+ return a;
1585
+ };
1586
+ switch (sig.k) {
1587
+ case "prim":
1588
+ return mark(
1589
+ abs(
1590
+ { k: "prim", type: sig.type },
1591
+ void 0,
1592
+ void 0,
1593
+ "mock"
1594
+ )
1595
+ );
1596
+ case "unknown":
1597
+ return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
1598
+ case "any":
1599
+ return mark(abs({ k: "any" }, void 0, void 0, "mock"));
1600
+ case "never":
1601
+ return mark(abs({ k: "never" }, void 0, void 0, "mock"));
1602
+ case "lit": {
1603
+ const t = typeof sig.value === "number" ? "number" : typeof sig.value === "string" ? "string" : typeof sig.value === "boolean" ? "boolean" : typeof sig.value === "bigint" ? "bigint" : null;
1604
+ if (sig.value === null || t === null) {
1605
+ return mark(
1606
+ abs({ k: "unknown" }, { op: "lit", value: sig.value }, void 0, "mock")
1607
+ );
1608
+ }
1609
+ return mark(
1610
+ abs(
1611
+ { k: "prim", type: t },
1612
+ { op: "lit", value: sig.value },
1613
+ void 0,
1614
+ "mock"
1615
+ )
1616
+ );
1617
+ }
1618
+ case "arr":
1619
+ return mark(abs({ k: "arr", element: harvestSigToAbs(sig.element) }, void 0, void 0, "mock"));
1620
+ case "tuple":
1621
+ return mark(
1622
+ abs({ k: "tuple", elements: sig.elements.map(harvestSigToAbs) }, void 0, void 0, "mock")
1623
+ );
1624
+ case "obj": {
1625
+ const slots = {};
1626
+ for (const [key, slot] of Object.entries(sig.slots)) {
1627
+ slots[key] = {
1628
+ value: harvestSigToAbs(slot.v),
1629
+ ...slot.opt ? { optional: true } : {}
1630
+ };
1631
+ }
1632
+ return mark(abs({ k: "obj", slots }, void 0, void 0, "mock"));
1633
+ }
1634
+ case "tvar":
1635
+ return mark(abs({ k: "any" }, { op: "var", id: sig.name }, void 0, "mock"));
1636
+ case "fn": {
1637
+ const paramTypes = sig.paramTypes.map(harvestSigToAbs);
1638
+ const returnType = harvestSigToAbs(sig.returns);
1639
+ return mark(
1640
+ relationFn2(paramTypes, returnType, {
1641
+ params: sig.params,
1642
+ conf: "mock"
1643
+ })
1644
+ );
1645
+ }
1646
+ case "sum":
1647
+ return mark(
1648
+ abs({ k: "sum", members: sig.members.map(harvestSigToAbs) }, void 0, void 0, "mock")
1649
+ );
1650
+ case "promise":
1651
+ case "generator":
1652
+ return mark(
1653
+ abs(
1654
+ {
1655
+ k: "eff",
1656
+ eff: sig.k === "generator" ? "generator" : "promise",
1657
+ inner: harvestSigToAbs(sig.value)
1658
+ },
1659
+ void 0,
1660
+ void 0,
1661
+ "mock"
1662
+ )
1663
+ );
1664
+ case "brand":
1665
+ return mark(
1666
+ abs(
1667
+ { k: "brand", name: sig.name, shape: harvestSigToAbs(sig.shape) },
1668
+ void 0,
1669
+ void 0,
1670
+ "mock"
1671
+ )
1672
+ );
1673
+ default:
1674
+ return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
1675
+ }
1676
+ }
1677
+ function serializeHarvestJson(pkg, env, meta) {
1678
+ const modules = {};
1679
+ for (const [mod, rec] of Object.entries(env.modules)) {
1680
+ const named = {};
1681
+ for (const [k, v] of Object.entries(rec)) named[k] = absToHarvestSig(v);
1682
+ modules[mod] = named;
1683
+ }
1684
+ const globals = {};
1685
+ for (const [k, v] of Object.entries(env.globals)) globals[k] = absToHarvestSig(v);
1686
+ return {
1687
+ v: 1,
1688
+ pkg,
1689
+ ...meta.pkgVersion ? { pkgVersion: meta.pkgVersion } : {},
1690
+ knobs: { maxFiles: meta.maxFiles, abi: HARVEST_DISK_ABI },
1691
+ dtsHash: meta.dtsHash,
1692
+ modules,
1693
+ globals,
1694
+ stats: env.stats
1695
+ };
1696
+ }
1697
+ function materializeHarvestJson(j) {
1698
+ if (!j || typeof j !== "object") return null;
1699
+ const h = j;
1700
+ if (h.v !== 1) return null;
1701
+ if (h.knobs?.abi !== HARVEST_DISK_ABI) return null;
1702
+ if (!h.modules || typeof h.modules !== "object") return null;
1703
+ try {
1704
+ const modules = {};
1705
+ for (const [mod, rec] of Object.entries(h.modules)) {
1706
+ const named = {};
1707
+ for (const [k, sig] of Object.entries(rec)) named[k] = harvestSigToAbs(sig);
1708
+ modules[mod] = named;
1709
+ }
1710
+ const globals = {};
1711
+ for (const [k, sig] of Object.entries(h.globals ?? {})) {
1712
+ globals[k] = harvestSigToAbs(sig);
1713
+ }
1714
+ return {
1715
+ globals,
1716
+ modules,
1717
+ stats: h.stats ?? { files: 0, symbols: 0, skipped: 0 }
1718
+ };
1719
+ } catch {
1720
+ return null;
1721
+ }
1722
+ }
1723
+ function harvestCacheKey(pkg, meta) {
1724
+ const raw = [
1725
+ HARVEST_DISK_ABI,
1726
+ pkg,
1727
+ meta.pkgVersion ?? "-",
1728
+ String(meta.maxFiles),
1729
+ meta.dtsHash
1730
+ ].join("\0");
1731
+ return sha256Hex(raw);
1732
+ }
1733
+
1734
+ // src/harvest-disk.ts
1735
+ function depsCacheRoot() {
1736
+ const env = process.env.NUDO_DEPS_CACHE_DIR;
1737
+ if (env === "off" || env === "0") return void 0;
1738
+ if (env && env.length > 0) return env;
1739
+ try {
1740
+ return join3(homedir(), ".cache", "nudo", "deps");
1741
+ } catch {
1742
+ return void 0;
1743
+ }
1744
+ }
1745
+ function readPkgVersion(root) {
1746
+ try {
1747
+ const raw = JSON.parse(readFileSync4(join3(root, "package.json"), "utf8"));
1748
+ return raw.version;
1749
+ } catch {
1750
+ return void 0;
1751
+ }
1752
+ }
1753
+ function dtsClosureHash(files) {
1754
+ const h = createHash2("sha256");
1755
+ for (const f of [...files].sort()) {
1756
+ h.update(f.replace(/\\/g, "/"));
1757
+ try {
1758
+ h.update(readFileSync4(f));
1759
+ } catch {
1760
+ h.update("miss");
1761
+ }
1762
+ }
1763
+ return h.digest("hex");
1764
+ }
1765
+ function diskPath(root, key) {
1766
+ return join3(root, key.slice(0, 2), `${key}.json`);
1767
+ }
1768
+ function readHarvestDisk(root, key) {
1769
+ try {
1770
+ const p = diskPath(root, key);
1771
+ if (!existsSync3(p)) return void 0;
1772
+ const parsed = JSON.parse(readFileSync4(p, "utf8"));
1773
+ if (parsed?.abi !== HARVEST_DISK_ABI) return void 0;
1774
+ return parsed.value;
1775
+ } catch {
1776
+ return void 0;
1777
+ }
1778
+ }
1779
+ function writeHarvestDisk(root, key, value) {
1780
+ try {
1781
+ const p = diskPath(root, key);
1782
+ mkdirSync(dirname3(p), { recursive: true });
1783
+ writeFileSync(p, JSON.stringify({ abi: HARVEST_DISK_ABI, value }), "utf8");
1784
+ } catch {
1785
+ }
1786
+ }
1787
+ function listPackageDts(pkg, fromDir, maxFiles) {
1788
+ const root = resolvePackageRoot(pkg, fromDir);
1789
+ if (!root) return null;
1790
+ const entry = (() => {
1791
+ try {
1792
+ const raw = JSON.parse(readFileSync4(join3(root, "package.json"), "utf8"));
1793
+ const e = raw.types ?? raw.typings;
1794
+ return typeof e === "string" && e.endsWith(".d.ts") ? join3(root, e) : void 0;
1795
+ } catch {
1796
+ return void 0;
1797
+ }
1798
+ })();
1799
+ let dtsFiles = entry && existsSync3(entry) ? collectDtsFromEntry(entry, Math.max(maxFiles, 24)) : [];
1800
+ if (dtsFiles.length === 0) {
1801
+ const collected = collectDtsFiles(root, maxFiles);
1802
+ dtsFiles = entry && existsSync3(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
1803
+ }
1804
+ if (dtsFiles.length === 0) return null;
1805
+ return { root, dtsFiles, pkgVersion: readPkgVersion(root) };
1806
+ }
1807
+ function harvestPackageWithDisk(pkg, fromDir, maxFiles = 24) {
1808
+ const cacheRoot = depsCacheRoot();
1809
+ const listed = listPackageDts(pkg, fromDir, maxFiles);
1810
+ if (!listed) return null;
1811
+ const key = harvestCacheKey(pkg, {
1812
+ dtsHash: dtsClosureHash(listed.dtsFiles),
1813
+ maxFiles,
1814
+ pkgVersion: listed.pkgVersion
1815
+ });
1816
+ if (cacheRoot) {
1817
+ const hit = readHarvestDisk(cacheRoot, key);
1818
+ if (hit) {
1819
+ const env = materializeHarvestJson(hit);
1820
+ if (env) {
1821
+ return { pkg, root: listed.root, dtsFiles: listed.dtsFiles, env };
1822
+ }
1823
+ }
1824
+ }
1825
+ let h = null;
1826
+ try {
1827
+ const raw = harvestPackage(pkg, fromDir, maxFiles);
1828
+ if (!("error" in raw)) h = raw;
1829
+ } catch {
1830
+ h = null;
1831
+ }
1832
+ if (!h) return null;
1833
+ if (cacheRoot) {
1834
+ writeHarvestDisk(
1835
+ cacheRoot,
1836
+ key,
1837
+ serializeHarvestJson(pkg, h.env, {
1838
+ dtsHash: dtsClosureHash(h.dtsFiles),
1839
+ maxFiles,
1840
+ pkgVersion: listed.pkgVersion
1841
+ })
1842
+ );
1843
+ }
1844
+ return h;
1845
+ }
1846
+ function loadHarvestEnvFromDisk(pkg, meta) {
1847
+ const root = depsCacheRoot();
1848
+ if (!root) return null;
1849
+ const key = harvestCacheKey(pkg, meta);
1850
+ const hit = readHarvestDisk(root, key);
1851
+ return hit ? materializeHarvestJson(hit) : null;
1852
+ }
1853
+
1854
+ // src/lru-map.ts
1855
+ var BoundedLruMap = class {
1856
+ map = /* @__PURE__ */ new Map();
1857
+ max;
1858
+ constructor(max) {
1859
+ this.max = max;
1860
+ }
1861
+ get size() {
1862
+ return this.map.size;
1863
+ }
1864
+ /** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
1865
+ getMax() {
1866
+ return this.max;
1867
+ }
1868
+ setMax(max) {
1869
+ this.max = max;
1870
+ this.trim();
1871
+ }
1872
+ has(key) {
1873
+ return this.map.has(key);
1874
+ }
1875
+ get(key) {
1876
+ const hit = this.map.get(key);
1877
+ if (hit === void 0) return void 0;
1878
+ this.map.delete(key);
1879
+ this.map.set(key, hit);
1880
+ return hit;
1881
+ }
1882
+ /** 命中不改变 LRU 序(只读探测) */
1883
+ peek(key) {
1884
+ return this.map.get(key);
1885
+ }
1886
+ set(key, value) {
1887
+ if (this.max <= 0) return;
1888
+ while (this.map.size >= this.max && !this.map.has(key)) {
1889
+ const oldest = this.map.keys().next().value;
1890
+ if (oldest === void 0) break;
1891
+ this.map.delete(oldest);
1892
+ }
1893
+ this.map.delete(key);
1894
+ this.map.set(key, value);
1895
+ }
1896
+ delete(key) {
1897
+ return this.map.delete(key);
1898
+ }
1899
+ clear() {
1900
+ this.map.clear();
1901
+ }
1902
+ keys() {
1903
+ return this.map.keys();
1904
+ }
1905
+ values() {
1906
+ return this.map.values();
1907
+ }
1908
+ entries() {
1909
+ return this.map.entries();
1910
+ }
1911
+ /** 压到当前 max(调低上限时收内存) */
1912
+ trim() {
1913
+ while (this.map.size > this.max) {
1914
+ const oldest = this.map.keys().next().value;
1915
+ if (oldest === void 0) break;
1916
+ this.map.delete(oldest);
1917
+ }
1918
+ }
1919
+ };
1920
+
1921
+ // src/harvest-auto.ts
1922
+ var NODE_BUILTINS = /* @__PURE__ */ new Set([
1923
+ "assert",
1924
+ "async_hooks",
1925
+ "buffer",
1926
+ "child_process",
1927
+ "cluster",
1928
+ "console",
1929
+ "constants",
1930
+ "crypto",
1931
+ "dgram",
1932
+ "diagnostics_channel",
1933
+ "dns",
1934
+ "domain",
1935
+ "events",
1936
+ "fs",
1937
+ "http",
1938
+ "http2",
1939
+ "https",
1940
+ "inspector",
1941
+ "module",
1942
+ "net",
1943
+ "os",
1944
+ "path",
1945
+ "perf_hooks",
1946
+ "process",
1947
+ "punycode",
1948
+ "querystring",
1949
+ "readline",
1950
+ "repl",
1951
+ "stream",
1952
+ "string_decoder",
1953
+ "timers",
1954
+ "tls",
1955
+ "trace_events",
1956
+ "tty",
1957
+ "url",
1958
+ "util",
1959
+ "v8",
1960
+ "vm",
1961
+ "wasi",
1962
+ "worker_threads",
1963
+ "zlib"
1964
+ ]);
1965
+ function barePackageName(spec) {
1966
+ if (!spec) return void 0;
1967
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) return void 0;
1968
+ const parts = spec.split("/");
1969
+ if (spec.startsWith("@")) {
1970
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
1971
+ }
1972
+ const name = parts[0];
1973
+ if (NODE_BUILTINS.has(name)) return void 0;
1974
+ return name;
1975
+ }
1976
+ function collectBarePackages(source) {
1977
+ try {
1978
+ const ast = parse(source);
1979
+ const names = /* @__PURE__ */ new Set();
1980
+ for (const spec of collectDependencySpecs(ast)) {
1981
+ const pkg = barePackageName(spec);
1982
+ if (pkg) names.add(pkg);
1983
+ }
1984
+ return [...names];
1985
+ } catch {
1986
+ return [];
1987
+ }
1988
+ }
1989
+ var HARVEST_CACHE_MAX = 128;
1990
+ var harvestCache = new BoundedLruMap(HARVEST_CACHE_MAX);
1991
+ function getHarvestCacheSize() {
1992
+ return harvestCache.size;
1993
+ }
1994
+ function harvestPackageCached(pkg, fromDir) {
1995
+ const key = `${fromDir}::${pkg}`;
1996
+ if (harvestCache.has(key)) return harvestCache.get(key);
1997
+ let result = null;
1998
+ try {
1999
+ result = harvestPackageWithDisk(pkg, fromDir);
2000
+ } catch {
2001
+ result = null;
2002
+ }
2003
+ harvestCache.set(key, result);
2004
+ return result;
2005
+ }
2006
+ function clearHarvestCache() {
2007
+ harvestCache.clear();
2008
+ }
2009
+ function autoHarvestModules(source, fromDir) {
2010
+ const packages = collectBarePackages(source);
2011
+ if (packages.length === 0) return {};
2012
+ const modules = {};
2013
+ for (const pkg of packages) {
2014
+ const h = harvestPackageCached(pkg, fromDir);
2015
+ if (!h) continue;
2016
+ for (const [mod, exports] of Object.entries(h.env.modules)) {
2017
+ modules[mod] = exports;
2018
+ if (!modules[pkg]) modules[pkg] = exports;
2019
+ const base = mod.replace(/^@types\//, "").replace(/\\/g, "/");
2020
+ if (base && !modules[base]) modules[base] = exports;
2021
+ }
2022
+ for (const [k, v] of Object.entries(h.env.globals)) {
2023
+ if (!modules[pkg]) modules[pkg] = {};
2024
+ if (modules[pkg][k] === void 0) modules[pkg][k] = v;
2025
+ }
2026
+ }
2027
+ return modules;
2028
+ }
2029
+
2030
+ // src/harvest-to-abs.ts
2031
+ import {
2032
+ confJoin as confJoin2,
2033
+ absFunction,
2034
+ getFnImpl as getFnImpl3,
2035
+ instantiateReturn
2036
+ } from "@nudojs/core";
2037
+ import { dirname as dirname4 } from "path";
2038
+ function markMockConf(a) {
2039
+ a.conf = confJoin2(a.conf, "mock");
2040
+ return a;
2041
+ }
2042
+ function harvestedValueToAbs(a) {
2043
+ if (a.shape.k === "fn") {
2044
+ const ret = a.shape.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
2045
+ const relation = getFnImpl3(a)?.relation;
2046
+ const params = a.shape.params.length > 0 ? a.shape.params : a.shape.paramTypes?.map((_, i) => `_arg${i}`) ?? ["...args"];
2047
+ const dummyBody = {
2048
+ type: "BlockStatement",
2049
+ body: [],
2050
+ directives: []
2051
+ };
2052
+ return markMockConf(
2053
+ absFunction(params, {
2054
+ body: dummyBody,
2055
+ apply: (args) => {
2056
+ if (relation) {
2057
+ return markMockConf(instantiateReturn(a, args));
2058
+ }
2059
+ return markMockConf({ ...ret });
2060
+ },
2061
+ ...relation ? { relation } : {}
2062
+ })
2063
+ );
2064
+ }
2065
+ return markMockConf({ ...a });
2066
+ }
2067
+ function harvestToAbsModules(pkg, fromDir) {
2068
+ const h = harvestPackageCached(pkg, fromDir);
2069
+ if (!h) return {};
2070
+ return packageHarvestToAbsModules(pkg, h);
2071
+ }
2072
+ function packageHarvestToAbsModules(pkg, h) {
2073
+ const out = {};
2074
+ const convertRecord = (rec) => {
2075
+ const named = {};
2076
+ for (const [k, v] of Object.entries(rec)) {
2077
+ named[k] = harvestedValueToAbs(v);
2078
+ }
2079
+ return named;
2080
+ };
2081
+ const register = (key, exports) => {
2082
+ if (!out[key]) out[key] = exports;
2083
+ };
2084
+ for (const [mod, rec] of Object.entries(h.env.modules)) {
2085
+ const named = convertRecord(rec);
2086
+ const exports = { named };
2087
+ const def = rec["default"];
2088
+ if (def) exports.default = harvestedValueToAbs(def);
2089
+ register(mod, exports);
2090
+ register(pkg, exports);
2091
+ const stripped = mod.replace(/^@types\//, "").replace(/\\/g, "/");
2092
+ if (stripped) register(stripped, exports);
2093
+ const base = stripped.split("/").filter(Boolean).pop();
2094
+ if (base && base !== pkg) register(base, exports);
2095
+ }
2096
+ const globalNamed = convertRecord(h.env.globals);
2097
+ if (Object.keys(globalNamed).length > 0) {
2098
+ const existing = out[pkg];
2099
+ if (existing) {
2100
+ out[pkg] = { named: { ...globalNamed, ...existing.named }, default: existing.default };
2101
+ } else {
2102
+ out[pkg] = { named: globalNamed };
2103
+ }
2104
+ }
2105
+ return out;
2106
+ }
2107
+ function bareSpecToAbsModules(spec, fromFile) {
2108
+ if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) {
2109
+ return void 0;
2110
+ }
2111
+ const parts = spec.split("/");
2112
+ const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
2113
+ if (!pkg) return void 0;
2114
+ const table = harvestToAbsModules(pkg, dirname4(fromFile));
2115
+ return table[spec] ?? table[pkg];
2116
+ }
2117
+
2118
+ // src/harvest-node.ts
2119
+ import { existsSync as existsSync4, statSync as statSync4, readFileSync as readFileSync5 } from "fs";
2120
+ import { join as join4 } from "path";
2121
+ import { createEnvironment } from "@nudojs/core";
2122
+ import { defineEnv as defineNodeEnv } from "@nudojs/env/node";
2123
+ import { defineEnv as defineEsEnv } from "@nudojs/env/es";
2124
+ var HARVEST_NODE_DEFAULT_MAX_FILES = 12;
2125
+ var HARVEST_NODE_DEFAULT_MAX_MS = 2500;
2126
+ var NODE_HARVEST_CACHE_MAX = 32;
2127
+ var nodeHarvestCache = new BoundedLruMap(NODE_HARVEST_CACHE_MAX);
2128
+ function notFoundCacheKey(fromDir) {
2129
+ return `not-found|@types/node|${fromDir ?? ""}`;
2130
+ }
2131
+ function nodeHarvestCacheKey(root, maxFiles, maxMs) {
2132
+ let sig = "nostat";
2133
+ try {
2134
+ const st = statSync4(join4(root, "package.json"));
2135
+ sig = `${st.size}:${Math.floor(st.mtimeMs)}`;
2136
+ } catch {
2137
+ }
2138
+ return `${root}|${maxFiles}|${maxMs}|${sig}`;
2139
+ }
2140
+ function clearNodeHarvestCache() {
2141
+ nodeHarvestCache.clear();
2142
+ }
2143
+ function getNodeHarvestCacheSize() {
2144
+ return nodeHarvestCache.size;
2145
+ }
2146
+ function isHarvestNodeDisabled(env = process.env) {
2147
+ return env.NUDO_HARVEST_NODE === "off";
2148
+ }
2149
+ function readPkgVersion2(root) {
2150
+ try {
2151
+ const raw = JSON.parse(readFileSync5(join4(root, "package.json"), "utf8"));
2152
+ return raw.version;
2153
+ } catch {
2154
+ return void 0;
2155
+ }
2156
+ }
2157
+ function handwrittenNodeEnv() {
2158
+ try {
2159
+ const globalEnv = createEnvironment();
2160
+ const allModules = {};
2161
+ const allGlobals = {};
2162
+ for (const def of [defineEsEnv(), defineNodeEnv()]) {
2163
+ for (const [key, value] of Object.entries(def.globals)) {
2164
+ allGlobals[key] = value;
2165
+ globalEnv.bind(key, value);
2166
+ }
2167
+ if (def.modules) {
2168
+ for (const [modName, exports] of Object.entries(def.modules)) {
2169
+ allModules[modName] = { ...allModules[modName], ...exports };
2170
+ }
2171
+ }
2172
+ }
2173
+ return {
2174
+ modules: allModules,
2175
+ globals: allGlobals,
2176
+ stats: {
2177
+ files: 0,
2178
+ symbols: Object.keys(allGlobals).length,
2179
+ skipped: 0
2180
+ }
2181
+ };
2182
+ } catch {
2183
+ return null;
2184
+ }
2185
+ }
2186
+ function degradeToHandwritten(error, reason, cached = false) {
2187
+ const env = handwrittenNodeEnv();
2188
+ if (env) {
2189
+ return {
2190
+ ok: true,
2191
+ env,
2192
+ root: "@nudojs/env",
2193
+ files: 0,
2194
+ stats: env.stats,
2195
+ cached,
2196
+ degraded: true
2197
+ };
2198
+ }
2199
+ return { ok: false, error, reason, ...cached ? { cached: true } : {} };
2200
+ }
2201
+ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, maxMs = HARVEST_NODE_DEFAULT_MAX_MS) {
2202
+ if (isHarvestNodeDisabled()) {
2203
+ return {
2204
+ ok: false,
2205
+ error: "harvest disabled via NUDO_HARVEST_NODE=off",
2206
+ reason: "disabled"
2207
+ };
2208
+ }
2209
+ const root = resolvePackageRoot("@types/node", fromDir) ?? resolvePackageRoot("node", fromDir);
2210
+ if (!root || !existsSync4(root)) {
2211
+ const missKey = notFoundCacheKey(fromDir);
2212
+ const miss = nodeHarvestCache.get(missKey);
2213
+ if (miss) {
2214
+ return miss.ok ? { ...miss, cached: true } : { ...miss, cached: true };
2215
+ }
2216
+ const result = degradeToHandwritten("@types/node not found", "not-found");
2217
+ nodeHarvestCache.set(missKey, result);
2218
+ return result;
2219
+ }
2220
+ const key = nodeHarvestCacheKey(root, maxFiles, maxMs);
2221
+ const hit = nodeHarvestCache.get(key);
2222
+ if (hit) {
2223
+ return hit.ok ? { ...hit, cached: true } : { ...hit, cached: true };
2224
+ }
2225
+ const dts = collectDtsFiles(root, maxFiles);
2226
+ if (dts.length === 0) {
2227
+ const result = degradeToHandwritten(`no .d.ts under ${root}`, "no-dts");
2228
+ nodeHarvestCache.set(key, result);
2229
+ return result;
2230
+ }
2231
+ const diskRoot = depsCacheRoot();
2232
+ const dtsHash = dtsClosureHash(dts);
2233
+ const pkgVersion = readPkgVersion2(root);
2234
+ const diskKey = harvestCacheKey("@types/node", {
2235
+ dtsHash,
2236
+ maxFiles,
2237
+ ...pkgVersion !== void 0 ? { pkgVersion } : {}
2238
+ });
2239
+ if (diskRoot) {
2240
+ const diskHit = readHarvestDisk(diskRoot, diskKey);
2241
+ const env = diskHit ? materializeHarvestJson(diskHit) : null;
2242
+ if (env) {
2243
+ const stats = {
2244
+ files: env.stats.files,
2245
+ symbols: env.stats.symbols,
2246
+ skipped: env.stats.skipped
2247
+ };
2248
+ const result = {
2249
+ ok: true,
2250
+ env,
2251
+ root,
2252
+ files: stats.files,
2253
+ stats,
2254
+ cached: true
2255
+ };
2256
+ nodeHarvestCache.set(key, result);
2257
+ return result;
2258
+ }
2259
+ }
2260
+ try {
2261
+ const env = harvestDts(dts, { maxMs });
2262
+ const stats = {
2263
+ files: env.stats.files,
2264
+ symbols: env.stats.symbols,
2265
+ skipped: env.stats.skipped
2266
+ };
2267
+ if (diskRoot) {
2268
+ writeHarvestDisk(
2269
+ diskRoot,
2270
+ diskKey,
2271
+ serializeHarvestJson("@types/node", env, {
2272
+ dtsHash,
2273
+ maxFiles,
2274
+ ...pkgVersion !== void 0 ? { pkgVersion } : {}
2275
+ })
2276
+ );
2277
+ }
2278
+ const result = {
2279
+ ok: true,
2280
+ env,
2281
+ root,
2282
+ files: stats.files,
2283
+ stats,
2284
+ cached: false
2285
+ };
2286
+ nodeHarvestCache.set(key, result);
2287
+ return result;
2288
+ } catch (e) {
2289
+ const result = degradeToHandwritten(
2290
+ `harvest failed: ${e.message}`,
2291
+ "failed"
2292
+ );
2293
+ nodeHarvestCache.set(key, result);
2294
+ return result;
2295
+ }
2296
+ }
2297
+ function summarizeNodeEnv(env) {
2298
+ return {
2299
+ modules: Object.keys(env.modules).sort(),
2300
+ globals: Object.keys(env.globals).sort().slice(0, 50),
2301
+ symbolCount: env.stats.symbols
2302
+ };
2303
+ }
687
2304
  export {
2305
+ HARVEST_NODE_DEFAULT_MAX_FILES,
2306
+ HARVEST_NODE_DEFAULT_MAX_MS,
2307
+ absToHarvestSig,
2308
+ autoHarvestModules,
2309
+ barePackageName,
2310
+ bareSpecToAbsModules,
2311
+ clearHarvestCache,
2312
+ clearNodeHarvestCache,
2313
+ collectBarePackages,
2314
+ collectDtsFromEntry,
2315
+ depsCacheRoot,
2316
+ dtsClosureHash,
2317
+ dtsPathToContractDraft,
2318
+ dtsToContractDraft,
688
2319
  emitEnvModule,
689
- harvestDts
2320
+ formatDtsContractDraft,
2321
+ formatHarvestSummary,
2322
+ getHarvestCacheSize,
2323
+ getNodeHarvestCacheSize,
2324
+ handwrittenNodeEnv,
2325
+ harvestCacheKey,
2326
+ harvestDts,
2327
+ harvestNodeTypes,
2328
+ harvestPackage,
2329
+ harvestPackageCached,
2330
+ harvestPackageWithDisk,
2331
+ harvestSigToAbs,
2332
+ harvestToAbsModules,
2333
+ harvestedValueToAbs,
2334
+ isHarvestNodeDisabled,
2335
+ loadHarvestEnvFromDisk,
2336
+ lookupHarvested,
2337
+ materializeHarvestJson,
2338
+ packageHarvestToAbsModules,
2339
+ readHarvestDisk,
2340
+ resolvePackageRoot,
2341
+ serializeHarvestJson,
2342
+ summarizeNodeEnv,
2343
+ writeHarvestDisk
690
2344
  };