@gabrielbryk/json-schema-to-zod 2.8.0 → 2.9.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/CHANGELOG.md +13 -0
- package/dist/cjs/core/analyzeSchema.js +62 -0
- package/dist/cjs/core/emitZod.js +141 -0
- package/dist/cjs/generators/generateBundle.js +103 -59
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/jsonSchemaToZod.js +5 -167
- package/dist/cjs/parsers/parseSchema.js +124 -24
- package/dist/cjs/utils/buildRefRegistry.js +56 -0
- package/dist/cjs/utils/resolveUri.js +16 -0
- package/dist/esm/Types.js +1 -2
- package/dist/esm/cli.js +10 -12
- package/dist/esm/core/analyzeSchema.js +58 -0
- package/dist/esm/core/emitZod.js +137 -0
- package/dist/esm/generators/generateBundle.js +104 -64
- package/dist/esm/index.js +34 -46
- package/dist/esm/jsonSchemaToZod.js +5 -171
- package/dist/esm/parsers/parseAllOf.js +5 -8
- package/dist/esm/parsers/parseAnyOf.js +6 -10
- package/dist/esm/parsers/parseArray.js +11 -15
- package/dist/esm/parsers/parseBoolean.js +1 -5
- package/dist/esm/parsers/parseConst.js +1 -5
- package/dist/esm/parsers/parseDefault.js +3 -7
- package/dist/esm/parsers/parseEnum.js +1 -5
- package/dist/esm/parsers/parseIfThenElse.js +5 -9
- package/dist/esm/parsers/parseMultipleType.js +3 -7
- package/dist/esm/parsers/parseNot.js +4 -8
- package/dist/esm/parsers/parseNull.js +1 -5
- package/dist/esm/parsers/parseNullable.js +4 -8
- package/dist/esm/parsers/parseNumber.js +11 -15
- package/dist/esm/parsers/parseObject.js +25 -28
- package/dist/esm/parsers/parseOneOf.js +6 -10
- package/dist/esm/parsers/parseSchema.js +183 -87
- package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +6 -10
- package/dist/esm/parsers/parseString.js +11 -15
- package/dist/esm/utils/anyOrUnknown.js +1 -5
- package/dist/esm/utils/buildRefRegistry.js +52 -0
- package/dist/esm/utils/cliTools.js +7 -13
- package/dist/esm/utils/cycles.js +3 -9
- package/dist/esm/utils/half.js +1 -5
- package/dist/esm/utils/jsdocs.js +3 -8
- package/dist/esm/utils/omit.js +1 -5
- package/dist/esm/utils/resolveUri.js +12 -0
- package/dist/esm/utils/withMessage.js +1 -4
- package/dist/esm/zodToJsonSchema.js +1 -4
- package/dist/types/Types.d.ts +28 -0
- package/dist/types/core/analyzeSchema.d.ts +24 -0
- package/dist/types/core/emitZod.d.ts +2 -0
- package/dist/types/generators/generateBundle.d.ts +5 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/jsonSchemaToZod.d.ts +1 -1
- package/dist/types/parsers/parseSchema.d.ts +2 -1
- package/dist/types/utils/buildRefRegistry.d.ts +12 -0
- package/dist/types/utils/resolveUri.d.ts +1 -0
- package/docs/proposals/bundle-refactor.md +43 -0
- package/docs/proposals/ref-anchor-support.md +65 -0
- package/eslint.config.js +26 -0
- package/package.json +10 -4
- /package/{jest.config.js → jest.config.cjs} +0 -0
- /package/{postcjs.js → postcjs.cjs} +0 -0
- /package/{postesm.js → postesm.cjs} +0 -0
|
@@ -1,36 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) => {
|
|
1
|
+
import { parseAnyOf } from "./parseAnyOf.js";
|
|
2
|
+
import { parseBoolean } from "./parseBoolean.js";
|
|
3
|
+
import { parseDefault } from "./parseDefault.js";
|
|
4
|
+
import { parseMultipleType } from "./parseMultipleType.js";
|
|
5
|
+
import { parseNot } from "./parseNot.js";
|
|
6
|
+
import { parseNull } from "./parseNull.js";
|
|
7
|
+
import { parseAllOf } from "./parseAllOf.js";
|
|
8
|
+
import { parseArray } from "./parseArray.js";
|
|
9
|
+
import { parseConst } from "./parseConst.js";
|
|
10
|
+
import { parseEnum } from "./parseEnum.js";
|
|
11
|
+
import { parseIfThenElse } from "./parseIfThenElse.js";
|
|
12
|
+
import { parseNumber } from "./parseNumber.js";
|
|
13
|
+
import { parseObject } from "./parseObject.js";
|
|
14
|
+
import { parseString } from "./parseString.js";
|
|
15
|
+
import { parseOneOf } from "./parseOneOf.js";
|
|
16
|
+
import { parseSimpleDiscriminatedOneOf } from "./parseSimpleDiscriminatedOneOf.js";
|
|
17
|
+
import { parseNullable } from "./parseNullable.js";
|
|
18
|
+
import { anyOrUnknown } from "../utils/anyOrUnknown.js";
|
|
19
|
+
import { resolveUri } from "../utils/resolveUri.js";
|
|
20
|
+
import { buildRefRegistry } from "../utils/buildRefRegistry.js";
|
|
21
|
+
export const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) => {
|
|
23
22
|
// Ensure ref bookkeeping exists so $ref declarations and getter-based recursion work
|
|
24
23
|
refs.root = refs.root ?? schema;
|
|
24
|
+
refs.rootBaseUri = refs.rootBaseUri ?? "root:///";
|
|
25
25
|
refs.declarations = refs.declarations ?? new Map();
|
|
26
26
|
refs.dependencies = refs.dependencies ?? new Map();
|
|
27
27
|
refs.inProgress = refs.inProgress ?? new Set();
|
|
28
28
|
refs.refNameByPointer = refs.refNameByPointer ?? new Map();
|
|
29
29
|
refs.usedNames = refs.usedNames ?? new Set();
|
|
30
30
|
if (typeof schema !== "object")
|
|
31
|
-
return schema ?
|
|
31
|
+
return schema ? anyOrUnknown(refs) : "z.never()";
|
|
32
|
+
const parentBase = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
|
|
33
|
+
const baseUri = typeof schema.$id === "string"
|
|
34
|
+
? resolveUri(parentBase, schema.$id)
|
|
35
|
+
: parentBase;
|
|
36
|
+
const dynamicAnchors = Array.isArray(refs.dynamicAnchors) ? [...refs.dynamicAnchors] : [];
|
|
37
|
+
if (typeof schema.$dynamicAnchor === "string") {
|
|
38
|
+
dynamicAnchors.push({
|
|
39
|
+
name: schema.$dynamicAnchor,
|
|
40
|
+
uri: baseUri,
|
|
41
|
+
path: refs.path,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
32
44
|
if (refs.parserOverride) {
|
|
33
|
-
const custom = refs.parserOverride(schema, refs);
|
|
45
|
+
const custom = refs.parserOverride(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
|
|
34
46
|
if (typeof custom === "string") {
|
|
35
47
|
return custom;
|
|
36
48
|
}
|
|
@@ -41,7 +53,7 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
|
|
|
41
53
|
return seen.r;
|
|
42
54
|
}
|
|
43
55
|
if (refs.depth === undefined || seen.n >= refs.depth) {
|
|
44
|
-
return
|
|
56
|
+
return anyOrUnknown(refs);
|
|
45
57
|
}
|
|
46
58
|
seen.n += 1;
|
|
47
59
|
}
|
|
@@ -49,15 +61,15 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
|
|
|
49
61
|
seen = { r: undefined, n: 0 };
|
|
50
62
|
refs.seen.set(schema, seen);
|
|
51
63
|
}
|
|
52
|
-
if (
|
|
53
|
-
const parsedRef = parseRef(schema, refs);
|
|
64
|
+
if (its.a.ref(schema)) {
|
|
65
|
+
const parsedRef = parseRef(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
|
|
54
66
|
seen.r = parsedRef;
|
|
55
67
|
return parsedRef;
|
|
56
68
|
}
|
|
57
|
-
let parsed = selectParser(schema, refs);
|
|
69
|
+
let parsed = selectParser(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
|
|
58
70
|
if (!blockMeta) {
|
|
59
71
|
if (!refs.withoutDescribes) {
|
|
60
|
-
parsed = addDescribes(schema, parsed, refs);
|
|
72
|
+
parsed = addDescribes(schema, parsed, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
|
|
61
73
|
}
|
|
62
74
|
if (!refs.withoutDefaults) {
|
|
63
75
|
parsed = addDefaults(schema, parsed);
|
|
@@ -67,19 +79,21 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
|
|
|
67
79
|
seen.r = parsed;
|
|
68
80
|
return parsed;
|
|
69
81
|
};
|
|
70
|
-
exports.parseSchema = parseSchema;
|
|
71
82
|
const parseRef = (schema, refs) => {
|
|
72
|
-
const
|
|
83
|
+
const refValue = schema.$dynamicRef ?? schema.$ref;
|
|
84
|
+
const resolved = resolveRef(schema, refValue, refs);
|
|
73
85
|
if (!resolved) {
|
|
74
|
-
|
|
86
|
+
refs.onUnresolvedRef?.(refValue, refs.path);
|
|
87
|
+
return anyOrUnknown(refs);
|
|
75
88
|
}
|
|
76
|
-
const { schema: target, path } = resolved;
|
|
77
|
-
const refName = getOrCreateRefName(
|
|
89
|
+
const { schema: target, path, pointerKey } = resolved;
|
|
90
|
+
const refName = getOrCreateRefName(pointerKey, path, refs);
|
|
78
91
|
if (!refs.declarations.has(refName) && !refs.inProgress.has(refName)) {
|
|
79
92
|
refs.inProgress.add(refName);
|
|
80
|
-
const declaration =
|
|
93
|
+
const declaration = parseSchema(target, {
|
|
81
94
|
...refs,
|
|
82
95
|
path,
|
|
96
|
+
currentBaseUri: resolved.baseUri,
|
|
83
97
|
currentSchemaName: refName,
|
|
84
98
|
root: refs.root,
|
|
85
99
|
});
|
|
@@ -132,23 +146,93 @@ const addDescribes = (schema, parsed, refs) => {
|
|
|
132
146
|
}
|
|
133
147
|
return parsed;
|
|
134
148
|
};
|
|
135
|
-
const resolveRef = (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
const resolveRef = (schemaNode, ref, refs) => {
|
|
150
|
+
const base = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
|
|
151
|
+
// Handle dynamicRef lookup via dynamicAnchors stack
|
|
152
|
+
const isDynamic = typeof schemaNode.$dynamicRef === "string";
|
|
153
|
+
if (isDynamic && refs.dynamicAnchors && ref.startsWith("#")) {
|
|
154
|
+
const name = ref.slice(1);
|
|
155
|
+
for (let i = refs.dynamicAnchors.length - 1; i >= 0; i -= 1) {
|
|
156
|
+
const entry = refs.dynamicAnchors[i];
|
|
157
|
+
if (entry.name === name) {
|
|
158
|
+
const key = `${entry.uri}#${name}`;
|
|
159
|
+
const target = refs.refRegistry?.get(key);
|
|
160
|
+
if (target) {
|
|
161
|
+
return { schema: target.schema, path: target.path, baseUri: target.baseUri, pointerKey: key };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Resolve URI against base
|
|
167
|
+
const resolvedUri = resolveUri(base, ref);
|
|
168
|
+
const [uriBase, fragment] = resolvedUri.split("#");
|
|
169
|
+
const key = fragment ? `${uriBase}#${fragment}` : uriBase;
|
|
170
|
+
let regEntry = refs.refRegistry?.get(key);
|
|
171
|
+
if (regEntry) {
|
|
172
|
+
return { schema: regEntry.schema, path: regEntry.path, baseUri: regEntry.baseUri, pointerKey: key };
|
|
173
|
+
}
|
|
174
|
+
// Legacy recursive ref: treat as dynamic to __recursive__
|
|
175
|
+
if (schemaNode.$recursiveRef) {
|
|
176
|
+
const recursiveKey = `${base}#__recursive__`;
|
|
177
|
+
regEntry = refs.refRegistry?.get(recursiveKey);
|
|
178
|
+
if (regEntry) {
|
|
179
|
+
return {
|
|
180
|
+
schema: regEntry.schema,
|
|
181
|
+
path: regEntry.path,
|
|
182
|
+
baseUri: regEntry.baseUri,
|
|
183
|
+
pointerKey: recursiveKey,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// External resolver hook
|
|
188
|
+
const extBase = uriBaseFromRef(resolvedUri);
|
|
189
|
+
if (refs.resolveExternalRef && extBase && !isLocalBase(extBase, refs.rootBaseUri ?? "")) {
|
|
190
|
+
const loaded = refs.resolveExternalRef(extBase);
|
|
191
|
+
if (loaded) {
|
|
192
|
+
// If async resolver is used synchronously here, it will be ignored; keep simple sync for now
|
|
193
|
+
const schema = loaded.then ? undefined : loaded;
|
|
194
|
+
if (schema) {
|
|
195
|
+
const { registry } = buildRefRegistry(schema, extBase);
|
|
196
|
+
registry.forEach((entry, k) => refs.refRegistry?.set(k, entry));
|
|
197
|
+
regEntry = refs.refRegistry?.get(key);
|
|
198
|
+
if (regEntry) {
|
|
199
|
+
return {
|
|
200
|
+
schema: regEntry.schema,
|
|
201
|
+
path: regEntry.path,
|
|
202
|
+
baseUri: regEntry.baseUri,
|
|
203
|
+
pointerKey: key,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Backward compatibility: JSON Pointer into root
|
|
210
|
+
if (refs.root && ref.startsWith("#/")) {
|
|
211
|
+
const rawSegments = ref
|
|
212
|
+
.slice(2)
|
|
213
|
+
.split("/")
|
|
214
|
+
.filter((segment) => segment.length > 0)
|
|
215
|
+
.map(decodePointerSegment);
|
|
216
|
+
let current = refs.root;
|
|
217
|
+
for (const segment of rawSegments) {
|
|
218
|
+
if (typeof current !== "object" || current === null)
|
|
219
|
+
return undefined;
|
|
220
|
+
current = current[segment];
|
|
221
|
+
}
|
|
222
|
+
return { schema: current, path: rawSegments, baseUri: base, pointerKey: ref };
|
|
223
|
+
}
|
|
224
|
+
return undefined;
|
|
150
225
|
};
|
|
151
226
|
const decodePointerSegment = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
227
|
+
const uriBaseFromRef = (resolvedUri) => {
|
|
228
|
+
const hashIdx = resolvedUri.indexOf("#");
|
|
229
|
+
return hashIdx === -1 ? resolvedUri : resolvedUri.slice(0, hashIdx);
|
|
230
|
+
};
|
|
231
|
+
const isLocalBase = (base, rootBase) => {
|
|
232
|
+
if (!rootBase)
|
|
233
|
+
return false;
|
|
234
|
+
return base === rootBase;
|
|
235
|
+
};
|
|
152
236
|
const getOrCreateRefName = (pointer, path, refs) => {
|
|
153
237
|
if (refs.refNameByPointer?.has(pointer)) {
|
|
154
238
|
return refs.refNameByPointer.get(pointer);
|
|
@@ -159,12 +243,24 @@ const getOrCreateRefName = (pointer, path, refs) => {
|
|
|
159
243
|
return preferred;
|
|
160
244
|
};
|
|
161
245
|
const buildNameFromPath = (path, used) => {
|
|
162
|
-
const filtered = path
|
|
246
|
+
const filtered = path
|
|
247
|
+
.map((segment, idx) => {
|
|
248
|
+
if (idx === 0 && (segment === "$defs" || segment === "definitions")) {
|
|
249
|
+
return undefined; // root-level defs prefix is redundant for naming
|
|
250
|
+
}
|
|
251
|
+
if (segment === "properties")
|
|
252
|
+
return undefined; // skip noisy properties segment
|
|
253
|
+
if (segment === "$defs" || segment === "definitions")
|
|
254
|
+
return "Defs";
|
|
255
|
+
return segment;
|
|
256
|
+
})
|
|
257
|
+
.filter((segment) => segment !== undefined);
|
|
163
258
|
const base = filtered.length
|
|
164
259
|
? filtered
|
|
165
260
|
.map((segment) => typeof segment === "number"
|
|
166
261
|
? `Ref${segment}`
|
|
167
262
|
: segment
|
|
263
|
+
.toString()
|
|
168
264
|
.replace(/[^a-zA-Z0-9_$]/g, " ")
|
|
169
265
|
.split(" ")
|
|
170
266
|
.filter(Boolean)
|
|
@@ -201,60 +297,60 @@ const addAnnotations = (schema, parsed) => {
|
|
|
201
297
|
return parsed;
|
|
202
298
|
};
|
|
203
299
|
const selectParser = (schema, refs) => {
|
|
204
|
-
if (
|
|
205
|
-
return
|
|
300
|
+
if (its.a.nullable(schema)) {
|
|
301
|
+
return parseNullable(schema, refs);
|
|
206
302
|
}
|
|
207
|
-
else if (
|
|
208
|
-
return
|
|
303
|
+
else if (its.an.object(schema)) {
|
|
304
|
+
return parseObject(schema, refs);
|
|
209
305
|
}
|
|
210
|
-
else if (
|
|
211
|
-
return
|
|
306
|
+
else if (its.an.array(schema)) {
|
|
307
|
+
return parseArray(schema, refs);
|
|
212
308
|
}
|
|
213
|
-
else if (
|
|
214
|
-
return
|
|
309
|
+
else if (its.an.anyOf(schema)) {
|
|
310
|
+
return parseAnyOf(schema, refs);
|
|
215
311
|
}
|
|
216
|
-
else if (
|
|
217
|
-
return
|
|
312
|
+
else if (its.an.allOf(schema)) {
|
|
313
|
+
return parseAllOf(schema, refs);
|
|
218
314
|
}
|
|
219
|
-
else if (
|
|
220
|
-
return
|
|
315
|
+
else if (its.a.simpleDiscriminatedOneOf(schema)) {
|
|
316
|
+
return parseSimpleDiscriminatedOneOf(schema, refs);
|
|
221
317
|
}
|
|
222
|
-
else if (
|
|
223
|
-
return
|
|
318
|
+
else if (its.a.oneOf(schema)) {
|
|
319
|
+
return parseOneOf(schema, refs);
|
|
224
320
|
}
|
|
225
|
-
else if (
|
|
226
|
-
return
|
|
321
|
+
else if (its.a.not(schema)) {
|
|
322
|
+
return parseNot(schema, refs);
|
|
227
323
|
}
|
|
228
|
-
else if (
|
|
229
|
-
return
|
|
324
|
+
else if (its.an.enum(schema)) {
|
|
325
|
+
return parseEnum(schema); //<-- needs to come before primitives
|
|
230
326
|
}
|
|
231
|
-
else if (
|
|
232
|
-
return
|
|
327
|
+
else if (its.a.const(schema)) {
|
|
328
|
+
return parseConst(schema);
|
|
233
329
|
}
|
|
234
|
-
else if (
|
|
235
|
-
return
|
|
330
|
+
else if (its.a.multipleType(schema)) {
|
|
331
|
+
return parseMultipleType(schema, refs);
|
|
236
332
|
}
|
|
237
|
-
else if (
|
|
238
|
-
return
|
|
333
|
+
else if (its.a.primitive(schema, "string")) {
|
|
334
|
+
return parseString(schema, refs);
|
|
239
335
|
}
|
|
240
|
-
else if (
|
|
241
|
-
|
|
242
|
-
return
|
|
336
|
+
else if (its.a.primitive(schema, "number") ||
|
|
337
|
+
its.a.primitive(schema, "integer")) {
|
|
338
|
+
return parseNumber(schema);
|
|
243
339
|
}
|
|
244
|
-
else if (
|
|
245
|
-
return
|
|
340
|
+
else if (its.a.primitive(schema, "boolean")) {
|
|
341
|
+
return parseBoolean(schema);
|
|
246
342
|
}
|
|
247
|
-
else if (
|
|
248
|
-
return
|
|
343
|
+
else if (its.a.primitive(schema, "null")) {
|
|
344
|
+
return parseNull(schema);
|
|
249
345
|
}
|
|
250
|
-
else if (
|
|
251
|
-
return
|
|
346
|
+
else if (its.a.conditional(schema)) {
|
|
347
|
+
return parseIfThenElse(schema, refs);
|
|
252
348
|
}
|
|
253
349
|
else {
|
|
254
|
-
return
|
|
350
|
+
return parseDefault(schema, refs);
|
|
255
351
|
}
|
|
256
352
|
};
|
|
257
|
-
|
|
353
|
+
export const its = {
|
|
258
354
|
an: {
|
|
259
355
|
object: (x) => x.type === "object",
|
|
260
356
|
array: (x) => x.type === "array",
|
|
@@ -266,7 +362,7 @@ exports.its = {
|
|
|
266
362
|
nullable: (x) => x.nullable === true,
|
|
267
363
|
multipleType: (x) => Array.isArray(x.type),
|
|
268
364
|
not: (x) => x.not !== undefined,
|
|
269
|
-
ref: (x) => typeof x.$ref === "string",
|
|
365
|
+
ref: (x) => typeof x.$ref === "string" || typeof x.$dynamicRef === "string",
|
|
270
366
|
const: (x) => x.const !== undefined,
|
|
271
367
|
primitive: (x, p) => x.type === p,
|
|
272
368
|
conditional: (x) => Boolean("if" in x && x.if && "then" in x && "else" in x && x.then && x.else),
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const parseSchema_js_1 = require("./parseSchema.js");
|
|
5
|
-
const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
|
|
6
|
-
const parseSimpleDiscriminatedOneOf = (schema, refs) => {
|
|
1
|
+
import { parseSchema } from "./parseSchema.js";
|
|
2
|
+
import { anyOrUnknown } from "../utils/anyOrUnknown.js";
|
|
3
|
+
export const parseSimpleDiscriminatedOneOf = (schema, refs) => {
|
|
7
4
|
const discriminator = schema.discriminator.propertyName;
|
|
8
|
-
const options = schema.oneOf.map((option, i) =>
|
|
5
|
+
const options = schema.oneOf.map((option, i) => parseSchema(option, {
|
|
9
6
|
...refs,
|
|
10
7
|
path: [...refs.path, "oneOf", i],
|
|
11
8
|
}));
|
|
12
9
|
return schema.oneOf.length
|
|
13
10
|
? schema.oneOf.length === 1
|
|
14
|
-
?
|
|
11
|
+
? parseSchema(schema.oneOf[0], {
|
|
15
12
|
...refs,
|
|
16
13
|
path: [...refs.path, "oneOf", 0],
|
|
17
14
|
})
|
|
18
15
|
: `z.discriminatedUnion("${discriminator}", [${options.join(", ")}])`
|
|
19
|
-
:
|
|
16
|
+
: anyOrUnknown(refs);
|
|
20
17
|
};
|
|
21
|
-
exports.parseSimpleDiscriminatedOneOf = parseSimpleDiscriminatedOneOf;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const withMessage_js_1 = require("../utils/withMessage.js");
|
|
5
|
-
const parseSchema_js_1 = require("./parseSchema.js");
|
|
6
|
-
const parseString = (schema, refs) => {
|
|
1
|
+
import { withMessage } from "../utils/withMessage.js";
|
|
2
|
+
import { parseSchema } from "./parseSchema.js";
|
|
3
|
+
export const parseString = (schema, refs) => {
|
|
7
4
|
const formatError = schema.errorMessage?.format;
|
|
8
5
|
const refContext = ensureRefs(refs);
|
|
9
6
|
const topLevelFormatMap = {
|
|
@@ -29,7 +26,7 @@ const parseString = (schema, refs) => {
|
|
|
29
26
|
const formatHandled = Boolean(formatFn);
|
|
30
27
|
let formatWasHandled = formatHandled;
|
|
31
28
|
if (!formatHandled) {
|
|
32
|
-
r +=
|
|
29
|
+
r += withMessage(schema, "format", ({ value }) => {
|
|
33
30
|
switch (value) {
|
|
34
31
|
case "email":
|
|
35
32
|
formatWasHandled = true;
|
|
@@ -248,25 +245,25 @@ const parseString = (schema, refs) => {
|
|
|
248
245
|
if (schema.format && !formatWasHandled) {
|
|
249
246
|
refContext.onUnknownFormat?.(schema.format, refContext.path);
|
|
250
247
|
}
|
|
251
|
-
r +=
|
|
248
|
+
r += withMessage(schema, "pattern", ({ json }) => ({
|
|
252
249
|
opener: `.regex(new RegExp(${json})`,
|
|
253
250
|
closer: ")",
|
|
254
251
|
messagePrefix: ", { error: ",
|
|
255
252
|
messageCloser: " })",
|
|
256
253
|
}));
|
|
257
|
-
r +=
|
|
254
|
+
r += withMessage(schema, "minLength", ({ json }) => ({
|
|
258
255
|
opener: `.min(${json}`,
|
|
259
256
|
closer: ")",
|
|
260
257
|
messagePrefix: ", { error: ",
|
|
261
258
|
messageCloser: " })",
|
|
262
259
|
}));
|
|
263
|
-
r +=
|
|
260
|
+
r += withMessage(schema, "maxLength", ({ json }) => ({
|
|
264
261
|
opener: `.max(${json}`,
|
|
265
262
|
closer: ")",
|
|
266
263
|
messagePrefix: ", { error: ",
|
|
267
264
|
messageCloser: " })",
|
|
268
265
|
}));
|
|
269
|
-
r +=
|
|
266
|
+
r += withMessage(schema, "contentEncoding", ({ value }) => {
|
|
270
267
|
if (value === "base64") {
|
|
271
268
|
return {
|
|
272
269
|
opener: ".base64(",
|
|
@@ -276,7 +273,7 @@ const parseString = (schema, refs) => {
|
|
|
276
273
|
};
|
|
277
274
|
}
|
|
278
275
|
});
|
|
279
|
-
const contentMediaType =
|
|
276
|
+
const contentMediaType = withMessage(schema, "contentMediaType", ({ value }) => {
|
|
280
277
|
if (value === "application/json") {
|
|
281
278
|
return {
|
|
282
279
|
opener: '.transform((str, ctx) => { try { return JSON.parse(str); } catch (err) { ctx.addIssue({ code: "custom", message: "Invalid JSON" }); }}',
|
|
@@ -288,10 +285,10 @@ const parseString = (schema, refs) => {
|
|
|
288
285
|
});
|
|
289
286
|
if (contentMediaType != "") {
|
|
290
287
|
r += contentMediaType;
|
|
291
|
-
r +=
|
|
288
|
+
r += withMessage(schema, "contentSchema", ({ value }) => {
|
|
292
289
|
if (value && value instanceof Object) {
|
|
293
290
|
return {
|
|
294
|
-
opener: `.pipe(${
|
|
291
|
+
opener: `.pipe(${parseSchema(value, refContext)}`,
|
|
295
292
|
closer: ")",
|
|
296
293
|
messagePrefix: ", { error: ",
|
|
297
294
|
messageCloser: " })",
|
|
@@ -301,7 +298,6 @@ const parseString = (schema, refs) => {
|
|
|
301
298
|
}
|
|
302
299
|
return r;
|
|
303
300
|
};
|
|
304
|
-
exports.parseString = parseString;
|
|
305
301
|
function ensureRefs(refs) {
|
|
306
302
|
if (refs)
|
|
307
303
|
return refs;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.anyOrUnknown = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Returns "z.unknown()" if the useUnknown option is enabled, otherwise "z.any()".
|
|
6
3
|
* This helper is used throughout the library for fallback cases.
|
|
@@ -8,7 +5,6 @@ exports.anyOrUnknown = void 0;
|
|
|
8
5
|
* @param refs - The refs object containing options
|
|
9
6
|
* @returns The appropriate Zod schema string
|
|
10
7
|
*/
|
|
11
|
-
const anyOrUnknown = (refs) => {
|
|
8
|
+
export const anyOrUnknown = (refs) => {
|
|
12
9
|
return refs?.useUnknown ? "z.unknown()" : "z.any()";
|
|
13
10
|
};
|
|
14
|
-
exports.anyOrUnknown = anyOrUnknown;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolveUri } from "./resolveUri.js";
|
|
2
|
+
export const buildRefRegistry = (schema, rootBaseUri = "root:///", opts = {}) => {
|
|
3
|
+
const registry = new Map();
|
|
4
|
+
const walk = (node, baseUri, path) => {
|
|
5
|
+
if (typeof node !== "object" || node === null)
|
|
6
|
+
return;
|
|
7
|
+
const obj = node;
|
|
8
|
+
const nextBase = obj.$id ? resolveUri(baseUri, obj.$id) : baseUri;
|
|
9
|
+
// Legacy recursive anchor
|
|
10
|
+
if (obj.$recursiveAnchor === true) {
|
|
11
|
+
const name = "__recursive__";
|
|
12
|
+
registry.set(`${nextBase}#${name}`, {
|
|
13
|
+
schema: node,
|
|
14
|
+
path,
|
|
15
|
+
baseUri: nextBase,
|
|
16
|
+
dynamic: true,
|
|
17
|
+
anchor: name,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// Register base entry
|
|
21
|
+
registry.set(nextBase, { schema: node, path, baseUri: nextBase });
|
|
22
|
+
if (typeof obj.$anchor === "string") {
|
|
23
|
+
registry.set(`${nextBase}#${obj.$anchor}`, {
|
|
24
|
+
schema: node,
|
|
25
|
+
path,
|
|
26
|
+
baseUri: nextBase,
|
|
27
|
+
anchor: obj.$anchor,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (typeof obj.$dynamicAnchor === "string") {
|
|
31
|
+
const name = obj.$dynamicAnchor;
|
|
32
|
+
registry.set(`${nextBase}#${name}`, {
|
|
33
|
+
schema: node,
|
|
34
|
+
path,
|
|
35
|
+
baseUri: nextBase,
|
|
36
|
+
dynamic: true,
|
|
37
|
+
anchor: name,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const key in obj) {
|
|
41
|
+
const value = obj[key];
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
value.forEach((v, i) => walk(v, nextBase, [...path, key, i]));
|
|
44
|
+
}
|
|
45
|
+
else if (typeof value === "object" && value !== null) {
|
|
46
|
+
walk(value, nextBase, [...path, key]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
walk(schema, rootBaseUri, []);
|
|
51
|
+
return { registry, rootBaseUri };
|
|
52
|
+
};
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.parseArgs = parseArgs;
|
|
4
|
-
exports.parseOrReadJSON = parseOrReadJSON;
|
|
5
|
-
exports.readPipe = readPipe;
|
|
6
|
-
exports.printParams = printParams;
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
function parseArgs(params, args, help) {
|
|
1
|
+
import { statSync, readFileSync } from "fs";
|
|
2
|
+
export function parseArgs(params, args, help) {
|
|
9
3
|
const result = {};
|
|
10
4
|
if (help) {
|
|
11
5
|
let index = args.indexOf("--help");
|
|
@@ -62,15 +56,15 @@ function parseArgs(params, args, help) {
|
|
|
62
56
|
}
|
|
63
57
|
return result;
|
|
64
58
|
}
|
|
65
|
-
function parseOrReadJSON(jsonOrPath) {
|
|
59
|
+
export function parseOrReadJSON(jsonOrPath) {
|
|
66
60
|
jsonOrPath = jsonOrPath.trim();
|
|
67
61
|
if (jsonOrPath.length < 255 &&
|
|
68
|
-
|
|
69
|
-
jsonOrPath =
|
|
62
|
+
statSync(jsonOrPath, { throwIfNoEntry: false })?.isFile()) {
|
|
63
|
+
jsonOrPath = readFileSync(jsonOrPath, "utf-8");
|
|
70
64
|
}
|
|
71
65
|
return JSON.parse(jsonOrPath);
|
|
72
66
|
}
|
|
73
|
-
function readPipe() {
|
|
67
|
+
export function readPipe() {
|
|
74
68
|
return new Promise((resolve, reject) => {
|
|
75
69
|
let buf = "";
|
|
76
70
|
process.stdin
|
|
@@ -85,7 +79,7 @@ function readPipe() {
|
|
|
85
79
|
});
|
|
86
80
|
});
|
|
87
81
|
}
|
|
88
|
-
function printParams(params) {
|
|
82
|
+
export function printParams(params) {
|
|
89
83
|
const longest = Object.keys(params).reduce((l, c) => (c.length > l ? c.length : l), 5);
|
|
90
84
|
const header = "Name " + " ".repeat(longest - 2) + "Short Description";
|
|
91
85
|
console.log(header);
|
package/dist/esm/utils/cycles.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.computeScc = exports.detectCycles = exports.findRefDependencies = void 0;
|
|
4
|
-
const findRefDependencies = (schema, validDefNames) => {
|
|
1
|
+
export const findRefDependencies = (schema, validDefNames) => {
|
|
5
2
|
const deps = new Set();
|
|
6
3
|
function traverse(obj) {
|
|
7
4
|
if (obj === null || typeof obj !== "object")
|
|
@@ -25,8 +22,7 @@ const findRefDependencies = (schema, validDefNames) => {
|
|
|
25
22
|
traverse(schema);
|
|
26
23
|
return deps;
|
|
27
24
|
};
|
|
28
|
-
|
|
29
|
-
const detectCycles = (defNames, deps) => {
|
|
25
|
+
export const detectCycles = (defNames, deps) => {
|
|
30
26
|
const cycleNodes = new Set();
|
|
31
27
|
const visited = new Set();
|
|
32
28
|
const recursionStack = new Set();
|
|
@@ -57,8 +53,7 @@ const detectCycles = (defNames, deps) => {
|
|
|
57
53
|
}
|
|
58
54
|
return cycleNodes;
|
|
59
55
|
};
|
|
60
|
-
|
|
61
|
-
const computeScc = (defNames, deps) => {
|
|
56
|
+
export const computeScc = (defNames, deps) => {
|
|
62
57
|
// Tarjan's algorithm, keeps mapping for quick "is this ref in my cycle?"
|
|
63
58
|
const index = new Map();
|
|
64
59
|
const lowlink = new Map();
|
|
@@ -110,4 +105,3 @@ const computeScc = (defNames, deps) => {
|
|
|
110
105
|
}
|
|
111
106
|
return { cycleMembers, componentByName };
|
|
112
107
|
};
|
|
113
|
-
exports.computeScc = computeScc;
|
package/dist/esm/utils/half.js
CHANGED
package/dist/esm/utils/jsdocs.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addJsdocs = exports.expandJsdocs = void 0;
|
|
4
|
-
const expandJsdocs = (jsdocs) => {
|
|
1
|
+
export const expandJsdocs = (jsdocs) => {
|
|
5
2
|
const lines = jsdocs.split("\n");
|
|
6
3
|
const result = lines.length === 1
|
|
7
4
|
? lines[0]
|
|
@@ -9,12 +6,10 @@ const expandJsdocs = (jsdocs) => {
|
|
|
9
6
|
.join("\n")}\n`;
|
|
10
7
|
return `/**${result}*/\n`;
|
|
11
8
|
};
|
|
12
|
-
|
|
13
|
-
const addJsdocs = (schema, parsed) => {
|
|
9
|
+
export const addJsdocs = (schema, parsed) => {
|
|
14
10
|
const description = schema.description;
|
|
15
11
|
if (!description) {
|
|
16
12
|
return parsed;
|
|
17
13
|
}
|
|
18
|
-
return `\n${
|
|
14
|
+
return `\n${expandJsdocs(description)}${parsed}`;
|
|
19
15
|
};
|
|
20
|
-
exports.addJsdocs = addJsdocs;
|