From fec985ed5bfff8942fca4c61eadb2b6cb6362107 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 19 Nov 2025 16:32:09 -0300 Subject: [PATCH 1/2] feat: Introduce middleware support for function patching --- packages/patch-injection/src/definition.ts | 2 +- packages/patch-injection/src/index.ts | 4 +- .../patch-injection/src/makeFunction.spec.ts | 6 +-- packages/patch-injection/src/makeFunction.ts | 48 +++++++------------ packages/patch-injection/src/midleware.ts | 36 ++++++++++++++ 5 files changed, 60 insertions(+), 36 deletions(-) create mode 100644 packages/patch-injection/src/midleware.ts diff --git a/packages/patch-injection/src/definition.ts b/packages/patch-injection/src/definition.ts index 85f35752e1415..48a0377d88afa 100644 --- a/packages/patch-injection/src/definition.ts +++ b/packages/patch-injection/src/definition.ts @@ -1,5 +1,5 @@ export type BaseFunction = (...args: any[]) => any; -export type PatchFunction = (next: T, ...args: Parameters) => ReturnType; +export type PatchFunction = (next: () => ReturnType, ...args: Parameters) => ReturnType; export type PatchData = { patchFunction: PatchFunction; condition?: () => boolean; diff --git a/packages/patch-injection/src/index.ts b/packages/patch-injection/src/index.ts index 405790427177d..7bfd782afcd20 100644 --- a/packages/patch-injection/src/index.ts +++ b/packages/patch-injection/src/index.ts @@ -1,2 +1,4 @@ -export * from './definition'; +// export * from './definition'; export * from './makeFunction'; +export * from './midleware'; +// export * from './addPatch'; diff --git a/packages/patch-injection/src/makeFunction.spec.ts b/packages/patch-injection/src/makeFunction.spec.ts index f88efff2be49d..2b14ddba53d9c 100644 --- a/packages/patch-injection/src/makeFunction.spec.ts +++ b/packages/patch-injection/src/makeFunction.spec.ts @@ -133,10 +133,10 @@ describe('Chained calls', () => { it('should send the parameters in the correct order every time', () => { const fn = makeFunction((a: string, b: string) => `3=${[a, b].join('')}`); - fn.patch((next, a, b) => `2=${[a, b].join('')},${next('E', 'F')}`); - fn.patch((next, a, b) => `1=${[a, b].join('')},${next('C', 'D')}`); + fn.patch((next, a, b) => `2=${[a, b].join('')},${next()}`); + fn.patch((next, a, b) => `1=${[a, b].join('')},${next()}`); - expect(fn('A', 'B')).toBe('1=AB,2=CD,3=EF'); + expect(fn('A', 'B')).toBe('1=AB,2=AB,3=AB'); }); }); diff --git a/packages/patch-injection/src/makeFunction.ts b/packages/patch-injection/src/makeFunction.ts index 6bf8e5200a1bf..2d4420ccad989 100644 --- a/packages/patch-injection/src/makeFunction.ts +++ b/packages/patch-injection/src/makeFunction.ts @@ -1,40 +1,26 @@ -import { addPatch } from './addPatch'; -import { calledFunctions, functions } from './data'; -import type { BaseFunction, PatchData, PatchFunction, PatchedFunction } from './definition'; +import type { BaseFunction, PatchFunction, PatchedFunction } from './definition'; +import { withMiddleware } from './midleware'; export const makeFunction = (fn: T): PatchedFunction => { - const patches = new Set>(); - - patches.add({ - patchFunction: (_next, ...args) => fn(...args), - }); - - const result = ((...args: Parameters): ReturnType => { - let newFn: T = fn; - - for (const patch of patches) { - if (patch.condition && !patch.condition()) { - continue; + const wrapped = withMiddleware(fn); + const patch = (patch: PatchFunction, condition?: () => boolean) => { + return wrapped.use((ctx, next) => { + if (!condition || condition()) { + return patch(() => next(), ...ctx); } - - const nextFn = newFn; - newFn = ((...args: Parameters) => patch.patchFunction(nextFn, ...args)) as T; - } - - calledFunctions.add(result); - return newFn(...args); - }) as PatchedFunction; - - functions.set(result, patches as Set>); - - result.patch = (patch: PatchFunction, condition?: () => boolean) => addPatch(result, patch, condition); - - result.originalSignature = (() => { + return next(); + }); + }; + const originalSignature = (() => { throw new Error('OriginalSignature of patched functions is not meant to be executed directly.'); }) as unknown as T; - result.patchSignature = (() => { + const patchSignature = (() => { throw new Error('PatchSignature of patched functions is not meant to be executed directly.'); }) as unknown as PatchFunction; - return result; + return Object.assign(wrapped, { + patch, + originalSignature, + patchSignature, + }) as PatchedFunction; }; diff --git a/packages/patch-injection/src/midleware.ts b/packages/patch-injection/src/midleware.ts new file mode 100644 index 0000000000000..df60489984f9a --- /dev/null +++ b/packages/patch-injection/src/midleware.ts @@ -0,0 +1,36 @@ +type Middleware any> = (ctx: Parameters, next: NextFunction) => ReturnType; +type NextFunction any> = () => ReturnType; + +export function withMiddleware any>(fn: F) { + const middlewares: Middleware[] = []; + + const buildRunner = (): ((ctx: Parameters) => ReturnType) => { + return middlewares.reduce( + (next, middleware) => { + return (ctx) => middleware(ctx, () => next(ctx)); + }, + (ctx: Parameters) => fn(...ctx), + ); + }; + + let runner = buildRunner(); + + const use = (middleware: Middleware) => { + middlewares.push(middleware); + + runner = buildRunner(); + return () => { + const index = middlewares.indexOf(middleware); + if (index > -1) { + middlewares.splice(index, 1); + } + runner = buildRunner(); + }; + }; + + const run = (...args: Parameters): ReturnType => { + return runner(args); + }; + + return Object.assign(run as F, { use, fn }); +} From 3f5c94de878a92e1318c62bdcf21d1f287d405fa Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 24 Dec 2025 00:22:34 -0300 Subject: [PATCH 2/2] refactor: update patch function signature and middleware to allow rewrite args --- packages/patch-injection/src/definition.ts | 2 +- packages/patch-injection/src/makeFunction.spec.ts | 6 +++--- packages/patch-injection/src/makeFunction.ts | 6 +++--- packages/patch-injection/src/midleware.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/patch-injection/src/definition.ts b/packages/patch-injection/src/definition.ts index 48a0377d88afa..85f35752e1415 100644 --- a/packages/patch-injection/src/definition.ts +++ b/packages/patch-injection/src/definition.ts @@ -1,5 +1,5 @@ export type BaseFunction = (...args: any[]) => any; -export type PatchFunction = (next: () => ReturnType, ...args: Parameters) => ReturnType; +export type PatchFunction = (next: T, ...args: Parameters) => ReturnType; export type PatchData = { patchFunction: PatchFunction; condition?: () => boolean; diff --git a/packages/patch-injection/src/makeFunction.spec.ts b/packages/patch-injection/src/makeFunction.spec.ts index 2b14ddba53d9c..f88efff2be49d 100644 --- a/packages/patch-injection/src/makeFunction.spec.ts +++ b/packages/patch-injection/src/makeFunction.spec.ts @@ -133,10 +133,10 @@ describe('Chained calls', () => { it('should send the parameters in the correct order every time', () => { const fn = makeFunction((a: string, b: string) => `3=${[a, b].join('')}`); - fn.patch((next, a, b) => `2=${[a, b].join('')},${next()}`); - fn.patch((next, a, b) => `1=${[a, b].join('')},${next()}`); + fn.patch((next, a, b) => `2=${[a, b].join('')},${next('E', 'F')}`); + fn.patch((next, a, b) => `1=${[a, b].join('')},${next('C', 'D')}`); - expect(fn('A', 'B')).toBe('1=AB,2=AB,3=AB'); + expect(fn('A', 'B')).toBe('1=AB,2=CD,3=EF'); }); }); diff --git a/packages/patch-injection/src/makeFunction.ts b/packages/patch-injection/src/makeFunction.ts index 2d4420ccad989..13cbda272bc0c 100644 --- a/packages/patch-injection/src/makeFunction.ts +++ b/packages/patch-injection/src/makeFunction.ts @@ -3,12 +3,12 @@ import { withMiddleware } from './midleware'; export const makeFunction = (fn: T): PatchedFunction => { const wrapped = withMiddleware(fn); - const patch = (patch: PatchFunction, condition?: () => boolean) => { + const patch = (fn: PatchFunction, condition?: () => boolean) => { return wrapped.use((ctx, next) => { if (!condition || condition()) { - return patch(() => next(), ...ctx); + return fn(next as unknown as T, ...ctx); } - return next(); + return next(...ctx); }); }; const originalSignature = (() => { diff --git a/packages/patch-injection/src/midleware.ts b/packages/patch-injection/src/midleware.ts index df60489984f9a..2159c8e96773f 100644 --- a/packages/patch-injection/src/midleware.ts +++ b/packages/patch-injection/src/midleware.ts @@ -1,5 +1,5 @@ type Middleware any> = (ctx: Parameters, next: NextFunction) => ReturnType; -type NextFunction any> = () => ReturnType; +type NextFunction any> = (...args: Parameters | []) => ReturnType; export function withMiddleware any>(fn: F) { const middlewares: Middleware[] = []; @@ -7,7 +7,7 @@ export function withMiddleware any>(fn: F) { const buildRunner = (): ((ctx: Parameters) => ReturnType) => { return middlewares.reduce( (next, middleware) => { - return (ctx) => middleware(ctx, () => next(ctx)); + return (ctx) => middleware(ctx, (...args) => next(args.length ? (args as Parameters) : ctx)); }, (ctx: Parameters) => fn(...ctx), );