@gabrielbryk/json-schema-to-zod 2.12.1 → 2.13.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.
- package/.github/RELEASE_SETUP.md +120 -0
- package/.github/TOOLING_GUIDE.md +169 -0
- package/.github/dependabot.yml +52 -0
- package/.github/workflows/ci.yml +33 -0
- package/.github/workflows/release.yml +12 -4
- package/.github/workflows/security.yml +40 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.lintstagedrc.json +3 -0
- package/.prettierrc +20 -0
- package/AGENTS.md +7 -0
- package/CHANGELOG.md +7 -4
- package/README.md +9 -9
- package/commitlint.config.js +24 -0
- package/createIndex.ts +4 -4
- package/dist/cli.js +3 -4
- package/dist/core/analyzeSchema.js +28 -5
- package/dist/core/emitZod.js +11 -4
- package/dist/generators/generateBundle.js +67 -92
- package/dist/parsers/parseAllOf.js +11 -12
- package/dist/parsers/parseAnyOf.js +2 -2
- package/dist/parsers/parseArray.js +38 -12
- package/dist/parsers/parseMultipleType.js +2 -2
- package/dist/parsers/parseNumber.js +44 -102
- package/dist/parsers/parseObject.js +136 -443
- package/dist/parsers/parseOneOf.js +57 -110
- package/dist/parsers/parseSchema.js +132 -55
- package/dist/parsers/parseSimpleDiscriminatedOneOf.js +2 -2
- package/dist/parsers/parseString.js +113 -253
- package/dist/types/Types.d.ts +22 -1
- package/dist/types/core/analyzeSchema.d.ts +1 -0
- package/dist/types/generators/generateBundle.d.ts +1 -1
- package/dist/utils/cliTools.js +1 -2
- package/dist/utils/esmEmitter.js +6 -2
- package/dist/utils/extractInlineObject.js +1 -3
- package/dist/utils/jsdocs.js +1 -4
- package/dist/utils/liftInlineObjects.js +76 -15
- package/dist/utils/resolveRef.js +35 -10
- package/dist/utils/schemaRepresentation.js +35 -66
- package/dist/zodToJsonSchema.js +1 -2
- package/docs/IMPROVEMENT-PLAN.md +30 -12
- package/docs/ZOD-V4-RECURSIVE-TYPE-LIMITATIONS.md +70 -25
- package/docs/proposals/allof-required-merging.md +10 -4
- package/docs/proposals/bundle-refactor.md +10 -4
- package/docs/proposals/discriminated-union-with-default.md +18 -14
- package/docs/proposals/inline-object-lifting.md +15 -5
- package/docs/proposals/ref-anchor-support.md +11 -0
- package/output.txt +67 -0
- package/package.json +18 -5
- package/scripts/generateWorkflowSchema.ts +5 -14
- package/scripts/regenerate_bundle.ts +25 -0
- package/tsc_output.txt +542 -0
- package/tsc_output_2.txt +489 -0
|
@@ -3,246 +3,114 @@ import { parseSchema } from "./parseSchema.js";
|
|
|
3
3
|
export const parseString = (schema, refs) => {
|
|
4
4
|
const formatError = schema.errorMessage?.format;
|
|
5
5
|
const refContext = ensureRefs(refs);
|
|
6
|
-
// Map formats to
|
|
6
|
+
// Map formats to Zod string methods
|
|
7
7
|
const topLevelFormatMap = {
|
|
8
|
-
email: { fn: "z.email", zodType: "z.
|
|
9
|
-
ipv4: { fn: "z.ipv4", zodType: "z.
|
|
10
|
-
ipv6: { fn: "z.ipv6", zodType: "z.
|
|
11
|
-
uri: { fn: "z.url", zodType: "z.
|
|
12
|
-
uuid: { fn: "z.uuid", zodType: "z.
|
|
13
|
-
cuid: { fn: "z.cuid", zodType: "z.
|
|
14
|
-
cuid2: { fn: "z.cuid2", zodType: "z.
|
|
15
|
-
nanoid: { fn: "z.nanoid", zodType: "z.
|
|
16
|
-
ulid: { fn: "z.ulid", zodType: "z.
|
|
17
|
-
jwt: { fn: "z.jwt", zodType: "z.
|
|
18
|
-
e164: { fn: "z.e164", zodType: "z.
|
|
19
|
-
base64url: { fn: "z.base64url", zodType: "z.
|
|
20
|
-
base64: { fn: "z.base64", zodType: "z.
|
|
21
|
-
emoji: { fn: "z.emoji", zodType: "z.
|
|
22
|
-
"idn-email": { fn: "z.email", zodType: "z.
|
|
8
|
+
email: { fn: "z.email", zodType: "z.ZodString" },
|
|
9
|
+
ipv4: { fn: "z.ipv4", zodType: "z.ZodString" },
|
|
10
|
+
ipv6: { fn: "z.ipv6", zodType: "z.ZodString" },
|
|
11
|
+
uri: { fn: "z.url", zodType: "z.ZodString" },
|
|
12
|
+
uuid: { fn: "z.uuid", zodType: "z.ZodString" },
|
|
13
|
+
cuid: { fn: "z.cuid", zodType: "z.ZodString" },
|
|
14
|
+
cuid2: { fn: "z.cuid2", zodType: "z.ZodString" },
|
|
15
|
+
nanoid: { fn: "z.nanoid", zodType: "z.ZodString" },
|
|
16
|
+
ulid: { fn: "z.ulid", zodType: "z.ZodString" },
|
|
17
|
+
jwt: { fn: "z.jwt", zodType: "z.ZodString" },
|
|
18
|
+
e164: { fn: "z.e164", zodType: "z.ZodString" },
|
|
19
|
+
base64url: { fn: "z.base64url", zodType: "z.ZodString" },
|
|
20
|
+
base64: { fn: "z.base64", zodType: "z.ZodString" },
|
|
21
|
+
emoji: { fn: "z.emoji", zodType: "z.ZodString" },
|
|
22
|
+
"idn-email": { fn: "z.email", zodType: "z.ZodString" },
|
|
23
|
+
"date-time": { fn: "z.iso.datetime", zodType: "z.ZodString" },
|
|
24
|
+
date: { fn: "z.iso.date", zodType: "z.ZodString" },
|
|
25
|
+
time: { fn: "z.iso.time", zodType: "z.ZodString" },
|
|
26
|
+
duration: { fn: "z.iso.duration", zodType: "z.ZodString" },
|
|
23
27
|
};
|
|
24
28
|
const formatInfo = schema.format ? topLevelFormatMap[schema.format] : undefined;
|
|
25
29
|
const formatFn = formatInfo?.fn;
|
|
26
|
-
|
|
27
|
-
let
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
messagePrefix: "{ error: ",
|
|
111
|
-
messageCloser: " })",
|
|
112
|
-
};
|
|
113
|
-
case "jwt":
|
|
114
|
-
formatWasHandled = true;
|
|
115
|
-
return {
|
|
116
|
-
opener: ".jwt(",
|
|
117
|
-
closer: ")",
|
|
118
|
-
messagePrefix: "{ error: ",
|
|
119
|
-
messageCloser: " })",
|
|
120
|
-
};
|
|
121
|
-
case "e164":
|
|
122
|
-
formatWasHandled = true;
|
|
123
|
-
return {
|
|
124
|
-
opener: ".e164(",
|
|
125
|
-
closer: ")",
|
|
126
|
-
messagePrefix: "{ error: ",
|
|
127
|
-
messageCloser: " })",
|
|
128
|
-
};
|
|
129
|
-
case "base64url":
|
|
130
|
-
formatWasHandled = true;
|
|
131
|
-
return {
|
|
132
|
-
opener: ".base64url(",
|
|
133
|
-
closer: ")",
|
|
134
|
-
messagePrefix: "{ error: ",
|
|
135
|
-
messageCloser: " })",
|
|
136
|
-
};
|
|
137
|
-
case "emoji":
|
|
138
|
-
formatWasHandled = true;
|
|
139
|
-
return {
|
|
140
|
-
opener: ".emoji(",
|
|
141
|
-
closer: ")",
|
|
142
|
-
messagePrefix: "{ error: ",
|
|
143
|
-
messageCloser: " })",
|
|
144
|
-
};
|
|
145
|
-
case "date-time":
|
|
146
|
-
formatWasHandled = true;
|
|
147
|
-
return {
|
|
148
|
-
opener: ".datetime({ offset: true",
|
|
149
|
-
closer: " })",
|
|
150
|
-
messagePrefix: ", error: ",
|
|
151
|
-
messageCloser: " })",
|
|
152
|
-
};
|
|
153
|
-
case "time":
|
|
154
|
-
formatWasHandled = true;
|
|
155
|
-
return {
|
|
156
|
-
opener: ".time(",
|
|
157
|
-
closer: ")",
|
|
158
|
-
messagePrefix: "{ error: ",
|
|
159
|
-
messageCloser: " })",
|
|
160
|
-
};
|
|
161
|
-
case "date":
|
|
162
|
-
formatWasHandled = true;
|
|
163
|
-
return {
|
|
164
|
-
opener: ".date(",
|
|
165
|
-
closer: ")",
|
|
166
|
-
messagePrefix: "{ error: ",
|
|
167
|
-
messageCloser: " })",
|
|
168
|
-
};
|
|
169
|
-
case "binary":
|
|
170
|
-
formatWasHandled = true;
|
|
171
|
-
return {
|
|
172
|
-
opener: ".base64(",
|
|
173
|
-
closer: ")",
|
|
174
|
-
messagePrefix: "{ error: ",
|
|
175
|
-
messageCloser: " })",
|
|
176
|
-
};
|
|
177
|
-
case "duration":
|
|
178
|
-
formatWasHandled = true;
|
|
179
|
-
return {
|
|
180
|
-
opener: ".duration(",
|
|
181
|
-
closer: ")",
|
|
182
|
-
messagePrefix: "{ error: ",
|
|
183
|
-
messageCloser: " })",
|
|
184
|
-
};
|
|
185
|
-
case "hostname":
|
|
186
|
-
case "idn-hostname":
|
|
187
|
-
formatWasHandled = true;
|
|
188
|
-
return {
|
|
189
|
-
opener: ".refine((val) => { if (typeof val !== \"string\" || val.length === 0 || val.length > 253) return false; return val.split(\".\").every((label) => label.length > 0 && label.length <= 63 && /^[A-Za-z0-9-]+$/.test(label) && label[0] !== \"-\" && label[label.length - 1] !== \"-\"); }",
|
|
190
|
-
closer: ")",
|
|
191
|
-
messagePrefix: ", { error: ",
|
|
192
|
-
messageCloser: " })",
|
|
193
|
-
};
|
|
194
|
-
case "idn-email":
|
|
195
|
-
formatWasHandled = true;
|
|
196
|
-
return {
|
|
197
|
-
opener: ".email(",
|
|
198
|
-
closer: ")",
|
|
199
|
-
messagePrefix: "{ error: ",
|
|
200
|
-
messageCloser: " })",
|
|
201
|
-
};
|
|
202
|
-
case "uri-reference":
|
|
203
|
-
case "iri":
|
|
204
|
-
case "iri-reference":
|
|
205
|
-
formatWasHandled = true;
|
|
206
|
-
return {
|
|
207
|
-
opener: '.refine((val) => { try { new URL(val, "http://example.com"); return true; } catch { return false; } }',
|
|
208
|
-
closer: ")",
|
|
209
|
-
messagePrefix: ", { error: ",
|
|
210
|
-
messageCloser: " })",
|
|
211
|
-
};
|
|
212
|
-
case "json-pointer":
|
|
213
|
-
formatWasHandled = true;
|
|
214
|
-
return {
|
|
215
|
-
opener: ".refine((val) => typeof val === \"string\" && /^(?:\\/(?:[^/~]|~[01])*)*$/.test(val)",
|
|
216
|
-
closer: ")",
|
|
217
|
-
messagePrefix: ", { error: ",
|
|
218
|
-
messageCloser: " })",
|
|
219
|
-
};
|
|
220
|
-
case "relative-json-pointer":
|
|
221
|
-
formatWasHandled = true;
|
|
222
|
-
return {
|
|
223
|
-
opener: ".refine((val) => typeof val === \"string\" && /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^/~]|~[01])*))*$/.test(val)",
|
|
224
|
-
closer: ")",
|
|
225
|
-
messagePrefix: ", { error: ",
|
|
226
|
-
messageCloser: " })",
|
|
227
|
-
};
|
|
228
|
-
case "uri-template":
|
|
229
|
-
formatWasHandled = true;
|
|
230
|
-
return {
|
|
231
|
-
opener: ".refine((val) => { if (typeof val !== \"string\") return false; const opens = (val.match(/\\{/g) || []).length; const closes = (val.match(/\\}/g) || []).length; return opens === closes; }",
|
|
232
|
-
closer: ")",
|
|
233
|
-
messagePrefix: ", { error: ",
|
|
234
|
-
messageCloser: " })",
|
|
235
|
-
};
|
|
236
|
-
case "regex":
|
|
237
|
-
formatWasHandled = true;
|
|
238
|
-
return {
|
|
239
|
-
opener: ".refine((val) => { try { new RegExp(val); return true; } catch { return false; } }",
|
|
240
|
-
closer: ")",
|
|
241
|
-
messagePrefix: ", { error: ",
|
|
242
|
-
messageCloser: " })",
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
});
|
|
30
|
+
let r = "z.string()";
|
|
31
|
+
let zodType = "z.ZodString";
|
|
32
|
+
if (formatFn) {
|
|
33
|
+
const params = formatError !== undefined ? `{ message: ${JSON.stringify(formatError)} }` : "";
|
|
34
|
+
if (schema.format === "date-time") {
|
|
35
|
+
r = `z.iso.datetime({ offset: true${formatError ? `, message: ${JSON.stringify(formatError)}` : ""} })`;
|
|
36
|
+
}
|
|
37
|
+
else if (schema.format === "ipv4") {
|
|
38
|
+
r = `z.ipv4(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
39
|
+
}
|
|
40
|
+
else if (schema.format === "ipv6") {
|
|
41
|
+
r = `z.ipv6(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
r = `${formatFn}(${params})`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
let formatWasHandled = Boolean(formatFn);
|
|
48
|
+
if (!formatWasHandled && schema.format) {
|
|
49
|
+
switch (schema.format) {
|
|
50
|
+
case "ip":
|
|
51
|
+
r += `.refine((val) => {
|
|
52
|
+
const v4 = z.ipv4().safeParse(val).success;
|
|
53
|
+
const v6 = z.ipv6().safeParse(val).success;
|
|
54
|
+
return v4 || v6;
|
|
55
|
+
}${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
56
|
+
formatWasHandled = true;
|
|
57
|
+
break;
|
|
58
|
+
case "binary":
|
|
59
|
+
r = `z.base64(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
60
|
+
formatWasHandled = true;
|
|
61
|
+
break;
|
|
62
|
+
case "hostname":
|
|
63
|
+
case "idn-hostname":
|
|
64
|
+
r += `.refine((val) => {
|
|
65
|
+
if (typeof val !== "string" || val.length === 0 || val.length > 253) return false;
|
|
66
|
+
return val.split(".").every((label) => {
|
|
67
|
+
return label.length > 0 && label.length <= 63 && /^[A-Za-z0-9-]+$/.test(label) && label[0] !== "-" && label[label.length - 1] !== "-";
|
|
68
|
+
});
|
|
69
|
+
}${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
70
|
+
formatWasHandled = true;
|
|
71
|
+
break;
|
|
72
|
+
case "uri-reference":
|
|
73
|
+
case "iri":
|
|
74
|
+
case "iri-reference":
|
|
75
|
+
r += `.refine((val) => {
|
|
76
|
+
try {
|
|
77
|
+
new URL(val, "http://example.com");
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
83
|
+
formatWasHandled = true;
|
|
84
|
+
break;
|
|
85
|
+
case "json-pointer":
|
|
86
|
+
r += `.refine((val) => typeof val === "string" && /^(?:\\/(?:[^/~]|~[01])*)*$/.test(val)${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
87
|
+
formatWasHandled = true;
|
|
88
|
+
break;
|
|
89
|
+
case "relative-json-pointer":
|
|
90
|
+
r += `.refine((val) => typeof val === "string" && /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^/~]|~[01])*))*$/.test(val)${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
91
|
+
formatWasHandled = true;
|
|
92
|
+
break;
|
|
93
|
+
case "uri-template":
|
|
94
|
+
r += `.refine((val) => {
|
|
95
|
+
if (typeof val !== "string") return false;
|
|
96
|
+
const opens = (val.match(/\\{/g) || []).length;
|
|
97
|
+
const closes = (val.match(/\\}/g) || []).length;
|
|
98
|
+
return opens === closes;
|
|
99
|
+
}${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
100
|
+
formatWasHandled = true;
|
|
101
|
+
break;
|
|
102
|
+
case "regex":
|
|
103
|
+
r += `.refine((val) => {
|
|
104
|
+
try {
|
|
105
|
+
new RegExp(val);
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
|
|
111
|
+
formatWasHandled = true;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
246
114
|
}
|
|
247
115
|
if (schema.format && !formatWasHandled) {
|
|
248
116
|
refContext.onUnknownFormat?.(schema.format, refContext.path);
|
|
@@ -250,37 +118,31 @@ export const parseString = (schema, refs) => {
|
|
|
250
118
|
r += withMessage(schema, "pattern", ({ json }) => ({
|
|
251
119
|
opener: `.regex(new RegExp(${json})`,
|
|
252
120
|
closer: ")",
|
|
253
|
-
messagePrefix: ", {
|
|
121
|
+
messagePrefix: ", { message: ",
|
|
254
122
|
messageCloser: " })",
|
|
255
123
|
}));
|
|
256
124
|
r += withMessage(schema, "minLength", ({ json }) => ({
|
|
257
125
|
opener: `.min(${json}`,
|
|
258
126
|
closer: ")",
|
|
259
|
-
messagePrefix: ", {
|
|
127
|
+
messagePrefix: ", { message: ",
|
|
260
128
|
messageCloser: " })",
|
|
261
129
|
}));
|
|
262
130
|
r += withMessage(schema, "maxLength", ({ json }) => ({
|
|
263
131
|
opener: `.max(${json}`,
|
|
264
132
|
closer: ")",
|
|
265
|
-
messagePrefix: ", {
|
|
133
|
+
messagePrefix: ", { message: ",
|
|
266
134
|
messageCloser: " })",
|
|
267
135
|
}));
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
closer: ")",
|
|
273
|
-
messagePrefix: "{ error: ",
|
|
274
|
-
messageCloser: " })",
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
});
|
|
136
|
+
if (schema.contentEncoding === "base64" && schema.format !== "base64") {
|
|
137
|
+
const encodingError = schema.errorMessage?.contentEncoding;
|
|
138
|
+
r = `z.base64(${encodingError ? `{ message: ${JSON.stringify(encodingError)} }` : ""})`;
|
|
139
|
+
}
|
|
278
140
|
const contentMediaType = withMessage(schema, "contentMediaType", ({ value }) => {
|
|
279
141
|
if (value === "application/json") {
|
|
280
142
|
return {
|
|
281
143
|
opener: '.transform((str, ctx) => { try { return JSON.parse(str); } catch (err) { ctx.addIssue({ code: "custom", message: "Invalid JSON" }); }}',
|
|
282
144
|
closer: ")",
|
|
283
|
-
messagePrefix: ", {
|
|
145
|
+
messagePrefix: ", { message: ",
|
|
284
146
|
messageCloser: " })",
|
|
285
147
|
};
|
|
286
148
|
}
|
|
@@ -296,14 +158,12 @@ export const parseString = (schema, refs) => {
|
|
|
296
158
|
return {
|
|
297
159
|
opener: `.pipe(${contentExpr}`,
|
|
298
160
|
closer: ")",
|
|
299
|
-
messagePrefix: ", {
|
|
161
|
+
messagePrefix: ", { message: ",
|
|
300
162
|
messageCloser: " })",
|
|
301
163
|
};
|
|
302
164
|
}
|
|
303
165
|
});
|
|
304
166
|
}
|
|
305
|
-
// Use the correct Zod type based on whether a format function was used
|
|
306
|
-
const zodType = formatInfo?.zodType ?? "z.ZodString";
|
|
307
167
|
return {
|
|
308
168
|
expression: r,
|
|
309
169
|
type: zodType,
|
package/dist/types/Types.d.ts
CHANGED
|
@@ -25,9 +25,10 @@ export type JsonSchemaObject = {
|
|
|
25
25
|
definitions?: Record<string, JsonSchema>;
|
|
26
26
|
title?: string;
|
|
27
27
|
description?: string;
|
|
28
|
-
examples?: Serializable[];
|
|
28
|
+
examples?: Serializable | Serializable[];
|
|
29
29
|
deprecated?: boolean;
|
|
30
30
|
dependentSchemas?: Record<string, JsonSchema>;
|
|
31
|
+
dependentRequired?: Record<string, string[]>;
|
|
31
32
|
contains?: JsonSchema;
|
|
32
33
|
minContains?: number;
|
|
33
34
|
maxContains?: number;
|
|
@@ -44,6 +45,7 @@ export type JsonSchemaObject = {
|
|
|
44
45
|
required?: string[] | boolean;
|
|
45
46
|
propertyNames?: JsonSchema;
|
|
46
47
|
items?: JsonSchema | JsonSchema[];
|
|
48
|
+
prefixItems?: JsonSchema[];
|
|
47
49
|
additionalItems?: JsonSchema;
|
|
48
50
|
minItems?: number;
|
|
49
51
|
maxItems?: number;
|
|
@@ -118,6 +120,14 @@ export type Options = {
|
|
|
118
120
|
* @default false
|
|
119
121
|
*/
|
|
120
122
|
strictOneOf?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Root schema instance for JSON Pointer resolution (#/...).
|
|
125
|
+
*/
|
|
126
|
+
root?: JsonSchema;
|
|
127
|
+
/**
|
|
128
|
+
* Full document root schema instance for cross-reference resolution.
|
|
129
|
+
*/
|
|
130
|
+
documentRoot?: JsonSchema;
|
|
121
131
|
/**
|
|
122
132
|
* Called when a string format is encountered that has no built-in mapping.
|
|
123
133
|
* Can be used to log or throw on unknown formats.
|
|
@@ -133,6 +143,16 @@ export type Options = {
|
|
|
133
143
|
* Return a JsonSchema to register, or undefined if not found.
|
|
134
144
|
*/
|
|
135
145
|
resolveExternalRef?: (uri: string) => JsonSchema | Promise<JsonSchema> | undefined;
|
|
146
|
+
/** Root/base URI for the document */
|
|
147
|
+
rootBaseUri?: string;
|
|
148
|
+
/** Prebuilt registry of resolved URIs/anchors */
|
|
149
|
+
refRegistry?: Map<string, {
|
|
150
|
+
schema: JsonSchema;
|
|
151
|
+
path: (string | number)[];
|
|
152
|
+
baseUri: string;
|
|
153
|
+
dynamic?: boolean;
|
|
154
|
+
anchor?: string;
|
|
155
|
+
}>;
|
|
136
156
|
/**
|
|
137
157
|
* Lift inline object schemas into top-level defs to improve reusability.
|
|
138
158
|
* Default is ON; set enable: false to opt out.
|
|
@@ -180,6 +200,7 @@ export type Refs = Options & {
|
|
|
180
200
|
dynamic?: boolean;
|
|
181
201
|
anchor?: string;
|
|
182
202
|
}>;
|
|
203
|
+
definitions?: Record<string, JsonSchema>;
|
|
183
204
|
/** Stack of active dynamic anchors (nearest last) */
|
|
184
205
|
dynamicAnchors?: {
|
|
185
206
|
name: string;
|
|
@@ -2,7 +2,7 @@ import { JsonSchema, Options } from "../Types.js";
|
|
|
2
2
|
export type SplitDefsOptions = {
|
|
3
3
|
/** Include a root schema file in addition to $defs */
|
|
4
4
|
includeRoot?: boolean;
|
|
5
|
-
/** Override file name for each schema (default: `${def}.schema.ts`) */
|
|
5
|
+
/** Override file name for each schema (default: `${ def }.schema.ts`) */
|
|
6
6
|
fileName?: (defName: string, ctx: {
|
|
7
7
|
isRoot: boolean;
|
|
8
8
|
}) => string;
|
package/dist/utils/cliTools.js
CHANGED
|
@@ -58,8 +58,7 @@ export function parseArgs(params, args, help) {
|
|
|
58
58
|
}
|
|
59
59
|
export function parseOrReadJSON(jsonOrPath) {
|
|
60
60
|
jsonOrPath = jsonOrPath.trim();
|
|
61
|
-
if (jsonOrPath.length < 255 &&
|
|
62
|
-
statSync(jsonOrPath, { throwIfNoEntry: false })?.isFile()) {
|
|
61
|
+
if (jsonOrPath.length < 255 && statSync(jsonOrPath, { throwIfNoEntry: false })?.isFile()) {
|
|
63
62
|
jsonOrPath = readFileSync(jsonOrPath, "utf-8");
|
|
64
63
|
}
|
|
65
64
|
return JSON.parse(jsonOrPath);
|
package/dist/utils/esmEmitter.js
CHANGED
|
@@ -52,7 +52,9 @@ export class EsmEmitter {
|
|
|
52
52
|
const initializer = parseExpression(statement.expression);
|
|
53
53
|
const typeNode = statement.typeAnnotation ? parseType(statement.typeAnnotation) : undefined;
|
|
54
54
|
const decl = ts.factory.createVariableDeclaration(statement.name, undefined, typeNode, initializer);
|
|
55
|
-
const modifiers = statement.exported
|
|
55
|
+
const modifiers = statement.exported
|
|
56
|
+
? [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)]
|
|
57
|
+
: undefined;
|
|
56
58
|
const varStmt = ts.factory.createVariableStatement(modifiers, ts.factory.createVariableDeclarationList([decl], ts.NodeFlags.Const));
|
|
57
59
|
__classPrivateFieldGet(this, _EsmEmitter_statements, "f").push({ node: attachJsdoc(varStmt, statement.jsdoc) });
|
|
58
60
|
}
|
|
@@ -72,7 +74,9 @@ export class EsmEmitter {
|
|
|
72
74
|
const sf = ts.createSourceFile("out.ts", "", ts.ScriptTarget.ES2020, false, ts.ScriptKind.TS);
|
|
73
75
|
const importStmts = [...__classPrivateFieldGet(this, _EsmEmitter_imports, "f").entries()]
|
|
74
76
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
75
|
-
.map(([source, names]) => ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([...names]
|
|
77
|
+
.map(([source, names]) => ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([...names]
|
|
78
|
+
.sort()
|
|
79
|
+
.map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))))), ts.factory.createStringLiteral(source)));
|
|
76
80
|
const allStatements = [
|
|
77
81
|
...importStmts.map((node) => ({ node })),
|
|
78
82
|
...__classPrivateFieldGet(this, _EsmEmitter_statements, "f"),
|
|
@@ -99,9 +99,7 @@ const sanitizeIdentifier = (value) => {
|
|
|
99
99
|
.replace(/[^a-zA-Z0-9_$\s]/g, " ")
|
|
100
100
|
.split(/\s+/)
|
|
101
101
|
.filter(Boolean);
|
|
102
|
-
const pascalCase = words
|
|
103
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
104
|
-
.join("");
|
|
102
|
+
const pascalCase = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
105
103
|
const cleaned = pascalCase.replace(/^[^a-zA-Z_$]+/, "").replace(/[^a-zA-Z0-9_$]/g, "");
|
|
106
104
|
return cleaned || "InlineSchema";
|
|
107
105
|
};
|
package/dist/utils/jsdocs.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
export const expandJsdocs = (jsdocs) => {
|
|
2
2
|
const lines = jsdocs.split("\n");
|
|
3
|
-
const result = lines.length === 1
|
|
4
|
-
? lines[0]
|
|
5
|
-
: `\n${lines.map(x => `* ${x}`)
|
|
6
|
-
.join("\n")}\n`;
|
|
3
|
+
const result = lines.length === 1 ? lines[0] : `\n${lines.map((x) => `* ${x}`).join("\n")}\n`;
|
|
7
4
|
return `/**${result}*/\n`;
|
|
8
5
|
};
|
|
9
6
|
export const addJsdocs = (schema, parsed) => {
|