New:Socket for Asana Is Now Available.Learn more
Get Started

zod

Package Overview
Dependencies
Maintainers
1
Versions
1005
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zod - npm Package Compare versions

Comparing version
4.5.3
to
4.5.4
+1
-1
package.json
{
"name": "zod",
"version": "4.5.3",
"version": "4.5.4",
"type": "module",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -679,1 +679,43 @@ import { expect, test } from "vitest";

});
test("detects a cycle reachable only through a merged catchall", () => {
const Cyclic: any = z.object({
id: z.string(),
get next() {
return z.optional(Root);
},
});
const Root: any = z.object({ tag: z.string() }).merge(z.object({}).catchall(Cyclic));
const input: any = { tag: "root" };
input.child = { id: "1", next: input };
const out: any = Root.parse(input);
expect(out.child.next).toBe(out);
});
// the per-type switch can only enumerate kinds Zod ships, so an unknown `def.type` falls back to scanning the def; without it a cycle through a third-party container overflows the stack
test("detects a cycle through a user-defined container type", () => {
const MyBox: any = z.core.$constructor("MyBox", (inst: any, def: any) => {
z.core.$ZodType.init(inst, def);
inst._zod.parse = (payload: any, ctx: any) => {
if (payload.value === null || typeof payload.value !== "object") return payload;
const inner = def.inner._zod.run({ value: payload.value.v, issues: [] }, ctx);
payload.value = { v: inner.value };
return payload;
};
});
const Node: any = z.object({
id: z.string(),
get boxed() {
return z.optional(new MyBox({ type: "mybox", inner: z.lazy(() => Node) }));
},
});
const input: any = { id: "1" };
input.boxed = { v: input };
const out: any = Node.parse(input);
expect(out.boxed.v).toBe(out);
});

@@ -410,1 +410,47 @@ import { expect, expectTypeOf, test } from "vitest";

});
test("default factory runs once per parse inside a container", () => {
let calls = 0;
const schema = z.object({
a: z.string().default(() => {
calls++;
return "d";
}),
});
expect(schema.parse({})).toEqual({ a: "d" });
expect(calls).toBe(1);
// the key is present, so the factory has nothing to produce
expect(schema.parse({ a: "given" })).toEqual({ a: "given" });
expect(calls).toBe(1);
});
test("prefault factory runs once per parse inside a container", () => {
let calls = 0;
const schema = z.object({
a: z.string().prefault(() => {
calls++;
return "d";
}),
});
expect(schema.parse({})).toEqual({ a: "d" });
expect(calls).toBe(1);
});
test("default factory does not run at compile time", () => {
let calls = 0;
const compiled = z.compile(
z.object({
a: z.string().default(() => {
calls++;
return "d";
}),
})
);
expect(calls).toBe(0);
expect(compiled.parse({})).toEqual({ a: "d" });
expect(calls).toBe(1);
});
import type * as errors from "./errors.js";
import type { $ZodMemoizer, $ZodType, ParseContextInternal, ParsePayload } from "./schemas.js";
import type { $ZodMemoizer, $ZodType, $ZodTypeDef, ParseContextInternal, ParsePayload } from "./schemas.js";
import type * as util from "./util.js";

@@ -51,15 +51,91 @@

const def = inst._zod.def as any;
if (def.type === "lazy") {
check((inst as any)._zod.innerType);
} else {
// $ZodObject redefines `shape` as a non-enumerable accessor, so `for...in` misses it.
const shape = def.shape;
// `for...in` skips symbols, so a cycle through a declared symbol key would read as non-recursive
if (shape) for (const key of Reflect.ownKeys(shape)) check(shape[key]);
for (const key in def) {
const value = def[key];
if (!value || typeof value !== "object") continue;
if (value._zod) check(value);
else if (Array.isArray(value)) for (const el of value) check(el);
const kind = def.type as $ZodTypeDef["type"];
switch (kind) {
case "object": {
// `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
for (const key of Reflect.ownKeys(def.shape)) check(def.shape[key]);
check(def.catchall);
break;
}
case "array":
check(def.element);
break;
case "tuple":
for (const el of def.items) check(el);
check(def.rest);
break;
case "record":
case "map":
check(def.keyType);
check(def.valueType);
break;
case "set":
check(def.valueType);
break;
case "union":
for (const el of def.options) check(el);
break;
case "intersection":
check(def.left);
check(def.right);
break;
case "optional":
case "nullable":
case "default":
case "prefault":
case "catch":
case "readonly":
case "nonoptional":
case "promise":
case "success":
check(def.innerType);
break;
case "pipe":
check(def.in);
check(def.out);
break;
case "function":
check(def.input);
check(def.output);
break;
// reading `_zod.innerType` resolves the getter once and caches it
case "lazy":
check((inst as any)._zod.innerType);
break;
// a leaf by choice: `parts` are regex fragments, not data positions
case "template_literal":
// leaves
case "string":
case "number":
case "int":
case "boolean":
case "bigint":
case "symbol":
case "undefined":
case "null":
case "void":
case "never":
case "any":
case "unknown":
case "date":
case "nan":
case "enum":
case "literal":
case "file":
case "transform":
case "custom":
break;
default: {
// a new built-in kind becomes a compile error here
kind satisfies never;
// a user-defined kind can still hold children, and only its author knows where, so fall back to scanning the def — skipping accessors, since reading one can run user code
for (const key in def) {
const desc = Object.getOwnPropertyDescriptor(def, key);
if (!desc || desc.get) continue;
const value = desc.value;
if (!value || typeof value !== "object") continue;
if (value._zod) check(value);
else if (Array.isArray(value)) for (const el of value) check(el);
}
}
}

@@ -66,0 +142,0 @@

export const version = {
major: 4,
minor: 5,
patch: 3 as number,
patch: 4 as number,
} as const;

@@ -37,22 +37,99 @@ "use strict";

const def = inst._zod.def;
if (def.type === "lazy") {
check(inst._zod.innerType);
}
else {
// $ZodObject redefines `shape` as a non-enumerable accessor, so `for...in` misses it.
const shape = def.shape;
// `for...in` skips symbols, so a cycle through a declared symbol key would read as non-recursive
if (shape)
for (const key of Reflect.ownKeys(shape))
check(shape[key]);
for (const key in def) {
const value = def[key];
if (!value || typeof value !== "object")
continue;
if (value._zod)
check(value);
else if (Array.isArray(value))
for (const el of value)
check(el);
const kind = def.type;
switch (kind) {
case "object": {
// `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
for (const key of Reflect.ownKeys(def.shape))
check(def.shape[key]);
check(def.catchall);
break;
}
case "array":
check(def.element);
break;
case "tuple":
for (const el of def.items)
check(el);
check(def.rest);
break;
case "record":
case "map":
check(def.keyType);
check(def.valueType);
break;
case "set":
check(def.valueType);
break;
case "union":
for (const el of def.options)
check(el);
break;
case "intersection":
check(def.left);
check(def.right);
break;
case "optional":
case "nullable":
case "default":
case "prefault":
case "catch":
case "readonly":
case "nonoptional":
case "promise":
case "success":
check(def.innerType);
break;
case "pipe":
check(def.in);
check(def.out);
break;
case "function":
check(def.input);
check(def.output);
break;
// reading `_zod.innerType` resolves the getter once and caches it
case "lazy":
check(inst._zod.innerType);
break;
// a leaf by choice: `parts` are regex fragments, not data positions
case "template_literal":
// leaves
case "string":
case "number":
case "int":
case "boolean":
case "bigint":
case "symbol":
case "undefined":
case "null":
case "void":
case "never":
case "any":
case "unknown":
case "date":
case "nan":
case "enum":
case "literal":
case "file":
case "transform":
case "custom":
break;
default: {
// a new built-in kind becomes a compile error here
kind;
// a user-defined kind can still hold children, and only its author knows where, so fall back to scanning the def — skipping accessors, since reading one can run user code
for (const key in def) {
const desc = Object.getOwnPropertyDescriptor(def, key);
if (!desc || desc.get)
continue;
const value = desc.value;
if (!value || typeof value !== "object")
continue;
if (value._zod)
check(value);
else if (Array.isArray(value))
for (const el of value)
check(el);
}
}
}

@@ -59,0 +136,0 @@ stack.delete(inst);

@@ -30,22 +30,99 @@ export class $ZodCyclicError extends Error {

const def = inst._zod.def;
if (def.type === "lazy") {
check(inst._zod.innerType);
}
else {
// $ZodObject redefines `shape` as a non-enumerable accessor, so `for...in` misses it.
const shape = def.shape;
// `for...in` skips symbols, so a cycle through a declared symbol key would read as non-recursive
if (shape)
for (const key of Reflect.ownKeys(shape))
check(shape[key]);
for (const key in def) {
const value = def[key];
if (!value || typeof value !== "object")
continue;
if (value._zod)
check(value);
else if (Array.isArray(value))
for (const el of value)
check(el);
const kind = def.type;
switch (kind) {
case "object": {
// `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
for (const key of Reflect.ownKeys(def.shape))
check(def.shape[key]);
check(def.catchall);
break;
}
case "array":
check(def.element);
break;
case "tuple":
for (const el of def.items)
check(el);
check(def.rest);
break;
case "record":
case "map":
check(def.keyType);
check(def.valueType);
break;
case "set":
check(def.valueType);
break;
case "union":
for (const el of def.options)
check(el);
break;
case "intersection":
check(def.left);
check(def.right);
break;
case "optional":
case "nullable":
case "default":
case "prefault":
case "catch":
case "readonly":
case "nonoptional":
case "promise":
case "success":
check(def.innerType);
break;
case "pipe":
check(def.in);
check(def.out);
break;
case "function":
check(def.input);
check(def.output);
break;
// reading `_zod.innerType` resolves the getter once and caches it
case "lazy":
check(inst._zod.innerType);
break;
// a leaf by choice: `parts` are regex fragments, not data positions
case "template_literal":
// leaves
case "string":
case "number":
case "int":
case "boolean":
case "bigint":
case "symbol":
case "undefined":
case "null":
case "void":
case "never":
case "any":
case "unknown":
case "date":
case "nan":
case "enum":
case "literal":
case "file":
case "transform":
case "custom":
break;
default: {
// a new built-in kind becomes a compile error here
kind;
// a user-defined kind can still hold children, and only its author knows where, so fall back to scanning the def — skipping accessors, since reading one can run user code
for (const key in def) {
const desc = Object.getOwnPropertyDescriptor(def, key);
if (!desc || desc.get)
continue;
const value = desc.value;
if (!value || typeof value !== "object")
continue;
if (value._zod)
check(value);
else if (Array.isArray(value))
for (const el of value)
check(el);
}
}
}

@@ -52,0 +129,0 @@ stack.delete(inst);

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

minor: 5,
patch: 3,
patch: 4,
};
export const version = {
major: 4,
minor: 5,
patch: 3,
patch: 4,
};