@halsp/core
Version:
面向云的现代渐进式轻量 Node.js 框架
279 lines (246 loc) • 7.3 kB
text/typescript
import { isPromise } from "util/types";
import { Context, Response } from "./context";
import { BaseLogger, ILogger } from "./logger";
import {
Middleware,
MiddlewareItem,
invokeMiddlewares,
MiddlewareConstructor,
LambdaMiddleware,
} from "./middlewares";
import { Register } from "./register";
import { ObjectConstructor } from "./utils";
import { HookManager } from "./hook/hook.manager";
import { HookType, MdHook } from "./hook";
import {
execBeginingHooks,
execContextHooks,
execInitializationHooks,
} from "./hook/hook.exec";
export class Startup {
constructor() {
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = "production";
}
}
readonly #mds: MiddlewareItem[] = [];
use(lambda: (ctx: Context, next: () => Promise<void>) => Promise<void>): this;
use(lambda: (ctx: Context, next: () => Promise<void>) => void): this;
use(
lambda:
| ((ctx: Context, next: () => Promise<void>) => void)
| ((ctx: Context, next: () => Promise<void>) => Promise<void>),
): this {
this.#mds.push(() => new LambdaMiddleware(lambda));
return this;
}
add(
builder: (ctx: Context) => Middleware,
type?: MiddlewareConstructor,
): this;
add(
builder: (ctx: Context) => Promise<Middleware>,
type?: MiddlewareConstructor,
): this;
add(
builder: (ctx: Context) => MiddlewareConstructor,
type?: MiddlewareConstructor,
): this;
add(
builder: (ctx: Context) => Promise<MiddlewareConstructor>,
type?: MiddlewareConstructor,
): this;
add(md: Middleware): this;
add(md: MiddlewareConstructor): this;
add(
md:
| ((ctx: Context) => Middleware)
| ((ctx: Context) => Promise<Middleware>)
| ((ctx: Context) => MiddlewareConstructor)
| ((ctx: Context) => Promise<MiddlewareConstructor>)
| Middleware
| MiddlewareConstructor,
type?: MiddlewareConstructor,
): this {
if (type) {
this.#mds.push([md as any, type]);
} else {
this.#mds.push(md);
}
return this;
}
hook<T extends Middleware = Middleware>(
type: HookType.Constructor,
mh: (ctx: Context, middlewareConstructor: ObjectConstructor<T>) => T | void,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
type: HookType.Constructor,
mh: (
ctx: Context,
middlewareConstructor: ObjectConstructor<T>,
) => Promise<T | void>,
isGlobal?: true,
): this;
hook<T extends Error = Error>(
type: HookType.Error,
mh: (ctx: Context, middleware: Middleware, error: T) => boolean,
isGlobal?: true,
): this;
hook<T extends Error = Error>(
type: HookType.Error,
mh: (ctx: Context, middleware: Middleware, error: T) => Promise<boolean>,
isGlobal?: true,
): this;
hook<T extends Error = Error>(
type: HookType.Unhandled,
mh: (ctx: Context, middleware: Middleware, error: T) => void,
isGlobal?: true,
): this;
hook<T extends Error = Error>(
type: HookType.Unhandled,
mh: (ctx: Context, middleware: Middleware, error: T) => Promise<void>,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
type: HookType.BeforeInvoke | HookType.BeforeNext,
mh: (ctx: Context, middleware: T) => boolean | void,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
type: HookType.BeforeInvoke | HookType.BeforeNext,
mh: (ctx: Context, middleware: T) => Promise<boolean | void>,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
type: HookType.AfterInvoke,
mh: (ctx: Context, middleware: T) => void,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
type: HookType.AfterInvoke,
mh: (ctx: Context, middleware: T) => Promise<void>,
isGlobal?: true,
): this;
hook(type: HookType.Begining, mh: (ctx: Context) => boolean | void): this;
hook(
type: HookType.Begining,
mh: (ctx: Context) => Promise<boolean | void>,
): this;
hook(type: HookType.Context, mh: (args: any[]) => Context | void): this;
hook(
type: HookType.Context,
mh: (...args: any[]) => Promise<Context | void>,
): this;
hook(type: HookType.Initialization, mh: (args: any[]) => void): this;
hook(type: HookType.Initialization, mh: (args: any[]) => Promise<void>): this;
hook<T extends Middleware = Middleware>(
mh: (ctx: Context, middleware: T) => void,
isGlobal?: true,
): this;
hook<T extends Middleware = Middleware>(
mh: (ctx: Context, middleware: T) => Promise<void>,
isGlobal?: true,
): this;
hook(
arg1: MdHook | HookType,
arg2?: MdHook | HookType | true,
arg3?: true,
): this {
let mh: MdHook;
let type: HookType;
let isGlobal: true | undefined;
if (typeof arg1 == "function") {
mh = arg1;
type = HookType.BeforeInvoke;
isGlobal = arg2 as true | undefined;
} else {
type = arg1;
mh = arg2 as MdHook;
isGlobal = arg3 as true | undefined;
}
if (
type == HookType.Context ||
type == HookType.Begining ||
type == HookType.Initialization
) {
isGlobal = true;
}
if (isGlobal) {
HookManager.addGlobalHook(this, { hook: mh, type: type });
} else {
this.use(async (ctx, next) => {
HookManager.addHook(ctx, { hook: mh, type: type });
await next();
});
}
return this;
}
protected async invoke(...args: any[]): Promise<Response> {
const ctx = await execContextHooks(this, args);
Object.defineProperty(ctx, "startup", {
configurable: true,
get: () => this,
});
if (!this.#mds.length) {
return ctx.res;
}
if (false == (await execBeginingHooks(ctx))) {
return ctx.res;
}
await invokeMiddlewares(ctx, this.#mds, true);
return ctx.res;
}
protected async initialize(...args: any[]) {
await execInitializationHooks(this, args);
}
logger: ILogger = new BaseLogger();
public extend<T extends keyof this>(name: T, fn: (typeof this)[T]): this {
const beforeFn = this[name] as any;
this[name as string] = (...args: any[]) => {
let beforeResult: any;
if (beforeFn) {
beforeResult = beforeFn.call(this, ...args);
}
let currentResult = (fn as any).call(this, ...args);
if (!isPromise(beforeResult) && !isPromise(currentResult)) {
return currentResult ?? beforeResult;
}
return new Promise(async (resolve, reject) => {
if (isPromise(beforeResult)) {
beforeResult = await beforeResult.catch((err) => {
reject(err);
});
}
if (isPromise(currentResult)) {
currentResult = await currentResult.catch((err) => {
reject(err);
});
}
resolve(currentResult ?? beforeResult);
});
};
return this;
}
public call(when: (startup: this) => boolean, fn: (startup: this) => void) {
if (!when(this)) {
return this;
}
fn(this);
return this;
}
#registers: Register[] = [];
get registers() {
return this.#registers;
}
public register(
pattern: string,
handler?: (ctx: Context) => Promise<void> | void,
) {
this.#registers.push({
pattern,
handler,
});
return this;
}
}