fix(plugins/zod): fix support for string types in binary format
This fixes support for string types in binary formats by converting the stringExpression into a Zod union type with File and Blob. It matches the behavior of @hey-api/typescript's handling of the said type.
Run & review this pull request in StackBlitz Codeflow.
🦋 Changeset detected
Latest commit: f84102871a933a2dfcf50abb39dac9ecab18375b
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 1 package
| Name | Type |
|---|---|
| @hey-api/openapi-ts | Minor |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
The latest updates on your projects. Learn more about Vercel for Git ↗︎
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| hey-api-docs | ✅ Ready (Inspect) | Visit Preview | 💬 Add feedback | Jan 24, 2025 2:35pm |
Hi @nedpals, are you able to add tests please?
@mrlubos overlooked it my bad. will add the tests once i get back online tomorrow
Codecov Report
:x: Patch coverage is 4.76190% with 20 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 56.22%. Comparing base (3d17490) to head (f841028).
:warning: Report is 1796 commits behind head on main.
| Files with missing lines | Patch % | Lines |
|---|---|---|
| packages/openapi-ts/src/plugins/zod/plugin.ts | 4.76% | 20 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## main #1616 +/- ##
==========================================
- Coverage 56.26% 56.22% -0.05%
==========================================
Files 158 158
Lines 24875 24896 +21
Branches 1816 1816
==========================================
+ Hits 13997 13998 +1
- Misses 10868 10888 +20
Partials 10 10
| Flag | Coverage Δ | |
|---|---|---|
| unittests | 56.22% <4.76%> (-0.05%) |
:arrow_down: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@nedpals Actually I do have a question here. Since it's now no longer matching strings, won't this fail response validations?
@nedpals Actually I do have a question here. Since it's now no longer matching strings, won't this fail response validations?
My use case for this is for form validation (react-hook-forms specifically) since I presume that the correct response types were already inferred during client generation.
Another thing is that I also expect that it should match 1:1 with the generated Typescript types.
hi @mrlubos any updates on your verdict on this?
I don't think we can merge this due to my concern above :( I'm not sure there's an easy win available, would need to think how to solve it. You could theoretically add strings to the union, but then the response would be still incorrect because that's always going to be a string. Long story short, I'd need (someone) to test this with various responses, see how it impacts that part of the experience, and possibly add some unit tests as snapshots don't capture that behaviour well
I encountered an issue with file upload and validator errors.
Here’s my type definition:
export type MediaControllerCreateData = {
body: {
file?: Blob | File;
};
path?: never;
query?: never;
url: '/media';
};
But the generated Zod validator is:
export const zMediaControllerCreateData = z.object({
body: z.object({
file: z.optional(z.string()),
}),
path: z.optional(z.never()),
query: z.optional(z.never()),
});
This causes a problem because the validator expects a string, while I actually want to accept a File or Blob.
There has been some progress since the last update. We now split schemas based on their direction: in/request/payload and out/response/data. With this we could potentially start adjusting behavior for schemas (and types) based on the direction, e.g. payloads expect a file, responses expect a string. It's not fully there yet, however. Maybe the best approach for now would be to simply allow a union of string, file, and blob in both directions?
I am also being hit by this issue. I just started using hey-api/openapi-ts, and so far I love it, it is easy to use, typescript-first and well documented. Don't get me wrong, I am grateful to Hey for making this package, but limitations like this must be clearly documented.
For my project uploading files is crucial, and since there is no workaround (as far as I am aware), I must move to a different package or make the call manually.
However for the sake of other users of this package, I believe this package could benefit from having a document describing supported OpenAPI features. For example here is how another OpenAPI generator documents this https://swiftpackageindex.com/apple/swift-openapi-generator/1.10.2/documentation/swift-openapi-generator/supported-openapi-features
any idea when this might merge. Blob type is much needed
there is no workaround (as far as I am aware)
There is a workaround depending on your use case. Here's what I do in the meantime:
// in `zodGenWrapper.ts
import { zMyModel as oldMyModel } from "./heyapi-types/zod.gen"
// Overwrite the file field to be seen by zod as an actual file
// Limited documentation of `z.file()` in the wild, so using old hack instead
export const zMyModel = oldMyModel.extend({
FileField: z.instanceof(File).or(z.instanceof(Blob)),
});
// Credit: https://stackoverflow.com/a/61897244
// Apparently, since we export zMyModel already above, reexports of the same name get ignored and this works
export * from "./heyapi-types/zod.gen"
Then, just import the models from zodGenWrapper.ts instead of heyapi-types/zod.gen.ts. It's not ideal, but at least works in the meantime
much needed feature