Skip to content

feat(client-generator): expose response headers via opt-in throw-mode envelope - #3002

Open
adamaltman wants to merge 3 commits into
mainfrom
feat/client-generator-typed-response-headers
Open

feat(client-generator): expose response headers via opt-in throw-mode envelope#3002
adamaltman wants to merge 3 commits into
mainfrom
feat/client-generator-typed-response-headers

Conversation

@adamaltman

Copy link
Copy Markdown
Member

What/Why/How?

What: Generated throw-mode clients can now return declared response headers. Passing { envelope: true } on a call returns { data, headers, response } instead of the parsed body alone.

Why: Throw mode (the default) returns only the body, so OpenAPI-declared success-response headers — Pagination-Total, rate limits, Location — were unreachable without switching the whole client to --error-mode result.

How:

  • IR: operations carry successResponseHeaders built from the selected success response (including bodyless responses like 201 + Location); referenced scalar header schemas are dereferenced.
  • Runtime: execute returns the envelope when envelope === true; header values are coerced to number/boolean per schema (blank/unparsable values are omitted, never 0). Pagination iterators strip the flag so page pointers keep resolving against the body.
  • Types: the EnvelopeResult conditional type keeps default call sites body-only (non-breaking) and narrows to Envelope<…> only for a literal envelope: true; a statically unknowable flag widens to the union. Result-mode clients ignore the option (they already return response).
  • Emitters: header names become identifier-safe camelCase keys (Pagination-TotalpaginationTotal, 3D-Secure_3dSecure) with deterministic collision suffixes; <Op>ResponseHeaders aliases are emitted; required headers are required properties (the type trusts the description, like body types).
  • Wrappers: TanStack Query and SWR inits are Omit<RequestOptions, 'envelope'> and the forwarded calls strip the flag at runtime, so cached data is always the plain body. This also fixes a latent bug where grouped-style no-input SWR hooks passed init in the args slot.

Reference

Closes #3001

Testing

  • New unit coverage across IR build, descriptor/alias/wrapper emitters, runtime coercion, and EnvelopeResult typing (993 client-generator tests).
  • New e2e suite (envelope.test.ts) generates a client from a response-headers fixture and strict-tsc-checks envelope and default call sites; the TanStack e2e pins useQuery(...).data to the plain body type.
  • Full suite: compile, typecheck, unit, e2e, lint, format all pass locally (one pre-existing generator-contract e2e failure in the local environment only; passes on CI from main).

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only)
  • New package installed? - Tested in different environments (browser/node)
  • Documentation update has been considered

Security

  • The security impact of the change has been considered
  • Code follows company security practices and guidelines

Made with Cursor

… envelope

Throw-mode calls return only the parsed body, so declared response
headers (Pagination-Total, rate limits, Location) were unreachable
without switching to --error-mode result. Passing { envelope: true }
on a call now returns { data, headers, response }: headers is a typed
camelCase object built from the operation's declared success-response
headers with number/boolean coercion, and response is the raw Response
for anything undocumented.

Default call sites stay body-only via the EnvelopeResult conditional
type, pagination iterators and the TanStack Query/SWR wrappers exclude
and strip the option so cached data is always the plain body.

Co-authored-by: Cursor <cursoragent@cursor.com>
@adamaltman
adamaltman requested review from a team as code owners July 31, 2026 23:00
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a3855be

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@redocly/client-generator 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

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 77.13% (🎯 77%) 11460 / 14858
🔵 Statements 77.22% (🎯 77%) 12257 / 15871
🔵 Functions 81.63% (🎯 81%) 2329 / 2853
🔵 Branches 70.78% (🎯 70%) 8401 / 11868
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/client-generator/src/index.ts 100% 100% 100% 100%
packages/client-generator/src/emitters/client-assembly.ts 97.63% 95.23% 100% 97.24% 356, 453-456
packages/client-generator/src/emitters/descriptor.ts 100% 100% 100% 100%
packages/client-generator/src/emitters/operation-aliases.ts 84.9% 75.86% 61.53% 84.31% 89, 93, 218, 229, 240, 247-252
packages/client-generator/src/emitters/pagination.ts 98.65% 98.78% 100% 99.19% 260, 313
packages/client-generator/src/emitters/reserved-names.ts 100% 92.85% 100% 100%
packages/client-generator/src/emitters/response-headers.ts 75% 62.5% 100% 80.95% 24-25, 28-29
packages/client-generator/src/emitters/runtime-sources.ts 100% 100% 100% 100%
packages/client-generator/src/emitters/support.ts 100% 100% 100% 100%
packages/client-generator/src/emitters/tanstack-query.ts 98.8% 89.7% 100% 100% 299
packages/client-generator/src/emitters/ts.ts 100% 95.45% 100% 100%
packages/client-generator/src/emitters/wrapper-support.ts 98.11% 96.66% 100% 100% 128
packages/client-generator/src/intermediate-representation/build.ts 100% 99.71% 100% 100%
packages/client-generator/src/intermediate-representation/model.ts 100% 100% 100% 100%
packages/client-generator/src/runtime/create-client.ts 96% 92.8% 96.77% 96.32% 95, 227-228, 364-369, 449-453
Generated in workflow #11061 for commit a3855be by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Performance Benchmark (Lower is Faster)

CLI Version Bundle Lint Check Config
cli-latest ▓ 1.03x ± 0.01 ▓ 1.01x ± 0.01 ▓ 1.00x ± 0.01
cli-next ▓ 1.00x (Fastest) ▓ 1.00x (Fastest) ▓ 1.00x (Fastest)

@adamaltman
adamaltman requested a review from Marshevskyy July 31, 2026 23:41
Comment thread docs/@v2/guides/use-generated-client.md Outdated
Comment thread docs/@v2/guides/use-generated-client.md Outdated
Comment thread docs/@v2/guides/use-generated-client.md Outdated
Comment thread docs/@v2/guides/use-generated-client.md Outdated
Co-authored-by: Jacek Łękawa <164185257+JLekawa@users.noreply.github.com>
oxfmt requires continuation lines under a bullet to be indented;
the tech-writer rewrite left two of them flush left.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

feat(client-generator): expose response headers on successful calls (e.g. pagination totals)

2 participants