@nudojs/harvester 0.4.2 → 0.4.3

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +72 -2
  2. package/dist/index.js +2737 -500
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -9,12 +9,16 @@ import {
9
9
  joinAbs,
10
10
  relationFn,
11
11
  getFnImpl,
12
- substAbs
12
+ substAbs,
13
+ formatShape,
14
+ absShapeKey,
15
+ termToString
13
16
  } from "@nudojs/core";
14
17
  import { absLit } from "@nudojs/core/internal";
15
18
  var MAX_ALIAS_DEPTH = 8;
16
19
  function scopeKey(scope) {
17
- return scope.moduleName ?? "\0global";
20
+ const base = scope.moduleName ?? "\0global";
21
+ return scope.nsRec && scope.ns.length > 0 ? `${base}\0ns:${scope.ns.join(".")}` : base;
18
22
  }
19
23
  function absExact(shape) {
20
24
  return makeAbs(shape, void 0, void 0, "exact");
@@ -115,10 +119,14 @@ function harvestDts(files, opts) {
115
119
  typeParams: /* @__PURE__ */ new Set(),
116
120
  moduleAliases: [],
117
121
  importEquals: /* @__PURE__ */ new Map(),
122
+ exportEquals: /* @__PURE__ */ new Map(),
118
123
  declaredModules: /* @__PURE__ */ new Set(),
119
124
  pending: [],
120
125
  symbols: 0,
121
- skipped: 0
126
+ skipped: 0,
127
+ degraded: 0,
128
+ degradedMark: 0,
129
+ anonDefaultClass: 0
122
130
  };
123
131
  let parsed = 0;
124
132
  for (const file of files) {
@@ -143,10 +151,16 @@ function harvestDts(files, opts) {
143
151
  parsed++;
144
152
  }
145
153
  materialize(ctx);
154
+ const stats = {
155
+ files: parsed,
156
+ symbols: ctx.symbols,
157
+ skipped: ctx.skipped
158
+ };
159
+ if (ctx.degraded > 0) stats.degraded = ctx.degraded;
146
160
  return {
147
161
  globals: ctx.globals,
148
162
  modules: ctx.modules,
149
- stats: { files: parsed, symbols: ctx.symbols, skipped: ctx.skipped }
163
+ stats
150
164
  };
151
165
  }
152
166
  function emitEnvModule(env, pkgName) {
@@ -173,7 +187,8 @@ function emitEnvModule(env, pkgName) {
173
187
  lines.push("// Auto-generated by @nudojs/harvester \u2014 DO NOT EDIT");
174
188
  lines.push(`// Source package: ${pkgName}`);
175
189
  lines.push(
176
- 'import { num, str, bool, never as absNever, unknown as absUnknown, numLit, strLit, boolLit, objOf, relationFn, abs as makeAbs } from "@nudojs/core";'
190
+ // 族 27 Bug 811:泛型 α 的 var term 要落盘 ⇒ 生成代码需要 `termVar`。
191
+ 'import { num, str, bool, never as absNever, unknown as absUnknown, numLit, strLit, boolLit, objOf, relationFn, abs as makeAbs, v as termVar } from "@nudojs/core";'
177
192
  );
178
193
  lines.push("");
179
194
  lines.push("export function defineEnv() {");
@@ -199,11 +214,23 @@ function emitEnvModule(env, pkgName) {
199
214
  lines.push("");
200
215
  return lines.join("\n");
201
216
  }
217
+ function nullDict() {
218
+ return /* @__PURE__ */ Object.create(null);
219
+ }
220
+ function toNullDict(src) {
221
+ if (Object.getPrototypeOf(src) === null) return src;
222
+ const out = nullDict();
223
+ for (const key of Object.keys(src)) out[key] = src[key];
224
+ return out;
225
+ }
202
226
  function recordFor(ctx, scope) {
227
+ ctx.globals = toNullDict(ctx.globals);
228
+ ctx.modules = toNullDict(ctx.modules);
229
+ if (scope.nsRec) return scope.nsRec;
203
230
  if (scope.moduleName === void 0) return ctx.globals;
204
- let rec = ctx.modules[scope.moduleName];
231
+ let rec = Object.hasOwn(ctx.modules, scope.moduleName) ? ctx.modules[scope.moduleName] : void 0;
205
232
  if (!rec) {
206
- rec = {};
233
+ rec = nullDict();
207
234
  ctx.modules[scope.moduleName] = rec;
208
235
  }
209
236
  return rec;
@@ -213,6 +240,10 @@ function collectStatements(ctx, statements, scope) {
213
240
  collectStatement(ctx, stmt, scope);
214
241
  }
215
242
  }
243
+ function isDefaultExportDecl(node) {
244
+ const flags = ts.getCombinedModifierFlags(node);
245
+ return (flags & ts.ModifierFlags.Default) !== 0;
246
+ }
216
247
  function collectStatement(ctx, stmt, scope) {
217
248
  if (ts.isModuleDeclaration(stmt)) {
218
249
  const body = stmt.body;
@@ -226,22 +257,36 @@ function collectStatement(ctx, stmt, scope) {
226
257
  } else if (stmt.name.text === "global") {
227
258
  collectStatements(ctx, body.statements, { moduleName: void 0, ns: [] });
228
259
  } else {
229
- collectStatements(ctx, body.statements, { moduleName: scope.moduleName, ns: [...scope.ns, stmt.name.text] });
260
+ const nsName = stmt.name.text;
261
+ const nsRec = nullDict();
262
+ collectStatements(ctx, body.statements, {
263
+ moduleName: scope.moduleName,
264
+ ns: [...scope.ns, nsName],
265
+ nsRec
266
+ });
267
+ ctx.pending.push({ t: "ns", scope, name: nsName, rec: nsRec });
268
+ ctx.symbols++;
230
269
  }
231
270
  return;
232
271
  }
233
272
  if (ts.isFunctionDeclaration(stmt)) {
234
273
  if (!stmt.name) {
274
+ if (isDefaultExportDecl(stmt)) {
275
+ ctx.pending.push({ t: "fn", scope, name: "default", decls: [stmt] });
276
+ ctx.symbols++;
277
+ return;
278
+ }
235
279
  ctx.skipped++;
236
280
  return;
237
281
  }
238
282
  const name = stmt.name.text;
239
- const existing = ctx.pending.find((p) => p.t === "fn" && p.name === name && scopeKey(p.scope) === scopeKey(scope));
283
+ const exportName = isDefaultExportDecl(stmt) ? "default" : name;
284
+ const existing = ctx.pending.find((p) => p.t === "fn" && p.name === exportName && scopeKey(p.scope) === scopeKey(scope));
240
285
  if (existing && existing.t === "fn") {
241
286
  existing.decls.push(stmt);
242
287
  return;
243
288
  }
244
- ctx.pending.push({ t: "fn", scope, name, decls: [stmt] });
289
+ ctx.pending.push({ t: "fn", scope, name: exportName, decls: [stmt] });
245
290
  ctx.symbols++;
246
291
  return;
247
292
  }
@@ -279,13 +324,24 @@ function collectStatement(ctx, stmt, scope) {
279
324
  }
280
325
  if (ts.isClassDeclaration(stmt)) {
281
326
  if (!stmt.name) {
282
- ctx.skipped++;
327
+ if (isDefaultExportDecl(stmt)) {
328
+ const synth = `__nudo_default_class_${ctx.anonDefaultClass++}`;
329
+ registerTypeDecl(ctx, scope, synth, stmt);
330
+ if (!ctx.classes.has(synth)) ctx.classes.set(synth, stmt);
331
+ ctx.pending.push({ t: "value", scope, name: "default", lookup: synth });
332
+ } else {
333
+ ctx.skipped++;
334
+ }
283
335
  return;
284
336
  }
285
337
  registerTypeDecl(ctx, scope, stmt.name.text, stmt);
286
338
  if (!ctx.classes.has(stmt.name.text)) ctx.classes.set(stmt.name.text, stmt);
287
- ctx.pending.push({ t: "value", scope, name: stmt.name.text });
288
- ctx.symbols++;
339
+ if (isDefaultExportDecl(stmt)) {
340
+ ctx.pending.push({ t: "value", scope, name: "default", lookup: stmt.name.text });
341
+ } else {
342
+ ctx.pending.push({ t: "value", scope, name: stmt.name.text });
343
+ ctx.symbols++;
344
+ }
289
345
  return;
290
346
  }
291
347
  if (ts.isEnumDeclaration(stmt)) {
@@ -327,16 +383,15 @@ function collectStatement(ctx, stmt, scope) {
327
383
  return;
328
384
  }
329
385
  if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
386
+ const clauseTypeOnly = stmt.isTypeOnly === true;
330
387
  for (const el of stmt.exportClause.elements) {
331
388
  const localName = (el.propertyName ?? el.name).text;
332
389
  const exportedName = el.name.text;
333
- const isValue = ctx.classes.has(localName) || !el.isTypeOnly && (ctx.interfaces.has(localName) || ctx.qualifiedInterfaces.has(localName));
334
- if (isValue) {
335
- ctx.pending.push({ t: "value", scope, name: exportedName, lookup: localName });
336
- ctx.symbols++;
337
- } else {
390
+ if (clauseTypeOnly || el.isTypeOnly) {
338
391
  ctx.skipped++;
392
+ continue;
339
393
  }
394
+ ctx.pending.push({ t: "value", scope, name: exportedName, lookup: localName });
340
395
  }
341
396
  return;
342
397
  }
@@ -344,13 +399,20 @@ function collectStatement(ctx, stmt, scope) {
344
399
  return;
345
400
  }
346
401
  if (ts.isExportAssignment(stmt)) {
347
- if (stmt.expression && ts.isIdentifier(stmt.expression) && scope.moduleName !== void 0) {
402
+ if (stmt.isExportEquals && stmt.expression && ts.isIdentifier(stmt.expression) && scope.moduleName !== void 0) {
348
403
  const target = ctx.importEquals.get(scopeKey(scope))?.get(stmt.expression.text);
349
404
  if (target !== void 0) {
350
405
  ctx.moduleAliases.push([scope.moduleName, target]);
351
406
  return;
352
407
  }
353
408
  }
409
+ if (stmt.expression && ts.isIdentifier(stmt.expression)) {
410
+ if (stmt.isExportEquals && scope.moduleName !== void 0) {
411
+ ctx.exportEquals.set(scope.moduleName, stmt.expression.text);
412
+ }
413
+ ctx.pending.push({ t: "value", scope, name: "default", lookup: stmt.expression.text });
414
+ return;
415
+ }
354
416
  ctx.skipped++;
355
417
  return;
356
418
  }
@@ -366,58 +428,198 @@ function registerTypeDecl(ctx, scope, name, decl) {
366
428
  }
367
429
  };
368
430
  push(ctx.interfaces, name);
369
- if (ts.isClassDeclaration(decl)) return;
370
431
  const qualified = [...scope.ns, name].join(".");
371
432
  if (qualified !== name) push(ctx.qualifiedInterfaces, qualified);
372
433
  }
373
- function interfaceDeclsOf(ctx, typeNode) {
434
+ function namespaceChainOf(decl) {
435
+ const cached = nsChainCache.get(decl);
436
+ if (cached) return cached;
437
+ const chain = [];
438
+ let node = decl.parent;
439
+ while (node) {
440
+ if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name) && node.name.text !== "global") {
441
+ chain.unshift(node.name.text);
442
+ }
443
+ node = node.parent;
444
+ }
445
+ nsChainCache.set(decl, chain);
446
+ return chain;
447
+ }
448
+ var nsChainCache = /* @__PURE__ */ new WeakMap();
449
+ function inAmbientModuleBlock(decl) {
450
+ let node = decl.parent;
451
+ while (node) {
452
+ if (ts.isModuleDeclaration(node) && ts.isStringLiteral(node.name)) return true;
453
+ node = node.parent;
454
+ }
455
+ return false;
456
+ }
457
+ function heritageBaseName(expr) {
458
+ const text = (node) => {
459
+ if (ts.isIdentifier(node)) return node.text;
460
+ if (ts.isPropertyAccessExpression(node)) {
461
+ const left = text(node.expression);
462
+ if (left === void 0) return void 0;
463
+ return `${left}.${node.name.text}`;
464
+ }
465
+ return void 0;
466
+ };
467
+ return text(expr.expression);
468
+ }
469
+ function computedMemberName(name) {
470
+ if (!name || !ts.isComputedPropertyName(name)) return void 0;
471
+ const expr = name.expression;
472
+ if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "Symbol") {
473
+ if (expr.name.text === "iterator") return "@@iterator";
474
+ if (expr.name.text === "asyncIterator") return "@@asyncIterator";
475
+ return void 0;
476
+ }
477
+ if (ts.isStringLiteral(expr)) return expr.text;
478
+ return void 0;
479
+ }
480
+ function resolveIfaceDecls(ctx, name, refNs) {
481
+ const short = name.includes(".") ? name.split(".").pop() : name;
482
+ const qualified = name !== short;
483
+ const seen = /* @__PURE__ */ new Set();
484
+ const out = [];
485
+ const add = (d) => {
486
+ if (seen.has(d)) return;
487
+ seen.add(d);
488
+ out.push(d);
489
+ };
490
+ if (qualified) {
491
+ if (refNs && refNs.length > 0) {
492
+ for (let i = refNs.length - 1; i >= 0; i--) {
493
+ const rel = [...refNs.slice(0, i + 1), name].join(".");
494
+ const decls = ctx.qualifiedInterfaces.get(rel);
495
+ if (decls && decls.length > 0) {
496
+ for (const d of decls) add(d);
497
+ return out;
498
+ }
499
+ }
500
+ }
501
+ for (const d of ctx.qualifiedInterfaces.get(name) ?? []) add(d);
502
+ for (const d of ctx.interfaces.get(short) ?? []) {
503
+ if (namespaceChainOf(d).length === 0 && inAmbientModuleBlock(d)) add(d);
504
+ }
505
+ return out;
506
+ }
507
+ if (refNs && refNs.length > 0) {
508
+ for (let i = refNs.length - 1; i >= 0; i--) {
509
+ const rel = [...refNs.slice(0, i + 1), name].join(".");
510
+ const decls = ctx.qualifiedInterfaces.get(rel);
511
+ if (decls && decls.length > 0) {
512
+ for (const d of decls) add(d);
513
+ return out;
514
+ }
515
+ }
516
+ }
517
+ for (const d of ctx.interfaces.get(name) ?? []) {
518
+ if (namespaceChainOf(d).length === 0) add(d);
519
+ }
520
+ return out;
521
+ }
522
+ function interfaceDeclsOf(ctx, typeNode, chain, refNs) {
374
523
  const name = typeof typeNode === "string" ? typeNode : ts.isTypeReferenceNode(typeNode) ? typeNameString(typeNode.typeName) : void 0;
375
524
  if (!name) return [];
376
- const short = name.includes(".") ? name.split(".").pop() : name;
377
- const keys = name === short ? [name] : [name, short];
525
+ const visited = chain ?? /* @__PURE__ */ new Set();
526
+ if (visited.has(name)) return [];
527
+ const nextChain = new Set(visited);
528
+ nextChain.add(name);
378
529
  const seen = /* @__PURE__ */ new Set();
379
530
  const out = [];
380
- for (const key of keys) {
381
- for (const d of [
382
- ...ctx.interfaces.get(key) ?? [],
383
- ...ctx.qualifiedInterfaces.get(key) ?? []
384
- ]) {
385
- if (ts.isInterfaceDeclaration(d) && !seen.has(d)) {
386
- seen.add(d);
387
- out.push(d);
531
+ const add = (d) => {
532
+ if (seen.has(d)) return;
533
+ seen.add(d);
534
+ out.push(d);
535
+ };
536
+ for (const d of resolveIfaceDecls(ctx, name, refNs)) add(d);
537
+ for (const d of [...out]) {
538
+ for (const clause of d.heritageClauses ?? []) {
539
+ if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
540
+ for (const expr of clause.types) {
541
+ const baseName = heritageBaseName(expr);
542
+ if (!baseName) continue;
543
+ for (const b of interfaceDeclsOf(ctx, baseName, nextChain, namespaceChainOf(d))) add(b);
388
544
  }
389
545
  }
390
546
  }
391
547
  return out;
392
548
  }
549
+ var realDeclNameCache = /* @__PURE__ */ new WeakMap();
550
+ function realDeclNames(ctx) {
551
+ const cached = realDeclNameCache.get(ctx);
552
+ if (cached) return cached;
553
+ const set = /* @__PURE__ */ new Set();
554
+ for (const p of ctx.pending) {
555
+ if (p.t === "iface-expand") continue;
556
+ set.add(`${scopeKey(p.scope)}::${p.name}`);
557
+ }
558
+ realDeclNameCache.set(ctx, set);
559
+ return set;
560
+ }
561
+ function declNameKey(scope, name) {
562
+ return `${scopeKey(scope)}::${name}`;
563
+ }
564
+ function optionalPropTypeNode(member) {
565
+ if (!member.type) return void 0;
566
+ if (!member.questionToken) return member.type;
567
+ return ts.factory.createUnionTypeNode([
568
+ member.type,
569
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
570
+ ]);
571
+ }
572
+ function optionalMethodTypeNode(member) {
573
+ const fn = ts.factory.createFunctionTypeNode(
574
+ member.typeParameters,
575
+ member.parameters,
576
+ // 无返回类型标注 ⇒ `any`(`signatureToFnSig` 对缺省返回位的口径是 unknown)
577
+ member.type ?? ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
578
+ );
579
+ return ts.factory.createUnionTypeNode([
580
+ fn,
581
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
582
+ ]);
583
+ }
393
584
  function expandInterfaceAsModuleMembers(ctx, scope, ifaces) {
585
+ const real = realDeclNames(ctx);
586
+ const methods = /* @__PURE__ */ new Map();
587
+ const props = /* @__PURE__ */ new Map();
394
588
  for (const iface of ifaces) {
589
+ if ((iface.typeParameters ?? []).length > 0) continue;
395
590
  for (const member of iface.members) {
396
591
  if (ts.isMethodSignature(member)) {
397
592
  const name = memberName(member);
398
593
  if (name === void 0) continue;
399
- const existing = ctx.pending.find(
400
- (p) => p.t === "fn" && p.name === name && scopeKey(p.scope) === scopeKey(scope)
401
- );
402
- if (existing && existing.t === "fn") {
403
- existing.decls.push(member);
404
- continue;
405
- }
406
- ctx.pending.push({ t: "fn", scope, name, decls: [member] });
407
- ctx.symbols++;
594
+ const list = methods.get(name);
595
+ if (list) list.push(member);
596
+ else methods.set(name, [member]);
408
597
  continue;
409
598
  }
410
599
  if (ts.isPropertySignature(member)) {
411
600
  const name = memberName(member);
412
601
  if (name === void 0) continue;
413
- if (ctx.pending.some((p) => p.t === "var" && p.name === name && scopeKey(p.scope) === scopeKey(scope))) {
414
- continue;
415
- }
416
- ctx.pending.push({ t: "var", scope, name, typeNode: member.type });
417
- ctx.symbols++;
602
+ props.set(name, member);
418
603
  }
419
604
  }
420
605
  }
606
+ for (const [name, decls] of methods) {
607
+ if (props.has(name)) continue;
608
+ if (real.has(declNameKey(scope, name))) continue;
609
+ const first = decls[0];
610
+ if (first.questionToken) {
611
+ ctx.pending.push({ t: "var", scope, name, typeNode: optionalMethodTypeNode(first) });
612
+ ctx.symbols++;
613
+ continue;
614
+ }
615
+ ctx.pending.push({ t: "fn", scope, name, decls });
616
+ ctx.symbols++;
617
+ }
618
+ for (const [name, member] of props) {
619
+ if (real.has(declNameKey(scope, name))) continue;
620
+ ctx.pending.push({ t: "var", scope, name, typeNode: optionalPropTypeNode(member) });
621
+ ctx.symbols++;
622
+ }
421
623
  }
422
624
  function registerAlias(ctx, scope, stmt) {
423
625
  const name = stmt.name.text;
@@ -432,6 +634,126 @@ function registerAlias(ctx, scope, stmt) {
432
634
  push(ctx.aliases, name);
433
635
  const qualified = [...scope.ns, name].join(".");
434
636
  if (qualified !== name) push(ctx.qualifiedAliases, qualified);
637
+ registerRecursiveAliases(ctx, name);
638
+ }
639
+ var RECURSIVE_ALIAS_MARKER = "__nudoRecursiveAlias";
640
+ var aliasRefsCache = /* @__PURE__ */ new WeakMap();
641
+ var recursiveAliasCache = /* @__PURE__ */ new WeakMap();
642
+ function aliasRefNames(decl) {
643
+ const hit = aliasRefsCache.get(decl);
644
+ if (hit) return hit;
645
+ const out = /* @__PURE__ */ new Set();
646
+ const visit = (n) => {
647
+ if (!n) return;
648
+ if (ts.isTypeReferenceNode(n)) out.add(typeNameString(n.typeName));
649
+ n.forEachChild(visit);
650
+ };
651
+ visit(decl.type);
652
+ aliasRefsCache.set(decl, out);
653
+ return out;
654
+ }
655
+ function aliasRefsOf(ctx, name) {
656
+ const out = /* @__PURE__ */ new Set();
657
+ for (const decl of ctx.aliases.get(name) ?? []) {
658
+ for (const r of aliasRefNames(decl)) out.add(r);
659
+ }
660
+ return out;
661
+ }
662
+ function aliasReachesSelf(ctx, name, aliasNames) {
663
+ const seen = /* @__PURE__ */ new Set();
664
+ const stack = [];
665
+ for (const r of aliasRefsOf(ctx, name)) if (aliasNames.has(r)) stack.push(r);
666
+ while (stack.length > 0) {
667
+ const cur = stack.pop();
668
+ if (cur === name) return true;
669
+ if (seen.has(cur)) continue;
670
+ seen.add(cur);
671
+ for (const r of aliasRefsOf(ctx, cur)) if (aliasNames.has(r)) stack.push(r);
672
+ }
673
+ return false;
674
+ }
675
+ function registerRecursiveAliases(ctx, newName) {
676
+ const aliasNames = new Set(ctx.aliases.keys());
677
+ const known = recursiveAliasCache.get(ctx) ?? /* @__PURE__ */ new Set();
678
+ const found = [];
679
+ if (!known.has(newName) && aliasReachesSelf(ctx, newName, aliasNames)) {
680
+ found.push(newName);
681
+ }
682
+ for (const name of aliasNames) {
683
+ if (name === newName || known.has(name)) continue;
684
+ if (!aliasRefsOf(ctx, name).has(newName)) continue;
685
+ if (aliasReachesSelf(ctx, name, aliasNames)) found.push(name);
686
+ }
687
+ for (const name of found) {
688
+ known.add(name);
689
+ ensureRecursiveAliasInterface(ctx, name);
690
+ }
691
+ recursiveAliasCache.set(ctx, known);
692
+ }
693
+ function isRecursiveAliasDecl(decl) {
694
+ return decl[RECURSIVE_ALIAS_MARKER] === true;
695
+ }
696
+ function ensureRecursiveAliasInterface(ctx, name) {
697
+ const decl = ctx.aliases.get(name)?.[0];
698
+ if (!decl) return;
699
+ const existing = ctx.interfaces.get(name) ?? [];
700
+ if (existing.some((d) => isRecursiveAliasDecl(d))) return;
701
+ const members = ts.isTypeLiteralNode(decl.type) ? [...decl.type.members] : [];
702
+ const iface = ts.factory.createInterfaceDeclaration(
703
+ void 0,
704
+ ts.factory.createIdentifier(name),
705
+ decl.typeParameters,
706
+ void 0,
707
+ ts.factory.createNodeArray(members)
708
+ );
709
+ iface[RECURSIVE_ALIAS_MARKER] = true;
710
+ ctx.interfaces.set(name, [...existing, iface]);
711
+ }
712
+ function classValueAbs(ctx, cls, key) {
713
+ const ctor = cls.members.find(ts.isConstructorDeclaration);
714
+ const params = [];
715
+ if (ctor) {
716
+ for (const param of ctor.parameters) {
717
+ if (isThisParameter(param)) continue;
718
+ let type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
719
+ if (param.questionToken) type = absUnion([type, absUndefLit()]);
720
+ params.push(type);
721
+ }
722
+ }
723
+ return absCtorSig(params, instanceFor(ctx, key));
724
+ }
725
+ function resolveLocalValue(ctx, scope, key) {
726
+ const rec = recordFor(ctx, scope);
727
+ if (Object.hasOwn(rec, key)) return rec[key];
728
+ if (scope.moduleName !== void 0 && Object.hasOwn(ctx.globals, key)) return ctx.globals[key];
729
+ const cls = ctx.classes.get(key);
730
+ if (cls) return classValueAbs(ctx, cls, key);
731
+ if (ctx.interfaces.has(key) || ctx.qualifiedInterfaces.has(key)) return instanceFor(ctx, key);
732
+ return void 0;
733
+ }
734
+ function mergeOverloadParams(sigs) {
735
+ const first = sigs[0];
736
+ if (!first || first.shape.k !== "fn") return void 0;
737
+ const base = first.shape.paramTypes ?? [];
738
+ if (base.length === 0) return void 0;
739
+ const perPos = base.map(() => []);
740
+ for (const s of sigs) {
741
+ if (s.shape.k !== "fn") return void 0;
742
+ const pts = s.shape.paramTypes ?? [];
743
+ if (pts.length !== base.length) return void 0;
744
+ for (let i = 0; i < pts.length; i++) {
745
+ const t = pts[i];
746
+ if (hasTypeVar(t)) return void 0;
747
+ perPos[i].push(t);
748
+ }
749
+ }
750
+ return perPos.map((ts4) => ts4.length === 1 ? ts4[0] : absUnion(ts4));
751
+ }
752
+ function mergeModuleRecords(base, over) {
753
+ const merged = nullDict();
754
+ for (const k of Object.keys(base)) merged[k] = base[k];
755
+ for (const k of Object.keys(over)) merged[k] = over[k];
756
+ return merged;
435
757
  }
436
758
  function materialize(ctx) {
437
759
  const expanded = [];
@@ -452,27 +774,83 @@ function materialize(ctx) {
452
774
  }
453
775
  }
454
776
  ctx.pending = expanded;
777
+ const nsSlots = [];
778
+ const nsSlotsByName = /* @__PURE__ */ new Map();
455
779
  for (const p of ctx.pending) {
456
780
  if (p.t === "iface-expand") continue;
457
781
  const rec = recordFor(ctx, p.scope);
782
+ if (p.t === "ns") {
783
+ const names = Object.keys(p.rec);
784
+ if (names.length === 0) {
785
+ continue;
786
+ }
787
+ const slots = nullDict();
788
+ for (const n of names) slots[n] = { value: p.rec[n] };
789
+ if (p.scope.moduleName !== void 0 && ctx.exportEquals.get(p.scope.moduleName) === p.name) {
790
+ const flat = recordFor(ctx, p.scope);
791
+ for (const [k, v] of Object.entries(p.rec)) {
792
+ if (Object.hasOwn(flat, k)) continue;
793
+ flat[k] = v;
794
+ }
795
+ nsSlots.push({ rec: p.rec, slots, flat });
796
+ continue;
797
+ }
798
+ const existing = rec[p.name];
799
+ if (existing === void 0) {
800
+ rec[p.name] = objOf(slots);
801
+ nsSlotsByName.set(`${scopeKey(p.scope)}\0${p.name}`, slots);
802
+ } else if (existing.shape.k === "fn") {
803
+ existing.shape.slots = slots;
804
+ nsSlotsByName.set(`${scopeKey(p.scope)}\0${p.name}`, slots);
805
+ } else {
806
+ ctx.skipped++;
807
+ continue;
808
+ }
809
+ nsSlots.push({ rec: p.rec, slots });
810
+ continue;
811
+ }
458
812
  if (p.t === "fn") {
459
- if (rec[p.name] !== void 0) continue;
460
- const sigs = p.decls.map((d) => signatureToFnSig(ctx, d));
461
- const value = sigs[0];
462
- const shape0 = value.shape.k === "fn" ? value.shape : void 0;
463
- if (shape0 && sigs.length > 1) {
464
- const returns = [];
465
- for (const s of sigs) {
466
- if (s.shape.k !== "fn") continue;
467
- const ret = s.shape.returnType;
468
- if (ret && !isPlainUnknown(ret)) returns.push(ret);
813
+ const fnValue = () => {
814
+ const sigs = p.decls.map((d) => signatureToFnSig(ctx, d));
815
+ const value2 = sigs[0];
816
+ const shape0 = value2.shape.k === "fn" ? value2.shape : void 0;
817
+ if (shape0 && sigs.length > 1) {
818
+ const mergedParams = mergeOverloadParams(sigs);
819
+ if (mergedParams) {
820
+ shape0.paramTypes = mergedParams;
821
+ const rel0 = getFnImpl(value2)?.relation;
822
+ if (rel0) rel0.paramTypes = mergedParams;
823
+ }
824
+ const returns = [];
825
+ for (const s of sigs) {
826
+ if (s.shape.k !== "fn") continue;
827
+ const ret = s.shape.returnType;
828
+ if (ret && !isPlainUnknown(ret)) returns.push(ret);
829
+ }
830
+ const withVars = returns.filter((r) => hasTypeVar(r));
831
+ const pool = withVars.length > 0 ? withVars : returns;
832
+ const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
833
+ shape0.returnType = mergedRet;
834
+ const rel = getFnImpl(value2)?.relation;
835
+ if (rel) rel.returnType = mergedRet;
469
836
  }
470
- const withVars = returns.filter((r) => hasTypeVar(r));
471
- const pool = withVars.length > 0 ? withVars : returns;
472
- const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
473
- shape0.returnType = mergedRet;
474
- const rel = getFnImpl(value)?.relation;
475
- if (rel) rel.returnType = mergedRet;
837
+ return value2;
838
+ };
839
+ const existing = rec[p.name];
840
+ if (existing !== void 0) {
841
+ const nsStatic = nsSlotsByName.get(`${scopeKey(p.scope)}${p.name}`);
842
+ if (nsStatic && existing.shape.k === "obj") {
843
+ const value2 = fnValue();
844
+ value2.shape.slots = nsStatic;
845
+ rec[p.name] = value2;
846
+ }
847
+ continue;
848
+ }
849
+ const markBefore = ctx.degradedMark;
850
+ const value = fnValue();
851
+ if (ctx.degradedMark > markBefore) {
852
+ ctx.degraded++;
853
+ ctx.degradedMark = markBefore;
476
854
  }
477
855
  rec[p.name] = value;
478
856
  continue;
@@ -480,30 +858,32 @@ function materialize(ctx) {
480
858
  if (rec[p.name] !== void 0) continue;
481
859
  switch (p.t) {
482
860
  case "var": {
861
+ const markBefore = ctx.degradedMark;
483
862
  const mapped = p.typeNode ? mapType(ctx, p.typeNode, 0) : absUnknown();
484
- if (isPlainUnknown(mapped)) {
485
- ctx.skipped++;
486
- break;
863
+ if (ctx.degradedMark > markBefore) {
864
+ ctx.degraded++;
865
+ ctx.degradedMark = markBefore;
487
866
  }
488
867
  rec[p.name] = mapped;
489
868
  break;
490
869
  }
491
870
  case "value": {
492
- const cls = ctx.classes.get(p.lookup ?? p.name);
871
+ const key = p.lookup ?? p.name;
872
+ const cls = ctx.classes.get(key);
493
873
  if (cls) {
494
- const ctor = cls.members.find(ts.isConstructorDeclaration);
495
- const params = [];
496
- if (ctor) {
497
- for (const param of ctor.parameters) {
498
- let type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
499
- if (param.questionToken) type = absUnion([type, absUndefLit()]);
500
- params.push(type);
501
- }
874
+ const markBefore = ctx.degradedMark;
875
+ rec[p.name] = classValueAbs(ctx, cls, key);
876
+ if (ctx.degradedMark > markBefore) {
877
+ ctx.degraded++;
878
+ ctx.degradedMark = markBefore;
502
879
  }
503
- rec[p.name] = absFnSig(params, instanceFor(ctx, p.lookup ?? p.name));
504
- } else {
505
- rec[p.name] = instanceFor(ctx, p.lookup ?? p.name);
880
+ if (p.lookup !== void 0) ctx.symbols++;
881
+ break;
506
882
  }
883
+ if (p.lookup !== void 0) {
884
+ break;
885
+ }
886
+ rec[p.name] = instanceFor(ctx, key);
507
887
  break;
508
888
  }
509
889
  case "enum":
@@ -511,14 +891,69 @@ function materialize(ctx) {
511
891
  break;
512
892
  }
513
893
  }
894
+ for (const p of ctx.pending) {
895
+ if (p.t !== "value" || p.lookup === void 0) continue;
896
+ const rec = recordFor(ctx, p.scope);
897
+ if (Object.hasOwn(rec, p.name)) continue;
898
+ const resolved = resolveLocalValue(ctx, p.scope, p.lookup);
899
+ if (resolved === void 0) {
900
+ ctx.skipped++;
901
+ continue;
902
+ }
903
+ rec[p.name] = resolved;
904
+ ctx.symbols++;
905
+ }
906
+ for (const { rec: nsRec, slots, flat } of nsSlots) {
907
+ for (const [k, v] of Object.entries(nsRec)) {
908
+ if (Object.hasOwn(slots, k)) continue;
909
+ slots[k] = { value: v };
910
+ if (flat && !Object.hasOwn(flat, k)) flat[k] = v;
911
+ }
912
+ }
913
+ const aliasTargets = /* @__PURE__ */ new Map();
514
914
  for (const [alias, target] of ctx.moduleAliases) {
515
- if (ctx.modules[target] && !ctx.modules[alias]) {
516
- ctx.modules[alias] = ctx.modules[target];
915
+ const list = aliasTargets.get(alias);
916
+ if (list) {
917
+ if (!list.includes(target)) list.push(target);
918
+ } else {
919
+ aliasTargets.set(alias, [target]);
920
+ }
921
+ }
922
+ const aliasResolved = /* @__PURE__ */ new Map();
923
+ const aliasResolving = /* @__PURE__ */ new Set();
924
+ const resolveModule = (name, depth) => {
925
+ const cached = aliasResolved.get(name);
926
+ if (cached) return cached;
927
+ if (depth > MAX_ALIAS_DEPTH || aliasResolving.has(name)) return void 0;
928
+ aliasResolving.add(name);
929
+ const own = ctx.modules[name];
930
+ const targets = aliasTargets.get(name);
931
+ let merged;
932
+ if (targets) {
933
+ for (const target of targets) {
934
+ const t = resolveModule(target, depth + 1);
935
+ if (!t) continue;
936
+ merged = merged ? mergeModuleRecords(merged, t) : t;
937
+ }
517
938
  }
939
+ aliasResolving.delete(name);
940
+ if (!merged) return own;
941
+ const out = own && Object.keys(own).length > 0 ? mergeModuleRecords(merged, own) : merged;
942
+ aliasResolved.set(name, out);
943
+ return out;
944
+ };
945
+ for (const name of aliasTargets.keys()) {
946
+ const out = resolveModule(name, 0);
947
+ if (out) ctx.modules[name] = out;
518
948
  }
519
949
  }
520
950
  function typeNameString(name) {
521
951
  if (ts.isIdentifier(name)) return name.text;
952
+ if (ts.isPropertyAccessExpression(name)) {
953
+ const left = name.expression;
954
+ if (!ts.isIdentifier(left) && !ts.isPropertyAccessExpression(left)) return name.name.text;
955
+ return `${typeNameString(left)}.${name.name.text}`;
956
+ }
522
957
  return `${typeNameString(name.left)}.${name.right.text}`;
523
958
  }
524
959
  function memberName(node) {
@@ -527,82 +962,220 @@ function memberName(node) {
527
962
  if (ts.isIdentifier(n) || ts.isStringLiteral(n) || ts.isNumericLiteral(n)) return n.text;
528
963
  return void 0;
529
964
  }
530
- function instanceFor(ctx, key, typeArgs) {
531
- const cacheKey = typeArgs && typeArgs.length > 0 ? `${key}::${typeArgs.length}` : key;
532
- const cached = ctx.instanceCache.get(cacheKey);
533
- if (cached !== void 0) return cached;
534
- const decls = interfaceDeclsOf(ctx, key);
965
+ function absBrandOpen(name) {
966
+ return absExact({ k: "brand", name, shape: absUnknown() });
967
+ }
968
+ function absFingerprint(a) {
969
+ const key = absShapeKey(a);
970
+ const term = a.term && a.term.op !== "lit" ? termToString(a.term) : "";
971
+ return term ? `${key}#${term}` : key;
972
+ }
973
+ function memberDict() {
974
+ return /* @__PURE__ */ Object.create(null);
975
+ }
976
+ function effectiveTypeArgs(ctx, decls, typeArgs) {
977
+ const tps = decls[0]?.typeParameters ?? [];
978
+ if (tps.length === 0) return [];
979
+ const out = [];
980
+ for (let i = 0; i < tps.length; i++) {
981
+ const tp = tps[i];
982
+ const explicit = typeArgs?.[i];
983
+ if (explicit !== void 0) {
984
+ out.push(explicit);
985
+ continue;
986
+ }
987
+ if (tp.default) {
988
+ out.push(mapType(ctx, tp.default, 0));
989
+ continue;
990
+ }
991
+ if (tp.constraint) {
992
+ const c = mapType(ctx, tp.constraint, 0);
993
+ out.push(isPlainUnknown(c) ? absUnknown() : c);
994
+ continue;
995
+ }
996
+ out.push(absUnknown());
997
+ }
998
+ return out;
999
+ }
1000
+ function mergeOverloadSigs(sigs) {
1001
+ const first = sigs[0];
1002
+ if (!first) return absUnknown();
1003
+ if (sigs.length === 1) return first;
1004
+ if (first.shape.k !== "fn") return first;
1005
+ const params = mergeOverloadParams(sigs) ?? first.shape.paramTypes ?? [];
1006
+ const returns = [];
1007
+ for (const s of sigs) {
1008
+ if (s.shape.k !== "fn") continue;
1009
+ const ret = s.shape.returnType;
1010
+ if (ret && !isPlainUnknown(ret)) returns.push(ret);
1011
+ }
1012
+ const withVars = returns.filter((r) => hasTypeVar(r));
1013
+ const pool = withVars.length > 0 ? withVars : returns;
1014
+ const mergedRet = pool.length > 0 ? absUnion(pool) : absUnknown();
1015
+ const rel0 = getFnImpl(first)?.relation;
1016
+ const extra = rel0 ? {
1017
+ ...rel0.inferFrom ? { inferFrom: rel0.inferFrom } : {},
1018
+ ...rel0.condFallback !== void 0 ? { condFallback: rel0.condFallback } : {}
1019
+ } : void 0;
1020
+ return absFnSig(params, mergedRet, extra);
1021
+ }
1022
+ function typeParamBindings(decls, args) {
1023
+ const map = /* @__PURE__ */ new Map();
1024
+ const tps = decls[0]?.typeParameters ?? [];
1025
+ tps.forEach((tp, i) => {
1026
+ const arg = args[i];
1027
+ if (arg) map.set(tp.name.text, arg);
1028
+ });
1029
+ return map;
1030
+ }
1031
+ function collectIfaceEntries(ctx, decls, args, chain, refNs, outerBindings) {
1032
+ const out = [];
1033
+ const seen = /* @__PURE__ */ new Set();
1034
+ for (const d of decls) {
1035
+ if (seen.has(d)) continue;
1036
+ seen.add(d);
1037
+ out.push({ decl: d, typeArgs: [...args], bindings: typeParamBindings([d], args) });
1038
+ }
1039
+ for (const d of decls) {
1040
+ const dNs = namespaceChainOf(d);
1041
+ for (const clause of d.heritageClauses ?? []) {
1042
+ if (clause.token !== ts.SyntaxKind.ExtendsKeyword) continue;
1043
+ for (const expr of clause.types) {
1044
+ const baseName = heritageBaseName(expr);
1045
+ if (!baseName || chain.has(baseName)) continue;
1046
+ const baseDecls = resolveIfaceDecls(ctx, baseName, dNs);
1047
+ if (baseDecls.length === 0) continue;
1048
+ const explicit = expr.typeArguments ?? [];
1049
+ const baseArgs = explicit.length > 0 ? explicit.map((a) => mapType(ctx, a, 0, outerBindings)) : effectiveTypeArgs(ctx, baseDecls, void 0);
1050
+ const nextChain = new Set(chain);
1051
+ nextChain.add(baseName);
1052
+ out.push(
1053
+ ...collectIfaceEntries(
1054
+ ctx,
1055
+ baseDecls,
1056
+ baseArgs,
1057
+ nextChain,
1058
+ dNs,
1059
+ typeParamBindings(baseDecls, baseArgs)
1060
+ )
1061
+ );
1062
+ }
1063
+ }
1064
+ }
1065
+ return out;
1066
+ }
1067
+ function instanceFor(ctx, key, typeArgs, refNs) {
1068
+ const decls = resolveIfaceDecls(ctx, key, refNs);
535
1069
  const decl = decls[0];
536
1070
  if (!decl) return absUnknown();
537
- if (ctx.expanding.has(key)) {
538
- return absBrand(decl.name?.text ?? key, {});
1071
+ const nsTag = refNs && refNs.length > 0 ? `ns:${refNs.join(".")}:` : "";
1072
+ const cacheKey = `${nsTag}${key}`;
1073
+ if (ctx.expanding.has(cacheKey)) {
1074
+ return absBrandOpen(decl.name?.text ?? key);
539
1075
  }
540
- ctx.expanding.add(key);
1076
+ ctx.expanding.add(cacheKey);
541
1077
  const savedTp = ctx.typeParams;
542
1078
  ctx.typeParams = new Set(savedTp);
543
- for (const d of decls) {
544
- for (const tp of d.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
545
- }
546
- const properties = {};
547
1079
  try {
548
1080
  for (const d of decls) {
1081
+ for (const tp of d.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
1082
+ }
1083
+ const effective = effectiveTypeArgs(ctx, decls, typeArgs);
1084
+ const argKey = effective.length > 0 ? `${cacheKey}::${effective.map(absFingerprint).join("|")}` : cacheKey;
1085
+ const cached = ctx.instanceCache.get(argKey);
1086
+ if (cached !== void 0) return cached;
1087
+ const entries = collectIfaceEntries(
1088
+ ctx,
1089
+ decls,
1090
+ effective,
1091
+ /* @__PURE__ */ new Set([key]),
1092
+ refNs,
1093
+ typeParamBindings(decls, effective)
1094
+ );
1095
+ for (const entry of entries) {
1096
+ for (const tp of entry.decl.typeParameters ?? []) ctx.typeParams.add(tp.name.text);
1097
+ }
1098
+ const properties = memberDict();
1099
+ const methodSigs = /* @__PURE__ */ new Map();
1100
+ const optionalMethods = /* @__PURE__ */ new Map();
1101
+ for (const entry of entries) {
1102
+ const d = entry.decl;
549
1103
  const members = d.members;
550
1104
  for (const member of members) {
551
1105
  const modifiers = member.modifiers;
552
1106
  if (modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) continue;
553
1107
  if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
554
- const name = memberName(member);
1108
+ const name = memberName(member) ?? computedMemberName(member.name);
555
1109
  if (name === void 0) continue;
556
- let type = member.type ? mapType(ctx, member.type, 0) : absUnknown();
1110
+ let type = member.type ? mapType(ctx, member.type, 0, entry.bindings) : absUnknown();
557
1111
  if (member.questionToken) type = absUnion([type, absUndefLit()]);
558
1112
  properties[name] = type;
1113
+ methodSigs.delete(name);
1114
+ optionalMethods.delete(name);
559
1115
  } else if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member)) {
560
- const name = memberName(member);
561
- if (name === void 0 || properties[name] !== void 0) continue;
562
- properties[name] = signatureToFnSig(ctx, member);
1116
+ const name = memberName(member) ?? computedMemberName(member.name);
1117
+ if (name === void 0) continue;
1118
+ if (properties[name] !== void 0) continue;
1119
+ const sig = signatureToFnSig(ctx, member, entry.bindings);
1120
+ const list = methodSigs.get(name);
1121
+ if (list) list.push(sig);
1122
+ else methodSigs.set(name, [sig]);
1123
+ optionalMethods.set(
1124
+ name,
1125
+ (optionalMethods.get(name) ?? true) && member.questionToken !== void 0
1126
+ );
563
1127
  } else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
564
- const name = memberName(member);
1128
+ const name = memberName(member) ?? computedMemberName(member.name);
565
1129
  if (name === void 0 || properties[name] !== void 0 || !member.type) continue;
566
- properties[name] = mapType(ctx, member.type, 0);
1130
+ properties[name] = mapType(ctx, member.type, 0, entry.bindings);
567
1131
  }
568
1132
  }
569
1133
  }
1134
+ for (const [name, sigs] of methodSigs) {
1135
+ const merged = mergeOverloadSigs(sigs);
1136
+ properties[name] = optionalMethods.get(name) ? absUnion([merged, absUndefLit()]) : merged;
1137
+ }
1138
+ const opaque = isRecursiveAliasDecl(decl) && Object.keys(properties).length === 0;
1139
+ let value = opaque ? absBrandOpen(decl.name?.text ?? key) : absBrand(decl.name?.text ?? key, properties);
1140
+ if (effective.length > 0 && Object.keys(properties).length <= 40) {
1141
+ const map = /* @__PURE__ */ new Map();
1142
+ const tps = decls[0].typeParameters ?? [];
1143
+ tps.forEach((tp, i) => {
1144
+ const arg = effective[i];
1145
+ if (arg) map.set(tp.name.text, arg);
1146
+ });
1147
+ if (map.size > 0) value = substAbs(value, map);
1148
+ }
1149
+ ctx.instanceCache.set(argKey, value);
1150
+ return value;
570
1151
  } finally {
571
1152
  ctx.typeParams = savedTp;
572
- ctx.expanding.delete(key);
573
- }
574
- let value = absBrand(decl.name?.text ?? key, properties);
575
- if (typeArgs && typeArgs.length > 0 && Object.keys(properties).length <= 40) {
576
- const map = /* @__PURE__ */ new Map();
577
- const tps = decls[0].typeParameters ?? [];
578
- tps.forEach((tp, i) => {
579
- const arg = typeArgs[i];
580
- if (arg) map.set(tp.name.text, arg);
581
- });
582
- if (map.size > 0) value = substAbs(value, map);
1153
+ ctx.expanding.delete(cacheKey);
583
1154
  }
584
- ctx.instanceCache.set(cacheKey, value);
585
- return value;
586
1155
  }
587
- function signatureToFnSig(ctx, sig) {
1156
+ function isThisParameter(param) {
1157
+ const name = param.name;
1158
+ return ts.isIdentifier(name) && name.text === "this";
1159
+ }
1160
+ function signatureToFnSig(ctx, sig, bindings) {
588
1161
  const savedTp = ctx.typeParams;
589
1162
  ctx.typeParams = new Set(savedTp);
590
1163
  for (const tp of sig.typeParameters ?? []) {
591
1164
  ctx.typeParams.add(tp.name.text);
592
1165
  }
593
1166
  try {
594
- const paramTypes = sig.parameters.map((param) => {
1167
+ const paramTypes = sig.parameters.filter((param) => !isThisParameter(param)).map((param) => {
595
1168
  let type;
596
1169
  if (param.dotDotDotToken) {
597
1170
  const inner = param.type && ts.isArrayTypeNode(param.type) ? param.type.elementType : param.type;
598
- type = inner ? absArr(mapType(ctx, inner, 0)) : absUnknown();
1171
+ type = inner ? absArr(mapType(ctx, inner, 0, bindings)) : absUnknown();
599
1172
  } else {
600
- type = param.type ? mapType(ctx, param.type, 0) : absUnknown();
1173
+ type = param.type ? mapType(ctx, param.type, 0, bindings) : absUnknown();
601
1174
  }
602
1175
  if (param.questionToken && !param.dotDotDotToken) type = absUnion([type, absUndefLit()]);
603
1176
  return type;
604
1177
  });
605
- const returnType = sig.type ? mapType(ctx, sig.type, 0) : absUnknown();
1178
+ const returnType = sig.type ? mapType(ctx, sig.type, 0, bindings) : absUnknown();
606
1179
  const cond = ctx.pendingCond;
607
1180
  ctx.pendingCond = void 0;
608
1181
  return absFnSig(paramTypes, returnType, cond ? {
@@ -613,123 +1186,506 @@ function signatureToFnSig(ctx, sig) {
613
1186
  ctx.typeParams = savedTp;
614
1187
  }
615
1188
  }
616
- function mapTypeMembers(ctx, members, depth) {
1189
+ function mapTypeMembers(ctx, members, depth, bindings) {
617
1190
  const properties = {};
1191
+ let index;
1192
+ const accessorReads = /* @__PURE__ */ new Map();
1193
+ const accessorWrites = /* @__PURE__ */ new Map();
618
1194
  for (const member of members) {
619
1195
  if (ts.isPropertySignature(member)) {
620
- const name = memberName(member);
1196
+ const name = memberName(member) ?? computedMemberName(member.name);
621
1197
  if (name === void 0) continue;
622
- let type = member.type ? mapType(ctx, member.type, depth) : absUnknown();
1198
+ let type = member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown();
623
1199
  if (member.questionToken) type = absUnion([type, absUndefLit()]);
624
1200
  properties[name] = type;
625
1201
  } else if (ts.isMethodSignature(member)) {
626
- const name = memberName(member);
1202
+ const name = memberName(member) ?? computedMemberName(member.name);
627
1203
  if (name === void 0 || properties[name] !== void 0) continue;
628
- properties[name] = signatureToFnSig(ctx, member);
1204
+ const sig = signatureToFnSig(ctx, member);
1205
+ properties[name] = member.questionToken ? absUnion([sig, absUndefLit()]) : sig;
1206
+ } else if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
1207
+ const name = memberName(member) ?? computedMemberName(member.name);
1208
+ if (name === void 0) continue;
1209
+ if (ts.isGetAccessorDeclaration(member)) {
1210
+ accessorReads.set(
1211
+ name,
1212
+ member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown()
1213
+ );
1214
+ } else {
1215
+ const param = member.parameters[0];
1216
+ accessorWrites.set(
1217
+ name,
1218
+ param?.type ? mapType(ctx, param.type, depth, bindings) : absUnknown()
1219
+ );
1220
+ }
1221
+ } else if (ts.isIndexSignatureDeclaration(member)) {
1222
+ const keyParam = member.parameters[0];
1223
+ const key = keyParam?.type ? mapType(ctx, keyParam.type, depth, bindings) : absPrim("string");
1224
+ const value = member.type ? mapType(ctx, member.type, depth, bindings) : absUnknown();
1225
+ index = { key, value };
1226
+ }
1227
+ }
1228
+ for (const [name, read] of accessorReads) {
1229
+ if (properties[name] === void 0) properties[name] = read;
1230
+ }
1231
+ for (const [name, write] of accessorWrites) {
1232
+ if (properties[name] === void 0) {
1233
+ properties[name] = absUnion([write, absUndefLit()]);
1234
+ }
1235
+ }
1236
+ return { properties, index };
1237
+ }
1238
+ function stripParens(node) {
1239
+ let n = node;
1240
+ while (ts.isParenthesizedTypeNode(n)) n = n.type;
1241
+ return n;
1242
+ }
1243
+ function fileSaltOf(fileName) {
1244
+ let h = 2166136261;
1245
+ for (let i = 0; i < fileName.length; i++) {
1246
+ h ^= fileName.charCodeAt(i);
1247
+ h = Math.imul(h, 16777619) >>> 0;
1248
+ }
1249
+ return h.toString(36);
1250
+ }
1251
+ function scopeTagOf(scope) {
1252
+ let file = "";
1253
+ try {
1254
+ file = scope.getSourceFile()?.fileName ?? "";
1255
+ } catch {
1256
+ file = "";
1257
+ }
1258
+ return `${fileSaltOf(file)}${scope.pos}`;
1259
+ }
1260
+ function typeParamVarId(node, name) {
1261
+ const scopes = [];
1262
+ let p = node.parent;
1263
+ while (p) {
1264
+ const tps = p.typeParameters;
1265
+ if (tps) {
1266
+ for (const tp of tps) {
1267
+ if (tp.name.text === name) {
1268
+ scopes.push(p);
1269
+ break;
1270
+ }
1271
+ }
1272
+ }
1273
+ p = p.parent;
1274
+ }
1275
+ if (scopes.length <= 1) return name;
1276
+ return `${name}@${scopeTagOf(scopes[0])}`;
1277
+ }
1278
+ function lookupTypeParamBinding(bindings, node, name) {
1279
+ if (!bindings) return void 0;
1280
+ const id = typeParamVarId(node, name);
1281
+ return bindings.get(id) ?? bindings.get(`infer:${name}`);
1282
+ }
1283
+ function absOpenObj(properties, index) {
1284
+ const slots = {};
1285
+ for (const [k, v] of Object.entries(properties)) {
1286
+ slots[k] = { value: v };
1287
+ }
1288
+ return objOf(slots, index ? { index, open: true } : { open: true });
1289
+ }
1290
+ function slotsOfAbs(a) {
1291
+ const s = a.shape;
1292
+ if (s.k === "obj") {
1293
+ const out = {};
1294
+ for (const [k, slot] of Object.entries(s.slots)) out[k] = slot.value;
1295
+ return out;
1296
+ }
1297
+ if (s.k === "brand") return slotsOfAbs(s.shape);
1298
+ return void 0;
1299
+ }
1300
+ function sameAbsType(a, b) {
1301
+ if (a === b) return true;
1302
+ return formatShape(a) === formatShape(b);
1303
+ }
1304
+ function mergeIntersection(members) {
1305
+ const slots = {};
1306
+ let merged = 0;
1307
+ for (const member of members) {
1308
+ const mSlots = slotsOfAbs(member);
1309
+ if (!mSlots) return void 0;
1310
+ merged++;
1311
+ for (const [key, value] of Object.entries(mSlots)) {
1312
+ if (!Object.hasOwn(slots, key)) {
1313
+ slots[key] = value;
1314
+ continue;
1315
+ }
1316
+ const prev = slots[key];
1317
+ if (!sameAbsType(prev, value)) slots[key] = absNever();
1318
+ }
1319
+ }
1320
+ if (merged === 0) return void 0;
1321
+ return absObj(slots);
1322
+ }
1323
+ function absCtorSig(params, instance, extra) {
1324
+ const a = absFnSig(params, instance, extra);
1325
+ a.shape.ctor = true;
1326
+ return a;
1327
+ }
1328
+ function takePendingCond(ctx) {
1329
+ const cond = ctx.pendingCond;
1330
+ ctx.pendingCond = void 0;
1331
+ return cond;
1332
+ }
1333
+ function condExtra(cond) {
1334
+ if (!cond) return void 0;
1335
+ return {
1336
+ inferFrom: { fromVar: cond.fromVar, via: cond.via, inferVar: cond.inferVar },
1337
+ condFallback: cond.fallback
1338
+ };
1339
+ }
1340
+ function attachFnSlots(fn, properties) {
1341
+ const names = Object.keys(properties);
1342
+ if (names.length === 0) return fn;
1343
+ const slots = {};
1344
+ for (const [k, v] of Object.entries(properties)) slots[k] = { value: v };
1345
+ fn.shape.slots = slots;
1346
+ return fn;
1347
+ }
1348
+ function keyofNamesOf(ctx, node, depth) {
1349
+ const n = stripParens(node);
1350
+ if (ts.isTypeLiteralNode(n)) {
1351
+ const names = [];
1352
+ for (const member of n.members) {
1353
+ if (ts.isIndexSignatureDeclaration(member)) continue;
1354
+ const name = memberName(member);
1355
+ if (name !== void 0) names.push(name);
1356
+ }
1357
+ return names;
1358
+ }
1359
+ if (ts.isTypeReferenceNode(n)) {
1360
+ const name = typeNameString(n.typeName);
1361
+ if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) {
1362
+ const slots = slotsOfAbs(instanceFor(ctx, name));
1363
+ return slots ? Object.keys(slots) : void 0;
1364
+ }
1365
+ const aliases = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? [];
1366
+ const alias = aliases[0];
1367
+ if (alias) {
1368
+ const slots = slotsOfAbs(mapType(ctx, alias.type, depth + 1));
1369
+ return slots ? Object.keys(slots) : void 0;
1370
+ }
1371
+ }
1372
+ return void 0;
1373
+ }
1374
+ var BUILTIN_TYPE_NAMES = /* @__PURE__ */ new Set([
1375
+ "Array",
1376
+ "ReadonlyArray",
1377
+ "Promise",
1378
+ "PromiseLike",
1379
+ "Map",
1380
+ "ReadonlyMap",
1381
+ "WeakMap",
1382
+ "Set",
1383
+ "ReadonlySet",
1384
+ "WeakSet",
1385
+ "Record",
1386
+ "Partial",
1387
+ "Readonly",
1388
+ "Required",
1389
+ "Pick",
1390
+ "Omit",
1391
+ "Exclude",
1392
+ "Extract",
1393
+ "NonNullable",
1394
+ "Parameters",
1395
+ "ReturnType",
1396
+ "Awaited",
1397
+ "Iterable",
1398
+ "Iterator",
1399
+ "IterableIterator",
1400
+ "AsyncIterable",
1401
+ "AsyncIterator",
1402
+ "AsyncIterableIterator",
1403
+ "Generator",
1404
+ "AsyncGenerator",
1405
+ "Date",
1406
+ "RegExp",
1407
+ "Error",
1408
+ "JSON",
1409
+ "Object",
1410
+ "Function",
1411
+ "ArrayBuffer",
1412
+ "Uint8Array",
1413
+ "Int8Array",
1414
+ "Uint16Array",
1415
+ "Int16Array",
1416
+ "Uint32Array",
1417
+ "Int32Array",
1418
+ "Float32Array",
1419
+ "Float64Array",
1420
+ "BigInt64Array",
1421
+ "BigUint64Array",
1422
+ "ArrayLike",
1423
+ "List",
1424
+ "Collection",
1425
+ "Many",
1426
+ "NumericDictionary",
1427
+ "Dictionary"
1428
+ ]);
1429
+ function typeSideKind(ctx, node, depth) {
1430
+ const n = stripParens(node);
1431
+ if (ts.isTypeOperatorNode(n)) {
1432
+ if (n.operator === ts.SyntaxKind.KeyOfKeyword) {
1433
+ return keyofNamesOf(ctx, n.type, depth) ? "ok" : "degraded";
629
1434
  }
1435
+ if (n.operator === ts.SyntaxKind.ReadonlyKeyword) return typeSideKind(ctx, n.type, depth);
1436
+ return "degraded";
1437
+ }
1438
+ if (ts.isTypeQueryNode(n)) return "degraded";
1439
+ if (ts.isMappedTypeNode(n)) return "degraded";
1440
+ if (ts.isIndexedAccessTypeNode(n)) return "degraded";
1441
+ if (ts.isImportTypeNode(n)) return "degraded";
1442
+ if (ts.isTemplateLiteralTypeNode(n)) return "degraded";
1443
+ if (ts.isConditionalTypeNode(n)) return "degraded";
1444
+ if (ts.isUnionTypeNode(n)) {
1445
+ for (const t of n.types) {
1446
+ if (typeSideKind(ctx, t, depth) === "degraded") return "degraded";
1447
+ }
1448
+ return "ok";
1449
+ }
1450
+ if (ts.isTypeReferenceNode(n)) {
1451
+ const name = typeNameString(n.typeName);
1452
+ if (ctx.typeParams.has(name)) return "ok";
1453
+ if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) return "ok";
1454
+ if ((ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? []).length > 0) return "ok";
1455
+ if (BUILTIN_TYPE_NAMES.has(name)) return "ok";
1456
+ return "degraded";
1457
+ }
1458
+ return "ok";
1459
+ }
1460
+ function isSignatureReturnPos(node) {
1461
+ let cur = node;
1462
+ let p = cur.parent;
1463
+ while (p && ts.isParenthesizedTypeNode(p)) {
1464
+ cur = p;
1465
+ p = p.parent;
1466
+ }
1467
+ if (!p) return false;
1468
+ switch (p.kind) {
1469
+ case ts.SyntaxKind.FunctionType:
1470
+ case ts.SyntaxKind.ConstructorType:
1471
+ return p.type === cur;
1472
+ case ts.SyntaxKind.FunctionDeclaration:
1473
+ case ts.SyntaxKind.MethodDeclaration:
1474
+ case ts.SyntaxKind.MethodSignature:
1475
+ case ts.SyntaxKind.CallSignature:
1476
+ case ts.SyntaxKind.ConstructSignature:
1477
+ return p.type === cur;
1478
+ default:
1479
+ return false;
630
1480
  }
631
- return properties;
632
1481
  }
633
- function mapTypeRef(ctx, node, depth) {
1482
+ function mapTypeRef(ctx, node, depth, bindings) {
634
1483
  const name = typeNameString(node.typeName);
635
1484
  const args = node.typeArguments ?? [];
636
1485
  if (ctx.typeParams.has(name) && args.length === 0) {
637
- return typeVarAbs(name);
1486
+ const bound = lookupTypeParamBinding(bindings, node, name);
1487
+ if (bound) return bound;
1488
+ return typeVarAbs(typeParamVarId(node, name));
638
1489
  }
639
1490
  if ((name === "Array" || name === "ReadonlyArray") && args.length >= 1) {
640
- return absArr(mapType(ctx, args[0], depth));
1491
+ return absArr(mapType(ctx, args[0], depth, bindings));
641
1492
  }
642
1493
  if ((name === "List" || name === "ArrayLike" || name === "Collection" || name === "Many" || name === "NumericDictionary" || name === "Dictionary") && args.length >= 1) {
643
- return absArr(mapType(ctx, args[0], depth));
1494
+ return absArr(mapType(ctx, args[0], depth, bindings));
644
1495
  }
645
1496
  if (name === "Promise" && args.length >= 1) {
646
- return absPromise(mapType(ctx, args[0], depth));
1497
+ return absPromise(mapType(ctx, args[0], depth, bindings));
647
1498
  }
648
1499
  if ((name === "Map" || name === "ReadonlyMap" || name === "WeakMap") && args.length >= 2) {
649
- return absMapType(name, mapType(ctx, args[0], depth), mapType(ctx, args[1], depth));
1500
+ return absMapType(
1501
+ name,
1502
+ mapType(ctx, args[0], depth, bindings),
1503
+ mapType(ctx, args[1], depth, bindings)
1504
+ );
650
1505
  }
651
1506
  if ((name === "Set" || name === "ReadonlySet" || name === "WeakSet") && args.length >= 1) {
652
- return absSetType(name, mapType(ctx, args[0], depth));
1507
+ return absSetType(name, mapType(ctx, args[0], depth, bindings));
653
1508
  }
654
1509
  if (name === "Record") {
655
- return absObj({});
1510
+ const value = args[1] ? mapType(ctx, args[1], depth, bindings) : absUnknown();
1511
+ const key = args[0] ? mapType(ctx, args[0], depth, bindings) : absPrim("string");
1512
+ return absOpenObj({}, { key, value });
656
1513
  }
657
1514
  if (ctx.interfaces.has(name) || ctx.qualifiedInterfaces.has(name)) {
658
- return instanceFor(ctx, name, args.map((a) => mapType(ctx, a, depth)));
1515
+ return instanceFor(
1516
+ ctx,
1517
+ name,
1518
+ args.map((a) => mapType(ctx, a, depth, bindings)),
1519
+ namespaceChainOf(node)
1520
+ );
659
1521
  }
660
1522
  const aliases = ctx.aliases.get(name) ?? ctx.qualifiedAliases.get(name) ?? [];
661
1523
  const alias = aliases[0];
662
- if (alias) return mapType(ctx, alias.type, depth + 1);
1524
+ if (alias) return mapAliasWithArgs(ctx, alias, args, depth, bindings);
663
1525
  return absUnknown();
664
1526
  }
665
- function collectInferNames(node, out = /* @__PURE__ */ new Set()) {
666
- const visit = (n) => {
667
- if (!n) return;
668
- if (ts.isInferTypeNode(n)) {
669
- out.add(n.typeParameter.name.text);
670
- visit(n.typeParameter.constraint);
671
- return;
672
- }
1527
+ function mapAliasWithArgs(ctx, alias, args, depth, bindings) {
1528
+ const tps = alias.typeParameters ?? [];
1529
+ if (tps.length === 0 || args.length === 0) return mapType(ctx, alias.type, depth + 1);
1530
+ const savedTp = ctx.typeParams;
1531
+ ctx.typeParams = new Set(savedTp);
1532
+ for (const tp of tps) ctx.typeParams.add(tp.name.text);
1533
+ try {
1534
+ const body = mapType(ctx, alias.type, depth + 1);
1535
+ const map = /* @__PURE__ */ new Map();
1536
+ tps.forEach((tp, i) => {
1537
+ const arg = args[i];
1538
+ if (!arg) return;
1539
+ map.set(
1540
+ typeParamVarId(alias.type, tp.name.text),
1541
+ mapType(ctx, arg, depth + 1, bindings)
1542
+ );
1543
+ });
1544
+ return map.size > 0 ? substAbs(body, map) : body;
1545
+ } finally {
1546
+ ctx.typeParams = savedTp;
1547
+ }
1548
+ }
1549
+ function collectInferNames(node, out = /* @__PURE__ */ new Set()) {
1550
+ const visit = (n) => {
1551
+ if (!n) return;
1552
+ if (ts.isParenthesizedTypeNode(n)) {
1553
+ visit(n.type);
1554
+ return;
1555
+ }
1556
+ if (ts.isInferTypeNode(n)) {
1557
+ out.add(n.typeParameter.name.text);
1558
+ visit(n.typeParameter.constraint);
1559
+ return;
1560
+ }
673
1561
  n.forEachChild(visit);
674
1562
  };
675
1563
  visit(node);
676
1564
  return out;
677
1565
  }
678
- function absExtends(check, ext) {
1566
+ function absExtends(check, ext, depth = 0) {
1567
+ if (depth > 6) return "unknown";
679
1568
  if (check.term?.op === "var") return "unknown";
680
- if (ext.term?.op === "lit" && (ext.term.value === null || ext.term.value === void 0)) {
681
- if (check.term?.op === "lit") {
682
- return check.term.value === ext.term.value ? "true" : "false";
1569
+ const cs = check.shape;
1570
+ const es = ext.shape;
1571
+ const cIsLit = check.term?.op === "lit";
1572
+ const eIsLit = ext.term?.op === "lit";
1573
+ const cLit = check.term && check.term.op === "lit" ? check.term.value : void 0;
1574
+ const eLit = ext.term && ext.term.op === "lit" ? ext.term.value : void 0;
1575
+ if (cIsLit && eIsLit) {
1576
+ return cLit === eLit && typeof cLit === typeof eLit ? "true" : "false";
1577
+ }
1578
+ if (cIsLit) {
1579
+ if (eIsLit && (eLit === null || eLit === void 0)) return "false";
1580
+ if (es.k === "prim") {
1581
+ if (cLit === null) return "false";
1582
+ return typeof cLit === es.type ? "true" : "false";
683
1583
  }
684
- 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") {
685
- return "false";
1584
+ if (es.k === "sum") {
1585
+ const flags = es.members.map((m) => absExtends(check, m, depth + 1));
1586
+ if (flags.some((f) => f === "true")) return "true";
1587
+ if (flags.every((f) => f === "false")) return "false";
1588
+ return "unknown";
686
1589
  }
1590
+ if (es.k === "unknown") return "true";
1591
+ if (es.k === "never") return "false";
1592
+ if (es.k === "any") return "unknown";
1593
+ if (es.k === "arr" || es.k === "tuple") return "false";
687
1594
  return "unknown";
688
1595
  }
689
- if (ext.shape.k === "unknown") return "true";
690
- if (ext.shape.k === "any") return "true";
691
- const cs = check.shape;
692
- const es = ext.shape;
1596
+ if (eIsLit) {
1597
+ return "false";
1598
+ }
1599
+ if (es.k === "any") return "unknown";
1600
+ if (es.k === "unknown") return "true";
1601
+ if (cs.k === "any") return "unknown";
1602
+ if (cs.k === "unknown") return "false";
1603
+ if (cs.k === "never") return "true";
1604
+ if (es.k === "never") return "false";
693
1605
  if (es.k === "prim" && cs.k === "prim") {
694
1606
  return cs.type === es.type ? "true" : "false";
695
1607
  }
1608
+ if ((es.k === "arr" || es.k === "tuple") && (cs.k === "prim" || cs.k === "obj" || cs.k === "fn" || cs.k === "brand")) {
1609
+ return "false";
1610
+ }
696
1611
  if (es.k === "sum") {
697
- const flags = es.members.map((m) => absExtends(check, m));
1612
+ const flags = es.members.map((m) => absExtends(check, m, depth + 1));
698
1613
  if (flags.some((f) => f === "true")) return "true";
699
1614
  if (flags.every((f) => f === "false")) return "false";
700
1615
  return "unknown";
701
1616
  }
702
1617
  if (cs.k === "sum") {
703
- const flags = cs.members.map((m) => absExtends(m, ext));
1618
+ const flags = cs.members.map((m) => absExtends(m, ext, depth + 1));
704
1619
  if (flags.every((f) => f === "true")) return "true";
705
1620
  if (flags.every((f) => f === "false")) return "false";
706
1621
  return "unknown";
707
1622
  }
708
1623
  if (cs.k === "arr" && es.k === "arr") {
709
- return absExtends(cs.element, es.element);
1624
+ return absExtends(cs.element, es.element, depth + 1);
1625
+ }
1626
+ if (cs.k === "tuple" && es.k === "arr") {
1627
+ const flags = cs.elements.map((el) => absExtends(el, es.element, depth + 1));
1628
+ return flags.some((f) => f === "false") ? "false" : "true";
1629
+ }
1630
+ if (cs.k === "tuple" && es.k === "tuple") {
1631
+ if (cs.elements.length !== es.elements.length) return "unknown";
1632
+ const flags = cs.elements.map((el, i) => absExtends(el, es.elements[i], depth + 1));
1633
+ if (flags.some((f) => f === "false")) return "false";
1634
+ if (flags.every((f) => f === "true")) return "true";
1635
+ return "unknown";
710
1636
  }
711
1637
  if (cs.k === "eff" && es.k === "eff" && cs.eff === es.eff) {
712
- return absExtends(cs.inner, es.inner);
1638
+ return absExtends(cs.inner, es.inner, depth + 1);
713
1639
  }
714
1640
  if (cs.k === "brand" && es.k === "brand") {
715
- return cs.name === es.name ? "true" : "false";
1641
+ if (cs.name === es.name) return "true";
1642
+ return structuralExtends(check, ext, depth);
1643
+ }
1644
+ if (cs.k === "obj" && es.k === "obj" || cs.k === "brand" && es.k === "obj" || cs.k === "obj" && es.k === "brand") {
1645
+ return structuralExtends(check, ext, depth);
1646
+ }
1647
+ if (cs.k === "fn" && es.k === "fn") {
1648
+ const a = cs.returnType;
1649
+ const b = es.returnType;
1650
+ if (!a || !b) return "unknown";
1651
+ return absExtends(a, b, depth + 1);
716
1652
  }
717
- if (cs.k === "never") return "true";
1653
+ return "unknown";
1654
+ }
1655
+ function structuralExtends(check, ext, depth) {
1656
+ const cs = slotsOfAbs(check);
1657
+ const es = slotsOfAbs(ext);
1658
+ if (!cs || !es) return "unknown";
1659
+ const extKeys = Object.keys(es);
1660
+ if (extKeys.length === 0) return "unknown";
1661
+ const flags = [];
1662
+ for (const key of extKeys) {
1663
+ const target = es[key];
1664
+ const source = cs[key];
1665
+ if (source === void 0) {
1666
+ flags.push("false");
1667
+ continue;
1668
+ }
1669
+ flags.push(absExtends(source, target, depth + 1));
1670
+ }
1671
+ if (flags.some((f) => f === "false")) return "false";
1672
+ if (flags.every((f) => f === "true")) return "true";
718
1673
  return "unknown";
719
1674
  }
720
1675
  function matchExtendsInfer(pattern, actual, inferMap, depth = 0) {
721
1676
  if (depth > 6) return "unknown";
722
- if (ts.isInferTypeNode(pattern)) {
723
- const name = pattern.typeParameter.name.text;
1677
+ const p = stripParens(pattern);
1678
+ if (ts.isInferTypeNode(p)) {
1679
+ const name = p.typeParameter.name.text;
724
1680
  if (!inferMap.has(name)) inferMap.set(name, actual);
725
1681
  return "true";
726
1682
  }
727
- if (ts.isArrayTypeNode(pattern) && actual.shape.k === "arr") {
728
- return matchExtendsInfer(pattern.elementType, actual.shape.element, inferMap, depth + 1);
1683
+ if (ts.isArrayTypeNode(p) && actual.shape.k === "arr") {
1684
+ return matchExtendsInfer(p.elementType, actual.shape.element, inferMap, depth + 1);
729
1685
  }
730
- if (ts.isTypeReferenceNode(pattern)) {
731
- const name = typeNameString(pattern.typeName);
732
- const args = pattern.typeArguments ?? [];
1686
+ if (ts.isTypeReferenceNode(p)) {
1687
+ const name = typeNameString(p.typeName);
1688
+ const args = p.typeArguments ?? [];
733
1689
  if ((name === "Array" || name === "ReadonlyArray" || name === "List" || name === "ArrayLike") && args[0] && actual.shape.k === "arr") {
734
1690
  return matchExtendsInfer(args[0], actual.shape.element, inferMap, depth + 1);
735
1691
  }
@@ -738,32 +1694,65 @@ function matchExtendsInfer(pattern, actual, inferMap, depth = 0) {
738
1694
  }
739
1695
  return "unknown";
740
1696
  }
741
- if (ts.isUnionTypeNode(pattern)) {
742
- const flags = pattern.types.map((t) => matchExtendsInfer(t, actual, inferMap, depth + 1));
1697
+ if (ts.isUnionTypeNode(p)) {
1698
+ const flags = p.types.map((t) => matchExtendsInfer(t, actual, inferMap, depth + 1));
743
1699
  if (flags.some((f) => f === "true")) return "true";
744
1700
  if (flags.every((f) => f === "false")) return "false";
745
1701
  return "unknown";
746
1702
  }
1703
+ if (ts.isTypeLiteralNode(p)) {
1704
+ const slots = slotsOfAbs(actual);
1705
+ if (!slots) return "unknown";
1706
+ const flags = [];
1707
+ for (const member of p.members) {
1708
+ if (!ts.isPropertySignature(member) || !member.type) continue;
1709
+ const name = memberName(member);
1710
+ if (name === void 0) continue;
1711
+ const source = slots[name];
1712
+ if (source === void 0) {
1713
+ flags.push("false");
1714
+ continue;
1715
+ }
1716
+ flags.push(matchExtendsInfer(member.type, source, inferMap, depth + 1));
1717
+ }
1718
+ if (flags.length === 0) return "unknown";
1719
+ if (flags.some((f) => f === "false")) return "false";
1720
+ if (flags.every((f) => f === "true")) return "true";
1721
+ return "unknown";
1722
+ }
747
1723
  return "unknown";
748
1724
  }
749
- function mapConditionalType(ctx, node, depth) {
1725
+ function mapConditionalType(ctx, node, depth, bindings) {
750
1726
  const inferMap = /* @__PURE__ */ new Map();
751
1727
  const inferNames = collectInferNames(node.extendsType);
752
- const check = mapType(ctx, node.checkType, depth + 1);
1728
+ const check = mapType(ctx, node.checkType, depth + 1, bindings);
753
1729
  const astMatch = matchExtendsInfer(node.extendsType, check, inferMap);
754
- const extAbs = mapType(ctx, node.extendsType, depth + 1);
755
- const absMatch = astMatch !== "unknown" ? astMatch : absExtends(check, extAbs);
1730
+ const extAbs = mapType(ctx, node.extendsType, depth + 1, bindings);
1731
+ const checkSide = typeSideKind(ctx, node.checkType, depth + 1);
1732
+ const extSide = typeSideKind(ctx, node.extendsType, depth + 1);
1733
+ let absMatch;
1734
+ if (extSide === "degraded" || checkSide === "degraded") {
1735
+ absMatch = "unknown";
1736
+ } else if (extSide === "unknownKeyword") {
1737
+ absMatch = "true";
1738
+ } else {
1739
+ absMatch = astMatch !== "unknown" ? astMatch : absExtends(check, extAbs);
1740
+ }
756
1741
  const condMeta = analyzeCondPattern(ctx, node, check);
757
1742
  const savedTp = ctx.typeParams;
758
1743
  ctx.typeParams = new Set(savedTp);
759
1744
  for (const n of inferNames) ctx.typeParams.add(n);
760
1745
  try {
761
- const trueAbs = inferMap.size > 0 ? substAbs(mapType(ctx, node.trueType, depth + 1), inferMap) : mapType(ctx, node.trueType, depth + 1);
762
- const falseAbs = mapType(ctx, node.falseType, depth + 1);
1746
+ const trueBindings = new Map(bindings ?? []);
1747
+ for (const [k, v] of inferMap) trueBindings.set(`infer:${k}`, v);
1748
+ const trueAbs = mapType(ctx, node.trueType, depth + 1, trueBindings);
1749
+ const falseAbs = mapType(ctx, node.falseType, depth + 1, bindings);
763
1750
  if (absMatch === "true") return trueAbs;
764
1751
  if (absMatch === "false") return falseAbs;
765
- if (condMeta && check.term?.op === "var") {
766
- ctx.pendingCond = { ...condMeta, fallback: falseAbs };
1752
+ if (condMeta && check.term?.op === "var" && trueBranchIsInferVar(node, condMeta)) {
1753
+ if (isSignatureReturnPos(node)) {
1754
+ ctx.pendingCond = { ...condMeta, fallback: falseAbs };
1755
+ }
767
1756
  return typeVarAbs(condMeta.inferVar);
768
1757
  }
769
1758
  return absUnion([trueAbs, falseAbs]);
@@ -771,10 +1760,18 @@ function mapConditionalType(ctx, node, depth) {
771
1760
  ctx.typeParams = savedTp;
772
1761
  }
773
1762
  }
1763
+ function trueBranchIsInferVar(node, condMeta) {
1764
+ const t = stripParens(node.trueType);
1765
+ if (ts.isInferTypeNode(t)) return t.typeParameter.name.text === condMeta.inferVar;
1766
+ if (ts.isTypeReferenceNode(t) && !(t.typeArguments && t.typeArguments.length > 0)) {
1767
+ return typeNameString(t.typeName) === condMeta.inferVar;
1768
+ }
1769
+ return false;
1770
+ }
774
1771
  function analyzeCondPattern(_ctx, node, check) {
775
1772
  if (check.term?.op !== "var") return void 0;
776
1773
  const fromVar = check.term.id;
777
- const ext = node.extendsType;
1774
+ const ext = stripParens(node.extendsType);
778
1775
  const names = collectInferNames(ext);
779
1776
  const inferVar = [...names][0];
780
1777
  if (!inferVar) return void 0;
@@ -790,20 +1787,40 @@ function analyzeCondPattern(_ctx, node, check) {
790
1787
  }
791
1788
  return void 0;
792
1789
  }
793
- function mapType(ctx, node, depth) {
1790
+ function mapType(ctx, node, depth, bindings) {
794
1791
  if (!node || depth > MAX_ALIAS_DEPTH) return absUnknown();
795
- if (ts.isParenthesizedTypeNode(node)) return mapType(ctx, node.type, depth);
796
- if (ts.isTypeReferenceNode(node)) return mapTypeRef(ctx, node, depth);
797
- if (ts.isArrayTypeNode(node)) return absArr(mapType(ctx, node.elementType, depth));
1792
+ if (ts.isParenthesizedTypeNode(node)) return mapType(ctx, node.type, depth, bindings);
1793
+ if (ts.isTypeReferenceNode(node)) return mapTypeRef(ctx, node, depth, bindings);
1794
+ if (ts.isArrayTypeNode(node)) return absArr(mapType(ctx, node.elementType, depth, bindings));
798
1795
  if (ts.isTupleTypeNode(node)) {
1796
+ const elements = [];
1797
+ let rest;
799
1798
  for (const element of node.elements) {
800
- const inner = ts.isNamedTupleMember(element) ? element.type : element;
801
- return absArr(mapType(ctx, inner, depth));
1799
+ if (ts.isNamedTupleMember(element)) {
1800
+ let t = mapType(ctx, element.type, depth, bindings);
1801
+ if (element.questionToken) t = absUnion([t, absUndefLit()]);
1802
+ elements.push(t);
1803
+ continue;
1804
+ }
1805
+ if (ts.isOptionalTypeNode(element)) {
1806
+ elements.push(absUnion([mapType(ctx, element.type, depth, bindings), absUndefLit()]));
1807
+ continue;
1808
+ }
1809
+ if (ts.isRestTypeNode(element)) {
1810
+ rest = mapType(ctx, element.type, depth, bindings);
1811
+ continue;
1812
+ }
1813
+ elements.push(mapType(ctx, element, depth, bindings));
802
1814
  }
803
- return absUnknown();
1815
+ return makeAbs(
1816
+ { k: "tuple", elements, ...rest ? { rest } : {} },
1817
+ void 0,
1818
+ void 0,
1819
+ "exact"
1820
+ );
804
1821
  }
805
1822
  if (ts.isUnionTypeNode(node)) {
806
- const members = node.types.map((t) => mapType(ctx, t, depth));
1823
+ const members = node.types.map((t) => mapType(ctx, t, depth, bindings));
807
1824
  const hasLit = members.some((m) => m.term?.op === "lit");
808
1825
  if (hasLit && members.length > 1) {
809
1826
  return absExact({ k: "sum", members });
@@ -811,17 +1828,9 @@ function mapType(ctx, node, depth) {
811
1828
  return absUnion(members);
812
1829
  }
813
1830
  if (ts.isIntersectionTypeNode(node)) {
814
- const members = node.types.map((t) => mapType(ctx, t, depth));
815
- if (members.length > 0 && members.every((m) => m.shape.k === "obj")) {
816
- const properties = {};
817
- for (const member of members) {
818
- if (member.shape.k !== "obj") continue;
819
- for (const [k, slot] of Object.entries(member.shape.slots)) {
820
- properties[k] = slot.value;
821
- }
822
- }
823
- return absObj(properties);
824
- }
1831
+ const members = node.types.map((t) => mapType(ctx, t, depth, bindings));
1832
+ const merged = mergeIntersection(members);
1833
+ if (merged) return merged;
825
1834
  return members[0] ?? absUnknown();
826
1835
  }
827
1836
  if (ts.isLiteralTypeNode(node)) {
@@ -842,22 +1851,61 @@ function mapType(ctx, node, depth) {
842
1851
  if (ctor) {
843
1852
  const params = [];
844
1853
  for (const param of ctor.parameters) {
845
- let type = param.type ? mapType(ctx, param.type, depth) : absUnknown();
1854
+ let type = param.type ? mapType(ctx, param.type, depth, bindings) : absUnknown();
1855
+ if (param.questionToken) type = absUnion([type, absUndefLit()]);
1856
+ params.push(type);
1857
+ }
1858
+ const instance = ctor.type ? mapType(ctx, ctor.type, depth, bindings) : absUnknown();
1859
+ const cond = takePendingCond(ctx);
1860
+ const { properties: properties2 } = mapTypeMembers(ctx, node.members, depth, bindings);
1861
+ return attachFnSlots(absCtorSig(params, instance, condExtra(cond)), properties2);
1862
+ }
1863
+ const call = node.members.find(ts.isCallSignatureDeclaration);
1864
+ if (call) {
1865
+ const params = [];
1866
+ for (const param of call.parameters) {
1867
+ let type = param.type ? mapType(ctx, param.type, depth, bindings) : absUnknown();
846
1868
  if (param.questionToken) type = absUnion([type, absUndefLit()]);
847
1869
  params.push(type);
848
1870
  }
849
- return absFnSig(params, ctor.type ? mapType(ctx, ctor.type, depth) : absUnknown());
1871
+ const ret = call.type ? mapType(ctx, call.type, depth, bindings) : absUnknown();
1872
+ const cond = takePendingCond(ctx);
1873
+ const { properties: properties2 } = mapTypeMembers(ctx, node.members, depth, bindings);
1874
+ return attachFnSlots(absFnSig(params, ret, condExtra(cond)), properties2);
850
1875
  }
851
- return absObj(mapTypeMembers(ctx, node.members, depth));
1876
+ const { properties, index } = mapTypeMembers(ctx, node.members, depth, bindings);
1877
+ if (index) return absOpenObj(properties, index);
1878
+ return absObj(properties);
852
1879
  }
853
1880
  if (ts.isTypeOperatorNode(node)) {
854
- return node.operator === ts.SyntaxKind.ReadonlyKeyword ? mapType(ctx, node.type, depth) : absUnknown();
1881
+ if (node.operator === ts.SyntaxKind.ReadonlyKeyword) {
1882
+ return mapType(ctx, node.type, depth, bindings);
1883
+ }
1884
+ if (node.operator === ts.SyntaxKind.KeyOfKeyword) {
1885
+ const names = keyofNamesOf(ctx, node.type, depth);
1886
+ if (!names) {
1887
+ ctx.degradedMark++;
1888
+ return absUnknown();
1889
+ }
1890
+ if (names.length === 0) return absNever();
1891
+ return absExact({ k: "sum", members: names.map((n) => absLit(n)) });
1892
+ }
1893
+ ctx.degradedMark++;
1894
+ return absUnknown();
855
1895
  }
856
1896
  if (ts.isConditionalTypeNode(node)) {
857
- return mapConditionalType(ctx, node, depth);
1897
+ const cond = mapConditionalType(ctx, node, depth, bindings);
1898
+ if (isPlainUnknown(cond)) ctx.degradedMark++;
1899
+ return cond;
858
1900
  }
859
1901
  if (ts.isInferTypeNode(node)) {
860
- return typeVarAbs(node.typeParameter.name.text);
1902
+ const name = node.typeParameter.name.text;
1903
+ const bound = bindings?.get(`infer:${name}`);
1904
+ if (bound) return bound;
1905
+ return typeVarAbs(name);
1906
+ }
1907
+ if (ts.isTypePredicateNode(node)) {
1908
+ return node.assertsModifier ? absUndefLit() : absPrim("boolean");
861
1909
  }
862
1910
  switch (node.kind) {
863
1911
  case ts.SyntaxKind.StringKeyword:
@@ -882,6 +1930,7 @@ function mapType(ctx, node, depth) {
882
1930
  case ts.SyntaxKind.NullKeyword:
883
1931
  return absNullLit();
884
1932
  default:
1933
+ ctx.degradedMark++;
885
1934
  return absUnknown();
886
1935
  }
887
1936
  }
@@ -889,6 +1938,7 @@ function indent(level) {
889
1938
  return " ".repeat(level);
890
1939
  }
891
1940
  function emitKey(key) {
1941
+ if (key === "__proto__") return `["__proto__"]`;
892
1942
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
893
1943
  }
894
1944
  function emitEntries(lines, record, level) {
@@ -912,6 +1962,9 @@ function emitAbs(a, expanding) {
912
1962
  }
913
1963
  return "absUnknown";
914
1964
  case "any":
1965
+ if (a.term?.op === "var") {
1966
+ return `makeAbs({ k: "any" }, termVar(${JSON.stringify(a.term.id)}), undefined, "path")`;
1967
+ }
915
1968
  return "absUnknown";
916
1969
  case "prim": {
917
1970
  if (a.term?.op === "lit") {
@@ -923,6 +1976,12 @@ function emitAbs(a, expanding) {
923
1976
  if (s.type === "number") return "num()";
924
1977
  if (s.type === "string") return "str()";
925
1978
  if (s.type === "boolean") return "bool()";
1979
+ if (s.type === "bigint") {
1980
+ return `makeAbs({ k: "prim", type: "bigint" }, undefined, undefined, "exact")`;
1981
+ }
1982
+ if (s.type === "symbol") {
1983
+ return `makeAbs({ k: "prim", type: "symbol" }, undefined, undefined, "exact")`;
1984
+ }
926
1985
  return "absUnknown";
927
1986
  }
928
1987
  case "arr":
@@ -957,7 +2016,15 @@ function emitAbs(a, expanding) {
957
2016
  const pts = s.paramTypes ?? [];
958
2017
  const params = pts.map((p) => emitAbs(p, expanding)).join(", ");
959
2018
  const ret = s.returnType ? emitAbs(s.returnType, expanding) : "absUnknown";
960
- return `relationFn([${params}], ${ret}, { conf: "exact" })`;
2019
+ const facets = [];
2020
+ if (s.ctor !== void 0) facets.push(`f.shape.ctor = ${s.ctor ? "true" : "false"};`);
2021
+ const slotEntries = Object.entries(s.slots ?? {});
2022
+ if (slotEntries.length > 0) {
2023
+ const entries = slotEntries.map(([key, slot]) => `${emitKey(key)}: { value: ${emitAbs(slot.value, expanding)} }`).join(", ");
2024
+ facets.push(`f.shape.slots = { ${entries} };`);
2025
+ }
2026
+ if (facets.length === 0) return `relationFn([${params}], ${ret}, { conf: "exact" })`;
2027
+ return `(() => { const f = relationFn([${params}], ${ret}, { conf: "exact" }); ${facets.join(" ")} return f; })()`;
961
2028
  }
962
2029
  default:
963
2030
  return "absUnknown";
@@ -968,7 +2035,11 @@ function emitAbs(a, expanding) {
968
2035
  import { readFileSync as readFileSync2, existsSync, statSync as statSync2, readdirSync } from "fs";
969
2036
  import { basename, dirname, join } from "path";
970
2037
  import ts2 from "typescript";
971
- import { toJsBindingIdent } from "@nudojs/core/internal";
2038
+ import {
2039
+ formatObjectKey,
2040
+ isJsBindingIdent,
2041
+ toJsBindingIdent
2042
+ } from "@nudojs/core/internal";
972
2043
  var BUILDERS = [
973
2044
  "fn",
974
2045
  "number",
@@ -977,38 +2048,52 @@ var BUILDERS = [
977
2048
  "any",
978
2049
  "shape",
979
2050
  "array",
980
- "lit",
981
- "union"
2051
+ "union",
2052
+ "partial"
982
2053
  ];
983
2054
  function keywordDsl(node) {
984
2055
  switch (node.kind) {
985
2056
  case ts2.SyntaxKind.NumberKeyword:
986
- return "number()";
2057
+ return { dsl: "number()" };
987
2058
  case ts2.SyntaxKind.StringKeyword:
988
- return "string()";
2059
+ return { dsl: "string()" };
989
2060
  case ts2.SyntaxKind.BooleanKeyword:
990
- return "boolean()";
2061
+ return { dsl: "boolean()" };
2062
+ // any / unknown:TS 语义就是「无约束」⇒ any() 是**忠实**映射
991
2063
  case ts2.SyntaxKind.AnyKeyword:
992
2064
  case ts2.SyntaxKind.UnknownKeyword:
2065
+ // void 返回:调用方拿不到值 ⇒ 无返回义务
993
2066
  case ts2.SyntaxKind.VoidKeyword:
994
- case ts2.SyntaxKind.UndefinedKeyword:
995
- case ts2.SyntaxKind.NullKeyword:
2067
+ // object:非原始类型 ⇒ 无约束(宽松但不引入假红)
996
2068
  case ts2.SyntaxKind.ObjectKeyword:
2069
+ return { dsl: "any()" };
2070
+ // null:TS 的**真 null 面**(不是 any())——Bug 801
2071
+ case ts2.SyntaxKind.NullKeyword:
2072
+ return { dsl: "null", raw: true };
2073
+ // undefined:TS 的**真 undefined 面**(union 里不能被 any() 吞掉)
2074
+ case ts2.SyntaxKind.UndefinedKeyword:
2075
+ return { dsl: "undefined", raw: true };
2076
+ // never 是底类型:DSL 无底约束 ⇒ 标为未建模(不能静默放宽成 any())
997
2077
  case ts2.SyntaxKind.NeverKeyword:
998
- return "any()";
2078
+ return { unmodeled: "never (bottom type is not expressible)" };
999
2079
  case ts2.SyntaxKind.BigIntKeyword:
2080
+ return { unmodeled: "bigint" };
1000
2081
  case ts2.SyntaxKind.SymbolKeyword:
1001
- return void 0;
2082
+ return { unmodeled: "symbol" };
1002
2083
  default:
1003
2084
  return void 0;
1004
2085
  }
1005
2086
  }
1006
2087
  function litDsl(node) {
1007
2088
  const lit = node.literal;
1008
- if (ts2.isStringLiteral(lit)) return `lit(${JSON.stringify(lit.text)})`;
1009
- if (ts2.isNumericLiteral(lit)) return `lit(${lit.text})`;
1010
- if (lit.kind === ts2.SyntaxKind.TrueKeyword) return "lit(true)";
1011
- if (lit.kind === ts2.SyntaxKind.FalseKeyword) return "lit(false)";
2089
+ if (ts2.isStringLiteral(lit)) return { dsl: JSON.stringify(lit.text), modeled: true, raw: true };
2090
+ if (ts2.isNumericLiteral(lit)) return { dsl: lit.text, modeled: true, raw: true };
2091
+ if (lit.kind === ts2.SyntaxKind.TrueKeyword) return { dsl: "true", modeled: true, raw: true };
2092
+ if (lit.kind === ts2.SyntaxKind.FalseKeyword) return { dsl: "false", modeled: true, raw: true };
2093
+ if (lit.kind === ts2.SyntaxKind.NullKeyword) return { dsl: "null", modeled: true, raw: true };
2094
+ if (ts2.isPrefixUnaryExpression(lit) && lit.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(lit.operand)) {
2095
+ return { dsl: `-${lit.operand.text}`, modeled: true, raw: true };
2096
+ }
1012
2097
  return void 0;
1013
2098
  }
1014
2099
  function typeRefName(node) {
@@ -1026,173 +2111,1001 @@ function typeRefName(node) {
1026
2111
  }
1027
2112
  return "?";
1028
2113
  }
1029
- function typeToDsl(node, depth = 0) {
1030
- if (!node || depth > 6) return "any()";
2114
+ function memberKey(name) {
2115
+ if (!name) return void 0;
2116
+ if (ts2.isIdentifier(name)) return name.text;
2117
+ if (ts2.isStringLiteral(name) || ts2.isNumericLiteral(name)) return name.text;
2118
+ if (ts2.isComputedPropertyName(name)) {
2119
+ const expr = name.expression;
2120
+ if (ts2.isPropertyAccessExpression(expr) && ts2.isIdentifier(expr.expression) && expr.expression.text === "Symbol") {
2121
+ if (expr.name.text === "iterator") return "@@iterator";
2122
+ if (expr.name.text === "asyncIterator") return "@@asyncIterator";
2123
+ return void 0;
2124
+ }
2125
+ if (ts2.isStringLiteral(expr)) return expr.text;
2126
+ return void 0;
2127
+ }
2128
+ return void 0;
2129
+ }
2130
+ function memberKeyOrNote(name, what, ctx) {
2131
+ const key = memberKey(name);
2132
+ if (key !== void 0) return key;
2133
+ ctx.notes.push(`${what} member with a name that cannot be resolved statically is not projected`);
2134
+ return void 0;
2135
+ }
2136
+ function draftKey(key) {
2137
+ if (key === "__proto__") return `["__proto__"]`;
2138
+ return formatObjectKey(key);
2139
+ }
2140
+ function optionalize(d) {
2141
+ const base = !d.raw ? d.dsl : d.dsl === "undefined" ? "union(undefined)" : `union(${d.dsl}, undefined)`;
2142
+ return `${base}.optional()`;
2143
+ }
2144
+ function constraintSlot(d) {
2145
+ return d.raw ? `union(${d.dsl})` : d.dsl;
2146
+ }
2147
+ function typeToDsl(node, ctx) {
2148
+ if (!node) return void 0;
2149
+ if (ctx.depth > 6) {
2150
+ ctx.notes.push("type nesting too deep \u2192 any()");
2151
+ return { dsl: "any()", modeled: false };
2152
+ }
1031
2153
  const kw = keywordDsl(node);
1032
- if (kw !== void 0) return kw;
2154
+ if (kw) {
2155
+ if ("dsl" in kw) {
2156
+ return { dsl: kw.dsl, modeled: true, ...kw.raw ? { raw: true } : {} };
2157
+ }
2158
+ ctx.notes.push(`unmodeled type '${kw.unmodeled}' \u2192 any()`);
2159
+ return { dsl: "any()", modeled: false };
2160
+ }
1033
2161
  if (ts2.isLiteralTypeNode(node)) {
1034
- return litDsl(node) ?? "any()";
2162
+ return litDsl(node) ?? { dsl: "any()", modeled: false };
1035
2163
  }
1036
2164
  if (ts2.isArrayTypeNode(node)) {
1037
- const el = typeToDsl(node.elementType, depth + 1) ?? "any()";
1038
- return `array(${el})`;
2165
+ const el = typeToDsl(node.elementType, child(ctx)) ?? { dsl: "any()", modeled: false };
2166
+ return { dsl: `array(${el.dsl})`, modeled: el.modeled };
1039
2167
  }
1040
2168
  if (ts2.isUnionTypeNode(node)) {
1041
- const parts = node.types.map((t) => typeToDsl(t, depth + 1) ?? "any()");
1042
- if (parts.length === 0) return "any()";
2169
+ const parts = [];
2170
+ for (const t of node.types) {
2171
+ const d = typeToDsl(t, child(ctx));
2172
+ parts.push(d ?? { dsl: "any()", modeled: false });
2173
+ }
2174
+ if (parts.length === 0) return { dsl: "any()", modeled: false };
2175
+ if (parts.some((p) => !p.modeled)) {
2176
+ ctx.notes.push("union member not modeled \u2192 any()");
2177
+ return { dsl: "any()", modeled: false };
2178
+ }
1043
2179
  if (parts.length === 1) return parts[0];
1044
- return `union(${parts.join(", ")})`;
2180
+ return { dsl: `union(${parts.map((p) => p.dsl).join(", ")})`, modeled: true };
1045
2181
  }
1046
2182
  if (ts2.isParenthesizedTypeNode(node)) {
1047
- return typeToDsl(node.type, depth + 1);
2183
+ return typeToDsl(node.type, child(ctx));
1048
2184
  }
1049
2185
  if (ts2.isTypeLiteralNode(node)) {
1050
- const fields = [];
1051
- for (const m of node.members) {
1052
- if (!ts2.isPropertySignature(m)) continue;
1053
- const key = m.name && (ts2.isIdentifier(m.name) || ts2.isStringLiteral(m.name)) ? ts2.isIdentifier(m.name) ? m.name.text : m.name.text : void 0;
1054
- if (!key) continue;
1055
- const optional = m.questionToken ? true : false;
1056
- const val = typeToDsl(m.type, depth + 1) ?? "any()";
1057
- fields.push(optional ? `${key}: ${val}.optional()` : `${key}: ${val}`);
1058
- }
1059
- return fields.length === 0 ? "shape({})" : `shape({ ${fields.join(", ")} })`;
2186
+ const mark = noteMark(ctx);
2187
+ const dsl = membersToShape(node.members, ctx);
2188
+ return { dsl, modeled: ctx.notes.length === mark };
1060
2189
  }
1061
2190
  if (ts2.isFunctionTypeNode(node) || ts2.isConstructorTypeNode(node)) {
1062
- return fnSignatureDsl(node.parameters, node.type, depth);
2191
+ return fnSignatureDsl(node.parameters, node.type, ctx);
2192
+ }
2193
+ if (ts2.isConditionalTypeNode(node)) {
2194
+ return conditionalDsl(node, ctx);
1063
2195
  }
1064
2196
  if (ts2.isTypeReferenceNode(node)) {
1065
- const name = typeRefName(node);
1066
- const args = node.typeArguments ?? [];
1067
- if (name === "Array" || name === "ReadonlyArray" || name === "readonly") {
1068
- const el = args[0] ? typeToDsl(args[0], depth + 1) : "any()";
1069
- return `array(${el ?? "any()"})`;
2197
+ return typeRefDsl(node, ctx);
2198
+ }
2199
+ if (ts2.isExpressionWithTypeArguments(node)) {
2200
+ ctx.notes.push("unmodeled type reference \u2192 any()");
2201
+ return { dsl: "any()", modeled: false };
2202
+ }
2203
+ ctx.notes.push("unmodeled type \u2192 any()");
2204
+ return { dsl: "any()", modeled: false };
2205
+ }
2206
+ function conditionalDsl(node, ctx) {
2207
+ const ext = ts2.isParenthesizedTypeNode(node.extendsType) ? node.extendsType.type : node.extendsType;
2208
+ if (ext.kind === ts2.SyntaxKind.UnknownKeyword) {
2209
+ const t2 = typeToDsl(node.trueType, child(ctx));
2210
+ ctx.notes.push(
2211
+ "conditional type \u2014 the extends side is `unknown` (always true); only the true branch is projected"
2212
+ );
2213
+ if (!t2) return { dsl: "any()", modeled: false };
2214
+ return { dsl: t2.dsl, modeled: false, ...t2.raw ? { raw: true } : {} };
2215
+ }
2216
+ const t = typeToDsl(node.trueType, child(ctx));
2217
+ const f = typeToDsl(node.falseType, child(ctx));
2218
+ if (!t || !f || !t.modeled || !f.modeled) {
2219
+ ctx.notes.push(
2220
+ "conditional type \u2014 not decidable from the declaration and its branches are not modeled \u2192 any() (same as the harvest face's undecidable branch)"
2221
+ );
2222
+ return { dsl: "any()", modeled: false };
2223
+ }
2224
+ ctx.notes.push(
2225
+ "conditional type \u2014 not decidable from the declaration; both branches are projected (union), same as the harvest face"
2226
+ );
2227
+ if (t.dsl === f.dsl) {
2228
+ return { dsl: t.dsl, modeled: false, ...t.raw ? { raw: true } : {} };
2229
+ }
2230
+ return { dsl: `union(${t.dsl}, ${f.dsl})`, modeled: false };
2231
+ }
2232
+ function noteMark(ctx) {
2233
+ return ctx.notes.length;
2234
+ }
2235
+ function child(ctx) {
2236
+ return {
2237
+ symbols: ctx.symbols,
2238
+ notes: ctx.notes,
2239
+ depth: ctx.depth + 1,
2240
+ resolving: ctx.resolving,
2241
+ lost: ctx.lost
2242
+ };
2243
+ }
2244
+ function literalKeys(node) {
2245
+ if (!node) return void 0;
2246
+ if (ts2.isLiteralTypeNode(node)) {
2247
+ const lit = node.literal;
2248
+ if (ts2.isStringLiteral(lit) || ts2.isNumericLiteral(lit)) return [lit.text];
2249
+ return void 0;
2250
+ }
2251
+ if (ts2.isUnionTypeNode(node)) {
2252
+ const out = [];
2253
+ for (const t of node.types) {
2254
+ const k = literalKeys(t);
2255
+ if (!k) return void 0;
2256
+ out.push(...k);
1070
2257
  }
1071
- if (name === "Promise") {
1072
- return args[0] ? typeToDsl(args[0], depth + 1) : "any()";
2258
+ return out.length > 0 ? out : void 0;
2259
+ }
2260
+ return void 0;
2261
+ }
2262
+ function recordDsl(args, ctx) {
2263
+ const val = args[1] ? typeToDsl(args[1], child(ctx)) : void 0;
2264
+ const keys = literalKeys(args[0]);
2265
+ if (keys && keys.length > 0 && val) {
2266
+ const fields = keys.map((k) => `${draftKey(k)}: ${val.dsl}`);
2267
+ return { dsl: `shape({ ${fields.join(", ")} })`, modeled: val.modeled };
2268
+ }
2269
+ ctx.notes.push(
2270
+ "Record value domain is NOT modeled \u2014 the contract DSL has no index signature, so this position only requires an object (weaker than TypeScript)"
2271
+ );
2272
+ return { dsl: "shape({})", modeled: false };
2273
+ }
2274
+ function partialDsl(args, ctx) {
2275
+ const inner = args[0];
2276
+ if (!inner) {
2277
+ ctx.notes.push("Partial<> without a type argument \u2192 any()");
2278
+ return { dsl: "any()", modeled: false };
2279
+ }
2280
+ const d = typeToDsl(inner, child(ctx));
2281
+ if (!d) {
2282
+ ctx.notes.push("Partial<T> over an unmodeled type \u2192 any()");
2283
+ return { dsl: "any()", modeled: false };
2284
+ }
2285
+ if (!d.dsl.startsWith("shape(")) {
2286
+ ctx.notes.push("Partial<T> over a non-shape type is not expressible \u2192 any()");
2287
+ return { dsl: "any()", modeled: false };
2288
+ }
2289
+ return { dsl: `partial(${d.dsl})`, modeled: d.modeled };
2290
+ }
2291
+ function typeRefDsl(node, ctx) {
2292
+ const name = typeRefName(node);
2293
+ const args = node.typeArguments ?? [];
2294
+ if (name === "Array" || name === "ReadonlyArray" || name === "readonly") {
2295
+ const el = args[0] ? typeToDsl(args[0], child(ctx)) : void 0;
2296
+ return { dsl: `array(${el?.dsl ?? "any()"})`, modeled: el?.modeled ?? false };
2297
+ }
2298
+ if (name === "Promise" || name === "PromiseLike") {
2299
+ ctx.notes.push(
2300
+ `${name}<\u2026> is not modeled \u2014 the Promise shell is kept (same as the harvest face); this position carries no constraint`
2301
+ );
2302
+ return { dsl: "any()", modeled: false };
2303
+ }
2304
+ if (name === "Record") return recordDsl(args, ctx);
2305
+ if (name === "Partial") return partialDsl(args, ctx);
2306
+ const alias = lookupAlias(name, ctx);
2307
+ if (alias) {
2308
+ if (ctx.resolving.has(name)) {
2309
+ ctx.notes.push(`type alias '${name}' is recursive \u2192 any()`);
2310
+ return { dsl: "any()", modeled: false };
2311
+ }
2312
+ ctx.resolving.add(name);
2313
+ const d = typeToDsl(alias.type, child(ctx));
2314
+ ctx.resolving.delete(name);
2315
+ if (!d) {
2316
+ ctx.notes.push(`type alias '${name}' has no type \u2192 any()`);
2317
+ return { dsl: "any()", modeled: false };
2318
+ }
2319
+ return d;
2320
+ }
2321
+ const iface = lookupInterface(name, ctx);
2322
+ if (iface) {
2323
+ const mark = noteMark(ctx);
2324
+ ctx.resolving.add(name);
2325
+ const dsl = membersToShape(interfaceMembers(iface, ctx), ctx);
2326
+ ctx.resolving.delete(name);
2327
+ return { dsl, modeled: ctx.notes.length === mark };
2328
+ }
2329
+ ctx.notes.push(`unmodeled type reference '${name}' \u2192 any()`);
2330
+ return { dsl: "any()", modeled: false };
2331
+ }
2332
+ function lookupInterface(name, ctx) {
2333
+ if (!name.includes(".")) return ctx.symbols.interfaces.get(name);
2334
+ const qualified = ctx.symbols.qualifiedInterfaces.get(name);
2335
+ if (qualified) return qualified;
2336
+ const short = name.slice(name.lastIndexOf(".") + 1);
2337
+ const decl = ctx.symbols.interfaces.get(short);
2338
+ if (decl && inAmbientModuleBlock2(decl)) return decl;
2339
+ return void 0;
2340
+ }
2341
+ function lookupAlias(name, ctx) {
2342
+ if (!name.includes(".")) return ctx.symbols.aliases.get(name);
2343
+ const qualified = ctx.symbols.qualifiedAliases.get(name);
2344
+ if (qualified) return qualified;
2345
+ const short = name.slice(name.lastIndexOf(".") + 1);
2346
+ const decl = ctx.symbols.aliases.get(short);
2347
+ if (decl && inAmbientModuleBlock2(decl)) return decl;
2348
+ return void 0;
2349
+ }
2350
+ function inAmbientModuleBlock2(decl) {
2351
+ let node = decl.parent;
2352
+ while (node) {
2353
+ if (ts2.isModuleDeclaration(node) && ts2.isStringLiteral(node.name)) return true;
2354
+ node = node.parent;
2355
+ }
2356
+ return false;
2357
+ }
2358
+ function interfaceMembers(iface, ctx) {
2359
+ const out = [];
2360
+ for (const clause of iface.heritageClauses ?? []) {
2361
+ if (clause.token !== ts2.SyntaxKind.ExtendsKeyword) continue;
2362
+ for (const t of clause.types) {
2363
+ const name = heritageBaseName2(t);
2364
+ const base = name ? lookupInterface(name, ctx) : void 0;
2365
+ if (!base || !name) {
2366
+ ctx.notes.push(
2367
+ `interface '${iface.name.text}' extends ${name ?? "a non-local type"} \u2014 the inherited members are not modeled`
2368
+ );
2369
+ continue;
2370
+ }
2371
+ if (ctx.resolving.has(name)) {
2372
+ ctx.notes.push(`interface '${name}' is recursive \u2014 the inherited members are not modeled`);
2373
+ continue;
2374
+ }
2375
+ ctx.resolving.add(name);
2376
+ out.push(...interfaceMembers(base, ctx));
2377
+ ctx.resolving.delete(name);
1073
2378
  }
1074
- if (name === "Record") {
1075
- const val = args[1] ? typeToDsl(args[1], depth + 1) : "any()";
1076
- return `shape({}) /* open Record \u2192 ${val ?? "any()"} */`;
2379
+ }
2380
+ out.push(...iface.members);
2381
+ return out;
2382
+ }
2383
+ function heritageBaseName2(t) {
2384
+ const expr = t.expression;
2385
+ if (ts2.isIdentifier(expr)) return expr.text;
2386
+ if (ts2.isPropertyAccessExpression(expr) && ts2.isIdentifier(expr.expression) && ts2.isIdentifier(expr.name)) {
2387
+ return `${expr.expression.text}.${expr.name.text}`;
2388
+ }
2389
+ return void 0;
2390
+ }
2391
+ function membersToShape(members, ctx) {
2392
+ const fields = [];
2393
+ let dropped = 0;
2394
+ for (const m of members) {
2395
+ const field = memberToField(m, ctx);
2396
+ if (field === void 0) {
2397
+ dropped++;
2398
+ continue;
1077
2399
  }
1078
- if (name === "Partial" && args[0]) {
1079
- const inner = typeToDsl(args[0], depth + 1);
1080
- return inner ?? "any()";
2400
+ fields.push(field);
2401
+ }
2402
+ if (fields.length === 0) {
2403
+ if (dropped > 0) {
2404
+ ctx.notes.push("all members of the type are unmodeled \u2192 any()");
2405
+ return "any()";
1081
2406
  }
2407
+ return "shape({})";
2408
+ }
2409
+ return `shape({ ${fields.join(", ")} })`;
2410
+ }
2411
+ function memberToField(m, ctx) {
2412
+ if (ts2.isPropertySignature(m)) {
2413
+ const key = memberKeyOrNote(m.name, "property", ctx);
2414
+ if (key === void 0) return void 0;
2415
+ const d = typeToDsl(m.type, child(ctx)) ?? { dsl: "any()", modeled: false };
2416
+ const v = m.questionToken ? optionalize(d) : d.dsl;
2417
+ return `${draftKey(key)}: ${v}`;
2418
+ }
2419
+ if (ts2.isMethodSignature(m)) {
2420
+ const key = memberKeyOrNote(m.name, "method", ctx);
2421
+ if (key === void 0) return void 0;
2422
+ const d = fnSignatureDsl(m.parameters, m.type, ctx);
2423
+ const v = m.questionToken ? optionalize(d) : d.dsl;
2424
+ return `${draftKey(key)}: ${v}`;
2425
+ }
2426
+ if (ts2.isIndexSignatureDeclaration(m)) {
2427
+ ctx.notes.push("index signature member is not modeled (the contract DSL has no index signature)");
1082
2428
  return void 0;
1083
2429
  }
1084
- if (ts2.isExpressionWithTypeArguments(node)) {
2430
+ if (ts2.isCallSignatureDeclaration(m) || ts2.isConstructSignatureDeclaration(m)) {
2431
+ ctx.notes.push("call/construct signature member is not modeled");
1085
2432
  return void 0;
1086
2433
  }
2434
+ if (ts2.isGetAccessorDeclaration(m) || ts2.isSetAccessorDeclaration(m)) {
2435
+ ctx.notes.push("accessor member is not modeled");
2436
+ return void 0;
2437
+ }
2438
+ ctx.notes.push("member of an unsupported form is not projected");
1087
2439
  return void 0;
1088
2440
  }
1089
- function fnSignatureDsl(params, ret, depth) {
1090
- const fields = [];
1091
- const notes = [];
2441
+ function paramSlots(params, ctx) {
2442
+ const out = [];
1092
2443
  params.forEach((p, i) => {
1093
- let name;
1094
- if (p.name && ts2.isIdentifier(p.name)) name = p.name.text;
1095
- else name = `p${i}`;
2444
+ const name = p.name && ts2.isIdentifier(p.name) ? p.name.text : `p${i}`;
2445
+ if (name === "this") return;
1096
2446
  if (p.dotDotDotToken) {
1097
- notes.push(`${name}: rest \u2014 array()`);
1098
- fields.push(`${name}: array(any())`);
2447
+ ctx.notes.push(`${name}: rest parameter \u2192 array(any())`);
2448
+ out.push({ name, optional: false, dsl: "array(any())", modeled: false });
1099
2449
  return;
1100
2450
  }
1101
- const optional = Boolean(p.questionToken) || p.initializer;
1102
- let dsl = typeToDsl(p.type, depth + 1);
1103
- if (dsl === void 0) {
1104
- notes.push(`${name}: unmodeled type \u2192 any()`);
1105
- dsl = "any()";
2451
+ const optional = Boolean(p.questionToken) || Boolean(p.initializer);
2452
+ const d = typeToDsl(p.type, child(ctx));
2453
+ if (!d) {
2454
+ ctx.notes.push(`${name}: no type annotation \u2192 any()`);
2455
+ out.push({ name, optional, dsl: "any()", modeled: false, plain: true });
2456
+ return;
1106
2457
  }
1107
- fields.push(optional ? `${name}: ${dsl}.optional()` : `${name}: ${dsl}`);
2458
+ out.push({
2459
+ name,
2460
+ optional,
2461
+ dsl: d.dsl,
2462
+ ...d.raw ? { raw: true } : {},
2463
+ modeled: d.modeled
2464
+ });
1108
2465
  });
1109
- let retDsl = typeToDsl(ret, depth + 1);
1110
- if (retDsl === void 0) {
1111
- notes.push(`returns: unmodeled \u2192 any()`);
1112
- retDsl = "any()";
2466
+ return out;
2467
+ }
2468
+ function slotText(s) {
2469
+ if (s.plain) return s.dsl;
2470
+ if (!s.optional) return s.dsl;
2471
+ return optionalize({
2472
+ dsl: s.dsl,
2473
+ modeled: s.modeled,
2474
+ ...s.raw ? { raw: true } : {}
2475
+ });
2476
+ }
2477
+ function slotsToDsl(slots) {
2478
+ const fields = slots.map((s) => `${s.name}: ${slotText(s)}`);
2479
+ return fields.length === 0 ? "{}" : `{ ${fields.join(", ")} }`;
2480
+ }
2481
+ function paramsDsl(params, ctx) {
2482
+ return slotsToDsl(paramSlots(params, ctx));
2483
+ }
2484
+ function mergeParamSlots(per) {
2485
+ const first = per[0];
2486
+ if (!first || first.length === 0) return void 0;
2487
+ const out = [];
2488
+ for (let i = 0; i < first.length; i++) {
2489
+ const slot = first[i];
2490
+ const texts = [];
2491
+ let ok = true;
2492
+ for (const slots of per) {
2493
+ const s = slots[i];
2494
+ if (!s || s.optional !== slot.optional || !s.modeled || !slot.modeled) {
2495
+ ok = false;
2496
+ break;
2497
+ }
2498
+ if (!texts.includes(s.dsl)) texts.push(s.dsl);
2499
+ }
2500
+ if (!ok) return void 0;
2501
+ const mergedText = texts.length === 1 ? texts[0] : `union(${texts.join(", ")})`;
2502
+ out.push({
2503
+ name: slot.name,
2504
+ optional: slot.optional,
2505
+ dsl: mergedText,
2506
+ modeled: true,
2507
+ ...texts.length === 1 && slot.raw ? { raw: true } : {}
2508
+ });
2509
+ }
2510
+ return out;
2511
+ }
2512
+ function fnSignatureDsl(params, ret, ctx) {
2513
+ const p = paramsDsl(params, ctx);
2514
+ const r = typeToDsl(ret, child(ctx));
2515
+ if (!r) {
2516
+ return { dsl: `fn(${p}, any())`, modeled: true };
1113
2517
  }
1114
- const obj = fields.length === 0 ? "{}" : `{ ${fields.join(", ")} }`;
1115
- return `fn(${obj}, ${retDsl})`;
2518
+ return { dsl: `fn(${p}, ${constraintSlot(r)})`, modeled: r.modeled };
1116
2519
  }
1117
2520
  function pickName(decl) {
1118
2521
  return decl.name && ts2.isIdentifier(decl.name) ? decl.name.text : void 0;
1119
2522
  }
2523
+ function isExported(stmt) {
2524
+ const mods = stmt.modifiers;
2525
+ return mods?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
2526
+ }
2527
+ function isDefaultExported(stmt) {
2528
+ const mods = stmt.modifiers;
2529
+ return mods?.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword) ?? false;
2530
+ }
2531
+ function collectSymbols(sf) {
2532
+ const aliases = /* @__PURE__ */ new Map();
2533
+ const interfaces = /* @__PURE__ */ new Map();
2534
+ const qualifiedAliases = /* @__PURE__ */ new Map();
2535
+ const qualifiedInterfaces = /* @__PURE__ */ new Map();
2536
+ const walk = (stmts, ns) => {
2537
+ for (const stmt of stmts) {
2538
+ if (ts2.isTypeAliasDeclaration(stmt)) {
2539
+ if (!aliases.has(stmt.name.text)) aliases.set(stmt.name.text, stmt);
2540
+ const q = [...ns, stmt.name.text].join(".");
2541
+ if (q !== stmt.name.text && !qualifiedAliases.has(q)) qualifiedAliases.set(q, stmt);
2542
+ } else if (ts2.isInterfaceDeclaration(stmt)) {
2543
+ if (!interfaces.has(stmt.name.text)) interfaces.set(stmt.name.text, stmt);
2544
+ const q = [...ns, stmt.name.text].join(".");
2545
+ if (q !== stmt.name.text && !qualifiedInterfaces.has(q)) qualifiedInterfaces.set(q, stmt);
2546
+ } else if (ts2.isModuleDeclaration(stmt) && stmt.body && ts2.isModuleBlock(stmt.body) && ts2.isIdentifier(stmt.name) && stmt.name.text !== "global") {
2547
+ walk(stmt.body.statements, [...ns, stmt.name.text]);
2548
+ }
2549
+ }
2550
+ };
2551
+ walk(sf.statements, []);
2552
+ return { aliases, interfaces, qualifiedAliases, qualifiedInterfaces };
2553
+ }
2554
+ function collectLocals(stmts) {
2555
+ const out = /* @__PURE__ */ new Map();
2556
+ const put = (name, stmt) => {
2557
+ if (name && !out.has(name)) out.set(name, stmt);
2558
+ };
2559
+ for (const stmt of stmts) {
2560
+ if (ts2.isVariableStatement(stmt)) {
2561
+ for (const d of stmt.declarationList.declarations) put(pickName(d), stmt);
2562
+ continue;
2563
+ }
2564
+ if (ts2.isFunctionDeclaration(stmt) || ts2.isClassDeclaration(stmt) || ts2.isEnumDeclaration(stmt) || ts2.isModuleDeclaration(stmt)) {
2565
+ put(pickName(stmt), stmt);
2566
+ }
2567
+ }
2568
+ return out;
2569
+ }
2570
+ function takeNotes(ctx) {
2571
+ const out = ctx.notes.slice();
2572
+ ctx.notes.length = 0;
2573
+ return out;
2574
+ }
2575
+ function enumMemberValue(m, counter, ctx) {
2576
+ const init = m.initializer;
2577
+ if (!init) {
2578
+ const v = counter.value;
2579
+ counter.value = v + 1;
2580
+ return String(v);
2581
+ }
2582
+ if (ts2.isStringLiteral(init)) return JSON.stringify(init.text);
2583
+ if (ts2.isNumericLiteral(init)) {
2584
+ const v = Number(init.text);
2585
+ counter.value = v + 1;
2586
+ return init.text;
2587
+ }
2588
+ if (ts2.isPrefixUnaryExpression(init) && init.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(init.operand)) {
2589
+ const v = -Number(init.operand.text);
2590
+ counter.value = v + 1;
2591
+ return `-${init.operand.text}`;
2592
+ }
2593
+ ctx.notes.push("enum member with a computed initializer is not projected");
2594
+ return void 0;
2595
+ }
2596
+ function namespaceMemberField(stmt, ctx) {
2597
+ if (!isExported(stmt)) return void 0;
2598
+ if (ts2.isVariableStatement(stmt)) {
2599
+ const fields = [];
2600
+ for (const d of stmt.declarationList.declarations) {
2601
+ const name = pickName(d);
2602
+ if (!name) continue;
2603
+ if (d.type) {
2604
+ const t = typeToDsl(d.type, child(ctx));
2605
+ if (!t) continue;
2606
+ fields.push(`${draftKey(name)}: ${t.dsl}`);
2607
+ continue;
2608
+ }
2609
+ const init = d.initializer;
2610
+ if (init && (ts2.isStringLiteral(init) || ts2.isNumericLiteral(init))) {
2611
+ const raw = ts2.isStringLiteral(init) ? JSON.stringify(init.text) : init.text;
2612
+ fields.push(`${draftKey(name)}: ${raw}`);
2613
+ continue;
2614
+ }
2615
+ ctx.notes.push(`namespace member '${name}' has no inferable value type`);
2616
+ }
2617
+ return fields.length > 0 ? fields.join(", ") : void 0;
2618
+ }
2619
+ if (ts2.isFunctionDeclaration(stmt)) {
2620
+ const name = pickName(stmt);
2621
+ if (!name) return void 0;
2622
+ const d = fnSignatureDsl(stmt.parameters, stmt.type, ctx);
2623
+ return `${draftKey(name)}: ${d.dsl}`;
2624
+ }
2625
+ if (ts2.isEnumDeclaration(stmt)) {
2626
+ const name = pickName(stmt);
2627
+ if (!name) return void 0;
2628
+ const inner = enumShape(stmt, ctx);
2629
+ return `${draftKey(name)}: ${inner}`;
2630
+ }
2631
+ if (ts2.isModuleDeclaration(stmt)) {
2632
+ const name = pickName(stmt);
2633
+ if (!name || !stmt.body || !ts2.isModuleBlock(stmt.body)) return void 0;
2634
+ const fields = [];
2635
+ for (const inner of stmt.body.statements) {
2636
+ const f = namespaceMemberField(inner, ctx);
2637
+ if (f) fields.push(f);
2638
+ }
2639
+ return `${draftKey(name)}: shape({ ${fields.join(", ")} })`;
2640
+ }
2641
+ return void 0;
2642
+ }
2643
+ function enumShape(decl, ctx) {
2644
+ const fields = [];
2645
+ const counter = { value: 0 };
2646
+ for (const m of decl.members) {
2647
+ const key = memberKey(m.name);
2648
+ if (key === void 0) {
2649
+ ctx.notes.push("enum member with a computed name is not projected");
2650
+ continue;
2651
+ }
2652
+ const value = enumMemberValue(m, counter, ctx);
2653
+ if (value === void 0) continue;
2654
+ fields.push(`${draftKey(key)}: ${value}`);
2655
+ }
2656
+ return fields.length === 0 ? "shape({})" : `shape({ ${fields.join(", ")} })`;
2657
+ }
2658
+ function mergeOverloads(decls, ctx) {
2659
+ const first = decls[0];
2660
+ if (decls.length === 1) {
2661
+ const d = fnSignatureDsl(first.parameters, first.type, ctx);
2662
+ return {
2663
+ dsl: d,
2664
+ sigs: [d.dsl],
2665
+ paramsMerged: true,
2666
+ arity: arityOf(first),
2667
+ arityDiffers: false
2668
+ };
2669
+ }
2670
+ const slotsPer = [];
2671
+ const rets = [];
2672
+ const sigs = [];
2673
+ for (const d of decls) {
2674
+ const slots = paramSlots(d.parameters, ctx);
2675
+ slotsPer.push(slots);
2676
+ const params = slotsToDsl(slots);
2677
+ const r = typeToDsl(d.type, child(ctx));
2678
+ const ret2 = r ?? { dsl: "any()", modeled: true };
2679
+ rets.push(ret2);
2680
+ sigs.push(`fn(${params}, ${constraintSlot(ret2)})`);
2681
+ }
2682
+ const arity = slotsPer[0].length;
2683
+ const arityDiffers = slotsPer.some((s) => s.length !== arity);
2684
+ const merged = arityDiffers ? void 0 : mergeParamSlots(slotsPer);
2685
+ const p = slotsToDsl(merged ?? slotsPer[0]);
2686
+ if (rets.some((r) => !r.modeled)) {
2687
+ ctx.notes.push("an overload return type is not modeled \u2192 any()");
2688
+ return {
2689
+ dsl: { dsl: `fn(${p}, any())`, modeled: false },
2690
+ sigs,
2691
+ paramsMerged: merged !== void 0,
2692
+ arity,
2693
+ arityDiffers
2694
+ };
2695
+ }
2696
+ const uniq = /* @__PURE__ */ new Map();
2697
+ for (const r of rets) if (!uniq.has(r.dsl)) uniq.set(r.dsl, r);
2698
+ const texts = [...uniq.values()];
2699
+ const ret = texts.length === 1 ? constraintSlot(texts[0]) : `union(${texts.map((r) => r.dsl).join(", ")})`;
2700
+ return {
2701
+ dsl: { dsl: `fn(${p}, ${ret})`, modeled: true },
2702
+ sigs,
2703
+ paramsMerged: merged !== void 0,
2704
+ arity,
2705
+ arityDiffers
2706
+ };
2707
+ }
2708
+ function arityOf(d) {
2709
+ let n = 0;
2710
+ for (const p of d.parameters) {
2711
+ if (p.name && ts2.isIdentifier(p.name) && p.name.text === "this") continue;
2712
+ n++;
2713
+ }
2714
+ return n;
2715
+ }
1120
2716
  function collectExports(sourceFile) {
2717
+ const symbols = collectSymbols(sourceFile);
2718
+ const ctx = {
2719
+ symbols,
2720
+ notes: [],
2721
+ depth: 0,
2722
+ resolving: /* @__PURE__ */ new Set(),
2723
+ lost: { n: 0 }
2724
+ };
1121
2725
  const out = [];
2726
+ const fnGroups = /* @__PURE__ */ new Map();
2727
+ const emittedFns = /* @__PURE__ */ new Set();
1122
2728
  const push = (e) => {
2729
+ const notes = takeNotes(ctx);
2730
+ if (notes.length > 0) {
2731
+ e.note = e.note ? `${e.note}; ${notes.join("; ")}` : notes.join("; ");
2732
+ if (e.dsl !== void 0) e.partial = true;
2733
+ }
1123
2734
  out.push(e);
1124
2735
  };
1125
- for (const stmt of sourceFile.statements) {
1126
- if ((ts2.isFunctionDeclaration(stmt) || ts2.isMethodSignature(stmt)) && stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword)) {
1127
- const name = pickName(stmt);
1128
- if (!name) continue;
1129
- const fn = stmt;
1130
- const dsl = fnSignatureDsl(fn.parameters, fn.type, 0);
1131
- push({ name, dsl, kind: "function" });
1132
- continue;
1133
- }
1134
- if (ts2.isFunctionDeclaration(stmt) && pickName(stmt)) {
1135
- const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1136
- if (!isExport) continue;
1137
- const name = pickName(stmt);
1138
- push({ name, dsl: fnSignatureDsl(stmt.parameters, stmt.type, 0), kind: "function" });
1139
- continue;
2736
+ const pushScoped = (e, ambient) => {
2737
+ if (ambient) {
2738
+ e.note = e.note ? `${e.note}; ${ambient}` : ambient;
2739
+ if (e.dsl !== void 0) e.partial = true;
1140
2740
  }
1141
- if (ts2.isVariableStatement(stmt)) {
1142
- const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1143
- if (!isExport) continue;
1144
- for (const d of stmt.declarationList.declarations) {
1145
- const name = pickName(d);
1146
- if (!name) continue;
1147
- const dsl = typeToDsl(d.type, 0);
1148
- if (dsl === void 0) {
1149
- push({ name, note: "unmodeled type", kind: "value" });
1150
- } else if (dsl.startsWith("fn(")) {
1151
- push({ name, dsl, kind: "function" });
1152
- } else {
1153
- push({ name, dsl, kind: "value" });
2741
+ push(e);
2742
+ };
2743
+ const fnDecl = (stmt, name, ambient) => {
2744
+ const group = fnGroups.get(name);
2745
+ if (group) group.decls.push(stmt);
2746
+ else fnGroups.set(name, { decls: [stmt], ambient });
2747
+ };
2748
+ const collectStatements2 = (stmts, prefix, ambient) => {
2749
+ const locals = collectLocals(stmts);
2750
+ for (const stmt of stmts) {
2751
+ if (ts2.isFunctionDeclaration(stmt)) {
2752
+ const exported = isExported(stmt);
2753
+ const isDefault = isDefaultExported(stmt);
2754
+ if (!exported && !isDefault) continue;
2755
+ const name = isDefault ? "default" : pickName(stmt);
2756
+ if (!name) {
2757
+ pushScoped(
2758
+ {
2759
+ name: `${prefix}default`,
2760
+ note: "anonymous default function is not projected",
2761
+ kind: "other"
2762
+ },
2763
+ ambient
2764
+ );
2765
+ continue;
1154
2766
  }
2767
+ fnDecl(stmt, `${prefix}${name}`, ambient);
2768
+ continue;
1155
2769
  }
1156
- continue;
1157
- }
1158
- if (ts2.isClassDeclaration(stmt)) {
1159
- const isExport = stmt.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword) ?? false;
1160
- if (!isExport) continue;
1161
- const name = pickName(stmt);
1162
- if (!name) {
1163
- push({ name: "default", note: "anonymous class", kind: "class" });
2770
+ if (ts2.isVariableStatement(stmt)) {
2771
+ if (!isExported(stmt)) continue;
2772
+ for (const d of stmt.declarationList.declarations) {
2773
+ const name = pickName(d);
2774
+ if (!name) continue;
2775
+ const t = typeToDsl(d.type, ctx);
2776
+ if (!t) {
2777
+ pushScoped(
2778
+ {
2779
+ name: `${prefix}${name}`,
2780
+ note: "no type annotation \u2014 the value is not projected",
2781
+ kind: "value"
2782
+ },
2783
+ ambient
2784
+ );
2785
+ continue;
2786
+ }
2787
+ pushScoped(
2788
+ {
2789
+ name: `${prefix}${name}`,
2790
+ dsl: constraintSlot(t),
2791
+ kind: t.dsl.startsWith("fn(") ? "function" : "value",
2792
+ ...t.modeled ? {} : { partial: true }
2793
+ },
2794
+ ambient
2795
+ );
2796
+ }
2797
+ continue;
2798
+ }
2799
+ if (ts2.isClassDeclaration(stmt)) {
2800
+ if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
2801
+ const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
2802
+ if (!name) {
2803
+ pushScoped(
2804
+ { name: `${prefix}default`, note: "anonymous class is not projected", kind: "class" },
2805
+ ambient
2806
+ );
2807
+ continue;
2808
+ }
2809
+ const ctor = stmt.members.find(ts2.isConstructorDeclaration);
2810
+ if (ctor) {
2811
+ const slots = paramSlots(ctor.parameters, ctx);
2812
+ pushScoped(
2813
+ {
2814
+ name: `${prefix}${name}`,
2815
+ dsl: `fn(${slotsToDsl(slots)}, any())`,
2816
+ kind: "class",
2817
+ note: "constructor signature projected as a plain fn \u2014 the contract DSL has no `new` semantics, and the instance side is not modeled",
2818
+ partial: true
2819
+ },
2820
+ ambient
2821
+ );
2822
+ }
2823
+ let anyMethod = false;
2824
+ for (const m of stmt.members) {
2825
+ if (ts2.isMethodDeclaration(m) || ts2.isMethodSignature(m)) {
2826
+ const mname = pickName(m);
2827
+ if (!mname) continue;
2828
+ anyMethod = true;
2829
+ const d = fnSignatureDsl(m.parameters, m.type, ctx);
2830
+ pushScoped(
2831
+ {
2832
+ name: `${prefix}${name}.${mname}`,
2833
+ dsl: d.dsl,
2834
+ kind: "function",
2835
+ ...d.modeled ? {} : { partial: true }
2836
+ },
2837
+ ambient
2838
+ );
2839
+ }
2840
+ }
2841
+ if (!anyMethod && !ctor) {
2842
+ pushScoped(
2843
+ {
2844
+ name: `${prefix}${name}`,
2845
+ note: "class without public methods projected",
2846
+ kind: "class"
2847
+ },
2848
+ ambient
2849
+ );
2850
+ }
1164
2851
  continue;
1165
2852
  }
1166
- let anyMethod = false;
1167
- for (const m of stmt.members) {
1168
- if (ts2.isMethodDeclaration(m) || ts2.isMethodSignature(m)) {
1169
- const mname = pickName(m);
1170
- if (!mname) continue;
1171
- anyMethod = true;
1172
- push({
1173
- name: `${name}.${mname}`,
1174
- dsl: fnSignatureDsl(m.parameters, m.type, 0),
1175
- kind: "function"
1176
- });
2853
+ if (ts2.isEnumDeclaration(stmt)) {
2854
+ if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
2855
+ const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
2856
+ if (!name) {
2857
+ pushScoped(
2858
+ { name: `${prefix}enum`, note: "anonymous enum is not projected", kind: "other" },
2859
+ ambient
2860
+ );
2861
+ continue;
1177
2862
  }
2863
+ const dsl = enumShape(stmt, ctx);
2864
+ pushScoped({ name: `${prefix}${name}`, dsl, kind: "value" }, ambient);
2865
+ continue;
1178
2866
  }
1179
- if (!anyMethod) {
1180
- push({ name, note: "class without public methods projected", kind: "class" });
2867
+ if (ts2.isModuleDeclaration(stmt)) {
2868
+ if (ts2.isStringLiteral(stmt.name)) {
2869
+ const modName = stmt.name.text;
2870
+ if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
2871
+ pushScoped(
2872
+ {
2873
+ name: `${prefix}${modName}`,
2874
+ note: "ambient module without a body is not projected",
2875
+ kind: "other"
2876
+ },
2877
+ ambient
2878
+ );
2879
+ continue;
2880
+ }
2881
+ collectStatements2(
2882
+ stmt.body.statements,
2883
+ `${prefix}${modName}.`,
2884
+ `ambient module "${modName}" \u2014 not an export of this file (review before accepting)`
2885
+ );
2886
+ continue;
2887
+ }
2888
+ if (ts2.isIdentifier(stmt.name) && stmt.name.text === "global") {
2889
+ if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
2890
+ pushScoped(
2891
+ { name: `${prefix}global`, note: "ambient global without a body", kind: "other" },
2892
+ ambient
2893
+ );
2894
+ continue;
2895
+ }
2896
+ collectStatements2(
2897
+ stmt.body.statements,
2898
+ `${prefix}global.`,
2899
+ `ambient global declaration \u2014 not an export of this file (review before accepting)`
2900
+ );
2901
+ continue;
2902
+ }
2903
+ if (!isExported(stmt) && !isDefaultExported(stmt)) continue;
2904
+ const name = isDefaultExported(stmt) ? "default" : pickName(stmt);
2905
+ if (!name) {
2906
+ pushScoped(
2907
+ { name: `${prefix}namespace`, note: "anonymous namespace is not projected", kind: "other" },
2908
+ ambient
2909
+ );
2910
+ continue;
2911
+ }
2912
+ if (!stmt.body || !ts2.isModuleBlock(stmt.body)) {
2913
+ pushScoped(
2914
+ {
2915
+ name: `${prefix}${name}`,
2916
+ note: "namespace with a qualified name is not projected",
2917
+ kind: "other"
2918
+ },
2919
+ ambient
2920
+ );
2921
+ continue;
2922
+ }
2923
+ const fields = [];
2924
+ for (const inner of stmt.body.statements) {
2925
+ const f = namespaceMemberField(inner, ctx);
2926
+ if (f) fields.push(f);
2927
+ }
2928
+ pushScoped(
2929
+ {
2930
+ name: `${prefix}${name}`,
2931
+ dsl: fields.length > 0 ? `shape({ ${fields.join(", ")} })` : "shape({})",
2932
+ kind: "value"
2933
+ },
2934
+ ambient
2935
+ );
2936
+ continue;
2937
+ }
2938
+ if (ts2.isExportDeclaration(stmt)) {
2939
+ const spec = stmt.moduleSpecifier?.getText(sourceFile);
2940
+ if (!stmt.exportClause) {
2941
+ pushScoped(
2942
+ {
2943
+ name: spec ? `export * from ${spec}` : "export *",
2944
+ note: "star re-export \u2014 the exported names are unknown, so nothing is projected",
2945
+ kind: "other"
2946
+ },
2947
+ ambient
2948
+ );
2949
+ continue;
2950
+ }
2951
+ if (ts2.isNamespaceExport(stmt.exportClause)) {
2952
+ pushScoped(
2953
+ {
2954
+ name: `${prefix}${stmt.exportClause.name.text}`,
2955
+ note: `namespace re-export${spec ? ` from ${spec}` : ""} is not projected`,
2956
+ kind: "other"
2957
+ },
2958
+ ambient
2959
+ );
2960
+ continue;
2961
+ }
2962
+ for (const el of stmt.exportClause.elements) {
2963
+ const exported = el.name.text;
2964
+ if (spec) {
2965
+ pushScoped(
2966
+ {
2967
+ name: `${prefix}${exported}`,
2968
+ note: `re-export from ${spec} is not projected`,
2969
+ kind: "other"
2970
+ },
2971
+ ambient
2972
+ );
2973
+ continue;
2974
+ }
2975
+ const localName = el.propertyName?.text ?? el.name.text;
2976
+ const local = locals.get(localName);
2977
+ if (!local) {
2978
+ pushScoped(
2979
+ {
2980
+ name: `${prefix}${exported}`,
2981
+ note: `local re-export of '${localName}' \u2014 the local declaration was not found`,
2982
+ kind: "other"
2983
+ },
2984
+ ambient
2985
+ );
2986
+ continue;
2987
+ }
2988
+ const projected = projectLocal(local, localName, exported, ctx);
2989
+ pushScoped({ ...projected, name: `${prefix}${projected.name}` }, ambient);
2990
+ }
2991
+ continue;
2992
+ }
2993
+ if (ts2.isExportAssignment(stmt)) {
2994
+ pushScoped(
2995
+ {
2996
+ name: `${prefix}default`,
2997
+ note: "export assignment \u2014 bind via sidecar default",
2998
+ kind: "other"
2999
+ },
3000
+ ambient
3001
+ );
3002
+ continue;
3003
+ }
3004
+ if (ts2.isImportEqualsDeclaration(stmt)) {
3005
+ if (!isExported(stmt)) continue;
3006
+ const name = pickName(stmt);
3007
+ pushScoped(
3008
+ {
3009
+ name: `${prefix}${name ?? "import="}`,
3010
+ note: "import-equals export \u2014 the aliased module value is not projected",
3011
+ kind: "other"
3012
+ },
3013
+ ambient
3014
+ );
3015
+ continue;
3016
+ }
3017
+ if (ts2.isInterfaceDeclaration(stmt) || ts2.isTypeAliasDeclaration(stmt)) {
3018
+ continue;
1181
3019
  }
1182
- continue;
1183
3020
  }
1184
- if (ts2.isExportAssignment(stmt)) {
1185
- push({ name: "default", note: "export assignment \u2014 bind via sidecar default", kind: "other" });
1186
- continue;
3021
+ };
3022
+ collectStatements2(sourceFile.statements, "", void 0);
3023
+ for (const [name, group] of fnGroups) {
3024
+ if (emittedFns.has(name)) continue;
3025
+ emittedFns.add(name);
3026
+ const decls = group.decls;
3027
+ const m = mergeOverloads(decls, ctx);
3028
+ if (decls.length > 1) {
3029
+ ctx.lost.n += decls.length - 1;
1187
3030
  }
1188
- if (ts2.isInterfaceDeclaration(stmt) || ts2.isTypeAliasDeclaration(stmt)) {
1189
- continue;
3031
+ const paramNote = m.paramsMerged ? "parameters merged position by position (union per position)" : m.arityDiffers ? `parameters from the first (arity ${m.arity} \u2014 calls with more arguments are not covered; the contract DSL has no overload set)` : "parameters from the first (the overload parameter types could not be merged)";
3032
+ pushScoped(
3033
+ {
3034
+ name,
3035
+ dsl: m.dsl.dsl,
3036
+ kind: "function",
3037
+ ...decls.length > 1 ? {
3038
+ note: `${decls.length} overloads merged \u2014 ${paramNote}, return = union of all; overloads: ${m.sigs.join(" | ")}`
3039
+ } : {},
3040
+ ...m.dsl.modeled ? {} : { partial: true }
3041
+ },
3042
+ group.ambient
3043
+ );
3044
+ }
3045
+ return { exports: out, lost: ctx.lost.n };
3046
+ }
3047
+ function projectLocal(stmt, localName, exported, ctx) {
3048
+ if (ts2.isFunctionDeclaration(stmt)) {
3049
+ const d = fnSignatureDsl(stmt.parameters, stmt.type, ctx);
3050
+ return {
3051
+ name: exported,
3052
+ dsl: d.dsl,
3053
+ kind: "function",
3054
+ ...d.modeled ? {} : { partial: true }
3055
+ };
3056
+ }
3057
+ if (ts2.isVariableStatement(stmt)) {
3058
+ for (const decl of stmt.declarationList.declarations) {
3059
+ if (pickName(decl) !== localName) continue;
3060
+ const t = typeToDsl(decl.type, ctx);
3061
+ if (!t) {
3062
+ return {
3063
+ name: exported,
3064
+ note: "no type annotation \u2014 the value is not projected",
3065
+ kind: "value"
3066
+ };
3067
+ }
3068
+ return {
3069
+ name: exported,
3070
+ dsl: constraintSlot(t),
3071
+ kind: t.dsl.startsWith("fn(") ? "function" : "value",
3072
+ ...t.modeled ? {} : { partial: true }
3073
+ };
1190
3074
  }
1191
3075
  }
1192
- return out;
3076
+ if (ts2.isEnumDeclaration(stmt)) {
3077
+ return { name: exported, dsl: enumShape(stmt, ctx), kind: "value" };
3078
+ }
3079
+ if (ts2.isClassDeclaration(stmt)) {
3080
+ return {
3081
+ name: exported,
3082
+ note: "class re-export is not projected",
3083
+ kind: "class"
3084
+ };
3085
+ }
3086
+ return {
3087
+ name: exported,
3088
+ note: `local re-export of '${localName}' is not projected`,
3089
+ kind: "other"
3090
+ };
3091
+ }
3092
+ function draftIdentity(name) {
3093
+ return name.includes(".") ? toJsBindingIdent(name.replace(/\./g, "_")) : name;
3094
+ }
3095
+ function draftBindingName(identity, taken) {
3096
+ if (isJsBindingIdent(identity) && !taken(identity)) return identity;
3097
+ const alias = toJsBindingIdent(identity);
3098
+ if (isJsBindingIdent(alias) && !taken(alias)) return alias;
3099
+ let n = 1;
3100
+ while (taken(`_nudo_${n}`)) n++;
3101
+ return `_nudo_${n}`;
1193
3102
  }
1194
- function draftExportName(name, used) {
1195
- return toJsBindingIdent(name.includes(".") ? name.replace(/\./g, "_") : name, used);
3103
+ function exportNameRepr(name) {
3104
+ return /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u.test(name) ? name : JSON.stringify(name);
3105
+ }
3106
+ function draftExportStmt(binding, identity, dsl) {
3107
+ if (binding === identity) return [`export const ${binding} = ${dsl};`];
3108
+ return [`const ${binding} = ${dsl};`, `export { ${binding} as ${exportNameRepr(identity)} };`];
1196
3109
  }
1197
3110
  function formatDtsContractDraft(label, files, exports) {
1198
3111
  const draftable = exports.filter((e) => e.dsl !== void 0);
@@ -1204,6 +3117,7 @@ function formatDtsContractDraft(label, files, exports) {
1204
3117
  }
1205
3118
  if (draftable.some((e) => /\bany\s*\(/.test(e.dsl))) used.add("any");
1206
3119
  const importList = BUILDERS.filter((b) => used.has(b)).join(", ");
3120
+ const importNames = new Set(used);
1207
3121
  const exportUsed = /* @__PURE__ */ new Set();
1208
3122
  const lines = [
1209
3123
  "// @nudo:draft",
@@ -1231,7 +3145,12 @@ function formatDtsContractDraft(label, files, exports) {
1231
3145
  }
1232
3146
  if (e.note) lines.push(`// ${e.name} \u2014 ${e.note}`);
1233
3147
  else lines.push(`// ${e.name} \u2014 from TypeScript`);
1234
- lines.push(`export const ${draftExportName(e.name, exportUsed)} = ${e.dsl};`);
3148
+ const identity = draftIdentity(e.name);
3149
+ const binding = draftBindingName(identity, (n) => importNames.has(n) || exportUsed.has(n));
3150
+ exportUsed.add(binding);
3151
+ for (const stmt of draftExportStmt(binding, identity, e.dsl)) {
3152
+ lines.push(stmt);
3153
+ }
1235
3154
  if (e.name.includes(".")) {
1236
3155
  lines.push(`// sidecar key may also be written as \`${e.name}\``);
1237
3156
  }
@@ -1239,14 +3158,20 @@ function formatDtsContractDraft(label, files, exports) {
1239
3158
  }
1240
3159
  return lines.join("\n");
1241
3160
  }
1242
- function readDtsFiles(paths, maxFiles = 16) {
1243
- const out = [];
3161
+ var MAX_WALK_DEPTH = 6;
3162
+ function readDtsFiles(paths) {
3163
+ const found = [];
3164
+ let droppedDirs = 0;
1244
3165
  const walk = (dir, depth) => {
1245
- if (depth > 6 || out.length >= maxFiles * 3) return;
3166
+ if (depth > MAX_WALK_DEPTH) {
3167
+ droppedDirs++;
3168
+ return;
3169
+ }
1246
3170
  let entries;
1247
3171
  try {
1248
3172
  entries = readdirSync(dir);
1249
3173
  } catch {
3174
+ droppedDirs++;
1250
3175
  return;
1251
3176
  }
1252
3177
  for (const name of entries) {
@@ -1256,71 +3181,183 @@ function readDtsFiles(paths, maxFiles = 16) {
1256
3181
  try {
1257
3182
  st = statSync2(p);
1258
3183
  } catch {
3184
+ droppedDirs++;
1259
3185
  continue;
1260
3186
  }
1261
3187
  if (st.isDirectory()) walk(p, depth + 1);
1262
- else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) out.push(p);
3188
+ else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) {
3189
+ found.push(p);
3190
+ }
3191
+ }
3192
+ };
3193
+ for (const p of paths) {
3194
+ const st = statSync2(p, { throwIfNoEntry: false });
3195
+ if (st === void 0) continue;
3196
+ if (st.isDirectory()) walk(p, 0);
3197
+ else if (/\.d\.ts$/.test(p) || /\.(m)?ts$/.test(p)) {
3198
+ if (!p.endsWith(".d.ts.map")) found.push(p);
3199
+ }
3200
+ }
3201
+ found.sort();
3202
+ return { files: found, droppedDirs };
3203
+ }
3204
+ function dtsToContractDraft(dtsFiles, label) {
3205
+ const files = dtsFiles.filter((f) => existsSync(f));
3206
+ const exports = [];
3207
+ let skippedFiles = 0;
3208
+ let droppedEntries = 0;
3209
+ let lostEntries = 0;
3210
+ for (const file of files) {
3211
+ let content;
3212
+ try {
3213
+ content = readFileSync2(file, "utf8");
3214
+ } catch {
3215
+ skippedFiles++;
3216
+ continue;
3217
+ }
3218
+ const sf = ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true);
3219
+ const collected = collectExports(sf);
3220
+ lostEntries += collected.lost;
3221
+ for (const e of collected.exports) {
3222
+ if (exports.some((x) => x.name === e.name && x.dsl)) {
3223
+ droppedEntries++;
3224
+ continue;
3225
+ }
3226
+ exports.push(e);
3227
+ }
3228
+ }
3229
+ const name = label ?? (files[0] ? basename(files[0]).replace(/\.d\.ts$/, "") : "dts");
3230
+ const draftSource = formatDtsContractDraft(name, files, exports);
3231
+ const unprojected = exports.filter((e) => e.dsl === void 0).length;
3232
+ const partial = exports.filter((e) => e.partial && e.dsl !== void 0).length;
3233
+ return {
3234
+ label: name,
3235
+ files,
3236
+ exports,
3237
+ draftSource,
3238
+ stats: {
3239
+ files: files.length - skippedFiles,
3240
+ exports: exports.filter((e) => e.dsl !== void 0).length,
3241
+ // skipped = 丢失的条数(整条没投影 + 投影了但有未建模部分 + 读失败 +
3242
+ // 同名去重丢掉的 + 声明被合并掉的)——汇总行报「0 skipped」的前提是
3243
+ // **真的**没丢东西
3244
+ skipped: unprojected + partial + skippedFiles + droppedEntries + lostEntries
3245
+ }
3246
+ };
3247
+ }
3248
+ function dtsPathToContractDraft(path, label) {
3249
+ const scan = readDtsFiles([path]);
3250
+ const draft = dtsToContractDraft(scan.files, label ?? basename(path).replace(/\.d\.ts$/, ""));
3251
+ if (scan.droppedDirs > 0) {
3252
+ draft.stats.skipped += scan.droppedDirs;
3253
+ draft.stats.droppedDirs = scan.droppedDirs;
3254
+ }
3255
+ return draft;
3256
+ }
3257
+
3258
+ // src/harvest-package.ts
3259
+ import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
3260
+ import { join as join2, resolve, dirname as dirname2, basename as basename2 } from "path";
3261
+ import ts3 from "typescript";
3262
+ import { formatShape as formatShape2 } from "@nudojs/core";
3263
+
3264
+ // src/lru-map.ts
3265
+ var BoundedLruMap = class {
3266
+ map = /* @__PURE__ */ new Map();
3267
+ max;
3268
+ constructor(max) {
3269
+ this.max = max;
3270
+ }
3271
+ get size() {
3272
+ return this.map.size;
3273
+ }
3274
+ /** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
3275
+ getMax() {
3276
+ return this.max;
3277
+ }
3278
+ setMax(max) {
3279
+ this.max = max;
3280
+ this.trim();
3281
+ }
3282
+ has(key) {
3283
+ return this.map.has(key);
3284
+ }
3285
+ get(key) {
3286
+ const hit = this.map.get(key);
3287
+ if (hit === void 0) return void 0;
3288
+ this.map.delete(key);
3289
+ this.map.set(key, hit);
3290
+ return hit;
3291
+ }
3292
+ /** 命中不改变 LRU 序(只读探测) */
3293
+ peek(key) {
3294
+ return this.map.get(key);
3295
+ }
3296
+ set(key, value) {
3297
+ if (this.max <= 0) return;
3298
+ while (this.map.size >= this.max && !this.map.has(key)) {
3299
+ const oldest = this.map.keys().next().value;
3300
+ if (oldest === void 0) break;
3301
+ this.map.delete(oldest);
1263
3302
  }
1264
- };
1265
- for (const p of paths) {
1266
- const st = statSync2(p, { throwIfNoEntry: false });
1267
- if (st === void 0) continue;
1268
- if (st.isDirectory()) walk(p, 0);
1269
- else if (/\.d\.ts$/.test(p) || /\.(m)?ts$/.test(p)) {
1270
- if (!p.endsWith(".d.ts.map")) out.push(p);
3303
+ this.map.delete(key);
3304
+ this.map.set(key, value);
3305
+ }
3306
+ delete(key) {
3307
+ return this.map.delete(key);
3308
+ }
3309
+ clear() {
3310
+ this.map.clear();
3311
+ }
3312
+ keys() {
3313
+ return this.map.keys();
3314
+ }
3315
+ values() {
3316
+ return this.map.values();
3317
+ }
3318
+ entries() {
3319
+ return this.map.entries();
3320
+ }
3321
+ /** 压到当前 max(调低上限时收内存) */
3322
+ trim() {
3323
+ while (this.map.size > this.max) {
3324
+ const oldest = this.map.keys().next().value;
3325
+ if (oldest === void 0) break;
3326
+ this.map.delete(oldest);
1271
3327
  }
1272
3328
  }
1273
- return out.slice(0, maxFiles);
1274
- }
1275
- function dtsToContractDraft(dtsFiles, label) {
1276
- const files = dtsFiles.filter((f) => existsSync(f));
1277
- const exports = [];
1278
- let skippedFiles = 0;
1279
- for (const file of files) {
1280
- let content;
3329
+ };
3330
+
3331
+ // src/harvest-package.ts
3332
+ function packageDeclaresTypes(root) {
3333
+ const pj = join2(root, "package.json");
3334
+ if (existsSync2(pj)) {
1281
3335
  try {
1282
- content = readFileSync2(file, "utf8");
3336
+ const raw = JSON.parse(readFileSync3(pj, "utf8"));
3337
+ const entry = raw.types ?? raw.typings;
3338
+ if (typeof entry === "string" && entry.length > 0 && existsSync2(resolve(root, entry))) {
3339
+ return true;
3340
+ }
1283
3341
  } catch {
1284
- skippedFiles++;
1285
- continue;
1286
- }
1287
- const sf = ts2.createSourceFile(file, content, ts2.ScriptTarget.Latest, true);
1288
- for (const e of collectExports(sf)) {
1289
- if (exports.some((x) => x.name === e.name && x.dsl)) continue;
1290
- exports.push(e);
1291
3342
  }
1292
3343
  }
1293
- const name = label ?? (files[0] ? basename(files[0]).replace(/\.d\.ts$/, "") : "dts");
1294
- const draftSource = formatDtsContractDraft(name, files, exports);
1295
- return {
1296
- label: name,
1297
- files,
1298
- exports,
1299
- draftSource,
1300
- stats: {
1301
- files: files.length - skippedFiles,
1302
- exports: exports.filter((e) => e.dsl !== void 0).length,
1303
- skipped: exports.filter((e) => e.dsl === void 0).length + skippedFiles
1304
- }
1305
- };
1306
- }
1307
- function dtsPathToContractDraft(path, label) {
1308
- const files = readDtsFiles([path]);
1309
- return dtsToContractDraft(files, label ?? basename(path).replace(/\.d\.ts$/, ""));
3344
+ return existsSync2(join2(root, "index.d.ts"));
1310
3345
  }
1311
-
1312
- // src/harvest-package.ts
1313
- import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync3 } from "fs";
1314
- import { join as join2, resolve, dirname as dirname2, basename as basename2 } from "path";
1315
- import { formatShape as formatShape2 } from "@nudojs/core";
1316
3346
  function resolvePackageRoot(pkg, fromDir = process.cwd()) {
1317
- let dir = resolve(fromDir);
1318
3347
  const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
3348
+ const start = resolve(fromDir);
3349
+ let dir = start;
3350
+ for (let i = 0; i < 8; i++) {
3351
+ const pkgPath = join2(dir, "node_modules", pkg);
3352
+ if (existsSync2(pkgPath) && packageDeclaresTypes(pkgPath)) return pkgPath;
3353
+ const parent = resolve(dir, "..");
3354
+ if (parent === dir) break;
3355
+ dir = parent;
3356
+ }
3357
+ dir = start;
1319
3358
  for (let i = 0; i < 8; i++) {
1320
3359
  const typesPath = join2(dir, "node_modules", "@types", bare);
1321
3360
  if (existsSync2(typesPath)) return typesPath;
1322
- const pkgPath = join2(dir, "node_modules", pkg);
1323
- if (existsSync2(pkgPath)) return pkgPath;
1324
3361
  const parent = resolve(dir, "..");
1325
3362
  if (parent === dir) break;
1326
3363
  dir = parent;
@@ -1435,9 +3472,170 @@ function harvestPackage(pkg, fromDir, maxFiles = 24) {
1435
3472
  const env = harvestDts(dtsFiles);
1436
3473
  return { pkg, root, dtsFiles, env };
1437
3474
  }
3475
+ function topLevelNames(stmt) {
3476
+ if (ts3.isFunctionDeclaration(stmt) || ts3.isClassDeclaration(stmt)) {
3477
+ return stmt.name ? [stmt.name.text] : [];
3478
+ }
3479
+ if (ts3.isInterfaceDeclaration(stmt) || ts3.isTypeAliasDeclaration(stmt)) {
3480
+ return [stmt.name.text];
3481
+ }
3482
+ if (ts3.isEnumDeclaration(stmt)) return [stmt.name.text];
3483
+ if (ts3.isModuleDeclaration(stmt)) {
3484
+ return ts3.isIdentifier(stmt.name) ? [stmt.name.text] : [];
3485
+ }
3486
+ if (ts3.isVariableStatement(stmt)) {
3487
+ const names = [];
3488
+ for (const decl of stmt.declarationList.declarations) {
3489
+ if (ts3.isIdentifier(decl.name)) names.push(decl.name.text);
3490
+ }
3491
+ return names;
3492
+ }
3493
+ return [];
3494
+ }
3495
+ function ambientNames(stmt) {
3496
+ const names = [];
3497
+ if (!stmt.body || !ts3.isModuleBlock(stmt.body)) return names;
3498
+ for (const inner of stmt.body.statements) names.push(...topLevelNames(inner));
3499
+ return names;
3500
+ }
3501
+ function modifierKinds(stmt) {
3502
+ if (!ts3.canHaveModifiers(stmt)) return [];
3503
+ return (ts3.getModifiers(stmt) ?? []).map((m) => m.kind);
3504
+ }
3505
+ function emptyScan() {
3506
+ return {
3507
+ exported: /* @__PURE__ */ new Set(),
3508
+ ambient: /* @__PURE__ */ new Set(),
3509
+ stars: [],
3510
+ isModule: false
3511
+ };
3512
+ }
3513
+ function scanDtsFile(file) {
3514
+ let content;
3515
+ try {
3516
+ content = readFileSync3(file, "utf8");
3517
+ } catch {
3518
+ return emptyScan();
3519
+ }
3520
+ let sf;
3521
+ try {
3522
+ sf = ts3.createSourceFile(file, content, ts3.ScriptTarget.Latest, true);
3523
+ } catch {
3524
+ return emptyScan();
3525
+ }
3526
+ const scan = {
3527
+ exported: /* @__PURE__ */ new Set(),
3528
+ ambient: /* @__PURE__ */ new Set(),
3529
+ stars: [],
3530
+ isModule: ts3.isExternalModule(sf)
3531
+ };
3532
+ for (const stmt of sf.statements) {
3533
+ if (ts3.isModuleDeclaration(stmt)) {
3534
+ if (ts3.isIdentifier(stmt.name) && stmt.name.text === "global") {
3535
+ for (const n of ambientNames(stmt)) scan.ambient.add(n);
3536
+ }
3537
+ continue;
3538
+ }
3539
+ if (ts3.isExportDeclaration(stmt)) {
3540
+ const clause = stmt.exportClause;
3541
+ if (!clause) {
3542
+ if (stmt.moduleSpecifier && ts3.isStringLiteral(stmt.moduleSpecifier)) {
3543
+ scan.stars.push(stmt.moduleSpecifier.text);
3544
+ }
3545
+ continue;
3546
+ }
3547
+ if (ts3.isNamespaceExport(clause)) {
3548
+ scan.exported.add(clause.name.text);
3549
+ continue;
3550
+ }
3551
+ for (const el of clause.elements) scan.exported.add(el.name.text);
3552
+ continue;
3553
+ }
3554
+ if (ts3.isExportAssignment(stmt)) {
3555
+ scan.exported.add("default");
3556
+ continue;
3557
+ }
3558
+ const names = topLevelNames(stmt);
3559
+ const mods = modifierKinds(stmt);
3560
+ if (mods.includes(ts3.SyntaxKind.DefaultKeyword)) {
3561
+ scan.exported.add("default");
3562
+ continue;
3563
+ }
3564
+ const isExported2 = mods.includes(ts3.SyntaxKind.ExportKeyword);
3565
+ for (const n of names) {
3566
+ if (isExported2) scan.exported.add(n);
3567
+ else if (!scan.isModule) scan.ambient.add(n);
3568
+ }
3569
+ }
3570
+ return scan;
3571
+ }
3572
+ var fileScanCache = new BoundedLruMap(64);
3573
+ function dtsFileScan(file) {
3574
+ const hit = fileScanCache.get(file);
3575
+ if (hit) return hit;
3576
+ const scan = scanDtsFile(file);
3577
+ fileScanCache.set(file, scan);
3578
+ return scan;
3579
+ }
3580
+ function resolveStarTarget(spec, fromFile) {
3581
+ if (!spec.startsWith(".")) return void 0;
3582
+ const raw = resolve(dirname2(fromFile), spec);
3583
+ const stem = raw.replace(/\.(js|mjs|cjs|ts)$/, "");
3584
+ for (const cand of [`${raw}.d.ts`, `${stem}.d.ts`, join2(raw, "index.d.ts"), join2(stem, "index.d.ts")]) {
3585
+ if (existsSync2(cand)) return cand;
3586
+ }
3587
+ return void 0;
3588
+ }
3589
+ function packageEntryFile(h) {
3590
+ const entry = entryDtsFromPackageJson(h.root) ?? join2(h.root, "index.d.ts");
3591
+ if (existsSync2(entry)) return entry;
3592
+ return h.dtsFiles[0];
3593
+ }
3594
+ var surfaceCache = /* @__PURE__ */ new WeakMap();
3595
+ function packageExportSurface(h) {
3596
+ const hit = surfaceCache.get(h);
3597
+ if (hit) return hit;
3598
+ const names = /* @__PURE__ */ new Set();
3599
+ let unresolved = false;
3600
+ const visited = /* @__PURE__ */ new Set();
3601
+ const visit = (file) => {
3602
+ if (visited.has(file)) return;
3603
+ visited.add(file);
3604
+ const scan = dtsFileScan(file);
3605
+ for (const n of scan.exported) names.add(n);
3606
+ for (const spec of scan.stars) {
3607
+ const target = resolveStarTarget(spec, file);
3608
+ if (target) visit(target);
3609
+ else unresolved = true;
3610
+ }
3611
+ };
3612
+ const entry = packageEntryFile(h);
3613
+ if (entry) visit(entry);
3614
+ const surface = { names, unresolved };
3615
+ surfaceCache.set(h, surface);
3616
+ return surface;
3617
+ }
3618
+ function packageAmbientNames(h) {
3619
+ const ambient = /* @__PURE__ */ new Set();
3620
+ for (const file of h.dtsFiles) {
3621
+ for (const n of dtsFileScan(file).ambient) ambient.add(n);
3622
+ }
3623
+ return ambient;
3624
+ }
3625
+ function resolveSubpathEntry(root, sub) {
3626
+ if (!sub) return void 0;
3627
+ const base = resolve(root, sub);
3628
+ for (const cand of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
3629
+ if (existsSync2(cand)) return cand;
3630
+ }
3631
+ return void 0;
3632
+ }
1438
3633
  function formatHarvestSummary(h) {
1439
3634
  const lines = [];
1440
- lines.push(`${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols`);
3635
+ const degraded = h.env.stats.degraded ?? 0;
3636
+ lines.push(
3637
+ `${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols` + (degraded > 0 ? `, ${degraded} degraded` : "")
3638
+ );
1441
3639
  const mods = Object.keys(h.env.modules);
1442
3640
  for (const m of mods.slice(0, 5)) {
1443
3641
  const exports = h.env.modules[m];
@@ -1513,7 +3711,10 @@ import {
1513
3711
  relationFn as relationFn2,
1514
3712
  getFnImpl as getFnImpl2
1515
3713
  } from "@nudojs/core";
1516
- var HARVEST_DISK_ABI = "nudo-harvest-disk-v8";
3714
+ function nullDict2() {
3715
+ return /* @__PURE__ */ Object.create(null);
3716
+ }
3717
+ var HARVEST_DISK_ABI = "nudo-harvest-disk-v11";
1517
3718
  function sha256Hex(data) {
1518
3719
  return createHash("sha256").update(data).digest("hex");
1519
3720
  }
@@ -1541,27 +3742,44 @@ function absToHarvestSig(a) {
1541
3742
  case "arr":
1542
3743
  return { k: "arr", element: absToHarvestSig(s.element) };
1543
3744
  case "tuple":
1544
- return { k: "tuple", elements: s.elements.map(absToHarvestSig) };
3745
+ return {
3746
+ k: "tuple",
3747
+ elements: s.elements.map(absToHarvestSig),
3748
+ ...s.rest ? { rest: absToHarvestSig(s.rest) } : {}
3749
+ };
1545
3750
  case "obj": {
1546
- const slots = {};
3751
+ const slots = nullDict2();
1547
3752
  for (const [key, slot] of Object.entries(s.slots)) {
1548
3753
  slots[key] = {
1549
3754
  v: absToHarvestSig(slot.value),
1550
3755
  ...slot.optional ? { opt: true } : {}
1551
3756
  };
1552
3757
  }
1553
- return { k: "obj", slots };
3758
+ return {
3759
+ k: "obj",
3760
+ slots,
3761
+ ...s.index ? { index: { key: absToHarvestSig(s.index.key), value: absToHarvestSig(s.index.value) } } : {},
3762
+ ...s.open ? { open: true } : {}
3763
+ };
1554
3764
  }
1555
3765
  case "fn": {
1556
3766
  const rel = getFnImpl2(a)?.relation;
1557
3767
  const pts = rel?.paramTypes ?? s.paramTypes ?? s.params.map(() => ({ shape: { k: "unknown" }, conf: "mock" }));
1558
3768
  const paramTypes = pts.map(absToHarvestSig);
1559
3769
  const retSrc = rel?.returnType ?? s.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
3770
+ const fnSlots = nullDict2();
3771
+ for (const [key, slot] of Object.entries(s.slots ?? {})) {
3772
+ fnSlots[key] = absToHarvestSig(slot.value);
3773
+ }
1560
3774
  return {
1561
3775
  k: "fn",
1562
3776
  params: s.params,
1563
3777
  paramTypes,
1564
- returns: absToHarvestSig(retSrc)
3778
+ returns: absToHarvestSig(retSrc),
3779
+ ...s.ctor !== void 0 ? { ctor: s.ctor } : {},
3780
+ ...Object.keys(fnSlots).length > 0 ? { slots: fnSlots } : {},
3781
+ ...rel?.inferFrom ? { inferFrom: rel.inferFrom } : {},
3782
+ ...rel?.condFallback ? { condFallback: absToHarvestSig(rel.condFallback) } : {}
1565
3783
  };
1566
3784
  }
1567
3785
  case "sum":
@@ -1583,15 +3801,13 @@ function harvestSigToAbs(sig) {
1583
3801
  return a;
1584
3802
  };
1585
3803
  switch (sig.k) {
1586
- case "prim":
1587
- return mark(
1588
- abs(
1589
- { k: "prim", type: sig.type },
1590
- void 0,
1591
- void 0,
1592
- "mock"
1593
- )
1594
- );
3804
+ case "prim": {
3805
+ const t = sig.type;
3806
+ if (t !== "number" && t !== "string" && t !== "boolean" && t !== "bigint" && t !== "symbol") {
3807
+ return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
3808
+ }
3809
+ return mark(abs({ k: "prim", type: t }, void 0, void 0, "mock"));
3810
+ }
1595
3811
  case "unknown":
1596
3812
  return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
1597
3813
  case "any":
@@ -1618,29 +3834,65 @@ function harvestSigToAbs(sig) {
1618
3834
  return mark(abs({ k: "arr", element: harvestSigToAbs(sig.element) }, void 0, void 0, "mock"));
1619
3835
  case "tuple":
1620
3836
  return mark(
1621
- abs({ k: "tuple", elements: sig.elements.map(harvestSigToAbs) }, void 0, void 0, "mock")
3837
+ abs(
3838
+ {
3839
+ k: "tuple",
3840
+ elements: sig.elements.map(harvestSigToAbs),
3841
+ ...sig.rest ? { rest: harvestSigToAbs(sig.rest) } : {}
3842
+ },
3843
+ void 0,
3844
+ void 0,
3845
+ "mock"
3846
+ )
1622
3847
  );
1623
3848
  case "obj": {
1624
- const slots = {};
3849
+ const slots = nullDict2();
1625
3850
  for (const [key, slot] of Object.entries(sig.slots)) {
1626
3851
  slots[key] = {
1627
3852
  value: harvestSigToAbs(slot.v),
1628
3853
  ...slot.opt ? { optional: true } : {}
1629
3854
  };
1630
3855
  }
1631
- return mark(abs({ k: "obj", slots }, void 0, void 0, "mock"));
3856
+ return mark(
3857
+ abs(
3858
+ {
3859
+ k: "obj",
3860
+ slots,
3861
+ ...sig.index ? { index: { key: harvestSigToAbs(sig.index.key), value: harvestSigToAbs(sig.index.value) } } : {},
3862
+ ...sig.open ? { open: true } : {}
3863
+ },
3864
+ void 0,
3865
+ void 0,
3866
+ "mock"
3867
+ )
3868
+ );
1632
3869
  }
1633
3870
  case "tvar":
1634
3871
  return mark(abs({ k: "any" }, { op: "var", id: sig.name }, void 0, "mock"));
1635
3872
  case "fn": {
1636
3873
  const paramTypes = sig.paramTypes.map(harvestSigToAbs);
1637
3874
  const returnType = harvestSigToAbs(sig.returns);
1638
- return mark(
1639
- relationFn2(paramTypes, returnType, {
1640
- params: sig.params,
1641
- conf: "mock"
1642
- })
1643
- );
3875
+ const fn = relationFn2(paramTypes, returnType, {
3876
+ params: sig.params,
3877
+ conf: "mock",
3878
+ // 族 27 Bug 821 残留:条件元数据必须一起往返,否则缓存命中后
3879
+ // `infer` 绑定与假支丢失 ⇒ 冷/热缓存结论不一致。
3880
+ ...sig.inferFrom ? { inferFrom: sig.inferFrom } : {},
3881
+ ...sig.condFallback ? { condFallback: harvestSigToAbs(sig.condFallback) } : {}
3882
+ });
3883
+ const fnShape = fn.shape;
3884
+ if (fnShape.k === "fn") {
3885
+ const fnSlots = nullDict2();
3886
+ for (const [key, v] of Object.entries(sig.slots ?? {})) {
3887
+ fnSlots[key] = { value: harvestSigToAbs(v) };
3888
+ }
3889
+ fn.shape = {
3890
+ ...fnShape,
3891
+ ...sig.ctor !== void 0 ? { ctor: sig.ctor } : {},
3892
+ ...Object.keys(fnSlots).length > 0 ? { slots: fnSlots } : {}
3893
+ };
3894
+ }
3895
+ return mark(fn);
1644
3896
  }
1645
3897
  case "sum":
1646
3898
  return mark(
@@ -1674,13 +3926,13 @@ function harvestSigToAbs(sig) {
1674
3926
  }
1675
3927
  }
1676
3928
  function serializeHarvestJson(pkg, env, meta) {
1677
- const modules = {};
3929
+ const modules = nullDict2();
1678
3930
  for (const [mod, rec] of Object.entries(env.modules)) {
1679
- const named = {};
3931
+ const named = nullDict2();
1680
3932
  for (const [k, v] of Object.entries(rec)) named[k] = absToHarvestSig(v);
1681
3933
  modules[mod] = named;
1682
3934
  }
1683
- const globals = {};
3935
+ const globals = nullDict2();
1684
3936
  for (const [k, v] of Object.entries(env.globals)) globals[k] = absToHarvestSig(v);
1685
3937
  return {
1686
3938
  v: 1,
@@ -1700,13 +3952,13 @@ function materializeHarvestJson(j) {
1700
3952
  if (h.knobs?.abi !== HARVEST_DISK_ABI) return null;
1701
3953
  if (!h.modules || typeof h.modules !== "object") return null;
1702
3954
  try {
1703
- const modules = {};
3955
+ const modules = nullDict2();
1704
3956
  for (const [mod, rec] of Object.entries(h.modules)) {
1705
- const named = {};
3957
+ const named = nullDict2();
1706
3958
  for (const [k, sig] of Object.entries(rec)) named[k] = harvestSigToAbs(sig);
1707
3959
  modules[mod] = named;
1708
3960
  }
1709
- const globals = {};
3961
+ const globals = nullDict2();
1710
3962
  for (const [k, sig] of Object.entries(h.globals ?? {})) {
1711
3963
  globals[k] = harvestSigToAbs(sig);
1712
3964
  }
@@ -1850,73 +4102,6 @@ function loadHarvestEnvFromDisk(pkg, meta) {
1850
4102
  return hit ? materializeHarvestJson(hit) : null;
1851
4103
  }
1852
4104
 
1853
- // src/lru-map.ts
1854
- var BoundedLruMap = class {
1855
- map = /* @__PURE__ */ new Map();
1856
- max;
1857
- constructor(max) {
1858
- this.max = max;
1859
- }
1860
- get size() {
1861
- return this.map.size;
1862
- }
1863
- /** 当前上限(可动态收紧;收紧时立刻按 LRU 压到新上限) */
1864
- getMax() {
1865
- return this.max;
1866
- }
1867
- setMax(max) {
1868
- this.max = max;
1869
- this.trim();
1870
- }
1871
- has(key) {
1872
- return this.map.has(key);
1873
- }
1874
- get(key) {
1875
- const hit = this.map.get(key);
1876
- if (hit === void 0) return void 0;
1877
- this.map.delete(key);
1878
- this.map.set(key, hit);
1879
- return hit;
1880
- }
1881
- /** 命中不改变 LRU 序(只读探测) */
1882
- peek(key) {
1883
- return this.map.get(key);
1884
- }
1885
- set(key, value) {
1886
- if (this.max <= 0) return;
1887
- while (this.map.size >= this.max && !this.map.has(key)) {
1888
- const oldest = this.map.keys().next().value;
1889
- if (oldest === void 0) break;
1890
- this.map.delete(oldest);
1891
- }
1892
- this.map.delete(key);
1893
- this.map.set(key, value);
1894
- }
1895
- delete(key) {
1896
- return this.map.delete(key);
1897
- }
1898
- clear() {
1899
- this.map.clear();
1900
- }
1901
- keys() {
1902
- return this.map.keys();
1903
- }
1904
- values() {
1905
- return this.map.values();
1906
- }
1907
- entries() {
1908
- return this.map.entries();
1909
- }
1910
- /** 压到当前 max(调低上限时收内存) */
1911
- trim() {
1912
- while (this.map.size > this.max) {
1913
- const oldest = this.map.keys().next().value;
1914
- if (oldest === void 0) break;
1915
- this.map.delete(oldest);
1916
- }
1917
- }
1918
- };
1919
-
1920
4105
  // src/harvest-auto.ts
1921
4106
  var NODE_BUILTINS = /* @__PURE__ */ new Set([
1922
4107
  "assert",
@@ -2048,17 +4233,22 @@ function harvestedValueToAbs(a) {
2048
4233
  body: [],
2049
4234
  directives: []
2050
4235
  };
4236
+ const ctor = a.shape.ctor;
2051
4237
  return markMockConf(
2052
- absFunction(params, {
2053
- body: dummyBody,
2054
- apply: (args) => {
2055
- if (relation) {
2056
- return markMockConf(instantiateReturn(a, args));
2057
- }
2058
- return markMockConf({ ...ret });
4238
+ absFunction(
4239
+ params,
4240
+ {
4241
+ body: dummyBody,
4242
+ apply: (args) => {
4243
+ if (relation) {
4244
+ return markMockConf(instantiateReturn(a, args));
4245
+ }
4246
+ return markMockConf({ ...ret });
4247
+ },
4248
+ ...relation ? { relation } : {}
2059
4249
  },
2060
- ...relation ? { relation } : {}
2061
- })
4250
+ ctor !== void 0 ? { ctor } : void 0
4251
+ )
2062
4252
  );
2063
4253
  }
2064
4254
  return markMockConf({ ...a });
@@ -2078,7 +4268,7 @@ function packageHarvestToAbsModules(pkg, h) {
2078
4268
  return named;
2079
4269
  };
2080
4270
  const register = (key, exports) => {
2081
- if (!out[key]) out[key] = exports;
4271
+ if (!Object.hasOwn(out, key)) out[key] = exports;
2082
4272
  };
2083
4273
  for (const [mod, rec] of Object.entries(h.env.modules)) {
2084
4274
  const named = convertRecord(rec);
@@ -2086,22 +4276,63 @@ function packageHarvestToAbsModules(pkg, h) {
2086
4276
  const def = rec["default"];
2087
4277
  if (def) exports.default = harvestedValueToAbs(def);
2088
4278
  register(mod, exports);
2089
- register(pkg, exports);
2090
4279
  const stripped = mod.replace(/^@types\//, "").replace(/\\/g, "/");
2091
- if (stripped) register(stripped, exports);
4280
+ if (stripped && stripped !== pkg) register(stripped, exports);
2092
4281
  const base = stripped.split("/").filter(Boolean).pop();
2093
4282
  if (base && base !== pkg) register(base, exports);
2094
4283
  }
2095
- const globalNamed = convertRecord(h.env.globals);
2096
- if (Object.keys(globalNamed).length > 0) {
2097
- const existing = out[pkg];
2098
- if (existing) {
2099
- out[pkg] = { named: { ...globalNamed, ...existing.named }, default: existing.default };
2100
- } else {
2101
- out[pkg] = { named: globalNamed };
4284
+ out[pkg] = packageRootExports(pkg, h);
4285
+ return out;
4286
+ }
4287
+ function packageRootExports(pkg, h) {
4288
+ const globals = h.env.globals;
4289
+ const surface = packageExportSurface(h);
4290
+ const named = {};
4291
+ if (surface.unresolved) {
4292
+ const ambient = packageAmbientNames(h);
4293
+ for (const [k, v] of Object.entries(globals)) {
4294
+ if (ambient.has(k)) continue;
4295
+ named[k] = harvestedValueToAbs(v);
4296
+ }
4297
+ } else {
4298
+ for (const n of surface.names) {
4299
+ if (!Object.hasOwn(globals, n)) continue;
4300
+ named[n] = harvestedValueToAbs(globals[n]);
2102
4301
  }
2103
4302
  }
2104
- return out;
4303
+ const exports = { named };
4304
+ if (surface.unresolved || surface.names.has("default")) {
4305
+ const def = Object.hasOwn(globals, "default") ? globals["default"] : void 0;
4306
+ if (def) exports.default = harvestedValueToAbs(def);
4307
+ }
4308
+ const aug = Object.hasOwn(h.env.modules, pkg) ? h.env.modules[pkg] : void 0;
4309
+ if (aug) {
4310
+ for (const [k, v] of Object.entries(aug)) {
4311
+ if (Object.hasOwn(named, k)) continue;
4312
+ named[k] = harvestedValueToAbs(v);
4313
+ }
4314
+ }
4315
+ return exports;
4316
+ }
4317
+ var subpathCache = new BoundedLruMap(32);
4318
+ function subpathExports(pkg, spec, h) {
4319
+ const sub = spec.slice(pkg.length + 1);
4320
+ const file = resolveSubpathEntry(h.root, sub);
4321
+ if (!file) return void 0;
4322
+ const hit = subpathCache.get(file);
4323
+ if (hit) return hit;
4324
+ const env = harvestDts([file]);
4325
+ const scan = dtsFileScan(file);
4326
+ const named = {};
4327
+ for (const n of scan.exported) {
4328
+ if (!Object.hasOwn(env.globals, n)) continue;
4329
+ named[n] = harvestedValueToAbs(env.globals[n]);
4330
+ }
4331
+ const exports = { named };
4332
+ const def = Object.hasOwn(env.globals, "default") ? env.globals["default"] : void 0;
4333
+ if (def) exports.default = harvestedValueToAbs(def);
4334
+ subpathCache.set(file, exports);
4335
+ return exports;
2105
4336
  }
2106
4337
  function bareSpecToAbsModules(spec, fromFile) {
2107
4338
  if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) {
@@ -2110,8 +4341,12 @@ function bareSpecToAbsModules(spec, fromFile) {
2110
4341
  const parts = spec.split("/");
2111
4342
  const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
2112
4343
  if (!pkg) return void 0;
2113
- const table = harvestToAbsModules(pkg, dirname4(fromFile));
2114
- return table[spec] ?? table[pkg];
4344
+ const h = harvestPackageCached(pkg, dirname4(fromFile));
4345
+ if (!h) return void 0;
4346
+ const table = packageHarvestToAbsModules(pkg, h);
4347
+ if (spec === pkg) return table[pkg];
4348
+ if (Object.hasOwn(table, spec)) return table[spec];
4349
+ return subpathExports(pkg, spec, h);
2115
4350
  }
2116
4351
 
2117
4352
  // src/harvest-node.ts
@@ -2242,7 +4477,8 @@ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, ma
2242
4477
  const stats = {
2243
4478
  files: env.stats.files,
2244
4479
  symbols: env.stats.symbols,
2245
- skipped: env.stats.skipped
4480
+ skipped: env.stats.skipped,
4481
+ ...env.stats.degraded !== void 0 ? { degraded: env.stats.degraded } : {}
2246
4482
  };
2247
4483
  const result = {
2248
4484
  ok: true,
@@ -2261,7 +4497,8 @@ function harvestNodeTypes(fromDir, maxFiles = HARVEST_NODE_DEFAULT_MAX_FILES, ma
2261
4497
  const stats = {
2262
4498
  files: env.stats.files,
2263
4499
  symbols: env.stats.symbols,
2264
- skipped: env.stats.skipped
4500
+ skipped: env.stats.skipped,
4501
+ ...env.stats.degraded !== void 0 ? { degraded: env.stats.degraded } : {}
2265
4502
  };
2266
4503
  if (diskRoot) {
2267
4504
  writeHarvestDisk(