0.1.17 (#18)
All checks were successful
Publish / publish (push) Successful in 13s
Lint / lint (push) Successful in 11s

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:
Julien Valverdé
2024-09-07 20:56:30 +02:00
parent 07578a7ac7
commit 02d8e38f4d
25 changed files with 666 additions and 82 deletions

22
src/Express/ExpressApp.ts Normal file
View 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
}))

View 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
View 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
View File

@@ -0,0 +1,2 @@
export * as ExpressApp from "./ExpressApp"
export * as ExpressNodeHTTPServer from "./ExpressNodeHTTPServer"