Skip to content

Conversation

@superxiao
Copy link
Contributor

@superxiao superxiao commented Dec 12, 2025

Adds support for OAS 3.1 contentMediaType and encoding.contentType to properly detect binary/file fields in OpenAPI schemas. This enables orval to correctly handle file uploads from tools like orpc that emit contentMediaType via z.file().

  • contentMediaType presence → Blob type (unless contentEncoding is present or media key contradicts)
  • encoding.contentType on string fields → Blob type (file upload intent)
  • Binary MIME types → Blob; Text MIME types → Blob | string
  • Zod: binary → instanceof(File), text → instanceof(File).or(string())

Why Blob | string for text MIMEs?
Binary data (images, audio) has no string representation—must be Blob. Text content (CSV, XML, plain text) can be passed either as a File or as a string directly (e.g., from a textarea or generated content). The runtime wraps strings in new Blob([str], { type }) automatically.

Precedence: media key > encoding.contentType > contentMediaType > format

This unifies handling for both OAS 3.0 (format: binary) and OAS 3.1 (contentMediaType). Existing format signals are respected first, and the new 3.1 signals only apply when no format is present. A more spec-following approach is to handle format only for OAS 3.0 input and contentMediaType only for OAS 3.1 input. This PR opts for a simpler format > contentMediaType approach without looking at input OAS version, but can be updated later if needed.

Related issues: #2636 #869

Test plan

  • Unit tests for isBinaryContentType() covering binary and text MIME types
  • Unit tests for media key precedence (binary overrides schema, text/JSON ignores contentMediaType)
  • Comprehensive FormData generation test covering all content type combinations
  • Zod tests for binary contentMediaType, text contentMediaType, and contentEncoding cases

Fix #2636 #869

superxiao and others added 5 commits December 12, 2025 22:12
… support

- Binary contentMediaType/encoding.contentType → Blob type
- Text contentMediaType/encoding.contentType → Blob | string type
- encoding.contentType takes precedence over contentMediaType
- Binary media key (e.g. application/octet-stream) overrides schema to Blob
- Text/JSON media key causes contentMediaType to be ignored per OAS 3.1
- FormData generation: text types use instanceof Blob runtime check
- Zod: binary → instanceof(File), text → instanceof(File).or(string())
- contentEncoding (e.g. base64) keeps type as string
Follow codebase standard of exact string matching with template literals
instead of pattern matching. Consolidates 8 separate assertions into one
comprehensive test covering all content type combinations.
- Add !item.format guard so contentMediaType doesn't override existing
  format: binary handling
- Update comments to clarify OAS 3.0/3.1 relationship and Blob | string rationale
melloware
melloware previously approved these changes Dec 12, 2025
Copy link
Collaborator

@melloware melloware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@melloware
Copy link
Collaborator

@snebjorn mind reviewing this one too?

@superxiao
Copy link
Contributor Author

I found some issues and are still working on a more complete fix. Will submit a new version tomorrow.

@melloware melloware marked this pull request as draft December 13, 2025 15:53
@melloware
Copy link
Collaborator

Excellent @superxiao thanks for reporting back

…nd FormData

- Replace callback-based propertyOverride with data-only propertyOverrides
- Consolidate file type detection in getFormDataFieldFileType for all paths
- Add direct TypeScript type assertion test for Blob vs Blob | string
- Clean up docstrings and fix semicolon inconsistency in FormData output
@superxiao superxiao marked this pull request as ready for review December 14, 2025 08:34
@superxiao
Copy link
Contributor Author

Just wondering: currently, zod generation only processes application/json and multipart/form-data content types. Other media keys like application/octet-stream are skipped entirely.

  // packages/zod/src/index.ts:1087-1092
  // Only handle JSON and form-data; other content types (e.g., application/octet-stream)
  // are skipped - unclear if this is correct behavior for root-level binary/text bodies
  const jsonMedia = resolvedRef.content?.['application/json'];
  const formDataMedia = resolvedRef.content?.['multipart/form-data'];

Type generation handles this case - a request body with application/octet-stream generates Blob type. But zod produces nothing.

Is this intentional, or should zod generate z.instanceof(Blob) for binary media keys?

@melloware
Copy link
Collaborator

I actually don't know what Zod is supposed to do for binary?

…e overrides

resolveFormDataRootObject now returns undefined when there are no file type
overrides, allowing the caller to fall through to normal resolveObject which
preserves reference names like Pet instead of inlining the dereferenced type.
@superxiao
Copy link
Contributor Author

I actually don't know what Zod is supposed to do for binary?

I will ignore that issue then since there's no feature request for it.

@superxiao
Copy link
Contributor Author

I don't have any more change in mind for this PR, feel free to merge when you think it's ready.

@melloware melloware merged commit bf98c73 into orval-labs:master Dec 14, 2025
2 checks passed
@melloware
Copy link
Collaborator

thanks @superxiao !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support OpenAPI 3.1 contentMediaType for Blob type generation

2 participants