From f869e8de36f32a7cecd1731301da953fde83c623 Mon Sep 17 00:00:00 2001 From: Stephen Bluck Date: Wed, 15 May 2024 13:40:48 +0100 Subject: [PATCH] add toWebHandlerLayer to loaders/actions --- app/services.server/Remix.ts | 169 ++++++++++++++++++++++++----------- 1 file changed, 117 insertions(+), 52 deletions(-) diff --git a/app/services.server/Remix.ts b/app/services.server/Remix.ts index c425383..2d43aee 100644 --- a/app/services.server/Remix.ts +++ b/app/services.server/Remix.ts @@ -7,7 +7,12 @@ import { MigratorLive } from "./Database"; import { Password } from "./Password"; import { Users } from "./Users"; -const AppLayer = Layer.mergeAll(Auth.layer, Users.layer, Password.layer, MigratorLive); +const AppLayer = Layer.mergeAll( + Auth.layer, + Users.layer, + Password.layer, + MigratorLive +); const runtime = ManagedRuntime.make(AppLayer); @@ -18,18 +23,17 @@ const Params = Context.GenericTag("@services/Params"); type AppEnv = Layer.Layer.Success; -type RequestEnv = HttpServer.request.ServerRequest | Params | Session; +type RequestEnv = Layer.Layer.Success>; -export interface RemixHandler extends - Effect.Effect< +export interface RemixHandler + extends Effect.Effect< HttpServer.response.ServerResponse, E | HttpServer.response.ServerResponse, R | AppEnv | RequestEnv - > -{} + > {} -export const makeServerContext = ( - args: LoaderFunctionArgs | ActionFunctionArgs, +export const makeRequestContext = ( + args: LoaderFunctionArgs | ActionFunctionArgs ) => Layer.provideMerge( Session.layer, @@ -37,26 +41,51 @@ export const makeServerContext = ( Context.empty().pipe( Context.add( HttpServer.request.ServerRequest, - HttpServer.request.fromWeb(args.request), + HttpServer.request.fromWeb(args.request) ), - Context.add(Params, args.params), - ), - ), + Context.add(Params, args.params) + ) + ) ); -export const loader = - (effect: RemixHandler) => async (args: LoaderFunctionArgs) => - effect.pipe( - Effect.map(HttpServer.response.toWeb), - Effect.provide(makeServerContext(args)), - runtime.runPromise, +export const loader = ( + effect: RemixHandler +) => { + const webHandler = effect.pipe( + Effect.map((response) => + HttpServer.app.toWebHandlerLayer(response, AppLayer) + ) + ); + + return async (args: LoaderFunctionArgs) => { + const { close, handler } = await webHandler.pipe( + Effect.provide(makeRequestContext(args)), + runtime.runPromise ); -export const unwrapLoader = ( - effect: Effect.Effect, E2, R2>, + process.on("SIGTERM", () => { + close().then(() => process.exit(0)); + }); + + process.on("SIGINT", () => { + close().then(() => process.exit(0)); + }); + + return handler(args.request); + }; +}; + +export const unwrapLoader = < + E1, + R1 extends AppEnv | RequestEnv, + E2, + R2 extends AppEnv +>( + effect: Effect.Effect, E2, R2> ) => { const awaitedHandler = runtime.runPromise(effect).then(action); - return async (args: LoaderFunctionArgs) => awaitedHandler.then(handler => handler(args)); + return async (args: LoaderFunctionArgs) => + awaitedHandler.then((handler) => handler(args)); }; // >, AEff>( @@ -70,99 +99,135 @@ export const loaderGen: { // eslint-disable-next-line @typescript-eslint/no-explicit-any >>( f: ( - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: LoaderFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any - >>( + < + Self, + Eff extends Utils.YieldWrap> + >( self: Self, f: ( this: Self, - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: LoaderFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any } = (...args: [any]) => loader(Effect.gen(...args)); export const unwrapLoaderGen: { // eslint-disable-next-line @typescript-eslint/no-explicit-any - >, AEff extends RemixHandler>( + < + Eff extends Utils.YieldWrap>, + AEff extends RemixHandler + >( f: ( - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: LoaderFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any < Self, Eff extends Utils.YieldWrap>, - AEff extends RemixHandler, + AEff extends RemixHandler >( self: Self, f: ( this: Self, - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: LoaderFunctionArgs) => Promise; } = (...args: [any]) => unwrapLoader(Effect.gen(...args) as any); -export const action = - (effect: RemixHandler) => async (args: ActionFunctionArgs) => - effect.pipe( - Effect.map(HttpServer.response.toWeb), - Effect.provide(makeServerContext(args)), - runtime.runPromise, +export const action = ( + effect: RemixHandler +) => { + const webHandler = effect.pipe( + Effect.map((response) => + HttpServer.app.toWebHandlerLayer(response, AppLayer) + ) + ); + + return async (args: ActionFunctionArgs) => { + const { close, handler } = await webHandler.pipe( + Effect.provide(makeRequestContext(args)), + runtime.runPromise ); -export const unwrapAction = ( - effect: Effect.Effect, E2, R2>, + process.on("SIGTERM", () => { + close().then(() => process.exit(0)); + }); + + process.on("SIGINT", () => { + close().then(() => process.exit(0)); + }); + + return handler(args.request); + }; +}; +export const unwrapAction = < + E1, + R1 extends AppEnv | RequestEnv, + E2, + R2 extends AppEnv +>( + effect: Effect.Effect, E2, R2> ) => { const awaitedHandler = runtime.runPromise(effect).then(action); - return async (args: LoaderFunctionArgs) => awaitedHandler.then(handler => handler(args)); + return async (args: LoaderFunctionArgs) => + awaitedHandler.then((handler) => handler(args)); }; export const actionGen: { // eslint-disable-next-line @typescript-eslint/no-explicit-any >>( f: ( - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: ActionFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any - >>( + < + Self, + Eff extends Utils.YieldWrap> + >( self: Self, f: ( this: Self, - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: ActionFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any } = (...args: [any]) => action(Effect.gen(...args)); export const unwrapActionGen: { // eslint-disable-next-line @typescript-eslint/no-explicit-any - >, AEff extends RemixHandler>( + < + Eff extends Utils.YieldWrap>, + AEff extends RemixHandler + >( f: ( - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: ActionFunctionArgs) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any < Self, Eff extends Utils.YieldWrap>, - AEff extends RemixHandler, + AEff extends RemixHandler >( self: Self, f: ( this: Self, - resume: Effect.Adapter, + resume: Effect.Adapter // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) => Generator, + ) => Generator ): (args: ActionFunctionArgs) => Promise; } = (...args: [any]) => unwrapAction(Effect.gen(...args) as any);