You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
5.1 KiB

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<Module>) => Kernel,
after: (other?: Instantiable<Module>) => 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<Module>
*/
protected preflight: Collection<Module> = new Collection<Module>()
/**
* Module considered to be the main handler.
* @type Module
*/
protected inflight?: Module
/**
* Collection of postflight modules to apply.
* @type Collection<Module>
*/
protected postflight: Collection<Module> = new Collection<Module>()
/**
* Handle the incoming request, applying the preflight modules, inflight module, then postflight modules.
* @param {Request} request
* @return Promise<Request>
*/
public async handle(request: Request): Promise<Request> {
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>} module
* @return ModuleRegistrationFluency
*/
public register(module: Instantiable<Module>): ModuleRegistrationFluency {
this.make(Logging).verbose(`Registering HTTP kernel module: ${module.name}`)
return {
before: (other?: Instantiable<Module>): 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<Module>): 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
},
}
}
}