diff --git a/bun.lockb b/bun.lockb index 22706bc..a1e2534 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 2545760..8550066 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,11 @@ "@effect/schema": "^0.72.0", "@prisma/studio-server": "^0.502.0", "@tanstack/form-core": "^0.30.0", + "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", "bun-types": "^1.1.26", "effect": "^3.7.0", + "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "mobx": "^6.13.1", "npm-check-updates": "^17.1.1", diff --git a/src/Layers/express/ExpressApp.ts b/src/Layers/express/ExpressApp.ts new file mode 100644 index 0000000..bcf72a6 --- /dev/null +++ b/src/Layers/express/ExpressApp.ts @@ -0,0 +1,19 @@ +import { Config, Context, Effect, Layer } from "effect" +import type { Express } from "express" + + +export class ExpressApp extends Context.Tag("ExpressApp")() {} + + +const importExpress = Effect.tryPromise({ + try: () => import("express"), + catch: cause => new Error("Could not import 'express'. Make sure it is installed.", { cause }), +}) + +export const ExpressAppLive = (config: { + readonly trustProxy?: Config.Config +}) => Layer.effect(ExpressApp, Effect.gen(function*() { + const app = (yield* importExpress).default() + app.set("trust proxy", yield* config.trustProxy || Config.succeed(false)) + return app +})) diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts new file mode 100644 index 0000000..c634979 --- /dev/null +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -0,0 +1,67 @@ +import { Config, Context, Effect, Layer, Match } from "effect" +import type { Server } from "node:http" +import type { AddressInfo } from "node:net" +import { ExpressApp } from "./ExpressApp" + + +export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")() {} + + +const importNodeHTTP = Effect.tryPromise({ + try: () => import("node:http"), + catch: cause => new Error("Could not import 'node:http'. Make sure you are using a runtime that implements Node APIs.", { cause }), +}) + +const serverListeningMessage = Match.type().pipe( + Match.when(Match.null, () => "HTTP server listening"), + Match.when(Match.string, v => `HTTP server listening on ${ v }`), + Match.orElse(v => `HTTP server listening on ${ v.address }:${ v.port }`), +) + +export const ExpressNodeHTTPServerLive = (config: { + readonly backlog?: Config.Config + readonly exclusive?: Config.Config + readonly host?: Config.Config + readonly ipv6Only?: Config.Config + readonly path?: Config.Config + readonly port?: Config.Config + readonly readableAll?: Config.Config + readonly signal?: AbortSignal + readonly writableAll?: Config.Config +}) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease( + Effect.gen(function*() { + const app = yield* ExpressApp + const http = yield* importNodeHTTP + + const options = { + backlog: yield* config.backlog || Config.succeed(undefined), + exclusive: yield* config.exclusive || Config.succeed(undefined), + host: yield* config.host || Config.succeed(undefined), + ipv6Only: yield* config.ipv6Only || Config.succeed(undefined), + path: yield* config.path || Config.succeed(undefined), + port: yield* config.port || Config.succeed(undefined), + readableAll: yield* config.readableAll || Config.succeed(undefined), + signal: config.signal, + writableAll: yield* config.writableAll || Config.succeed(undefined), + } as const + + return yield* Effect.async(resume => { + const server = http.createServer(app).listen(options, + () => resume( + Effect.succeed(server).pipe( + Effect.tap(Effect.logInfo( + serverListeningMessage(server.address()) + )) + ) + ) + ) + }) + }), + + server => Effect.gen(function*() { + yield* Effect.logInfo("HTTP server is stopping. Waiting for existing connections to end...") + yield* Effect.async(resume => { + server.close(() => resume(Effect.logInfo("HTTP server closed"))) + }) + }), +)) diff --git a/src/Layers/express/index.ts b/src/Layers/express/index.ts new file mode 100644 index 0000000..22f20a7 --- /dev/null +++ b/src/Layers/express/index.ts @@ -0,0 +1,2 @@ +export * from "./ExpressApp" +export * from "./ExpressNodeHTTPServer"