@gabrielbryk/json-schema-to-zod 2.10.1 → 2.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +44 -0
- package/CHANGELOG.md +38 -0
- package/README.md +6 -33
- package/check-types-lift.sh +23 -0
- package/check-types.sh +20 -0
- package/dist/{esm/cli.js → cli.js} +0 -6
- package/dist/{esm/core → core}/analyzeSchema.js +4 -5
- package/dist/core/emitZod.js +263 -0
- package/dist/{esm/generators → generators}/generateBundle.js +26 -13
- package/dist/{esm/index.js → index.js} +6 -0
- package/dist/jsonSchemaToZod.js +17 -0
- package/dist/parsers/parseAllOf.js +125 -0
- package/dist/parsers/parseAnyOf.js +28 -0
- package/dist/{esm/parsers → parsers}/parseArray.js +27 -11
- package/dist/parsers/parseBoolean.js +4 -0
- package/dist/parsers/parseConst.js +22 -0
- package/dist/parsers/parseEnum.js +35 -0
- package/dist/{esm/parsers → parsers}/parseIfThenElse.js +10 -6
- package/dist/parsers/parseMultipleType.js +10 -0
- package/dist/parsers/parseNot.js +14 -0
- package/dist/parsers/parseNull.js +4 -0
- package/dist/parsers/parseNullable.js +12 -0
- package/dist/{esm/parsers → parsers}/parseNumber.js +4 -1
- package/dist/{esm/parsers → parsers}/parseObject.js +200 -37
- package/dist/parsers/parseOneOf.js +365 -0
- package/dist/{esm/parsers → parsers}/parseSchema.js +55 -117
- package/dist/parsers/parseSimpleDiscriminatedOneOf.js +24 -0
- package/dist/{esm/parsers → parsers}/parseString.js +29 -18
- package/dist/types/Types.d.ts +32 -4
- package/dist/types/core/analyzeSchema.d.ts +3 -2
- package/dist/types/generators/generateBundle.d.ts +0 -2
- package/dist/types/index.d.ts +6 -0
- package/dist/types/parsers/parseAllOf.d.ts +2 -2
- package/dist/types/parsers/parseAnyOf.d.ts +2 -2
- package/dist/types/parsers/parseArray.d.ts +2 -2
- package/dist/types/parsers/parseBoolean.d.ts +2 -1
- package/dist/types/parsers/parseConst.d.ts +2 -2
- package/dist/types/parsers/parseDefault.d.ts +2 -2
- package/dist/types/parsers/parseEnum.d.ts +2 -2
- package/dist/types/parsers/parseIfThenElse.d.ts +2 -2
- package/dist/types/parsers/parseMultipleType.d.ts +2 -2
- package/dist/types/parsers/parseNot.d.ts +2 -2
- package/dist/types/parsers/parseNull.d.ts +2 -1
- package/dist/types/parsers/parseNullable.d.ts +2 -2
- package/dist/types/parsers/parseNumber.d.ts +2 -2
- package/dist/types/parsers/parseObject.d.ts +2 -2
- package/dist/types/parsers/parseOneOf.d.ts +2 -2
- package/dist/types/parsers/parseSchema.d.ts +2 -2
- package/dist/types/parsers/parseSimpleDiscriminatedOneOf.d.ts +2 -2
- package/dist/types/parsers/parseString.d.ts +2 -2
- package/dist/types/utils/anyOrUnknown.d.ts +5 -4
- package/dist/types/utils/esmEmitter.d.ts +29 -0
- package/dist/types/utils/extractInlineObject.d.ts +15 -0
- package/dist/types/utils/liftInlineObjects.d.ts +21 -0
- package/dist/types/utils/namingService.d.ts +21 -0
- package/dist/types/utils/resolveRef.d.ts +7 -0
- package/dist/types/utils/schemaRepresentation.d.ts +71 -0
- package/dist/utils/anyOrUnknown.js +13 -0
- package/dist/{esm/utils → utils}/buildRefRegistry.js +4 -0
- package/dist/utils/esmEmitter.js +87 -0
- package/dist/utils/extractInlineObject.js +119 -0
- package/dist/utils/liftInlineObjects.js +476 -0
- package/dist/utils/namingService.js +58 -0
- package/dist/utils/resolveRef.js +92 -0
- package/dist/utils/schemaRepresentation.js +569 -0
- package/docs/IMPROVEMENT-PLAN.md +243 -0
- package/docs/ZOD-V4-RECURSIVE-TYPE-LIMITATIONS.md +292 -0
- package/docs/proposals/bundle-refactor.md +1 -1
- package/docs/proposals/discriminated-union-with-default.md +248 -0
- package/docs/proposals/inline-object-lifting.md +77 -0
- package/eslint.config.js +4 -2
- package/jest.config.mjs +19 -0
- package/package.json +17 -20
- package/scripts/generateWorkflowSchema.ts +0 -1
- package/dist/cjs/Types.js +0 -2
- package/dist/cjs/cli.js +0 -70
- package/dist/cjs/core/analyzeSchema.js +0 -62
- package/dist/cjs/core/emitZod.js +0 -157
- package/dist/cjs/generators/generateBundle.js +0 -510
- package/dist/cjs/index.js +0 -50
- package/dist/cjs/jsonSchemaToZod.js +0 -10
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/parsers/parseAllOf.js +0 -46
- package/dist/cjs/parsers/parseAnyOf.js +0 -18
- package/dist/cjs/parsers/parseArray.js +0 -90
- package/dist/cjs/parsers/parseBoolean.js +0 -5
- package/dist/cjs/parsers/parseConst.js +0 -7
- package/dist/cjs/parsers/parseDefault.js +0 -8
- package/dist/cjs/parsers/parseEnum.js +0 -21
- package/dist/cjs/parsers/parseIfThenElse.js +0 -35
- package/dist/cjs/parsers/parseMultipleType.js +0 -10
- package/dist/cjs/parsers/parseNot.js +0 -12
- package/dist/cjs/parsers/parseNull.js +0 -5
- package/dist/cjs/parsers/parseNullable.js +0 -12
- package/dist/cjs/parsers/parseNumber.js +0 -116
- package/dist/cjs/parsers/parseObject.js +0 -318
- package/dist/cjs/parsers/parseOneOf.js +0 -53
- package/dist/cjs/parsers/parseSchema.js +0 -419
- package/dist/cjs/parsers/parseSimpleDiscriminatedOneOf.js +0 -21
- package/dist/cjs/parsers/parseString.js +0 -317
- package/dist/cjs/utils/anyOrUnknown.js +0 -14
- package/dist/cjs/utils/buildRefRegistry.js +0 -56
- package/dist/cjs/utils/cliTools.js +0 -108
- package/dist/cjs/utils/cycles.js +0 -113
- package/dist/cjs/utils/half.js +0 -7
- package/dist/cjs/utils/jsdocs.js +0 -20
- package/dist/cjs/utils/omit.js +0 -11
- package/dist/cjs/utils/resolveUri.js +0 -16
- package/dist/cjs/utils/withMessage.js +0 -21
- package/dist/cjs/zodToJsonSchema.js +0 -89
- package/dist/esm/core/emitZod.js +0 -153
- package/dist/esm/jsonSchemaToZod.js +0 -6
- package/dist/esm/package.json +0 -1
- package/dist/esm/parsers/parseAllOf.js +0 -43
- package/dist/esm/parsers/parseAnyOf.js +0 -14
- package/dist/esm/parsers/parseBoolean.js +0 -1
- package/dist/esm/parsers/parseConst.js +0 -3
- package/dist/esm/parsers/parseEnum.js +0 -17
- package/dist/esm/parsers/parseMultipleType.js +0 -6
- package/dist/esm/parsers/parseNot.js +0 -8
- package/dist/esm/parsers/parseNull.js +0 -1
- package/dist/esm/parsers/parseNullable.js +0 -8
- package/dist/esm/parsers/parseOneOf.js +0 -49
- package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +0 -17
- package/dist/esm/utils/anyOrUnknown.js +0 -10
- package/jest.config.cjs +0 -4
- package/postcjs.cjs +0 -1
- package/postesm.cjs +0 -1
- /package/dist/{esm/Types.js → Types.js} +0 -0
- /package/dist/{esm/parsers → parsers}/parseDefault.js +0 -0
- /package/dist/{esm/utils → utils}/cliTools.js +0 -0
- /package/dist/{esm/utils → utils}/cycles.js +0 -0
- /package/dist/{esm/utils → utils}/half.js +0 -0
- /package/dist/{esm/utils → utils}/jsdocs.js +0 -0
- /package/dist/{esm/utils → utils}/omit.js +0 -0
- /package/dist/{esm/utils → utils}/resolveUri.js +0 -0
- /package/dist/{esm/utils → utils}/withMessage.js +0 -0
- /package/dist/{esm/zodToJsonSchema.js → zodToJsonSchema.js} +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { parseSchema } from "./parseSchema.js";
|
|
2
|
+
import { half } from "../utils/half.js";
|
|
3
|
+
const originalIndexKey = "__originalIndex";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a schema defines object properties (inline object shape) without any refs.
|
|
6
|
+
*/
|
|
7
|
+
const isInlineObjectOnly = (schema) => {
|
|
8
|
+
if (typeof schema !== "object" || schema === null)
|
|
9
|
+
return false;
|
|
10
|
+
const obj = schema;
|
|
11
|
+
// Must have properties
|
|
12
|
+
if (!obj.properties || Object.keys(obj.properties).length === 0)
|
|
13
|
+
return false;
|
|
14
|
+
// Must NOT have $ref or $dynamicRef (can't use spread with refs)
|
|
15
|
+
if (obj.$ref || obj.$dynamicRef)
|
|
16
|
+
return false;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse just the shape entries from an object schema (without z.object wrapper).
|
|
21
|
+
* Returns array of "key: expression" strings for spreading.
|
|
22
|
+
*/
|
|
23
|
+
const parseObjectShape = (schema, refs, pathPrefix) => {
|
|
24
|
+
const shapeEntries = [];
|
|
25
|
+
const shapeTypes = [];
|
|
26
|
+
for (const key of Object.keys(schema.properties)) {
|
|
27
|
+
const propSchema = schema.properties[key];
|
|
28
|
+
const parsedProp = parseSchema(propSchema, {
|
|
29
|
+
...refs,
|
|
30
|
+
path: [...pathPrefix, "properties", key],
|
|
31
|
+
});
|
|
32
|
+
const hasDefault = typeof propSchema === "object" && propSchema.default !== undefined;
|
|
33
|
+
const required = Array.isArray(schema.required)
|
|
34
|
+
? schema.required.includes(key)
|
|
35
|
+
: typeof propSchema === "object" && propSchema.required === true;
|
|
36
|
+
const optional = !hasDefault && !required;
|
|
37
|
+
const valueExpr = optional
|
|
38
|
+
? `${parsedProp.expression}.optional()`
|
|
39
|
+
: parsedProp.expression;
|
|
40
|
+
const valueType = optional
|
|
41
|
+
? `z.ZodOptional<${parsedProp.type}>`
|
|
42
|
+
: parsedProp.type;
|
|
43
|
+
shapeEntries.push(`${JSON.stringify(key)}: ${valueExpr}`);
|
|
44
|
+
shapeTypes.push(`${JSON.stringify(key)}: ${valueType}`);
|
|
45
|
+
}
|
|
46
|
+
return { shapeEntries, shapeTypes };
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Check if all allOf members can be combined using spread syntax.
|
|
50
|
+
* Only works when ALL members are inline objects (no $refs).
|
|
51
|
+
* Returns the merged object if possible, undefined otherwise.
|
|
52
|
+
*/
|
|
53
|
+
const trySpreadPattern = (allOfMembers, refs) => {
|
|
54
|
+
const shapeEntries = [];
|
|
55
|
+
const shapeTypes = [];
|
|
56
|
+
for (let i = 0; i < allOfMembers.length; i++) {
|
|
57
|
+
const member = allOfMembers[i];
|
|
58
|
+
const idx = member[originalIndexKey] ?? i;
|
|
59
|
+
// Only handle pure inline objects - no refs allowed
|
|
60
|
+
if (!isInlineObjectOnly(member)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
// Extract shape entries from inline object
|
|
64
|
+
const { shapeEntries: entries, shapeTypes: types } = parseObjectShape(member, refs, [...refs.path, "allOf", idx]);
|
|
65
|
+
shapeEntries.push(...entries);
|
|
66
|
+
shapeTypes.push(...types);
|
|
67
|
+
}
|
|
68
|
+
if (shapeEntries.length === 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
return {
|
|
71
|
+
expression: `z.object({ ${shapeEntries.join(", ")} })`,
|
|
72
|
+
type: `z.ZodObject<{ ${shapeTypes.join(", ")} }>`,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const ensureOriginalIndex = (arr) => {
|
|
76
|
+
const newArr = [];
|
|
77
|
+
for (let i = 0; i < arr.length; i++) {
|
|
78
|
+
const item = arr[i];
|
|
79
|
+
if (typeof item === "boolean") {
|
|
80
|
+
newArr.push(item ? { [originalIndexKey]: i } : { [originalIndexKey]: i, not: {} });
|
|
81
|
+
}
|
|
82
|
+
else if (typeof item === "object" &&
|
|
83
|
+
item !== null &&
|
|
84
|
+
originalIndexKey in item) {
|
|
85
|
+
return arr;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
newArr.push({ ...item, [originalIndexKey]: i });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return newArr;
|
|
92
|
+
};
|
|
93
|
+
export function parseAllOf(schema, refs) {
|
|
94
|
+
if (schema.allOf.length === 0) {
|
|
95
|
+
return { expression: "z.never()", type: "z.ZodNever" };
|
|
96
|
+
}
|
|
97
|
+
else if (schema.allOf.length === 1) {
|
|
98
|
+
const item = schema.allOf[0];
|
|
99
|
+
return parseSchema(item, {
|
|
100
|
+
...refs,
|
|
101
|
+
path: [
|
|
102
|
+
...refs.path,
|
|
103
|
+
"allOf",
|
|
104
|
+
item[originalIndexKey] ?? 0,
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Try spread pattern first (more efficient than intersection)
|
|
110
|
+
// This works when all members are either $refs to object schemas or inline objects
|
|
111
|
+
const indexed = ensureOriginalIndex(schema.allOf);
|
|
112
|
+
const spreadResult = trySpreadPattern(indexed, refs);
|
|
113
|
+
if (spreadResult) {
|
|
114
|
+
return spreadResult;
|
|
115
|
+
}
|
|
116
|
+
// Fallback to intersection-based approach
|
|
117
|
+
const [left, right] = half(indexed);
|
|
118
|
+
const leftResult = parseAllOf({ allOf: left }, refs);
|
|
119
|
+
const rightResult = parseAllOf({ allOf: right }, refs);
|
|
120
|
+
return {
|
|
121
|
+
expression: `z.intersection(${leftResult.expression}, ${rightResult.expression})`,
|
|
122
|
+
type: `z.ZodIntersection<${leftResult.type}, ${rightResult.type}>`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parseSchema } from "./parseSchema.js";
|
|
2
|
+
import { anyOrUnknown } from "../utils/anyOrUnknown.js";
|
|
3
|
+
import { extractInlineObject } from "../utils/extractInlineObject.js";
|
|
4
|
+
export const parseAnyOf = (schema, refs) => {
|
|
5
|
+
if (!schema.anyOf.length) {
|
|
6
|
+
return anyOrUnknown(refs);
|
|
7
|
+
}
|
|
8
|
+
if (schema.anyOf.length === 1) {
|
|
9
|
+
return parseSchema(schema.anyOf[0], {
|
|
10
|
+
...refs,
|
|
11
|
+
path: [...refs.path, "anyOf", 0],
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
// Rule 1: Extract inline objects to top-level declarations
|
|
15
|
+
const members = schema.anyOf.map((memberSchema, i) => {
|
|
16
|
+
const extracted = extractInlineObject(memberSchema, refs, [...refs.path, "anyOf", i]);
|
|
17
|
+
if (extracted) {
|
|
18
|
+
return { expression: extracted, type: `typeof ${extracted}` };
|
|
19
|
+
}
|
|
20
|
+
return parseSchema(memberSchema, { ...refs, path: [...refs.path, "anyOf", i] });
|
|
21
|
+
});
|
|
22
|
+
const expressions = members.map(m => m.expression).join(", ");
|
|
23
|
+
const types = members.map(m => m.type).join(", ");
|
|
24
|
+
const expression = `z.union([${expressions}])`;
|
|
25
|
+
// Use readonly tuple for union type annotations (required for recursive type inference)
|
|
26
|
+
const type = `z.ZodUnion<readonly [${types}]>`;
|
|
27
|
+
return { expression, type };
|
|
28
|
+
};
|
|
@@ -3,16 +3,20 @@ import { parseSchema } from "./parseSchema.js";
|
|
|
3
3
|
import { anyOrUnknown } from "../utils/anyOrUnknown.js";
|
|
4
4
|
export const parseArray = (schema, refs) => {
|
|
5
5
|
if (Array.isArray(schema.items)) {
|
|
6
|
-
|
|
6
|
+
// Tuple case
|
|
7
|
+
const itemResults = schema.items.map((v, i) => parseSchema(v, { ...refs, path: [...refs.path, "items", i] }));
|
|
8
|
+
let tuple = `z.tuple([${itemResults.map(r => r.expression).join(", ")}])`;
|
|
9
|
+
const tupleTypes = itemResults.map(r => r.type).join(", ");
|
|
10
|
+
let tupleType = `z.ZodTuple<[${tupleTypes}]>`;
|
|
7
11
|
if (schema.contains) {
|
|
8
|
-
const
|
|
12
|
+
const containsResult = parseSchema(schema.contains, {
|
|
9
13
|
...refs,
|
|
10
14
|
path: [...refs.path, "contains"],
|
|
11
15
|
});
|
|
12
16
|
const minContains = schema.minContains ?? (schema.contains ? 1 : undefined);
|
|
13
17
|
const maxContains = schema.maxContains;
|
|
14
18
|
tuple += `.superRefine((arr, ctx) => {
|
|
15
|
-
const matches = arr.filter((item) => ${
|
|
19
|
+
const matches = arr.filter((item) => ${containsResult.expression}.safeParse(item).success).length;
|
|
16
20
|
if (${minContains ?? 0} && matches < ${minContains ?? 0}) {
|
|
17
21
|
ctx.addIssue({ code: "custom", message: "Array contains too few matching items" });
|
|
18
22
|
}
|
|
@@ -20,15 +24,23 @@ export const parseArray = (schema, refs) => {
|
|
|
20
24
|
ctx.addIssue({ code: "custom", message: "Array contains too many matching items" });
|
|
21
25
|
}
|
|
22
26
|
})`;
|
|
27
|
+
// In Zod v4, .superRefine() doesn't change the type
|
|
23
28
|
}
|
|
24
|
-
return
|
|
29
|
+
return {
|
|
30
|
+
expression: tuple,
|
|
31
|
+
type: tupleType,
|
|
32
|
+
};
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
// Array case
|
|
35
|
+
const anyOrUnknownResult = anyOrUnknown(refs);
|
|
36
|
+
const itemResult = !schema.items
|
|
37
|
+
? anyOrUnknownResult
|
|
38
|
+
: parseSchema(schema.items, {
|
|
29
39
|
...refs,
|
|
30
40
|
path: [...refs.path, "items"],
|
|
31
|
-
})
|
|
41
|
+
});
|
|
42
|
+
let r = `z.array(${itemResult.expression})`;
|
|
43
|
+
let arrayType = `z.ZodArray<${itemResult.type}>`;
|
|
32
44
|
r += withMessage(schema, "minItems", ({ json }) => ({
|
|
33
45
|
opener: `.min(${json}`,
|
|
34
46
|
closer: ")",
|
|
@@ -66,14 +78,14 @@ export const parseArray = (schema, refs) => {
|
|
|
66
78
|
})`;
|
|
67
79
|
}
|
|
68
80
|
if (schema.contains) {
|
|
69
|
-
const
|
|
81
|
+
const containsResult = parseSchema(schema.contains, {
|
|
70
82
|
...refs,
|
|
71
83
|
path: [...refs.path, "contains"],
|
|
72
84
|
});
|
|
73
85
|
const minContains = schema.minContains ?? (schema.contains ? 1 : undefined);
|
|
74
86
|
const maxContains = schema.maxContains;
|
|
75
87
|
r += `.superRefine((arr, ctx) => {
|
|
76
|
-
const matches = arr.filter((item) => ${
|
|
88
|
+
const matches = arr.filter((item) => ${containsResult.expression}.safeParse(item).success).length;
|
|
77
89
|
if (${minContains ?? 0} && matches < ${minContains ?? 0}) {
|
|
78
90
|
ctx.addIssue({ code: "custom", message: "Array contains too few matching items" });
|
|
79
91
|
}
|
|
@@ -82,5 +94,9 @@ export const parseArray = (schema, refs) => {
|
|
|
82
94
|
}
|
|
83
95
|
})`;
|
|
84
96
|
}
|
|
85
|
-
|
|
97
|
+
// In Zod v4, .superRefine() doesn't change the type, so no wrapping needed
|
|
98
|
+
return {
|
|
99
|
+
expression: r,
|
|
100
|
+
type: arrayType,
|
|
101
|
+
};
|
|
86
102
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const parseConst = (schema) => {
|
|
2
|
+
const value = schema.const;
|
|
3
|
+
const expression = `z.literal(${JSON.stringify(value)})`;
|
|
4
|
+
// Determine the literal type based on the value type
|
|
5
|
+
let type;
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
type = `z.ZodLiteral<${JSON.stringify(value)}>`;
|
|
8
|
+
}
|
|
9
|
+
else if (typeof value === "number") {
|
|
10
|
+
type = `z.ZodLiteral<${value}>`;
|
|
11
|
+
}
|
|
12
|
+
else if (typeof value === "boolean") {
|
|
13
|
+
type = `z.ZodLiteral<${value}>`;
|
|
14
|
+
}
|
|
15
|
+
else if (value === null) {
|
|
16
|
+
type = "z.ZodLiteral<null>";
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
type = "z.ZodLiteral<unknown>";
|
|
20
|
+
}
|
|
21
|
+
return { expression, type };
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const parseEnum = (schema) => {
|
|
2
|
+
if (schema.enum.length === 0) {
|
|
3
|
+
return {
|
|
4
|
+
expression: "z.never()",
|
|
5
|
+
type: "z.ZodNever",
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
else if (schema.enum.length === 1) {
|
|
9
|
+
// union does not work when there is only one element
|
|
10
|
+
const value = schema.enum[0];
|
|
11
|
+
return {
|
|
12
|
+
expression: `z.literal(${JSON.stringify(value)})`,
|
|
13
|
+
type: `z.ZodLiteral<${typeof value === "string" ? JSON.stringify(value) : value}>`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
else if (schema.enum.every((x) => typeof x === "string")) {
|
|
17
|
+
const values = schema.enum;
|
|
18
|
+
// Zod v4 ZodEnum uses object format: { key: "key"; ... }
|
|
19
|
+
const enumObject = values.map((x) => `${JSON.stringify(x)}: ${JSON.stringify(x)}`).join("; ");
|
|
20
|
+
return {
|
|
21
|
+
expression: `z.enum([${values.map((x) => JSON.stringify(x))}])`,
|
|
22
|
+
type: `z.ZodEnum<{ ${enumObject} }>`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Mixed types: create union of literals
|
|
27
|
+
const literalTypes = schema.enum.map((x) => typeof x === "string" ? JSON.stringify(x) : x === null ? "null" : String(x));
|
|
28
|
+
return {
|
|
29
|
+
expression: `z.union([${schema.enum
|
|
30
|
+
.map((x) => `z.literal(${JSON.stringify(x)})`)
|
|
31
|
+
.join(", ")}])`,
|
|
32
|
+
type: `z.ZodUnion<[${literalTypes.map((t) => `z.ZodLiteral<${t}>`).join(", ")}]>`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -9,10 +9,10 @@ export const parseIfThenElse = (schema, refs) => {
|
|
|
9
9
|
...refs,
|
|
10
10
|
path: [...refs.path, "else"],
|
|
11
11
|
});
|
|
12
|
-
let
|
|
13
|
-
const result = ${$if}.safeParse(value).success
|
|
14
|
-
? ${$then}.safeParse(value)
|
|
15
|
-
: ${$else}.safeParse(value);
|
|
12
|
+
let expression = `z.union([${$then.expression}, ${$else.expression}]).superRefine((value,ctx) => {
|
|
13
|
+
const result = ${$if.expression}.safeParse(value).success
|
|
14
|
+
? ${$then.expression}.safeParse(value)
|
|
15
|
+
: ${$else.expression}.safeParse(value);
|
|
16
16
|
if (!result.success) {
|
|
17
17
|
const issues = result.error.issues;
|
|
18
18
|
issues.forEach((issue) => ctx.addIssue({ ...issue }))
|
|
@@ -25,7 +25,11 @@ export const parseIfThenElse = (schema, refs) => {
|
|
|
25
25
|
then: schema.then,
|
|
26
26
|
else: schema.else,
|
|
27
27
|
});
|
|
28
|
-
|
|
28
|
+
expression += `.meta({ __jsonSchema: { conditional: ${conditionalMeta} } })`;
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return {
|
|
31
|
+
expression,
|
|
32
|
+
// In Zod v4, .superRefine() doesn't change the type
|
|
33
|
+
type: `z.ZodUnion<[${$then.type}, ${$else.type}]>`,
|
|
34
|
+
};
|
|
31
35
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parseSchema } from "./parseSchema.js";
|
|
2
|
+
export const parseMultipleType = (schema, refs) => {
|
|
3
|
+
const schemas = schema.type.map((type) => parseSchema({ ...schema, type }, { ...refs, withoutDefaults: true }));
|
|
4
|
+
const expressions = schemas.map(s => s.expression).join(", ");
|
|
5
|
+
const types = schemas.map(s => s.type).join(", ");
|
|
6
|
+
return {
|
|
7
|
+
expression: `z.union([${expressions}])`,
|
|
8
|
+
type: `z.ZodUnion<[${types}]>`,
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { parseSchema } from "./parseSchema.js";
|
|
2
|
+
import { anyOrUnknown } from "../utils/anyOrUnknown.js";
|
|
3
|
+
export const parseNot = (schema, refs) => {
|
|
4
|
+
const baseSchema = anyOrUnknown(refs);
|
|
5
|
+
const notSchema = parseSchema(schema.not, {
|
|
6
|
+
...refs,
|
|
7
|
+
path: [...refs.path, "not"],
|
|
8
|
+
});
|
|
9
|
+
return {
|
|
10
|
+
expression: `${baseSchema.expression}.refine((value) => !${notSchema.expression}.safeParse(value).success, "Invalid input: Should NOT be valid against schema")`,
|
|
11
|
+
// In Zod v4, .refine() doesn't change the type
|
|
12
|
+
type: baseSchema.type,
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { omit } from "../utils/omit.js";
|
|
2
|
+
import { parseSchema } from "./parseSchema.js";
|
|
3
|
+
/**
|
|
4
|
+
* For compatibility with open api 3.0 nullable
|
|
5
|
+
*/
|
|
6
|
+
export const parseNullable = (schema, refs) => {
|
|
7
|
+
const innerSchema = parseSchema(omit(schema, "nullable"), refs, true);
|
|
8
|
+
return {
|
|
9
|
+
expression: `${innerSchema.expression}.nullable()`,
|
|
10
|
+
type: `z.ZodNullable<${innerSchema.type}>`,
|
|
11
|
+
};
|
|
12
|
+
};
|