@gabrielbryk/json-schema-to-zod 2.9.0 → 2.10.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 +5 -0
- package/dist/cjs/core/emitZod.js +2 -2
- package/dist/cjs/generators/generateBundle.js +16 -6
- package/dist/cjs/parsers/parseAllOf.js +12 -6
- package/dist/cjs/parsers/parseBoolean.js +1 -3
- package/dist/cjs/parsers/parseIfThenElse.js +1 -1
- package/dist/cjs/parsers/parseNull.js +1 -3
- package/dist/cjs/parsers/parseObject.js +8 -3
- package/dist/cjs/parsers/parseSchema.js +10 -6
- package/dist/cjs/parsers/parseString.js +1 -1
- package/dist/cjs/utils/buildRefRegistry.js +1 -1
- package/dist/cjs/utils/omit.js +3 -2
- package/dist/esm/core/emitZod.js +2 -2
- package/dist/esm/generators/generateBundle.js +16 -6
- package/dist/esm/parsers/parseAllOf.js +12 -6
- package/dist/esm/parsers/parseBoolean.js +1 -3
- package/dist/esm/parsers/parseIfThenElse.js +1 -1
- package/dist/esm/parsers/parseNull.js +1 -3
- package/dist/esm/parsers/parseObject.js +8 -3
- package/dist/esm/parsers/parseSchema.js +10 -6
- package/dist/esm/parsers/parseString.js +1 -1
- package/dist/esm/utils/buildRefRegistry.js +1 -1
- package/dist/esm/utils/omit.js +3 -2
- package/dist/types/Types.d.ts +6 -3
- package/dist/types/parsers/parseBoolean.d.ts +1 -4
- package/dist/types/parsers/parseNull.d.ts +1 -4
- package/dist/types/utils/buildRefRegistry.d.ts +2 -2
- package/eslint.config.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
### Patch Changes
|
|
12
12
|
|
|
13
13
|
- b8f6248: - Commit remaining utility, ref resolution test, and config updates.
|
|
14
|
+
- 04b6c6b: Fix bundle output by emitting z.lazy for cyclical refs outside object properties, use ZodError.issues in generated conditionals, and make nested type extraction array-aware to avoid invalid indexers.
|
|
15
|
+
- b8f7b29: Fix type errors in CI by replacing symbol-based allOf indexing, guarding invalid refs, and tightening string content schema parsing types.
|
|
16
|
+
- 691cc5b: - Added ESLint (recommended + no-require-imports) and cleaned all lint issues across src/tests; tightened types and removed unused vars.
|
|
17
|
+
- Ensured ESM-friendly test evals and ref/anchor resolver code without require usage.
|
|
18
|
+
- 4d127fe: Remove fallback to the non-existent ZodError.errors property in generated conditional schemas; rely on ZodError.issues to avoid TypeScript errors.
|
|
14
19
|
- 7d257dd: - Ensure dist/esm emits real ESM with NodeNext settings and type:module, and update tests to run under ESM by providing createRequire shims.
|
|
15
20
|
|
|
16
21
|
## 2.8.0
|
package/dist/cjs/core/emitZod.js
CHANGED
|
@@ -101,8 +101,8 @@ const emitZod = (analysis) => {
|
|
|
101
101
|
})
|
|
102
102
|
.join("\n")
|
|
103
103
|
: "";
|
|
104
|
-
const jsdocs = rest.withJsdocs && typeof schema
|
|
105
|
-
? (0, jsdocs_js_1.expandJsdocs)(schema.description)
|
|
104
|
+
const jsdocs = rest.withJsdocs && typeof schema === "object" && schema !== null && "description" in schema
|
|
105
|
+
? (0, jsdocs_js_1.expandJsdocs)(String(schema.description ?? ""))
|
|
106
106
|
: "";
|
|
107
107
|
const lines = [];
|
|
108
108
|
if (module === "cjs" && !noImport) {
|
|
@@ -83,6 +83,7 @@ const buildBundleContext = (defNames, defs, options) => {
|
|
|
83
83
|
return { defInfoMap, rootName, rootTypeName };
|
|
84
84
|
};
|
|
85
85
|
const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options) => {
|
|
86
|
+
const useLazyCrossRefs = options.refResolution?.lazyCrossRefs ?? true;
|
|
86
87
|
return (schema, refs) => {
|
|
87
88
|
if (typeof schema["$ref"] === "string") {
|
|
88
89
|
const refPath = schema["$ref"];
|
|
@@ -109,7 +110,7 @@ const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options
|
|
|
109
110
|
});
|
|
110
111
|
if (resolved)
|
|
111
112
|
return resolved;
|
|
112
|
-
if (isCycle &&
|
|
113
|
+
if (isCycle && useLazyCrossRefs) {
|
|
113
114
|
return `z.lazy(() => ${refInfo.schemaName})`;
|
|
114
115
|
}
|
|
115
116
|
return refInfo.schemaName;
|
|
@@ -289,7 +290,7 @@ const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath =
|
|
|
289
290
|
}
|
|
290
291
|
// inline $defs
|
|
291
292
|
if (record.$defs && typeof record.$defs === "object") {
|
|
292
|
-
for (const [
|
|
293
|
+
for (const [, defSchema] of Object.entries(record.$defs)) {
|
|
293
294
|
nestedTypes.push(...findNestedTypesInSchema(defSchema, parentTypeName, defNames, currentPath));
|
|
294
295
|
}
|
|
295
296
|
}
|
|
@@ -338,14 +339,23 @@ const generateNestedTypesFile = (nestedTypes) => {
|
|
|
338
339
|
lines.push(`import type { ${typeName} } from './${file}.schema.js';`);
|
|
339
340
|
}
|
|
340
341
|
lines.push("");
|
|
342
|
+
const buildAccessExpr = (parentType, propertyPath) => {
|
|
343
|
+
let accessExpr = parentType;
|
|
344
|
+
for (const prop of propertyPath) {
|
|
345
|
+
const accessor = prop === "items"
|
|
346
|
+
? "[number]"
|
|
347
|
+
: typeof prop === "number"
|
|
348
|
+
? `[${prop}]`
|
|
349
|
+
: `[${JSON.stringify(prop)}]`;
|
|
350
|
+
accessExpr = `NonNullable<${accessExpr}${accessor}>`;
|
|
351
|
+
}
|
|
352
|
+
return accessExpr;
|
|
353
|
+
};
|
|
341
354
|
for (const [parentType, types] of [...byParent.entries()].sort()) {
|
|
342
355
|
lines.push(`// From ${parentType}`);
|
|
343
356
|
for (const info of types.sort((a, b) => a.typeName.localeCompare(b.typeName))) {
|
|
344
357
|
if (info.propertyPath.length > 0) {
|
|
345
|
-
|
|
346
|
-
for (const prop of info.propertyPath) {
|
|
347
|
-
accessExpr = `NonNullable<${accessExpr}['${prop}']>`;
|
|
348
|
-
}
|
|
358
|
+
const accessExpr = buildAccessExpr(parentType, info.propertyPath);
|
|
349
359
|
lines.push(`export type ${info.typeName} = ${accessExpr};`);
|
|
350
360
|
}
|
|
351
361
|
}
|
|
@@ -3,19 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseAllOf = parseAllOf;
|
|
4
4
|
const parseSchema_js_1 = require("./parseSchema.js");
|
|
5
5
|
const half_js_1 = require("../utils/half.js");
|
|
6
|
-
const
|
|
6
|
+
const originalIndexKey = "__originalIndex";
|
|
7
7
|
const ensureOriginalIndex = (arr) => {
|
|
8
|
-
|
|
8
|
+
const newArr = [];
|
|
9
9
|
for (let i = 0; i < arr.length; i++) {
|
|
10
10
|
const item = arr[i];
|
|
11
11
|
if (typeof item === "boolean") {
|
|
12
|
-
newArr.push(item ? { [
|
|
12
|
+
newArr.push(item ? { [originalIndexKey]: i } : { [originalIndexKey]: i, not: {} });
|
|
13
13
|
}
|
|
14
|
-
else if (
|
|
14
|
+
else if (typeof item === "object" &&
|
|
15
|
+
item !== null &&
|
|
16
|
+
originalIndexKey in item) {
|
|
15
17
|
return arr;
|
|
16
18
|
}
|
|
17
19
|
else {
|
|
18
|
-
newArr.push({ ...item, [
|
|
20
|
+
newArr.push({ ...item, [originalIndexKey]: i });
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
return newArr;
|
|
@@ -28,7 +30,11 @@ function parseAllOf(schema, refs) {
|
|
|
28
30
|
const item = schema.allOf[0];
|
|
29
31
|
return (0, parseSchema_js_1.parseSchema)(item, {
|
|
30
32
|
...refs,
|
|
31
|
-
path: [
|
|
33
|
+
path: [
|
|
34
|
+
...refs.path,
|
|
35
|
+
"allOf",
|
|
36
|
+
item[originalIndexKey] ?? 0,
|
|
37
|
+
],
|
|
32
38
|
});
|
|
33
39
|
}
|
|
34
40
|
else {
|
|
@@ -17,7 +17,7 @@ const parseIfThenElse = (schema, refs) => {
|
|
|
17
17
|
? ${$then}.safeParse(value)
|
|
18
18
|
: ${$else}.safeParse(value);
|
|
19
19
|
if (!result.success) {
|
|
20
|
-
const issues = result.error.issues
|
|
20
|
+
const issues = result.error.issues;
|
|
21
21
|
issues.forEach((issue) => ctx.addIssue(issue))
|
|
22
22
|
}
|
|
23
23
|
})`;
|
|
@@ -200,6 +200,7 @@ function parseObject(objectSchema, refs) {
|
|
|
200
200
|
output += `.and(${(0, parseAnyOf_js_1.parseAnyOf)({
|
|
201
201
|
...objectSchema,
|
|
202
202
|
anyOf: objectSchema.anyOf.map((x) => typeof x === "object" &&
|
|
203
|
+
x !== null &&
|
|
203
204
|
!x.type &&
|
|
204
205
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
205
206
|
? { ...x, type: "object" }
|
|
@@ -210,6 +211,7 @@ function parseObject(objectSchema, refs) {
|
|
|
210
211
|
output += `.and(${(0, parseOneOf_js_1.parseOneOf)({
|
|
211
212
|
...objectSchema,
|
|
212
213
|
oneOf: objectSchema.oneOf.map((x) => typeof x === "object" &&
|
|
214
|
+
x !== null &&
|
|
213
215
|
!x.type &&
|
|
214
216
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
215
217
|
? { ...x, type: "object" }
|
|
@@ -220,6 +222,7 @@ function parseObject(objectSchema, refs) {
|
|
|
220
222
|
output += `.and(${(0, parseAllOf_js_1.parseAllOf)({
|
|
221
223
|
...objectSchema,
|
|
222
224
|
allOf: objectSchema.allOf.map((x) => typeof x === "object" &&
|
|
225
|
+
x !== null &&
|
|
223
226
|
!x.type &&
|
|
224
227
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
225
228
|
? { ...x, type: "object" }
|
|
@@ -233,6 +236,7 @@ function parseObject(objectSchema, refs) {
|
|
|
233
236
|
// propertyNames
|
|
234
237
|
if (objectSchema.propertyNames) {
|
|
235
238
|
const normalizedPropNames = typeof objectSchema.propertyNames === "object" &&
|
|
239
|
+
objectSchema.propertyNames !== null &&
|
|
236
240
|
!objectSchema.propertyNames.type &&
|
|
237
241
|
objectSchema.propertyNames.pattern
|
|
238
242
|
? { ...objectSchema.propertyNames, type: "string" }
|
|
@@ -261,12 +265,12 @@ function parseObject(objectSchema, refs) {
|
|
|
261
265
|
if (entries.length) {
|
|
262
266
|
output += `.superRefine((obj, ctx) => {
|
|
263
267
|
${entries
|
|
264
|
-
.map(([key, schema]
|
|
268
|
+
.map(([key, schema]) => {
|
|
265
269
|
const parsed = (0, parseSchema_js_1.parseSchema)(schema, { ...refs, path: [...refs.path, "dependentSchemas", key] });
|
|
266
270
|
return `if (Object.prototype.hasOwnProperty.call(obj, ${JSON.stringify(key)})) {
|
|
267
271
|
const result = ${parsed}.safeParse(obj);
|
|
268
272
|
if (!result.success) {
|
|
269
|
-
ctx.addIssue({ code: "custom", message: "Dependent schema failed", path: [], params: { issues: result.error.issues } });
|
|
273
|
+
ctx.addIssue({ code: "custom", message: ${objectSchema.errorMessage?.dependentSchemas ?? JSON.stringify("Dependent schema failed")}, path: [], params: { issues: result.error.issues } });
|
|
270
274
|
}
|
|
271
275
|
}`;
|
|
272
276
|
})
|
|
@@ -278,7 +282,8 @@ function parseObject(objectSchema, refs) {
|
|
|
278
282
|
if (objectSchema.dependentRequired && typeof objectSchema.dependentRequired === "object") {
|
|
279
283
|
const entries = Object.entries(objectSchema.dependentRequired);
|
|
280
284
|
if (entries.length) {
|
|
281
|
-
const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ??
|
|
285
|
+
const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ??
|
|
286
|
+
"Dependent required properties missing";
|
|
282
287
|
output += `.superRefine((obj, ctx) => {
|
|
283
288
|
${entries
|
|
284
289
|
.map(([prop, deps]) => {
|
|
@@ -33,9 +33,7 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
|
|
|
33
33
|
if (typeof schema !== "object")
|
|
34
34
|
return schema ? (0, anyOrUnknown_js_1.anyOrUnknown)(refs) : "z.never()";
|
|
35
35
|
const parentBase = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
|
|
36
|
-
const baseUri = typeof schema.$id === "string"
|
|
37
|
-
? (0, resolveUri_js_1.resolveUri)(parentBase, schema.$id)
|
|
38
|
-
: parentBase;
|
|
36
|
+
const baseUri = typeof schema.$id === "string" ? (0, resolveUri_js_1.resolveUri)(parentBase, schema.$id) : parentBase;
|
|
39
37
|
const dynamicAnchors = Array.isArray(refs.dynamicAnchors) ? [...refs.dynamicAnchors] : [];
|
|
40
38
|
if (typeof schema.$dynamicAnchor === "string") {
|
|
41
39
|
dynamicAnchors.push({
|
|
@@ -85,6 +83,9 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
|
|
|
85
83
|
exports.parseSchema = parseSchema;
|
|
86
84
|
const parseRef = (schema, refs) => {
|
|
87
85
|
const refValue = schema.$dynamicRef ?? schema.$ref;
|
|
86
|
+
if (typeof refValue !== "string") {
|
|
87
|
+
return (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
|
|
88
|
+
}
|
|
88
89
|
const resolved = resolveRef(schema, refValue, refs);
|
|
89
90
|
if (!resolved) {
|
|
90
91
|
refs.onUnresolvedRef?.(refValue, refs.path);
|
|
@@ -194,7 +195,10 @@ const resolveRef = (schemaNode, ref, refs) => {
|
|
|
194
195
|
const loaded = refs.resolveExternalRef(extBase);
|
|
195
196
|
if (loaded) {
|
|
196
197
|
// If async resolver is used synchronously here, it will be ignored; keep simple sync for now
|
|
197
|
-
const
|
|
198
|
+
const maybePromise = loaded;
|
|
199
|
+
const schema = typeof maybePromise.then === "function"
|
|
200
|
+
? undefined
|
|
201
|
+
: loaded;
|
|
198
202
|
if (schema) {
|
|
199
203
|
const { registry } = (0, buildRefRegistry_js_1.buildRefRegistry)(schema, extBase);
|
|
200
204
|
registry.forEach((entry, k) => refs.refRegistry?.set(k, entry));
|
|
@@ -342,10 +346,10 @@ const selectParser = (schema, refs) => {
|
|
|
342
346
|
return (0, parseNumber_js_1.parseNumber)(schema);
|
|
343
347
|
}
|
|
344
348
|
else if (exports.its.a.primitive(schema, "boolean")) {
|
|
345
|
-
return (0, parseBoolean_js_1.parseBoolean)(
|
|
349
|
+
return (0, parseBoolean_js_1.parseBoolean)();
|
|
346
350
|
}
|
|
347
351
|
else if (exports.its.a.primitive(schema, "null")) {
|
|
348
|
-
return (0, parseNull_js_1.parseNull)(
|
|
352
|
+
return (0, parseNull_js_1.parseNull)();
|
|
349
353
|
}
|
|
350
354
|
else if (exports.its.a.conditional(schema)) {
|
|
351
355
|
return (0, parseIfThenElse_js_1.parseIfThenElse)(schema, refs);
|
|
@@ -289,7 +289,7 @@ const parseString = (schema, refs) => {
|
|
|
289
289
|
if (contentMediaType != "") {
|
|
290
290
|
r += contentMediaType;
|
|
291
291
|
r += (0, withMessage_js_1.withMessage)(schema, "contentSchema", ({ value }) => {
|
|
292
|
-
if (value && value
|
|
292
|
+
if (value && typeof value === "object") {
|
|
293
293
|
return {
|
|
294
294
|
opener: `.pipe(${(0, parseSchema_js_1.parseSchema)(value, refContext)}`,
|
|
295
295
|
closer: ")",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildRefRegistry = void 0;
|
|
4
4
|
const resolveUri_js_1 = require("./resolveUri.js");
|
|
5
|
-
const buildRefRegistry = (schema, rootBaseUri = "root:///"
|
|
5
|
+
const buildRefRegistry = (schema, rootBaseUri = "root:///") => {
|
|
6
6
|
const registry = new Map();
|
|
7
7
|
const walk = (node, baseUri, path) => {
|
|
8
8
|
if (typeof node !== "object" || node === null)
|
package/dist/cjs/utils/omit.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.omit = void 0;
|
|
4
4
|
const omit = (obj, ...keys) => Object.keys(obj).reduce((acc, key) => {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const typedKey = key;
|
|
6
|
+
if (!keys.includes(typedKey)) {
|
|
7
|
+
acc[typedKey] = obj[typedKey];
|
|
7
8
|
}
|
|
8
9
|
return acc;
|
|
9
10
|
}, {});
|
package/dist/esm/core/emitZod.js
CHANGED
|
@@ -98,8 +98,8 @@ export const emitZod = (analysis) => {
|
|
|
98
98
|
})
|
|
99
99
|
.join("\n")
|
|
100
100
|
: "";
|
|
101
|
-
const jsdocs = rest.withJsdocs && typeof schema
|
|
102
|
-
? expandJsdocs(schema.description)
|
|
101
|
+
const jsdocs = rest.withJsdocs && typeof schema === "object" && schema !== null && "description" in schema
|
|
102
|
+
? expandJsdocs(String(schema.description ?? ""))
|
|
103
103
|
: "";
|
|
104
104
|
const lines = [];
|
|
105
105
|
if (module === "cjs" && !noImport) {
|
|
@@ -79,6 +79,7 @@ const buildBundleContext = (defNames, defs, options) => {
|
|
|
79
79
|
return { defInfoMap, rootName, rootTypeName };
|
|
80
80
|
};
|
|
81
81
|
const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options) => {
|
|
82
|
+
const useLazyCrossRefs = options.refResolution?.lazyCrossRefs ?? true;
|
|
82
83
|
return (schema, refs) => {
|
|
83
84
|
if (typeof schema["$ref"] === "string") {
|
|
84
85
|
const refPath = schema["$ref"];
|
|
@@ -105,7 +106,7 @@ const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options
|
|
|
105
106
|
});
|
|
106
107
|
if (resolved)
|
|
107
108
|
return resolved;
|
|
108
|
-
if (isCycle &&
|
|
109
|
+
if (isCycle && useLazyCrossRefs) {
|
|
109
110
|
return `z.lazy(() => ${refInfo.schemaName})`;
|
|
110
111
|
}
|
|
111
112
|
return refInfo.schemaName;
|
|
@@ -285,7 +286,7 @@ const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath =
|
|
|
285
286
|
}
|
|
286
287
|
// inline $defs
|
|
287
288
|
if (record.$defs && typeof record.$defs === "object") {
|
|
288
|
-
for (const [
|
|
289
|
+
for (const [, defSchema] of Object.entries(record.$defs)) {
|
|
289
290
|
nestedTypes.push(...findNestedTypesInSchema(defSchema, parentTypeName, defNames, currentPath));
|
|
290
291
|
}
|
|
291
292
|
}
|
|
@@ -334,14 +335,23 @@ const generateNestedTypesFile = (nestedTypes) => {
|
|
|
334
335
|
lines.push(`import type { ${typeName} } from './${file}.schema.js';`);
|
|
335
336
|
}
|
|
336
337
|
lines.push("");
|
|
338
|
+
const buildAccessExpr = (parentType, propertyPath) => {
|
|
339
|
+
let accessExpr = parentType;
|
|
340
|
+
for (const prop of propertyPath) {
|
|
341
|
+
const accessor = prop === "items"
|
|
342
|
+
? "[number]"
|
|
343
|
+
: typeof prop === "number"
|
|
344
|
+
? `[${prop}]`
|
|
345
|
+
: `[${JSON.stringify(prop)}]`;
|
|
346
|
+
accessExpr = `NonNullable<${accessExpr}${accessor}>`;
|
|
347
|
+
}
|
|
348
|
+
return accessExpr;
|
|
349
|
+
};
|
|
337
350
|
for (const [parentType, types] of [...byParent.entries()].sort()) {
|
|
338
351
|
lines.push(`// From ${parentType}`);
|
|
339
352
|
for (const info of types.sort((a, b) => a.typeName.localeCompare(b.typeName))) {
|
|
340
353
|
if (info.propertyPath.length > 0) {
|
|
341
|
-
|
|
342
|
-
for (const prop of info.propertyPath) {
|
|
343
|
-
accessExpr = `NonNullable<${accessExpr}['${prop}']>`;
|
|
344
|
-
}
|
|
354
|
+
const accessExpr = buildAccessExpr(parentType, info.propertyPath);
|
|
345
355
|
lines.push(`export type ${info.typeName} = ${accessExpr};`);
|
|
346
356
|
}
|
|
347
357
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { parseSchema } from "./parseSchema.js";
|
|
2
2
|
import { half } from "../utils/half.js";
|
|
3
|
-
const
|
|
3
|
+
const originalIndexKey = "__originalIndex";
|
|
4
4
|
const ensureOriginalIndex = (arr) => {
|
|
5
|
-
|
|
5
|
+
const newArr = [];
|
|
6
6
|
for (let i = 0; i < arr.length; i++) {
|
|
7
7
|
const item = arr[i];
|
|
8
8
|
if (typeof item === "boolean") {
|
|
9
|
-
newArr.push(item ? { [
|
|
9
|
+
newArr.push(item ? { [originalIndexKey]: i } : { [originalIndexKey]: i, not: {} });
|
|
10
10
|
}
|
|
11
|
-
else if (
|
|
11
|
+
else if (typeof item === "object" &&
|
|
12
|
+
item !== null &&
|
|
13
|
+
originalIndexKey in item) {
|
|
12
14
|
return arr;
|
|
13
15
|
}
|
|
14
16
|
else {
|
|
15
|
-
newArr.push({ ...item, [
|
|
17
|
+
newArr.push({ ...item, [originalIndexKey]: i });
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
return newArr;
|
|
@@ -25,7 +27,11 @@ export function parseAllOf(schema, refs) {
|
|
|
25
27
|
const item = schema.allOf[0];
|
|
26
28
|
return parseSchema(item, {
|
|
27
29
|
...refs,
|
|
28
|
-
path: [
|
|
30
|
+
path: [
|
|
31
|
+
...refs.path,
|
|
32
|
+
"allOf",
|
|
33
|
+
item[originalIndexKey] ?? 0,
|
|
34
|
+
],
|
|
29
35
|
});
|
|
30
36
|
}
|
|
31
37
|
else {
|
|
@@ -14,7 +14,7 @@ export const parseIfThenElse = (schema, refs) => {
|
|
|
14
14
|
? ${$then}.safeParse(value)
|
|
15
15
|
: ${$else}.safeParse(value);
|
|
16
16
|
if (!result.success) {
|
|
17
|
-
const issues = result.error.issues
|
|
17
|
+
const issues = result.error.issues;
|
|
18
18
|
issues.forEach((issue) => ctx.addIssue(issue))
|
|
19
19
|
}
|
|
20
20
|
})`;
|
|
@@ -197,6 +197,7 @@ export function parseObject(objectSchema, refs) {
|
|
|
197
197
|
output += `.and(${parseAnyOf({
|
|
198
198
|
...objectSchema,
|
|
199
199
|
anyOf: objectSchema.anyOf.map((x) => typeof x === "object" &&
|
|
200
|
+
x !== null &&
|
|
200
201
|
!x.type &&
|
|
201
202
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
202
203
|
? { ...x, type: "object" }
|
|
@@ -207,6 +208,7 @@ export function parseObject(objectSchema, refs) {
|
|
|
207
208
|
output += `.and(${parseOneOf({
|
|
208
209
|
...objectSchema,
|
|
209
210
|
oneOf: objectSchema.oneOf.map((x) => typeof x === "object" &&
|
|
211
|
+
x !== null &&
|
|
210
212
|
!x.type &&
|
|
211
213
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
212
214
|
? { ...x, type: "object" }
|
|
@@ -217,6 +219,7 @@ export function parseObject(objectSchema, refs) {
|
|
|
217
219
|
output += `.and(${parseAllOf({
|
|
218
220
|
...objectSchema,
|
|
219
221
|
allOf: objectSchema.allOf.map((x) => typeof x === "object" &&
|
|
222
|
+
x !== null &&
|
|
220
223
|
!x.type &&
|
|
221
224
|
(x.properties || x.additionalProperties || x.patternProperties)
|
|
222
225
|
? { ...x, type: "object" }
|
|
@@ -230,6 +233,7 @@ export function parseObject(objectSchema, refs) {
|
|
|
230
233
|
// propertyNames
|
|
231
234
|
if (objectSchema.propertyNames) {
|
|
232
235
|
const normalizedPropNames = typeof objectSchema.propertyNames === "object" &&
|
|
236
|
+
objectSchema.propertyNames !== null &&
|
|
233
237
|
!objectSchema.propertyNames.type &&
|
|
234
238
|
objectSchema.propertyNames.pattern
|
|
235
239
|
? { ...objectSchema.propertyNames, type: "string" }
|
|
@@ -258,12 +262,12 @@ export function parseObject(objectSchema, refs) {
|
|
|
258
262
|
if (entries.length) {
|
|
259
263
|
output += `.superRefine((obj, ctx) => {
|
|
260
264
|
${entries
|
|
261
|
-
.map(([key, schema]
|
|
265
|
+
.map(([key, schema]) => {
|
|
262
266
|
const parsed = parseSchema(schema, { ...refs, path: [...refs.path, "dependentSchemas", key] });
|
|
263
267
|
return `if (Object.prototype.hasOwnProperty.call(obj, ${JSON.stringify(key)})) {
|
|
264
268
|
const result = ${parsed}.safeParse(obj);
|
|
265
269
|
if (!result.success) {
|
|
266
|
-
ctx.addIssue({ code: "custom", message: "Dependent schema failed", path: [], params: { issues: result.error.issues } });
|
|
270
|
+
ctx.addIssue({ code: "custom", message: ${objectSchema.errorMessage?.dependentSchemas ?? JSON.stringify("Dependent schema failed")}, path: [], params: { issues: result.error.issues } });
|
|
267
271
|
}
|
|
268
272
|
}`;
|
|
269
273
|
})
|
|
@@ -275,7 +279,8 @@ export function parseObject(objectSchema, refs) {
|
|
|
275
279
|
if (objectSchema.dependentRequired && typeof objectSchema.dependentRequired === "object") {
|
|
276
280
|
const entries = Object.entries(objectSchema.dependentRequired);
|
|
277
281
|
if (entries.length) {
|
|
278
|
-
const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ??
|
|
282
|
+
const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ??
|
|
283
|
+
"Dependent required properties missing";
|
|
279
284
|
output += `.superRefine((obj, ctx) => {
|
|
280
285
|
${entries
|
|
281
286
|
.map(([prop, deps]) => {
|
|
@@ -30,9 +30,7 @@ export const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockM
|
|
|
30
30
|
if (typeof schema !== "object")
|
|
31
31
|
return schema ? anyOrUnknown(refs) : "z.never()";
|
|
32
32
|
const parentBase = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
|
|
33
|
-
const baseUri = typeof schema.$id === "string"
|
|
34
|
-
? resolveUri(parentBase, schema.$id)
|
|
35
|
-
: parentBase;
|
|
33
|
+
const baseUri = typeof schema.$id === "string" ? resolveUri(parentBase, schema.$id) : parentBase;
|
|
36
34
|
const dynamicAnchors = Array.isArray(refs.dynamicAnchors) ? [...refs.dynamicAnchors] : [];
|
|
37
35
|
if (typeof schema.$dynamicAnchor === "string") {
|
|
38
36
|
dynamicAnchors.push({
|
|
@@ -81,6 +79,9 @@ export const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockM
|
|
|
81
79
|
};
|
|
82
80
|
const parseRef = (schema, refs) => {
|
|
83
81
|
const refValue = schema.$dynamicRef ?? schema.$ref;
|
|
82
|
+
if (typeof refValue !== "string") {
|
|
83
|
+
return anyOrUnknown(refs);
|
|
84
|
+
}
|
|
84
85
|
const resolved = resolveRef(schema, refValue, refs);
|
|
85
86
|
if (!resolved) {
|
|
86
87
|
refs.onUnresolvedRef?.(refValue, refs.path);
|
|
@@ -190,7 +191,10 @@ const resolveRef = (schemaNode, ref, refs) => {
|
|
|
190
191
|
const loaded = refs.resolveExternalRef(extBase);
|
|
191
192
|
if (loaded) {
|
|
192
193
|
// If async resolver is used synchronously here, it will be ignored; keep simple sync for now
|
|
193
|
-
const
|
|
194
|
+
const maybePromise = loaded;
|
|
195
|
+
const schema = typeof maybePromise.then === "function"
|
|
196
|
+
? undefined
|
|
197
|
+
: loaded;
|
|
194
198
|
if (schema) {
|
|
195
199
|
const { registry } = buildRefRegistry(schema, extBase);
|
|
196
200
|
registry.forEach((entry, k) => refs.refRegistry?.set(k, entry));
|
|
@@ -338,10 +342,10 @@ const selectParser = (schema, refs) => {
|
|
|
338
342
|
return parseNumber(schema);
|
|
339
343
|
}
|
|
340
344
|
else if (its.a.primitive(schema, "boolean")) {
|
|
341
|
-
return parseBoolean(
|
|
345
|
+
return parseBoolean();
|
|
342
346
|
}
|
|
343
347
|
else if (its.a.primitive(schema, "null")) {
|
|
344
|
-
return parseNull(
|
|
348
|
+
return parseNull();
|
|
345
349
|
}
|
|
346
350
|
else if (its.a.conditional(schema)) {
|
|
347
351
|
return parseIfThenElse(schema, refs);
|
|
@@ -286,7 +286,7 @@ export const parseString = (schema, refs) => {
|
|
|
286
286
|
if (contentMediaType != "") {
|
|
287
287
|
r += contentMediaType;
|
|
288
288
|
r += withMessage(schema, "contentSchema", ({ value }) => {
|
|
289
|
-
if (value && value
|
|
289
|
+
if (value && typeof value === "object") {
|
|
290
290
|
return {
|
|
291
291
|
opener: `.pipe(${parseSchema(value, refContext)}`,
|
|
292
292
|
closer: ")",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveUri } from "./resolveUri.js";
|
|
2
|
-
export const buildRefRegistry = (schema, rootBaseUri = "root:///"
|
|
2
|
+
export const buildRefRegistry = (schema, rootBaseUri = "root:///") => {
|
|
3
3
|
const registry = new Map();
|
|
4
4
|
const walk = (node, baseUri, path) => {
|
|
5
5
|
if (typeof node !== "object" || node === null)
|
package/dist/esm/utils/omit.js
CHANGED
package/dist/types/Types.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export type JsonSchemaObject = {
|
|
|
6
6
|
type?: string | string[];
|
|
7
7
|
$id?: string;
|
|
8
8
|
$ref?: string;
|
|
9
|
+
$anchor?: string;
|
|
10
|
+
$dynamicRef?: string;
|
|
11
|
+
$dynamicAnchor?: string;
|
|
12
|
+
$recursiveRef?: string;
|
|
13
|
+
$recursiveAnchor?: boolean;
|
|
9
14
|
$defs?: Record<string, JsonSchema>;
|
|
10
15
|
definitions?: Record<string, JsonSchema>;
|
|
11
16
|
title?: string;
|
|
@@ -53,9 +58,7 @@ export type JsonSchemaObject = {
|
|
|
53
58
|
errorMessage?: {
|
|
54
59
|
[key: string]: string | undefined;
|
|
55
60
|
};
|
|
56
|
-
} &
|
|
57
|
-
[key: string]: any;
|
|
58
|
-
};
|
|
61
|
+
} & Record<string, unknown>;
|
|
59
62
|
export type ParserSelector = (schema: JsonSchemaObject, refs: Refs) => string;
|
|
60
63
|
export type ParserOverride = (schema: JsonSchemaObject, refs: Refs) => string | void;
|
|
61
64
|
export type Options = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JsonSchema
|
|
1
|
+
import { JsonSchema } from "../Types.js";
|
|
2
2
|
export type RefRegistryEntry = {
|
|
3
3
|
schema: JsonSchema;
|
|
4
4
|
path: (string | number)[];
|
|
@@ -6,7 +6,7 @@ export type RefRegistryEntry = {
|
|
|
6
6
|
dynamic?: boolean;
|
|
7
7
|
anchor?: string;
|
|
8
8
|
};
|
|
9
|
-
export declare const buildRefRegistry: (schema: JsonSchema, rootBaseUri?: string
|
|
9
|
+
export declare const buildRefRegistry: (schema: JsonSchema, rootBaseUri?: string) => {
|
|
10
10
|
registry: Map<string, RefRegistryEntry>;
|
|
11
11
|
rootBaseUri: string;
|
|
12
12
|
};
|
package/eslint.config.js
CHANGED