add some basic validation

This commit is contained in:
Devin Zuczek
2026-08-05 14:00:31 -04:00
parent 6bfd4d9e50
commit b82a5e1dc0
4 changed files with 168 additions and 66 deletions
+37 -1
View File
@@ -33,9 +33,44 @@ function addIntegerExamples(node: unknown): void {
for (const value of Object.values(obj)) addIntegerExamples(value)
}
/**
* hono-openapi registers a `validator('form', …)` body under `multipart/form-data` ONLY,
* and there is no way to ask it for another media type: its media selection reads
* `options?.media ?? target === 'json' ? 'application/json' : 'multipart/form-data'`,
* which by JS precedence is `((options?.media ?? target) === 'json') ? … : …` — so the
* `media` option can never produce anything else.
*
* That leaves the spec claiming a validated route accepts only multipart, when the real
* callers (the Rec Room client and the website) post `application/x-www-form-urlencoded`
* and Hono's `parseBody()` reads both. So the urlencoded variant is mirrored back in.
*
* Safe because nothing here documents a genuinely multipart-only body — there are no file
* uploads on these workers, and hand-written form bodies already declare both types. If
* one is ever added, it will need to opt out of this.
*/
function mirrorFormBodies(node: unknown): void {
if (Array.isArray(node)) {
for (const item of node) mirrorFormBodies(item)
return
}
if (node === null || typeof node !== 'object') return
const obj = node as Record<string, unknown>
const content = obj.content
if (content !== null && typeof content === 'object') {
const media = content as Record<string, unknown>
const multipart = media['multipart/form-data']
if (multipart !== undefined && media['application/x-www-form-urlencoded'] === undefined) {
media['application/x-www-form-urlencoded'] = multipart
}
}
for (const value of Object.values(obj)) mirrorFormBodies(value)
}
/**
* Wrap `openAPIRouteHandler(...)` so the generated document gets example values for its
* integer fields. Purely cosmetic — nothing about the documented shapes changes.
* integer fields, and so a validated form body documents both content types it really
* accepts. Nothing about the runtime behaviour changes — this only corrects the document.
*
* ```ts
* app.get('/openapi.json', describeRoute({ hide: true }), withCleanSpec(openAPIRouteHandler(app, { ... })))
@@ -47,6 +82,7 @@ export function withCleanSpec(handler: Handler | MiddlewareHandler): Handler {
if (!(res instanceof Response)) return res as never
const spec: unknown = await res.json()
addIntegerExamples(spec)
mirrorFormBodies(spec)
return c.json(spec as Record<string, unknown>)
}
}