11/**
22 * @since 1.4.0
33 */
4- import type { HttpApi , HttpRouter } from "@effect/platform" ;
4+ import type { HttpApi , HttpRouter , HttpServerError } from "@effect/platform" ;
55import { HttpApiBuilder , HttpApp } from "@effect/platform" ;
6- import type { Context as LambdaContext } from "aws-lambda" ;
7- import type { Context } from "effect" ;
8- import { Effect , Function , Layer } from "effect" ;
6+ import type { Cause } from "effect" ;
7+ import { Context , Effect , Function , Layer } from "effect" ;
98import { getEventSource } from "./internal/index.js" ;
109import type { EventSource , LambdaEvent , LambdaResult } from "./internal/types.js" ;
1110import { encodeBase64 , isContentEncodingBinary , isContentTypeBinary } from "./internal/utils.js" ;
@@ -75,174 +74,75 @@ export const make: {
7574 // Deprecated case
7675 if ( globalLayer ) {
7776 const runtime = LambdaRuntime . fromLayer ( globalLayer ) ;
78- return async ( event : T , context : LambdaContext ) => handlerOrOptions ( event , context ) . pipe ( runtime . runPromise ) ;
77+ return async ( event , context ) => handlerOrOptions ( event , context ) . pipe ( runtime . runPromise ) ;
7978 }
8079
81- return async ( event : T , context : LambdaContext ) =>
80+ return async ( event , context ) =>
8281 handlerOrOptions ( event , context ) . pipe ( Effect . runPromise as < A , E > ( effect : Effect . Effect < A , E , R > ) => Promise < A > ) ;
8382 }
8483
85- const runtime = LambdaRuntime . fromLayer ( handlerOrOptions . layer ) ;
86- return async ( event : T , context : LambdaContext ) => handlerOrOptions . handler ( event , context ) . pipe ( runtime . runPromise ) ;
84+ const runtime = LambdaRuntime . fromLayer ( handlerOrOptions . layer , { memoMap : handlerOrOptions . memoMap } ) ;
85+ return async ( event , context ) => handlerOrOptions . handler ( event , context ) . pipe ( runtime . runPromise ) ;
8786} ;
8887
89- // const apiHandler = (options?: {
90- // readonly middleware?: (
91- // httpApp: HttpApp.Default,
92- // ) => HttpApp.Default<
93- // never,
94- // HttpApi.Api | HttpApiBuilder.Router | HttpRouter.HttpRouter.DefaultServices
95- // >;
96- // }): EffectHandler<
97- // LambdaEvent,
98- // HttpApi.Api | HttpApiBuilder.Router | HttpApiBuilder.Middleware | HttpRouter.HttpRouter.DefaultServices,
99- // HttpServerError.ResponseError,
100- // LambdaResult
101- // > =>
102- // (event) =>
103- // Effect.gen(function*() {
104- // const eventSource = getEventSource(event) as EventSource<LambdaEvent, LambdaResult>;
105- // const requestValues = eventSource.getRequest(event);
88+ interface HttpApiOptions {
89+ readonly middleware ?: (
90+ httpApp : HttpApp . Default ,
91+ ) => HttpApp . Default <
92+ never ,
93+ HttpApi . Api | HttpApiBuilder . Router | HttpRouter . HttpRouter . DefaultServices
94+ > ;
95+ readonly memoMap ?: Layer . MemoMap ;
96+ }
10697
107- // const req = new Request(
108- // `https://${requestValues.remoteAddress}${requestValues.path}`,
109- // {
110- // method: requestValues.method,
111- // headers: requestValues.headers,
112- // body: requestValues.body,
113- // },
114- // );
115-
116- // const app = yield* HttpApiBuilder.httpApp;
117-
118- // const appWithMiddleware = options?.middleware ? options.middleware(app as any) : app;
119-
120- // const request = HttpServerRequest.fromWeb(req);
121- // const response = yield* appWithMiddleware.pipe(
122- // Effect.provideService(HttpServerRequest.HttpServerRequest, request),
123- // );
124-
125- // const handler = yield* FiberRef.get(HttpApp.currentPreResponseHandlers);
126-
127- // const resp = Option.isSome(handler) ? yield* handler.value(request, response) : response;
128-
129- // const res = HttpServerResponse.toWeb(resp, { runtime: yield* Effect.runtime() });
130-
131- // const contentType = res.headers.get("content-type");
132- // let isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false;
133-
134- // if (!isBase64Encoded) {
135- // const contentEncoding = res.headers.get("content-encoding");
136- // isBase64Encoded = isContentEncodingBinary(contentEncoding);
137- // }
138-
139- // const body = isBase64Encoded
140- // ? encodeBase64(yield* Effect.promise(() => res.arrayBuffer()))
141- // : yield* Effect.promise(() => res.text());
142-
143- // const headers: Record<string, string> = {};
144-
145- // if (res.headers.has("set-cookie")) {
146- // const cookies = res.headers.getSetCookie
147- // ? res.headers.getSetCookie()
148- // : Array.from((res.headers as any).entries())
149- // .filter(([k]: any) => k === "set-cookie")
150- // .map(([, v]: any) => v);
151-
152- // if (Array.isArray(cookies)) {
153- // headers["set-cookie"] = cookies.join(", ");
154- // res.headers.delete("set-cookie");
155- // }
156- // }
157-
158- // res.headers.forEach((value, key) => {
159- // headers[key] = value;
160- // });
161-
162- // return eventSource.getResponse({
163- // event,
164- // statusCode: res.status,
165- // body,
166- // headers,
167- // isBase64Encoded,
168- // });
169- // });
98+ type WebHandler = ReturnType < typeof HttpApp . toWebHandler > ;
99+ const WebHandler = Context . GenericTag < WebHandler > ( "@effect-aws/lambda/WebHandler" ) ;
170100
171101/**
172- * Construct a lambda handler from an `HttpApi` instance.
173- *
174- * @example
175- * ```ts
176- * import { LambdaHandler } from "@effect-aws/lambda"
177- * import { HttpApi, HttpApiBuilder, HttpServer } from "@effect/platform"
178- * import { Layer } from "effect"
179- *
180- * class MyApi extends HttpApi.make("api") {}
181- *
182- * const MyApiLive = HttpApiBuilder.api(MyApi)
183- *
184- * export const handler = LambdaHandler.fromHttpApi(
185- * Layer.mergeAll(
186- * MyApiLive,
187- * // you could also use NodeHttpServer.layerContext, depending on your
188- * // server's platform
189- * HttpServer.layerContext
190- * )
191- * )
192- * ```
102+ * Construct an `WebHandler` from an `HttpApi` instance.
193103 *
194104 * @since 1.4.0
195105 * @category constructors
196106 */
197- export const fromHttpApi = < LA , LE > (
198- layer : Layer . Layer < LA | HttpApi . Api | HttpRouter . HttpRouter . DefaultServices , LE > ,
199- options ?: {
200- readonly middleware ?: (
201- httpApp : HttpApp . Default ,
202- ) => HttpApp . Default <
203- never ,
204- HttpApi . Api | HttpApiBuilder . Router | HttpRouter . HttpRouter . DefaultServices
205- > ;
206- readonly memoMap ?: Layer . MemoMap ;
207- } ,
208- ) : Handler < LambdaEvent , LambdaResult > => {
209- const runtime = LambdaRuntime . fromLayer (
210- Layer . mergeAll ( layer , HttpApiBuilder . Router . Live , HttpApiBuilder . Middleware . layer ) ,
211- options ,
212- ) ;
213- // // Alternative implementation (I keep it commented here to understand the differences)
214- // const handler = apiHandler(options);
215- // return async (event: LambdaEvent, context: LambdaContext) => handler(event, context).pipe(runtime.runPromise);
216- let handlerCached :
217- | ( ( request : Request , context ?: Context . Context < never > | undefined ) => Promise < Response > )
218- | undefined ;
219-
220- const handlerPromise = Effect . gen ( function * ( ) {
107+ export const makeWebHandler = ( options ?: Pick < HttpApiOptions , "middleware" > ) : Effect . Effect <
108+ WebHandler ,
109+ never ,
110+ HttpApiBuilder . Router | HttpApi . Api | HttpRouter . HttpRouter . DefaultServices | HttpApiBuilder . Middleware
111+ > =>
112+ Effect . gen ( function * ( ) {
221113 const app = yield * HttpApiBuilder . httpApp ;
222- const rt = yield * runtime . runtimeEffect ;
223- const handler = HttpApp . toWebHandlerRuntime ( rt ) (
114+ const rt = yield * Effect . runtime < HttpRouter . HttpRouter . DefaultServices > ( ) ;
115+ return HttpApp . toWebHandlerRuntime ( rt ) (
224116 options ?. middleware ? options . middleware ( app as any ) as any : app ,
225117 ) ;
226- handlerCached = handler ;
227- return handler ;
228- } ) . pipe ( runtime . runPromise ) ;
118+ } ) ;
229119
230- async function handler ( event : LambdaEvent ) {
120+ /**
121+ * Construct an `EffectHandler` from an `HttpApi` instance.
122+ *
123+ * @since 1.4.0
124+ * @category constructors
125+ */
126+ export const httpApiHandler : EffectHandler <
127+ LambdaEvent ,
128+ WebHandler ,
129+ HttpServerError . ResponseError | Cause . UnknownException ,
130+ LambdaResult
131+ > = ( event ) =>
132+ Effect . gen ( function * ( ) {
231133 const eventSource = getEventSource ( event ) as EventSource < LambdaEvent , LambdaResult > ;
232134 const requestValues = eventSource . getRequest ( event ) ;
233135
234- const request = new Request (
235- `http ://${ requestValues . remoteAddress } ${ requestValues . path } ` ,
136+ const req = new Request (
137+ `https ://${ requestValues . remoteAddress } ${ requestValues . path } ` ,
236138 {
237139 method : requestValues . method ,
238140 headers : requestValues . headers ,
239141 body : requestValues . body ,
240142 } ,
241143 ) ;
242144
243- const res = handlerCached !== undefined
244- ? await handlerCached ( request )
245- : await handlerPromise . then ( ( handler ) => handler ( request ) ) ;
145+ const res = yield * WebHandler . pipe ( Effect . andThen ( ( handler ) => handler ( req ) ) ) ;
246146
247147 const contentType = res . headers . get ( "content-type" ) ;
248148 let isBase64Encoded = contentType && isContentTypeBinary ( contentType ) ? true : false ;
@@ -252,7 +152,9 @@ export const fromHttpApi = <LA, LE>(
252152 isBase64Encoded = isContentEncodingBinary ( contentEncoding ) ;
253153 }
254154
255- const body = isBase64Encoded ? encodeBase64 ( await res . arrayBuffer ( ) ) : await res . text ( ) ;
155+ const body = isBase64Encoded
156+ ? encodeBase64 ( yield * Effect . promise ( ( ) => res . arrayBuffer ( ) ) )
157+ : yield * Effect . promise ( ( ) => res . text ( ) ) ;
256158
257159 const headers : Record < string , string > = { } ;
258160
@@ -280,7 +182,40 @@ export const fromHttpApi = <LA, LE>(
280182 headers,
281183 isBase64Encoded,
282184 } ) ;
283- }
185+ } ) ;
284186
285- return handler ;
187+ /**
188+ * Construct a lambda handler from an `HttpApi` instance.
189+ *
190+ * @example
191+ * ```ts
192+ * import { LambdaHandler } from "@effect-aws/lambda"
193+ * import { HttpApi, HttpApiBuilder, HttpServer } from "@effect/platform"
194+ * import { Layer } from "effect"
195+ *
196+ * class MyApi extends HttpApi.make("api") {}
197+ *
198+ * const MyApiLive = HttpApiBuilder.api(MyApi)
199+ *
200+ * export const handler = LambdaHandler.fromHttpApi(
201+ * Layer.mergeAll(
202+ * MyApiLive,
203+ * // you could also use NodeHttpServer.layerContext, depending on your
204+ * // server's platform
205+ * HttpServer.layerContext
206+ * )
207+ * )
208+ * ```
209+ *
210+ * @since 1.4.0
211+ * @category constructors
212+ */
213+ export const fromHttpApi = < LA , LE > (
214+ layer : Layer . Layer < LA | HttpApi . Api | HttpRouter . HttpRouter . DefaultServices , LE > ,
215+ options ?: HttpApiOptions ,
216+ ) : Handler < LambdaEvent , LambdaResult > => {
217+ const httpApiLayer = Layer . effect ( WebHandler , makeWebHandler ( options ) ) . pipe (
218+ Layer . provide ( Layer . mergeAll ( layer , HttpApiBuilder . Router . Live , HttpApiBuilder . Middleware . layer ) ) ,
219+ ) ;
220+ return make ( { handler : httpApiHandler , layer : httpApiLayer , memoMap : options ?. memoMap } ) ;
286221} ;
0 commit comments