You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

zod-to-json-schema

Package Overview
Dependencies
Maintainers
1
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zod-to-json-schema - npm Package Compare versions

Comparing version

to
3.24.4

1

changelog.md

@@ -5,2 +5,3 @@ # Changelog

| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 3.24.4 | Added options to set the value of additionalProperties in objects and record |
| 3.24.3 | Adds postProcess callback option |

@@ -7,0 +8,0 @@ | 3.24.2 | Restructured internals to remove circular dependencies which apparently might cause some build systems to whine a bit. Big thanks to [Víctor Hernández](https://github.com/NanezX) for the fix. |

@@ -27,2 +27,4 @@ "use strict";

removeAdditionalStrategy: "passthrough",
allowedAdditionalProperties: true,
rejectedAdditionalProperties: false,
definitionPath: "definitions",

@@ -29,0 +31,0 @@ target: "jsonSchema7",

110

dist/cjs/parsers/object.js

@@ -6,20 +6,2 @@ "use strict";

const parseDef_js_1 = require("../parseDef.js");
function decideAdditionalProperties(def, refs) {
if (refs.removeAdditionalStrategy === "strict") {
return def.catchall._def.typeName === "ZodNever"
? def.unknownKeys !== "strict"
: (0, parseDef_js_1.parseDef)(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? true;
}
else {
return def.catchall._def.typeName === "ZodNever"
? def.unknownKeys === "passthrough"
: (0, parseDef_js_1.parseDef)(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? true;
}
}
function parseObjectDef(def, refs) {

@@ -29,33 +11,69 @@ const forceOptionalIntoNullable = refs.target === "openAi";

type: "object",
...Object.entries(def.shape()).reduce((acc, [propName, propDef]) => {
if (propDef === undefined || propDef._def === undefined)
return acc;
let propOptional = propDef.isOptional();
if (propOptional && forceOptionalIntoNullable) {
if (propDef instanceof zod_1.ZodOptional) {
propDef = propDef._def.innerType;
}
if (!propDef.isNullable()) {
propDef = propDef.nullable();
}
propOptional = false;
properties: {},
};
const required = [];
const shape = def.shape();
for (const propName in shape) {
let propDef = shape[propName];
if (propDef === undefined || propDef._def === undefined) {
continue;
}
let propOptional = safeIsOptional(propDef);
if (propOptional && forceOptionalIntoNullable) {
if (propDef instanceof zod_1.ZodOptional) {
propDef = propDef._def.innerType;
}
const parsedDef = (0, parseDef_js_1.parseDef)(propDef._def, {
...refs,
currentPath: [...refs.currentPath, "properties", propName],
propertyPath: [...refs.currentPath, "properties", propName],
});
if (parsedDef === undefined)
return acc;
return {
properties: { ...acc.properties, [propName]: parsedDef },
required: propOptional ? acc.required : [...acc.required, propName],
};
}, { properties: {}, required: [] }),
additionalProperties: decideAdditionalProperties(def, refs),
};
if (!result.required.length)
delete result.required;
if (!propDef.isNullable()) {
propDef = propDef.nullable();
}
propOptional = false;
}
const parsedDef = (0, parseDef_js_1.parseDef)(propDef._def, {
...refs,
currentPath: [...refs.currentPath, "properties", propName],
propertyPath: [...refs.currentPath, "properties", propName],
});
if (parsedDef === undefined) {
continue;
}
result.properties[propName] = parsedDef;
if (!propOptional) {
required.push(propName);
}
}
if (required.length) {
result.required = required;
}
const additionalProperties = decideAdditionalProperties(def, refs);
if (additionalProperties !== undefined) {
result.additionalProperties = additionalProperties;
}
return result;
}
exports.parseObjectDef = parseObjectDef;
function decideAdditionalProperties(def, refs) {
if (def.catchall._def.typeName !== "ZodNever") {
return (0, parseDef_js_1.parseDef)(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
});
}
switch (def.unknownKeys) {
case "passthrough":
return refs.allowedAdditionalProperties;
case "strict":
return refs.rejectedAdditionalProperties;
case "strip":
return refs.removeAdditionalStrategy === "strict"
? refs.allowedAdditionalProperties
: refs.rejectedAdditionalProperties;
}
}
function safeIsOptional(schema) {
try {
return schema.isOptional();
}
catch {
return true;
}
}

@@ -24,3 +24,3 @@ "use strict";

}), {}),
additionalProperties: false,
additionalProperties: refs.rejectedAdditionalProperties,
};

@@ -33,3 +33,3 @@ }

currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? {},
}) ?? refs.allowedAdditionalProperties,
};

@@ -36,0 +36,0 @@ if (refs.target === "openApi3") {

@@ -23,2 +23,4 @@ export const ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use");

removeAdditionalStrategy: "passthrough",
allowedAdditionalProperties: true,
rejectedAdditionalProperties: false,
definitionPath: "definitions",

@@ -25,0 +27,0 @@ target: "jsonSchema7",

import { ZodOptional } from "zod";
import { parseDef } from "../parseDef.js";
function decideAdditionalProperties(def, refs) {
if (refs.removeAdditionalStrategy === "strict") {
return def.catchall._def.typeName === "ZodNever"
? def.unknownKeys !== "strict"
: parseDef(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? true;
}
else {
return def.catchall._def.typeName === "ZodNever"
? def.unknownKeys === "passthrough"
: parseDef(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? true;
}
}
export function parseObjectDef(def, refs) {

@@ -25,32 +7,68 @@ const forceOptionalIntoNullable = refs.target === "openAi";

type: "object",
...Object.entries(def.shape()).reduce((acc, [propName, propDef]) => {
if (propDef === undefined || propDef._def === undefined)
return acc;
let propOptional = propDef.isOptional();
if (propOptional && forceOptionalIntoNullable) {
if (propDef instanceof ZodOptional) {
propDef = propDef._def.innerType;
}
if (!propDef.isNullable()) {
propDef = propDef.nullable();
}
propOptional = false;
properties: {},
};
const required = [];
const shape = def.shape();
for (const propName in shape) {
let propDef = shape[propName];
if (propDef === undefined || propDef._def === undefined) {
continue;
}
let propOptional = safeIsOptional(propDef);
if (propOptional && forceOptionalIntoNullable) {
if (propDef instanceof ZodOptional) {
propDef = propDef._def.innerType;
}
const parsedDef = parseDef(propDef._def, {
...refs,
currentPath: [...refs.currentPath, "properties", propName],
propertyPath: [...refs.currentPath, "properties", propName],
});
if (parsedDef === undefined)
return acc;
return {
properties: { ...acc.properties, [propName]: parsedDef },
required: propOptional ? acc.required : [...acc.required, propName],
};
}, { properties: {}, required: [] }),
additionalProperties: decideAdditionalProperties(def, refs),
};
if (!result.required.length)
delete result.required;
if (!propDef.isNullable()) {
propDef = propDef.nullable();
}
propOptional = false;
}
const parsedDef = parseDef(propDef._def, {
...refs,
currentPath: [...refs.currentPath, "properties", propName],
propertyPath: [...refs.currentPath, "properties", propName],
});
if (parsedDef === undefined) {
continue;
}
result.properties[propName] = parsedDef;
if (!propOptional) {
required.push(propName);
}
}
if (required.length) {
result.required = required;
}
const additionalProperties = decideAdditionalProperties(def, refs);
if (additionalProperties !== undefined) {
result.additionalProperties = additionalProperties;
}
return result;
}
function decideAdditionalProperties(def, refs) {
if (def.catchall._def.typeName !== "ZodNever") {
return parseDef(def.catchall._def, {
...refs,
currentPath: [...refs.currentPath, "additionalProperties"],
});
}
switch (def.unknownKeys) {
case "passthrough":
return refs.allowedAdditionalProperties;
case "strict":
return refs.rejectedAdditionalProperties;
case "strip":
return refs.removeAdditionalStrategy === "strict"
? refs.allowedAdditionalProperties
: refs.rejectedAdditionalProperties;
}
}
function safeIsOptional(schema) {
try {
return schema.isOptional();
}
catch {
return true;
}
}

@@ -21,3 +21,3 @@ import { ZodFirstPartyTypeKind, } from "zod";

}), {}),
additionalProperties: false,
additionalProperties: refs.rejectedAdditionalProperties,
};

@@ -30,3 +30,3 @@ }

currentPath: [...refs.currentPath, "additionalProperties"],
}) ?? {},
}) ?? refs.allowedAdditionalProperties,
};

@@ -33,0 +33,0 @@ if (refs.target === "openApi3") {

@@ -19,2 +19,4 @@ import { ZodSchema, ZodTypeDef } from "zod";

removeAdditionalStrategy: "passthrough" | "strict";
allowedAdditionalProperties: true | undefined;
rejectedAdditionalProperties: false | undefined;
target: Target;

@@ -21,0 +23,0 @@ strictUnions: boolean;

@@ -7,5 +7,5 @@ import { ZodObjectDef } from "zod";

properties: Record<string, JsonSchema7Type>;
additionalProperties: boolean | JsonSchema7Type;
additionalProperties?: boolean | JsonSchema7Type;
required?: string[];
};
export declare function parseObjectDef(def: ZodObjectDef, refs: Refs): JsonSchema7ObjectType;

@@ -9,3 +9,3 @@ import { ZodMapDef, ZodRecordDef, ZodTypeAny } from "zod";

type: "object";
additionalProperties: JsonSchema7Type;
additionalProperties?: JsonSchema7Type | true;
propertyNames?: JsonSchema7RecordPropertyNamesType;

@@ -12,0 +12,0 @@ };

{
"name": "zod-to-json-schema",
"version": "3.24.3",
"version": "3.24.4",
"description": "Converts Zod schemas to Json Schemas",

@@ -5,0 +5,0 @@ "types": "./dist/types/index.d.ts",

@@ -20,3 +20,3 @@ # Zod to Json Schema

A great big thank you to our amazing sponsors! Please consider joining them through my [GitHub Sponsors page](https://github.com/sponsors/StefanTerdell). Every cent helps, but these fellas have really gone above and beyond 💚:
<table align="center" style="justify-content: center;align-items: center;display: flex;">

@@ -63,4 +63,2 @@ <tr>

## Usage

@@ -140,3 +138,5 @@

| **pipeStrategy**?: "all" \| "input" \| "output" | Decide which types should be included when using `z.pipe`, for example `z.string().pipe(z.number())` would return both `string` and `number` by default, only `string` for "input" and only `number` for "output". |
| **removeAdditionalStrategy**?: "passthrough" \| "strict" | Decide when `additionalProperties ` should be false - whether according to strict or to passthrough. Since most parsers would retain properties given that `additionalProperties = false` while zod strips them, the default is to strip them unless `passthrough` is explicitly in the schema. On the other hand, it is useful to retain all fields unless `strict` is explicit in the schema which is the second option for the removeAdditional |
| **removeAdditionalStrategy**?: "passthrough" \| "strict" | Decide when `additionalProperties` should be allowed. See the section on additional properties for details. |
| **allowedAdditionalProperties**?: `true` \| `undefined` | What value to give `additionalProperties` when allowed. See the section on additional properties for details. |
| **rejectedAdditionalProperties**?: `false` \| `undefined` | What value to give `additionalProperties` when rejected. See the section on additional properties for details. |
| **override**?: callback | See section |

@@ -223,4 +223,31 @@ | **postProcess**?: callback | See section |

## `override` option
### Additional properties
By default, Zod removes undeclared properties when parsing object schemas. In order to replicate the expected output of this behaviour, the default for behaviour of zodToJsonSchema is to set `"additionalProperties"` to `false` (although the correctness of this can be debated). If you wish to allow undeclared properties you can either:
- Set `removeAdditionalStrategy` to `"strict"`. This will allow additional properties for any object schema that is not declared with `.strict()`.
- Leave `removeAdditionalStrategy` set to its default value of `"passthrough"`, and add `.passtrough()` to your object schema.
#### Removing the `additionalProperties` keyword using the `allowedAdditionalProperties` and/or `rejectedAdditionalProperties` options.
Some schema definitions (like Googles Gen AI API for instance) does not allow the `additionalProperties` keyword at all. Luckily the JSON Schema spec allows for this: leaving the keyword undefined _should_ have the same effect as setting it to true (as per usual YMMV). To enable this behaviour, set the option `allowedAdditionalProperties` to `undefined`.
To exclude the keyword even when additional properties are _not_ allowed, set the `rejectedAdditionalProperties` to `undefined` as well.
_Heads up ⚠️: Both of these options will be ignored if your schema is declared with `.catchall(...)` as the provided schema will be used instead (if valid)._
#### Expected outputs
| `z.object({})` + option | `"additionalProperties"` value |
| ------------------------- | ----------------------------------------------------------- |
| `.strip()` (default) | `false` if strategy is `"passtrough"`, `true` if `"strict"` |
| `.passtrough()` | `true` |
| `.strict()` | `false` |
| `.catchall(z.string())` | `{ "type": "string" }` |
| `.catchall(z.function())` | `undefined` (function schemas are not currently parseable) |
Substitute `true` and `false` for `undefined` according to `allowedAdditionalProperties` and/or `rejectedAdditionalProperties` respectively.
### `override`
This options takes a callback receiving a Zod schema definition, the current reference object (containing the current ref path and other options), an argument containing inforation about wether or not the schema has been encountered before, and a forceResolution argument.

@@ -278,3 +305,3 @@

## `postProcess` option
### `postProcess`

@@ -323,3 +350,3 @@ Besided receiving all arguments of the `override` callback, the `postProcess` callback also receives the generated schema. It should always return a JSON Schema, or `undefined` if you wish to filter it out. Unlike the `override` callback you do not have to return `ignoreOverride` if you are happy with the produced schema; simply return it unchanged.

### Using `postProcess` for including examples and other meta
#### Using `postProcess` for including examples and other meta

@@ -326,0 +353,0 @@ Adding support for examples and other JSON Schema meta keys are among the most commonly requested features for this project. Unfortunately the current Zod major (3) has pretty anemic support for this, so some userland hacking is required. Since this is such a common usecase I've included a helper function that simply tries to parse any description as JSON and expand it into the resulting schema.