Http Kernel!
This commit is contained in:
parent
60bb9afa29
commit
b9f2f844f3
@ -2,3 +2,5 @@ export { default as ConfigUnit } from '../../lib/src/unit/Config.ts'
|
||||
export { DatabaseUnit } from '../../orm/src/DatabaseUnit.ts'
|
||||
export { default as ControllerUnit } from '../../lib/src/unit/Controllers.ts'
|
||||
export { default as MiddlewareUnit } from '../../lib/src/unit/Middlewares.ts'
|
||||
export { default as RoutesUnit } from '../../lib/src/unit/Routes.ts'
|
||||
export { default as HttpKernelUnit } from '../../lib/src/unit/HttpKernel.ts'
|
||||
|
@ -1,4 +1,9 @@
|
||||
export default {
|
||||
port: 8080,
|
||||
use_ssl: false,
|
||||
|
||||
powered_by: {
|
||||
enable: true,
|
||||
text: 'Daton',
|
||||
},
|
||||
}
|
||||
|
5
app/http/middleware/Test.middleware.ts
Normal file
5
app/http/middleware/Test.middleware.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import Middleware from '../../../lib/src/http/Middleware.ts'
|
||||
|
||||
export default class TestMiddleware extends Middleware {
|
||||
|
||||
}
|
11
app/units.ts
11
app/units.ts
@ -1,8 +1,17 @@
|
||||
import {ConfigUnit, DatabaseUnit, ControllerUnit, MiddlewareUnit} from './bundle/daton_units.ts'
|
||||
import {
|
||||
ConfigUnit,
|
||||
DatabaseUnit,
|
||||
ControllerUnit,
|
||||
MiddlewareUnit,
|
||||
RoutesUnit,
|
||||
HttpKernelUnit
|
||||
} from './bundle/daton_units.ts'
|
||||
|
||||
export default [
|
||||
ConfigUnit,
|
||||
DatabaseUnit,
|
||||
HttpKernelUnit,
|
||||
MiddlewareUnit,
|
||||
ControllerUnit,
|
||||
RoutesUnit,
|
||||
]
|
||||
|
@ -205,6 +205,17 @@ class Collection<T> {
|
||||
return new Collection(this._items.filter(func))
|
||||
}
|
||||
|
||||
find<T2>(func: KeyFunction<T, T2>): number | undefined {
|
||||
let found_index: number | undefined = undefined
|
||||
this._items.some((item, index) => {
|
||||
if ( func(item, index) ) {
|
||||
found_index = index
|
||||
return true
|
||||
}
|
||||
})
|
||||
return found_index
|
||||
}
|
||||
|
||||
when<T2>(bool: boolean, then: CollectionFunction<T, T2>): Collection<T> {
|
||||
if ( bool ) then(this)
|
||||
return this
|
||||
|
1
lib/src/external/std.ts
vendored
1
lib/src/external/std.ts
vendored
@ -2,3 +2,4 @@ export * from 'https://deno.land/std@0.53.0/fmt/colors.ts'
|
||||
export { config as dotenv } from 'https://deno.land/x/dotenv/mod.ts'
|
||||
export * as path from 'https://deno.land/std@0.53.0/path/mod.ts'
|
||||
export * as fs from 'https://deno.land/std@0.53.0/fs/mod.ts'
|
||||
export { generate as uuid } from 'https://deno.land/std/uuid/v4.ts'
|
||||
|
@ -1,4 +1,5 @@
|
||||
import AppClass from '../lifecycle/AppClass.ts'
|
||||
|
||||
export default class Controller {
|
||||
export default class Controller extends AppClass {
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Injectable } from '../../../di/src/decorator/Injection.ts'
|
||||
import { getCookies, setCookie, delCookie, ServerRequest } from '../external/http.ts'
|
||||
import { InMemCache } from '../support/InMemCache.ts'
|
||||
import { HTTPRequest } from './type/HTTPRequest.ts'
|
||||
import {logger} from "../service/logging/global.ts";
|
||||
|
||||
export interface Cookie {
|
||||
key: string,
|
||||
@ -23,10 +24,15 @@ export class CookieJar {
|
||||
this._parsed = getCookies(this.request.to_native)
|
||||
}
|
||||
|
||||
public async get_raw(key: string): Promise<string | undefined> {
|
||||
return this._parsed[key]
|
||||
}
|
||||
|
||||
public async get(key: string): Promise<MaybeCookie> {
|
||||
// Try the cache
|
||||
if ( await this._cache.has(key) )
|
||||
return this._cache.fetch(key)
|
||||
if ( await this._cache.has(key) ) {
|
||||
return JSON.parse((await this._cache.fetch(key)) || '""') as Cookie
|
||||
}
|
||||
|
||||
// If the cache missed, try to parse it and load in cache
|
||||
if ( key in this._parsed ) {
|
||||
@ -41,7 +47,7 @@ export class CookieJar {
|
||||
original_value: this._parsed[key],
|
||||
}
|
||||
|
||||
await this._cache.put(key, cookie)
|
||||
await this._cache.put(key, JSON.stringify(cookie))
|
||||
return cookie
|
||||
}
|
||||
}
|
||||
@ -54,7 +60,7 @@ export class CookieJar {
|
||||
original_value,
|
||||
}
|
||||
|
||||
await this._cache.put(key, value)
|
||||
await this._cache.put(key, JSON.stringify(cookie))
|
||||
setCookie(this.request.response, { name: key, value: original_value })
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
export class Middleware {
|
||||
import AppClass from '../lifecycle/AppClass.ts'
|
||||
|
||||
export default class Middleware extends AppClass {
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { HTTPResponse } from './type/HTTPResponse.ts'
|
||||
import { HTTPRequest } from './type/HTTPRequest.ts'
|
||||
import { ServerRequest } from '../external/http.ts'
|
||||
import {CookieJar} from "./CookieJar.ts";
|
||||
import {HTTPResponse} from './type/HTTPResponse.ts'
|
||||
import {HTTPRequest} from './type/HTTPRequest.ts'
|
||||
import {ServerRequest} from '../external/http.ts'
|
||||
import {CookieJar} from './CookieJar.ts'
|
||||
|
||||
export class Response implements HTTPResponse {
|
||||
public status = 200
|
||||
|
93
lib/src/http/kernel/Kernel.ts
Normal file
93
lib/src/http/kernel/Kernel.ts
Normal file
@ -0,0 +1,93 @@
|
||||
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'
|
||||
|
||||
export interface ModuleRegistrationFluency {
|
||||
before: (other?: Instantiable<Module>) => Kernel,
|
||||
after: (other?: Instantiable<Module>) => Kernel,
|
||||
first: () => Kernel,
|
||||
last: () => Kernel,
|
||||
}
|
||||
|
||||
export class KernelModuleNotFoundError extends Error {
|
||||
constructor(mod_name: string) {
|
||||
super(`The kernel module ${mod_name} is not registered with the kernel.`)
|
||||
}
|
||||
}
|
||||
|
||||
@Service()
|
||||
export default class Kernel extends AppClass {
|
||||
protected preflight: Collection<Module> = new Collection<Module>()
|
||||
protected postflight: Collection<Module> = new Collection<Module>()
|
||||
|
||||
public async handle(request: Request): Promise<Request> {
|
||||
for ( const module of this.preflight.toArray() ) {
|
||||
request = await module.apply(request)
|
||||
}
|
||||
|
||||
for ( const module of this.postflight.toArray() ) {
|
||||
request = await module.apply(request)
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
public register(module: Instantiable<Module>): ModuleRegistrationFluency {
|
||||
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))
|
||||
} 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))
|
||||
} 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 {
|
||||
console.log(this.preflight, this.postflight)
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
16
lib/src/http/kernel/Module.ts
Normal file
16
lib/src/http/kernel/Module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {Request} from '../Request.ts'
|
||||
import Kernel from './Kernel.ts'
|
||||
|
||||
export default class Module {
|
||||
public async match(request: Request): Promise<boolean> {
|
||||
return true
|
||||
}
|
||||
|
||||
public async apply(request: Request): Promise<Request> {
|
||||
return request
|
||||
}
|
||||
|
||||
public static register(kernel: Kernel) {
|
||||
kernel.register(this).before()
|
||||
}
|
||||
}
|
16
lib/src/http/kernel/module/PrepareRequest.ts
Normal file
16
lib/src/http/kernel/module/PrepareRequest.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Module from '../Module.ts'
|
||||
import Kernel from '../Kernel.ts'
|
||||
import {Request} from '../../Request.ts'
|
||||
|
||||
export default class PrepareRequest extends Module {
|
||||
|
||||
public static register(kernel: Kernel) {
|
||||
kernel.register(this).first()
|
||||
}
|
||||
|
||||
public async apply(request: Request): Promise<Request> {
|
||||
await request.prepare()
|
||||
return request
|
||||
}
|
||||
|
||||
}
|
24
lib/src/http/kernel/module/SetDatonHeaders.ts
Normal file
24
lib/src/http/kernel/module/SetDatonHeaders.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import Module from '../Module.ts'
|
||||
import Kernel from '../Kernel.ts'
|
||||
import {Request} from '../../Request.ts'
|
||||
import {Injectable} from '../../../../../di/src/decorator/Injection.ts'
|
||||
import Config from '../../../unit/Config.ts'
|
||||
|
||||
@Injectable()
|
||||
export default class SetDatonHeaders extends Module {
|
||||
public static register(kernel: Kernel) {
|
||||
kernel.register(this).after()
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected readonly config: Config,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async apply(request: Request): Promise<Request> {
|
||||
const text = this.config.get('server.powered_by.text', 'Daton')
|
||||
request.response.headers.set('X-Powered-By', text)
|
||||
return request
|
||||
}
|
||||
}
|
28
lib/src/http/kernel/module/SetSessionCookie.ts
Normal file
28
lib/src/http/kernel/module/SetSessionCookie.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import Module from '../Module.ts'
|
||||
import Kernel from '../Kernel.ts'
|
||||
import {Request} from '../../Request.ts'
|
||||
import PrepareRequest from './PrepareRequest.ts'
|
||||
import Utility from '../../../service/utility/Utility.ts'
|
||||
import {Injectable} from '../../../../../di/src/decorator/Injection.ts'
|
||||
|
||||
@Injectable()
|
||||
export default class SetSessionCookie extends Module {
|
||||
|
||||
public static register(kernel: Kernel) {
|
||||
kernel.register(this).after(PrepareRequest)
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected utility: Utility,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async apply(request: Request): Promise<Request> {
|
||||
if ( !(await request.cookies.has('daton.session')) ) {
|
||||
await request.cookies.set('daton.session', `${this.utility.uuid()}-${this.utility.uuid()}`)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
}
|
77
lib/src/http/type/RouterDefinition.ts
Normal file
77
lib/src/http/type/RouterDefinition.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {logger} from '../../service/logging/global.ts'
|
||||
|
||||
export type RouteVerb = 'get' | 'post' | 'patch' | 'delete' | 'head' | 'put' | 'connect' | 'options' | 'trace'
|
||||
export type RouteVerbGroup = { [key: string]: string | string[] }
|
||||
|
||||
export interface RouterDefinition {
|
||||
prefix?: string,
|
||||
middleware?: string[],
|
||||
get?: RouteVerbGroup,
|
||||
post?: RouteVerbGroup,
|
||||
patch?: RouteVerbGroup,
|
||||
delete?: RouteVerbGroup,
|
||||
head?: RouteVerbGroup,
|
||||
put?: RouteVerbGroup,
|
||||
connect?: RouteVerbGroup,
|
||||
options?: RouteVerbGroup,
|
||||
trace?: RouteVerbGroup,
|
||||
}
|
||||
|
||||
export function isRouteVerb(something: any): something is RouteVerb {
|
||||
const route_verbs = ['get', 'post', 'patch', 'delete', 'head', 'put', 'connect', 'options', 'trace']
|
||||
return route_verbs.includes(something)
|
||||
}
|
||||
|
||||
export function isRouteVerbGroup(something: any): something is RouteVerbGroup {
|
||||
if ( !(typeof something === 'object' ) ) return false
|
||||
for ( const key in something ) {
|
||||
if ( !something.hasOwnProperty(key) ) continue
|
||||
if ( typeof key !== 'string' ) {
|
||||
logger.debug(`Route verb group key is not a string: ${key}`)
|
||||
return false
|
||||
}
|
||||
if (
|
||||
!(typeof something[key] === 'string')
|
||||
&& !(Array.isArray(something[key]) && something[key].every((x: any) => typeof x === 'string'))
|
||||
) {
|
||||
logger.info(`Route verb group for key ${key} is not a string or array of strings.`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function isRouterDefinition(something: any): something is RouterDefinition {
|
||||
if ( !(typeof something === 'object') ) {
|
||||
logger.debug('Routing definition is not an object.')
|
||||
return false
|
||||
}
|
||||
for ( const key in something ) {
|
||||
if (!something.hasOwnProperty(key)) continue
|
||||
if ( key === 'prefix' ) {
|
||||
if ( typeof something[key] !== 'string' ) {
|
||||
logger.debug(`Invalid route prefix: ${something[key]}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
else if ( key === 'middleware' ) {
|
||||
if ( !Array.isArray(something[key]) ) {
|
||||
logger.debug('Middleware is not an array.')
|
||||
return false
|
||||
}
|
||||
else if ( !something[key].every((x: any) => typeof x === 'string') ) {
|
||||
logger.debug('Middleware array contains non-string values.')
|
||||
return false
|
||||
}
|
||||
} else if ( isRouteVerb(key) ) {
|
||||
if ( !isRouteVerbGroup(something[key as any]) ) {
|
||||
logger.debug('Verb group value is not a valid route verb group.')
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
logger.debug(`Invalid key: ${key}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
22
lib/src/lifecycle/AppClass.ts
Normal file
22
lib/src/lifecycle/AppClass.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import Instantiable from '../../../di/src/type/Instantiable.ts'
|
||||
import {DependencyKey} from '../../../di/src/type/DependencyKey.ts'
|
||||
import {make} from '../../../di/src/global.ts'
|
||||
import Application from '../lifecycle/Application.ts'
|
||||
|
||||
export default class AppClass {
|
||||
protected static make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
||||
return make(target, ...parameters)
|
||||
}
|
||||
|
||||
protected static get app() {
|
||||
return make(Application)
|
||||
}
|
||||
|
||||
protected make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
||||
return make(target, ...parameters)
|
||||
}
|
||||
|
||||
protected get app() {
|
||||
return make(Application)
|
||||
}
|
||||
}
|
@ -3,12 +3,13 @@ import { Collection } from '../collection/Collection.ts'
|
||||
import {container, make} from '../../../di/src/global.ts'
|
||||
import {DependencyKey} from '../../../di/src/type/DependencyKey.ts'
|
||||
import Instantiable, {isInstantiable} from '../../../di/src/type/Instantiable.ts'
|
||||
import AppClass from './AppClass.ts'
|
||||
|
||||
const isLifecycleUnit = (something: any): something is (typeof LifecycleUnit) => {
|
||||
return isInstantiable(something) && something.prototype instanceof LifecycleUnit
|
||||
}
|
||||
|
||||
export default abstract class LifecycleUnit {
|
||||
export default abstract class LifecycleUnit extends AppClass {
|
||||
private _status = Status.Stopped
|
||||
|
||||
public get status() {
|
||||
@ -34,8 +35,4 @@ export default abstract class LifecycleUnit {
|
||||
}
|
||||
return new Collection<typeof LifecycleUnit>()
|
||||
}
|
||||
|
||||
protected make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
||||
return make(target, ...parameters)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Service } from '../../../../di/src/decorator/Service.ts'
|
||||
import { Logging } from '../logging/Logging.ts'
|
||||
import {uuid} from '../../external/std.ts'
|
||||
|
||||
@Service()
|
||||
export default class Utility {
|
||||
@ -52,4 +53,8 @@ export default class Utility {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
uuid(): string {
|
||||
return uuid()
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,31 @@
|
||||
import {Service} from '../../../di/src/decorator/Service.ts'
|
||||
|
||||
export type CanonicalResolver = (key: string) => any
|
||||
import {Canonical} from './Canonical.ts'
|
||||
|
||||
export class DuplicateResolverKeyError extends Error {
|
||||
constructor(key: string) {
|
||||
super(`There is already a canonical resource with the scope ${key} registered.`)
|
||||
super(`There is already a canonical unit with the scope ${key} registered.`)
|
||||
}
|
||||
}
|
||||
|
||||
export class NoSuchCanonicalResolverKeyError extends Error {
|
||||
constructor(key: string) {
|
||||
super(`There is no such canonical unit with the scope ${key} registered.`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Service()
|
||||
export class Canon {
|
||||
protected resources: { [key: string]: any } = {}
|
||||
protected resources: { [key: string]: Canonical<any> } = {}
|
||||
|
||||
get(key: string): any {
|
||||
const key_parts = key.split('::')
|
||||
let desc_value = this.resources
|
||||
key_parts.forEach(part => {
|
||||
if ( typeof desc_value === 'function' ) {
|
||||
desc_value = desc_value(part)
|
||||
} else {
|
||||
desc_value = desc_value[part]
|
||||
}
|
||||
})
|
||||
return desc_value
|
||||
resource<T>(key: string): Canonical<T> {
|
||||
if ( !this.resources[key] ) throw new NoSuchCanonicalResolverKeyError(key)
|
||||
return this.resources[key] as Canonical<T>
|
||||
}
|
||||
|
||||
register_resource(scope: string, resolver: CanonicalResolver) {
|
||||
if ( this.resources[scope] ) {
|
||||
throw new DuplicateResolverKeyError(scope)
|
||||
}
|
||||
|
||||
this.resources[scope] = resolver
|
||||
register_canonical(unit: Canonical<any>) {
|
||||
const key = unit.canonical_items
|
||||
if ( this.resources[key] ) throw new DuplicateResolverKeyError(key)
|
||||
this.resources[key] = unit
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,7 @@ export class Canonical<T> extends LifecycleUnit {
|
||||
const def = await this._get_canonical_definition(entry.path)
|
||||
this._items[def.canonical_name] = await this.init_canonical_item(def)
|
||||
}
|
||||
|
||||
this.make(Canon).register_resource(this.canonical_items, (key: string) => this.get(key))
|
||||
this.make(Canon).register_canonical(this)
|
||||
}
|
||||
|
||||
public async init_canonical_item(definition: CanonicalDefinition): Promise<T> {
|
||||
|
28
lib/src/unit/HttpKernel.ts
Normal file
28
lib/src/unit/HttpKernel.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import LifecycleUnit from "../lifecycle/Unit.ts";
|
||||
import {Unit} from "../lifecycle/decorators.ts";
|
||||
import Kernel from "../http/kernel/Kernel.ts";
|
||||
import PrepareRequest from "../http/kernel/module/PrepareRequest.ts";
|
||||
import SetSessionCookie from "../http/kernel/module/SetSessionCookie.ts";
|
||||
import Config from "./Config.ts";
|
||||
import SetDatonHeaders from "../http/kernel/module/SetDatonHeaders.ts";
|
||||
|
||||
@Unit()
|
||||
export default class HttpKernel extends LifecycleUnit {
|
||||
|
||||
constructor(
|
||||
protected readonly kernel: Kernel,
|
||||
protected readonly config: Config,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async up() {
|
||||
PrepareRequest.register(this.kernel)
|
||||
SetSessionCookie.register(this.kernel)
|
||||
|
||||
if ( this.config.get('server.powered_by.enable') ) {
|
||||
SetDatonHeaders.register(this.kernel)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { InstantiableCanonical } from './InstantiableCanonical.ts'
|
||||
import { CanonicalDefinition } from './Canonical.ts'
|
||||
import { Middleware } from '../http/Middleware.ts'
|
||||
import Middleware from '../http/Middleware.ts'
|
||||
import { Unit } from '../lifecycle/decorators.ts'
|
||||
|
||||
@Unit()
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {Canonical} from './Canonical.ts'
|
||||
|
||||
export class RecursiveCanonical extends Canonical<any> {
|
||||
public get(key: string): any | undefined {
|
||||
public get(key: string, fallback?: any): any | undefined {
|
||||
const parts = key.split('.')
|
||||
let current_value = this._items
|
||||
for ( const part of parts ) {
|
||||
current_value = current_value?.[part]
|
||||
}
|
||||
return current_value
|
||||
return current_value ?? fallback
|
||||
}
|
||||
}
|
||||
|
16
lib/src/unit/Routes.ts
Normal file
16
lib/src/unit/Routes.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {Canonical, CanonicalDefinition} from './Canonical.ts'
|
||||
import {isRouterDefinition, RouterDefinition} from '../http/type/RouterDefinition.ts'
|
||||
|
||||
export default class Routes extends Canonical<RouterDefinition> {
|
||||
protected base_path = './app/http/routes'
|
||||
protected canonical_item = 'route_group'
|
||||
protected suffix = '.routes.ts'
|
||||
|
||||
public async init_canonical_item(def: CanonicalDefinition): Promise<RouterDefinition> {
|
||||
const item = await super.init_canonical_item(def)
|
||||
if ( !isRouterDefinition(item) ) {
|
||||
throw new TypeError(`Invalid routes definition: ${def.original_name}.`)
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user