@toolproof-core/schema 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/generated/schemas/zod/Goal.d.ts +2 -0
  2. package/dist/generated/schemas/zod/Goal.js +5 -0
  3. package/dist/generated/schemas/zod/Job.d.ts +6 -0
  4. package/dist/generated/schemas/zod/Job.js +25 -0
  5. package/dist/generated/schemas/zod/RawStrategy.d.ts +12 -0
  6. package/dist/generated/schemas/zod/RawStrategy.js +66 -0
  7. package/dist/generated/schemas/zod/ResourceType.d.ts +2 -0
  8. package/dist/generated/schemas/zod/ResourceType.js +18 -0
  9. package/dist/generated/schemas/zod/RunnableStrategy.d.ts +12 -0
  10. package/dist/generated/schemas/zod/RunnableStrategy.js +74 -0
  11. package/dist/generated/schemas/zod/StrategyRun.d.ts +12 -0
  12. package/dist/generated/schemas/zod/StrategyRun.js +88 -0
  13. package/dist/generated/schemas/zod/index.d.ts +6 -0
  14. package/dist/generated/schemas/zod/index.js +7 -0
  15. package/dist/index.d.ts +11 -1
  16. package/dist/index.js +12 -0
  17. package/dist/internals/booleans.json +32 -0
  18. package/dist/internals/jobs.json +266 -0
  19. package/dist/internals/naturals.json +167 -0
  20. package/dist/scripts/_lib/config.d.ts +3 -0
  21. package/dist/scripts/_lib/config.js +9 -0
  22. package/dist/scripts/_lib/utils/jsonSchemaToZod.d.ts +18 -0
  23. package/dist/scripts/_lib/utils/jsonSchemaToZod.js +607 -0
  24. package/dist/scripts/generateStandaloneZodSchema.d.ts +1 -0
  25. package/dist/scripts/generateStandaloneZodSchema.js +116 -0
  26. package/package.json +6 -2
  27. package/src/generated/schemas/zod/Goal.ts +8 -0
  28. package/src/generated/schemas/zod/Job.ts +26 -0
  29. package/src/generated/schemas/zod/RawStrategy.ts +64 -0
  30. package/src/generated/schemas/zod/ResourceType.ts +13 -0
  31. package/src/generated/schemas/zod/RunnableStrategy.ts +69 -0
  32. package/src/generated/schemas/zod/StrategyRun.ts +83 -0
  33. package/src/generated/schemas/zod/index.ts +7 -0
  34. package/src/index.ts +15 -0
  35. package/src/internals/booleans.json +32 -0
  36. package/src/internals/jobs.json +266 -0
  37. package/src/internals/naturals.json +167 -0
  38. package/src/scripts/_lib/config.ts +12 -0
  39. package/src/scripts/_lib/utils/jsonSchemaToZod.ts +646 -0
  40. package/src/scripts/generateStandaloneZodSchema.ts +139 -0
@@ -0,0 +1,607 @@
1
+ /*
2
+ * Best-effort JSON Schema (draft 2020-12-ish) -> Zod v4 code generator.
3
+ *
4
+ * Intent:
5
+ * - Convert ToolProof standalone schemas (generated by generateStandaloneSchema.ts)
6
+ * into runtime validators usable by consumers.
7
+ * - Be explicit about approximations: return warnings for keywords we ignore or
8
+ * cannot faithfully map.
9
+ */
10
+ function isObject(v) {
11
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
12
+ }
13
+ function pathJoin(base, key) {
14
+ if (!base)
15
+ return String(key);
16
+ if (typeof key === 'number')
17
+ return `${base}[${key}]`;
18
+ return `${base}.${key}`;
19
+ }
20
+ function warn(ctx, path, message) {
21
+ ctx.warnings.push({ path, message });
22
+ }
23
+ function asDefSchemaRef(defName) {
24
+ return `${defName}Schema`;
25
+ }
26
+ function extractPointerDefName(ref) {
27
+ if (!ref || typeof ref !== 'string')
28
+ return null;
29
+ if (!ref.startsWith('#/'))
30
+ return null;
31
+ const parts = ref.slice(2).split('/');
32
+ if (parts.length !== 2)
33
+ return null;
34
+ if (parts[0] !== '$defs')
35
+ return null;
36
+ const name = parts[1].replace(/~1/g, '/').replace(/~0/g, '~');
37
+ return name;
38
+ }
39
+ function resolveLocalRef(ref, ctx) {
40
+ const byPointer = extractPointerDefName(ref);
41
+ if (byPointer && ctx.defNames.has(byPointer))
42
+ return byPointer;
43
+ if (ref.startsWith('#') && !ref.startsWith('#/')) {
44
+ const anchor = ref.slice(1);
45
+ const defName = ctx.anchorToDefName[anchor];
46
+ if (defName)
47
+ return defName;
48
+ }
49
+ return null;
50
+ }
51
+ function jsonString(value) {
52
+ return JSON.stringify(value);
53
+ }
54
+ function emitRegex(pattern) {
55
+ return `new RegExp(${jsonString(pattern)})`;
56
+ }
57
+ function hasOwn(obj, key) {
58
+ return Object.prototype.hasOwnProperty.call(obj, key);
59
+ }
60
+ function isBooleanSchema(node) {
61
+ return typeof node === 'boolean';
62
+ }
63
+ function isDictLikeObjectSchema(node) {
64
+ if (!isObject(node))
65
+ return false;
66
+ if (node.type !== 'object')
67
+ return false;
68
+ const hasFixedProps = hasOwn(node, 'properties') && isObject(node.properties) && Object.keys(node.properties).length > 0;
69
+ const hasPropertyNames = isObject(node.propertyNames);
70
+ const hasAdditionalSchema = isObject(node.additionalProperties) || isBooleanSchema(node.additionalProperties);
71
+ return !hasFixedProps && hasPropertyNames && hasAdditionalSchema;
72
+ }
73
+ function emitDictLikeObjectSchema(node, ctx, path) {
74
+ const valueSchemaNode = node.additionalProperties;
75
+ const valueExpr = emitSchema(valueSchemaNode, ctx, pathJoin(path, 'additionalProperties'));
76
+ const pn = node.propertyNames;
77
+ let keyPattern;
78
+ if (isObject(pn)) {
79
+ if (typeof pn.pattern === 'string') {
80
+ keyPattern = pn.pattern;
81
+ }
82
+ else if (typeof pn.$ref === 'string') {
83
+ const defName = resolveLocalRef(pn.$ref, ctx);
84
+ const resolved = defName ? ctx.defsByName[defName] : undefined;
85
+ if (isObject(resolved) && typeof resolved.pattern === 'string') {
86
+ keyPattern = resolved.pattern;
87
+ }
88
+ else {
89
+ warn(ctx, pathJoin(path, 'propertyNames'), 'Unsupported propertyNames $ref target; keys will not be validated.');
90
+ }
91
+ }
92
+ else {
93
+ warn(ctx, pathJoin(path, 'propertyNames'), 'Unsupported propertyNames shape; keys will not be validated.');
94
+ }
95
+ }
96
+ else {
97
+ warn(ctx, pathJoin(path, 'propertyNames'), 'Unsupported propertyNames shape; keys will not be validated.');
98
+ }
99
+ const minProps = typeof node.minProperties === 'number' ? node.minProperties : undefined;
100
+ const maxProps = typeof node.maxProperties === 'number' ? node.maxProperties : undefined;
101
+ const checks = [];
102
+ if (keyPattern) {
103
+ checks.push(`for (const k of Object.keys(obj)) { if (!${emitRegex(keyPattern)}.test(k)) ctx.addIssue({ code: 'custom', message: 'Invalid key: ' + k }); }`);
104
+ }
105
+ if (minProps !== undefined) {
106
+ checks.push(`if (Object.keys(obj).length < ${minProps}) ctx.addIssue({ code: 'custom', message: 'Expected at least ${minProps} properties' });`);
107
+ }
108
+ if (maxProps !== undefined) {
109
+ checks.push(`if (Object.keys(obj).length > ${maxProps}) ctx.addIssue({ code: 'custom', message: 'Expected at most ${maxProps} properties' });`);
110
+ }
111
+ if (checks.length === 0) {
112
+ return `z.record(z.string(), ${valueExpr})`;
113
+ }
114
+ return `z.record(z.string(), ${valueExpr}).superRefine((obj, ctx) => { ${checks.join(' ')} })`;
115
+ }
116
+ function resolveSchemaIfRef(node, ctx) {
117
+ if (isObject(node) && typeof node.$ref === 'string') {
118
+ const defName = resolveLocalRef(node.$ref, ctx);
119
+ if (defName)
120
+ return ctx.defsByName[defName];
121
+ }
122
+ return node;
123
+ }
124
+ function isDirectObjectishSchema(node) {
125
+ if (!isObject(node))
126
+ return false;
127
+ if (node.type === 'object')
128
+ return true;
129
+ return (hasOwn(node, 'properties') ||
130
+ hasOwn(node, 'required') ||
131
+ hasOwn(node, 'additionalProperties') ||
132
+ hasOwn(node, 'unevaluatedProperties') ||
133
+ hasOwn(node, 'propertyNames') ||
134
+ hasOwn(node, 'patternProperties') ||
135
+ hasOwn(node, 'dependentRequired') ||
136
+ hasOwn(node, 'dependentSchemas'));
137
+ }
138
+ function expandAllOfObjectishSchemas(node, ctx, path, seen) {
139
+ const resolved = resolveSchemaIfRef(node, ctx);
140
+ if (!isObject(resolved))
141
+ return null;
142
+ if (seen.has(resolved))
143
+ return null;
144
+ seen.add(resolved);
145
+ if (isDirectObjectishSchema(resolved))
146
+ return [resolved];
147
+ if (Array.isArray(resolved.allOf) && resolved.allOf.length > 0) {
148
+ const out = [];
149
+ for (let i = 0; i < resolved.allOf.length; i++) {
150
+ const child = resolved.allOf[i];
151
+ const expanded = expandAllOfObjectishSchemas(child, ctx, pathJoin(pathJoin(path, 'allOf'), i), seen);
152
+ if (!expanded)
153
+ return null;
154
+ out.push(...expanded);
155
+ }
156
+ return out;
157
+ }
158
+ return null;
159
+ }
160
+ function mergePropertySchemas(a, b) {
161
+ if (a === undefined)
162
+ return b;
163
+ if (b === undefined)
164
+ return a;
165
+ return { allOf: [a, b] };
166
+ }
167
+ function mergeAllOfObjectSchemas(allOfSchemas, ctx, path) {
168
+ const flattened = [];
169
+ const seen = new Set();
170
+ for (let i = 0; i < allOfSchemas.length; i++) {
171
+ const expanded = expandAllOfObjectishSchemas(allOfSchemas[i], ctx, pathJoin(pathJoin(path, 'allOf'), i), seen);
172
+ if (!expanded)
173
+ return null;
174
+ flattened.push(...expanded);
175
+ }
176
+ const merged = { type: 'object', properties: {} };
177
+ const required = new Set();
178
+ let additionalProperties = undefined;
179
+ let unevaluatedProperties = undefined;
180
+ for (let i = 0; i < flattened.length; i++) {
181
+ const s = flattened[i];
182
+ const p = pathJoin(pathJoin(path, 'allOfFlattened'), i);
183
+ if (!isObject(s))
184
+ continue;
185
+ if (isObject(s.properties)) {
186
+ for (const [k, v] of Object.entries(s.properties)) {
187
+ merged.properties[k] = mergePropertySchemas(merged.properties[k], v);
188
+ }
189
+ }
190
+ if (Array.isArray(s.required)) {
191
+ for (const r of s.required)
192
+ if (typeof r === 'string')
193
+ required.add(r);
194
+ }
195
+ if (hasOwn(s, 'additionalProperties')) {
196
+ if (additionalProperties === undefined) {
197
+ additionalProperties = s.additionalProperties;
198
+ }
199
+ else {
200
+ if (additionalProperties === false || s.additionalProperties === false) {
201
+ additionalProperties = false;
202
+ }
203
+ else if (isObject(additionalProperties) && isObject(s.additionalProperties)) {
204
+ additionalProperties = { allOf: [additionalProperties, s.additionalProperties] };
205
+ }
206
+ else {
207
+ additionalProperties = isObject(additionalProperties)
208
+ ? additionalProperties
209
+ : (isObject(s.additionalProperties) ? s.additionalProperties : true);
210
+ }
211
+ }
212
+ }
213
+ if (s.unevaluatedProperties === false) {
214
+ // Preserve the flag on the merged schema; it can be enforced once the
215
+ // object facets are merged into a single z.object(...).
216
+ unevaluatedProperties = false;
217
+ }
218
+ else if (hasOwn(s, 'unevaluatedProperties')) {
219
+ warn(ctx, pathJoin(p, 'unevaluatedProperties'), 'Unsupported unevaluatedProperties value; ignoring.');
220
+ }
221
+ if (hasOwn(s, 'propertyNames') && !hasOwn(merged, 'propertyNames'))
222
+ merged.propertyNames = s.propertyNames;
223
+ if (hasOwn(s, 'patternProperties') && !hasOwn(merged, 'patternProperties'))
224
+ merged.patternProperties = s.patternProperties;
225
+ if (hasOwn(s, 'dependentRequired') && !hasOwn(merged, 'dependentRequired'))
226
+ merged.dependentRequired = s.dependentRequired;
227
+ if (hasOwn(s, 'dependentSchemas') && !hasOwn(merged, 'dependentSchemas'))
228
+ merged.dependentSchemas = s.dependentSchemas;
229
+ }
230
+ if (Object.keys(merged.properties).length === 0)
231
+ delete merged.properties;
232
+ if (required.size > 0)
233
+ merged.required = Array.from(required);
234
+ if (additionalProperties !== undefined)
235
+ merged.additionalProperties = additionalProperties;
236
+ if (unevaluatedProperties === false)
237
+ merged.unevaluatedProperties = false;
238
+ return merged;
239
+ }
240
+ function emitAllOf(node, ctx, path) {
241
+ const arr = node.allOf;
242
+ if (!Array.isArray(arr) || arr.length === 0) {
243
+ warn(ctx, pathJoin(path, 'allOf'), 'Expected non-empty allOf array; treating as z.any().');
244
+ return 'z.any()';
245
+ }
246
+ const mergedObject = mergeAllOfObjectSchemas(arr, ctx, path);
247
+ if (mergedObject) {
248
+ return emitSchema(mergedObject, ctx, pathJoin(path, 'allOfMerged'));
249
+ }
250
+ const parts = arr.map((s, i) => emitSchema(s, ctx, pathJoin(pathJoin(path, 'allOf'), i)));
251
+ let out = parts[0];
252
+ for (let i = 1; i < parts.length; i++) {
253
+ out = `z.intersection(${out}, ${parts[i]})`;
254
+ }
255
+ return out;
256
+ }
257
+ function emitAnyOfUnion(kind, node, ctx, path) {
258
+ const arr = node[kind];
259
+ if (!Array.isArray(arr) || arr.length === 0) {
260
+ warn(ctx, pathJoin(path, kind), `Expected non-empty ${kind} array; treating as z.any().`);
261
+ return 'z.any()';
262
+ }
263
+ const parts = arr.map((s, i) => emitSchema(s, ctx, pathJoin(pathJoin(path, kind), i)));
264
+ return `z.union([${parts.join(', ')}])`;
265
+ }
266
+ function emitEnum(node, ctx, path) {
267
+ const values = node.enum;
268
+ if (!Array.isArray(values) || values.length === 0) {
269
+ warn(ctx, pathJoin(path, 'enum'), 'Expected non-empty enum array; treating as z.any().');
270
+ return 'z.any()';
271
+ }
272
+ const literals = values.map((v) => `z.literal(${jsonString(v)})`);
273
+ return `z.union([${literals.join(', ')}])`;
274
+ }
275
+ function emitType(typeValue, node, ctx, path) {
276
+ const types = Array.isArray(typeValue) ? typeValue : [typeValue];
277
+ const exprs = [];
278
+ for (const t of types) {
279
+ if (t === 'string') {
280
+ let s = 'z.string()';
281
+ if (typeof node.minLength === 'number')
282
+ s += `.min(${node.minLength})`;
283
+ if (typeof node.maxLength === 'number')
284
+ s += `.max(${node.maxLength})`;
285
+ if (typeof node.pattern === 'string')
286
+ s += `.regex(${emitRegex(node.pattern)})`;
287
+ if (typeof node.format === 'string') {
288
+ // Enforce only a small subset where Zod semantics are reasonably aligned.
289
+ if (node.format === 'uri') {
290
+ s += '.url()';
291
+ }
292
+ else if (node.format === 'date-time') {
293
+ s += '.datetime()';
294
+ }
295
+ else if (node.format === 'uuid') {
296
+ s += '.uuid()';
297
+ }
298
+ else {
299
+ warn(ctx, pathJoin(path, 'format'), `Ignoring format '${node.format}' (not enforced by generated Zod).`);
300
+ }
301
+ }
302
+ exprs.push(s);
303
+ }
304
+ else if (t === 'number') {
305
+ let n = 'z.number()';
306
+ if (typeof node.minimum === 'number')
307
+ n += `.min(${node.minimum})`;
308
+ if (typeof node.maximum === 'number')
309
+ n += `.max(${node.maximum})`;
310
+ if (typeof node.exclusiveMinimum === 'number')
311
+ n += `.gt(${node.exclusiveMinimum})`;
312
+ if (typeof node.exclusiveMaximum === 'number')
313
+ n += `.lt(${node.exclusiveMaximum})`;
314
+ if (typeof node.multipleOf === 'number')
315
+ n += `.multipleOf(${node.multipleOf})`;
316
+ exprs.push(n);
317
+ }
318
+ else if (t === 'integer') {
319
+ let n = 'z.number().int()';
320
+ if (typeof node.minimum === 'number')
321
+ n += `.min(${node.minimum})`;
322
+ if (typeof node.maximum === 'number')
323
+ n += `.max(${node.maximum})`;
324
+ if (typeof node.exclusiveMinimum === 'number')
325
+ n += `.gt(${node.exclusiveMinimum})`;
326
+ if (typeof node.exclusiveMaximum === 'number')
327
+ n += `.lt(${node.exclusiveMaximum})`;
328
+ if (typeof node.multipleOf === 'number')
329
+ n += `.multipleOf(${node.multipleOf})`;
330
+ exprs.push(n);
331
+ }
332
+ else if (t === 'boolean') {
333
+ exprs.push('z.boolean()');
334
+ }
335
+ else if (t === 'null') {
336
+ exprs.push('z.null()');
337
+ }
338
+ else if (t === 'array') {
339
+ const itemsExpr = hasOwn(node, 'items') ? emitSchema(node.items, ctx, pathJoin(path, 'items')) : 'z.any()';
340
+ let a = `z.array(${itemsExpr})`;
341
+ if (typeof node.minItems === 'number')
342
+ a += `.min(${node.minItems})`;
343
+ if (typeof node.maxItems === 'number')
344
+ a += `.max(${node.maxItems})`;
345
+ if (node.uniqueItems === true) {
346
+ warn(ctx, pathJoin(path, 'uniqueItems'), 'uniqueItems not enforced by generated Zod.');
347
+ }
348
+ exprs.push(a);
349
+ }
350
+ else if (t === 'object') {
351
+ exprs.push(emitObject(node, ctx, path));
352
+ }
353
+ else {
354
+ warn(ctx, pathJoin(path, 'type'), `Unsupported type '${String(t)}'; treating as z.any().`);
355
+ exprs.push('z.any()');
356
+ }
357
+ }
358
+ if (exprs.length === 1)
359
+ return exprs[0];
360
+ return `z.union([${exprs.join(', ')}])`;
361
+ }
362
+ function emitObject(node, ctx, path) {
363
+ if (isDictLikeObjectSchema(node)) {
364
+ return emitDictLikeObjectSchema(node, ctx, path);
365
+ }
366
+ const props = isObject(node.properties) ? node.properties : {};
367
+ const required = Array.isArray(node.required) ? new Set(node.required.filter((x) => typeof x === 'string')) : new Set();
368
+ const propEntries = [];
369
+ for (const [key, schemaNode] of Object.entries(props)) {
370
+ const expr = emitSchema(schemaNode, ctx, pathJoin(pathJoin(path, 'properties'), key));
371
+ const withOptional = required.has(key) ? expr : `${expr}.optional()`;
372
+ propEntries.push(`${jsonString(key)}: ${withOptional}`);
373
+ }
374
+ let out = `z.object({ ${propEntries.join(', ')} })`;
375
+ const hasAllOf = Array.isArray(node.allOf) && node.allOf.length > 0;
376
+ const additional = node.additionalProperties;
377
+ const unevaluated = node.unevaluatedProperties;
378
+ const hasPatternProperties = isObject(node.patternProperties);
379
+ if (additional === false) {
380
+ if (hasAllOf) {
381
+ warn(ctx, pathJoin(path, 'additionalProperties'), 'additionalProperties:false with allOf is approximated; not emitting .strict().');
382
+ }
383
+ else {
384
+ out += '.strict()';
385
+ }
386
+ }
387
+ else if (isObject(additional) || isBooleanSchema(additional)) {
388
+ if (additional === true) {
389
+ // passthrough is closest to allowing extras without validation
390
+ out += '.passthrough()';
391
+ }
392
+ else if (additional === false) {
393
+ // handled above
394
+ }
395
+ else {
396
+ const catchallExpr = emitSchema(additional, ctx, pathJoin(path, 'additionalProperties'));
397
+ out += `.catchall(${catchallExpr})`;
398
+ }
399
+ }
400
+ if (unevaluated === false) {
401
+ if (hasAllOf) {
402
+ warn(ctx, pathJoin(path, 'unevaluatedProperties'), 'unevaluatedProperties:false with allOf is approximated; not emitting .strict().');
403
+ }
404
+ else if (hasPatternProperties) {
405
+ warn(ctx, pathJoin(path, 'unevaluatedProperties'), 'unevaluatedProperties:false with patternProperties is approximated; not emitting .strict().');
406
+ }
407
+ else if (hasOwn(node, 'additionalProperties') && additional !== false) {
408
+ // additionalProperties evaluates extra keys; unevaluatedProperties:false doesn't
409
+ // further restrict them, so .strict() would be incorrect.
410
+ }
411
+ else {
412
+ out += '.strict()';
413
+ }
414
+ }
415
+ else if (unevaluated !== undefined) {
416
+ warn(ctx, pathJoin(path, 'unevaluatedProperties'), 'Unsupported unevaluatedProperties value; ignoring.');
417
+ }
418
+ if (typeof node.minProperties === 'number') {
419
+ out += `.refine((o) => Object.keys(o).length >= ${node.minProperties}, { message: 'Expected at least ${node.minProperties} properties' })`;
420
+ }
421
+ if (typeof node.maxProperties === 'number') {
422
+ out += `.refine((o) => Object.keys(o).length <= ${node.maxProperties}, { message: 'Expected at most ${node.maxProperties} properties' })`;
423
+ }
424
+ if (isObject(node.propertyNames)) {
425
+ // Zod doesn't support propertyNames on object schemas directly; warn.
426
+ warn(ctx, pathJoin(path, 'propertyNames'), 'propertyNames on fixed-shape object not enforced by generated Zod.');
427
+ }
428
+ if (hasPatternProperties) {
429
+ warn(ctx, pathJoin(path, 'patternProperties'), 'patternProperties not enforced by generated Zod.');
430
+ }
431
+ if (isObject(node.dependentRequired) || isObject(node.dependentSchemas)) {
432
+ warn(ctx, pathJoin(path, 'dependentRequired'), 'dependentRequired/dependentSchemas not enforced by generated Zod.');
433
+ }
434
+ return out;
435
+ }
436
+ function emitSchema(node, ctx, path) {
437
+ if (isBooleanSchema(node)) {
438
+ return node ? 'z.any()' : 'z.never()';
439
+ }
440
+ if (!isObject(node)) {
441
+ warn(ctx, path, 'Expected schema object/boolean; treating as z.any().');
442
+ return 'z.any()';
443
+ }
444
+ if (typeof node.$ref === 'string') {
445
+ const defName = resolveLocalRef(node.$ref, ctx);
446
+ if (defName)
447
+ return asDefSchemaRef(defName);
448
+ warn(ctx, pathJoin(path, '$ref'), `Unsupported $ref '${node.$ref}'; treating as z.any().`);
449
+ return 'z.any()';
450
+ }
451
+ if (hasOwn(node, 'const')) {
452
+ return `z.literal(${jsonString(node.const)})`;
453
+ }
454
+ if (hasOwn(node, 'enum')) {
455
+ return emitEnum(node, ctx, path);
456
+ }
457
+ if (Array.isArray(node.allOf) && node.allOf.length > 0) {
458
+ return emitAllOf(node, ctx, path);
459
+ }
460
+ if (Array.isArray(node.oneOf) && node.oneOf.length > 0) {
461
+ return emitAnyOfUnion('oneOf', node, ctx, path);
462
+ }
463
+ if (Array.isArray(node.anyOf) && node.anyOf.length > 0) {
464
+ return emitAnyOfUnion('anyOf', node, ctx, path);
465
+ }
466
+ if (hasOwn(node, 'type')) {
467
+ return emitType(node.type, node, ctx, path);
468
+ }
469
+ // Heuristics: object-ish schemas without explicit type.
470
+ if (hasOwn(node, 'properties') || hasOwn(node, 'required') || hasOwn(node, 'additionalProperties') || hasOwn(node, 'unevaluatedProperties')) {
471
+ return emitObject({ ...node, type: 'object' }, ctx, path);
472
+ }
473
+ if (hasOwn(node, 'items')) {
474
+ return emitType('array', node, ctx, path);
475
+ }
476
+ if (hasOwn(node, 'not')) {
477
+ warn(ctx, pathJoin(path, 'not'), 'Keyword `not` is not supported; treating as z.any().');
478
+ return 'z.any()';
479
+ }
480
+ if (hasOwn(node, 'semanticValidation')) {
481
+ warn(ctx, pathJoin(path, 'semanticValidation'), 'Ajv custom keyword semanticValidation is not enforced by generated Zod.');
482
+ }
483
+ return 'z.any()';
484
+ }
485
+ function buildAnchorMap(defs) {
486
+ const map = {};
487
+ for (const [defName, defSchema] of Object.entries(defs)) {
488
+ if (!isObject(defSchema))
489
+ continue;
490
+ const top = defSchema.$anchor;
491
+ if (typeof top === 'string' && top)
492
+ map[top] = defName;
493
+ const nested = defSchema.nucleusSchema;
494
+ if (isObject(nested) && typeof nested.$anchor === 'string' && nested.$anchor) {
495
+ map[nested.$anchor] = defName;
496
+ }
497
+ }
498
+ return map;
499
+ }
500
+ // PURE: Deep-clone JSON-ish values (no mutation of input).
501
+ function deepClone(v) {
502
+ if (Array.isArray(v))
503
+ return v.map((x) => deepClone(x));
504
+ if (isObject(v)) {
505
+ const out = {};
506
+ for (const k of Object.keys(v))
507
+ out[k] = deepClone(v[k]);
508
+ return out;
509
+ }
510
+ return v;
511
+ }
512
+ // PURE: Move sibling object keywords into `allOf` overlays (returns a new tree; does not mutate inputs).
513
+ // Genesis frequently uses `allOf` alongside sibling `properties` and `required`.
514
+ function normalizeAllOfSiblingObjectKeywords(node) {
515
+ if (Array.isArray(node)) {
516
+ let changed = false;
517
+ const out = node.map((item) => {
518
+ const next = normalizeAllOfSiblingObjectKeywords(item);
519
+ if (next !== item)
520
+ changed = true;
521
+ return next;
522
+ });
523
+ return changed ? out : node;
524
+ }
525
+ if (!node || typeof node !== 'object')
526
+ return node;
527
+ const hasAllOf = Array.isArray(node.allOf) && node.allOf.length > 0;
528
+ const looksLikeObjectSchema = node.type === 'object' ||
529
+ node.properties !== undefined ||
530
+ node.required !== undefined ||
531
+ node.unevaluatedProperties !== undefined ||
532
+ node.additionalProperties !== undefined ||
533
+ node.propertyNames !== undefined;
534
+ const siblingKeys = [
535
+ 'type',
536
+ 'properties',
537
+ 'required',
538
+ 'additionalProperties',
539
+ 'unevaluatedProperties',
540
+ 'propertyNames',
541
+ 'patternProperties',
542
+ 'dependentRequired',
543
+ 'dependentSchemas',
544
+ 'minProperties',
545
+ 'maxProperties'
546
+ ];
547
+ let localNode = node;
548
+ if (hasAllOf && looksLikeObjectSchema) {
549
+ const hasSiblingObjectKeywords = siblingKeys.some((k) => k in localNode);
550
+ if (hasSiblingObjectKeywords) {
551
+ const overlay = {};
552
+ const nextNode = { ...localNode };
553
+ for (const k of siblingKeys) {
554
+ if (k in nextNode) {
555
+ overlay[k] = nextNode[k];
556
+ delete nextNode[k];
557
+ }
558
+ }
559
+ nextNode.allOf = [overlay, ...nextNode.allOf];
560
+ localNode = nextNode;
561
+ }
562
+ }
563
+ let changed = localNode !== node;
564
+ const out = localNode === node ? {} : { ...localNode };
565
+ for (const [k, v] of Object.entries(localNode)) {
566
+ const next = normalizeAllOfSiblingObjectKeywords(v);
567
+ if (next !== v) {
568
+ if (!changed) {
569
+ changed = true;
570
+ Object.assign(out, localNode);
571
+ }
572
+ out[k] = next;
573
+ }
574
+ else if (changed) {
575
+ out[k] = v;
576
+ }
577
+ }
578
+ return changed ? out : node;
579
+ }
580
+ function normalizeSchemaForEmitter(schema) {
581
+ return normalizeAllOfSiblingObjectKeywords(schema);
582
+ }
583
+ /**
584
+ * Convert a standalone schema JSON into a set of named Zod expressions.
585
+ *
586
+ * The output intentionally returns *expressions* (no imports/exports), so the
587
+ * caller can decide on file/module layout.
588
+ */
589
+ export function jsonSchemaToZodExpressions(standaloneSchema, rootName) {
590
+ const normalized = normalizeSchemaForEmitter(deepClone(standaloneSchema));
591
+ const defs = isObject(normalized?.$defs) ? normalized.$defs : {};
592
+ const defNames = new Set(Object.keys(defs));
593
+ const ctx = {
594
+ defNames,
595
+ defsByName: defs,
596
+ anchorToDefName: buildAnchorMap(defs),
597
+ warnings: []
598
+ };
599
+ const expressionsByName = {};
600
+ // Emit defs first (order does not matter if caller wraps in z.lazy).
601
+ for (const defName of Object.keys(defs)) {
602
+ expressionsByName[defName] = emitSchema(defs[defName], ctx, `$defs.${defName}`);
603
+ }
604
+ // Root schema expression.
605
+ expressionsByName[rootName] = emitSchema(normalized, ctx, rootName);
606
+ return { expressionsByName, warnings: ctx.warnings };
607
+ }
@@ -0,0 +1 @@
1
+ export {};