0.1.17 (#18)
Co-authored-by: Julien Valverdé <julien.valverde@mailo.com> Reviewed-on: #18
This commit was merged in pull request #18.
This commit is contained in:
22
src/Express/ExpressApp.ts
Normal file
22
src/Express/ExpressApp.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Config, Context, Effect, Layer } from "effect"
|
||||
import type { Express } from "express"
|
||||
import { ImportError } from "../ImportError"
|
||||
|
||||
|
||||
export class ExpressApp extends Context.Tag("ExpressApp")<ExpressApp, Express>() {}
|
||||
|
||||
|
||||
const importExpress = Effect.tryPromise({
|
||||
try: () => import("express"),
|
||||
catch: cause => new ImportError({ path: "express", cause }),
|
||||
})
|
||||
|
||||
export const ExpressAppLive = (
|
||||
config: {
|
||||
readonly trustProxy?: Config.Config<boolean | undefined>
|
||||
} = {}
|
||||
) => Layer.effect(ExpressApp, Effect.gen(function*() {
|
||||
const app = (yield* importExpress).default()
|
||||
app.set("trust proxy", (yield* config.trustProxy ?? Config.succeed(undefined)) ?? false)
|
||||
return app
|
||||
}))
|
||||
70
src/Express/ExpressNodeHTTPServer.ts
Normal file
70
src/Express/ExpressNodeHTTPServer.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Config, Context, Effect, Layer, Match } from "effect"
|
||||
import type { Server } from "node:http"
|
||||
import type { AddressInfo } from "node:net"
|
||||
import { ImportError } from "../ImportError"
|
||||
import { ExpressApp } from "./ExpressApp"
|
||||
|
||||
|
||||
export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")<ExpressNodeHTTPServer, Server>() {}
|
||||
|
||||
|
||||
const importNodeHTTP = Effect.tryPromise({
|
||||
try: () => import("node:http"),
|
||||
catch: cause => new ImportError({ path: "node:http", cause }),
|
||||
})
|
||||
|
||||
const serverListeningMessage = Match.type<AddressInfo | string | null>().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.address }:${ v.port }`),
|
||||
)
|
||||
|
||||
export const ExpressNodeHTTPServerLive = (
|
||||
config: {
|
||||
readonly backlog?: Config.Config<number | undefined>
|
||||
readonly exclusive?: Config.Config<boolean | undefined>
|
||||
readonly host?: Config.Config<string | undefined>
|
||||
readonly ipv6Only?: Config.Config<boolean | undefined>
|
||||
readonly path?: Config.Config<string | undefined>
|
||||
readonly port?: Config.Config<number | undefined>
|
||||
readonly readableAll?: Config.Config<boolean | undefined>
|
||||
readonly signal?: AbortSignal
|
||||
readonly writableAll?: Config.Config<boolean | undefined>
|
||||
} = {}
|
||||
) => 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<Server>(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")))
|
||||
})
|
||||
}),
|
||||
))
|
||||
17
src/Express/example.ts
Normal file
17
src/Express/example.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Effect, Layer } from "effect"
|
||||
import { ExpressAppLive } from "./ExpressApp"
|
||||
import { ExpressNodeHTTPServerLive } from "./ExpressNodeHTTPServer"
|
||||
|
||||
|
||||
const AppLive = ExpressAppLive()
|
||||
const HTTPServerLive = ExpressNodeHTTPServerLive()
|
||||
|
||||
const ServerLive = Layer.empty.pipe(
|
||||
Layer.provideMerge(HTTPServerLive),
|
||||
Layer.provideMerge(AppLive),
|
||||
)
|
||||
|
||||
Layer.launch(ServerLive).pipe(
|
||||
Effect.scoped,
|
||||
Effect.runPromise,
|
||||
)
|
||||
2
src/Express/index.ts
Normal file
2
src/Express/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * as ExpressApp from "./ExpressApp"
|
||||
export * as ExpressNodeHTTPServer from "./ExpressNodeHTTPServer"
|
||||
Reference in New Issue
Block a user