Commit 467ade8b authored by nanahira's avatar nanahira

add ProtoMiddlewareDispatcher

parent 9e394bd4
import { MiddlewareDispatcher } from '../middleware-dispatcher';
import { MiddlewareDispatcher } from '../middleware-dispatcher/middleware-dispatcher';
import { parseI18n } from '../utility/parse-i18n';
import { I18nMiddleware, I18nOptions } from './types';
import { patchStringInObject } from '../patch-string-in-object';
......
import { Awaitable } from './types';
type AnyFunc = (...args: any[]) => any;
type MiddlewareValue<F extends AnyFunc> = Awaited<ReturnType<F>>;
type MiddlewareResult<F extends AnyFunc> = Promise<MiddlewareValue<F>>;
type MiddlewareNext<F extends AnyFunc> = () => MiddlewareResult<F>;
type MiddlewareArgs<F extends AnyFunc> = [
...args: Parameters<F>,
next: MiddlewareNext<F>,
];
type MiddlewareReturn<F extends AnyFunc> = Awaitable<MiddlewareValue<F>>;
export type Middleware<F extends AnyFunc> = (
...args: MiddlewareArgs<F>
) => MiddlewareReturn<F>;
export type MiddlewareAcceptResult<F extends AnyFunc> = (
s: MiddlewareValue<F>,
) => Awaitable<boolean>;
export type MiddlewareErrorHandler<F extends AnyFunc> = (
e: any,
args: Parameters<F>,
next: MiddlewareNext<F>,
) => Awaitable<MiddlewareValue<F>>;
export interface MiddlewareDispatcherOptions<F extends AnyFunc> {
acceptResult?: MiddlewareAcceptResult<F>;
errorHandler?: MiddlewareErrorHandler<F>;
}
export class MiddlewareDispatcher<F extends AnyFunc> {
constructor(private options: MiddlewareDispatcherOptions<F> = {}) {}
middlewares: Middleware<F>[] = [];
middleware(mw: Middleware<F>, prior = false) {
if (prior) {
this.middlewares.unshift(mw);
} else {
this.middlewares.push(mw);
}
return this;
}
removeMiddleware(mw: Middleware<F>) {
const index = this.middlewares.indexOf(mw);
if (index >= 0) {
this.middlewares.splice(index, 1);
}
return this;
import {
AnyFunc,
MiddlewareDispatcherOptions,
Middleware,
MiddlewareResult,
MiddlewareAcceptResult,
MiddlewareErrorHandler,
MiddlewareReturn,
} from './types';
export class DynamicMiddlewareDispatcher<F extends AnyFunc> {
constructor(protected options: MiddlewareDispatcherOptions<F> = {}) {}
async buildMiddlewares(...args: Parameters<F>): Promise<Middleware<F>[]> {
return [];
}
dispatch(...args: Parameters<F>): MiddlewareResult<F> {
const mws = this.middlewares;
async dispatch(...args: Parameters<F>): MiddlewareResult<F> {
const mws = await this.buildMiddlewares(...args);
const acceptResult: MiddlewareAcceptResult<F> =
this.options.acceptResult || ((res) => res != null);
const errorHandler: MiddlewareErrorHandler<F> =
......
export * from './types';
export * from './middleware-dispatcher';
export * from './dynamic-middleware-dispatcher';
export * from './proto-middleware-dispatcher';
import { AnyFunc, Middleware } from './types';
import { DynamicMiddlewareDispatcher } from './dynamic-middleware-dispatcher';
export class MiddlewareDispatcher<
F extends AnyFunc,
> extends DynamicMiddlewareDispatcher<F> {
middlewares: Middleware<F>[] = [];
middleware(mw: Middleware<F>, prior = false) {
if (prior) {
this.middlewares.unshift(mw);
} else {
this.middlewares.push(mw);
}
return this;
}
removeMiddleware(mw: Middleware<F>) {
const index = this.middlewares.indexOf(mw);
if (index >= 0) {
this.middlewares.splice(index, 1);
}
return this;
}
async buildMiddlewares(...args: Parameters<F>): Promise<Middleware<F>[]> {
return this.middlewares;
}
}
import { Middleware, MiddlewareResult } from './types';
import { AnyClass, ClassType } from '../types';
import { DynamicMiddlewareDispatcher } from './dynamic-middleware-dispatcher';
export type ProtoMiddlewareArgs<A extends any[], T = any> = [
...args: A,
inst: T,
];
export type ProtoMiddlewareFunc<A extends any[], T = any> = (
...args: ProtoMiddlewareArgs<A, T>
) => T;
export class ProtoMiddlewareDispatcher<
A extends any[],
> extends DynamicMiddlewareDispatcher<ProtoMiddlewareFunc<A>> {
private middlewareProtoMap = new Map<
AnyClass,
Middleware<ProtoMiddlewareFunc<A>>[]
>();
private middlewareProtoMapPrior = new Map<
AnyClass,
Middleware<ProtoMiddlewareFunc<A>>[]
>();
middleware<T>(
cls: ClassType<T>,
mw: Middleware<ProtoMiddlewareFunc<A, T>>,
prior = false,
) {
const map = prior ? this.middlewareProtoMapPrior : this.middlewareProtoMap;
const mws = map.get(cls) || [];
mws.push(mw);
map.set(cls, mws);
return this;
}
removeMiddleware<T>(
cls: ClassType<T>,
mw: Middleware<ProtoMiddlewareFunc<A, T>>,
) {
for (const map of [this.middlewareProtoMap, this.middlewareProtoMapPrior]) {
const mws = map.get(cls);
if (mws) {
const index = mws.indexOf(mw);
if (index >= 0) {
mws.splice(index, 1);
}
}
}
return this;
}
async dispatch<T>(
...args: ProtoMiddlewareArgs<A, T>
): MiddlewareResult<ProtoMiddlewareFunc<A, T>> {
return super.dispatch(...args);
}
async buildMiddlewares(...args: ProtoMiddlewareArgs<A>) {
// buildMiddlewares 只需要知道 inst
if (args.length === 0) return [];
const inst = args[args.length - 1];
if (!inst || typeof inst !== 'object') return [];
// 1. 收集原型链(Base → Sub)
const chain: AnyClass[] = [];
let cur: any = inst.constructor;
while (cur && cur !== Object) {
chain.push(cur);
cur = Object.getPrototypeOf(cur.prototype)?.constructor;
}
chain.reverse();
const result: Middleware<ProtoMiddlewareFunc<A>>[] = [];
// 2. prior:Base → Sub
for (const cls of chain) {
const mws = this.middlewareProtoMapPrior.get(cls);
if (mws) {
result.push(...mws);
}
}
// 3. normal:Sub → Base
for (let i = chain.length - 1; i >= 0; i--) {
const cls = chain[i];
const mws = this.middlewareProtoMap.get(cls);
if (mws) {
result.push(...mws);
}
}
return result;
}
}
import { Awaitable } from '../types';
export type AnyFunc = (...args: any[]) => any;
export type MiddlewareValue<F extends AnyFunc> = Awaited<ReturnType<F>>;
export type MiddlewareResult<F extends AnyFunc> = Promise<MiddlewareValue<F>>;
export type MiddlewareNext<F extends AnyFunc> = () => MiddlewareResult<F>;
export type MiddlewareArgs<F extends AnyFunc> = [
...args: Parameters<F>,
next: MiddlewareNext<F>,
];
export type MiddlewareReturn<F extends AnyFunc> = Awaitable<MiddlewareValue<F>>;
export type Middleware<F extends AnyFunc> = (
...args: MiddlewareArgs<F>
) => MiddlewareReturn<F>;
export type MiddlewareAcceptResult<F extends AnyFunc> = (
s: MiddlewareValue<F>,
) => Awaitable<boolean>;
export type MiddlewareErrorHandler<F extends AnyFunc> = (
e: any,
args: Parameters<F>,
next: MiddlewareNext<F>,
) => Awaitable<MiddlewareValue<F>>;
export interface MiddlewareDispatcherOptions<F extends AnyFunc> {
acceptResult?: MiddlewareAcceptResult<F>;
errorHandler?: MiddlewareErrorHandler<F>;
}
export type Awaitable<T> = T | Promise<T>;
export type AnyClass = new (...args: any[]) => any;
export type ClassType<T = any> = new (...args: any[]) => T;
// i18n-lookup.spec.ts
import { I18nLookupMiddleware } from '../src/i18n';
import { MiddlewareDispatcher } from '../src/middleware-dispatcher';
import { MiddlewareDispatcher } from '../src/middleware-dispatcher/middleware-dispatcher';
type F = (locale: string, key: string) => string | undefined;
......
import { ProtoMiddlewareDispatcher } from '../src/middleware-dispatcher';
describe('ProtoMiddlewareDispatcher', () => {
class Base {}
class Sub extends Base {}
it('builds middlewares by prototype chain (prior: Base->Sub, normal: Sub->Base)', async () => {
const d = new ProtoMiddlewareDispatcher<[res: number]>();
const order: string[] = [];
d.middleware(
Base,
async (x, inst, next) => {
order.push('base:prior');
return next();
},
true,
);
d.middleware(
Sub,
async (x, inst, next) => {
order.push('sub:prior');
return next();
},
true,
);
d.middleware(Sub, async (x, inst, next) => {
order.push('sub:normal');
return next();
});
d.middleware(Base, async (x, inst) => {
order.push('base:normal');
return x * 10;
});
const res = await d.dispatch(3, new Sub());
expect(res).toBe(30);
expect(order).toEqual([
'base:prior',
'sub:prior',
'sub:normal',
'base:normal',
]);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment