@nudojs/service 5.0.0-beta.0 → 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,159 +1,25 @@
1
1
  import {
2
- unifiedDiff
3
- } from "./chunk-YLVZMHWC.js";
2
+ DiskCache,
3
+ collectLoadDepContents,
4
+ ifaceCacheKey,
5
+ injectBindings,
6
+ typeExprToDirective
7
+ } from "../chunk-J2PUE4NV.js";
4
8
  import {
5
9
  analyzeFileAsync,
6
10
  defaultLoadModule,
7
11
  evalAbsModuleGraph
8
- } from "./chunk-QAFMPM2E.js";
12
+ } from "../chunk-NQXAF4LJ.js";
9
13
  import {
10
14
  diskCacheRoot,
11
15
  findProjectConfig,
12
16
  interfaceConfig,
13
17
  matchesEmitAllowlist
14
- } from "./chunk-YLALZXTZ.js";
15
-
16
- // src/dep-contents.ts
17
- import { loadModuleDepsFingerprint } from "@nudojs/core/internal";
18
- function collectLoadDepContents(filePath, source, loadModule) {
19
- const fp = loadModuleDepsFingerprint(source, loadModule, filePath);
20
- const depContents = fp.contents.length > 0 ? fp.contents : fp.paths.map((path) => ({ path, content: null }));
21
- return { depContents, truncated: fp.truncated };
22
- }
23
-
24
- // src/disk-cache.ts
25
- import { createHash } from "crypto";
26
- import { createRequire } from "module";
27
- import { mkdirSync, readFileSync, writeFileSync, existsSync, rmSync } from "fs";
28
- import { join, dirname, relative, sep, isAbsolute } from "path";
29
- function readServiceVersion() {
30
- try {
31
- const require2 = createRequire(import.meta.url);
32
- for (const p of ["../package.json", "./package.json", "../../package.json"]) {
33
- try {
34
- const pkg = require2(p);
35
- if (pkg?.name === "@nudojs/service" && pkg.version) return pkg.version;
36
- if (pkg?.version && p.includes("service")) return pkg.version;
37
- } catch {
38
- }
39
- }
40
- } catch {
41
- }
42
- return "0";
43
- }
44
- var ANALYSIS_ABI = `nudo-check-cache-v3+${readServiceVersion()}`;
45
- function sha256Hex(data) {
46
- return createHash("sha256").update(data).digest("hex");
47
- }
48
- function relativizePath(p, root) {
49
- const norm = p.split(sep).join("/");
50
- if (!root) return norm;
51
- const r = relative(root, p).split(sep).join("/");
52
- if (!r.startsWith("..") && !isAbsolute(r)) return r;
53
- return `ext:${createHash("sha256").update(norm).digest("hex").slice(0, 16)}`;
54
- }
55
- function sanitizeCacheNamespace(ns) {
56
- return ns.replace(/[^a-z0-9_-]/gi, "_").replace(/^_+|_+$/g, "") || "cache";
57
- }
58
- var DiskCache = class {
59
- root;
60
- ns;
61
- enabled = false;
62
- constructor(opts) {
63
- this.root = opts.root;
64
- this.ns = sanitizeCacheNamespace(opts.namespace);
65
- this.enabled = !!opts.root;
66
- }
67
- pathFor(key) {
68
- if (!/^[a-f0-9]{16,128}$/i.test(key)) {
69
- throw new Error("DiskCache key must be a hex digest");
70
- }
71
- return join(this.root, this.ns, key.slice(0, 2), `${key}.json`);
72
- }
73
- get(key) {
74
- if (!this.enabled || !this.root) return void 0;
75
- try {
76
- const p = this.pathFor(key);
77
- if (!existsSync(p)) return void 0;
78
- const raw = readFileSync(p, "utf8");
79
- const parsed = JSON.parse(raw);
80
- if (parsed?.abi !== ANALYSIS_ABI) return void 0;
81
- return parsed.value;
82
- } catch {
83
- return void 0;
84
- }
85
- }
86
- set(key, value) {
87
- if (!this.enabled || !this.root) return;
88
- try {
89
- const p = this.pathFor(key);
90
- mkdirSync(dirname(p), { recursive: true });
91
- writeFileSync(p, JSON.stringify({ abi: ANALYSIS_ABI, value }), "utf8");
92
- } catch {
93
- }
94
- }
95
- clearNamespace() {
96
- if (!this.enabled || !this.root) return;
97
- try {
98
- rmSync(join(this.root, this.ns), { recursive: true, force: true });
99
- } catch {
100
- }
101
- }
102
- };
103
- function checkCacheKey(filePath, source, opts) {
104
- const rel = relativizePath(filePath, opts.projectDir);
105
- const sidecarSha = opts.sidecarContent != null ? sha256Hex(opts.sidecarContent) : "nosidecar";
106
- const depSeg = (opts.depContents ?? []).map(
107
- (d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
108
- ).join("\n");
109
- const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
110
- const cfgSeg = opts.analysisCfg ? `${opts.analysisCfg.mode ?? "-"}|${opts.analysisCfg.evalMissingSlot ?? "-"}|${opts.analysisCfg.callSiteBudget ?? "-"}|${opts.analysisCfg.entryThrows ?? "-"}|${opts.analysisCfg.ignoreThrows ?? "-"}` : "-";
111
- return sha256Hex(
112
- [
113
- ANALYSIS_ABI,
114
- rel,
115
- opts.autoBind ? "ab1" : "ab0",
116
- sha256Hex(source),
117
- sidecarSha,
118
- depSeg,
119
- envSeg,
120
- cfgSeg
121
- ].join("\0")
122
- );
123
- }
124
- function ifaceCacheKey(filePath, source, opts) {
125
- const rel = relativizePath(filePath, opts.projectDir);
126
- const sidecarSeg = opts.autoBind && opts.sidecarSource !== void 0 ? `sc:${sha256Hex(opts.sidecarSource)}` : "sc0";
127
- const depSeg = (opts.depContents ?? []).map(
128
- (d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
129
- ).join("\n");
130
- const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
131
- return sha256Hex(
132
- [
133
- ANALYSIS_ABI,
134
- "iface",
135
- rel,
136
- opts.autoBind ? "ab1" : "ab0",
137
- sha256Hex(source),
138
- sidecarSeg,
139
- depSeg,
140
- envSeg
141
- ].join("\0")
142
- );
143
- }
144
- function extractNudoImportSpecs(source) {
145
- const specs = /* @__PURE__ */ new Set();
146
- const named = /@nudo:import\s*\{[^}]*\}\s*from\s*["']([^"']+)["']/g;
147
- const ns = /@nudo:import\s+\*\s+as\s+\w+\s+from\s*["']([^"']+)["']/g;
148
- let m;
149
- while (m = named.exec(source)) specs.add(m[1]);
150
- while (m = ns.exec(source)) specs.add(m[1]);
151
- return [...specs];
152
- }
18
+ } from "../chunk-ZXVAX7DE.js";
153
19
 
154
- // src/interface-surface.ts
155
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
156
- import { dirname as dirname2, resolve } from "path";
20
+ // src/emit/interface-surface.ts
21
+ import { readFileSync, existsSync } from "fs";
22
+ import { dirname, resolve } from "path";
157
23
  import {
158
24
  effectiveInterface,
159
25
  formatConstraint,
@@ -199,11 +65,11 @@ function cachedToEffective(e) {
199
65
  }
200
66
  async function interfaceSurface(filePath, opts = {}) {
201
67
  const abs = resolve(filePath);
202
- const source = opts.source ?? readFileSync2(abs, "utf-8");
68
+ const source = opts.source ?? readFileSync(abs, "utf-8");
203
69
  const fromBuffer = opts.source !== void 0;
204
70
  const ifaceSince = interfaceDiagCount();
205
71
  const refineSince = refineDiagCount();
206
- const proj = findProjectConfig(dirname2(abs));
72
+ const proj = findProjectConfig(dirname(abs));
207
73
  const autoBind = opts.autoBind ?? interfaceConfig(proj?.config).autoBind;
208
74
  const loadModule = opts.loadModule ?? defaultLoadModule;
209
75
  const exported = localNamedExports(source);
@@ -222,7 +88,7 @@ async function interfaceSurface(filePath, opts = {}) {
222
88
  if (autoBind !== false) {
223
89
  const openSc = loadModule(`./${sc.slice(sc.lastIndexOf("/") + 1)}`, abs);
224
90
  if (openSc !== void 0) sidecarSource = openSc;
225
- else if (existsSync2(sc)) sidecarSource = readFileSync2(sc, "utf-8");
91
+ else if (existsSync(sc)) sidecarSource = readFileSync(sc, "utf-8");
226
92
  }
227
93
  } catch {
228
94
  sidecarSource = void 0;
@@ -314,9 +180,9 @@ async function interfaceSurface(filePath, opts = {}) {
314
180
  return entries;
315
181
  }
316
182
 
317
- // src/interface-emitter.ts
318
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
319
- import { basename, dirname as dirname3, relative as relative2, resolve as resolve2 } from "path";
183
+ // src/emit/interface-emitter.ts
184
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, renameSync, unlinkSync } from "fs";
185
+ import { basename, dirname as dirname2, relative, resolve as resolve2 } from "path";
320
186
  import {
321
187
  execNudoModule,
322
188
  formatConstraint as formatConstraint2,
@@ -332,6 +198,365 @@ import {
332
198
  takeRefineDiagsSince as takeRefineDiagsSince2
333
199
  } from "@nudojs/core";
334
200
  import { randomBytes } from "crypto";
201
+
202
+ // src/emit/case-emitter.ts
203
+ import { parse, extractDirectives } from "@nudojs/parser";
204
+ var GENERATED_PREFIX = "call@";
205
+ var GENERATED_CASE_LINE_REGEX = /^\s*\*?\s*@nudo:case\s+"(call@[^"]*)"/;
206
+ var IDENT_KEY_REGEX = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
207
+ var NUMBER_LITERAL_REGEX = /^-?\d+(\.\d+)?$/;
208
+ var UNSAFE_STRING_CHARS = /[,()[\]{}]/;
209
+ var UNSAFE_KEY_CHARS = /[,()[\]{}:]/;
210
+ var CONTROL_CHARS = /[\x00-\x1f\x7f]/;
211
+ function serializeStringLiteral(value) {
212
+ if (CONTROL_CHARS.test(value)) return null;
213
+ if (value.includes("*/")) return null;
214
+ if (UNSAFE_STRING_CHARS.test(value)) return null;
215
+ const hasDbl = value.includes('"');
216
+ const hasSgl = value.includes("'");
217
+ if (hasDbl && hasSgl) return null;
218
+ return hasDbl ? `'${value}'` : `"${value}"`;
219
+ }
220
+ function serializeObjectKey(key) {
221
+ if (IDENT_KEY_REGEX.test(key)) return key;
222
+ if (CONTROL_CHARS.test(key)) return null;
223
+ if (key.includes('"')) return null;
224
+ if (UNSAFE_KEY_CHARS.test(key) || key.includes("*/")) return null;
225
+ if (/^['"]|['"]$/.test(key)) return null;
226
+ return `"${key}"`;
227
+ }
228
+ function serializeCaseArg(a) {
229
+ const s = a.shape;
230
+ if (a.term?.op === "lit") {
231
+ const v = a.term.value;
232
+ if (typeof v === "number") {
233
+ if (!Number.isFinite(v)) return null;
234
+ const n = String(v);
235
+ return NUMBER_LITERAL_REGEX.test(n) ? n : null;
236
+ }
237
+ if (typeof v === "boolean") return v ? "true" : "false";
238
+ if (v === null) return "null";
239
+ if (v === void 0) return "undefined";
240
+ if (typeof v === "bigint") return null;
241
+ if (typeof v === "string") return serializeStringLiteral(v);
242
+ return null;
243
+ }
244
+ switch (s.k) {
245
+ case "prim":
246
+ if (s.type === "number") return "number()";
247
+ if (s.type === "string") return "string()";
248
+ if (s.type === "boolean") return "boolean()";
249
+ return null;
250
+ case "unknown":
251
+ case "any":
252
+ return "any()";
253
+ case "never":
254
+ return "never";
255
+ case "sum": {
256
+ const parts = [];
257
+ for (const member of s.members) {
258
+ const ser = serializeCaseArg(member);
259
+ if (ser === null) return null;
260
+ parts.push(ser);
261
+ }
262
+ return `union(${parts.join(", ")})`;
263
+ }
264
+ case "arr": {
265
+ const el = serializeCaseArg(s.element);
266
+ return el === null ? null : `array(${el})`;
267
+ }
268
+ case "tuple": {
269
+ const parts = [];
270
+ for (const el of s.elements) {
271
+ const ser = serializeCaseArg(el);
272
+ if (ser === null) return null;
273
+ parts.push(ser);
274
+ }
275
+ return `[${parts.join(", ")}]`;
276
+ }
277
+ case "obj": {
278
+ const parts = [];
279
+ for (const [key, slot] of Object.entries(s.slots)) {
280
+ const vs = serializeCaseArg(slot.value);
281
+ if (vs === null) return null;
282
+ const ks = serializeObjectKey(key);
283
+ if (ks === null) return null;
284
+ parts.push(`${ks}: ${vs}`);
285
+ }
286
+ return parts.length === 0 ? "{}" : `{ ${parts.join(", ")} }`;
287
+ }
288
+ default:
289
+ return null;
290
+ }
291
+ }
292
+ function buildCaseDirective(name, argsAbs) {
293
+ if (name.includes('"') || /[\r\n]/.test(name)) return null;
294
+ const parts = [];
295
+ for (const arg of argsAbs) {
296
+ const s = serializeCaseArg(arg);
297
+ if (s === null) return null;
298
+ parts.push(s);
299
+ }
300
+ return ` * @nudo:case "${name}" (${parts.join(", ")})`;
301
+ }
302
+ function stripGeneratedCaseDirectives(source) {
303
+ const lines = source.split("\n");
304
+ const removed = [];
305
+ const drop = new Array(lines.length).fill(false);
306
+ for (let i = 0; i < lines.length; i++) {
307
+ const m = lines[i].match(GENERATED_CASE_LINE_REGEX);
308
+ if (m) {
309
+ drop[i] = true;
310
+ removed.push(m[1]);
311
+ }
312
+ }
313
+ for (let i = 0; i < lines.length; i++) {
314
+ if (!/^\s*\/\*\*\s*$/.test(lines[i])) continue;
315
+ let j = i + 1;
316
+ let clean = true;
317
+ while (j < lines.length) {
318
+ if (/\*\//.test(lines[j])) break;
319
+ if (/\/\*/.test(lines[j])) {
320
+ clean = false;
321
+ break;
322
+ }
323
+ j++;
324
+ }
325
+ if (!clean || j >= lines.length) continue;
326
+ if (!/^\s*\*\/\s*$/.test(lines[j])) continue;
327
+ let onlyFiller = true;
328
+ for (let k = i + 1; k < j; k++) {
329
+ if (drop[k]) continue;
330
+ const t = lines[k].trim();
331
+ if (t !== "" && !/^\*{1,2}$/.test(t)) {
332
+ onlyFiller = false;
333
+ break;
334
+ }
335
+ }
336
+ if (onlyFiller) {
337
+ for (let k = i; k <= j; k++) drop[k] = true;
338
+ }
339
+ }
340
+ return {
341
+ source: lines.filter((_, i) => !drop[i]).join("\n"),
342
+ removed
343
+ };
344
+ }
345
+ function collectExistingCases(source) {
346
+ const byName = /* @__PURE__ */ new Map();
347
+ let ast;
348
+ try {
349
+ ast = parse(source, { errorRecovery: true });
350
+ } catch {
351
+ return byName;
352
+ }
353
+ for (const { name, directives } of extractDirectives(ast)) {
354
+ for (const d of directives) {
355
+ if (d.kind !== "case") continue;
356
+ const list = byName.get(name);
357
+ if (list) list.push(d);
358
+ else byName.set(name, [d]);
359
+ }
360
+ }
361
+ return byName;
362
+ }
363
+ function insertGeneratedCaseDirectives(source, analysis) {
364
+ const written = [];
365
+ const skipped = [];
366
+ const existing = collectExistingCases(source);
367
+ const edits = [];
368
+ for (const fn of analysis.functions) {
369
+ const existingCases = existing.get(fn.name) ?? [];
370
+ if (existingCases.some((c) => !c.name.startsWith(GENERATED_PREFIX))) {
371
+ skipped.push({ fn: fn.name, reason: "hand-written" });
372
+ continue;
373
+ }
374
+ if (existingCases.length > 0) {
375
+ skipped.push({ fn: fn.name, reason: "already-generated" });
376
+ continue;
377
+ }
378
+ if (fn.skipped) {
379
+ skipped.push({ fn: fn.name, reason: "skipped" });
380
+ continue;
381
+ }
382
+ if (fn.noDeclaration) {
383
+ skipped.push({ fn: fn.name, reason: "no-declaration" });
384
+ continue;
385
+ }
386
+ if (fn.entryOnly) {
387
+ skipped.push({ fn: fn.name, reason: "entry-only" });
388
+ continue;
389
+ }
390
+ const callsiteCases = fn.cases.filter((c) => c.source === "callsite");
391
+ if (callsiteCases.length === 0) {
392
+ skipped.push({ fn: fn.name, reason: "no-serializable-cases", detail: "no callsite cases" });
393
+ continue;
394
+ }
395
+ const built = [];
396
+ const names = [];
397
+ for (const c of callsiteCases) {
398
+ const directive = buildCaseDirective(c.name, c.argAbs ?? []);
399
+ if (directive === null) {
400
+ skipped.push({ fn: fn.name, reason: "no-serializable-cases", detail: `case ${c.name} not serializable` });
401
+ } else {
402
+ built.push(directive);
403
+ names.push(c.name);
404
+ }
405
+ }
406
+ if (built.length === 0) continue;
407
+ edits.push({ line: fn.loc.start.line, column: fn.loc.start.column, directives: built, fn: fn.name });
408
+ written.push({ fn: fn.name, cases: names });
409
+ }
410
+ edits.sort((a, b) => b.line - a.line);
411
+ const lines = source.split("\n");
412
+ for (const edit of edits) {
413
+ const declIdx = edit.line - 1;
414
+ const indent = " ".repeat(edit.column);
415
+ let insertIdx = -1;
416
+ let blockIndent = "";
417
+ const aboveIdx = declIdx - 1;
418
+ if (aboveIdx >= 0 && /^\s*\*\/\s*$/.test(lines[aboveIdx])) {
419
+ let s = aboveIdx - 1;
420
+ while (s >= 0) {
421
+ const t = lines[s];
422
+ if (/^\s*\/\*\*/.test(t)) {
423
+ insertIdx = s + 1;
424
+ blockIndent = t.match(/^\s*/)?.[0] ?? "";
425
+ break;
426
+ }
427
+ if (/^\s*\*/.test(t) || t.trim() === "") {
428
+ s--;
429
+ continue;
430
+ }
431
+ break;
432
+ }
433
+ }
434
+ if (insertIdx >= 0) {
435
+ lines.splice(insertIdx, 0, ...edit.directives.map((d) => blockIndent + d));
436
+ } else {
437
+ lines.splice(declIdx, 0, `${indent}/**`, ...edit.directives.map((d) => indent + d), `${indent} */`);
438
+ }
439
+ }
440
+ return {
441
+ source: lines.join("\n"),
442
+ changed: written.length > 0,
443
+ written,
444
+ skipped
445
+ };
446
+ }
447
+ function diffOps(a, b) {
448
+ let start = 0;
449
+ while (start < a.length && start < b.length && a[start] === b[start]) start++;
450
+ let endA = a.length;
451
+ let endB = b.length;
452
+ while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {
453
+ endA--;
454
+ endB--;
455
+ }
456
+ const midA = a.slice(start, endA);
457
+ const midB = b.slice(start, endB);
458
+ const n = midA.length;
459
+ const m = midB.length;
460
+ const dp = new Int32Array((n + 1) * (m + 1));
461
+ for (let i2 = n - 1; i2 >= 0; i2--) {
462
+ for (let j2 = m - 1; j2 >= 0; j2--) {
463
+ dp[i2 * (m + 1) + j2] = midA[i2] === midB[j2] ? dp[(i2 + 1) * (m + 1) + (j2 + 1)] + 1 : Math.max(dp[(i2 + 1) * (m + 1) + j2], dp[i2 * (m + 1) + (j2 + 1)]);
464
+ }
465
+ }
466
+ const ops = [];
467
+ for (let k = 0; k < start; k++) ops.push({ type: "equal", text: a[k] });
468
+ let i = 0;
469
+ let j = 0;
470
+ while (i < n && j < m) {
471
+ if (midA[i] === midB[j]) {
472
+ ops.push({ type: "equal", text: midA[i] });
473
+ i++;
474
+ j++;
475
+ } else if (dp[(i + 1) * (m + 1) + j] >= dp[i * (m + 1) + (j + 1)]) {
476
+ ops.push({ type: "delete", text: midA[i] });
477
+ i++;
478
+ } else {
479
+ ops.push({ type: "insert", text: midB[j] });
480
+ j++;
481
+ }
482
+ }
483
+ while (i < n) ops.push({ type: "delete", text: midA[i++] });
484
+ while (j < m) ops.push({ type: "insert", text: midB[j++] });
485
+ for (let k = endA; k < a.length; k++) ops.push({ type: "equal", text: a[k] });
486
+ return ops;
487
+ }
488
+ var DIFF_CONTEXT = 3;
489
+ function formatRange(start, count) {
490
+ const s = count === 0 ? start - 1 : start;
491
+ return count === 1 ? `${s}` : `${s},${count}`;
492
+ }
493
+ function unifiedDiff(a, b, path) {
494
+ if (a === b) return "";
495
+ const ops = diffOps(a.split("\n"), b.split("\n"));
496
+ const hunks = [];
497
+ let idx = 0;
498
+ while (idx < ops.length) {
499
+ if (ops[idx].type === "equal") {
500
+ idx++;
501
+ continue;
502
+ }
503
+ let start = idx;
504
+ let ctx = 0;
505
+ while (start > 0 && ops[start - 1].type === "equal" && ctx < DIFF_CONTEXT) {
506
+ start--;
507
+ ctx++;
508
+ }
509
+ let j = idx;
510
+ let equalRun = 0;
511
+ let lastChange = idx;
512
+ while (j < ops.length) {
513
+ if (ops[j].type === "equal") {
514
+ equalRun++;
515
+ if (equalRun > DIFF_CONTEXT * 2) break;
516
+ } else {
517
+ equalRun = 0;
518
+ lastChange = j;
519
+ }
520
+ j++;
521
+ }
522
+ let end = lastChange + 1;
523
+ ctx = 0;
524
+ while (end < ops.length && ops[end].type === "equal" && ctx < DIFF_CONTEXT) {
525
+ end++;
526
+ ctx++;
527
+ }
528
+ hunks.push({ start, ops: ops.slice(start, end) });
529
+ idx = end;
530
+ }
531
+ const out = [`--- a/${path}`, `+++ b/${path}`];
532
+ for (let h = 0; h < hunks.length; h++) {
533
+ const { start, ops: hunkOps } = hunks[h];
534
+ let oldCount = 0;
535
+ let newCount = 0;
536
+ for (let k = 0; k < start; k++) {
537
+ if (ops[k].type !== "insert") oldCount++;
538
+ if (ops[k].type !== "delete") newCount++;
539
+ }
540
+ let dels = 0;
541
+ let ins = 0;
542
+ for (const op of hunkOps) {
543
+ if (op.type === "delete") dels++;
544
+ else if (op.type === "insert") ins++;
545
+ else {
546
+ dels++;
547
+ ins++;
548
+ }
549
+ }
550
+ out.push(`@@ -${formatRange(oldCount + 1, dels)} +${formatRange(newCount + 1, ins)} @@`);
551
+ for (const op of hunkOps) {
552
+ const prefix = op.type === "equal" ? " " : op.type === "delete" ? "-" : "+";
553
+ out.push(prefix + op.text);
554
+ }
555
+ }
556
+ return out.join("\n") + "\n";
557
+ }
558
+
559
+ // src/emit/interface-emitter.ts
335
560
  var GENERATED_HEADER = "// @generated by nudo \u2014 do not edit; regenerate with `nudo contract --emit`";
336
561
  async function emitInterface(filePath, opts) {
337
562
  const abs = resolve2(filePath);
@@ -340,7 +565,7 @@ async function emitInterface(filePath, opts) {
340
565
  `emit target '${abs}' is inside node_modules; contract sidecars are never written there`
341
566
  );
342
567
  }
343
- const proj = findProjectConfig(dirname3(abs));
568
+ const proj = findProjectConfig(dirname2(abs));
344
569
  const allow = interfaceConfig(proj?.config).emit;
345
570
  if (!matchesEmitAllowlist(abs, proj?.projectDir, allow)) {
346
571
  return {
@@ -351,24 +576,24 @@ async function emitInterface(filePath, opts) {
351
576
  {
352
577
  code: "nudo:interface-emit-denied",
353
578
  severity: "warning",
354
- message: `emit target '${relative2(process.cwd(), abs) || abs}' is outside package.json#nudo.contract.emit allowlist`
579
+ message: `emit target '${relative(process.cwd(), abs) || abs}' is outside package.json#nudo.contract.emit allowlist`
355
580
  }
356
581
  ],
357
582
  sidecarPath: sidecarPathOf2(abs)
358
583
  };
359
584
  }
360
- const source = opts.source ?? readFileSync3(abs, "utf-8");
585
+ const source = opts.source ?? readFileSync2(abs, "utf-8");
361
586
  const ifaceSince = interfaceDiagCount2();
362
587
  const refineSince = refineDiagCount2();
363
588
  const sidecarPath = sidecarPathOf2(abs);
364
- const sidecarSrc = existsSync3(sidecarPath) ? readFileSync3(sidecarPath, "utf-8") : "";
365
- const srcRel = relative2(dirname3(sidecarPath), abs) || basename(abs);
589
+ const sidecarSrc = existsSync2(sidecarPath) ? readFileSync2(sidecarPath, "utf-8") : "";
590
+ const srcRel = relative(dirname2(sidecarPath), abs) || basename(abs);
366
591
  const sections = collectGeneratedSections(sidecarSrc);
367
592
  const generatedNames = new Set(sections.flatMap((s) => s.names));
368
593
  const declared = topLevelDeclaredNames(sidecarSrc);
369
594
  if (declared === void 0) {
370
595
  throw new Error(
371
- `sidecar '${relative2(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
596
+ `sidecar '${relative(process.cwd(), sidecarPath) || sidecarPath}' is not parseable; fix it before emitting`
372
597
  );
373
598
  }
374
599
  for (const n of generatedNames) declared.delete(n);
@@ -481,15 +706,15 @@ async function emitInterface(filePath, opts) {
481
706
  finalContent = joinSections(base, [...preserved, ...accepted.map((a) => a.text)]);
482
707
  }
483
708
  const changed = finalContent !== sidecarSrc;
484
- const diff = changed ? unifiedDiff(sidecarSrc, finalContent, relative2(process.cwd(), sidecarPath) || sidecarPath) : void 0;
709
+ const diff = changed ? unifiedDiff(sidecarSrc, finalContent, relative(process.cwd(), sidecarPath) || sidecarPath) : void 0;
485
710
  if (changed && !opts.dryRun) {
486
711
  const tmp = `${sidecarPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
487
712
  try {
488
- writeFileSync2(tmp, finalContent, "utf-8");
713
+ writeFileSync(tmp, finalContent, "utf-8");
489
714
  renameSync(tmp, sidecarPath);
490
715
  } catch (e) {
491
716
  try {
492
- if (existsSync3(tmp)) unlinkSync(tmp);
717
+ if (existsSync2(tmp)) unlinkSync(tmp);
493
718
  } catch {
494
719
  }
495
720
  throw e;
@@ -679,9 +904,9 @@ function joinSections(base, sectionTexts) {
679
904
  return lead + normalized.join("\n");
680
905
  }
681
906
 
682
- // src/interface-draft.ts
683
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, realpathSync } from "fs";
684
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, join as join2, relative as relative3 } from "path";
907
+ // src/emit/interface-draft.ts
908
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, realpathSync } from "fs";
909
+ import { basename as basename2, dirname as dirname3, isAbsolute, join, relative as relative2 } from "path";
685
910
  import {
686
911
  effectiveInterface as effectiveInterface2,
687
912
  formatConstraint as formatConstraint3,
@@ -692,12 +917,12 @@ import {
692
917
  localNamedExports as localNamedExports3,
693
918
  sidecarPathOf as sidecarPathOf3
694
919
  } from "@nudojs/core";
695
- import { parse } from "@nudojs/parser";
920
+ import { parse as parse2 } from "@nudojs/parser";
696
921
  function collectParamBodyAccesses(source) {
697
922
  const out = /* @__PURE__ */ new Map();
698
923
  let ast;
699
924
  try {
700
- ast = parse(source);
925
+ ast = parse2(source);
701
926
  } catch {
702
927
  return out;
703
928
  }
@@ -1059,7 +1284,7 @@ function suggestedBodyDsl(fnName, params) {
1059
1284
  return `// suggested (body-read, not a contract): ${fnName} = fn({ ${parts.join(", ")} })`;
1060
1285
  }
1061
1286
  async function draftInterface(filePath, opts = {}) {
1062
- const source = opts.source ?? readFileSync4(filePath, "utf-8");
1287
+ const source = opts.source ?? readFileSync3(filePath, "utf-8");
1063
1288
  const loadModule = opts.loadModule ?? defaultLoadModule;
1064
1289
  const sidecarPath = sidecarPathOf3(filePath);
1065
1290
  const wantBody = opts.bodyAccesses !== false;
@@ -1209,7 +1434,7 @@ function safeRealpath(p) {
1209
1434
  return realpathSync(p);
1210
1435
  } catch {
1211
1436
  try {
1212
- return join2(realpathSync(dirname4(p)), basename2(p));
1437
+ return join(realpathSync(dirname3(p)), basename2(p));
1213
1438
  } catch {
1214
1439
  return p;
1215
1440
  }
@@ -1232,17 +1457,17 @@ function writeInterfaceDraft(filePath, draftSource, opts = {}) {
1232
1457
  if (opts.projectDir) {
1233
1458
  const rootReal = safeRealpath(opts.projectDir);
1234
1459
  const draftReal = safeRealpath(draftPath);
1235
- const rel = relative3(rootReal, draftReal);
1236
- if (rel.startsWith("..") || isAbsolute2(rel)) {
1460
+ const rel = relative2(rootReal, draftReal);
1461
+ if (rel.startsWith("..") || isAbsolute(rel)) {
1237
1462
  throw new Error(`draft write refused: outside project root ${opts.projectDir}`);
1238
1463
  }
1239
1464
  }
1240
1465
  const draftable = opts.draftable !== void 0 ? opts.draftable : opts.entries !== void 0 ? isDraftableEntry(opts.entries) : /export\s+const\s+[\p{ID_Start}$_][\p{ID_Continue}$]*\s*=/u.test(draftSource);
1241
- const prev = existsSync4(draftPath) ? readFileSync4(draftPath, "utf-8") : void 0;
1466
+ const prev = existsSync3(draftPath) ? readFileSync3(draftPath, "utf-8") : void 0;
1242
1467
  const changed = prev !== draftSource;
1243
1468
  const written = !opts.dryRun && changed && draftable;
1244
1469
  if (written) {
1245
- writeFileSync3(draftPath, draftSource, "utf-8");
1470
+ writeFileSync2(draftPath, draftSource, "utf-8");
1246
1471
  }
1247
1472
  return {
1248
1473
  draftPath,
@@ -1288,10 +1513,10 @@ function formatDraftSummary(sourceRel, draftRel, result, write) {
1288
1513
  return lines;
1289
1514
  }
1290
1515
 
1291
- // src/interface-derivation-derive.ts
1292
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1293
- import { basename as basename3, dirname as dirname5, relative as relative4, resolve as resolve3 } from "path";
1294
- import { parse as parse2 } from "@nudojs/parser";
1516
+ // src/emit/interface-derivation-derive.ts
1517
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1518
+ import { basename as basename3, dirname as dirname4, relative as relative3, resolve as resolve3 } from "path";
1519
+ import { parse as parse3 } from "@nudojs/parser";
1295
1520
  import {
1296
1521
  constraintToEntryAbs,
1297
1522
  effectiveInterface as effectiveInterface3,
@@ -1313,7 +1538,7 @@ import {
1313
1538
  tagDerivationRoot
1314
1539
  } from "@nudojs/core/internal";
1315
1540
 
1316
- // src/interface-derivation-project.ts
1541
+ // src/emit/interface-derivation-project.ts
1317
1542
  import { formatConstraint as formatConstraint4, joinThenProject as joinThenProject3 } from "@nudojs/core";
1318
1543
  import { derivationChain, getDerivation, projectDerivationDsl } from "@nudojs/core/internal";
1319
1544
  function projectParamSlot(absList, paramName) {
@@ -1448,12 +1673,12 @@ function projectReturnRelativeToParams(node, params, paramNames) {
1448
1673
  return void 0;
1449
1674
  }
1450
1675
 
1451
- // src/interface-derivation-derive.ts
1676
+ // src/emit/interface-derivation-derive.ts
1452
1677
  function extractFnConstraintSources(sidecarSrc, fnName) {
1453
1678
  const out = { params: {} };
1454
1679
  let ast;
1455
1680
  try {
1456
- ast = parse2(sidecarSrc);
1681
+ ast = parse3(sidecarSrc);
1457
1682
  } catch {
1458
1683
  return out;
1459
1684
  }
@@ -1524,7 +1749,7 @@ function extractFnConstraintSources(sidecarSrc, fnName) {
1524
1749
  }
1525
1750
  function functionParamNames(source, fnName) {
1526
1751
  try {
1527
- const ast = parse2(source);
1752
+ const ast = parse3(source);
1528
1753
  if (fnName.includes(".")) {
1529
1754
  const [clsName, methodName] = fnName.split(".", 2);
1530
1755
  for (const stmt of ast.program.body) {
@@ -1585,8 +1810,8 @@ function topLevelFnNames(source) {
1585
1810
  function importLocalMap(source, fromFile) {
1586
1811
  const out = /* @__PURE__ */ new Map();
1587
1812
  try {
1588
- const ast = parse2(source);
1589
- const base = dirname5(resolve3(fromFile));
1813
+ const ast = parse3(source);
1814
+ const base = dirname4(resolve3(fromFile));
1590
1815
  for (const stmt of ast.program.body) {
1591
1816
  if (stmt.type !== "ImportDeclaration") continue;
1592
1817
  const spec = stmt.source.value;
@@ -1594,7 +1819,7 @@ function importLocalMap(source, fromFile) {
1594
1819
  const raw = resolve3(base, spec);
1595
1820
  let modulePath = null;
1596
1821
  for (const cand of [raw, `${raw}.js`, `${raw}.ts`, `${raw}.mjs`]) {
1597
- if (existsSync5(cand)) {
1822
+ if (existsSync4(cand)) {
1598
1823
  modulePath = cand;
1599
1824
  break;
1600
1825
  }
@@ -1613,12 +1838,12 @@ function importLocalMap(source, fromFile) {
1613
1838
  }
1614
1839
  function deriveFromRoot(filePath, opts = {}) {
1615
1840
  const abs = resolve3(filePath);
1616
- const source = readFileSync5(abs, "utf-8");
1841
+ const source = readFileSync4(abs, "utf-8");
1617
1842
  const since = interfaceDiagCount3();
1618
- const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(dirname5(abs))?.config).autoBind;
1843
+ const autoBind = opts.autoBind ?? interfaceConfig(findProjectConfig(dirname4(abs))?.config).autoBind;
1619
1844
  const loadModule = opts.loadModule ?? defaultLoadModule;
1620
1845
  const sidecarPath = sidecarPathOf4(abs);
1621
- const sidecarSrc = existsSync5(sidecarPath) && !isNodeModulesPath2(sidecarPath) ? readFileSync5(sidecarPath, "utf-8") : "";
1846
+ const sidecarSrc = existsSync4(sidecarPath) && !isNodeModulesPath2(sidecarPath) ? readFileSync4(sidecarPath, "utf-8") : "";
1622
1847
  const fnNames = topLevelFnNames(source);
1623
1848
  const roots = [];
1624
1849
  const plans = [];
@@ -1659,7 +1884,7 @@ function deriveFromRoot(filePath, opts = {}) {
1659
1884
  const wanted = opts.fnNames && opts.fnNames.length > 0 ? new Set(opts.fnNames) : void 0;
1660
1885
  const derived = [];
1661
1886
  for (const plan of plans) {
1662
- const relRoot = relative4(process.cwd(), abs);
1887
+ const relRoot = relative3(process.cwd(), abs);
1663
1888
  const label = `${relRoot === "" || relRoot.startsWith("..") ? basename3(abs) : relRoot}:${plan.fnName}`;
1664
1889
  const rows = deriveOneRoot(plan, source, abs, modules, importLocals, label);
1665
1890
  for (const row of rows) {
@@ -1729,7 +1954,7 @@ function deriveOneRoot(plan, source, file, modules, importLocals, label) {
1729
1954
  targetFile = file;
1730
1955
  }
1731
1956
  if (isNodeModulesPath2(targetFile)) continue;
1732
- if (!existsSync5(targetFile)) continue;
1957
+ if (!existsSync4(targetFile)) continue;
1733
1958
  const key = `${targetFile}::${targetExport}`;
1734
1959
  let agg = byCallee.get(key);
1735
1960
  if (!agg) {
@@ -1743,7 +1968,7 @@ function deriveOneRoot(plan, source, file, modules, importLocals, label) {
1743
1968
  for (const agg of byCallee.values()) {
1744
1969
  let targetSource;
1745
1970
  try {
1746
- targetSource = readFileSync5(agg.targetFile, "utf-8");
1971
+ targetSource = readFileSync4(agg.targetFile, "utf-8");
1747
1972
  } catch {
1748
1973
  continue;
1749
1974
  }
@@ -1822,11 +2047,11 @@ function deriveOneRoot(plan, source, file, modules, importLocals, label) {
1822
2047
  }
1823
2048
  }
1824
2049
 
1825
- // src/interface-derivation-rewrite.ts
1826
- import { relative as relative5, resolve as resolve4 } from "path";
2050
+ // src/emit/interface-derivation-rewrite.ts
2051
+ import { relative as relative4, resolve as resolve4 } from "path";
1827
2052
  function resolveRelImport(fromSpec, fromDir, targetDir) {
1828
2053
  const abs = resolve4(fromDir, fromSpec);
1829
- let rel = relative5(targetDir, abs);
2054
+ let rel = relative4(targetDir, abs);
1830
2055
  if (!rel.startsWith(".")) rel = `./${rel}`;
1831
2056
  return rel.split("\\").join("/");
1832
2057
  }
@@ -1943,9 +2168,9 @@ function formatDerivedSection(row, opts) {
1943
2168
  return { text: lines.join("\n"), usedNames: [...usedNames] };
1944
2169
  }
1945
2170
 
1946
- // src/interface-derivation-emit.ts
1947
- import { existsSync as existsSync6, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
1948
- import { basename as basename4, dirname as dirname6, relative as relative6, resolve as resolve5 } from "path";
2171
+ // src/emit/interface-derivation-emit.ts
2172
+ import { existsSync as existsSync5, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
2173
+ import { basename as basename4, dirname as dirname5, relative as relative5, resolve as resolve5 } from "path";
1949
2174
  import {
1950
2175
  execNudoModule as execNudoModule2,
1951
2176
  isNodeModulesPath as isNodeModulesPath3,
@@ -2011,10 +2236,10 @@ function emitDerivedFromRoot(rootFile, opts) {
2011
2236
  if (!derive.hasRoot) {
2012
2237
  return { sidecars: [], hasRoot: false, roots: [], entryOnly: true };
2013
2238
  }
2014
- const proj = findProjectConfig(dirname6(abs));
2239
+ const proj = findProjectConfig(dirname5(abs));
2015
2240
  const allow = interfaceConfig(proj?.config).emit;
2016
2241
  const projectDir = proj?.projectDir;
2017
- const rootSidecarDir = dirname6(sidecarPathOf5(abs));
2242
+ const rootSidecarDir = dirname5(sidecarPathOf5(abs));
2018
2243
  const wanted = opts.fnNames && opts.fnNames.length > 0 ? new Set(opts.fnNames) : void 0;
2019
2244
  const result = {
2020
2245
  sidecars: [],
@@ -2033,8 +2258,8 @@ function emitDerivedFromRoot(rootFile, opts) {
2033
2258
  }
2034
2259
  for (const [sidecarPath, rows0] of bySidecar) {
2035
2260
  const targetFile = rows0[0].file;
2036
- const targetSidecarDir = dirname6(sidecarPath);
2037
- const prevSrc = existsSync6(sidecarPath) ? readFileSync6(sidecarPath, "utf-8") : "";
2261
+ const targetSidecarDir = dirname5(sidecarPath);
2262
+ const prevSrc = existsSync5(sidecarPath) ? readFileSync5(sidecarPath, "utf-8") : "";
2038
2263
  const rows = opts.refreshExistingOnly ? rows0.filter((r) => findGeneratedSectionText(prevSrc, r.fn) !== void 0) : rows0;
2039
2264
  if (rows.length === 0) continue;
2040
2265
  const handwritten = handwrittenNames(prevSrc);
@@ -2053,7 +2278,7 @@ function emitDerivedFromRoot(rootFile, opts) {
2053
2278
  issues.push({
2054
2279
  code: "nudo:interface-name-clash",
2055
2280
  severity: "error",
2056
- message: `sidecar already has a handwritten binding '${row.fn}' (${relative6(process.cwd(), sidecarPath) || sidecarPath}); handwritten wins \u2014 skipping emit`
2281
+ message: `sidecar already has a handwritten binding '${row.fn}' (${relative5(process.cwd(), sidecarPath) || sidecarPath}); handwritten wins \u2014 skipping emit`
2057
2282
  });
2058
2283
  anySkip = "name-clash";
2059
2284
  continue;
@@ -2081,7 +2306,7 @@ function emitDerivedFromRoot(rootFile, opts) {
2081
2306
  continue;
2082
2307
  }
2083
2308
  for (const n of body.usedNames) taken.add(n);
2084
- const srcRel = relative6(targetSidecarDir, targetFile) || basename4(targetFile);
2309
+ const srcRel = relative5(targetSidecarDir, targetFile) || basename4(targetFile);
2085
2310
  const section = [
2086
2311
  DERIVED_HEADER,
2087
2312
  `// source: ${srcRel}:${row.fn}`,
@@ -2131,7 +2356,7 @@ function emitDerivedFromRoot(rootFile, opts) {
2131
2356
  issues.push({
2132
2357
  code: "nudo:interface-not-projectable",
2133
2358
  severity: "error",
2134
- message: `assembled sidecar failed round-trip (${relative6(process.cwd(), sidecarPath) || sidecarPath}); refusing to write`
2359
+ message: `assembled sidecar failed round-trip (${relative5(process.cwd(), sidecarPath) || sidecarPath}); refusing to write`
2135
2360
  });
2136
2361
  anySkip = "not-projectable";
2137
2362
  result.sidecars.push({
@@ -2146,15 +2371,15 @@ function emitDerivedFromRoot(rootFile, opts) {
2146
2371
  continue;
2147
2372
  }
2148
2373
  const changed = finalContent !== prevSrc;
2149
- const diff = changed ? unifiedDiff(prevSrc, finalContent, relative6(process.cwd(), sidecarPath) || sidecarPath) : void 0;
2374
+ const diff = changed ? unifiedDiff(prevSrc, finalContent, relative5(process.cwd(), sidecarPath) || sidecarPath) : void 0;
2150
2375
  if (changed && !opts.dryRun) {
2151
2376
  const tmp = `${sidecarPath}.tmp-${process.pid}-${Math.random().toString(16).slice(2, 10)}`;
2152
2377
  try {
2153
- writeFileSync4(tmp, finalContent, "utf-8");
2378
+ writeFileSync3(tmp, finalContent, "utf-8");
2154
2379
  renameSync2(tmp, sidecarPath);
2155
2380
  } catch (e) {
2156
2381
  try {
2157
- if (existsSync6(tmp)) unlinkSync2(tmp);
2382
+ if (existsSync5(tmp)) unlinkSync2(tmp);
2158
2383
  } catch {
2159
2384
  }
2160
2385
  throw e;
@@ -2305,115 +2530,1218 @@ function derivedRoundTrips(text, fn, fromFile, loadModule) {
2305
2530
  }
2306
2531
  }
2307
2532
 
2308
- // src/what-if.ts
2309
- import { parse as parse3 } from "@nudojs/parser";
2310
- function splitTopLevelUnion(expr) {
2311
- const members = [];
2312
- let start = 0;
2313
- let depth = 0;
2314
- for (let i = 0; i < expr.length; i++) {
2315
- const c = expr[i];
2316
- if (c === "(" || c === "[" || c === "{") depth++;
2317
- else if (c === ")" || c === "]" || c === "}") depth--;
2318
- else if (c === "|" && depth === 0) {
2319
- members.push(expr.slice(start, i));
2320
- start = i + 1;
2321
- }
2322
- }
2323
- members.push(expr.slice(start));
2324
- return members.map((m) => m.trim()).filter(Boolean);
2325
- }
2326
- function typeExprToDirective(expr) {
2327
- const members = splitTopLevelUnion(expr);
2328
- if (members.length === 0) return "any()";
2329
- const mapped = members.map((m) => {
2330
- if (m.startsWith("T.")) return "any()";
2331
- if (m === "number" || m === "string" || m === "boolean") return `${m}()`;
2332
- if (m === "unknown" || m === "any") return "any()";
2333
- if (m === "null" || m === "undefined" || m === "true" || m === "false") return m;
2334
- if (/^-?\d+(\.\d+)?$/.test(m)) return m;
2335
- if (/^["']/.test(m)) return m;
2336
- if (/[([{]|=>/.test(m) && !m.startsWith("T.")) return m;
2337
- if (/^(number|string|boolean|any|array|shape|lit|union|fn)\s*\(/.test(m)) return m;
2338
- return "any()";
2339
- });
2340
- return mapped.length === 1 ? mapped[0] : `union(${mapped.join(", ")})`;
2533
+ // src/emit/dts-generator.ts
2534
+ import { joinAbs, litValue, abs as makeAbs, collectAbsFreeVars } from "@nudojs/core";
2535
+ import { isTemplateLike, templatePartsOf } from "@nudojs/core/internal";
2536
+ function tsTypeParamName(id) {
2537
+ let n = id.replace(/[^A-Za-z0-9_$]/g, "_");
2538
+ if (!/^[A-Za-z_$]/.test(n)) n = `T_${n}`;
2539
+ if (n.length === 0) n = "T";
2540
+ return n;
2541
+ }
2542
+ function wrapComplexAbs(a, typeVars) {
2543
+ const ts = absToTSType(a, typeVars);
2544
+ if (a.shape.k === "sum" || a.shape.k === "fn") return `(${ts})`;
2545
+ return ts;
2546
+ }
2547
+ function wrapUnionMember(a, typeVars) {
2548
+ const ts = absToTSType(a, typeVars);
2549
+ if (a.shape.k === "fn") return `(${ts})`;
2550
+ return ts;
2551
+ }
2552
+ var TS_PARAM_RESERVED = /* @__PURE__ */ new Set([
2553
+ "break",
2554
+ "case",
2555
+ "catch",
2556
+ "class",
2557
+ "const",
2558
+ "continue",
2559
+ "debugger",
2560
+ "default",
2561
+ "delete",
2562
+ "do",
2563
+ "else",
2564
+ "enum",
2565
+ "export",
2566
+ "extends",
2567
+ "false",
2568
+ "finally",
2569
+ "for",
2570
+ "function",
2571
+ "if",
2572
+ "import",
2573
+ "in",
2574
+ "instanceof",
2575
+ "new",
2576
+ "null",
2577
+ "return",
2578
+ "super",
2579
+ "switch",
2580
+ "this",
2581
+ "throw",
2582
+ "true",
2583
+ "try",
2584
+ "typeof",
2585
+ "var",
2586
+ "void",
2587
+ "while",
2588
+ "with",
2589
+ "yield",
2590
+ "let",
2591
+ "static",
2592
+ "await",
2593
+ "implements",
2594
+ "interface",
2595
+ "package",
2596
+ "private",
2597
+ "protected",
2598
+ "public",
2599
+ "arguments",
2600
+ "eval",
2601
+ "constructor"
2602
+ ]);
2603
+ function isTsIdent(name) {
2604
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !TS_PARAM_RESERVED.has(name);
2605
+ }
2606
+ function sanitizeParamName(name, index) {
2607
+ if (name.startsWith("...")) {
2608
+ const rest = name.slice(3);
2609
+ if (isTsIdent(rest)) return name;
2610
+ return `...arg${index}`;
2611
+ }
2612
+ if (isTsIdent(name)) return name;
2613
+ return `arg${index}`;
2614
+ }
2615
+ function formatPropKey(k) {
2616
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k)) return k;
2617
+ if (/^\d+$/.test(k)) return k;
2618
+ return JSON.stringify(k);
2619
+ }
2620
+ function absToTSType(a, typeVars) {
2621
+ if (a.shape.k === "any" && a.term?.op === "var" && typeVars) {
2622
+ const mapped = typeVars.get(a.term.id);
2623
+ if (mapped) return mapped;
2624
+ }
2625
+ if (a.shape.k === "arr" && a.shape.element.shape.k === "any" && a.shape.element.term?.op === "var" && typeVars) {
2626
+ const mapped = typeVars.get(a.shape.element.term.id);
2627
+ if (mapped) return `${mapped}[]`;
2628
+ }
2629
+ if (a.term?.op === "lit") {
2630
+ const v = a.term.value;
2631
+ if (v === null) return "null";
2632
+ if (v === void 0) return "undefined";
2633
+ if (a.shape.k === "prim") {
2634
+ if (typeof v === "string") return JSON.stringify(v);
2635
+ if (typeof v === "boolean") return String(v);
2636
+ if (typeof v === "number") return String(v);
2637
+ }
2638
+ }
2639
+ if (a.shape.k === "prim" && a.shape.type === "string" && isTemplateLike(a)) {
2640
+ const parts = templatePartsOf(a);
2641
+ const inner = parts.map((p) => {
2642
+ const lv = litValue(p);
2643
+ if (typeof lv === "string") return lv;
2644
+ return `\${${absToTSType(p, typeVars)}}`;
2645
+ }).join("");
2646
+ return `\`${inner}\``;
2647
+ }
2648
+ switch (a.shape.k) {
2649
+ case "never":
2650
+ return "never";
2651
+ case "unknown":
2652
+ case "any":
2653
+ return "unknown";
2654
+ case "prim":
2655
+ return a.shape.type;
2656
+ case "obj": {
2657
+ const entries = Object.entries(a.shape.slots).map(([k, slot]) => {
2658
+ const inner = absToTSType(slot.value, typeVars);
2659
+ if (slot.optional) {
2660
+ const t = wrapComplexAbs(slot.value, typeVars);
2661
+ return `${formatPropKey(k)}: ${t} | undefined`;
2662
+ }
2663
+ return `${formatPropKey(k)}: ${inner}`;
2664
+ });
2665
+ if (entries.length === 0) return "{}";
2666
+ return `{ ${entries.join("; ")} }`;
2667
+ }
2668
+ case "arr":
2669
+ return `${wrapComplexAbs(a.shape.element, typeVars)}[]`;
2670
+ case "tuple": {
2671
+ const parts = a.shape.elements.map((e) => absToTSType(e, typeVars));
2672
+ if (a.shape.rest) {
2673
+ const rest = a.shape.rest;
2674
+ const restTs = rest.shape.k === "arr" ? absToTSType(rest, typeVars) : `${absToTSType(rest, typeVars)}[]`;
2675
+ parts.push(`...${restTs}`);
2676
+ }
2677
+ return `[${parts.join(", ")}]`;
2678
+ }
2679
+ case "fn": {
2680
+ const paramTypes = a.shape.paramTypes;
2681
+ const params = a.shape.params.map((p, i) => {
2682
+ const isRest = p.startsWith("...");
2683
+ const name = sanitizeParamName(p, i);
2684
+ const pt = paramTypes?.[i];
2685
+ let typeStr;
2686
+ if (pt) typeStr = absToTSType(pt, typeVars);
2687
+ else if (isRest) typeStr = "unknown[]";
2688
+ else typeStr = "unknown";
2689
+ return `${name}: ${typeStr}`;
2690
+ }).join(", ");
2691
+ const ret = a.shape.returnType ? absToTSType(a.shape.returnType, typeVars) : "unknown";
2692
+ return `(${params}) => ${ret}`;
2693
+ }
2694
+ case "brand":
2695
+ return isTsIdent(a.shape.name) || /^[A-Z][A-Za-z0-9_$]*$/.test(a.shape.name) ? a.shape.name : "unknown";
2696
+ case "eff":
2697
+ if (a.shape.eff === "promise")
2698
+ return `Promise<${absToTSType(a.shape.inner, typeVars)}>`;
2699
+ return absToTSType(a.shape.inner, typeVars);
2700
+ case "sum": {
2701
+ const parts = a.shape.members.map((m) => wrapUnionMember(m, typeVars)).filter((p) => p !== "never");
2702
+ const uniq = [...new Set(parts)];
2703
+ if (uniq.length === 0) return "never";
2704
+ if (uniq.length === 1) return uniq[0];
2705
+ return uniq.join(" | ");
2706
+ }
2707
+ default:
2708
+ return "unknown";
2709
+ }
2710
+ }
2711
+ function caseArgAbs(c, i) {
2712
+ return c.argAbs[i];
2713
+ }
2714
+ function caseResultAbs(c) {
2715
+ return c.abs;
2716
+ }
2717
+ function widenParamAbs(a) {
2718
+ const s = a.shape;
2719
+ switch (s.k) {
2720
+ case "prim":
2721
+ return makeAbs(s, void 0, void 0, "exact");
2722
+ case "tuple": {
2723
+ const widened = s.elements.map(widenParamAbs);
2724
+ const first = widened[0];
2725
+ if (first && widened.length > 0 && widened.every((el) => absToTSType(el) === absToTSType(first))) {
2726
+ return makeAbs({ k: "arr", element: first }, void 0, void 0, "exact");
2727
+ }
2728
+ return makeAbs({ k: "tuple", elements: widened }, void 0, void 0, "exact");
2729
+ }
2730
+ case "arr":
2731
+ return makeAbs({ k: "arr", element: widenParamAbs(s.element) }, void 0, void 0, "exact");
2732
+ case "obj": {
2733
+ const slots = {};
2734
+ for (const [k, slot] of Object.entries(s.slots)) {
2735
+ slots[k] = {
2736
+ value: widenParamAbs(slot.value),
2737
+ ...slot.optional ? { optional: true } : {}
2738
+ };
2739
+ }
2740
+ return makeAbs({ k: "obj", slots }, void 0, void 0, "exact");
2741
+ }
2742
+ case "eff":
2743
+ return makeAbs(
2744
+ { k: "eff", eff: s.eff, inner: widenParamAbs(s.inner) },
2745
+ void 0,
2746
+ void 0,
2747
+ "exact"
2748
+ );
2749
+ case "brand":
2750
+ return makeAbs(
2751
+ { k: "brand", name: s.name, shape: widenParamAbs(s.shape) },
2752
+ void 0,
2753
+ void 0,
2754
+ "exact"
2755
+ );
2756
+ case "sum":
2757
+ return makeAbs(
2758
+ { k: "sum", members: s.members.map(widenParamAbs) },
2759
+ void 0,
2760
+ void 0,
2761
+ "exact"
2762
+ );
2763
+ default:
2764
+ return a;
2765
+ }
2341
2766
  }
2342
- function declaredNames(stmt, out) {
2343
- if (stmt.type === "FunctionDeclaration" && stmt.id) out.add(stmt.id.name);
2344
- else if (stmt.type === "ClassDeclaration" && stmt.id) out.add(stmt.id.name);
2345
- else if (stmt.type === "VariableDeclaration") {
2346
- for (const decl of stmt.declarations) {
2347
- if (decl.id?.type === "Identifier") out.add(decl.id.name);
2767
+ function widenTopLevelAbs(a) {
2768
+ if (a.shape.k === "sum") {
2769
+ return makeAbs(
2770
+ { k: "sum", members: a.shape.members.map(widenTopLevelAbs) },
2771
+ void 0,
2772
+ void 0,
2773
+ a.conf
2774
+ );
2775
+ }
2776
+ return widenLiteralToPrimAbs(a);
2777
+ }
2778
+ function widenLiteralToPrimAbs(a) {
2779
+ if (a.term?.op === "lit" && a.term.value === null) return a;
2780
+ if (a.term?.op === "lit" && a.term.value === void 0) return a;
2781
+ if (a.term?.op === "lit" && a.shape.k === "prim") {
2782
+ const v = a.term.value;
2783
+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean" || typeof v === "bigint") {
2784
+ return makeAbs(a.shape, void 0, void 0, a.conf);
2348
2785
  }
2349
- } else if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
2350
- declaredNames(stmt.declaration, out);
2351
2786
  }
2787
+ return a;
2352
2788
  }
2353
- function injectBindings(source, bindings) {
2354
- const applied = [];
2355
- const unapplied = [];
2356
- if (bindings.length === 0) return { source, applied, unapplied };
2357
- const ast = parse3(source);
2358
- const declLines = /* @__PURE__ */ new Map();
2359
- for (const stmt of ast.program.body) {
2360
- const names = /* @__PURE__ */ new Set();
2361
- declaredNames(stmt, names);
2362
- if (names.size === 0 || !stmt.loc) continue;
2363
- const anchorLine = stmt.leadingComments?.[0]?.loc?.start.line ?? stmt.loc.start.line;
2364
- for (const name of names) {
2365
- if (!declLines.has(name)) declLines.set(name, anchorLine);
2366
- }
2367
- }
2368
- const byLine = /* @__PURE__ */ new Map();
2369
- for (const binding of bindings) {
2370
- const line = declLines.get(binding.name);
2371
- if (line === void 0) {
2372
- unapplied.push(binding.name);
2789
+ function paramTypeFromAbs(members) {
2790
+ if (members.length === 0) return "unknown";
2791
+ let joined;
2792
+ try {
2793
+ joined = members.reduce((a, b) => joinAbs(a, b));
2794
+ } catch {
2795
+ joined = members[0];
2796
+ }
2797
+ return absToTSType(widenParamAbs(joined));
2798
+ }
2799
+ function returnAbsOf(fn) {
2800
+ if (fn.combinedAbs) return fn.combinedAbs;
2801
+ const results = fn.cases.map(caseResultAbs);
2802
+ if (results.length > 0) {
2803
+ try {
2804
+ return results.reduce((a, b) => joinAbs(a, b));
2805
+ } catch {
2806
+ }
2807
+ return results[0];
2808
+ }
2809
+ return void 0;
2810
+ }
2811
+ function getParamName(fn, index) {
2812
+ if (fn.paramNames && fn.paramNames[index]) {
2813
+ return fn.paramNames[index];
2814
+ }
2815
+ return `arg${index}`;
2816
+ }
2817
+ function computeMainSignature(fn) {
2818
+ const arity = Math.max(...fn.cases.map((c) => c.argAbs.length));
2819
+ const minArity = Math.min(...fn.cases.map((c) => c.argAbs.length));
2820
+ const params = [];
2821
+ const paramNames = [];
2822
+ const paramTypes = [];
2823
+ const usedNames = /* @__PURE__ */ new Set();
2824
+ for (let i = 0; i < arity; i++) {
2825
+ const members = [];
2826
+ for (const c of fn.cases) {
2827
+ if (i >= c.argAbs.length) continue;
2828
+ const a = caseArgAbs(c, i);
2829
+ if (a) members.push(a);
2830
+ }
2831
+ const typeStr = paramTypeFromAbs(members);
2832
+ let name = getParamName(fn, i);
2833
+ const isRest = name.startsWith("...");
2834
+ const bare = isRest ? name.slice(3) : name;
2835
+ if (!isTsIdent(bare)) {
2836
+ name = isRest ? `...arg${i}` : `arg${i}`;
2837
+ }
2838
+ if (usedNames.has(name)) {
2839
+ let n = 2;
2840
+ while (usedNames.has(`${name}${n}`)) n++;
2841
+ name = `${name}${n}`;
2842
+ }
2843
+ usedNames.add(name);
2844
+ const optional = i >= minArity && !isRest;
2845
+ params.push(optional ? `${name}?: ${typeStr}` : `${name}: ${typeStr}`);
2846
+ paramNames.push(name);
2847
+ paramTypes.push(typeStr);
2848
+ }
2849
+ const retAbs = returnAbsOf(fn);
2850
+ const returnType = retAbs ? absToTSType(widenTopLevelAbs(retAbs)) : "unknown";
2851
+ return { params, paramNames, paramTypes, returnType };
2852
+ }
2853
+ function generateJSDoc(fn, sig) {
2854
+ if (fn.cases.length === 0) return "";
2855
+ const lines = ["/**"];
2856
+ for (const c of fn.cases) {
2857
+ const preciseDiffers = c.argAbs.length !== sig.paramTypes.length || c.argAbs.some((a, i) => absToTSType(a) !== sig.paramTypes[i]) || absToTSType(c.abs) !== sig.returnType;
2858
+ if (!preciseDiffers) continue;
2859
+ const argsStr = c.argAbs.map((a) => absToTSType(a)).join(", ");
2860
+ lines.push(` * Case: ${c.name} (${argsStr}) => ${absToTSType(c.abs)}`);
2861
+ }
2862
+ for (let i = 0; i < sig.paramTypes.length; i++) {
2863
+ lines.push(` * @param ${sig.paramNames[i]} - ${sig.paramTypes[i]}`);
2864
+ }
2865
+ lines.push(` * @returns ${sig.returnType}`);
2866
+ lines.push(" */");
2867
+ return lines.join("\n");
2868
+ }
2869
+ function computeHofSignature(fn) {
2870
+ const hof = fn.hof;
2871
+ if (!hof) return void 0;
2872
+ const byParam = /* @__PURE__ */ new Map();
2873
+ for (const s of hof.entryShapes ?? []) byParam.set(s.param, s.abs);
2874
+ for (const r of hof.fnRels ?? []) byParam.set(r.param, r.abs);
2875
+ if (byParam.size === 0 && !hof.symbolic) return void 0;
2876
+ if (fn.cases.length > 0) {
2877
+ for (const [param] of byParam) {
2878
+ const idx = fn.paramNames.indexOf(param);
2879
+ if (idx < 0) continue;
2880
+ const caseType = paramTypeFromAbs(
2881
+ fn.cases.map((c) => c.argAbs[idx]).filter((a) => !!a)
2882
+ );
2883
+ if (caseType !== "unknown" && caseType !== "unknown[]") {
2884
+ return void 0;
2885
+ }
2886
+ }
2887
+ }
2888
+ const arity = Math.max(
2889
+ fn.paramNames.length,
2890
+ ...[...byParam.keys()].map((p) => fn.paramNames.indexOf(p) + 1),
2891
+ 0
2892
+ );
2893
+ if (arity === 0 && !hof.symbolic) return void 0;
2894
+ const free = /* @__PURE__ */ new Set();
2895
+ for (const a of byParam.values()) {
2896
+ for (const id of collectAbsFreeVars(a)) free.add(id);
2897
+ }
2898
+ const retAbs = hof.symbolic ?? fn.combinedAbs;
2899
+ if (retAbs) {
2900
+ for (const id of collectAbsFreeVars(retAbs)) free.add(id);
2901
+ }
2902
+ if (free.size === 0) return void 0;
2903
+ const typeVars = /* @__PURE__ */ new Map();
2904
+ const used = /* @__PURE__ */ new Set();
2905
+ const typeParams = [];
2906
+ for (const id of [...free].sort()) {
2907
+ let n = tsTypeParamName(id);
2908
+ if (used.has(n) || TS_PARAM_RESERVED.has(n)) {
2909
+ let i = 2;
2910
+ while (used.has(`${n}${i}`)) i++;
2911
+ n = `${n}${i}`;
2912
+ }
2913
+ used.add(n);
2914
+ typeVars.set(id, n);
2915
+ typeParams.push(n);
2916
+ }
2917
+ const params = [];
2918
+ const paramNames = [];
2919
+ const paramTypes = [];
2920
+ const usedNames = /* @__PURE__ */ new Set();
2921
+ for (let i = 0; i < arity; i++) {
2922
+ const rawName = getParamName(fn, i);
2923
+ const isRest = rawName.startsWith("...");
2924
+ let name = sanitizeParamName(rawName, i);
2925
+ if (usedNames.has(name)) {
2926
+ let n = 2;
2927
+ while (usedNames.has(`${name}${n}`)) n++;
2928
+ name = isRest && name.startsWith("...") ? `...${name.slice(3)}${n}` : `${name}${n}`;
2929
+ }
2930
+ usedNames.add(name);
2931
+ const promoted = byParam.get(fn.paramNames[i] ?? rawName) ?? byParam.get(rawName);
2932
+ const typeStr = promoted ? absToTSType(promoted, typeVars) : isRest ? "unknown[]" : "unknown";
2933
+ params.push(`${name}: ${typeStr}`);
2934
+ paramNames.push(name);
2935
+ paramTypes.push(typeStr);
2936
+ }
2937
+ const returnType = retAbs ? absToTSType(retAbs, typeVars) : "unknown";
2938
+ return { typeParams, params, paramNames, paramTypes, returnType };
2939
+ }
2940
+ function generateFunctionDtsLines(fn) {
2941
+ let emitFn = fn;
2942
+ if (fn.noDeclaration) {
2943
+ const m = /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(fn.name);
2944
+ if (!m) return [];
2945
+ emitFn = { ...fn, name: `${m[1]}_${m[2]}`, noDeclaration: false };
2946
+ }
2947
+ fn = emitFn;
2948
+ const hofSig = computeHofSignature(fn);
2949
+ if (hofSig) {
2950
+ const lines2 = [];
2951
+ if (fn.cases.length > 0) {
2952
+ const jsdoc2 = generateJSDoc(fn, {
2953
+ params: hofSig.params,
2954
+ paramNames: hofSig.paramNames,
2955
+ paramTypes: hofSig.paramTypes,
2956
+ returnType: hofSig.returnType
2957
+ });
2958
+ if (jsdoc2) lines2.push(jsdoc2);
2959
+ }
2960
+ const tparams = hofSig.typeParams.length > 0 ? `<${hofSig.typeParams.join(", ")}>` : "";
2961
+ lines2.push(
2962
+ `export declare function ${fn.name}${tparams}(${hofSig.params.join(", ")}): ${hofSig.returnType};`
2963
+ );
2964
+ return lines2;
2965
+ }
2966
+ if (fn.cases.length === 0) {
2967
+ const retAbs = fn.combinedAbs;
2968
+ if (retAbs) {
2969
+ return [
2970
+ `export declare function ${fn.name}(...args: unknown[]): ${absToTSType(retAbs)};`
2971
+ ];
2972
+ }
2973
+ return [];
2974
+ }
2975
+ const sig = computeMainSignature(fn);
2976
+ const jsdoc = generateJSDoc(fn, sig);
2977
+ const lines = [];
2978
+ if (jsdoc) lines.push(jsdoc);
2979
+ lines.push(`export declare function ${fn.name}(${sig.params.join(", ")}): ${sig.returnType};`);
2980
+ return lines;
2981
+ }
2982
+ function generateDts(result) {
2983
+ const lines = [];
2984
+ for (const fn of result.functions) {
2985
+ lines.push(...generateFunctionDtsLines(fn));
2986
+ }
2987
+ return lines.join("\n") + "\n";
2988
+ }
2989
+
2990
+ // src/emit/schema-generator.ts
2991
+ import { absToConstraint, isIntFlag as isIntFlag2, predToString } from "@nudojs/core";
2992
+ function predLeaves(p) {
2993
+ if (!p || p.op === "true") return [];
2994
+ if (p.op === "and") {
2995
+ const out = [];
2996
+ for (const x of p.args) {
2997
+ const sub = predLeaves(x);
2998
+ if (sub === "unexpressible") return "unexpressible";
2999
+ out.push(...sub);
3000
+ }
3001
+ return out;
3002
+ }
3003
+ if (p.op === "or" || p.op === "not" || p.op === "false") return "unexpressible";
3004
+ return [p];
3005
+ }
3006
+ function isSelfVar(t) {
3007
+ return !!t && t.op === "var";
3008
+ }
3009
+ function litOf(t) {
3010
+ return t && t.op === "lit" ? t.value : void 0;
3011
+ }
3012
+ function eqLitValue(p) {
3013
+ if (p.op !== "eq") return "unanchored";
3014
+ const aLit = litOf(p.a);
3015
+ const bLit = litOf(p.b);
3016
+ if (aLit !== void 0 && (isSelfVar(p.b) || p.b.op === "app")) return aLit;
3017
+ if (bLit !== void 0 && (isSelfVar(p.a) || p.a.op === "app")) return bLit;
3018
+ if (aLit !== void 0 && bLit !== void 0) return aLit === bLit ? aLit : "unanchored";
3019
+ return "unanchored";
3020
+ }
3021
+ function isIntModOne(p) {
3022
+ if (p.op !== "eq") return false;
3023
+ const zero = (t) => !!t && t.op === "lit" && t.value === 0;
3024
+ const isModOne = (t) => !!t && t.op === "app" && t.fn === "%" && t.args.length === 2 && t.args[1]?.op === "lit" && t.args[1].value === 1 && (isSelfVar(t.args[0]) || t.args[0].op === "app");
3025
+ return isModOne(p.a) && zero(p.b) || isModOne(p.b) && zero(p.a);
3026
+ }
3027
+ function numericBound(p, allowSelfVar) {
3028
+ if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return "drop";
3029
+ const n = litOf(p.b);
3030
+ if (typeof n !== "number") return "drop";
3031
+ if (p.a.op === "app" && p.a.fn === "length") return "skip";
3032
+ if (allowSelfVar && isSelfVar(p.a)) return { op: p.op, n };
3033
+ if (p.a.op === "app" && (p.a.fn === "get" || p.a.fn === "length")) return "skip";
3034
+ return "drop";
3035
+ }
3036
+ function lengthBound(p) {
3037
+ if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
3038
+ if (p.a.op !== "app" || p.a.fn !== "length") return void 0;
3039
+ const n = litOf(p.b);
3040
+ if (typeof n !== "number") return void 0;
3041
+ if (p.op === "ge") return { dir: "min", n: Math.ceil(n) };
3042
+ if (p.op === "gt") return { dir: "min", n: Math.floor(n) + 1 };
3043
+ if (p.op === "le") return { dir: "max", n: Math.floor(n) };
3044
+ return { dir: "max", n: Math.ceil(n) - 1 };
3045
+ }
3046
+ function refinementsFromPreds(preds, opts) {
3047
+ const refinements = [];
3048
+ const dropped = [];
3049
+ let eqLit;
3050
+ for (const p of preds) {
3051
+ if (p.op === "typeof") continue;
3052
+ if (p.op === "eq") {
3053
+ if (opts.kind === "number" && isIntModOne(p)) {
3054
+ if (!refinements.some((r) => r.kind === "int")) refinements.push({ kind: "int" });
3055
+ continue;
3056
+ }
3057
+ const v = eqLitValue(p);
3058
+ if (v === "unanchored") {
3059
+ dropped.push(`pred not projected: ${predToString(p)}`);
3060
+ continue;
3061
+ }
3062
+ if (typeof v === "number" && Number.isNaN(v)) {
3063
+ dropped.push(`pred not projected (NaN): ${predToString(p)}`);
3064
+ continue;
3065
+ }
3066
+ if (eqLit !== void 0 && eqLit !== v) {
3067
+ dropped.push(`conflicting eq preds: ${predToString(p)}`);
3068
+ continue;
3069
+ }
3070
+ eqLit = v;
2373
3071
  continue;
2374
3072
  }
2375
- const group = byLine.get(line) ?? [];
2376
- group.push(binding);
2377
- byLine.set(line, group);
2378
- applied.push(binding.name);
3073
+ if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
3074
+ if (opts.kind === "string") {
3075
+ const lb = lengthBound(p);
3076
+ if (lb) {
3077
+ refinements.push(lb.dir === "min" ? { kind: "strMin", n: lb.n } : { kind: "strMax", n: lb.n });
3078
+ continue;
3079
+ }
3080
+ dropped.push(`pred not projected: ${predToString(p)}`);
3081
+ continue;
3082
+ }
3083
+ const b = numericBound(p, opts.kind === "number" || opts.kind === "other");
3084
+ if (b === "skip") continue;
3085
+ if (b === "drop") {
3086
+ dropped.push(`pred not projected: ${predToString(p)}`);
3087
+ continue;
3088
+ }
3089
+ if (opts.kind === "number" || opts.kind === "other") {
3090
+ refinements.push({ kind: "numBound", op: b.op, n: b.n });
3091
+ continue;
3092
+ }
3093
+ dropped.push(`pred not projected: ${predToString(p)}`);
3094
+ continue;
3095
+ }
3096
+ dropped.push(`pred not projected: ${predToString(p)}`);
2379
3097
  }
2380
- const insertions = [];
2381
- for (const [line, group] of byLine) {
2382
- insertions.push({
2383
- index: line - 1,
2384
- text: `// @nudo:as ${typeExprToDirective(group[0].type)}`
2385
- });
3098
+ return { refinements, ...eqLit !== void 0 ? { eqLit } : {}, dropped };
3099
+ }
3100
+ function constraintToSchemaNode(c) {
3101
+ if (c.fn) return { k: "fn" };
3102
+ if (c.members) {
3103
+ return { k: "union", members: c.members.map(constraintToSchemaNode) };
2386
3104
  }
2387
- insertions.sort((a, b) => b.index - a.index);
2388
- const lines = source.split("\n");
2389
- for (const ins of insertions) lines.splice(ins.index, 0, ins.text);
2390
- return { source: lines.join("\n"), applied, unapplied };
3105
+ if (c.fields) {
3106
+ return {
3107
+ k: "obj",
3108
+ slots: Object.entries(c.fields).map(([key, field]) => ({
3109
+ key,
3110
+ node: constraintToSchemaNode(field.constraint),
3111
+ ...field.optional || field.constraint.isOptional ? { optional: true } : {}
3112
+ }))
3113
+ };
3114
+ }
3115
+ if (c.element) {
3116
+ return { k: "arr", element: constraintToSchemaNode(c.element) };
3117
+ }
3118
+ const preds = c.preds ?? [];
3119
+ const kind = c.prim === "number" ? "number" : c.prim === "string" ? "string" : c.prim === "boolean" ? "boolean" : c.prim ? "other" : "other";
3120
+ const { refinements, eqLit, dropped } = refinementsFromPreds(preds, { kind });
3121
+ if (eqLit !== void 0 && (kind === "number" || kind === "string" || kind === "boolean" || !c.prim)) {
3122
+ return { k: "lit", value: eqLit };
3123
+ }
3124
+ if (isIntFlag2(c) && !refinements.some((r) => r.kind === "int")) {
3125
+ refinements.push({ kind: "int" });
3126
+ }
3127
+ void dropped;
3128
+ if (!c.prim) {
3129
+ if (Object.keys(c.fields ?? {}).length === 0 && !c.element && !c.members) {
3130
+ return { k: "unknown" };
3131
+ }
3132
+ }
3133
+ const type = c.prim === "number" || c.prim === "string" || c.prim === "boolean" || c.prim === "bigint" || c.prim === "symbol" ? c.prim : "unknown";
3134
+ if (type === "unknown") return { k: "unknown" };
3135
+ return { k: "prim", type, refinements };
3136
+ }
3137
+ function constraintDropped(c, prefix = "") {
3138
+ const out = [];
3139
+ const visit = (n, path) => {
3140
+ if (n.fields) {
3141
+ for (const [k, f] of Object.entries(n.fields)) visit(f.constraint, path ? `${path}.${k}` : k);
3142
+ return;
3143
+ }
3144
+ if (n.members) {
3145
+ n.members.forEach((m, i) => visit(m, `${path}[${i}]`));
3146
+ return;
3147
+ }
3148
+ if (n.element) {
3149
+ visit(n.element, `${path}[]`);
3150
+ return;
3151
+ }
3152
+ if (n.fn) return;
3153
+ const kind = n.prim === "number" ? "number" : n.prim === "string" ? "string" : n.prim === "boolean" ? "boolean" : "other";
3154
+ const { eqLit, dropped } = refinementsFromPreds(n.preds ?? [], { kind });
3155
+ for (const d of dropped) out.push(path ? `${path}: ${d}` : d);
3156
+ if (eqLit === void 0 && n.preds?.some((p) => p.op === "eq") && !isIntFlag2(n)) {
3157
+ }
3158
+ };
3159
+ visit(c, prefix);
3160
+ return out;
3161
+ }
3162
+ function absToSchemaNode(a) {
3163
+ const dropped = [];
3164
+ if (a.term?.op === "lit") {
3165
+ const v = a.term.value;
3166
+ if (typeof v === "number" && Number.isNaN(v)) {
3167
+ dropped.push("lit NaN not projected");
3168
+ } else if (v === null || v === void 0 || typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
3169
+ return { node: { k: "lit", value: v }, dropped };
3170
+ } else if (typeof v === "bigint") {
3171
+ dropped.push(`lit bigint not projected: ${String(v)}`);
3172
+ return { node: { k: "unknown" }, dropped };
3173
+ }
3174
+ }
3175
+ const conf = a.conf;
3176
+ const lowConf = conf !== "exact" && conf !== "path";
3177
+ const projected = absToConstraint(a);
3178
+ if (projected) {
3179
+ const node = constraintToSchemaNode(projected);
3180
+ dropped.push(...constraintDropped(projected));
3181
+ return { node, dropped };
3182
+ }
3183
+ if (lowConf) {
3184
+ dropped.push(`conf=${conf}: contract projection skipped; shape-only fallback`);
3185
+ }
3186
+ const s = a.shape;
3187
+ switch (s.k) {
3188
+ case "never":
3189
+ return { node: { k: "never" }, dropped };
3190
+ case "any":
3191
+ case "unknown":
3192
+ return { node: { k: "unknown" }, dropped };
3193
+ case "prim": {
3194
+ const kind = s.type === "number" ? "number" : s.type === "string" ? "string" : s.type === "boolean" ? "boolean" : "other";
3195
+ const leaves = predLeaves(a.pred);
3196
+ if (leaves === "unexpressible") {
3197
+ if (a.pred && a.pred.op !== "true") {
3198
+ dropped.push(`pred not projected: ${predToString(a.pred)}`);
3199
+ }
3200
+ return { node: { k: "prim", type: s.type, refinements: [] }, dropped };
3201
+ }
3202
+ const { refinements, eqLit, dropped: d } = refinementsFromPreds(leaves, { kind });
3203
+ dropped.push(...d);
3204
+ if (eqLit !== void 0 && !Number.isNaN(eqLit)) {
3205
+ return { node: { k: "lit", value: eqLit }, dropped };
3206
+ }
3207
+ return { node: { k: "prim", type: s.type, refinements }, dropped };
3208
+ }
3209
+ case "obj": {
3210
+ if (s.open || s.index) {
3211
+ dropped.push("open/index object not fully projected (known slots only)");
3212
+ }
3213
+ if (a.pred && a.pred.op !== "true") {
3214
+ dropped.push(`obj pred not projected: ${predToString(a.pred)}`);
3215
+ }
3216
+ const slots = [];
3217
+ for (const [key, slot] of Object.entries(s.slots)) {
3218
+ const sub = absToSchemaNode(slot.value);
3219
+ dropped.push(...sub.dropped.map((n) => `${key}: ${n}`));
3220
+ slots.push({ key, node: sub.node, ...slot.optional ? { optional: true } : {} });
3221
+ }
3222
+ return { node: { k: "obj", slots }, dropped };
3223
+ }
3224
+ case "arr": {
3225
+ if (a.pred && a.pred.op !== "true") {
3226
+ dropped.push(`arr pred not projected: ${predToString(a.pred)}`);
3227
+ }
3228
+ const sub = absToSchemaNode(s.element);
3229
+ dropped.push(...sub.dropped);
3230
+ return { node: { k: "arr", element: sub.node }, dropped };
3231
+ }
3232
+ case "tuple": {
3233
+ const elements = s.elements.map((e) => {
3234
+ const sub = absToSchemaNode(e);
3235
+ dropped.push(...sub.dropped);
3236
+ return sub.node;
3237
+ });
3238
+ return { node: { k: "tuple", elements }, dropped };
3239
+ }
3240
+ case "sum": {
3241
+ const members = s.members.map((m) => {
3242
+ const sub = absToSchemaNode(m);
3243
+ dropped.push(...sub.dropped);
3244
+ return sub.node;
3245
+ });
3246
+ return { node: { k: "union", members }, dropped };
3247
+ }
3248
+ case "fn":
3249
+ return { node: { k: "fn" }, dropped };
3250
+ case "eff": {
3251
+ const inner = absToSchemaNode(s.inner);
3252
+ dropped.push(...inner.dropped);
3253
+ return {
3254
+ node: s.eff === "promise" ? { k: "promise", inner: inner.node } : inner.node,
3255
+ dropped
3256
+ };
3257
+ }
3258
+ case "brand":
3259
+ return { node: { k: "brand", name: s.name }, dropped };
3260
+ default:
3261
+ return { node: { k: "unknown" }, dropped };
3262
+ }
3263
+ }
3264
+ function zodApplyRefinements(base, refinements) {
3265
+ let out = base;
3266
+ if (refinements.some((r) => r.kind === "int")) out += ".int()";
3267
+ for (const r of refinements) {
3268
+ switch (r.kind) {
3269
+ case "numBound":
3270
+ if (r.op === "gt") out += `.gt(${r.n})`;
3271
+ else if (r.op === "ge") out += `.gte(${r.n})`;
3272
+ else if (r.op === "lt") out += `.lt(${r.n})`;
3273
+ else out += `.lte(${r.n})`;
3274
+ break;
3275
+ case "strMin":
3276
+ out += `.min(${r.n})`;
3277
+ break;
3278
+ case "strMax":
3279
+ out += `.max(${r.n})`;
3280
+ break;
3281
+ default:
3282
+ break;
3283
+ }
3284
+ }
3285
+ return out;
3286
+ }
3287
+ function schemaNodeToZod(node) {
3288
+ switch (node.k) {
3289
+ case "lit": {
3290
+ const v = node.value;
3291
+ if (v === null) return "z.null()";
3292
+ if (v === void 0) return "z.undefined()";
3293
+ if (typeof v === "string") return `z.literal(${JSON.stringify(v)})`;
3294
+ if (typeof v === "boolean") return `z.literal(${v})`;
3295
+ if (typeof v === "number") return `z.literal(${v})`;
3296
+ return "z.unknown()";
3297
+ }
3298
+ case "prim":
3299
+ return zodApplyRefinements(`z.${node.type}()`, node.refinements);
3300
+ case "obj": {
3301
+ const entries = node.slots.map((slot) => {
3302
+ const inner = schemaNodeToZod(slot.node);
3303
+ return `${slot.key}: ${slot.optional ? `${inner}.optional()` : inner}`;
3304
+ }).join(", ");
3305
+ return `z.object({ ${entries} })`;
3306
+ }
3307
+ case "arr":
3308
+ return `z.array(${schemaNodeToZod(node.element)})`;
3309
+ case "tuple":
3310
+ return `z.tuple([${node.elements.map(schemaNodeToZod).join(", ")}])`;
3311
+ case "union":
3312
+ return `z.union([${node.members.map(schemaNodeToZod).join(", ")}])`;
3313
+ case "fn":
3314
+ return "z.function()";
3315
+ case "promise":
3316
+ return `z.promise(${schemaNodeToZod(node.inner)})`;
3317
+ case "brand":
3318
+ return `z.instanceof(${node.name})`;
3319
+ case "never":
3320
+ return "z.never()";
3321
+ case "unknown":
3322
+ default:
3323
+ return "z.unknown()";
3324
+ }
3325
+ }
3326
+ var DIALECT_RENDERERS = {
3327
+ zod: schemaNodeToZod
3328
+ };
3329
+ function projectAbsToSchema(a, opts) {
3330
+ const dialect = opts?.dialect ?? "zod";
3331
+ const { node, dropped } = absToSchemaNode(a);
3332
+ const render = DIALECT_RENDERERS[dialect] ?? schemaNodeToZod;
3333
+ return { source: render(node), dialect, dropped };
3334
+ }
3335
+ function absToSchemaSource(a, opts) {
3336
+ return projectAbsToSchema(a, opts).source;
2391
3337
  }
2392
3338
 
3339
+ // src/emit/standard-schema.ts
3340
+ function pushIssue(issues, path, message) {
3341
+ issues.push(path.length > 0 ? { message, path: [...path] } : { message });
3342
+ }
3343
+ function typeOf(v) {
3344
+ if (v === null) return "null";
3345
+ if (Array.isArray(v)) return "array";
3346
+ return typeof v;
3347
+ }
3348
+ function checkPrimRefinements(refinements, value, path, issues) {
3349
+ if (typeof value !== "number" && typeof value !== "string") return;
3350
+ for (const r of refinements) {
3351
+ if (r.kind === "int") {
3352
+ if (typeof value === "number" && !Number.isInteger(value)) {
3353
+ pushIssue(issues, path, "expected integer");
3354
+ }
3355
+ continue;
3356
+ }
3357
+ if (r.kind === "numBound" && typeof value === "number" && typeof r.n === "number") {
3358
+ const n = r.n;
3359
+ const op = r.op;
3360
+ let ok = true;
3361
+ if (op === "gt") ok = value > n;
3362
+ else if (op === "ge") ok = value >= n;
3363
+ else if (op === "lt") ok = value < n;
3364
+ else if (op === "le") ok = value <= n;
3365
+ if (!ok) pushIssue(issues, path, `expected number ${op} ${n}, got ${value}`);
3366
+ continue;
3367
+ }
3368
+ if (r.kind === "strMin" && typeof value === "string" && typeof r.n === "number") {
3369
+ if (value.length < r.n) {
3370
+ pushIssue(issues, path, `expected string length >= ${r.n}, got ${value.length}`);
3371
+ }
3372
+ continue;
3373
+ }
3374
+ if (r.kind === "strMax" && typeof value === "string" && typeof r.n === "number") {
3375
+ if (value.length > r.n) {
3376
+ pushIssue(issues, path, `expected string length <= ${r.n}, got ${value.length}`);
3377
+ }
3378
+ }
3379
+ }
3380
+ }
3381
+ function checkNode(node, value, path, issues) {
3382
+ switch (node.k) {
3383
+ case "unknown":
3384
+ case "fn":
3385
+ case "brand":
3386
+ case "promise":
3387
+ return;
3388
+ case "never":
3389
+ pushIssue(issues, path, "expected never");
3390
+ return;
3391
+ case "lit": {
3392
+ const expected = node.value;
3393
+ const same = expected === value || typeof expected === "number" && typeof value === "number" && Number.isNaN(expected) && Number.isNaN(value);
3394
+ if (!same) {
3395
+ pushIssue(issues, path, `expected ${JSON.stringify(expected)}, got ${JSON.stringify(value)}`);
3396
+ }
3397
+ return;
3398
+ }
3399
+ case "prim": {
3400
+ const t = node.type;
3401
+ const actual = typeOf(value);
3402
+ const ok = t === "number" && actual === "number" || t === "string" && actual === "string" || t === "boolean" && actual === "boolean" || t === "bigint" && actual === "bigint" || t === "symbol" && actual === "symbol";
3403
+ if (!ok) {
3404
+ pushIssue(issues, path, `expected ${t}, got ${actual}`);
3405
+ return;
3406
+ }
3407
+ checkPrimRefinements(node.refinements, value, path, issues);
3408
+ return;
3409
+ }
3410
+ case "obj": {
3411
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3412
+ pushIssue(issues, path, `expected object, got ${typeOf(value)}`);
3413
+ return;
3414
+ }
3415
+ const rec = value;
3416
+ for (const slot of node.slots) {
3417
+ const key = slot.key;
3418
+ if (!(key in rec) || rec[key] === void 0) {
3419
+ if (!slot.optional) pushIssue(issues, [...path, key], "required");
3420
+ continue;
3421
+ }
3422
+ checkNode(slot.node, rec[key], [...path, key], issues);
3423
+ }
3424
+ return;
3425
+ }
3426
+ case "arr": {
3427
+ if (!Array.isArray(value)) {
3428
+ pushIssue(issues, path, `expected array, got ${typeOf(value)}`);
3429
+ return;
3430
+ }
3431
+ value.forEach((item, i) => {
3432
+ checkNode(node.element, item, [...path, i], issues);
3433
+ });
3434
+ return;
3435
+ }
3436
+ case "tuple": {
3437
+ if (!Array.isArray(value)) {
3438
+ pushIssue(issues, path, `expected tuple, got ${typeOf(value)}`);
3439
+ return;
3440
+ }
3441
+ node.elements.forEach((el, i) => {
3442
+ checkNode(el, value[i], [...path, i], issues);
3443
+ });
3444
+ return;
3445
+ }
3446
+ case "union": {
3447
+ for (const m of node.members) {
3448
+ const local = [];
3449
+ checkNode(m, value, path, local);
3450
+ if (local.length === 0) return;
3451
+ }
3452
+ pushIssue(
3453
+ issues,
3454
+ path,
3455
+ `expected one of ${node.members.length} union members, got ${typeOf(value)}`
3456
+ );
3457
+ return;
3458
+ }
3459
+ case "promise": {
3460
+ return;
3461
+ }
3462
+ default:
3463
+ return;
3464
+ }
3465
+ }
3466
+ function validateSchemaNode(node, value) {
3467
+ const issues = [];
3468
+ checkNode(node, value, [], issues);
3469
+ return issues.length === 0 ? { value } : { issues };
3470
+ }
3471
+ var CHECK_FN_SOURCE = `
3472
+ function __nudoTypeOf(v) {
3473
+ if (v === null) return "null";
3474
+ if (Array.isArray(v)) return "array";
3475
+ return typeof v;
3476
+ }
3477
+ function __nudoCheck(node, value, path, issues) {
3478
+ function push(msg) {
3479
+ issues.push(path.length > 0 ? { message: msg, path: path.slice() } : { message: msg });
3480
+ }
3481
+ switch (node.k) {
3482
+ case "unknown":
3483
+ case "summarized":
3484
+ case "fn":
3485
+ case "brand":
3486
+ case "promise":
3487
+ return;
3488
+ case "never":
3489
+ push("expected never");
3490
+ return;
3491
+ case "lit": {
3492
+ const expected = node.value;
3493
+ const same = expected === value ||
3494
+ (typeof expected === "number" && typeof value === "number" &&
3495
+ Number.isNaN(expected) && Number.isNaN(value));
3496
+ if (!same) push("expected " + JSON.stringify(expected) + ", got " + JSON.stringify(value));
3497
+ return;
3498
+ }
3499
+ case "prim": {
3500
+ const t = node.type;
3501
+ const actual = __nudoTypeOf(value);
3502
+ const ok =
3503
+ (t === "number" && actual === "number") ||
3504
+ (t === "string" && actual === "string") ||
3505
+ (t === "boolean" && actual === "boolean") ||
3506
+ (t === "bigint" && actual === "bigint") ||
3507
+ (t === "symbol" && actual === "symbol");
3508
+ if (!ok) { push("expected " + t + ", got " + actual); return; }
3509
+ for (const r of node.refinements || []) {
3510
+ if (r.kind === "int" && typeof value === "number" && !Number.isInteger(value)) {
3511
+ push("expected integer");
3512
+ } else if (r.kind === "numBound" && typeof value === "number" && typeof r.n === "number") {
3513
+ var okB = true;
3514
+ if (r.op === "gt") okB = value > r.n;
3515
+ else if (r.op === "ge") okB = value >= r.n;
3516
+ else if (r.op === "lt") okB = value < r.n;
3517
+ else if (r.op === "le") okB = value <= r.n;
3518
+ if (!okB) push("expected number " + r.op + " " + r.n + ", got " + value);
3519
+ } else if (r.kind === "strMin" && typeof value === "string" && typeof r.n === "number") {
3520
+ if (value.length < r.n) push("expected string length >= " + r.n + ", got " + value.length);
3521
+ } else if (r.kind === "strMax" && typeof value === "string" && typeof r.n === "number") {
3522
+ if (value.length > r.n) push("expected string length <= " + r.n + ", got " + value.length);
3523
+ }
3524
+ }
3525
+ return;
3526
+ }
3527
+ case "obj": {
3528
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3529
+ push("expected object, got " + __nudoTypeOf(value));
3530
+ return;
3531
+ }
3532
+ for (const slot of node.slots) {
3533
+ const key = slot.key;
3534
+ if (!(key in value) || value[key] === undefined) {
3535
+ if (!slot.optional) { path.push(key); push("required"); path.pop(); }
3536
+ continue;
3537
+ }
3538
+ path.push(key);
3539
+ __nudoCheck(slot.node, value[key], path, issues);
3540
+ path.pop();
3541
+ }
3542
+ return;
3543
+ }
3544
+ case "arr": {
3545
+ if (!Array.isArray(value)) { push("expected array, got " + __nudoTypeOf(value)); return; }
3546
+ for (let i = 0; i < value.length; i++) {
3547
+ path.push(i);
3548
+ __nudoCheck(node.element, value[i], path, issues);
3549
+ path.pop();
3550
+ }
3551
+ return;
3552
+ }
3553
+ case "tuple": {
3554
+ if (!Array.isArray(value)) { push("expected tuple, got " + __nudoTypeOf(value)); return; }
3555
+ for (let i = 0; i < node.elements.length; i++) {
3556
+ path.push(i);
3557
+ __nudoCheck(node.elements[i], value[i], path, issues);
3558
+ path.pop();
3559
+ }
3560
+ return;
3561
+ }
3562
+ case "union": {
3563
+ for (const m of node.members) {
3564
+ const local = [];
3565
+ __nudoCheck(m, value, path, local);
3566
+ if (local.length === 0) return;
3567
+ }
3568
+ push("expected one of " + node.members.length + " union members, got " + __nudoTypeOf(value));
3569
+ return;
3570
+ }
3571
+ default:
3572
+ return;
3573
+ }
3574
+ }
3575
+ `.trim();
3576
+ function makeStandardSchemaSource(exportName, node, dropped) {
3577
+ const json = JSON.stringify(node);
3578
+ const notes = dropped.length > 0 ? dropped.map((d) => `// ${d}`).join("\n") + "\n" : "";
3579
+ return `${notes}export const ${exportName} = {
3580
+ "~standard": {
3581
+ version: 1,
3582
+ vendor: "nudo",
3583
+ validate(value) {
3584
+ const issues = [];
3585
+ __nudoCheck(${json}, value, [], issues);
3586
+ return issues.length ? { issues } : { value };
3587
+ },
3588
+ },
3589
+ } as const;`;
3590
+ }
3591
+ function absToStandardSchemaModule(exports, opts) {
3592
+ const dropped = [];
3593
+ const bodies = [];
3594
+ for (const [name, abs] of Object.entries(exports)) {
3595
+ const { node, dropped: d } = absToSchemaNode(abs);
3596
+ dropped.push(...d.map((n) => `${name}: ${n}`));
3597
+ bodies.push(makeStandardSchemaSource(name, node, d.map((n) => `${name}: ${n}`)));
3598
+ }
3599
+ const banner = opts?.banner ?? `// @generated by nudo export --format standard \u2014 Standard Schema v1 (vendor: nudo)
3600
+ // One-way lossy projection of Abs; do not edit. nudo check remains the gate.`;
3601
+ const source = `${banner}
3602
+
3603
+ ${CHECK_FN_SOURCE}
3604
+
3605
+ ${bodies.join("\n\n")}
3606
+ `;
3607
+ return { source, dropped };
3608
+ }
3609
+ function absToStandardSchema(a, opts) {
3610
+ return absToStandardSchemaModule({ [opts?.name ?? "schema"]: a });
3611
+ }
3612
+
3613
+ // src/emit/guard-generator.ts
3614
+ import { denoteGuard } from "@nudojs/core/internal";
3615
+ function generateGuardFunctionFromAbs(name, abs) {
3616
+ const body = denoteGuard(abs, "data");
3617
+ return `export function ${name}(data) {
3618
+ return ${body};
3619
+ }`;
3620
+ }
3621
+ function generateGuardFunction(name, abs) {
3622
+ return generateGuardFunctionFromAbs(name, abs);
3623
+ }
3624
+
3625
+ // src/emit/case-json.ts
3626
+ import { formatAbs, formatShape as formatShape3 } from "@nudojs/core";
3627
+ function mapCase(c) {
3628
+ const out = {
3629
+ name: c.name,
3630
+ args: c.argAbs.map((a) => formatShape3(a)),
3631
+ result: formatShape3(c.abs),
3632
+ throws: c.throwsAbs.shape.k !== "never" ? formatShape3(c.throwsAbs) : null,
3633
+ source: c.source ?? null
3634
+ };
3635
+ if (c.aggregatedFrom !== void 0) out.aggregatedFrom = c.aggregatedFrom;
3636
+ if (c.argAbs.length > 0) {
3637
+ out.argsAbs = c.argAbs.map((a) => {
3638
+ try {
3639
+ return formatAbs(a);
3640
+ } catch {
3641
+ return "unknown";
3642
+ }
3643
+ });
3644
+ }
3645
+ try {
3646
+ out.resultAbs = formatAbs(c.abs);
3647
+ } catch {
3648
+ }
3649
+ if (c.intension) {
3650
+ const i = c.intension;
3651
+ out.intension = {
3652
+ ...i.display !== void 0 ? { display: i.display } : {},
3653
+ ...i.abs !== void 0 ? { abs: i.abs } : {},
3654
+ ...i.absMultiline !== void 0 ? { absMultiline: i.absMultiline } : {},
3655
+ ...i.term !== void 0 ? { term: i.term } : {},
3656
+ ...i.pred !== void 0 ? { pred: i.pred } : {},
3657
+ ...i.conf !== void 0 ? { conf: i.conf } : {}
3658
+ };
3659
+ }
3660
+ return out;
3661
+ }
3662
+ function mapFunction(f) {
3663
+ const out = {
3664
+ name: f.name,
3665
+ loc: f.loc,
3666
+ entryOnly: f.entryOnly ?? false,
3667
+ cases: f.cases.map(mapCase)
3668
+ };
3669
+ if (f.noDeclaration) out.noDeclaration = true;
3670
+ if (f.combinedAbs) {
3671
+ out.combined = formatShape3(f.combinedAbs);
3672
+ try {
3673
+ out.combinedAbs = formatAbs(f.combinedAbs);
3674
+ } catch {
3675
+ }
3676
+ }
3677
+ return out;
3678
+ }
3679
+ function serializeCaseJson(result, file) {
3680
+ const functions = result.functions.map(mapFunction);
3681
+ const external = result.externalFunctions?.map((f) => ({
3682
+ name: f.name,
3683
+ fromModule: f.fromModule,
3684
+ cases: f.cases.map(mapCase)
3685
+ }));
3686
+ const caseCount = functions.reduce((n, f) => n + f.cases.length, 0) + (external ?? []).reduce((n, f) => n + f.cases.length, 0);
3687
+ const json = {
3688
+ version: 1,
3689
+ file,
3690
+ summary: {
3691
+ functions: functions.length,
3692
+ externalFunctions: external?.length ?? 0,
3693
+ cases: caseCount,
3694
+ diagnostics: result.diagnostics.length
3695
+ },
3696
+ functions,
3697
+ diagnostics: result.diagnostics.map((d) => ({
3698
+ range: d.range,
3699
+ severity: d.severity,
3700
+ message: d.message,
3701
+ ...d.code !== void 0 ? { code: d.code } : {},
3702
+ ...d.suggestions ? { suggestions: d.suggestions } : {},
3703
+ ...d.tags ? { tags: d.tags } : {},
3704
+ ...d.origin ? { origin: d.origin } : {}
3705
+ }))
3706
+ };
3707
+ if (external) json.externalFunctions = external;
3708
+ return json;
3709
+ }
2393
3710
  export {
2394
- collectLoadDepContents,
2395
- ANALYSIS_ABI,
2396
- sha256Hex,
2397
- relativizePath,
2398
- DiskCache,
2399
- checkCacheKey,
2400
- ifaceCacheKey,
2401
- extractNudoImportSpecs,
2402
- formatInterfaceSurfaceLine,
2403
- interfaceSurface,
2404
- emitInterface,
2405
- formatEmitSummary,
3711
+ absToSchemaNode,
3712
+ absToSchemaSource,
3713
+ absToStandardSchema,
3714
+ absToStandardSchemaModule,
3715
+ absToTSType,
3716
+ buildCaseDirective,
2406
3717
  collectParamBodyAccesses,
3718
+ constraintToSchemaNode,
3719
+ deriveFromRoot,
2407
3720
  draftInterface,
2408
- formatDraftModule,
2409
- sidecarDraftPath,
2410
- isDraftableEntry,
2411
- writeInterfaceDraft,
2412
- formatDraftSummary,
3721
+ emitDerivedFromRoot,
3722
+ emitInterface,
2413
3723
  extractFnConstraintSources,
2414
- deriveFromRoot,
2415
3724
  formatDerivedSection,
2416
- emitDerivedFromRoot,
3725
+ formatDraftModule,
3726
+ formatDraftSummary,
3727
+ formatEmitSummary,
3728
+ formatInterfaceSurfaceLine,
3729
+ generateDts,
3730
+ generateFunctionDtsLines,
3731
+ generateGuardFunction,
3732
+ generateGuardFunctionFromAbs,
3733
+ injectBindings,
3734
+ insertGeneratedCaseDirectives,
3735
+ interfaceSurface,
3736
+ isDraftableEntry,
3737
+ projectAbsToSchema,
3738
+ schemaNodeToZod,
3739
+ serializeCaseArg,
3740
+ serializeCaseJson,
3741
+ sidecarDraftPath,
3742
+ stripGeneratedCaseDirectives,
2417
3743
  typeExprToDirective,
2418
- injectBindings
3744
+ unifiedDiff,
3745
+ validateSchemaNode,
3746
+ writeInterfaceDraft
2419
3747
  };