diff --git a/package.json b/package.json index 5f6bca4..f7bd74f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zod-openapi", - "version": "4.2.0", + "version": "4.2.1", "description": "Convert Zod Schemas to OpenAPI v3.x documentation", "keywords": [ "typescript", @@ -97,6 +97,6 @@ "entryPoint": "src/index.ts", "template": "oss-npm-package", "type": "package", - "version": "9.0.0-main-20240928013837" + "version": "9.1.0" } } diff --git a/src/create/schema/index.ts b/src/create/schema/index.ts index 7a00d3a..5f69f07 100644 --- a/src/create/schema/index.ts +++ b/src/create/schema/index.ts @@ -1,5 +1,6 @@ import type { ZodType, ZodTypeDef } from 'zod'; +import { currentSymbol, previousSymbol } from '../../extendZodTypes'; import type { oas30, oas31 } from '../../openapi3-ts/dist'; import { type ComponentsObject, @@ -220,18 +221,22 @@ export const createSchemaOrRef = < return existingRef; } - const previous = zodSchema._def.zodOpenApi?.previous - ? (createSchemaOrRef(zodSchema._def.zodOpenApi.previous, state, true) as - | RefObject - | undefined) + const previous = zodSchema._def.zodOpenApi?.[previousSymbol] + ? (createSchemaOrRef( + zodSchema._def.zodOpenApi[previousSymbol], + state, + true, + ) as RefObject | undefined) : undefined; const current = - zodSchema._def.zodOpenApi?.current && - zodSchema._def.zodOpenApi.current !== zodSchema - ? (createSchemaOrRef(zodSchema._def.zodOpenApi.current, state, true) as - | RefObject - | undefined) + zodSchema._def.zodOpenApi?.[currentSymbol] && + zodSchema._def.zodOpenApi[currentSymbol] !== zodSchema + ? (createSchemaOrRef( + zodSchema._def.zodOpenApi[currentSymbol], + state, + true, + ) as RefObject | undefined) : undefined; const ref = zodSchema._def.zodOpenApi?.openapi?.ref ?? component?.ref; diff --git a/src/create/schema/metadata.test.ts b/src/create/schema/metadata.test.ts index c415adf..c61860c 100644 --- a/src/create/schema/metadata.test.ts +++ b/src/create/schema/metadata.test.ts @@ -253,10 +253,10 @@ describe('enhanceWithMetadata', () => { "coerce": false, "typeName": "ZodString", "zodOpenApi": { - "current": [Circular], "openapi": { "ref": "foo", }, + Symbol(current): [Circular], }, }, "and": [Function], @@ -339,10 +339,10 @@ describe('enhanceWithMetadata', () => { "coerce": false, "typeName": "ZodString", "zodOpenApi": { - "current": [Circular], "openapi": { "ref": "foo", }, + Symbol(current): [Circular], }, }, "and": [Function], @@ -604,10 +604,10 @@ describe('enhanceWithMetadata', () => { "coerce": false, "typeName": "ZodString", "zodOpenApi": { - "current": [Circular], "openapi": { "ref": "foo", }, + Symbol(current): [Circular], }, }, "and": [Function], @@ -690,10 +690,10 @@ describe('enhanceWithMetadata', () => { "coerce": false, "typeName": "ZodString", "zodOpenApi": { - "current": [Circular], "openapi": { "ref": "foo", }, + Symbol(current): [Circular], }, }, "and": [Function], @@ -934,4 +934,11 @@ describe('enhanceWithMetadata', () => { ] `); }); + + it('does not fail JSON serialization', () => { + const FooSchema = z.string().openapi({ ref: 'foo' }); + expect(() => { + JSON.stringify(FooSchema); + }).not.toThrow(); + }); }); diff --git a/src/entries/extend.ts b/src/entries/extend.ts index fea7d7e..ad509e9 100644 --- a/src/entries/extend.ts +++ b/src/entries/extend.ts @@ -4,5 +4,4 @@ import { extendZodWithOpenApi } from '../extendZod'; extendZodWithOpenApi(z); -// eslint-disable-next-line @typescript-eslint/consistent-type-exports export * from '../extendZodTypes'; // compatibility with < TS 5.0 as the export type * syntax is not supported diff --git a/src/extendZod.test.ts b/src/extendZod.test.ts index a39bb68..831ee36 100644 --- a/src/extendZod.test.ts +++ b/src/extendZod.test.ts @@ -2,6 +2,7 @@ import { z } from 'zod'; import { createSchema } from './create/schema/single'; import { extendZodWithOpenApi } from './extendZod'; +import { currentSymbol, previousSymbol } from './extendZodTypes'; extendZodWithOpenApi(z); @@ -22,7 +23,8 @@ describe('extendZodWithOpenApi', () => { expect(a._def.zodOpenApi?.openapi?.description).toBe('test'); expect(b._def.zodOpenApi?.openapi?.description).toBe('test2'); expect( - b._def.zodOpenApi?.previous?._def.zodOpenApi?.openapi?.description, + b._def.zodOpenApi?.[previousSymbol]?._def.zodOpenApi?.openapi + ?.description, ).toBe('test'); }); @@ -30,8 +32,8 @@ describe('extendZodWithOpenApi', () => { const a = z.string().openapi({ ref: 'a' }); const b = a.uuid(); - expect(a._def.zodOpenApi?.current).toBe(a); - expect(b._def.zodOpenApi?.current).toBe(a); + expect(a._def.zodOpenApi?.[currentSymbol]).toBe(a); + expect(b._def.zodOpenApi?.[currentSymbol]).toBe(a); }); it('adds ._def.zodOpenApi.openapi fields to a zod type', () => { @@ -53,7 +55,7 @@ describe('extendZodWithOpenApi', () => { const b = a.extend({ b: z.string() }); expect(a._def.zodOpenApi?.openapi?.ref).toBe('a'); - expect(b._def.zodOpenApi?.previous).toStrictEqual(a); + expect(b._def.zodOpenApi?.[previousSymbol]).toStrictEqual(a); }); it('removes previous openapi ref for an object when .omit or .pick is used', () => { @@ -74,7 +76,7 @@ describe('extendZodWithOpenApi', () => { }); expect(a._def.zodOpenApi?.openapi?.ref).toBe('a'); - expect(b._def.zodOpenApi?.previous).toStrictEqual(a); + expect(b._def.zodOpenApi?.[previousSymbol]).toStrictEqual(a); expect(c._def.zodOpenApi?.openapi).toEqual({}); expect(d._def.zodOpenApi?.openapi).toEqual({}); diff --git a/src/extendZod.ts b/src/extendZod.ts index 4738f1c..149daee 100644 --- a/src/extendZod.ts +++ b/src/extendZod.ts @@ -1,6 +1,6 @@ import type { ZodRawShape, ZodTypeDef, z } from 'zod'; -import './extendZodTypes'; +import { currentSymbol, previousSymbol } from './extendZodTypes'; type ZodOpenApiMetadataDef = NonNullable; type ZodOpenApiMetadata = ZodOpenApiMetadataDef['openapi']; @@ -42,11 +42,11 @@ export function extendZodWithOpenApi(zod: typeof z) { }); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - result._def.zodOpenApi.current = result; + result._def.zodOpenApi[currentSymbol] = result; if (zodOpenApi) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - result._def.zodOpenApi.previous = this; + result._def.zodOpenApi[previousSymbol] = this; } // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -63,13 +63,13 @@ export function extendZodWithOpenApi(zod: typeof z) { if (def.zodOpenApi) { const cloned = { ...def.zodOpenApi }; cloned.openapi = mergeOpenApi({ description: args[0] }, cloned.openapi); - cloned.previous = this; - cloned.current = result; + cloned[previousSymbol] = this; + cloned[currentSymbol] = result; def.zodOpenApi = cloned; } else { def.zodOpenApi = { openapi: { description: args[0] }, - current: result, + [currentSymbol]: result, }; } @@ -88,11 +88,11 @@ export function extendZodWithOpenApi(zod: typeof z) { if (zodOpenApi) { const cloned = { ...zodOpenApi }; cloned.openapi = mergeOpenApi({}, cloned.openapi); - cloned.previous = this; + cloned[previousSymbol] = this; extendResult._def.zodOpenApi = cloned; } else { extendResult._def.zodOpenApi = { - previous: this, + [previousSymbol]: this, }; } @@ -112,8 +112,8 @@ export function extendZodWithOpenApi(zod: typeof z) { if (zodOpenApi) { const cloned = { ...zodOpenApi }; cloned.openapi = mergeOpenApi({}, cloned.openapi); - delete cloned.previous; - delete cloned.current; + delete cloned[previousSymbol]; + delete cloned[currentSymbol]; omitResult._def.zodOpenApi = cloned; } @@ -133,8 +133,8 @@ export function extendZodWithOpenApi(zod: typeof z) { if (zodOpenApi) { const cloned = { ...zodOpenApi }; cloned.openapi = mergeOpenApi({}, cloned.openapi); - delete cloned.previous; - delete cloned.current; + delete cloned[previousSymbol]; + delete cloned[currentSymbol]; pickResult._def.zodOpenApi = cloned; } // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any diff --git a/src/extendZodTypes.ts b/src/extendZodTypes.ts index c1762fc..c4ff49d 100644 --- a/src/extendZodTypes.ts +++ b/src/extendZodTypes.ts @@ -7,6 +7,9 @@ type SchemaObject = oas30.SchemaObject & oas31.SchemaObject; type ReplaceDate = T extends Date ? Date | string : T; +export const currentSymbol = Symbol('current'); +export const previousSymbol = Symbol('previous'); + /** * zod-openapi metadata */ @@ -77,12 +80,12 @@ interface ZodOpenApiMetadataDef { /** * Used to keep track of the Zod Schema had `.openapi` called on it */ - current?: ZodTypeAny; + [currentSymbol]?: ZodTypeAny; /** * Used to keep track of the previous Zod Schema that had `.openapi` called on it if another `.openapi` is called. * This can also be present when .extend is called on an object. */ - previous?: ZodTypeAny; + [previousSymbol]?: ZodTypeAny; } interface ZodOpenApiExtendMetadata { diff --git a/tsconfig.json b/tsconfig.json index ff183e6..59cef52 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,6 @@ "removeComments": false, "target": "ES2022" }, - "exclude": ["lib*/**/*", "crackle.config.ts"], + "exclude": ["lib*/**/*", "crackle.config.ts", "dist", "api", "extend"], "extends": "skuba/config/tsconfig.json" }