import Module from './Module.ts' import Instantiable from '../../../../di/src/type/Instantiable.ts' import AppClass from '../../lifecycle/AppClass.ts' import {Collection} from '../../collection/Collection.ts' import {Service} from '../../../../di/src/decorator/Service.ts' import {Request} from '../Request.ts' import {Logging} from '../../service/logging/Logging.ts' /** * Interface for fluently registering kernel modules into the kernel. */ export interface ModuleRegistrationFluency { before: (other?: Instantiable) => Kernel, after: (other?: Instantiable) => Kernel, first: () => Kernel, last: () => Kernel, core: () => Kernel, } /** * Error thrown when a kernel module is requested that does not exist w/in the kernel. * @extends Error */ export class KernelModuleNotFoundError extends Error { constructor(mod_name: string) { super(`The kernel module ${mod_name} is not registered with the kernel.`) } } /** * A basic HTTP kernel used to process incoming and outgoing middleware. * @extends AppClass */ @Service() export default class Kernel extends AppClass { /** * Collection of preflight modules to apply. * @type Collection */ protected preflight: Collection = new Collection() /** * Module considered to be the main handler. * @type Module */ protected inflight?: Module /** * Collection of postflight modules to apply. * @type Collection */ protected postflight: Collection = new Collection() /** * Handle the incoming request, applying the preflight modules, inflight module, then postflight modules. * @param {Request} request * @return Promise */ public async handle(request: Request): Promise { const logger = this.make(Logging) for ( const module of this.preflight.toArray() ) { logger.verbose(`Applying pre-flight HTTP kernel module: ${module.constructor.name}`) request = await module.apply(request) } if ( this.inflight ) { logger.verbose(`Applying core HTTP kernel module: ${this.inflight.constructor.name}`) request = await this.inflight.apply(request) } for ( const module of this.postflight.toArray() ) { logger.verbose(`Applying post-flight HTTP kernel module: ${module.constructor.name}`) request = await module.apply(request) } return request } /** * Get a fluent interface for registering the given kernel module. * @param {Instantiable} module * @return ModuleRegistrationFluency */ public register(module: Instantiable): ModuleRegistrationFluency { this.make(Logging).verbose(`Registering HTTP kernel module: ${module.name}`) return { before: (other?: Instantiable): Kernel => { if ( !other ) { this.preflight = this.preflight.push(this.make(module)) return this } let found_index = this.preflight.find((mod: Module) => mod instanceof other) if ( typeof found_index !== 'undefined' ) { this.preflight = this.preflight.put(found_index, this.make(module)) return this } else { found_index = this.postflight.find((mod: Module) => mod instanceof other) } if ( typeof found_index !== 'undefined' ) { this.postflight = this.postflight.put(found_index, this.make(module)) } else { throw new KernelModuleNotFoundError(other.name) } return this }, after: (other?: Instantiable): Kernel => { if ( !other ) { this.postflight = this.postflight.push(this.make(module)) return this } let found_index = this.preflight.find((mod: Module) => mod instanceof other) if ( typeof found_index !== 'undefined' ) { this.preflight = this.preflight.put(found_index + 1, this.make(module)) return this } else { found_index = this.postflight.find((mod: Module) => mod instanceof other) } if ( typeof found_index !== 'undefined' ) { this.postflight = this.postflight.put(found_index + 1, this.make(module)) } else { throw new KernelModuleNotFoundError(other.name) } return this }, first: (): Kernel => { this.preflight = this.preflight.put(0, this.make(module)) return this }, last: (): Kernel => { this.postflight = this.postflight.push(this.make(module)) return this }, core: (): Kernel => { this.inflight = this.make(module) return this }, } } }