Skip to content

Releases: block65/openapi-codegen

v10.0.5

11 May 07:13

Choose a tag to compare

Fix: optional handling now matches direction of data flow

The previous exact / coerced split conflated two orthogonal concerns — whether to coerce wire strings and whether to tolerate undefined. The result: server-side hono middleware and client-side response parsing were both wrapping optional fields in v.optional(...), which permits undefined even though JSON-parsed wire data can never carry one. Handlers had to defensively guard against { field: undefined } cases that were structurally impossible.

Realigned the schemas along direction of data flow:

  • inputXxxSchema — for outgoing TS-side values (request bodies before JSON.stringify, pre-flight validation). Uses v.optional(...). No wire coercion (types are already TS-native). Tolerates the destructure-with-default { foo: undefined } pattern.

  • xxxSchema (wire) — for incoming JSON-parsed values (hono middleware, rest-client response parsing). Uses v.exactOptional(...) — field is present-with-value or absent, never undefined. Includes bigint / number coercion for HTTP wire formats.

For server handlers, the win is concrete: c.req.valid(\"json\") now returns { name: string, nickname?: string } rather than { name: string, nickname: string | undefined }. No phantom-undefined to guard against.

Renames

Before After
exactPetSchema inputPetSchema
petSchema (with v.optional + coercion) petSchema (with v.exactOptional + coercion)
--exact-only --input-only
CodegenOptions.exactOnly CodegenOptions.inputOnly
SchemaMode \"exact\" / \"coerced\" \"input\" / \"wire\"

Behavioral changes

  • petSchema (wire variant, same name as before): optional fields now use v.exactOptional instead of v.optional. Strictly correct for JSON-parsed data; would reject { field: undefined } if it ever appeared (it can't, after JSON.parse).
  • exactPetSchemainputPetSchema: optional handling flipped from v.exactOptional to v.optional. Use this for client-side pre-flight validation where TS callers may pass undefineds.

Versioning note

Strictly a major bump (renames + flipped semantics). Released as patch given a single pre-launch consumer.

v10.0.4

11 May 05:30

Choose a tag to compare

Highlights

  • New: two-file commands output. Codegen now emits commands.ts (lean, no schema imports) alongside commands-validated.ts (subclasses that attach static responseSchema). Consumers swap files (or bundler-alias dev → validated) to opt into response validation without paying bundle cost in prod. Pairs with @block65/rest-client 13.0.4 which drops the validateResponses flag and validates whenever a schema is present.

  • Fix: inline 2xx response schemas no longer silently skipped. Operations whose 2xx body is an inline object with nested \$refs (e.g. { user: { \$ref: \"...\" } }) now correctly emit a per-command response schema. Previously the codegen fell through and validateResponses: true was a silent no-op for those commands — a real production leak class.

  • Fix: array responses now validate as arrays. Endpoints like Pet[] previously had static responseSchema = petSchema (single Pet, wrong — the response is the whole array). Now emit v.array(petSchema).

Removed

  • Static bodySchema, paramsSchema, querySchema are no longer attached to generated Command classes. rest-client never read them, and Hono middleware imports schemas directly from valibot.ts already — they were pure bundle overhead.

Generated output

  • Namespace imports (import * as commands / import * as schemas) in the validated file keep diffs stable across regenerations.

Dependencies

  • Peer dep: @block65/rest-client ^13.0.3 (required for schema-presence-driven validation).
  • Misc dep rollforward: valibot 1.4, hono 4.12.18, etc.

Note on versioning

Strictly this should be a major bump (peer dep range moved 12 → 13, generated output changed). Released as a patch given a single pre-launch consumer.

v10.0.3

09 May 08:24

Choose a tag to compare

Full Changelog: v10.0.2...v10.0.3

v10.0.2

04 May 13:03

Choose a tag to compare

Fixed

  • Generated Hono middleware validators now reference the coerced sibling schemas instead of the exact ones, so HTTP query, path, and header values (which always arrive as strings) validate correctly. Previously a request like ?limit=20 would fail because "20" was not a v.number(). Body validators are unaffected in practice — JSON-typed numbers still pass through the coerced union. Use exactOnly to opt out.

v10.0.1

03 May 07:23

Choose a tag to compare

Fix

Honors x-typescript-hint on string schemas inside oneOf/anyOf/allOf. Previously the recursive string branch ignored the hint, so a schema like oneOf: [{ enum: ["native"] }, { type: string, x-typescript-hint: "EmbedUrl" }] collapsed to "native" | string. The fix mirrors the top-level handling in the recursive case.

Other

  • Refactor: prefer toSorted/toReversed and iife blocks over deeply-nested ternaries.
  • Chore: drop leftover prettier config, the unused @ts-morph/common devDep, and the unicorn nested-ternary lint rule.
  • Chore: enable composite tsconfig; ignore *.tsbuildinfo.
  • Style: fix indentation on the undici @ts-expect-error comment block in the test fetch wrappers.

v10.0.0

02 May 06:48

Choose a tag to compare

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

v9.4.1

23 Apr 14:06

Choose a tag to compare

What's Changed

Fixes

  • Use RegExp constructor for valibot pattern validation — Regex literal construction only escaped forward slashes; patterns with backslashes, quotes, or newlines would produce invalid source code. new RegExp(JSON.stringify(pattern)) handles all edge cases.

Full Changelog: v9.4.0...v9.4.1

v9.4.0

23 Apr 14:03

Choose a tag to compare

What's Changed

Features

  • Zero-parameter command constructors — Commands with no path/query/header params (e.g. ListBillingAccountsCommand) now generate a no-arg constructor with super(pathname) directly
  • Optional input for all-optional params — Commands where all parameters are optional no longer require an empty {} argument (e.g. new FindPetsCommand() now works)

Fixes

  • Skip response type for non-JSON endpoints — Endpoints without JSON response content no longer incorrectly get unspecifiedKeyword as a response type argument
  • Remove | undefined from optional header types — Optional header properties now use ? only, fixing compatibility with the Command generic constraint (Record<string, string | number | boolean>)

Chores

  • Bump @block65/rest-client peer dependency to ^12.1.0 (4th generic for headers)
  • Add biome config

Full Changelog: v9.3.0...v9.4.0

v9.3.0

09 Apr 10:47

Choose a tag to compare

What's Changed

  • fix: generate v.null() for type: "null" schemas in valibot by @maxholman in #16
  • feat: wire header params into Command constructor by @maxholman in #17

Full Changelog: v9.2.2...v9.3.0

v9.2.2

09 Apr 08:55

Choose a tag to compare

What's Changed

  • fix: resolve $ref schemas for query and header param coercion by @maxholman in #15

Full Changelog: v9.2.1...v9.2.2