@nudojs/harvester 0.2.8-beta.0 → 1.0.0-beta.1

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