Skip to content

Commit c842698

Browse files
committed
Implement .clear() in . Fixes #4834
1 parent 9f122ae commit c842698

File tree

7 files changed

+63
-47
lines changed

7 files changed

+63
-47
lines changed

packages/docs/content/json-schema.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ const jsonSchema = z.toJSONSchema(mySchema, { io: "input" });
451451

452452
Passing a schema into `z.toJSONSchema()` will return a *self-contained* JSON Schema.
453453

454-
In other cases, you may have a set of Zod schemas you'd like to represent using multiple interlinked JSON Schemas, perhaps to write to `.json` files and serve from a web server. To achieve this, you can pass a [registry](/metadata#registries) into `z.toJSONSchema()`.
454+
In other cases, you may have a set of Zod schemas you'd like to represent using multiple interlinked JSON Schemas, perhaps to write to `.json` files and serve from a web server.
455455

456456
```ts
457457
import * as z from "zod/v4";
@@ -475,7 +475,9 @@ z.globalRegistry.add(User, {id: "User"});
475475
z.globalRegistry.add(Post, {id: "Post"});
476476
```
477477

478-
The schemas above both have an `id` property registered in `z.globalRegistry`. To convert these to JSON Schema, pass the entire registry into `z.toJSONSchema()`.
478+
To achieve this, you can pass a [registry](/metadata#registries) into `z.toJSONSchema()`.
479+
480+
> **Important** — All schemas should have a registered `id` property in the registry! Any schemas without an `id` will be ignored.
479481
480482
```ts
481483
z.toJSONSchema(z.globalRegistry);
@@ -506,9 +508,7 @@ z.toJSONSchema(z.globalRegistry);
506508
// }
507509
```
508510

509-
> Any schema with an `id` property in the registry will be extracted into `schemas`.
510-
511-
By default, the `$ref` URIs are relative paths like `"User"`. To make these absolute URIs, use the `uri` option. This expects a function that converts an `id` to a fully-qualified URI.
511+
By default, the `$ref` URIs are simple relative paths like `"User"`. To make these absolute URIs, use the `uri` option. This expects a function that converts an `id` to a fully-qualified URI.
512512

513513
```ts
514514
z.toJSONSchema(z.globalRegistry, {

packages/docs/content/metadata.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ myRegistry.add(mySchema, { description: "A cool schema!"});
2828
myRegistry.has(mySchema); // => true
2929
myRegistry.get(mySchema); // => { description: "A cool schema!" }
3030
myRegistry.remove(mySchema);
31+
myRegistry.clear(); // wipe registry
3132
```
3233

3334
TypeScript enforces that the metadata for each schema matches the registry's **metadata type**.
@@ -37,7 +38,7 @@ myRegistry.add(mySchema, { description: "A cool schema!" }); // ✅
3738
myRegistry.add(mySchema, { description: 123 }); //
3839
```
3940

40-
> **Important**If you register metadata on a schema and include an `id` property, Zod will throw an error if multiple schemas share the same `id` value. Make sure all registered `id` values are unique per registry. This also applies to the global registry.
41+
> **Special handling for `id`**Zod registries treat the `id` property specially. An `Error` will be thrown if multiple schemas are registered with the same `id` value. This is true for all registries, including the global registry.
4142
4243

4344
### `.register()`

packages/zod/src/v4/classic/tests/recursive-types.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,33 @@ test("recursion compatibility", () => {
324324
},
325325
});
326326
});
327+
328+
// biome-ignore lint: sadf
329+
export type RecursiveA = z.ZodUnion<
330+
[
331+
z.ZodObject<{
332+
a: z.ZodDefault<RecursiveA>;
333+
b: z.ZodPrefault<RecursiveA>;
334+
c: z.ZodNonOptional<RecursiveA>;
335+
d: z.ZodOptional<RecursiveA>;
336+
e: z.ZodNullable<RecursiveA>;
337+
g: z.ZodReadonly<RecursiveA>;
338+
h: z.ZodPipe<RecursiveA, z.ZodString>;
339+
i: z.ZodArray<RecursiveA>;
340+
j: z.ZodSet<RecursiveA>;
341+
k: z.ZodMap<RecursiveA, RecursiveA>;
342+
l: z.ZodRecord<z.ZodString, RecursiveA>;
343+
m: z.ZodUnion<[RecursiveA, RecursiveA]>;
344+
n: z.ZodIntersection<RecursiveA, RecursiveA>;
345+
o: z.ZodLazy<RecursiveA>;
346+
p: z.ZodPromise<RecursiveA>;
347+
q: z.ZodCatch<RecursiveA>;
348+
r: z.ZodSuccess<RecursiveA>;
349+
s: z.ZodTransform<RecursiveA, string>;
350+
t: z.ZodTuple<[RecursiveA, RecursiveA]>;
351+
u: z.ZodObject<{
352+
a: RecursiveA;
353+
}>;
354+
}>,
355+
]
356+
>;

packages/zod/src/v4/classic/tests/registries.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,12 @@ test("function meta with replacement", () => {
193193

194194
expect(myReg.get(mySchema)!.defaulter("hello", true)).toEqual(5);
195195
});
196+
197+
test("test .clear()", () => {
198+
const reg = z.registry();
199+
const a = z.string();
200+
reg.add(a);
201+
expect(reg.has(a)).toEqual(true);
202+
reg.clear();
203+
expect(reg.has(a)).toEqual(false);
204+
});

packages/zod/src/v4/core/registries.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ export class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema exten
4545
return this as any;
4646
}
4747

48+
clear(): this {
49+
this._map = new WeakMap();
50+
this._idmap = new Map();
51+
return this;
52+
}
53+
4854
remove(schema: Schema): this {
4955
this._map.delete(schema);
5056
return this;

packages/zod/src/v4/core/to-json-schema.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,8 @@ export class JSONSchemaGenerator {
614614
// initialize result with root schema fields
615615
// Object.assign(result, seen.cached);
616616

617+
// returns a ref to the schema
618+
// defId will be empty if the ref points to an external schema (or #)
617619
const makeURI = (entry: [schemas.$ZodType<unknown, unknown>, Seen]): { ref: string; defId?: string } => {
618620
// comparing the seen objects because sometimes
619621
// multiple schemas map to the same seen object.
@@ -625,11 +627,13 @@ export class JSONSchemaGenerator {
625627
const externalId = params.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${this.counter++}`;
626628

627629
// check if schema is in the external registry
628-
if (externalId) return { ref: params.external.uri(externalId) };
630+
if (externalId) {
631+
return { ref: params.external.uri(externalId) };
632+
}
629633

630634
// otherwise, add to __shared
631635
const id: string = entry[1].defId ?? (entry[1].schema.id as string) ?? `schema${this.counter++}`;
632-
entry[1].defId = id;
636+
entry[1].defId = id; // set defId so it will be reused if needed
633637
return { defId: id, ref: `${params.external.uri("__shared")}#/${defsSegment}/${id}` };
634638
}
635639

play.ts

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,9 @@ import { z } from "zod/v4";
22

33
z;
44

5-
// import * as z from "zod/v4";
5+
const a = z.string().meta({ id: "stuff" });
6+
const b = z.string().meta({ id: "stuff" });
7+
const obj = z.object({ a, b });
68

7-
export type SomeRecursiveUnion1 = z.ZodUnion<
8-
[
9-
z.ZodObject<{
10-
foo: z.ZodReadonly<z.ZodPrefault<SomeRecursiveUnion1>>;
11-
}>,
12-
]
13-
>;
14-
15-
// type Foo = z.core.$ZodRecord<z.ZodString, B>;
16-
// type B = z.ZodPipe<B, Foo>;
17-
18-
const Category = z.object({
19-
name: z.string(),
20-
get subcategories() {
21-
return z.array(z.union([z.string(), Category]));
22-
},
23-
});
24-
25-
const err = new z.core.$ZodError([
26-
{
27-
code: "invalid_type",
28-
expected: "string",
29-
path: [],
30-
message: "Expected string, received number",
31-
input: 1,
32-
},
33-
]);
34-
console.log(err.toString());
35-
// But it works without `z.ZodReadonly` or without recursive reference:
36-
37-
// export type SomeRecursiveUnion2 = z.ZodUnion<
38-
// [
39-
// z.ZodObject<{
40-
// foo: z.ZodRecord<z.ZodString, SomeRecursiveUnion2>;
41-
// bar: z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodString>>;
42-
// }>,
43-
// ]
44-
// >;
9+
const json = z.toJSONSchema(obj);
10+
console.log(json);

0 commit comments

Comments
 (0)