Skip to content

Commit 8d08bd6

Browse files
GeKormsamchungy
andauthored
Fix zod/mini compatibility (#520)
Co-authored-by: Sam Chung <[email protected]>
1 parent bcd0ec8 commit 8d08bd6

File tree

5 files changed

+157
-12
lines changed

5 files changed

+157
-12
lines changed

.changeset/easy-crews-call.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'zod-openapi': patch
3+
---
4+
5+
Fix `zod/mini` compatibility

src/create/schema/override.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { GlobalMeta } from 'zod/v4';
1+
import { globalRegistry } from 'zod/v4';
22
import type * as core from 'zod/v4/core';
33

44
import type { CreateDocumentOptions } from '../../index.js';
@@ -9,10 +9,6 @@ import type {
99

1010
import type { oas31 } from '@zod-openapi/openapi3-ts';
1111

12-
type ZodTypeWithMeta = core.$ZodTypes & {
13-
meta: () => GlobalMeta | undefined;
14-
};
15-
1612
export const override: ZodOpenApiOverride = (ctx) => {
1713
const def = ctx.zodSchema._zod.def;
1814
switch (def.type) {
@@ -68,7 +64,7 @@ export const override: ZodOpenApiOverride = (ctx) => {
6864
mapping;
6965
}
7066

71-
const meta = (ctx.zodSchema as ZodTypeWithMeta).meta();
67+
const meta = globalRegistry.get(ctx.zodSchema);
7268

7369
if (typeof meta?.unionOneOf === 'boolean') {
7470
if (meta.unionOneOf) {

src/create/schema/schema.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { type GlobalMeta, object, registry, toJSONSchema } from 'zod/v4';
1+
import {
2+
type GlobalMeta,
3+
globalRegistry,
4+
object,
5+
registry,
6+
toJSONSchema,
7+
} from 'zod/v4';
28
import type * as core from 'zod/v4/core';
39
import type { $ZodType } from 'zod/v4/core';
410

@@ -20,10 +26,6 @@ export interface SchemaResult {
2026
components: Record<string, oas31.SchemaObject>;
2127
}
2228

23-
type ZodTypeWithMeta = core.$ZodTypes & {
24-
meta: () => GlobalMeta | undefined;
25-
};
26-
2729
export const createSchema = (
2830
schema: core.$ZodType,
2931
ctx: {
@@ -128,7 +130,7 @@ export const createSchemas = <
128130
const outputIds = new Map<string, string>();
129131
const jsonSchema = toJSONSchema(zodRegistry, {
130132
override(context) {
131-
const meta = (context.zodSchema as ZodTypeWithMeta).meta();
133+
const meta = globalRegistry.get(context.zodSchema);
132134
if (meta?.outputId && meta?.id) {
133135
// If the schema has an outputId, we need to replace it later
134136
outputIds.set(meta.id, meta.outputId);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from 'vitest';
2+
import * as z from 'zod/mini';
3+
4+
import { type SchemaResult, createSchema } from '../schema.js';
5+
6+
describe('mini default', () => {
7+
it('creates a default string schema', () => {
8+
const schema = z._default(z.string(), 'a').register(z.globalRegistry, {});
9+
10+
const result = createSchema(schema);
11+
12+
expect(result).toEqual<SchemaResult>({
13+
schema: {
14+
type: 'string',
15+
default: 'a',
16+
},
17+
components: {},
18+
});
19+
});
20+
21+
it('adds a default property to a registered schema', () => {
22+
const schema = z._default(
23+
z.string().register(z.globalRegistry, { id: 'ref' }),
24+
'a',
25+
);
26+
27+
const result = createSchema(schema);
28+
29+
expect(result).toEqual<SchemaResult>({
30+
schema: {
31+
$ref: '#/components/schemas/ref',
32+
default: 'a',
33+
},
34+
components: {
35+
ref: {
36+
type: 'string',
37+
},
38+
},
39+
});
40+
});
41+
});
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { describe, expect, it } from 'vitest';
2+
import * as z from 'zod/mini';
3+
4+
import { createOutputContext } from '../../../testing/ctx.js';
5+
import { type SchemaResult, createSchema } from '../schema.js';
6+
7+
describe('mini union', () => {
8+
it('creates an anyOf schema for a union', () => {
9+
const schema = z.union([z.string(), z.number()]);
10+
11+
const result = createSchema(schema);
12+
13+
expect(result).toEqual<SchemaResult>({
14+
schema: {
15+
anyOf: [
16+
{
17+
type: 'string',
18+
},
19+
{
20+
type: 'number',
21+
},
22+
],
23+
},
24+
components: {},
25+
});
26+
});
27+
28+
it('creates an oneOf schema for a union', () => {
29+
const schema = z
30+
.union([z.string(), z.number()])
31+
.register(z.globalRegistry, {
32+
override: ({ jsonSchema }) => {
33+
jsonSchema.oneOf = jsonSchema.anyOf;
34+
delete jsonSchema.anyOf;
35+
},
36+
});
37+
38+
const result = createSchema(schema);
39+
40+
expect(result).toEqual<SchemaResult>({
41+
schema: {
42+
oneOf: [
43+
{
44+
type: 'string',
45+
},
46+
{
47+
type: 'number',
48+
},
49+
],
50+
},
51+
components: {},
52+
});
53+
});
54+
55+
it('creates an oneOf schema for a union if a document option overrides it', () => {
56+
const schema = z.union([z.string(), z.number()]);
57+
58+
const ctx = createOutputContext();
59+
ctx.opts = {
60+
override: ({ jsonSchema }) => {
61+
jsonSchema.oneOf = jsonSchema.anyOf;
62+
delete jsonSchema.anyOf;
63+
},
64+
};
65+
const result = createSchema(schema, ctx);
66+
67+
expect(result).toEqual<SchemaResult>({
68+
schema: {
69+
oneOf: [
70+
{
71+
type: 'string',
72+
},
73+
{
74+
type: 'number',
75+
},
76+
],
77+
},
78+
components: {},
79+
});
80+
});
81+
82+
it('produces not values in a union', () => {
83+
const schema = z.union([z.string(), z.never()]);
84+
85+
const result = createSchema(schema);
86+
87+
expect(result).toEqual<SchemaResult>({
88+
schema: {
89+
anyOf: [
90+
{
91+
type: 'string',
92+
},
93+
{
94+
not: {},
95+
},
96+
],
97+
},
98+
components: {},
99+
});
100+
});
101+
});

0 commit comments

Comments
 (0)