diff --git a/patches/effect@4.0.0-rc.109.patch b/patches/effect@4.0.0-rc.109.patch index 00f5b1c..06eee1d 100644 --- a/patches/effect@4.0.0-rc.109.patch +++ b/patches/effect@4.0.0-rc.109.patch @@ -58,6 +58,12 @@ index 9036e8e06ff04fbf3b2e42e085bb8bfcedf16b68..ec31b9ff1eceb5dac5b0954e601037c6 }); }; }; +@@ -109,1 +109,2 @@ export const makeClient = (api, options) => Effect.gen(function* () { + const encodeHeaders = UndefinedOr.map(endpoint.headers, Schema.encodeUnknownEffect); ++ const payloadCanBeOmitted = payloadSchemas.some(schema => HttpApiSchema.isNoContent(schema.ast)); +@@ -121,1 +122,1 @@ export const makeClient = (api, options) => Effect.gen(function* () { +- if (encodePayload !== undefined) { ++ if (encodePayload !== undefined && !(payloadCanBeOmitted && request.payload === undefined)) { diff --git a/dist/unstable/httpapi/HttpApiEndpoint.d.ts b/dist/unstable/httpapi/HttpApiEndpoint.d.ts index e95cfc448c7fd374d1781c59b601dec9703fd9a1..e2047691b17e9a16fbaa86b752161f8c6e89b000 100644 --- a/dist/unstable/httpapi/HttpApiEndpoint.d.ts @@ -119,6 +125,12 @@ index 365329af41ab4f3e4ec462baadf905c31cc24cf2..8c9466bcc496e4e247f2181bcf7d74b5 }) } } +@@ -396,1 +396,2 @@ export const makeClient = HttpApiSchema.isNoContent(schema.ast)) + const encodeHeaders = UndefinedOr.map(endpoint.headers, Schema.encodeUnknownEffect) +@@ -419,1 +420,1 @@ export const makeClient = { ); }); + test("optional request bodies are omitted from the request", async () => { + let received: Request | undefined; + await expect( + runGenerated( + "orderDrafts.createWorkerBootstrap", + ["--input", "-"], + '{"path":{"id":"odft_123"}}', + (input, init) => { + received = new Request(input, init); + return Promise.resolve(orderDraftNotFoundResponse()); + }, + ), + ).rejects.toMatchObject({ + _tag: "GeneratedCommandFailure", + reason: "api", + status: 404, + }); + + expect(received?.headers.get("content-type")).toBeNull(); + expect(await received?.text()).toBe(""); + }); + + test("optional request bodies preserve explicit JSON values", async () => { + let received: Request | undefined; + await expect( + runGenerated( + "orderDrafts.createWorkerBootstrap", + ["--input", "-"], + '{"path":{"id":"odft_123"},"body":{"ttl_seconds":60}}', + (input, init) => { + received = new Request(input, init); + return Promise.resolve(orderDraftNotFoundResponse()); + }, + ), + ).rejects.toMatchObject({ + _tag: "GeneratedCommandFailure", + reason: "api", + status: 404, + }); + + expect(received?.headers.get("content-type")).toContain( + "application/json", + ); + expect(await received?.json()).toEqual({ ttl_seconds: 60 }); + }); + + test("explicit null optional request bodies are rejected before transport", async () => { + let requests = 0; + await expect( + runGenerated( + "orderDrafts.createWorkerBootstrap", + ["--input", "-"], + '{"path":{"id":"odft_123"},"body":null}', + () => { + requests += 1; + return Promise.resolve(Response.json({})); + }, + ), + ).rejects.toMatchObject({ + _tag: "GeneratedCommandFailure", + reason: "input", + }); + + expect(requests).toBe(0); + }); + test("rejects malformed and excess input before transport", async () => { let requests = 0; const transport = () => { @@ -876,3 +942,14 @@ function machineOperation() { completed_at: null, }; } + +function orderDraftNotFoundResponse() { + return Response.json( + { + success: false, + errors: [{ code: 7002, message: "Order draft not found." }], + result: {}, + }, + { status: 404 }, + ); +}