@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,1107 @@
1
+ // src/dts-generator.ts
2
+ import { joinAbs, litValue, abs as makeAbs, collectAbsFreeVars } from "@nudojs/core";
3
+ import { isTemplateLike, templatePartsOf } from "@nudojs/core/internal";
4
+ function tsTypeParamName(id) {
5
+ let n = id.replace(/[^A-Za-z0-9_$]/g, "_");
6
+ if (!/^[A-Za-z_$]/.test(n)) n = `T_${n}`;
7
+ if (n.length === 0) n = "T";
8
+ return n;
9
+ }
10
+ function wrapComplexAbs(a, typeVars) {
11
+ const ts = absToTSType(a, typeVars);
12
+ if (a.shape.k === "sum" || a.shape.k === "fn") return `(${ts})`;
13
+ return ts;
14
+ }
15
+ function wrapUnionMember(a, typeVars) {
16
+ const ts = absToTSType(a, typeVars);
17
+ if (a.shape.k === "fn") return `(${ts})`;
18
+ return ts;
19
+ }
20
+ var TS_PARAM_RESERVED = /* @__PURE__ */ new Set([
21
+ "break",
22
+ "case",
23
+ "catch",
24
+ "class",
25
+ "const",
26
+ "continue",
27
+ "debugger",
28
+ "default",
29
+ "delete",
30
+ "do",
31
+ "else",
32
+ "enum",
33
+ "export",
34
+ "extends",
35
+ "false",
36
+ "finally",
37
+ "for",
38
+ "function",
39
+ "if",
40
+ "import",
41
+ "in",
42
+ "instanceof",
43
+ "new",
44
+ "null",
45
+ "return",
46
+ "super",
47
+ "switch",
48
+ "this",
49
+ "throw",
50
+ "true",
51
+ "try",
52
+ "typeof",
53
+ "var",
54
+ "void",
55
+ "while",
56
+ "with",
57
+ "yield",
58
+ "let",
59
+ "static",
60
+ "await",
61
+ "implements",
62
+ "interface",
63
+ "package",
64
+ "private",
65
+ "protected",
66
+ "public",
67
+ "arguments",
68
+ "eval",
69
+ "constructor"
70
+ ]);
71
+ function isTsIdent(name) {
72
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !TS_PARAM_RESERVED.has(name);
73
+ }
74
+ function sanitizeParamName(name, index) {
75
+ if (name.startsWith("...")) {
76
+ const rest = name.slice(3);
77
+ if (isTsIdent(rest)) return name;
78
+ return `...arg${index}`;
79
+ }
80
+ if (isTsIdent(name)) return name;
81
+ return `arg${index}`;
82
+ }
83
+ function formatPropKey(k) {
84
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k)) return k;
85
+ if (/^\d+$/.test(k)) return k;
86
+ return JSON.stringify(k);
87
+ }
88
+ function absToTSType(a, typeVars) {
89
+ if (a.shape.k === "any" && a.term?.op === "var" && typeVars) {
90
+ const mapped = typeVars.get(a.term.id);
91
+ if (mapped) return mapped;
92
+ }
93
+ if (a.shape.k === "arr" && a.shape.element.shape.k === "any" && a.shape.element.term?.op === "var" && typeVars) {
94
+ const mapped = typeVars.get(a.shape.element.term.id);
95
+ if (mapped) return `${mapped}[]`;
96
+ }
97
+ if (a.term?.op === "lit") {
98
+ const v = a.term.value;
99
+ if (v === null) return "null";
100
+ if (v === void 0) return "undefined";
101
+ if (a.shape.k === "prim") {
102
+ if (typeof v === "string") return JSON.stringify(v);
103
+ if (typeof v === "boolean") return String(v);
104
+ if (typeof v === "number") return String(v);
105
+ }
106
+ }
107
+ if (a.shape.k === "prim" && a.shape.type === "string" && isTemplateLike(a)) {
108
+ const parts = templatePartsOf(a);
109
+ const inner = parts.map((p) => {
110
+ const lv = litValue(p);
111
+ if (typeof lv === "string") return lv;
112
+ return `\${${absToTSType(p, typeVars)}}`;
113
+ }).join("");
114
+ return `\`${inner}\``;
115
+ }
116
+ switch (a.shape.k) {
117
+ case "never":
118
+ return "never";
119
+ case "unknown":
120
+ case "any":
121
+ return "unknown";
122
+ case "prim":
123
+ return a.shape.type;
124
+ case "obj": {
125
+ const entries = Object.entries(a.shape.slots).map(([k, slot]) => {
126
+ const inner = absToTSType(slot.value, typeVars);
127
+ if (slot.optional) {
128
+ const t = wrapComplexAbs(slot.value, typeVars);
129
+ return `${formatPropKey(k)}: ${t} | undefined`;
130
+ }
131
+ return `${formatPropKey(k)}: ${inner}`;
132
+ });
133
+ if (entries.length === 0) return "{}";
134
+ return `{ ${entries.join("; ")} }`;
135
+ }
136
+ case "arr":
137
+ return `${wrapComplexAbs(a.shape.element, typeVars)}[]`;
138
+ case "tuple": {
139
+ const parts = a.shape.elements.map((e) => absToTSType(e, typeVars));
140
+ if (a.shape.rest) {
141
+ const rest = a.shape.rest;
142
+ const restTs = rest.shape.k === "arr" ? absToTSType(rest, typeVars) : `${absToTSType(rest, typeVars)}[]`;
143
+ parts.push(`...${restTs}`);
144
+ }
145
+ return `[${parts.join(", ")}]`;
146
+ }
147
+ case "fn": {
148
+ const paramTypes = a.shape.paramTypes;
149
+ const params = a.shape.params.map((p, i) => {
150
+ const isRest = p.startsWith("...");
151
+ const name = sanitizeParamName(p, i);
152
+ const pt = paramTypes?.[i];
153
+ let typeStr;
154
+ if (pt) typeStr = absToTSType(pt, typeVars);
155
+ else if (isRest) typeStr = "unknown[]";
156
+ else typeStr = "unknown";
157
+ return `${name}: ${typeStr}`;
158
+ }).join(", ");
159
+ const ret = a.shape.returnType ? absToTSType(a.shape.returnType, typeVars) : "unknown";
160
+ return `(${params}) => ${ret}`;
161
+ }
162
+ case "brand":
163
+ return isTsIdent(a.shape.name) || /^[A-Z][A-Za-z0-9_$]*$/.test(a.shape.name) ? a.shape.name : "unknown";
164
+ case "eff":
165
+ if (a.shape.eff === "promise")
166
+ return `Promise<${absToTSType(a.shape.inner, typeVars)}>`;
167
+ return absToTSType(a.shape.inner, typeVars);
168
+ case "sum": {
169
+ const parts = a.shape.members.map((m) => wrapUnionMember(m, typeVars)).filter((p) => p !== "never");
170
+ const uniq = [...new Set(parts)];
171
+ if (uniq.length === 0) return "never";
172
+ if (uniq.length === 1) return uniq[0];
173
+ return uniq.join(" | ");
174
+ }
175
+ default:
176
+ return "unknown";
177
+ }
178
+ }
179
+ function caseArgAbs(c, i) {
180
+ return c.argAbs[i];
181
+ }
182
+ function caseResultAbs(c) {
183
+ return c.abs;
184
+ }
185
+ function widenParamAbs(a) {
186
+ const s = a.shape;
187
+ switch (s.k) {
188
+ case "prim":
189
+ return makeAbs(s, void 0, void 0, "exact");
190
+ case "tuple": {
191
+ const widened = s.elements.map(widenParamAbs);
192
+ const first = widened[0];
193
+ if (first && widened.length > 0 && widened.every((el) => absToTSType(el) === absToTSType(first))) {
194
+ return makeAbs({ k: "arr", element: first }, void 0, void 0, "exact");
195
+ }
196
+ return makeAbs({ k: "tuple", elements: widened }, void 0, void 0, "exact");
197
+ }
198
+ case "arr":
199
+ return makeAbs({ k: "arr", element: widenParamAbs(s.element) }, void 0, void 0, "exact");
200
+ case "obj": {
201
+ const slots = {};
202
+ for (const [k, slot] of Object.entries(s.slots)) {
203
+ slots[k] = {
204
+ value: widenParamAbs(slot.value),
205
+ ...slot.optional ? { optional: true } : {}
206
+ };
207
+ }
208
+ return makeAbs({ k: "obj", slots }, void 0, void 0, "exact");
209
+ }
210
+ case "eff":
211
+ return makeAbs(
212
+ { k: "eff", eff: s.eff, inner: widenParamAbs(s.inner) },
213
+ void 0,
214
+ void 0,
215
+ "exact"
216
+ );
217
+ case "brand":
218
+ return makeAbs(
219
+ { k: "brand", name: s.name, shape: widenParamAbs(s.shape) },
220
+ void 0,
221
+ void 0,
222
+ "exact"
223
+ );
224
+ case "sum":
225
+ return makeAbs(
226
+ { k: "sum", members: s.members.map(widenParamAbs) },
227
+ void 0,
228
+ void 0,
229
+ "exact"
230
+ );
231
+ default:
232
+ return a;
233
+ }
234
+ }
235
+ function widenTopLevelAbs(a) {
236
+ if (a.shape.k === "sum") {
237
+ return makeAbs(
238
+ { k: "sum", members: a.shape.members.map(widenTopLevelAbs) },
239
+ void 0,
240
+ void 0,
241
+ a.conf
242
+ );
243
+ }
244
+ return widenLiteralToPrimAbs(a);
245
+ }
246
+ function widenLiteralToPrimAbs(a) {
247
+ if (a.term?.op === "lit" && a.term.value === null) return a;
248
+ if (a.term?.op === "lit" && a.term.value === void 0) return a;
249
+ if (a.term?.op === "lit" && a.shape.k === "prim") {
250
+ const v = a.term.value;
251
+ if (typeof v === "number" || typeof v === "string" || typeof v === "boolean" || typeof v === "bigint") {
252
+ return makeAbs(a.shape, void 0, void 0, a.conf);
253
+ }
254
+ }
255
+ return a;
256
+ }
257
+ function paramTypeFromAbs(members) {
258
+ if (members.length === 0) return "unknown";
259
+ let joined;
260
+ try {
261
+ joined = members.reduce((a, b) => joinAbs(a, b));
262
+ } catch {
263
+ joined = members[0];
264
+ }
265
+ return absToTSType(widenParamAbs(joined));
266
+ }
267
+ function returnAbsOf(fn) {
268
+ if (fn.combinedAbs) return fn.combinedAbs;
269
+ const results = fn.cases.map(caseResultAbs);
270
+ if (results.length > 0) {
271
+ try {
272
+ return results.reduce((a, b) => joinAbs(a, b));
273
+ } catch {
274
+ }
275
+ return results[0];
276
+ }
277
+ return void 0;
278
+ }
279
+ function getParamName(fn, index) {
280
+ if (fn.paramNames && fn.paramNames[index]) {
281
+ return fn.paramNames[index];
282
+ }
283
+ return `arg${index}`;
284
+ }
285
+ function computeMainSignature(fn) {
286
+ const arity = Math.max(...fn.cases.map((c) => c.argAbs.length));
287
+ const minArity = Math.min(...fn.cases.map((c) => c.argAbs.length));
288
+ const params = [];
289
+ const paramNames = [];
290
+ const paramTypes = [];
291
+ const usedNames = /* @__PURE__ */ new Set();
292
+ for (let i = 0; i < arity; i++) {
293
+ const members = [];
294
+ for (const c of fn.cases) {
295
+ if (i >= c.argAbs.length) continue;
296
+ const a = caseArgAbs(c, i);
297
+ if (a) members.push(a);
298
+ }
299
+ const typeStr = paramTypeFromAbs(members);
300
+ let name = getParamName(fn, i);
301
+ const isRest = name.startsWith("...");
302
+ const bare = isRest ? name.slice(3) : name;
303
+ if (!isTsIdent(bare)) {
304
+ name = isRest ? `...arg${i}` : `arg${i}`;
305
+ }
306
+ if (usedNames.has(name)) {
307
+ let n = 2;
308
+ while (usedNames.has(`${name}${n}`)) n++;
309
+ name = `${name}${n}`;
310
+ }
311
+ usedNames.add(name);
312
+ const optional = i >= minArity && !isRest;
313
+ params.push(optional ? `${name}?: ${typeStr}` : `${name}: ${typeStr}`);
314
+ paramNames.push(name);
315
+ paramTypes.push(typeStr);
316
+ }
317
+ const retAbs = returnAbsOf(fn);
318
+ const returnType = retAbs ? absToTSType(widenTopLevelAbs(retAbs)) : "unknown";
319
+ return { params, paramNames, paramTypes, returnType };
320
+ }
321
+ function generateJSDoc(fn, sig) {
322
+ if (fn.cases.length === 0) return "";
323
+ const lines = ["/**"];
324
+ for (const c of fn.cases) {
325
+ const preciseDiffers = c.argAbs.length !== sig.paramTypes.length || c.argAbs.some((a, i) => absToTSType(a) !== sig.paramTypes[i]) || absToTSType(c.abs) !== sig.returnType;
326
+ if (!preciseDiffers) continue;
327
+ const argsStr = c.argAbs.map((a) => absToTSType(a)).join(", ");
328
+ lines.push(` * Case: ${c.name} (${argsStr}) => ${absToTSType(c.abs)}`);
329
+ }
330
+ for (let i = 0; i < sig.paramTypes.length; i++) {
331
+ lines.push(` * @param ${sig.paramNames[i]} - ${sig.paramTypes[i]}`);
332
+ }
333
+ lines.push(` * @returns ${sig.returnType}`);
334
+ lines.push(" */");
335
+ return lines.join("\n");
336
+ }
337
+ function computeHofSignature(fn) {
338
+ const hof = fn.hof;
339
+ if (!hof) return void 0;
340
+ const byParam = /* @__PURE__ */ new Map();
341
+ for (const s of hof.entryShapes ?? []) byParam.set(s.param, s.abs);
342
+ for (const r of hof.fnRels ?? []) byParam.set(r.param, r.abs);
343
+ if (byParam.size === 0 && !hof.symbolic) return void 0;
344
+ if (fn.cases.length > 0) {
345
+ for (const [param] of byParam) {
346
+ const idx = fn.paramNames.indexOf(param);
347
+ if (idx < 0) continue;
348
+ const caseType = paramTypeFromAbs(
349
+ fn.cases.map((c) => c.argAbs[idx]).filter((a) => !!a)
350
+ );
351
+ if (caseType !== "unknown" && caseType !== "unknown[]") {
352
+ return void 0;
353
+ }
354
+ }
355
+ }
356
+ const arity = Math.max(
357
+ fn.paramNames.length,
358
+ ...[...byParam.keys()].map((p) => fn.paramNames.indexOf(p) + 1),
359
+ 0
360
+ );
361
+ if (arity === 0 && !hof.symbolic) return void 0;
362
+ const free = /* @__PURE__ */ new Set();
363
+ for (const a of byParam.values()) {
364
+ for (const id of collectAbsFreeVars(a)) free.add(id);
365
+ }
366
+ const retAbs = hof.symbolic ?? fn.combinedAbs;
367
+ if (retAbs) {
368
+ for (const id of collectAbsFreeVars(retAbs)) free.add(id);
369
+ }
370
+ if (free.size === 0) return void 0;
371
+ const typeVars = /* @__PURE__ */ new Map();
372
+ const used = /* @__PURE__ */ new Set();
373
+ const typeParams = [];
374
+ for (const id of [...free].sort()) {
375
+ let n = tsTypeParamName(id);
376
+ if (used.has(n) || TS_PARAM_RESERVED.has(n)) {
377
+ let i = 2;
378
+ while (used.has(`${n}${i}`)) i++;
379
+ n = `${n}${i}`;
380
+ }
381
+ used.add(n);
382
+ typeVars.set(id, n);
383
+ typeParams.push(n);
384
+ }
385
+ const params = [];
386
+ const paramNames = [];
387
+ const paramTypes = [];
388
+ const usedNames = /* @__PURE__ */ new Set();
389
+ for (let i = 0; i < arity; i++) {
390
+ const rawName = getParamName(fn, i);
391
+ const isRest = rawName.startsWith("...");
392
+ let name = sanitizeParamName(rawName, i);
393
+ if (usedNames.has(name)) {
394
+ let n = 2;
395
+ while (usedNames.has(`${name}${n}`)) n++;
396
+ name = isRest && name.startsWith("...") ? `...${name.slice(3)}${n}` : `${name}${n}`;
397
+ }
398
+ usedNames.add(name);
399
+ const promoted = byParam.get(fn.paramNames[i] ?? rawName) ?? byParam.get(rawName);
400
+ const typeStr = promoted ? absToTSType(promoted, typeVars) : isRest ? "unknown[]" : "unknown";
401
+ params.push(`${name}: ${typeStr}`);
402
+ paramNames.push(name);
403
+ paramTypes.push(typeStr);
404
+ }
405
+ const returnType = retAbs ? absToTSType(retAbs, typeVars) : "unknown";
406
+ return { typeParams, params, paramNames, paramTypes, returnType };
407
+ }
408
+ function generateFunctionDtsLines(fn) {
409
+ let emitFn = fn;
410
+ if (fn.noDeclaration) {
411
+ const m = /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(fn.name);
412
+ if (!m) return [];
413
+ emitFn = { ...fn, name: `${m[1]}_${m[2]}`, noDeclaration: false };
414
+ }
415
+ fn = emitFn;
416
+ const hofSig = computeHofSignature(fn);
417
+ if (hofSig) {
418
+ const lines2 = [];
419
+ if (fn.cases.length > 0) {
420
+ const jsdoc2 = generateJSDoc(fn, {
421
+ params: hofSig.params,
422
+ paramNames: hofSig.paramNames,
423
+ paramTypes: hofSig.paramTypes,
424
+ returnType: hofSig.returnType
425
+ });
426
+ if (jsdoc2) lines2.push(jsdoc2);
427
+ }
428
+ const tparams = hofSig.typeParams.length > 0 ? `<${hofSig.typeParams.join(", ")}>` : "";
429
+ lines2.push(
430
+ `export declare function ${fn.name}${tparams}(${hofSig.params.join(", ")}): ${hofSig.returnType};`
431
+ );
432
+ return lines2;
433
+ }
434
+ if (fn.cases.length === 0) {
435
+ const retAbs = fn.combinedAbs;
436
+ if (retAbs) {
437
+ return [
438
+ `export declare function ${fn.name}(...args: unknown[]): ${absToTSType(retAbs)};`
439
+ ];
440
+ }
441
+ return [];
442
+ }
443
+ const sig = computeMainSignature(fn);
444
+ const jsdoc = generateJSDoc(fn, sig);
445
+ const lines = [];
446
+ if (jsdoc) lines.push(jsdoc);
447
+ lines.push(`export declare function ${fn.name}(${sig.params.join(", ")}): ${sig.returnType};`);
448
+ return lines;
449
+ }
450
+ function generateDts(result) {
451
+ const lines = [];
452
+ for (const fn of result.functions) {
453
+ lines.push(...generateFunctionDtsLines(fn));
454
+ }
455
+ return lines.join("\n") + "\n";
456
+ }
457
+
458
+ // src/schema-generator.ts
459
+ import { absToConstraint, isIntFlag, predToString } from "@nudojs/core";
460
+ function predLeaves(p) {
461
+ if (!p || p.op === "true") return [];
462
+ if (p.op === "and") {
463
+ const out = [];
464
+ for (const x of p.args) {
465
+ const sub = predLeaves(x);
466
+ if (sub === "unexpressible") return "unexpressible";
467
+ out.push(...sub);
468
+ }
469
+ return out;
470
+ }
471
+ if (p.op === "or" || p.op === "not" || p.op === "false") return "unexpressible";
472
+ return [p];
473
+ }
474
+ function isSelfVar(t) {
475
+ return !!t && t.op === "var";
476
+ }
477
+ function litOf(t) {
478
+ return t && t.op === "lit" ? t.value : void 0;
479
+ }
480
+ function eqLitValue(p) {
481
+ if (p.op !== "eq") return "unanchored";
482
+ const aLit = litOf(p.a);
483
+ const bLit = litOf(p.b);
484
+ if (aLit !== void 0 && (isSelfVar(p.b) || p.b.op === "app")) return aLit;
485
+ if (bLit !== void 0 && (isSelfVar(p.a) || p.a.op === "app")) return bLit;
486
+ if (aLit !== void 0 && bLit !== void 0) return aLit === bLit ? aLit : "unanchored";
487
+ return "unanchored";
488
+ }
489
+ function isIntModOne(p) {
490
+ if (p.op !== "eq") return false;
491
+ const zero = (t) => !!t && t.op === "lit" && t.value === 0;
492
+ 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");
493
+ return isModOne(p.a) && zero(p.b) || isModOne(p.b) && zero(p.a);
494
+ }
495
+ function numericBound(p, allowSelfVar) {
496
+ if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return "drop";
497
+ const n = litOf(p.b);
498
+ if (typeof n !== "number") return "drop";
499
+ if (p.a.op === "app" && p.a.fn === "length") return "skip";
500
+ if (allowSelfVar && isSelfVar(p.a)) return { op: p.op, n };
501
+ if (p.a.op === "app" && (p.a.fn === "get" || p.a.fn === "length")) return "skip";
502
+ return "drop";
503
+ }
504
+ function lengthBound(p) {
505
+ if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
506
+ if (p.a.op !== "app" || p.a.fn !== "length") return void 0;
507
+ const n = litOf(p.b);
508
+ if (typeof n !== "number") return void 0;
509
+ if (p.op === "ge") return { dir: "min", n: Math.ceil(n) };
510
+ if (p.op === "gt") return { dir: "min", n: Math.floor(n) + 1 };
511
+ if (p.op === "le") return { dir: "max", n: Math.floor(n) };
512
+ return { dir: "max", n: Math.ceil(n) - 1 };
513
+ }
514
+ function refinementsFromPreds(preds, opts) {
515
+ const refinements = [];
516
+ const dropped = [];
517
+ let eqLit;
518
+ for (const p of preds) {
519
+ if (p.op === "typeof") continue;
520
+ if (p.op === "eq") {
521
+ if (opts.kind === "number" && isIntModOne(p)) {
522
+ if (!refinements.some((r) => r.kind === "int")) refinements.push({ kind: "int" });
523
+ continue;
524
+ }
525
+ const v = eqLitValue(p);
526
+ if (v === "unanchored") {
527
+ dropped.push(`pred not projected: ${predToString(p)}`);
528
+ continue;
529
+ }
530
+ if (typeof v === "number" && Number.isNaN(v)) {
531
+ dropped.push(`pred not projected (NaN): ${predToString(p)}`);
532
+ continue;
533
+ }
534
+ if (eqLit !== void 0 && eqLit !== v) {
535
+ dropped.push(`conflicting eq preds: ${predToString(p)}`);
536
+ continue;
537
+ }
538
+ eqLit = v;
539
+ continue;
540
+ }
541
+ if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
542
+ if (opts.kind === "string") {
543
+ const lb = lengthBound(p);
544
+ if (lb) {
545
+ refinements.push(lb.dir === "min" ? { kind: "strMin", n: lb.n } : { kind: "strMax", n: lb.n });
546
+ continue;
547
+ }
548
+ dropped.push(`pred not projected: ${predToString(p)}`);
549
+ continue;
550
+ }
551
+ const b = numericBound(p, opts.kind === "number" || opts.kind === "other");
552
+ if (b === "skip") continue;
553
+ if (b === "drop") {
554
+ dropped.push(`pred not projected: ${predToString(p)}`);
555
+ continue;
556
+ }
557
+ if (opts.kind === "number" || opts.kind === "other") {
558
+ refinements.push({ kind: "numBound", op: b.op, n: b.n });
559
+ continue;
560
+ }
561
+ dropped.push(`pred not projected: ${predToString(p)}`);
562
+ continue;
563
+ }
564
+ dropped.push(`pred not projected: ${predToString(p)}`);
565
+ }
566
+ return { refinements, ...eqLit !== void 0 ? { eqLit } : {}, dropped };
567
+ }
568
+ function constraintToSchemaNode(c) {
569
+ if (c.fn) return { k: "fn" };
570
+ if (c.members) {
571
+ return { k: "union", members: c.members.map(constraintToSchemaNode) };
572
+ }
573
+ if (c.fields) {
574
+ return {
575
+ k: "obj",
576
+ slots: Object.entries(c.fields).map(([key, field]) => ({
577
+ key,
578
+ node: constraintToSchemaNode(field.constraint),
579
+ ...field.optional || field.constraint.isOptional ? { optional: true } : {}
580
+ }))
581
+ };
582
+ }
583
+ if (c.element) {
584
+ return { k: "arr", element: constraintToSchemaNode(c.element) };
585
+ }
586
+ const preds = c.preds ?? [];
587
+ const kind = c.prim === "number" ? "number" : c.prim === "string" ? "string" : c.prim === "boolean" ? "boolean" : c.prim ? "other" : "other";
588
+ const { refinements, eqLit, dropped } = refinementsFromPreds(preds, { kind });
589
+ if (eqLit !== void 0 && (kind === "number" || kind === "string" || kind === "boolean" || !c.prim)) {
590
+ return { k: "lit", value: eqLit };
591
+ }
592
+ if (isIntFlag(c) && !refinements.some((r) => r.kind === "int")) {
593
+ refinements.push({ kind: "int" });
594
+ }
595
+ void dropped;
596
+ if (!c.prim) {
597
+ if (Object.keys(c.fields ?? {}).length === 0 && !c.element && !c.members) {
598
+ return { k: "unknown" };
599
+ }
600
+ }
601
+ const type = c.prim === "number" || c.prim === "string" || c.prim === "boolean" || c.prim === "bigint" || c.prim === "symbol" ? c.prim : "unknown";
602
+ if (type === "unknown") return { k: "unknown" };
603
+ return { k: "prim", type, refinements };
604
+ }
605
+ function constraintDropped(c, prefix = "") {
606
+ const out = [];
607
+ const visit = (n, path) => {
608
+ if (n.fields) {
609
+ for (const [k, f] of Object.entries(n.fields)) visit(f.constraint, path ? `${path}.${k}` : k);
610
+ return;
611
+ }
612
+ if (n.members) {
613
+ n.members.forEach((m, i) => visit(m, `${path}[${i}]`));
614
+ return;
615
+ }
616
+ if (n.element) {
617
+ visit(n.element, `${path}[]`);
618
+ return;
619
+ }
620
+ if (n.fn) return;
621
+ const kind = n.prim === "number" ? "number" : n.prim === "string" ? "string" : n.prim === "boolean" ? "boolean" : "other";
622
+ const { eqLit, dropped } = refinementsFromPreds(n.preds ?? [], { kind });
623
+ for (const d of dropped) out.push(path ? `${path}: ${d}` : d);
624
+ if (eqLit === void 0 && n.preds?.some((p) => p.op === "eq") && !isIntFlag(n)) {
625
+ }
626
+ };
627
+ visit(c, prefix);
628
+ return out;
629
+ }
630
+ function absToSchemaNode(a) {
631
+ const dropped = [];
632
+ if (a.term?.op === "lit") {
633
+ const v = a.term.value;
634
+ if (typeof v === "number" && Number.isNaN(v)) {
635
+ dropped.push("lit NaN not projected");
636
+ } else if (v === null || v === void 0 || typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
637
+ return { node: { k: "lit", value: v }, dropped };
638
+ } else if (typeof v === "bigint") {
639
+ dropped.push(`lit bigint not projected: ${String(v)}`);
640
+ return { node: { k: "unknown" }, dropped };
641
+ }
642
+ }
643
+ const conf = a.conf;
644
+ const lowConf = conf !== "exact" && conf !== "path";
645
+ const projected = absToConstraint(a);
646
+ if (projected) {
647
+ const node = constraintToSchemaNode(projected);
648
+ dropped.push(...constraintDropped(projected));
649
+ return { node, dropped };
650
+ }
651
+ if (lowConf) {
652
+ dropped.push(`conf=${conf}: contract projection skipped; shape-only fallback`);
653
+ }
654
+ const s = a.shape;
655
+ switch (s.k) {
656
+ case "never":
657
+ return { node: { k: "never" }, dropped };
658
+ case "any":
659
+ case "unknown":
660
+ return { node: { k: "unknown" }, dropped };
661
+ case "prim": {
662
+ const kind = s.type === "number" ? "number" : s.type === "string" ? "string" : s.type === "boolean" ? "boolean" : "other";
663
+ const leaves = predLeaves(a.pred);
664
+ if (leaves === "unexpressible") {
665
+ if (a.pred && a.pred.op !== "true") {
666
+ dropped.push(`pred not projected: ${predToString(a.pred)}`);
667
+ }
668
+ return { node: { k: "prim", type: s.type, refinements: [] }, dropped };
669
+ }
670
+ const { refinements, eqLit, dropped: d } = refinementsFromPreds(leaves, { kind });
671
+ dropped.push(...d);
672
+ if (eqLit !== void 0 && !Number.isNaN(eqLit)) {
673
+ return { node: { k: "lit", value: eqLit }, dropped };
674
+ }
675
+ return { node: { k: "prim", type: s.type, refinements }, dropped };
676
+ }
677
+ case "obj": {
678
+ if (s.open || s.index) {
679
+ dropped.push("open/index object not fully projected (known slots only)");
680
+ }
681
+ if (a.pred && a.pred.op !== "true") {
682
+ dropped.push(`obj pred not projected: ${predToString(a.pred)}`);
683
+ }
684
+ const slots = [];
685
+ for (const [key, slot] of Object.entries(s.slots)) {
686
+ const sub = absToSchemaNode(slot.value);
687
+ dropped.push(...sub.dropped.map((n) => `${key}: ${n}`));
688
+ slots.push({ key, node: sub.node, ...slot.optional ? { optional: true } : {} });
689
+ }
690
+ return { node: { k: "obj", slots }, dropped };
691
+ }
692
+ case "arr": {
693
+ if (a.pred && a.pred.op !== "true") {
694
+ dropped.push(`arr pred not projected: ${predToString(a.pred)}`);
695
+ }
696
+ const sub = absToSchemaNode(s.element);
697
+ dropped.push(...sub.dropped);
698
+ return { node: { k: "arr", element: sub.node }, dropped };
699
+ }
700
+ case "tuple": {
701
+ const elements = s.elements.map((e) => {
702
+ const sub = absToSchemaNode(e);
703
+ dropped.push(...sub.dropped);
704
+ return sub.node;
705
+ });
706
+ return { node: { k: "tuple", elements }, dropped };
707
+ }
708
+ case "sum": {
709
+ const members = s.members.map((m) => {
710
+ const sub = absToSchemaNode(m);
711
+ dropped.push(...sub.dropped);
712
+ return sub.node;
713
+ });
714
+ return { node: { k: "union", members }, dropped };
715
+ }
716
+ case "fn":
717
+ return { node: { k: "fn" }, dropped };
718
+ case "eff": {
719
+ const inner = absToSchemaNode(s.inner);
720
+ dropped.push(...inner.dropped);
721
+ return {
722
+ node: s.eff === "promise" ? { k: "promise", inner: inner.node } : inner.node,
723
+ dropped
724
+ };
725
+ }
726
+ case "brand":
727
+ return { node: { k: "brand", name: s.name }, dropped };
728
+ default:
729
+ return { node: { k: "unknown" }, dropped };
730
+ }
731
+ }
732
+ function zodApplyRefinements(base, refinements) {
733
+ let out = base;
734
+ if (refinements.some((r) => r.kind === "int")) out += ".int()";
735
+ for (const r of refinements) {
736
+ switch (r.kind) {
737
+ case "numBound":
738
+ if (r.op === "gt") out += `.gt(${r.n})`;
739
+ else if (r.op === "ge") out += `.gte(${r.n})`;
740
+ else if (r.op === "lt") out += `.lt(${r.n})`;
741
+ else out += `.lte(${r.n})`;
742
+ break;
743
+ case "strMin":
744
+ out += `.min(${r.n})`;
745
+ break;
746
+ case "strMax":
747
+ out += `.max(${r.n})`;
748
+ break;
749
+ default:
750
+ break;
751
+ }
752
+ }
753
+ return out;
754
+ }
755
+ function schemaNodeToZod(node) {
756
+ switch (node.k) {
757
+ case "lit": {
758
+ const v = node.value;
759
+ if (v === null) return "z.null()";
760
+ if (v === void 0) return "z.undefined()";
761
+ if (typeof v === "string") return `z.literal(${JSON.stringify(v)})`;
762
+ if (typeof v === "boolean") return `z.literal(${v})`;
763
+ if (typeof v === "number") return `z.literal(${v})`;
764
+ return "z.unknown()";
765
+ }
766
+ case "prim":
767
+ return zodApplyRefinements(`z.${node.type}()`, node.refinements);
768
+ case "obj": {
769
+ const entries = node.slots.map((slot) => {
770
+ const inner = schemaNodeToZod(slot.node);
771
+ return `${slot.key}: ${slot.optional ? `${inner}.optional()` : inner}`;
772
+ }).join(", ");
773
+ return `z.object({ ${entries} })`;
774
+ }
775
+ case "arr":
776
+ return `z.array(${schemaNodeToZod(node.element)})`;
777
+ case "tuple":
778
+ return `z.tuple([${node.elements.map(schemaNodeToZod).join(", ")}])`;
779
+ case "union":
780
+ return `z.union([${node.members.map(schemaNodeToZod).join(", ")}])`;
781
+ case "fn":
782
+ return "z.function()";
783
+ case "promise":
784
+ return `z.promise(${schemaNodeToZod(node.inner)})`;
785
+ case "brand":
786
+ return `z.instanceof(${node.name})`;
787
+ case "never":
788
+ return "z.never()";
789
+ case "unknown":
790
+ default:
791
+ return "z.unknown()";
792
+ }
793
+ }
794
+ var DIALECT_RENDERERS = {
795
+ zod: schemaNodeToZod
796
+ };
797
+ function projectAbsToSchema(a, opts) {
798
+ const dialect = opts?.dialect ?? "zod";
799
+ const { node, dropped } = absToSchemaNode(a);
800
+ const render = DIALECT_RENDERERS[dialect] ?? schemaNodeToZod;
801
+ return { source: render(node), dialect, dropped };
802
+ }
803
+ function absToSchemaSource(a, opts) {
804
+ return projectAbsToSchema(a, opts).source;
805
+ }
806
+
807
+ // src/standard-schema.ts
808
+ function pushIssue(issues, path, message) {
809
+ issues.push(path.length > 0 ? { message, path: [...path] } : { message });
810
+ }
811
+ function typeOf(v) {
812
+ if (v === null) return "null";
813
+ if (Array.isArray(v)) return "array";
814
+ return typeof v;
815
+ }
816
+ function checkPrimRefinements(refinements, value, path, issues) {
817
+ if (typeof value !== "number" && typeof value !== "string") return;
818
+ for (const r of refinements) {
819
+ if (r.kind === "int") {
820
+ if (typeof value === "number" && !Number.isInteger(value)) {
821
+ pushIssue(issues, path, "expected integer");
822
+ }
823
+ continue;
824
+ }
825
+ if (r.kind === "numBound" && typeof value === "number" && typeof r.n === "number") {
826
+ const n = r.n;
827
+ const op = r.op;
828
+ let ok = true;
829
+ if (op === "gt") ok = value > n;
830
+ else if (op === "ge") ok = value >= n;
831
+ else if (op === "lt") ok = value < n;
832
+ else if (op === "le") ok = value <= n;
833
+ if (!ok) pushIssue(issues, path, `expected number ${op} ${n}, got ${value}`);
834
+ continue;
835
+ }
836
+ if (r.kind === "strMin" && typeof value === "string" && typeof r.n === "number") {
837
+ if (value.length < r.n) {
838
+ pushIssue(issues, path, `expected string length >= ${r.n}, got ${value.length}`);
839
+ }
840
+ continue;
841
+ }
842
+ if (r.kind === "strMax" && typeof value === "string" && typeof r.n === "number") {
843
+ if (value.length > r.n) {
844
+ pushIssue(issues, path, `expected string length <= ${r.n}, got ${value.length}`);
845
+ }
846
+ }
847
+ }
848
+ }
849
+ function checkNode(node, value, path, issues) {
850
+ switch (node.k) {
851
+ case "unknown":
852
+ case "fn":
853
+ case "brand":
854
+ case "promise":
855
+ return;
856
+ case "never":
857
+ pushIssue(issues, path, "expected never");
858
+ return;
859
+ case "lit": {
860
+ const expected = node.value;
861
+ const same = expected === value || typeof expected === "number" && typeof value === "number" && Number.isNaN(expected) && Number.isNaN(value);
862
+ if (!same) {
863
+ pushIssue(issues, path, `expected ${JSON.stringify(expected)}, got ${JSON.stringify(value)}`);
864
+ }
865
+ return;
866
+ }
867
+ case "prim": {
868
+ const t = node.type;
869
+ const actual = typeOf(value);
870
+ const ok = t === "number" && actual === "number" || t === "string" && actual === "string" || t === "boolean" && actual === "boolean" || t === "bigint" && actual === "bigint" || t === "symbol" && actual === "symbol";
871
+ if (!ok) {
872
+ pushIssue(issues, path, `expected ${t}, got ${actual}`);
873
+ return;
874
+ }
875
+ checkPrimRefinements(node.refinements, value, path, issues);
876
+ return;
877
+ }
878
+ case "obj": {
879
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
880
+ pushIssue(issues, path, `expected object, got ${typeOf(value)}`);
881
+ return;
882
+ }
883
+ const rec = value;
884
+ for (const slot of node.slots) {
885
+ const key = slot.key;
886
+ if (!(key in rec) || rec[key] === void 0) {
887
+ if (!slot.optional) pushIssue(issues, [...path, key], "required");
888
+ continue;
889
+ }
890
+ checkNode(slot.node, rec[key], [...path, key], issues);
891
+ }
892
+ return;
893
+ }
894
+ case "arr": {
895
+ if (!Array.isArray(value)) {
896
+ pushIssue(issues, path, `expected array, got ${typeOf(value)}`);
897
+ return;
898
+ }
899
+ value.forEach((item, i) => {
900
+ checkNode(node.element, item, [...path, i], issues);
901
+ });
902
+ return;
903
+ }
904
+ case "tuple": {
905
+ if (!Array.isArray(value)) {
906
+ pushIssue(issues, path, `expected tuple, got ${typeOf(value)}`);
907
+ return;
908
+ }
909
+ node.elements.forEach((el, i) => {
910
+ checkNode(el, value[i], [...path, i], issues);
911
+ });
912
+ return;
913
+ }
914
+ case "union": {
915
+ for (const m of node.members) {
916
+ const local = [];
917
+ checkNode(m, value, path, local);
918
+ if (local.length === 0) return;
919
+ }
920
+ pushIssue(
921
+ issues,
922
+ path,
923
+ `expected one of ${node.members.length} union members, got ${typeOf(value)}`
924
+ );
925
+ return;
926
+ }
927
+ case "promise": {
928
+ return;
929
+ }
930
+ default:
931
+ return;
932
+ }
933
+ }
934
+ function validateSchemaNode(node, value) {
935
+ const issues = [];
936
+ checkNode(node, value, [], issues);
937
+ return issues.length === 0 ? { value } : { issues };
938
+ }
939
+ var CHECK_FN_SOURCE = `
940
+ function __nudoTypeOf(v) {
941
+ if (v === null) return "null";
942
+ if (Array.isArray(v)) return "array";
943
+ return typeof v;
944
+ }
945
+ function __nudoCheck(node, value, path, issues) {
946
+ function push(msg) {
947
+ issues.push(path.length > 0 ? { message: msg, path: path.slice() } : { message: msg });
948
+ }
949
+ switch (node.k) {
950
+ case "unknown":
951
+ case "summarized":
952
+ case "fn":
953
+ case "brand":
954
+ case "promise":
955
+ return;
956
+ case "never":
957
+ push("expected never");
958
+ return;
959
+ case "lit": {
960
+ const expected = node.value;
961
+ const same = expected === value ||
962
+ (typeof expected === "number" && typeof value === "number" &&
963
+ Number.isNaN(expected) && Number.isNaN(value));
964
+ if (!same) push("expected " + JSON.stringify(expected) + ", got " + JSON.stringify(value));
965
+ return;
966
+ }
967
+ case "prim": {
968
+ const t = node.type;
969
+ const actual = __nudoTypeOf(value);
970
+ const ok =
971
+ (t === "number" && actual === "number") ||
972
+ (t === "string" && actual === "string") ||
973
+ (t === "boolean" && actual === "boolean") ||
974
+ (t === "bigint" && actual === "bigint") ||
975
+ (t === "symbol" && actual === "symbol");
976
+ if (!ok) { push("expected " + t + ", got " + actual); return; }
977
+ for (const r of node.refinements || []) {
978
+ if (r.kind === "int" && typeof value === "number" && !Number.isInteger(value)) {
979
+ push("expected integer");
980
+ } else if (r.kind === "numBound" && typeof value === "number" && typeof r.n === "number") {
981
+ var okB = true;
982
+ if (r.op === "gt") okB = value > r.n;
983
+ else if (r.op === "ge") okB = value >= r.n;
984
+ else if (r.op === "lt") okB = value < r.n;
985
+ else if (r.op === "le") okB = value <= r.n;
986
+ if (!okB) push("expected number " + r.op + " " + r.n + ", got " + value);
987
+ } else if (r.kind === "strMin" && typeof value === "string" && typeof r.n === "number") {
988
+ if (value.length < r.n) push("expected string length >= " + r.n + ", got " + value.length);
989
+ } else if (r.kind === "strMax" && typeof value === "string" && typeof r.n === "number") {
990
+ if (value.length > r.n) push("expected string length <= " + r.n + ", got " + value.length);
991
+ }
992
+ }
993
+ return;
994
+ }
995
+ case "obj": {
996
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
997
+ push("expected object, got " + __nudoTypeOf(value));
998
+ return;
999
+ }
1000
+ for (const slot of node.slots) {
1001
+ const key = slot.key;
1002
+ if (!(key in value) || value[key] === undefined) {
1003
+ if (!slot.optional) { path.push(key); push("required"); path.pop(); }
1004
+ continue;
1005
+ }
1006
+ path.push(key);
1007
+ __nudoCheck(slot.node, value[key], path, issues);
1008
+ path.pop();
1009
+ }
1010
+ return;
1011
+ }
1012
+ case "arr": {
1013
+ if (!Array.isArray(value)) { push("expected array, got " + __nudoTypeOf(value)); return; }
1014
+ for (let i = 0; i < value.length; i++) {
1015
+ path.push(i);
1016
+ __nudoCheck(node.element, value[i], path, issues);
1017
+ path.pop();
1018
+ }
1019
+ return;
1020
+ }
1021
+ case "tuple": {
1022
+ if (!Array.isArray(value)) { push("expected tuple, got " + __nudoTypeOf(value)); return; }
1023
+ for (let i = 0; i < node.elements.length; i++) {
1024
+ path.push(i);
1025
+ __nudoCheck(node.elements[i], value[i], path, issues);
1026
+ path.pop();
1027
+ }
1028
+ return;
1029
+ }
1030
+ case "union": {
1031
+ for (const m of node.members) {
1032
+ const local = [];
1033
+ __nudoCheck(m, value, path, local);
1034
+ if (local.length === 0) return;
1035
+ }
1036
+ push("expected one of " + node.members.length + " union members, got " + __nudoTypeOf(value));
1037
+ return;
1038
+ }
1039
+ default:
1040
+ return;
1041
+ }
1042
+ }
1043
+ `.trim();
1044
+ function makeStandardSchemaSource(exportName, node, dropped) {
1045
+ const json = JSON.stringify(node);
1046
+ const notes = dropped.length > 0 ? dropped.map((d) => `// ${d}`).join("\n") + "\n" : "";
1047
+ return `${notes}export const ${exportName} = {
1048
+ "~standard": {
1049
+ version: 1,
1050
+ vendor: "nudo",
1051
+ validate(value) {
1052
+ const issues = [];
1053
+ __nudoCheck(${json}, value, [], issues);
1054
+ return issues.length ? { issues } : { value };
1055
+ },
1056
+ },
1057
+ } as const;`;
1058
+ }
1059
+ function absToStandardSchemaModule(exports, opts) {
1060
+ const dropped = [];
1061
+ const bodies = [];
1062
+ for (const [name, abs] of Object.entries(exports)) {
1063
+ const { node, dropped: d } = absToSchemaNode(abs);
1064
+ dropped.push(...d.map((n) => `${name}: ${n}`));
1065
+ bodies.push(makeStandardSchemaSource(name, node, d.map((n) => `${name}: ${n}`)));
1066
+ }
1067
+ const banner = opts?.banner ?? `// @generated by nudo export --format standard \u2014 Standard Schema v1 (vendor: nudo)
1068
+ // One-way lossy projection of Abs; do not edit. nudo check remains the gate.`;
1069
+ const source = `${banner}
1070
+
1071
+ ${CHECK_FN_SOURCE}
1072
+
1073
+ ${bodies.join("\n\n")}
1074
+ `;
1075
+ return { source, dropped };
1076
+ }
1077
+ function absToStandardSchema(a, opts) {
1078
+ return absToStandardSchemaModule({ [opts?.name ?? "schema"]: a });
1079
+ }
1080
+
1081
+ // src/guard-generator.ts
1082
+ import { denoteGuard } from "@nudojs/core/internal";
1083
+ function generateGuardFunctionFromAbs(name, abs) {
1084
+ const body = denoteGuard(abs, "data");
1085
+ return `export function ${name}(data) {
1086
+ return ${body};
1087
+ }`;
1088
+ }
1089
+ function generateGuardFunction(name, abs) {
1090
+ return generateGuardFunctionFromAbs(name, abs);
1091
+ }
1092
+
1093
+ export {
1094
+ absToTSType,
1095
+ generateFunctionDtsLines,
1096
+ generateDts,
1097
+ constraintToSchemaNode,
1098
+ absToSchemaNode,
1099
+ schemaNodeToZod,
1100
+ projectAbsToSchema,
1101
+ absToSchemaSource,
1102
+ validateSchemaNode,
1103
+ absToStandardSchemaModule,
1104
+ absToStandardSchema,
1105
+ generateGuardFunctionFromAbs,
1106
+ generateGuardFunction
1107
+ };