Scratch space for learning atproto app development
1import { createHttpTerminator } from 'http-terminator' 2import { once } from 'node:events' 3import type { 4 IncomingMessage, 5 RequestListener, 6 ServerResponse, 7} from 'node:http' 8import { createServer } from 'node:http' 9 10export type NextFunction = (err?: unknown) => void 11 12export type Middleware< 13 Req extends IncomingMessage = IncomingMessage, 14 Res extends ServerResponse<Req> = ServerResponse<Req>, 15> = (req: Req, res: Res, next: NextFunction) => void 16 17export type Handler< 18 Req extends IncomingMessage = IncomingMessage, 19 Res extends ServerResponse<Req> = ServerResponse<Req>, 20> = (req: Req, res: Res) => unknown | Promise<unknown> 21/** 22 * Wraps a request handler middleware to ensure that `next` is called if it 23 * throws or returns a promise that rejects. 24 */ 25export function handler< 26 Req extends IncomingMessage = IncomingMessage, 27 Res extends ServerResponse<Req> = ServerResponse<Req>, 28>(fn: Handler<Req, Res>): Middleware<Req, Res> { 29 return async (req, res, next) => { 30 try { 31 await fn(req, res) 32 } catch (err) { 33 next(err) 34 } 35 } 36} 37 38/** 39 * Create an HTTP server with the provided request listener, ensuring that it 40 * can bind the listening port, and returns a termination function that allows 41 * graceful termination of HTTP connections. 42 */ 43export async function startServer( 44 requestListener: RequestListener, 45 { 46 port, 47 gracefulTerminationTimeout, 48 }: { port?: number; gracefulTerminationTimeout?: number } = {}, 49) { 50 const server = createServer(requestListener) 51 const { terminate } = createHttpTerminator({ 52 gracefulTerminationTimeout, 53 server, 54 }) 55 server.listen(port) 56 await once(server, 'listening') 57 return { server, terminate } 58}