import * as http from "http"; import * as https from "https"; import * as utils from "./utils"; import { Context, contextFactory } from "./context"; import { Router } from "./router"; import { attachServerExtensions, MicroServer } from "./server-utils"; export interface ItemsContainer { items: Map; getItem(key: string): T; setItem(key: string, value: T): void; hasItem(key: string): boolean; } export interface IApplication extends ItemsContainer { middlewares: Middleware[]; createServer(secureOpts?: https.ServerOptions): MicroServer; createHandler(): (req: http.IncomingMessage, res: http.ServerResponse) => void; use(...middlewares: (Middleware | Router)[]): IApplication; setErrorHandler(handler: ErrorHandler): void; setFallbackHandler(handler: Middleware): void; } export type Middleware = (context: T, next: NextMiddleware) => MiddlewareResult; export type MiddlewareResult = Promise; export type NextMiddleware = () => MiddlewareResult; export type ErrorHandler = (err: Error, req: http.IncomingMessage, res: http.ServerResponse, context?: T) => void; export class Application implements IApplication { middlewares: Middleware[] = []; items: Map; constructor( private _contextFactory: (app: Application, req: http.IncomingMessage, res: http.ServerResponse) => T, private _errorHandler: ErrorHandler = utils.defaultErrorHandler, private _fallbackHandler = utils.defaultFallbackNotFoundHandler) { } createServer(secureOpts?: https.ServerOptions) { let isHttps = secureOpts ? true : false; const server = isHttps ? https.createServer(secureOpts, this.createHandler()) : http.createServer(this.createHandler()); return attachServerExtensions(server); } createHandler(): (req: http.IncomingMessage, res: http.ServerResponse) => void { const fn = utils.compose(...this.middlewares); return (req, res) => { let errorHandler = this._errorHandler || utils.defaultErrorHandler; let fallbackHandler = this._fallbackHandler || utils.defaultFallbackNotFoundHandler; let context: T = null; try { context = this._contextFactory(this, req, res); fn(context, () => fallbackHandler(context, null)) .catch((err) => errorHandler(err, req, res, context)); } catch (err) { errorHandler(err, req, res, context); } }; } use(...middleware: (Middleware | Router)[]): Application { if (!this.middlewares) this.middlewares = []; utils.addMiddlewares(middleware, this.middlewares); return this; } setErrorHandler(handler: ErrorHandler) { this._errorHandler = handler; } setFallbackHandler(handler: Middleware) { this._fallbackHandler = handler; } getItem(key: string): T { if (this.items) { return this.items.get(key); } return null; } setItem(key: string, value: T): void { if (!this.items) { this.items = new Map(); } this.items.set(key, value); } hasItem(key: string): boolean { if (this.items) { return this.items.has(key); } return false; } } export class App extends Application { constructor() { super(contextFactory); } }