@nudojs/service 3.0.0 → 5.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,888 @@
1
+ import {
2
+ BoundedLruMap
3
+ } from "./chunk-PVJIMQRI.js";
4
+
5
+ // src/static-imports.ts
6
+ import { parse } from "@nudojs/parser";
7
+ import { readFileSync, existsSync } from "fs";
8
+ import { resolve, dirname, join } from "path";
9
+ import { generalizeFromAst, foldStaticStringExpr } from "@nudojs/core";
10
+ function resolveRelative(fromFile, spec) {
11
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
12
+ const base = resolve(dirname(fromFile), spec);
13
+ for (const cand of [
14
+ base,
15
+ base + ".js",
16
+ base + ".cjs",
17
+ base + ".mjs",
18
+ base + ".ts",
19
+ base + ".mts",
20
+ base + ".cts",
21
+ join(base, "index.js"),
22
+ join(base, "index.cjs"),
23
+ join(base, "index.mjs"),
24
+ join(base, "index.ts")
25
+ ]) {
26
+ if (existsSync(cand)) return cand;
27
+ }
28
+ return void 0;
29
+ }
30
+ function collectDependencySpecs(ast) {
31
+ const specs = [];
32
+ for (const stmt of ast.program.body) {
33
+ if (stmt.type === "ImportDeclaration") {
34
+ specs.push(stmt.source.value);
35
+ }
36
+ if (stmt.type === "ExportNamedDeclaration" && stmt.source) {
37
+ specs.push(stmt.source.value);
38
+ }
39
+ if (stmt.type === "ExportAllDeclaration" && stmt.source) {
40
+ specs.push(stmt.source.value);
41
+ }
42
+ collectRequires(stmt, specs);
43
+ }
44
+ return specs;
45
+ }
46
+ function collectRequires(node, out) {
47
+ const visit = (n) => {
48
+ if (!n || typeof n !== "object") return;
49
+ const obj = n;
50
+ if (obj.type === "CallExpression" && obj.callee) {
51
+ const c = obj.callee;
52
+ let spec;
53
+ if (c.type === "Identifier" && c.name === "require") {
54
+ spec = foldStaticStringExpr(obj.arguments?.[0]);
55
+ } else if (c.type === "MemberExpression" && !c.computed && c.object?.type === "Identifier" && c.object.name === "require" && c.property?.type === "Identifier" && c.property.name === "resolve") {
56
+ spec = foldStaticStringExpr(obj.arguments?.[0]);
57
+ }
58
+ if (spec !== void 0) out.push(spec);
59
+ }
60
+ for (const key of Object.keys(obj)) {
61
+ if (key === "loc" || key === "start" || key === "end") continue;
62
+ const v = obj[key];
63
+ if (Array.isArray(v)) v.forEach(visit);
64
+ else if (v && typeof v === "object") visit(v);
65
+ }
66
+ };
67
+ visit(node);
68
+ }
69
+ function analyzeExportsFromSource(filePath, source) {
70
+ const file = parse(source);
71
+ const named = /* @__PURE__ */ new Map();
72
+ let defaultExport;
73
+ const poly = /* @__PURE__ */ new Map();
74
+ for (const stmt of file.program.body) {
75
+ let decl = stmt;
76
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
77
+ decl = stmt.declaration;
78
+ }
79
+ if (decl.type === "FunctionDeclaration" && decl.id) {
80
+ named.set(decl.id.name, decl.id.name);
81
+ }
82
+ if (decl.type === "VariableDeclaration") {
83
+ for (const d of decl.declarations) {
84
+ if (d.id.type === "Identifier") named.set(d.id.name, d.id.name);
85
+ }
86
+ }
87
+ if (decl.type === "ClassDeclaration" && decl.id) {
88
+ named.set(decl.id.name, decl.id.name);
89
+ }
90
+ if (stmt.type === "ExportDefaultDeclaration") {
91
+ const d = stmt.declaration;
92
+ if (d.type === "FunctionDeclaration" && d.id) defaultExport = d.id.name;
93
+ if (d.type === "Identifier") defaultExport = d.name;
94
+ }
95
+ collectCjsExports(stmt, named, (n) => {
96
+ defaultExport = n;
97
+ });
98
+ }
99
+ for (const name of named.values()) {
100
+ const g = generalizeFromAst(name, source);
101
+ if (g) poly.set(name, g);
102
+ }
103
+ if (defaultExport) {
104
+ const g = generalizeFromAst(defaultExport, source);
105
+ if (g) poly.set("default", g);
106
+ }
107
+ return { path: filePath, named, defaultExport, source, poly };
108
+ }
109
+ function collectCjsExports(stmt, named, setDefault) {
110
+ const visit = (n) => {
111
+ if (!n || typeof n !== "object") return;
112
+ const obj = n;
113
+ if (obj.type !== "AssignmentExpression") {
114
+ for (const key of Object.keys(obj)) {
115
+ if (key === "loc" || key === "start" || key === "end") continue;
116
+ const v = obj[key];
117
+ if (Array.isArray(v)) v.forEach(visit);
118
+ else if (v && typeof v === "object") visit(v);
119
+ }
120
+ return;
121
+ }
122
+ const left = obj.left;
123
+ const right = obj.right;
124
+ if (left?.type === "MemberExpression" && left.object?.type === "Identifier" && left.object.name === "exports" && left.property?.type === "Identifier" && left.property.name) {
125
+ named.set(left.property.name, left.property.name);
126
+ }
127
+ if (left?.type === "MemberExpression" && left.object?.type === "Identifier" && left.object.name === "module" && left.property?.type === "Identifier" && left.property.name === "exports") {
128
+ if (right?.type === "Identifier" && typeof right.name === "string") {
129
+ named.set(right.name, right.name);
130
+ setDefault(right.name);
131
+ }
132
+ if (right?.type === "ObjectExpression") {
133
+ const props = right.properties;
134
+ for (const p of props) {
135
+ if (p.type !== "ObjectProperty") continue;
136
+ const key = p.key;
137
+ const val = p.value;
138
+ const exportName = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? key.value : void 0;
139
+ if (exportName) named.set(exportName, exportName);
140
+ if (val?.type === "Identifier" && val.name && exportName) {
141
+ }
142
+ }
143
+ }
144
+ }
145
+ };
146
+ visit(stmt);
147
+ }
148
+ function collectStaticImports(entryFile, maxDepth = 8) {
149
+ const graph = /* @__PURE__ */ new Map();
150
+ const queue = [
151
+ { file: resolve(entryFile), depth: 0 }
152
+ ];
153
+ const seen = /* @__PURE__ */ new Set();
154
+ while (queue.length > 0) {
155
+ const { file, depth } = queue.shift();
156
+ if (seen.has(file) || depth > maxDepth) continue;
157
+ seen.add(file);
158
+ if (!existsSync(file)) continue;
159
+ const source = readFileSync(file, "utf8");
160
+ const mod = analyzeExportsFromSource(file, source);
161
+ graph.set(file, mod);
162
+ const ast = parse(source);
163
+ for (const spec of collectDependencySpecs(ast)) {
164
+ const resolved = resolveRelative(file, spec);
165
+ if (resolved) queue.push({ file: resolved, depth: depth + 1 });
166
+ }
167
+ }
168
+ return graph;
169
+ }
170
+
171
+ // src/harvest-package.ts
172
+ import { existsSync as existsSync2, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
173
+ import { join as join2, resolve as resolve2, dirname as dirname2, basename } from "path";
174
+ import { harvestDts } from "@nudojs/harvester";
175
+ import { formatShape } from "@nudojs/core";
176
+ function resolvePackageRoot(pkg, fromDir = process.cwd()) {
177
+ let dir = resolve2(fromDir);
178
+ const bare = pkg.replace(/^@/, "").replace(/\//g, "__");
179
+ for (let i = 0; i < 8; i++) {
180
+ const typesPath = join2(dir, "node_modules", "@types", bare);
181
+ if (existsSync2(typesPath)) return typesPath;
182
+ const pkgPath = join2(dir, "node_modules", pkg);
183
+ if (existsSync2(pkgPath)) return pkgPath;
184
+ const parent = resolve2(dir, "..");
185
+ if (parent === dir) break;
186
+ dir = parent;
187
+ }
188
+ return void 0;
189
+ }
190
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "test", "tests", "docs", "doc", "examples", "__tests__"]);
191
+ var MAX_DTS_BYTES = 15e5;
192
+ function dtsPriority(p, root) {
193
+ const base = basename(p);
194
+ const rel = p.slice(root.length);
195
+ if (base === "index.d.ts") return 0;
196
+ if (base === "index.d.mts") return 1;
197
+ if (rel.includes("/types/") && base === "index.d.ts") return 2;
198
+ if (base.endsWith(".d.ts") && !base.includes(".")) return 3;
199
+ return 10;
200
+ }
201
+ function collectDtsFiles(root, maxFiles = 8) {
202
+ const out = [];
203
+ const walk = (dir) => {
204
+ if (out.length >= maxFiles * 3) return;
205
+ let entries;
206
+ try {
207
+ entries = readdirSync(dir);
208
+ } catch {
209
+ return;
210
+ }
211
+ for (const name of entries) {
212
+ if (out.length >= maxFiles * 3) return;
213
+ if (SKIP_DIRS.has(name)) continue;
214
+ const p = join2(dir, name);
215
+ let st;
216
+ try {
217
+ st = statSync(p);
218
+ } catch {
219
+ continue;
220
+ }
221
+ if (st.isDirectory()) walk(p);
222
+ else if (name.endsWith(".d.ts") && !name.endsWith(".d.ts.map")) {
223
+ if (st.size > MAX_DTS_BYTES) continue;
224
+ out.push(p);
225
+ }
226
+ }
227
+ };
228
+ walk(root);
229
+ out.sort((a, b) => dtsPriority(a, root) - dtsPriority(b, root) || a.length - b.length);
230
+ return out.slice(0, maxFiles);
231
+ }
232
+ function entryDtsFromPackageJson(root) {
233
+ const pj = join2(root, "package.json");
234
+ if (!existsSync2(pj)) return void 0;
235
+ try {
236
+ const raw = JSON.parse(readFileSync2(pj, "utf8"));
237
+ const entry = raw.types ?? raw.typings;
238
+ if (typeof entry === "string" && entry.endsWith(".d.ts")) {
239
+ const p = resolve2(root, entry);
240
+ if (existsSync2(p)) {
241
+ const st = statSync(p);
242
+ if (st.size <= MAX_DTS_BYTES) return p;
243
+ }
244
+ }
245
+ } catch {
246
+ }
247
+ return void 0;
248
+ }
249
+ function collectDtsFromEntry(entry, maxFiles = 200) {
250
+ const files = [];
251
+ const seen = /* @__PURE__ */ new Set();
252
+ const queue = [entry];
253
+ const REFERENCE_PATH_REGEX = /<reference\s+path=["']([^"']+)["']\s*\/>/g;
254
+ const RELATIVE_FROM_REGEX = /\bfrom\s+["'](\.[^"']+)["']/g;
255
+ while (queue.length > 0 && files.length < maxFiles) {
256
+ const current = queue.shift();
257
+ if (seen.has(current) || !existsSync2(current)) continue;
258
+ seen.add(current);
259
+ if (!current.endsWith(".d.ts")) continue;
260
+ files.push(current);
261
+ let text;
262
+ try {
263
+ text = readFileSync2(current, "utf-8");
264
+ } catch {
265
+ continue;
266
+ }
267
+ const dir = dirname2(current);
268
+ for (const match of text.matchAll(REFERENCE_PATH_REGEX)) {
269
+ queue.push(resolve2(dir, match[1]));
270
+ }
271
+ for (const match of text.matchAll(RELATIVE_FROM_REGEX)) {
272
+ const base = resolve2(dir, match[1]);
273
+ for (const candidate of [`${base}.d.ts`, join2(base, "index.d.ts")]) {
274
+ if (existsSync2(candidate)) {
275
+ queue.push(candidate);
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ }
281
+ return files;
282
+ }
283
+ function harvestPackage(pkg, fromDir, maxFiles = 24) {
284
+ const root = resolvePackageRoot(pkg, fromDir);
285
+ if (!root) return { error: `package not found: ${pkg}` };
286
+ const entry = entryDtsFromPackageJson(root) ?? join2(root, "index.d.ts");
287
+ let dtsFiles = existsSync2(entry) ? collectDtsFromEntry(entry, maxFiles) : [];
288
+ if (dtsFiles.length === 0) {
289
+ const collected = collectDtsFiles(root, maxFiles);
290
+ dtsFiles = entry && existsSync2(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
291
+ }
292
+ if (dtsFiles.length === 0) {
293
+ return { error: `no .d.ts under ${root}` };
294
+ }
295
+ const env = harvestDts(dtsFiles);
296
+ return { pkg, root, dtsFiles, env };
297
+ }
298
+ function formatHarvestSummary(h) {
299
+ const lines = [];
300
+ lines.push(`${h.pkg}: ${h.dtsFiles.length} d.ts, ${h.env.stats.symbols} symbols`);
301
+ const mods = Object.keys(h.env.modules);
302
+ for (const m of mods.slice(0, 5)) {
303
+ const exports = h.env.modules[m];
304
+ const names = Object.keys(exports).slice(0, 12);
305
+ lines.push(` module "${m}": ${names.join(", ")}${Object.keys(exports).length > 12 ? "\u2026" : ""}`);
306
+ }
307
+ if (mods.length > 5) lines.push(` \u2026 ${mods.length - 5} more modules`);
308
+ return lines.join("\n");
309
+ }
310
+ function lookupHarvested(h, moduleName, exportName) {
311
+ const mod = h.env.modules[moduleName];
312
+ const a = mod?.[exportName] ?? h.env.globals[exportName];
313
+ if (!a) return void 0;
314
+ return formatShape(a);
315
+ }
316
+
317
+ // src/harvest-json.ts
318
+ import { createHash } from "crypto";
319
+ import {
320
+ abs,
321
+ confJoin,
322
+ relationFn,
323
+ getFnImpl
324
+ } from "@nudojs/core";
325
+ var HARVEST_DISK_ABI = "nudo-harvest-disk-v7";
326
+ function sha256Hex(data) {
327
+ return createHash("sha256").update(data).digest("hex");
328
+ }
329
+ function absToHarvestSig(a) {
330
+ if (a.term?.op === "var") {
331
+ return { k: "tvar", name: a.term.id };
332
+ }
333
+ const s = a.shape;
334
+ switch (s.k) {
335
+ case "prim":
336
+ return { k: "prim", type: s.type };
337
+ case "unknown":
338
+ return { k: "unknown" };
339
+ case "any":
340
+ return { k: "any" };
341
+ case "never":
342
+ return { k: "never" };
343
+ case "arr":
344
+ return { k: "arr", element: absToHarvestSig(s.element) };
345
+ case "tuple":
346
+ return { k: "tuple", elements: s.elements.map(absToHarvestSig) };
347
+ case "obj": {
348
+ const slots = {};
349
+ for (const [key, slot] of Object.entries(s.slots)) {
350
+ slots[key] = {
351
+ v: absToHarvestSig(slot.value),
352
+ ...slot.optional ? { opt: true } : {}
353
+ };
354
+ }
355
+ return { k: "obj", slots };
356
+ }
357
+ case "fn": {
358
+ const rel = getFnImpl(a)?.relation;
359
+ const pts = rel?.paramTypes ?? s.paramTypes ?? s.params.map(() => ({ shape: { k: "unknown" }, conf: "mock" }));
360
+ const paramTypes = pts.map(absToHarvestSig);
361
+ const retSrc = rel?.returnType ?? s.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
362
+ return {
363
+ k: "fn",
364
+ params: s.params,
365
+ paramTypes,
366
+ returns: absToHarvestSig(retSrc)
367
+ };
368
+ }
369
+ case "sum":
370
+ return { k: "sum", members: s.members.map(absToHarvestSig) };
371
+ case "eff":
372
+ return {
373
+ k: s.eff === "generator" ? "generator" : "promise",
374
+ value: absToHarvestSig(s.inner)
375
+ };
376
+ case "brand":
377
+ return { k: "brand", name: s.name, shape: absToHarvestSig(s.shape) };
378
+ default:
379
+ return { k: "unknown" };
380
+ }
381
+ }
382
+ function harvestSigToAbs(sig) {
383
+ const mark = (a) => {
384
+ a.conf = confJoin(a.conf, "mock");
385
+ return a;
386
+ };
387
+ switch (sig.k) {
388
+ case "prim":
389
+ return mark(
390
+ abs(
391
+ { k: "prim", type: sig.type },
392
+ void 0,
393
+ void 0,
394
+ "mock"
395
+ )
396
+ );
397
+ case "unknown":
398
+ return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
399
+ case "any":
400
+ return mark(abs({ k: "any" }, void 0, void 0, "mock"));
401
+ case "never":
402
+ return mark(abs({ k: "never" }, void 0, void 0, "mock"));
403
+ case "lit": {
404
+ const t = typeof sig.value === "number" ? "number" : typeof sig.value === "string" ? "string" : typeof sig.value === "boolean" ? "boolean" : typeof sig.value === "bigint" ? "bigint" : null;
405
+ if (sig.value === null || t === null) {
406
+ return mark(
407
+ abs({ k: "unknown" }, { op: "lit", value: sig.value }, void 0, "mock")
408
+ );
409
+ }
410
+ return mark(
411
+ abs(
412
+ { k: "prim", type: t },
413
+ { op: "lit", value: sig.value },
414
+ void 0,
415
+ "mock"
416
+ )
417
+ );
418
+ }
419
+ case "arr":
420
+ return mark(abs({ k: "arr", element: harvestSigToAbs(sig.element) }, void 0, void 0, "mock"));
421
+ case "tuple":
422
+ return mark(
423
+ abs({ k: "tuple", elements: sig.elements.map(harvestSigToAbs) }, void 0, void 0, "mock")
424
+ );
425
+ case "obj": {
426
+ const slots = {};
427
+ for (const [key, slot] of Object.entries(sig.slots)) {
428
+ slots[key] = {
429
+ value: harvestSigToAbs(slot.v),
430
+ ...slot.opt ? { optional: true } : {}
431
+ };
432
+ }
433
+ return mark(abs({ k: "obj", slots }, void 0, void 0, "mock"));
434
+ }
435
+ case "tvar":
436
+ return mark(abs({ k: "any" }, { op: "var", id: sig.name }, void 0, "mock"));
437
+ case "fn": {
438
+ const paramTypes = sig.paramTypes.map(harvestSigToAbs);
439
+ const returnType = harvestSigToAbs(sig.returns);
440
+ return mark(
441
+ relationFn(paramTypes, returnType, {
442
+ params: sig.params,
443
+ conf: "mock"
444
+ })
445
+ );
446
+ }
447
+ case "sum":
448
+ return mark(
449
+ abs({ k: "sum", members: sig.members.map(harvestSigToAbs) }, void 0, void 0, "mock")
450
+ );
451
+ case "promise":
452
+ case "generator":
453
+ return mark(
454
+ abs(
455
+ {
456
+ k: "eff",
457
+ eff: sig.k === "generator" ? "generator" : "promise",
458
+ inner: harvestSigToAbs(sig.value)
459
+ },
460
+ void 0,
461
+ void 0,
462
+ "mock"
463
+ )
464
+ );
465
+ case "brand":
466
+ return mark(
467
+ abs(
468
+ { k: "brand", name: sig.name, shape: harvestSigToAbs(sig.shape) },
469
+ void 0,
470
+ void 0,
471
+ "mock"
472
+ )
473
+ );
474
+ default:
475
+ return mark(abs({ k: "unknown" }, void 0, void 0, "mock"));
476
+ }
477
+ }
478
+ function serializeHarvestJson(pkg, env, meta) {
479
+ const modules = {};
480
+ for (const [mod, rec] of Object.entries(env.modules)) {
481
+ const named = {};
482
+ for (const [k, v] of Object.entries(rec)) named[k] = absToHarvestSig(v);
483
+ modules[mod] = named;
484
+ }
485
+ const globals = {};
486
+ for (const [k, v] of Object.entries(env.globals)) globals[k] = absToHarvestSig(v);
487
+ return {
488
+ v: 1,
489
+ pkg,
490
+ ...meta.pkgVersion ? { pkgVersion: meta.pkgVersion } : {},
491
+ knobs: { maxFiles: meta.maxFiles, abi: HARVEST_DISK_ABI },
492
+ dtsHash: meta.dtsHash,
493
+ modules,
494
+ globals,
495
+ stats: env.stats
496
+ };
497
+ }
498
+ function materializeHarvestJson(j) {
499
+ if (!j || typeof j !== "object") return null;
500
+ const h = j;
501
+ if (h.v !== 1) return null;
502
+ if (h.knobs?.abi !== HARVEST_DISK_ABI) return null;
503
+ if (!h.modules || typeof h.modules !== "object") return null;
504
+ try {
505
+ const modules = {};
506
+ for (const [mod, rec] of Object.entries(h.modules)) {
507
+ const named = {};
508
+ for (const [k, sig] of Object.entries(rec)) named[k] = harvestSigToAbs(sig);
509
+ modules[mod] = named;
510
+ }
511
+ const globals = {};
512
+ for (const [k, sig] of Object.entries(h.globals ?? {})) {
513
+ globals[k] = harvestSigToAbs(sig);
514
+ }
515
+ return {
516
+ globals,
517
+ modules,
518
+ stats: h.stats ?? { files: 0, symbols: 0, skipped: 0 }
519
+ };
520
+ } catch {
521
+ return null;
522
+ }
523
+ }
524
+ function harvestCacheKey(pkg, meta) {
525
+ const raw = [
526
+ HARVEST_DISK_ABI,
527
+ pkg,
528
+ meta.pkgVersion ?? "-",
529
+ String(meta.maxFiles),
530
+ meta.dtsHash
531
+ ].join("\0");
532
+ return sha256Hex(raw);
533
+ }
534
+
535
+ // src/harvest-disk.ts
536
+ import { createHash as createHash2 } from "crypto";
537
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
538
+ import { homedir } from "os";
539
+ import { join as join3, dirname as dirname3 } from "path";
540
+ function depsCacheRoot() {
541
+ const env = process.env.NUDO_DEPS_CACHE_DIR;
542
+ if (env === "off" || env === "0") return void 0;
543
+ if (env && env.length > 0) return env;
544
+ try {
545
+ return join3(homedir(), ".cache", "nudo", "deps");
546
+ } catch {
547
+ return void 0;
548
+ }
549
+ }
550
+ function readPkgVersion(root) {
551
+ try {
552
+ const raw = JSON.parse(readFileSync3(join3(root, "package.json"), "utf8"));
553
+ return raw.version;
554
+ } catch {
555
+ return void 0;
556
+ }
557
+ }
558
+ function dtsClosureHash(files) {
559
+ const h = createHash2("sha256");
560
+ for (const f of [...files].sort()) {
561
+ h.update(f.replace(/\\/g, "/"));
562
+ try {
563
+ h.update(readFileSync3(f));
564
+ } catch {
565
+ h.update("miss");
566
+ }
567
+ }
568
+ return h.digest("hex");
569
+ }
570
+ function diskPath(root, key) {
571
+ return join3(root, key.slice(0, 2), `${key}.json`);
572
+ }
573
+ function readHarvestDisk(root, key) {
574
+ try {
575
+ const p = diskPath(root, key);
576
+ if (!existsSync3(p)) return void 0;
577
+ const parsed = JSON.parse(readFileSync3(p, "utf8"));
578
+ if (parsed?.abi !== HARVEST_DISK_ABI) return void 0;
579
+ return parsed.value;
580
+ } catch {
581
+ return void 0;
582
+ }
583
+ }
584
+ function writeHarvestDisk(root, key, value) {
585
+ try {
586
+ const p = diskPath(root, key);
587
+ mkdirSync(dirname3(p), { recursive: true });
588
+ writeFileSync(p, JSON.stringify({ abi: HARVEST_DISK_ABI, value }), "utf8");
589
+ } catch {
590
+ }
591
+ }
592
+ function listPackageDts(pkg, fromDir, maxFiles) {
593
+ const root = resolvePackageRoot(pkg, fromDir);
594
+ if (!root) return null;
595
+ const entry = (() => {
596
+ try {
597
+ const raw = JSON.parse(readFileSync3(join3(root, "package.json"), "utf8"));
598
+ const e = raw.types ?? raw.typings;
599
+ return typeof e === "string" && e.endsWith(".d.ts") ? join3(root, e) : void 0;
600
+ } catch {
601
+ return void 0;
602
+ }
603
+ })();
604
+ let dtsFiles = entry && existsSync3(entry) ? collectDtsFromEntry(entry, Math.max(maxFiles, 24)) : [];
605
+ if (dtsFiles.length === 0) {
606
+ const collected = collectDtsFiles(root, maxFiles);
607
+ dtsFiles = entry && existsSync3(entry) && !collected.includes(entry) ? [entry, ...collected.filter((f) => f !== entry)].slice(0, maxFiles) : collected;
608
+ }
609
+ if (dtsFiles.length === 0) return null;
610
+ return { root, dtsFiles, pkgVersion: readPkgVersion(root) };
611
+ }
612
+ function harvestPackageWithDisk(pkg, fromDir, maxFiles = 24) {
613
+ const cacheRoot = depsCacheRoot();
614
+ const listed = listPackageDts(pkg, fromDir, maxFiles);
615
+ if (!listed) return null;
616
+ const key = harvestCacheKey(pkg, {
617
+ dtsHash: dtsClosureHash(listed.dtsFiles),
618
+ maxFiles,
619
+ pkgVersion: listed.pkgVersion
620
+ });
621
+ if (cacheRoot) {
622
+ const hit = readHarvestDisk(cacheRoot, key);
623
+ if (hit) {
624
+ const env = materializeHarvestJson(hit);
625
+ if (env) {
626
+ return { pkg, root: listed.root, dtsFiles: listed.dtsFiles, env };
627
+ }
628
+ }
629
+ }
630
+ let h = null;
631
+ try {
632
+ const raw = harvestPackage(pkg, fromDir, maxFiles);
633
+ if (!("error" in raw)) h = raw;
634
+ } catch {
635
+ h = null;
636
+ }
637
+ if (!h) return null;
638
+ if (cacheRoot) {
639
+ writeHarvestDisk(
640
+ cacheRoot,
641
+ key,
642
+ serializeHarvestJson(pkg, h.env, {
643
+ dtsHash: dtsClosureHash(h.dtsFiles),
644
+ maxFiles,
645
+ pkgVersion: listed.pkgVersion
646
+ })
647
+ );
648
+ }
649
+ return h;
650
+ }
651
+ function loadHarvestEnvFromDisk(pkg, meta) {
652
+ const root = depsCacheRoot();
653
+ if (!root) return null;
654
+ const key = harvestCacheKey(pkg, meta);
655
+ const hit = readHarvestDisk(root, key);
656
+ return hit ? materializeHarvestJson(hit) : null;
657
+ }
658
+
659
+ // src/harvest-auto.ts
660
+ import { parse as parse2 } from "@nudojs/parser";
661
+ var NODE_BUILTINS = /* @__PURE__ */ new Set([
662
+ "assert",
663
+ "async_hooks",
664
+ "buffer",
665
+ "child_process",
666
+ "cluster",
667
+ "console",
668
+ "constants",
669
+ "crypto",
670
+ "dgram",
671
+ "diagnostics_channel",
672
+ "dns",
673
+ "domain",
674
+ "events",
675
+ "fs",
676
+ "http",
677
+ "http2",
678
+ "https",
679
+ "inspector",
680
+ "module",
681
+ "net",
682
+ "os",
683
+ "path",
684
+ "perf_hooks",
685
+ "process",
686
+ "punycode",
687
+ "querystring",
688
+ "readline",
689
+ "repl",
690
+ "stream",
691
+ "string_decoder",
692
+ "timers",
693
+ "tls",
694
+ "trace_events",
695
+ "tty",
696
+ "url",
697
+ "util",
698
+ "v8",
699
+ "vm",
700
+ "wasi",
701
+ "worker_threads",
702
+ "zlib"
703
+ ]);
704
+ function barePackageName(spec) {
705
+ if (!spec) return void 0;
706
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) return void 0;
707
+ const parts = spec.split("/");
708
+ if (spec.startsWith("@")) {
709
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
710
+ }
711
+ const name = parts[0];
712
+ if (NODE_BUILTINS.has(name)) return void 0;
713
+ return name;
714
+ }
715
+ function collectBarePackages(source) {
716
+ try {
717
+ const ast = parse2(source);
718
+ const names = /* @__PURE__ */ new Set();
719
+ for (const spec of collectDependencySpecs(ast)) {
720
+ const pkg = barePackageName(spec);
721
+ if (pkg) names.add(pkg);
722
+ }
723
+ return [...names];
724
+ } catch {
725
+ return [];
726
+ }
727
+ }
728
+ var HARVEST_CACHE_MAX = 128;
729
+ var harvestCache = new BoundedLruMap(HARVEST_CACHE_MAX);
730
+ function getHarvestCacheSize() {
731
+ return harvestCache.size;
732
+ }
733
+ function harvestPackageCached(pkg, fromDir) {
734
+ const key = `${fromDir}::${pkg}`;
735
+ if (harvestCache.has(key)) return harvestCache.get(key);
736
+ let result = null;
737
+ try {
738
+ result = harvestPackageWithDisk(pkg, fromDir);
739
+ } catch {
740
+ result = null;
741
+ }
742
+ harvestCache.set(key, result);
743
+ return result;
744
+ }
745
+ function clearHarvestCache() {
746
+ harvestCache.clear();
747
+ }
748
+ function autoHarvestModules(source, fromDir) {
749
+ const packages = collectBarePackages(source);
750
+ if (packages.length === 0) return {};
751
+ const modules = {};
752
+ for (const pkg of packages) {
753
+ const h = harvestPackageCached(pkg, fromDir);
754
+ if (!h) continue;
755
+ for (const [mod, exports] of Object.entries(h.env.modules)) {
756
+ modules[mod] = exports;
757
+ if (!modules[pkg]) modules[pkg] = exports;
758
+ const base = mod.replace(/^@types\//, "").replace(/\\/g, "/");
759
+ if (base && !modules[base]) modules[base] = exports;
760
+ }
761
+ for (const [k, v] of Object.entries(h.env.globals)) {
762
+ if (!modules[pkg]) modules[pkg] = {};
763
+ if (modules[pkg][k] === void 0) modules[pkg][k] = v;
764
+ }
765
+ }
766
+ return modules;
767
+ }
768
+
769
+ // src/harvest-to-abs.ts
770
+ import {
771
+ confJoin as confJoin2,
772
+ absFunction,
773
+ getFnImpl as getFnImpl2,
774
+ instantiateReturn
775
+ } from "@nudojs/core";
776
+ import { dirname as dirname4 } from "path";
777
+ function markMockConf(a) {
778
+ a.conf = confJoin2(a.conf, "mock");
779
+ return a;
780
+ }
781
+ function harvestedValueToAbs(a) {
782
+ if (a.shape.k === "fn") {
783
+ const ret = a.shape.returnType ?? { shape: { k: "unknown" }, conf: "mock" };
784
+ const relation = getFnImpl2(a)?.relation;
785
+ const params = a.shape.params.length > 0 ? a.shape.params : a.shape.paramTypes?.map((_, i) => `_arg${i}`) ?? ["...args"];
786
+ const dummyBody = {
787
+ type: "BlockStatement",
788
+ body: [],
789
+ directives: []
790
+ };
791
+ return markMockConf(
792
+ absFunction(params, {
793
+ body: dummyBody,
794
+ apply: (args) => {
795
+ if (relation) {
796
+ return markMockConf(instantiateReturn(a, args));
797
+ }
798
+ return markMockConf({ ...ret });
799
+ },
800
+ ...relation ? { relation } : {}
801
+ })
802
+ );
803
+ }
804
+ return markMockConf({ ...a });
805
+ }
806
+ function harvestToAbsModules(pkg, fromDir) {
807
+ const h = harvestPackageCached(pkg, fromDir);
808
+ if (!h) return {};
809
+ return packageHarvestToAbsModules(pkg, h);
810
+ }
811
+ function packageHarvestToAbsModules(pkg, h) {
812
+ const out = {};
813
+ const convertRecord = (rec) => {
814
+ const named = {};
815
+ for (const [k, v] of Object.entries(rec)) {
816
+ named[k] = harvestedValueToAbs(v);
817
+ }
818
+ return named;
819
+ };
820
+ const register = (key, exports) => {
821
+ if (!out[key]) out[key] = exports;
822
+ };
823
+ for (const [mod, rec] of Object.entries(h.env.modules)) {
824
+ const named = convertRecord(rec);
825
+ const exports = { named };
826
+ const def = rec["default"];
827
+ if (def) exports.default = harvestedValueToAbs(def);
828
+ register(mod, exports);
829
+ register(pkg, exports);
830
+ const stripped = mod.replace(/^@types\//, "").replace(/\\/g, "/");
831
+ if (stripped) register(stripped, exports);
832
+ const base = stripped.split("/").filter(Boolean).pop();
833
+ if (base && base !== pkg) register(base, exports);
834
+ }
835
+ const globalNamed = convertRecord(h.env.globals);
836
+ if (Object.keys(globalNamed).length > 0) {
837
+ const existing = out[pkg];
838
+ if (existing) {
839
+ out[pkg] = { named: { ...globalNamed, ...existing.named }, default: existing.default };
840
+ } else {
841
+ out[pkg] = { named: globalNamed };
842
+ }
843
+ }
844
+ return out;
845
+ }
846
+ function bareSpecToAbsModules(spec, fromFile) {
847
+ if (!spec || spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) {
848
+ return void 0;
849
+ }
850
+ const parts = spec.split("/");
851
+ const pkg = spec.startsWith("@") ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0 : parts[0];
852
+ if (!pkg) return void 0;
853
+ const table = harvestToAbsModules(pkg, dirname4(fromFile));
854
+ return table[spec] ?? table[pkg];
855
+ }
856
+
857
+ export {
858
+ collectDependencySpecs,
859
+ analyzeExportsFromSource,
860
+ collectStaticImports,
861
+ resolvePackageRoot,
862
+ collectDtsFiles,
863
+ collectDtsFromEntry,
864
+ harvestPackage,
865
+ formatHarvestSummary,
866
+ lookupHarvested,
867
+ absToHarvestSig,
868
+ harvestSigToAbs,
869
+ serializeHarvestJson,
870
+ materializeHarvestJson,
871
+ harvestCacheKey,
872
+ depsCacheRoot,
873
+ dtsClosureHash,
874
+ readHarvestDisk,
875
+ writeHarvestDisk,
876
+ harvestPackageWithDisk,
877
+ loadHarvestEnvFromDisk,
878
+ barePackageName,
879
+ collectBarePackages,
880
+ getHarvestCacheSize,
881
+ harvestPackageCached,
882
+ clearHarvestCache,
883
+ autoHarvestModules,
884
+ harvestedValueToAbs,
885
+ harvestToAbsModules,
886
+ packageHarvestToAbsModules,
887
+ bareSpecToAbsModules
888
+ };