httpapi: WithHeaders response header schemas - #6934
Conversation
Adds the WithHeaders branded declaration schema for wrapping success schemas with a response headers schema, the branded WithHeaders.Value pair constructed via withHeaders, and the encodeToWithHeaders transform for folding response headers into domain types such as error classes. HttpApiEndpoint gains a WithHeaders case in its success types and unwraps the brand when validating per-status combinations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b581fb2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
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 |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — schema-level response-header support for unstable httpapi, plus endpoint validation/codec wiring to keep WithHeaders wrappers intact through construction.
- Added
HttpApiSchema.WithHeaders,withHeaders,isWithHeaders, and theWithHeadersTypeId/WithHeadersValueTypeIdbrands. - Added
HttpApiSchema.encodeToWithHeadersfor folding response headers into opaque schemas such as tagged errors. - Updated
HttpApiEndpointsuccess-type extraction and endpoint construction so wrapper annotations resolve first, inner schemas are transformed by the appropriate body codec, and headers becomeSchema.toCodecStringTree. - Rejected
WithHeadersin endpoint error position with a clear pointer towardencodeToWithHeaders.
The validation checks all pass locally, including pnpm lint, pnpm check, the targeted runtime tests, and the HttpApiEndpoint / HttpApiSchema type tests.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
There was a problem hiding this comment.
ℹ️ No critical issues — minor suggestions inline.
Reviewed changes — builder-level integration for the response-header schemas added in the previous commit, plus encoding support for HttpApiSchema.encodeToWithHeaders error schemas.
HttpApiBuildernow detects brandedWithHeaders.Valuehandler results and encodes their headers separately throughSchema.toCodecStringTree, merging them onto the encoded body/stream response; buffered and streaming body paths were updated to allow header injection.HttpApiBuilder.toResponseSchemahonors the~httpApiWithHeadersannotation produced byencodeToWithHeaders, encoding the annotated body schema and applying the resulting headers directly to the response.HttpServerResponse.setHeaders/makeResponsenow prefer explicitly suppliedcontent-typeandcontent-lengthheaders over body-derived values, enabling wrapper headers to override the body's default content type.HttpApiEndpointsuccess-type extraction, validation, codec transformation, and error rejection were updated to unwrapWithHeadersconsistently.HttpApiSchemaErrorgained aResponseHeaderskind, and runtime/type tests were added covering buffered headers, content-type override, mixed success unions, error headers, and header-encoding failures.
@v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No critical issues — minor suggestions inline.
Reviewed changes — client-side decoding for response-header schemas added since the prior review.
- Added client-side decoding for buffered
WithHeaderssuccess schemas: success alternatives are re-bucketed by wrapper-first status, the body and response headers are decoded together as{ body, headers }, and the result is wrapped back into the brandedWithHeaders.Valueused by handlers. - Extended the client decoder to handle
encodeToWithHeaderserror schemas by splitting body/headers before applying the user fold. - Refactored
toCodecArrayBufferintofromArrayBufferso both buffered and WithHeaders codecs share the same body-from-ArrayBuffer logic. - Added runtime tests covering buffered-header decode failures and round-tripping branded values through a client call.
- Retired two open Pullfrog review threads: the
HttpServerResponse.setHeadersbehavioural change is now documented in the PR description, andencodeToWithHeaderserror schemas now carryheadersCodecin their annotation so headers are validated and string-tree encoded.
@v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — test-only commit adding two builder/client integration tests for buffered WithHeaders success decoding.
- Added a test that a transformed buffered success body (
Schema.Datedecoded from the JSON payload) and its declared response headers round-trip through the generated client. - Added a test that a plain success alternative inside a mixed
WithHeaders/ non-WithHeadersunion decodes as the plain value. - Retired the open Pullfrog review thread at
HttpApiClient.ts:748: client-side streaming for wrapped headers is acknowledged as out of scope for this stage and deferred to the finalize stage.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — commit 79f3022e completed the deferred client-side streaming integration for WithHeaders response schemas.
- Added client-side decoding for
WithHeaders<StreamSse | StreamUint8Array, Headers>success schemas: wrapped streams are routed through the existing streaming path, response headers are decoded first, and the result is rebuilt as aWithHeaders.Value<body, headers>whose body is a live stream. - Updated
HttpApiClient.SuccessTypeto recurse throughWithHeadersbefore dispatching to stream types, preserving the stream error channel (SSE retry/SSE error/transport error) inside the branded wrapper. - Updated
HttpApiClient.makeClientto bucket bufferedWithHeadersschemas by wrapper-first status and to collect wrapped streams viagetStreamSuccessSchemas, including the mixed-union cases covered by tests. - Refactored
toCodecArrayBufferintofromArrayBufferso buffered and stream-with-headers codecs share the same body-from-ArrayBuffer logic. - Added runtime tests covering wrapped
StreamUint8ArrayandStreamSsedecode/encode, invalid stream headers failing before body consumption, and mixed buffered/stream alternatives; added type tests verifying the widened stream error channels for wrapped streams.
The previously open Pullfrog thread at HttpApiClient.ts:759 (client-side streaming for wrapped headers) is now addressed by this commit.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the final commit completes OpenAPI response-header rendering for WithHeaders success schemas and encodeToWithHeaders error schemas.
- Updated
HttpApiSchema.OpenApiresponse-body extraction to unwrapWithHeaderswrappers and~httpApiWithHeadersannotations, collecting header schemas alongside content and stream definitions. - Emitted response headers in generated OpenAPI specs:
"content-type"is intentionally excluded to avoid colliding with the response media type; other headers are written as lower-case names with JSON schemas andrequiredflags. - Added the
OpenAPISpecHeaderpublic type and surfaced it onOpenApiSpecResponse.headers. - Added runtime coverage for success, error, and stream response headers in
OpenApi.test.ts.
pnpm vitest --run --project effect packages/effect/test/unstable/httpapi/OpenApi.test.ts, pnpm test-types for the affected httpapi typetests, pnpm lint, and pnpm check all pass.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — final polish commit ca8ba123 tightened HttpApiBuilder.toResponseSchema header encoding and expanded documentation/tests.
- Simplified the
encodeHeadershelper intoResponseSchemaso theheadersCodecpath and thedisableCodecsfallback both consistently report schema errors viaerror.issue. - Added runnable JSDoc examples for
WithHeaders,isWithHeaders, andencodeToWithHeaders. - Documented the overlapping-union constraint for multiple
WithHeaderssuccess alternatives. - Added test coverage for user-managed header codecs (
disableCodecs: true) and theHttpServerResponse.setHeaderprecedence change.
All prior Pullfrog review threads remain resolved, and the incremental diff introduces no new behavioral concerns.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — since the prior pullfrog review (ca8ba123), commit ebc76321 added direct WithHeaders support for non-streaming error responses and tightened the builder narrowing.
- Exported
HttpApiSchema.isWithHeadersValueas an internal type guard and replaced thePredicate.hasPropertycast inHttpApiBuilder.handlerToHttpEffectso wrapper branches narrow correctly. - Removed the blanket rejection of
WithHeadersin endpoint error position:HttpApiEndpoint.getErrorResponsenow unwraps the wrapper only to reject streamed bodies, then routesWithHeaderserrors through a newtransformErrorthat applies body response encoding and string-tree header codecs and preserves the wrapper inendpoint.error. - Added
HttpApiSchema.getStatusErrorSchemafor wrapper-first error-status lookup and updatedHttpApi/OpenApiresponse extraction to use the wrapper-aware helpers. - Added
HttpApiBuilder.toResponseErrorSchema, which encodes a branded{ body, headers }error value into anHttpServerResponsewith status, body, and encoded headers. - Expanded tests and type tests to cover direct
WithHeaderserror schemas,encodeToWithHeaderserrors with and without codecs, and OpenAPI error-header rendering.
All prior Pullfrog review threads remain resolved, and the incremental diff introduces no new behavioral concerns.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
- share one WithHeaders response transformation between the error path and the encodeToWithHeaders annotation path in HttpApiBuilder - pass schema-level status getters into toResponseSchema, removing the wrapper ternaries and restoring caching for WithHeaders schemas - skip WithHeaders-wrapped streams in HttpApi response reflection so the client no longer re-groups success schemas by status - fold HttpApiEndpoint transformError into transformResponseSchema - drop the single-member union in toCodecArrayBufferWithHeaders - hoist header codec parsers out of the per-response encode closure - apply response header schemas once in OpenApi extractResponseBodies Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — since the prior pullfrog review (ebc7632), commit 90125f70 simplifies the WithHeaders integration across the unstable httpapi stack without changing behavior.
- Unified
HttpApiEndpoint.transformSuccessandtransformErrorinto a singletransformResponseSchemathat handles both buffered and stream bodies nested insideWithHeaders. - Simplified
HttpApiBuilder.toResponseSchemaby extractingwithHeadersTransformationand caching response schemas byWithHeadersinstance or AST. - Removed redundant client-side success re-bucketing in
HttpApiClient.makeClient;successesare already keyed by wrapper-first status fromHttpApi.extractResponseContent. - Simplified
HttpApiSchema.getResponseEncodingSchemaandOpenApi.extractResponseBodiesso header collection is centralized and the multipart guard remains ingetResponseEncoding.
Prior Pullfrog review threads remain resolved, and the targeted validation passes: pnpm check, pnpm lint, runtime tests (vitest --run --project effect test/unstable/httpapi/ test/unstable/http/HttpServerResponse.test.ts — 150 passed), pnpm doctest --run packages/effect/src/unstable/httpapi/HttpApiSchema.ts (3 passed), and the affected type tests (HttpApiSchema.tst.ts, HttpApiEndpoint.tst.ts, HttpApiClient.tst.ts — 226 passed / 494 assertions across TS 5.9 and 6.0).
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the final commit 4a0a24483 ensures WithHeaders response headers are stringified even when endpoint codecs are disabled.
- Updated
HttpApiBuilder.makeWithHeadersEncoderto encode the union of declared success header schemas throughSchema.toCodecStringTree, sodisableCodecs: truestill turns non-string header values into strings before they reach the HTTP response. - Updated
HttpApiBuilder.toResponseErrorSchemato apply the same string-tree encoding when encoding directWithHeaderserror responses. - Added a builder test covering both success and error
WithHeadersresponses withdisableCodecs: true, asserting the encoded header values are strings.
All prior Pullfrog review threads remain resolved, and the incremental diff introduces no new behavioral concerns.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
Expand the Setting Response Headers section with WithHeaders and encodeToWithHeaders usage, and keep the pre-response handler as the untyped escape hatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Summary
HttpApiSchema.WithHeadersand brandedwithHeadersresponse values for typed success headers across handlers and generated clients.HttpApiSchema.encodeToWithHeadersfor folding response headers into domain types such as error classes.HttpApiBuilder,HttpApiClient, andHttpApiTest, including mixed-success dispatch and user-managed codecs.content-typeandcontent-lengthvalues applied throughHttpServerResponse.setHeaderorsetHeadersoverride body-derived values.Closes EFF-369
Validation
pnpm checkpnpm lintpnpm vitest --run --project effect test/unstable/httpapi/ test/unstable/http/HttpServerResponse.test.ts(148 passed)pnpm doctest --run packages/effect/src/unstable/httpapi/HttpApiSchema.ts(3 passed)pnpm docgenreaches only the pre-existingHttpStaticServerexample error for the missingHttpPlatform.compressionfield; the new examples typecheck and execute successfully.