44import * as Context from "effect/Context"
55import * as Effect from "effect/Effect"
66import { identity } from "effect/Function"
7+ import { globalValue } from "effect/GlobalValue"
78import * as Option from "effect/Option"
89import * as ParseResult from "effect/ParseResult"
910import type * as Predicate from "effect/Predicate"
@@ -15,12 +16,14 @@ import * as HttpApi from "./HttpApi.js"
1516import type { HttpApiEndpoint } from "./HttpApiEndpoint.js"
1617import type { HttpApiGroup } from "./HttpApiGroup.js"
1718import * as HttpApiSchema from "./HttpApiSchema.js"
19+ import * as HttpBody from "./HttpBody.js"
1820import * as HttpClient from "./HttpClient.js"
1921import * as HttpClientError from "./HttpClientError.js"
2022import * as HttpClientRequest from "./HttpClientRequest.js"
2123import * as HttpClientResponse from "./HttpClientResponse.js"
2224import * as HttpMethod from "./HttpMethod.js"
2325import type { HttpApiMiddleware } from "./index.js"
26+ import * as UrlParams from "./UrlParams.js"
2427
2528/**
2629 * @since 1.0.0
@@ -160,8 +163,13 @@ const makeClient = <Groups extends HttpApiGroup.Any, ApiError, ApiR>(
160163 successes . forEach ( ( ast , status ) => {
161164 decodeMap [ status ] = ast . _tag === "None" ? responseAsVoid : schemaToResponse ( ast . value )
162165 } )
163- const encodePayload = endpoint . payloadSchema . pipe (
164- Option . map ( Schema . encodeUnknown )
166+ const encodePayloadBody = endpoint . payloadSchema . pipe (
167+ Option . map ( ( schema ) => {
168+ if ( HttpMethod . hasBody ( endpoint . method ) ) {
169+ return Schema . encodeUnknown ( payloadSchemaBody ( schema as any ) )
170+ }
171+ return Schema . encodeUnknown ( schema )
172+ } )
165173 )
166174 const encodeHeaders = endpoint . headersSchema . pipe (
167175 Option . map ( Schema . encodeUnknown )
@@ -180,13 +188,16 @@ const makeClient = <Groups extends HttpApiGroup.Any, ApiError, ApiR>(
180188 let httpRequest = HttpClientRequest . make ( endpoint . method ) (
181189 request && request . path ? makeUrl ( request . path ) : endpoint . path
182190 )
183- if ( request . payload instanceof FormData ) {
191+ if ( request && request . payload instanceof FormData ) {
184192 httpRequest = HttpClientRequest . bodyFormData ( httpRequest , request . payload )
185- } else if ( encodePayload . _tag === "Some" ) {
186- const payload = yield * encodePayload . value ( request . payload )
187- httpRequest = HttpMethod . hasBody ( endpoint . method )
188- ? yield * Effect . orDie ( HttpClientRequest . bodyJson ( httpRequest , payload ) )
189- : HttpClientRequest . setUrlParams ( httpRequest , payload as any )
193+ } else if ( encodePayloadBody . _tag === "Some" ) {
194+ if ( HttpMethod . hasBody ( endpoint . method ) ) {
195+ const body = ( yield * encodePayloadBody . value ( request . payload ) ) as HttpBody . HttpBody
196+ httpRequest = HttpClientRequest . setBody ( httpRequest , body )
197+ } else {
198+ const urlParams = ( yield * encodePayloadBody . value ( request . payload ) ) as Record < string , string >
199+ httpRequest = HttpClientRequest . setUrlParams ( httpRequest , urlParams )
200+ }
190201 }
191202 if ( encodeHeaders . _tag === "Some" ) {
192203 httpRequest = HttpClientRequest . setHeaders (
@@ -416,3 +427,58 @@ const statusCodeError = (response: HttpClientResponse.HttpClientResponse) =>
416427 )
417428
418429const responseAsVoid = ( _response : HttpClientResponse . HttpClientResponse ) => Effect . void
430+
431+ const HttpBodyFromSelf = Schema . declare ( HttpBody . isHttpBody )
432+
433+ const payloadSchemaBody = ( schema : Schema . Schema . All ) : Schema . Schema < any , HttpBody . HttpBody > => {
434+ const members = schema . ast . _tag === "Union" ? schema . ast . types : [ schema . ast ]
435+ return Schema . Union ( ...members . map ( bodyFromPayload ) ) as any
436+ }
437+
438+ const bodyFromPayloadCache = globalValue (
439+ "@effect/platform/HttpApiClient/bodyFromPayloadCache" ,
440+ ( ) => new WeakMap < AST . AST , Schema . Schema . Any > ( )
441+ )
442+
443+ const bodyFromPayload = ( ast : AST . AST ) => {
444+ if ( bodyFromPayloadCache . has ( ast ) ) {
445+ return bodyFromPayloadCache . get ( ast ) !
446+ }
447+ const schema = Schema . make ( ast )
448+ const encoding = HttpApiSchema . getEncoding ( ast )
449+ const transform = Schema . transformOrFail (
450+ HttpBodyFromSelf ,
451+ schema ,
452+ {
453+ decode ( fromA , _ , ast ) {
454+ return ParseResult . fail ( new ParseResult . Forbidden ( ast , fromA , "encode only schema" ) )
455+ } ,
456+ encode ( toI , _ , ast ) {
457+ switch ( encoding . kind ) {
458+ case "Json" : {
459+ return HttpBody . json ( toI ) . pipe (
460+ ParseResult . mapError ( ( error ) => new ParseResult . Type ( ast , toI , `Could not encode as JSON: ${ error } ` ) )
461+ )
462+ }
463+ case "Text" : {
464+ if ( typeof toI !== "string" ) {
465+ return ParseResult . fail ( new ParseResult . Type ( ast , toI , "Expected a string" ) )
466+ }
467+ return ParseResult . succeed ( HttpBody . text ( toI ) )
468+ }
469+ case "UrlParams" : {
470+ return ParseResult . succeed ( HttpBody . urlParams ( UrlParams . fromInput ( toI as any ) ) )
471+ }
472+ case "Uint8Array" : {
473+ if ( ! ( toI instanceof Uint8Array ) ) {
474+ return ParseResult . fail ( new ParseResult . Type ( ast , toI , "Expected a Uint8Array" ) )
475+ }
476+ return ParseResult . succeed ( HttpBody . uint8Array ( toI ) )
477+ }
478+ }
479+ }
480+ }
481+ )
482+ bodyFromPayloadCache . set ( ast , transform )
483+ return transform
484+ }
0 commit comments