From 6d0be2a8d3eb1fb0f55667f4adf4efef08e202a4 Mon Sep 17 00:00:00 2001 From: apteryxxyz Date: Mon, 23 Jun 2025 02:28:18 +1200 Subject: [PATCH 1/3] refactor: better type signature for `callable` --- typescript-packages/client/src/millennium-api.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/typescript-packages/client/src/millennium-api.ts b/typescript-packages/client/src/millennium-api.ts index 6841935..1286e67 100644 --- a/typescript-packages/client/src/millennium-api.ts +++ b/typescript-packages/client/src/millennium-api.ts @@ -1,5 +1,5 @@ /** Returnable IPC types */ -type IPC_types = string | number | boolean; +type IPCTypes = string | number | boolean | void; /* Global Millennium API for developers. @@ -40,7 +40,11 @@ type Millennium = { * pass * ``` */ -declare const callable: (route: string) => (...args: Args) => Promise; +declare const callable: < + // Ideally this would be `Params extends Record<...>` but for backwards compatibility we keep a tuple type + Params extends [params: Record] | [] = [], + Return extends IPCTypes = IPCTypes +>(route: string) => (params: Params[0]) => Promise; const m_private_context: any = undefined; export const pluginSelf = m_private_context; From d0b4a50f75c803116624409d02161f14e9a955d3 Mon Sep 17 00:00:00 2001 From: apteryxxyz Date: Mon, 23 Jun 2025 02:56:01 +1200 Subject: [PATCH 2/3] refactor: proper type signatures for patchers --- .../client/src/utils/patcher.ts | 83 ++++++++++++++----- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/typescript-packages/client/src/utils/patcher.ts b/typescript-packages/client/src/utils/patcher.ts index 8b33ff9..81f5189 100644 --- a/typescript-packages/client/src/utils/patcher.ts +++ b/typescript-packages/client/src/utils/patcher.ts @@ -6,52 +6,85 @@ export interface PatchOptions { singleShot?: boolean; } +// Unused at this point, exported for backwards compatibility export type GenericPatchHandler = (args: any[], ret?: any) => any; -export interface Patch { - original: Function; - property: string; - object: any; - patchedFunction: any; +export interface Patch< + Object extends Record = Record, + Property extends keyof Object = keyof Object, + Target extends Object[Property] = Object[Property] +> { + object: Object; + property: Property; + original: Target; + + patchedFunction: Target; hasUnpatched: boolean; - handler: GenericPatchHandler; + handler: ((args: Parameters, ret: ReturnType) => any) | ((args: Parameters) => any); - unpatch: () => void; + unpatch(): void; } // let patches = new Set(); -export function beforePatch(object: any, property: string, handler: (args: any[]) => any, options: PatchOptions = {}): Patch { +export function beforePatch< + Object extends Record, + Property extends keyof Object, + Target extends Object[Property], +>( + object: Object, + property: Property, + handler: (args: Parameters) => void, + options: PatchOptions = {}, +): Patch { const orig = object[property]; - object[property] = function (...args: any[]) { + object[property] = (function (this: any, ...args: Parameters) { handler.call(this, args); const ret = patch.original.call(this, ...args); if (options.singleShot) { patch.unpatch(); } return ret; - }; + }) as any; const patch = processPatch(object, property, handler, object[property], orig); return patch; } -export function afterPatch(object: any, property: string, handler: (args: any[], ret: any) => any, options: PatchOptions = {}): Patch { +export function afterPatch< + Object extends Record, + Property extends keyof Object, + Target extends Object[Property] +>( + object: Object, + property: Property, + handler: (args: Parameters, ret: ReturnType) => ReturnType, + options: PatchOptions = {}, +): Patch { const orig = object[property]; - object[property] = function (...args: any[]) { + object[property] = (function (this: any, ...args: Parameters) { let ret = patch.original.call(this, ...args); ret = handler.call(this, args, ret); if (options.singleShot) { patch.unpatch(); } - return ret; - }; + return ret + }) as any; const patch = processPatch(object, property, handler, object[property], orig); return patch; } -export function replacePatch(object: any, property: string, handler: (args: any[]) => any, options: PatchOptions = {}): Patch { +export function replacePatch< + Object extends Record, + Property extends keyof Object, + Target extends Object[Property] +>( + object: Object, + property: Property, + handler: (args: Parameters) => ReturnType, + options: PatchOptions = {}, +): Patch { const orig = object[property]; - object[property] = function (...args: any[]) { + object[property] = (function (this: any, ...args: Parameters) { const ret = handler.call(this, args); // console.debug('[Patcher] replacePatch', patch); @@ -60,12 +93,22 @@ export function replacePatch(object: any, property: string, handler: (args: any[ patch.unpatch(); } return ret; - }; + }) as any; const patch = processPatch(object, property, handler, object[property], orig); return patch; } -function processPatch(object: any, property: any, handler: GenericPatchHandler, patchedFunction: any, original: any): Patch { +function processPatch< + Object extends Record, + Property extends keyof Object, + Target extends Object[Property], +>( + object: Object, + property: Property, + handler: ((args: Parameters, ret: ReturnType) => any) | ((args: Parameters) => any), + patchedFunction: Target, + original: Target, +): Patch { // Assign all props of original function to new one Object.assign(object[property], original); // Allow toString webpack filters to continue to work @@ -78,14 +121,14 @@ function processPatch(object: any, property: any, handler: GenericPatchHandler, }); // Build a Patch object of this patch - const patch: Patch = { + const patch = { object, property, handler, patchedFunction, original, hasUnpatched: false, - unpatch: () => unpatch(patch), + unpatch: () => unpatch(patch as any), }; object[property].__millenniumPatch = patch; From 7ecff931fd29fc51570a8d980057075e4218f0ae Mon Sep 17 00:00:00 2001 From: apteryxxyz Date: Mon, 23 Jun 2025 03:08:45 +1200 Subject: [PATCH 3/3] fix: `callable` passed params --- typescript-packages/client/src/millennium-api.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/typescript-packages/client/src/millennium-api.ts b/typescript-packages/client/src/millennium-api.ts index 1286e67..c274925 100644 --- a/typescript-packages/client/src/millennium-api.ts +++ b/typescript-packages/client/src/millennium-api.ts @@ -1,5 +1,5 @@ /** Returnable IPC types */ -type IPCTypes = string | number | boolean | void; +type IPCType = string | number | boolean | void; /* Global Millennium API for developers. @@ -42,9 +42,9 @@ type Millennium = { */ declare const callable: < // Ideally this would be `Params extends Record<...>` but for backwards compatibility we keep a tuple type - Params extends [params: Record] | [] = [], - Return extends IPCTypes = IPCTypes ->(route: string) => (params: Params[0]) => Promise; + Params extends [params: Record] | [] = [], + Return extends IPCType = IPCType +>(route: string) => (...params: Params) => Promise; const m_private_context: any = undefined; export const pluginSelf = m_private_context;