Skip to content

v10.0.0

Choose a tag to compare

@maxholman maxholman released this 02 May 06:48
· 32 commits to master since this release

Whats change

v9.x generated a single valibot schema per type that mixed concerns - sometimes strict, sometimes coercing. This made it impossible to cleanly separate server validation (reject bad input) from client-side convenience (accept realistic wire types, trim whitespace, coerce strings to numbers). You ended up needing stripUndefined calls and other runtime workarounds to satisfy types that were stricter than necessary for the context.

v10 generates two schema variants per type: exact (the API contract - what the spec says) and coerced (the DX helper - accepts what the wire actually gives you, produces spec-compliant values). The server uses exact schemas and validates strictly. The client uses coerced schemas and gets clean data without manual cleanup. Each side gets exactly the schema it needs.

Breaking

  • All schemas now have exactFooSchema (strict) + fooSchema (coerced) variants - consumers importing schema names directly will need to update
  • Hono middleware uses exact schemas - the server no longer silently coerces or trims input. Malformed data is rejected, not rescued
  • Coerced schemas use v.optional() instead of v.exactOptional()undefined values are accepted in coerced mode since they can't survive JSON serialization anyway

New

  • Dual schemas — exact validates that data is spec-compliant; coerced takes untrusted data and makes it spec-compliant. Coerced composes exact via pipe, so constraints are never duplicated
  • int64 coercion — JSON can't represent bigint, so the coerced schema accepts string | number | bigint and produces bigint via v.toBigint(). Only applied where the wire format genuinely forces a different type
  • HTTP param coercion — query and header params are always strings on the wire. Coerced schemas accept strings and coerce to the spec type via v.toNumber() / v.toBigint(). Exact schemas expect the native type
  • String trimming — coerced mode applies v.trim() before validation for user-typed fields (strings, emails, URLs). Not applied to machine-generated values (UUIDs, patterns, enums, const values, byte/binary/password). Exact mode never trims
  • Command static schema props — generated commands now carry bodySchema, paramsSchema, querySchema, responseSchema as static properties. rest-client will consume these for automatic response validation and input checking in a future release
  • --exact-only CLI flagopenapi-codegen --exact-only emits only exact schemas, no coerced variants
  • Schema deduplication — when exact and coerced are structurally identical (enums, UUIDs, patterns, const values), the coerced schema is export const fooSchema = exactFooSchema instead of a full duplicate

Fixes

  • Emit unknown output type when a response has no JSON content — previously the Output type argument was omitted, shifting Query and Header generics into the wrong positions. This caused type errors for specs with streaming or non-JSON endpoints (e.g. Docker Engine API)

Tooling

  • Replaced biome with oxlint + oxfmt
  • Updated to undici 8, vitest 4, vite 8, TypeScript 6