Skip to content

Commit 328a709

Browse files
authored
chore: Introduce middleware support for function patching (#37560)
1 parent d7117ad commit 328a709

3 files changed

Lines changed: 54 additions & 32 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export type * from './definition';
21
export * from './makeFunction';
2+
export * from './midleware';
Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,26 @@
1-
import { addPatch } from './addPatch';
2-
import { calledFunctions, functions } from './data';
3-
import type { BaseFunction, PatchData, PatchFunction, PatchedFunction } from './definition';
1+
import type { BaseFunction, PatchFunction, PatchedFunction } from './definition';
2+
import { withMiddleware } from './midleware';
43

54
export const makeFunction = <T extends BaseFunction>(fn: T): PatchedFunction<T> => {
6-
const patches = new Set<PatchData<T>>();
7-
8-
patches.add({
9-
patchFunction: (_next, ...args) => fn(...args),
10-
});
11-
12-
const result = ((...args: Parameters<T>): ReturnType<T> => {
13-
let newFn: T = fn;
14-
15-
for (const patch of patches) {
16-
if (patch.condition && !patch.condition()) {
17-
continue;
5+
const wrapped = withMiddleware(fn);
6+
const patch = (fn: PatchFunction<T>, condition?: () => boolean) => {
7+
return wrapped.use((ctx, next) => {
8+
if (!condition || condition()) {
9+
return fn(next as unknown as T, ...ctx);
1810
}
19-
20-
const nextFn = newFn;
21-
newFn = ((...args: Parameters<T>) => patch.patchFunction(nextFn, ...args)) as T;
22-
}
23-
24-
calledFunctions.add(result);
25-
return newFn(...args);
26-
}) as PatchedFunction<T>;
27-
28-
functions.set(result, patches as Set<PatchData<BaseFunction>>);
29-
30-
result.patch = (patch: PatchFunction<T>, condition?: () => boolean) => addPatch(result, patch, condition);
31-
32-
result.originalSignature = (() => {
11+
return next(...ctx);
12+
});
13+
};
14+
const originalSignature = (() => {
3315
throw new Error('OriginalSignature of patched functions is not meant to be executed directly.');
3416
}) as unknown as T;
35-
result.patchSignature = (() => {
17+
const patchSignature = (() => {
3618
throw new Error('PatchSignature of patched functions is not meant to be executed directly.');
3719
}) as unknown as PatchFunction<T>;
3820

39-
return result;
21+
return Object.assign(wrapped, {
22+
patch,
23+
originalSignature,
24+
patchSignature,
25+
}) as PatchedFunction<T>;
4026
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
type Middleware<F extends (...args: any[]) => any> = (ctx: Parameters<F>, next: NextFunction<F>) => ReturnType<F>;
2+
type NextFunction<F extends (...args: any[]) => any> = (...args: Parameters<F> | []) => ReturnType<F>;
3+
4+
export function withMiddleware<F extends (...args: any[]) => any>(fn: F) {
5+
const middlewares: Middleware<F>[] = [];
6+
7+
const buildRunner = (): ((ctx: Parameters<F>) => ReturnType<F>) => {
8+
return middlewares.reduce(
9+
(next, middleware) => {
10+
return (ctx) => middleware(ctx, (...args) => next(args.length ? (args as Parameters<F>) : ctx));
11+
},
12+
(ctx: Parameters<F>) => fn(...ctx),
13+
);
14+
};
15+
16+
let runner = buildRunner();
17+
18+
const use = (middleware: Middleware<F>) => {
19+
middlewares.push(middleware);
20+
21+
runner = buildRunner();
22+
return () => {
23+
const index = middlewares.indexOf(middleware);
24+
if (index > -1) {
25+
middlewares.splice(index, 1);
26+
}
27+
runner = buildRunner();
28+
};
29+
};
30+
31+
const run = (...args: Parameters<F>): ReturnType<F> => {
32+
return runner(args);
33+
};
34+
35+
return Object.assign(run as F, { use, fn });
36+
}

0 commit comments

Comments
 (0)