electron-buff 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -98,15 +98,17 @@ declare class XpcMainHandler {
98
98
  /**
99
99
  * Helper: checks if a function type has at most 1 parameter.
100
100
  * Returns the function type itself if valid, `never` otherwise.
101
+ * Uses Parameters<> length check to avoid contravariance issues
102
+ * where (p: any) => any extends () => any in TypeScript.
101
103
  */
102
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
104
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
103
105
  /**
104
106
  * Utility type: extracts the method signatures from a handler class,
105
107
  * turning each method into an emitter-compatible signature.
106
108
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
107
109
  */
108
110
  type XpcEmitterOf<T> = {
109
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
111
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
110
112
  };
111
113
 
112
114
  /**
@@ -98,15 +98,17 @@ declare class XpcMainHandler {
98
98
  /**
99
99
  * Helper: checks if a function type has at most 1 parameter.
100
100
  * Returns the function type itself if valid, `never` otherwise.
101
+ * Uses Parameters<> length check to avoid contravariance issues
102
+ * where (p: any) => any extends () => any in TypeScript.
101
103
  */
102
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
104
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
103
105
  /**
104
106
  * Utility type: extracts the method signatures from a handler class,
105
107
  * turning each method into an emitter-compatible signature.
106
108
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
107
109
  */
108
110
  type XpcEmitterOf<T> = {
109
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
111
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
110
112
  };
111
113
 
112
114
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/main/xpcTask.helper.ts","../../../src/xpc/main/xpcId.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpcMain.helper.ts","../../../src/xpc/main/xpcCenter.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/main/xpcMain.handler.ts","../../../src/xpc/main/xpcMain.emitter.ts"],"names":["Semaphore","ipcMain","app","webContents"],"mappings":"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","file":"index.js","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpcCenter.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpcTask.helper';\nimport { generateXpcId } from './xpcId.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpcMain.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName (overwrites previous registration for the same handleName)\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n const existingId = this.registry.get(payload.handleName);\n if (existingId != null && existingId !== event.sender.id) {\n console.log(`[xpcCenter] handler \"${payload.handleName}\" overwritten: webContentsId ${existingId} → ${event.sender.id}`);\n }\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Base class for main-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcMainHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcMain.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Create a type-safe emitter proxy for a main-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcMain.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcMainEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcMainEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcMain.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/main/xpcTask.helper.ts","../../../src/xpc/main/xpcId.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpcMain.helper.ts","../../../src/xpc/main/xpcCenter.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/main/xpcMain.handler.ts","../../../src/xpc/main/xpcMain.emitter.ts"],"names":["Semaphore","ipcMain","app","webContents"],"mappings":"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","file":"index.js","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpcCenter.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpcTask.helper';\nimport { generateXpcId } from './xpcId.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpcMain.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName (overwrites previous registration for the same handleName)\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n const existingId = this.registry.get(payload.handleName);\n if (existingId != null && existingId !== event.sender.id) {\n console.log(`[xpcCenter] handler \"${payload.handleName}\" overwritten: webContentsId ${existingId} → ${event.sender.id}`);\n }\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Base class for main-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcMainHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcMain.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Create a type-safe emitter proxy for a main-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcMain.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcMainEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcMainEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcMain.send(channel, params);\n },\n });\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/main/xpcTask.helper.ts","../../../src/xpc/main/xpcId.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpcMain.helper.ts","../../../src/xpc/main/xpcCenter.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/main/xpcMain.handler.ts","../../../src/xpc/main/xpcMain.emitter.ts"],"names":["ipcMain"],"mappings":"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","file":"index.mjs","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpcCenter.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpcTask.helper';\nimport { generateXpcId } from './xpcId.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpcMain.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName (overwrites previous registration for the same handleName)\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n const existingId = this.registry.get(payload.handleName);\n if (existingId != null && existingId !== event.sender.id) {\n console.log(`[xpcCenter] handler \"${payload.handleName}\" overwritten: webContentsId ${existingId} → ${event.sender.id}`);\n }\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Base class for main-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcMainHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcMain.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Create a type-safe emitter proxy for a main-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcMain.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcMainEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcMainEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcMain.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/main/xpcTask.helper.ts","../../../src/xpc/main/xpcId.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpcMain.helper.ts","../../../src/xpc/main/xpcCenter.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/main/xpcMain.handler.ts","../../../src/xpc/main/xpcMain.emitter.ts"],"names":["ipcMain"],"mappings":"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","file":"index.mjs","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpcCenter.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpcTask.helper';\nimport { generateXpcId } from './xpcId.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpcMain.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName (overwrites previous registration for the same handleName)\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n const existingId = this.registry.get(payload.handleName);\n if (existingId != null && existingId !== event.sender.id) {\n console.log(`[xpcCenter] handler \"${payload.handleName}\" overwritten: webContentsId ${existingId} → ${event.sender.id}`);\n }\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Base class for main-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcMainHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcMain.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcMain } from './xpcMain.helper';\n\n/**\n * Create a type-safe emitter proxy for a main-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcMain.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcMainHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcMainEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcMainEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcMain.send(channel, params);\n },\n });\n};\n"]}
@@ -42,15 +42,17 @@ declare class XpcPreloadHandler {
42
42
  /**
43
43
  * Helper: checks if a function type has at most 1 parameter.
44
44
  * Returns the function type itself if valid, `never` otherwise.
45
+ * Uses Parameters<> length check to avoid contravariance issues
46
+ * where (p: any) => any extends () => any in TypeScript.
45
47
  */
46
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
48
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
47
49
  /**
48
50
  * Utility type: extracts the method signatures from a handler class,
49
51
  * turning each method into an emitter-compatible signature.
50
52
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
51
53
  */
52
54
  type XpcEmitterOf<T> = {
53
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
55
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
54
56
  };
55
57
 
56
58
  /**
@@ -42,15 +42,17 @@ declare class XpcPreloadHandler {
42
42
  /**
43
43
  * Helper: checks if a function type has at most 1 parameter.
44
44
  * Returns the function type itself if valid, `never` otherwise.
45
+ * Uses Parameters<> length check to avoid contravariance issues
46
+ * where (p: any) => any extends () => any in TypeScript.
45
47
  */
46
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
48
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
47
49
  /**
48
50
  * Utility type: extracts the method signatures from a handler class,
49
51
  * turning each method into an emitter-compatible signature.
50
52
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
51
53
  */
52
54
  type XpcEmitterOf<T> = {
53
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
55
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
54
56
  };
55
57
 
56
58
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/preload/xpcId.helper.ts","../../../src/xpc/preload/xpcPreload.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/preload/xpcPreload.handler.ts","../../../src/xpc/preload/xpcPreload.emitter.ts"],"names":["ipcRenderer","contextBridge"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { contextBridge, ipcRenderer } from 'electron';\nimport { XpcPayload, XpcRendererApi } from '../shared/xpc.type';\nimport { generateXpcId } from './xpcId.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/** Global handlers map, extracted from XpcRenderer class */\nexport const xpcHandlers = new Map<string, XpcHandler>();\n\nexport type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\nconst handle = (handleName: string, handler: XpcHandler): void => {\n // Remove existing listener to prevent stacking when the same handleName is re-registered\n if (xpcHandlers.has(handleName)) {\n ipcRenderer.removeAllListeners(handleName);\n }\n\n xpcHandlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = xpcHandlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n};\n\n/**\n * Remove a registered handler.\n */\nconst removeHandle = (handleName: string): void => {\n xpcHandlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n};\n\n/**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\nconst send = async (handleName: string, params?: any): Promise<any> => {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n */\nconst createXpcRendererApi = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return send(handleName, params);\n },\n };\n};\n\n/** The xpcRenderer API instance */\nexport const xpcRenderer: XpcRendererApi = createXpcRendererApi();\n\n// Auto-expose xpcRenderer to window on import\nif (process.contextIsolated) {\n try {\n contextBridge.exposeInMainWorld('xpcRenderer', xpcRenderer);\n } catch (error) {\n console.error('[xpcPreload] exposeInMainWorld failed:', error);\n }\n} else {\n (globalThis as any).xpcRenderer = xpcRenderer;\n}\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Base class for preload-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcPreloadHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Create a type-safe emitter proxy for a preload-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcPreloadEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcPreloadEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/preload/xpcId.helper.ts","../../../src/xpc/preload/xpcPreload.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/preload/xpcPreload.handler.ts","../../../src/xpc/preload/xpcPreload.emitter.ts"],"names":["ipcRenderer","contextBridge"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { contextBridge, ipcRenderer } from 'electron';\nimport { XpcPayload, XpcRendererApi } from '../shared/xpc.type';\nimport { generateXpcId } from './xpcId.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/** Global handlers map, extracted from XpcRenderer class */\nexport const xpcHandlers = new Map<string, XpcHandler>();\n\nexport type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\nconst handle = (handleName: string, handler: XpcHandler): void => {\n // Remove existing listener to prevent stacking when the same handleName is re-registered\n if (xpcHandlers.has(handleName)) {\n ipcRenderer.removeAllListeners(handleName);\n }\n\n xpcHandlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = xpcHandlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n};\n\n/**\n * Remove a registered handler.\n */\nconst removeHandle = (handleName: string): void => {\n xpcHandlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n};\n\n/**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\nconst send = async (handleName: string, params?: any): Promise<any> => {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n */\nconst createXpcRendererApi = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return send(handleName, params);\n },\n };\n};\n\n/** The xpcRenderer API instance */\nexport const xpcRenderer: XpcRendererApi = createXpcRendererApi();\n\n// Auto-expose xpcRenderer to window on import\nif (process.contextIsolated) {\n try {\n contextBridge.exposeInMainWorld('xpcRenderer', xpcRenderer);\n } catch (error) {\n console.error('[xpcPreload] exposeInMainWorld failed:', error);\n }\n} else {\n (globalThis as any).xpcRenderer = xpcRenderer;\n}\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Base class for preload-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcPreloadHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Create a type-safe emitter proxy for a preload-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcPreloadEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcPreloadEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/preload/xpcId.helper.ts","../../../src/xpc/preload/xpcPreload.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/preload/xpcPreload.handler.ts","../../../src/xpc/preload/xpcPreload.emitter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { contextBridge, ipcRenderer } from 'electron';\nimport { XpcPayload, XpcRendererApi } from '../shared/xpc.type';\nimport { generateXpcId } from './xpcId.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/** Global handlers map, extracted from XpcRenderer class */\nexport const xpcHandlers = new Map<string, XpcHandler>();\n\nexport type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\nconst handle = (handleName: string, handler: XpcHandler): void => {\n // Remove existing listener to prevent stacking when the same handleName is re-registered\n if (xpcHandlers.has(handleName)) {\n ipcRenderer.removeAllListeners(handleName);\n }\n\n xpcHandlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = xpcHandlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n};\n\n/**\n * Remove a registered handler.\n */\nconst removeHandle = (handleName: string): void => {\n xpcHandlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n};\n\n/**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\nconst send = async (handleName: string, params?: any): Promise<any> => {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n */\nconst createXpcRendererApi = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return send(handleName, params);\n },\n };\n};\n\n/** The xpcRenderer API instance */\nexport const xpcRenderer: XpcRendererApi = createXpcRendererApi();\n\n// Auto-expose xpcRenderer to window on import\nif (process.contextIsolated) {\n try {\n contextBridge.exposeInMainWorld('xpcRenderer', xpcRenderer);\n } catch (error) {\n console.error('[xpcPreload] exposeInMainWorld failed:', error);\n }\n} else {\n (globalThis as any).xpcRenderer = xpcRenderer;\n}\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Base class for preload-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcPreloadHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Create a type-safe emitter proxy for a preload-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcPreloadEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcPreloadEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/preload/xpcId.helper.ts","../../../src/xpc/preload/xpcPreload.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/preload/xpcPreload.handler.ts","../../../src/xpc/preload/xpcPreload.emitter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { contextBridge, ipcRenderer } from 'electron';\nimport { XpcPayload, XpcRendererApi } from '../shared/xpc.type';\nimport { generateXpcId } from './xpcId.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/** Global handlers map, extracted from XpcRenderer class */\nexport const xpcHandlers = new Map<string, XpcHandler>();\n\nexport type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\nconst handle = (handleName: string, handler: XpcHandler): void => {\n // Remove existing listener to prevent stacking when the same handleName is re-registered\n if (xpcHandlers.has(handleName)) {\n ipcRenderer.removeAllListeners(handleName);\n }\n\n xpcHandlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = xpcHandlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n};\n\n/**\n * Remove a registered handler.\n */\nconst removeHandle = (handleName: string): void => {\n xpcHandlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n};\n\n/**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\nconst send = async (handleName: string, params?: any): Promise<any> => {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n */\nconst createXpcRendererApi = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return send(handleName, params);\n },\n };\n};\n\n/** The xpcRenderer API instance */\nexport const xpcRenderer: XpcRendererApi = createXpcRendererApi();\n\n// Auto-expose xpcRenderer to window on import\nif (process.contextIsolated) {\n try {\n contextBridge.exposeInMainWorld('xpcRenderer', xpcRenderer);\n } catch (error) {\n console.error('[xpcPreload] exposeInMainWorld failed:', error);\n }\n} else {\n (globalThis as any).xpcRenderer = xpcRenderer;\n}\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Base class for preload-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcPreloadHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcPreload.helper';\n\n/**\n * Create a type-safe emitter proxy for a preload-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcPreloadHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcPreloadEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcPreloadEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
@@ -41,15 +41,17 @@ declare class XpcRendererHandler {
41
41
  /**
42
42
  * Helper: checks if a function type has at most 1 parameter.
43
43
  * Returns the function type itself if valid, `never` otherwise.
44
+ * Uses Parameters<> length check to avoid contravariance issues
45
+ * where (p: any) => any extends () => any in TypeScript.
44
46
  */
45
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
47
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
46
48
  /**
47
49
  * Utility type: extracts the method signatures from a handler class,
48
50
  * turning each method into an emitter-compatible signature.
49
51
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
50
52
  */
51
53
  type XpcEmitterOf<T> = {
52
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
54
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
53
55
  };
54
56
 
55
57
  /**
@@ -41,15 +41,17 @@ declare class XpcRendererHandler {
41
41
  /**
42
42
  * Helper: checks if a function type has at most 1 parameter.
43
43
  * Returns the function type itself if valid, `never` otherwise.
44
+ * Uses Parameters<> length check to avoid contravariance issues
45
+ * where (p: any) => any extends () => any in TypeScript.
44
46
  */
45
- type AssertSingleParam<F> = F extends () => any ? F : F extends (p: any) => any ? F extends (p: any, q: any, ...rest: any[]) => any ? never : F : never;
47
+ type AssertSingleParam<F> = F extends (...args: any[]) => any ? Parameters<F>['length'] extends 0 | 1 ? F : never : never;
46
48
  /**
47
49
  * Utility type: extracts the method signatures from a handler class,
48
50
  * turning each method into an emitter-compatible signature.
49
51
  * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.
50
52
  */
51
53
  type XpcEmitterOf<T> = {
52
- [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? (params: P) => Promise<any> : () => Promise<any>;
54
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: AssertSingleParam<T[K]> extends never ? never : T[K] extends (params: infer P) => any ? Parameters<T[K]>['length'] extends 0 ? () => Promise<any> : (params: P) => Promise<any> : () => Promise<any>;
53
55
  };
54
56
 
55
57
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/renderer/xpcRenderer.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/renderer/xpcRenderer.handler.ts","../../../src/xpc/renderer/xpcRenderer.emitter.ts"],"names":[],"mappings":";;;AAMO,IAAM,cAAe,UAAA,CAAmB;;;ACFxC,IAAM,kBAAA,GAAqB,MAAA;AAM3B,IAAM,eAAA,GAAkB,CAAC,SAAA,EAAmB,UAAA,KAA+B;AAChF,EAAA,OAAO,CAAA,EAAG,kBAAkB,CAAA,EAAG,SAAS,IAAI,UAAU,CAAA,CAAA;AACxD,CAAA;AAKO,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAAgC;AACpE,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,mBAAA,CAAoB,SAAS,CAAA;AACjD,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,QAAQ,aAAA,EAAe;AAC3B,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,GAAG,CAAA;AACjE,IAAA,IAAI,UAAA,IAAc,OAAO,UAAA,CAAW,KAAA,KAAU,UAAA,EAAY;AACxD,MAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT,CAAA;;;ACVO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,WAAA,GAAc;AACZ,IAAA,MAAM,SAAA,GAAY,KAAK,WAAA,CAAY,IAAA;AACnC,IAAA,MAAM,WAAA,GAAc,qBAAA,CAAsB,MAAA,CAAO,cAAA,CAAe,IAAI,CAAC,CAAA;AACrE,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,UAAU,CAAA;AACrD,MAAA,MAAM,MAAA,GAAU,IAAA,CAAa,UAAU,CAAA,CAAE,KAAK,IAAI,CAAA;AAClD,MAAA,WAAA,CAAY,MAAA,CAAO,OAAA,EAAS,OAAO,OAAA,KAAwB;AACzD,QAAA,OAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,MACpC,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF;;;ACZO,IAAM,wBAAA,GAA2B,CAAI,SAAA,KAAuC;AACjF,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAsB;AAAA,IACtC,GAAA,CAAI,SAAS,IAAA,EAAc;AACzB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC/C,MAAA,OAAO,CAAC,MAAA,KAAiB,WAAA,CAAY,IAAA,CAAK,SAAS,MAAM,CAAA;AAAA,IAC3D;AAAA,GACD,CAAA;AACH","file":"index.js","sourcesContent":["import type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Direct reference to window.xpcRenderer exposed by the preload script.\n * Import this in renderer (browser) code to use xpcRenderer without manual window casting.\n */\nexport const xpcRenderer = (globalThis as any).xpcRenderer as XpcRendererApi;\n\nexport type { XpcRendererApi, XpcPayload } from '../shared/xpc.type';\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Base class for renderer-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcRendererHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Create a type-safe emitter proxy for a renderer-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcRendererEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcRendererEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/renderer/xpcRenderer.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/renderer/xpcRenderer.handler.ts","../../../src/xpc/renderer/xpcRenderer.emitter.ts"],"names":[],"mappings":";;;AAMO,IAAM,cAAe,UAAA,CAAmB;;;ACFxC,IAAM,kBAAA,GAAqB,MAAA;AAM3B,IAAM,eAAA,GAAkB,CAAC,SAAA,EAAmB,UAAA,KAA+B;AAChF,EAAA,OAAO,CAAA,EAAG,kBAAkB,CAAA,EAAG,SAAS,IAAI,UAAU,CAAA,CAAA;AACxD,CAAA;AAKO,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAAgC;AACpE,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,mBAAA,CAAoB,SAAS,CAAA;AACjD,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,QAAQ,aAAA,EAAe;AAC3B,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,GAAG,CAAA;AACjE,IAAA,IAAI,UAAA,IAAc,OAAO,UAAA,CAAW,KAAA,KAAU,UAAA,EAAY;AACxD,MAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT,CAAA;;;ACVO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,WAAA,GAAc;AACZ,IAAA,MAAM,SAAA,GAAY,KAAK,WAAA,CAAY,IAAA;AACnC,IAAA,MAAM,WAAA,GAAc,qBAAA,CAAsB,MAAA,CAAO,cAAA,CAAe,IAAI,CAAC,CAAA;AACrE,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,UAAU,CAAA;AACrD,MAAA,MAAM,MAAA,GAAU,IAAA,CAAa,UAAU,CAAA,CAAE,KAAK,IAAI,CAAA;AAClD,MAAA,WAAA,CAAY,MAAA,CAAO,OAAA,EAAS,OAAO,OAAA,KAAwB;AACzD,QAAA,OAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,MACpC,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF;;;ACZO,IAAM,wBAAA,GAA2B,CAAI,SAAA,KAAuC;AACjF,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAsB;AAAA,IACtC,GAAA,CAAI,SAAS,IAAA,EAAc;AACzB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC/C,MAAA,OAAO,CAAC,MAAA,KAAiB,WAAA,CAAY,IAAA,CAAK,SAAS,MAAM,CAAA;AAAA,IAC3D;AAAA,GACD,CAAA;AACH","file":"index.js","sourcesContent":["import type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Direct reference to window.xpcRenderer exposed by the preload script.\n * Import this in renderer (browser) code to use xpcRenderer without manual window casting.\n */\nexport const xpcRenderer = (globalThis as any).xpcRenderer as XpcRendererApi;\n\nexport type { XpcRendererApi, XpcPayload } from '../shared/xpc.type';\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Base class for renderer-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcRendererHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Create a type-safe emitter proxy for a renderer-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcRendererEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcRendererEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/xpc/renderer/xpcRenderer.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/renderer/xpcRenderer.handler.ts","../../../src/xpc/renderer/xpcRenderer.emitter.ts"],"names":[],"mappings":";AAMO,IAAM,cAAe,UAAA,CAAmB;;;ACFxC,IAAM,kBAAA,GAAqB,MAAA;AAM3B,IAAM,eAAA,GAAkB,CAAC,SAAA,EAAmB,UAAA,KAA+B;AAChF,EAAA,OAAO,CAAA,EAAG,kBAAkB,CAAA,EAAG,SAAS,IAAI,UAAU,CAAA,CAAA;AACxD,CAAA;AAKO,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAAgC;AACpE,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,mBAAA,CAAoB,SAAS,CAAA;AACjD,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,QAAQ,aAAA,EAAe;AAC3B,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,GAAG,CAAA;AACjE,IAAA,IAAI,UAAA,IAAc,OAAO,UAAA,CAAW,KAAA,KAAU,UAAA,EAAY;AACxD,MAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT,CAAA;;;ACVO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,WAAA,GAAc;AACZ,IAAA,MAAM,SAAA,GAAY,KAAK,WAAA,CAAY,IAAA;AACnC,IAAA,MAAM,WAAA,GAAc,qBAAA,CAAsB,MAAA,CAAO,cAAA,CAAe,IAAI,CAAC,CAAA;AACrE,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,UAAU,CAAA;AACrD,MAAA,MAAM,MAAA,GAAU,IAAA,CAAa,UAAU,CAAA,CAAE,KAAK,IAAI,CAAA;AAClD,MAAA,WAAA,CAAY,MAAA,CAAO,OAAA,EAAS,OAAO,OAAA,KAAwB;AACzD,QAAA,OAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,MACpC,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF;;;ACZO,IAAM,wBAAA,GAA2B,CAAI,SAAA,KAAuC;AACjF,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAsB;AAAA,IACtC,GAAA,CAAI,SAAS,IAAA,EAAc;AACzB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC/C,MAAA,OAAO,CAAC,MAAA,KAAiB,WAAA,CAAY,IAAA,CAAK,SAAS,MAAM,CAAA;AAAA,IAC3D;AAAA,GACD,CAAA;AACH","file":"index.mjs","sourcesContent":["import type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Direct reference to window.xpcRenderer exposed by the preload script.\n * Import this in renderer (browser) code to use xpcRenderer without manual window casting.\n */\nexport const xpcRenderer = (globalThis as any).xpcRenderer as XpcRendererApi;\n\nexport type { XpcRendererApi, XpcPayload } from '../shared/xpc.type';\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n */\ntype AssertSingleParam<F> =\n F extends () => any ? F :\n F extends (p: any) => any ?\n F extends (p: any, q: any, ...rest: any[]) => any ? never : F\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Base class for renderer-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcRendererHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Create a type-safe emitter proxy for a renderer-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcRendererEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcRendererEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
1
+ {"version":3,"sources":["../../../src/xpc/renderer/xpcRenderer.helper.ts","../../../src/xpc/shared/xpcHandler.type.ts","../../../src/xpc/renderer/xpcRenderer.handler.ts","../../../src/xpc/renderer/xpcRenderer.emitter.ts"],"names":[],"mappings":";AAMO,IAAM,cAAe,UAAA,CAAmB;;;ACFxC,IAAM,kBAAA,GAAqB,MAAA;AAM3B,IAAM,eAAA,GAAkB,CAAC,SAAA,EAAmB,UAAA,KAA+B;AAChF,EAAA,OAAO,CAAA,EAAG,kBAAkB,CAAA,EAAG,SAAS,IAAI,UAAU,CAAA,CAAA;AACxD,CAAA;AAKO,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAAgC;AACpE,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,mBAAA,CAAoB,SAAS,CAAA;AACjD,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,QAAQ,aAAA,EAAe;AAC3B,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,GAAG,CAAA;AACjE,IAAA,IAAI,UAAA,IAAc,OAAO,UAAA,CAAW,KAAA,KAAU,UAAA,EAAY;AACxD,MAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT,CAAA;;;ACVO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,WAAA,GAAc;AACZ,IAAA,MAAM,SAAA,GAAY,KAAK,WAAA,CAAY,IAAA;AACnC,IAAA,MAAM,WAAA,GAAc,qBAAA,CAAsB,MAAA,CAAO,cAAA,CAAe,IAAI,CAAC,CAAA;AACrE,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,UAAU,CAAA;AACrD,MAAA,MAAM,MAAA,GAAU,IAAA,CAAa,UAAU,CAAA,CAAE,KAAK,IAAI,CAAA;AAClD,MAAA,WAAA,CAAY,MAAA,CAAO,OAAA,EAAS,OAAO,OAAA,KAAwB;AACzD,QAAA,OAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,MACpC,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AACF;;;ACZO,IAAM,wBAAA,GAA2B,CAAI,SAAA,KAAuC;AACjF,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAsB;AAAA,IACtC,GAAA,CAAI,SAAS,IAAA,EAAc;AACzB,MAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC/C,MAAA,OAAO,CAAC,MAAA,KAAiB,WAAA,CAAY,IAAA,CAAK,SAAS,MAAM,CAAA;AAAA,IAC3D;AAAA,GACD,CAAA;AACH","file":"index.mjs","sourcesContent":["import type { XpcRendererApi } from '../shared/xpc.type';\n\n/**\n * Direct reference to window.xpcRenderer exposed by the preload script.\n * Import this in renderer (browser) code to use xpcRenderer without manual window casting.\n */\nexport const xpcRenderer = (globalThis as any).xpcRenderer as XpcRendererApi;\n\nexport type { XpcRendererApi, XpcPayload } from '../shared/xpc.type';\n","/**\n * Prefix for all auto-registered xpc handler channels.\n * Channel format: `xpc:ClassName/methodName`\n */\nexport const XPC_HANDLER_PREFIX = 'xpc:';\n\n/**\n * Build the xpc channel name from class name and method name.\n * e.g. buildChannel('UserTable', 'getUserList') => 'xpc:UserTable/getUserList'\n */\nexport const buildXpcChannel = (className: string, methodName: string): string => {\n return `${XPC_HANDLER_PREFIX}${className}/${methodName}`;\n};\n\n/**\n * Extract own method names from a class prototype, excluding constructor.\n */\nexport const getHandlerMethodNames = (prototype: object): string[] => {\n const names: string[] = [];\n const keys = Object.getOwnPropertyNames(prototype);\n for (const key of keys) {\n if (key === 'constructor') continue;\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n if (descriptor && typeof descriptor.value === 'function') {\n names.push(key);\n }\n }\n return names;\n};\n\n/**\n * Constraint: handler methods must accept 0 or 1 parameter.\n * Methods with 2+ parameters will fail type checking.\n */\nexport type XpcHandlerMethod = (() => Promise<any>) | ((params: any) => Promise<any>);\n\n/**\n * Helper: checks if a function type has at most 1 parameter.\n * Returns the function type itself if valid, `never` otherwise.\n * Uses Parameters<> length check to avoid contravariance issues\n * where (p: any) => any extends () => any in TypeScript.\n */\ntype AssertSingleParam<F> =\n F extends (...args: any[]) => any\n ? Parameters<F>['length'] extends 0 | 1 ? F : never\n : never;\n\n/**\n * Utility type: extracts the method signatures from a handler class,\n * turning each method into an emitter-compatible signature.\n * Methods with 2+ parameters are mapped to `never`, causing a compile error on use.\n */\nexport type XpcEmitterOf<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:\n AssertSingleParam<T[K]> extends never\n ? never\n : T[K] extends (params: infer P) => any\n ? Parameters<T[K]>['length'] extends 0\n ? () => Promise<any>\n : (params: P) => Promise<any>\n : () => Promise<any>;\n};\n","import { XpcPayload } from '../shared/xpc.type';\nimport { buildXpcChannel, getHandlerMethodNames } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Base class for renderer-process xpc handlers.\n * Subclass this and define async methods — they will be auto-registered\n * as xpc handlers with channel `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTable = new UserTable();\n * // auto-registers handler for 'xpc:UserTable/getUserList'\n * ```\n */\nexport class XpcRendererHandler {\n constructor() {\n const className = this.constructor.name;\n const methodNames = getHandlerMethodNames(Object.getPrototypeOf(this));\n for (const methodName of methodNames) {\n const channel = buildXpcChannel(className, methodName);\n const method = (this as any)[methodName].bind(this);\n xpcRenderer.handle(channel, async (payload: XpcPayload) => {\n return await method(payload.params);\n });\n }\n }\n}\n","import { buildXpcChannel, XpcEmitterOf } from '../shared/xpcHandler.type';\nimport { xpcRenderer } from './xpcRenderer.helper';\n\n/**\n * Create a type-safe emitter proxy for a renderer-process xpc handler.\n * The emitter mirrors the handler's method signatures, but each call\n * sends a message via xpcRenderer.send() to `xpc:ClassName/methodName`.\n *\n * Example:\n * ```ts\n * class UserTable extends XpcRendererHandler {\n * async getUserList(params?: any): Promise<any> { ... }\n * }\n * const userTableEmitter = createXpcRendererEmitter<UserTable>('UserTable');\n * const list = await userTableEmitter.getUserList({ page: 1 });\n * // sends to 'xpc:UserTable/getUserList'\n * ```\n */\nexport const createXpcRendererEmitter = <T>(className: string): XpcEmitterOf<T> => {\n return new Proxy({} as XpcEmitterOf<T>, {\n get(_target, prop: string) {\n const channel = buildXpcChannel(className, prop);\n return (params?: any) => xpcRenderer.send(channel, params);\n },\n });\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electron-buff",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Electron enhancement utilities for electron-vite projects",
5
5
  "exports": {
6
6
  "./xpc/main": {