From 1902787770eba0bec2481b6423eadea5c5d81cf7 Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:18:18 +0900 Subject: [PATCH] Add HttpClientRequest header combinators --- .changeset/calm-headers-transform.md | 5 +++ packages/platform/src/HttpClientRequest.ts | 18 +++++++++++ .../src/internal/httpClientRequest.ts | 25 +++++++++++++++ packages/platform/test/HttpClient.test.ts | 31 +++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 .changeset/calm-headers-transform.md diff --git a/.changeset/calm-headers-transform.md b/.changeset/calm-headers-transform.md new file mode 100644 index 00000000000..d2ca8e74e3d --- /dev/null +++ b/.changeset/calm-headers-transform.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +Add `HttpClientRequest.removeHeader` and `HttpClientRequest.updateHeaders`. diff --git a/packages/platform/src/HttpClientRequest.ts b/packages/platform/src/HttpClientRequest.ts index c79db130e29..fb72928b984 100644 --- a/packages/platform/src/HttpClientRequest.ts +++ b/packages/platform/src/HttpClientRequest.ts @@ -164,6 +164,24 @@ export const setHeaders: { (self: HttpClientRequest, input: Headers.Input): HttpClientRequest } = internal.setHeaders +/** + * @since 1.0.0 + * @category combinators + */ +export const updateHeaders: { + (f: (headers: Headers.Headers) => Headers.Headers): (self: HttpClientRequest) => HttpClientRequest + (self: HttpClientRequest, f: (headers: Headers.Headers) => Headers.Headers): HttpClientRequest +} = internal.updateHeaders + +/** + * @since 1.0.0 + * @category combinators + */ +export const removeHeader: { + (key: string): (self: HttpClientRequest) => HttpClientRequest + (self: HttpClientRequest, key: string): HttpClientRequest +} = internal.removeHeader + /** * @since 1.0.0 * @category combinators diff --git a/packages/platform/src/internal/httpClientRequest.ts b/packages/platform/src/internal/httpClientRequest.ts index 14fb718c526..f2c8680ef5b 100644 --- a/packages/platform/src/internal/httpClientRequest.ts +++ b/packages/platform/src/internal/httpClientRequest.ts @@ -167,6 +167,31 @@ export const setHeaders = dual< self.body )) +/** @internal */ +export const updateHeaders = dual< + ( + f: (headers: Headers.Headers) => Headers.Headers + ) => (self: ClientRequest.HttpClientRequest) => ClientRequest.HttpClientRequest, + ( + self: ClientRequest.HttpClientRequest, + f: (headers: Headers.Headers) => Headers.Headers + ) => ClientRequest.HttpClientRequest +>(2, (self, f) => + makeInternal( + self.method, + self.url, + self.urlParams, + self.hash, + f(self.headers), + self.body + )) + +/** @internal */ +export const removeHeader = dual< + (key: string) => (self: ClientRequest.HttpClientRequest) => ClientRequest.HttpClientRequest, + (self: ClientRequest.HttpClientRequest, key: string) => ClientRequest.HttpClientRequest +>(2, (self, key) => updateHeaders(self, Headers.remove(key))) + const stringOrRedacted = (value: string | Redacted.Redacted): string => typeof value === "string" ? value : Redacted.value(value) diff --git a/packages/platform/test/HttpClient.test.ts b/packages/platform/test/HttpClient.test.ts index d9dfba4f4f8..c450b4dd54f 100644 --- a/packages/platform/test/HttpClient.test.ts +++ b/packages/platform/test/HttpClient.test.ts @@ -246,6 +246,37 @@ describe("HttpClient", () => { }) }) + describe("ClientRequest headers", () => { + it("removeHeader removes a header without mutating the original request", () => { + const original = HttpClientRequest.get("https://example.com").pipe( + HttpClientRequest.setHeaders({ + "Content-Length": "5", + "X-Test": "ok" + }) + ) + const request = original.pipe(HttpClientRequest.removeHeader("CONTENT-LENGTH")) + + deepStrictEqual({ ...request.headers }, { "x-test": "ok" }) + deepStrictEqual({ ...original.headers }, { "content-length": "5", "x-test": "ok" }) + }) + + it("updateHeaders transforms the complete header set", () => { + const original = HttpClientRequest.get("https://example.com").pipe( + HttpClientRequest.setHeaders({ + "Content-Length": "5", + "X-Test": "ok" + }) + ) + const request = HttpClientRequest.updateHeaders( + original, + (headers) => pipe(headers, Headers.remove("content-length"), Headers.set("x-next", "yes")) + ) + + deepStrictEqual({ ...request.headers }, { "x-test": "ok", "x-next": "yes" }) + deepStrictEqual({ ...original.headers }, { "content-length": "5", "x-test": "ok" }) + }) + }) + it.effect("matchStatus", () => Effect.gen(function*() { const jp = yield* JsonPlaceholder