Skip to content

Commit eb0978f

Browse files
pierre-isabel-bbcmelloware
authored andcommitted
fix(zod): handle "exotic" json mime types
1 parent 0dda64c commit eb0978f

2 files changed

Lines changed: 95 additions & 3 deletions

File tree

packages/zod/src/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,16 @@ const parseBodyAndResponse = ({
13371337
// Only handle JSON and form-data; other content types (e.g., application/octet-stream)
13381338
// are skipped - unclear if this is correct behavior for root-level binary/text bodies.
13391339
const contentEntries = Object.entries(resolvedRef.content ?? {});
1340-
const jsonContent = contentEntries.find(isMediaType('application/json'));
1340+
1341+
const jsonContent = contentEntries.find(
1342+
isMediaType(
1343+
// application/json
1344+
// application/geo+json
1345+
// application/ld+json
1346+
// application/manifest+json
1347+
String.raw`^application\/([\w-]+\+)?json$`,
1348+
),
1349+
);
13411350
const formDataContent = contentEntries.find(
13421351
isMediaType('multipart/form-data'),
13431352
);
@@ -1422,9 +1431,9 @@ const parseBodyAndResponse = ({
14221431
};
14231432

14241433
const isMediaType =
1425-
(expectedContentType: string) =>
1434+
(pattern: string) =>
14261435
([contentType]: [string, object]): boolean =>
1427-
contentType.split(';')[0].trim() === expectedContentType;
1436+
new RegExp(pattern).test(contentType.split(';')[0].trim());
14281437

14291438
const getSingleResponse = (
14301439
responses:

packages/zod/src/zod.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6403,6 +6403,89 @@ export const UploadFormResponse = zod.object({
64036403
"uploaded": zod.number().optional()
64046404
})
64056405
6406+
`);
6407+
});
6408+
6409+
it('json exotic content type: comprehensive content type handling', async () => {
6410+
const schema = {
6411+
pathRoute: '/upload-form',
6412+
context: {
6413+
spec: {
6414+
paths: {
6415+
'/upload-form': {
6416+
post: {
6417+
operationId: 'uploadForm',
6418+
requestBody: {
6419+
required: true,
6420+
content: {
6421+
'application/geo+json': {
6422+
schema: {
6423+
type: 'object',
6424+
properties: {
6425+
success: { type: 'boolean' },
6426+
uploaded: { type: 'number' },
6427+
},
6428+
},
6429+
},
6430+
},
6431+
},
6432+
responses: {
6433+
'200': {
6434+
content: {
6435+
'application/manifest+json': {
6436+
schema: {
6437+
type: 'object',
6438+
properties: {
6439+
success: { type: 'boolean' },
6440+
uploaded: { type: 'number' },
6441+
},
6442+
},
6443+
},
6444+
},
6445+
},
6446+
},
6447+
},
6448+
},
6449+
},
6450+
},
6451+
output: { override: { zod: { generateEachHttpStatus: false } } },
6452+
},
6453+
} as unknown as GeneratorOptions;
6454+
const result = await generateZod(
6455+
{
6456+
pathRoute: '/upload-form',
6457+
verb: 'post',
6458+
operationName: 'uploadForm',
6459+
override: {
6460+
...zodOverride,
6461+
zod: {
6462+
...zodOverride.zod,
6463+
generate: { ...zodOverride.zod.generate, response: true },
6464+
},
6465+
},
6466+
} as unknown as Parameters<typeof generateZod>[0],
6467+
schema,
6468+
testOutput,
6469+
);
6470+
// encBinary: encoding image/png → File
6471+
// encText: encoding text/plain → File | string
6472+
// cmtBinary: contentMediaType image/png → File
6473+
// cmtText: contentMediaType application/xml → File | string
6474+
// encOverride: encoding text/csv overrides contentMediaType image/png → File | string
6475+
// formatBinary: format: binary → File (same as instanceof check)
6476+
// base64Field: contentEncoding base64 → stays string
6477+
// metadata: object → object schema
6478+
expect(result.implementation)
6479+
.toBe(`export const UploadFormBody = zod.object({
6480+
"success": zod.boolean().optional(),
6481+
"uploaded": zod.number().optional()
6482+
})
6483+
6484+
export const UploadFormResponse = zod.object({
6485+
"success": zod.boolean().optional(),
6486+
"uploaded": zod.number().optional()
6487+
})
6488+
64066489
`);
64076490
});
64086491
});

0 commit comments

Comments
 (0)