electron-json-rpc 1.0.0

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +978 -0
  3. package/README.zh-CN.md +978 -0
  4. package/dist/debug.d.mts +92 -0
  5. package/dist/debug.d.mts.map +1 -0
  6. package/dist/debug.mjs +206 -0
  7. package/dist/debug.mjs.map +1 -0
  8. package/dist/error-xVRu7Lxq.mjs +131 -0
  9. package/dist/error-xVRu7Lxq.mjs.map +1 -0
  10. package/dist/event.d.mts +71 -0
  11. package/dist/event.d.mts.map +1 -0
  12. package/dist/event.mjs +60 -0
  13. package/dist/event.mjs.map +1 -0
  14. package/dist/index.d.mts +78 -0
  15. package/dist/index.d.mts.map +1 -0
  16. package/dist/index.mjs +4 -0
  17. package/dist/internal-BZK_0O3n.mjs +23 -0
  18. package/dist/internal-BZK_0O3n.mjs.map +1 -0
  19. package/dist/main.d.mts +151 -0
  20. package/dist/main.d.mts.map +1 -0
  21. package/dist/main.mjs +329 -0
  22. package/dist/main.mjs.map +1 -0
  23. package/dist/preload.d.mts +23 -0
  24. package/dist/preload.d.mts.map +1 -0
  25. package/dist/preload.mjs +417 -0
  26. package/dist/preload.mjs.map +1 -0
  27. package/dist/renderer/builder.d.mts +64 -0
  28. package/dist/renderer/builder.d.mts.map +1 -0
  29. package/dist/renderer/builder.mjs +101 -0
  30. package/dist/renderer/builder.mjs.map +1 -0
  31. package/dist/renderer/client.d.mts +42 -0
  32. package/dist/renderer/client.d.mts.map +1 -0
  33. package/dist/renderer/client.mjs +136 -0
  34. package/dist/renderer/client.mjs.map +1 -0
  35. package/dist/renderer/event.d.mts +17 -0
  36. package/dist/renderer/event.d.mts.map +1 -0
  37. package/dist/renderer/event.mjs +117 -0
  38. package/dist/renderer/event.mjs.map +1 -0
  39. package/dist/renderer/index.d.mts +6 -0
  40. package/dist/renderer/index.mjs +6 -0
  41. package/dist/stream.d.mts +38 -0
  42. package/dist/stream.d.mts.map +1 -0
  43. package/dist/stream.mjs +80 -0
  44. package/dist/stream.mjs.map +1 -0
  45. package/dist/types-BnGse9DF.d.mts +201 -0
  46. package/dist/types-BnGse9DF.d.mts.map +1 -0
  47. package/package.json +92 -0
@@ -0,0 +1,417 @@
1
+ import { STREAM_CHANNEL, createStreamChunk } from "./stream.mjs";
2
+ import { EVENT_CHANNEL } from "./event.mjs";
3
+
4
+ //#region src/preload.ts
5
+ /**
6
+ * 订阅指定事件到主进程
7
+ */
8
+ function subscribeToEvent(ipcRenderer, eventName) {
9
+ ipcRenderer.send(EVENT_CHANNEL, {
10
+ type: "subscribe",
11
+ eventName
12
+ });
13
+ }
14
+ /**
15
+ * 取消订阅指定事件
16
+ */
17
+ function unsubscribeFromEvent(ipcRenderer, eventName) {
18
+ ipcRenderer.send(EVENT_CHANNEL, {
19
+ type: "unsubscribe",
20
+ eventName
21
+ });
22
+ }
23
+ /**
24
+ * IPC channel name for JSON-RPC communication
25
+ */
26
+ const RPC_CHANNEL = "json-rpc";
27
+ /**
28
+ * Module-level state (not exposed to renderer, so can contain unclonable objects)
29
+ */
30
+ const state = {
31
+ eventHandlers: /* @__PURE__ */ new Map(),
32
+ activeStreams: /* @__PURE__ */ new Map(),
33
+ ipcRenderer: null,
34
+ requestIdCounter: 0
35
+ };
36
+ /**
37
+ * Setup IPC listeners for streams and events (only these need preload state)
38
+ */
39
+ function setupListeners(ipcRenderer) {
40
+ ipcRenderer.on(STREAM_CHANNEL, handleStreamChunk);
41
+ ipcRenderer.on(EVENT_CHANNEL, handleEventBusMessage);
42
+ }
43
+ /**
44
+ * Handle incoming stream chunks
45
+ */
46
+ function handleStreamChunk(_event, ...args) {
47
+ const { streamId, type, data, error } = args[0];
48
+ const streamKey = String(streamId);
49
+ const controller = state.activeStreams.get(streamKey);
50
+ if (!controller) return;
51
+ switch (type) {
52
+ case "chunk":
53
+ controller.enqueue(data);
54
+ break;
55
+ case "end":
56
+ state.activeStreams.delete(streamKey);
57
+ controller.close();
58
+ break;
59
+ case "error":
60
+ state.activeStreams.delete(streamKey);
61
+ const errorObj = new Error(error?.message ?? "Stream error");
62
+ controller.error(errorObj);
63
+ break;
64
+ }
65
+ }
66
+ /**
67
+ * Handle incoming event bus messages from main
68
+ */
69
+ function handleEventBusMessage(_event, ...args) {
70
+ const { type, eventName, data } = args[0];
71
+ if (type === "event") {
72
+ const handlers = state.eventHandlers.get(eventName);
73
+ if (handlers) for (const callback of handlers) try {
74
+ callback(data);
75
+ } catch (error) {
76
+ console.error(`Error in event handler for "${eventName}":`, error);
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Generate a unique request ID
82
+ */
83
+ function generateRequestId() {
84
+ return `rpc_${++state.requestIdCounter}_${Date.now()}`;
85
+ }
86
+ /**
87
+ * Create the API object to expose to renderer
88
+ *
89
+ * The API object functions only capture primitive values and use ipcRenderer
90
+ * which is available in renderer, making the object clonable by contextBridge.
91
+ */
92
+ function createApiObject(ipcRenderer) {
93
+ return {
94
+ call: (method, ...params) => {
95
+ const id = generateRequestId();
96
+ return new Promise((resolve, reject) => {
97
+ const timeout = setTimeout(() => {
98
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
99
+ reject(/* @__PURE__ */ new Error(`RPC timeout: ${method}`));
100
+ }, 3e4);
101
+ const listener = (_event, ...args) => {
102
+ const response = args[0];
103
+ if (response.id === id) {
104
+ clearTimeout(timeout);
105
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
106
+ if (response.error) {
107
+ const err = response.error;
108
+ reject(new Error(err.message));
109
+ } else resolve(response.result);
110
+ }
111
+ };
112
+ ipcRenderer.on(RPC_CHANNEL, listener);
113
+ ipcRenderer.send(RPC_CHANNEL, {
114
+ jsonrpc: "2.0",
115
+ id,
116
+ method,
117
+ params
118
+ });
119
+ });
120
+ },
121
+ notify: (method, ...params) => {
122
+ ipcRenderer.send(RPC_CHANNEL, {
123
+ jsonrpc: "2.0",
124
+ method,
125
+ params
126
+ });
127
+ },
128
+ stream: (method, ...params) => {
129
+ const callId = generateRequestId();
130
+ const ipc = state.ipcRenderer || ipcRenderer;
131
+ let streamKey = null;
132
+ return new ReadableStream({
133
+ start: async (controller) => {
134
+ try {
135
+ const streamId = await new Promise((resolve, reject) => {
136
+ const timeout = setTimeout(() => {
137
+ ipc.removeListener(RPC_CHANNEL, listener);
138
+ reject(/* @__PURE__ */ new Error("Stream timeout"));
139
+ }, 3e4);
140
+ const listener = (_event, ...args) => {
141
+ const response = args[0];
142
+ if (response.id === callId) {
143
+ clearTimeout(timeout);
144
+ ipc.removeListener(RPC_CHANNEL, listener);
145
+ if (response.result?.streamId) resolve(response.result.streamId);
146
+ else reject(/* @__PURE__ */ new Error("Not a stream method"));
147
+ }
148
+ };
149
+ ipc.on(RPC_CHANNEL, listener);
150
+ ipc.send(RPC_CHANNEL, {
151
+ jsonrpc: "2.0",
152
+ id: callId,
153
+ method,
154
+ params
155
+ });
156
+ });
157
+ streamKey = String(streamId);
158
+ state.activeStreams.set(streamKey, controller);
159
+ const originalClose = controller.close.bind(controller);
160
+ controller.close = () => {
161
+ state.activeStreams.delete(streamKey);
162
+ originalClose();
163
+ };
164
+ } catch (error) {
165
+ controller.error(error);
166
+ }
167
+ },
168
+ cancel: () => {
169
+ if (streamKey) {
170
+ state.activeStreams.delete(streamKey);
171
+ ipc.send(STREAM_CHANNEL, createStreamChunk(streamKey, "end"));
172
+ }
173
+ }
174
+ });
175
+ },
176
+ on: (eventName, callback) => {
177
+ const ipc = state.ipcRenderer || ipcRenderer;
178
+ if (!state.eventHandlers.has(eventName)) {
179
+ state.eventHandlers.set(eventName, /* @__PURE__ */ new Set());
180
+ subscribeToEvent(ipc, eventName);
181
+ }
182
+ state.eventHandlers.get(eventName).add(callback);
183
+ return () => {
184
+ const handlers = state.eventHandlers.get(eventName);
185
+ if (handlers) {
186
+ handlers.delete(callback);
187
+ if (handlers.size === 0) {
188
+ state.eventHandlers.delete(eventName);
189
+ unsubscribeFromEvent(ipc, eventName);
190
+ }
191
+ }
192
+ };
193
+ },
194
+ off: (eventName, callback) => {
195
+ const ipc = state.ipcRenderer || ipcRenderer;
196
+ const handlers = state.eventHandlers.get(eventName);
197
+ if (!handlers) return;
198
+ if (callback) {
199
+ handlers.delete(callback);
200
+ if (handlers.size === 0) {
201
+ state.eventHandlers.delete(eventName);
202
+ unsubscribeFromEvent(ipc, eventName);
203
+ }
204
+ } else {
205
+ state.eventHandlers.delete(eventName);
206
+ unsubscribeFromEvent(ipc, eventName);
207
+ }
208
+ },
209
+ once: (eventName, callback) => {
210
+ const ipc = state.ipcRenderer || ipcRenderer;
211
+ const wrappedCallback = (data) => {
212
+ try {
213
+ callback(data);
214
+ } finally {
215
+ const handlers = state.eventHandlers.get(eventName);
216
+ if (handlers) {
217
+ handlers.delete(wrappedCallback);
218
+ if (handlers.size === 0) {
219
+ state.eventHandlers.delete(eventName);
220
+ unsubscribeFromEvent(ipc, eventName);
221
+ }
222
+ }
223
+ }
224
+ };
225
+ if (!state.eventHandlers.has(eventName)) {
226
+ state.eventHandlers.set(eventName, /* @__PURE__ */ new Set());
227
+ subscribeToEvent(ipc, eventName);
228
+ }
229
+ state.eventHandlers.get(eventName).add(wrappedCallback);
230
+ }
231
+ };
232
+ }
233
+ /**
234
+ * Create the API object for whitelist mode
235
+ * Adds shortcut methods for whitelisted method names
236
+ */
237
+ function createWhitelistApi(methods, ipcRenderer) {
238
+ const api = createApiObject(ipcRenderer);
239
+ for (const method of methods) Object.defineProperty(api, method, {
240
+ value: (...args) => {
241
+ const callFn = api.call;
242
+ return callFn(method, ...args);
243
+ },
244
+ writable: false,
245
+ enumerable: true
246
+ });
247
+ return api;
248
+ }
249
+ /**
250
+ * Initialize the preload module
251
+ */
252
+ function init(ipcRenderer) {
253
+ if (state.ipcRenderer) return;
254
+ state.ipcRenderer = ipcRenderer;
255
+ setupListeners(ipcRenderer);
256
+ }
257
+ /**
258
+ * Expose an RPC API to the renderer process via contextBridge
259
+ */
260
+ function exposeRpcApi(options) {
261
+ const { contextBridge, ipcRenderer, methods, apiName = "rpc" } = options;
262
+ init(ipcRenderer);
263
+ const api = methods && methods.length > 0 ? createWhitelistApi(methods, ipcRenderer) : createApiObject(ipcRenderer);
264
+ contextBridge.exposeInMainWorld(apiName, api);
265
+ }
266
+ /**
267
+ * Create an RPC client for use in preload scripts
268
+ * (without exposing to renderer)
269
+ */
270
+ function createPreloadClient(ipcRenderer, timeout = 3e4) {
271
+ init(ipcRenderer);
272
+ return {
273
+ call: (method, ...params) => {
274
+ const id = generateRequestId();
275
+ return new Promise((resolve, reject) => {
276
+ const timeoutId = setTimeout(() => {
277
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
278
+ reject(/* @__PURE__ */ new Error(`RPC timeout: ${method}`));
279
+ }, timeout);
280
+ const listener = (_event, ...args) => {
281
+ const response = args[0];
282
+ if (response.id === id) {
283
+ clearTimeout(timeoutId);
284
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
285
+ if (response.error) {
286
+ const err = response.error;
287
+ reject(new Error(err.message));
288
+ } else resolve(response.result);
289
+ }
290
+ };
291
+ ipcRenderer.on(RPC_CHANNEL, listener);
292
+ ipcRenderer.send(RPC_CHANNEL, {
293
+ jsonrpc: "2.0",
294
+ id,
295
+ method,
296
+ params
297
+ });
298
+ });
299
+ },
300
+ notify: (method, ...params) => {
301
+ ipcRenderer.send(RPC_CHANNEL, {
302
+ jsonrpc: "2.0",
303
+ method,
304
+ params
305
+ });
306
+ },
307
+ stream: (method, ...params) => {
308
+ const callId = generateRequestId();
309
+ let streamKey = null;
310
+ return new ReadableStream({
311
+ start: async (controller) => {
312
+ try {
313
+ const streamId = await new Promise((resolve, reject) => {
314
+ const timeoutId = setTimeout(() => {
315
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
316
+ reject(/* @__PURE__ */ new Error("Stream timeout"));
317
+ }, timeout);
318
+ const listener = (_event, ...args) => {
319
+ const response = args[0];
320
+ if (response.id === callId) {
321
+ clearTimeout(timeoutId);
322
+ ipcRenderer.removeListener(RPC_CHANNEL, listener);
323
+ if (response.result?.streamId) resolve(response.result.streamId);
324
+ else reject(/* @__PURE__ */ new Error("Not a stream method"));
325
+ }
326
+ };
327
+ ipcRenderer.on(RPC_CHANNEL, listener);
328
+ ipcRenderer.send(RPC_CHANNEL, {
329
+ jsonrpc: "2.0",
330
+ id: callId,
331
+ method,
332
+ params
333
+ });
334
+ });
335
+ streamKey = String(streamId);
336
+ state.activeStreams.set(streamKey, controller);
337
+ const originalClose = controller.close.bind(controller);
338
+ controller.close = () => {
339
+ state.activeStreams.delete(streamKey);
340
+ originalClose();
341
+ };
342
+ } catch (error) {
343
+ controller.error(error);
344
+ }
345
+ },
346
+ cancel: () => {
347
+ if (streamKey) {
348
+ state.activeStreams.delete(streamKey);
349
+ ipcRenderer.send(STREAM_CHANNEL, createStreamChunk(streamKey, "end"));
350
+ }
351
+ }
352
+ });
353
+ },
354
+ on: (eventName, callback) => {
355
+ if (!state.eventHandlers.has(eventName)) {
356
+ state.eventHandlers.set(eventName, /* @__PURE__ */ new Set());
357
+ subscribeToEvent(ipcRenderer, eventName);
358
+ }
359
+ state.eventHandlers.get(eventName).add(callback);
360
+ return () => {
361
+ const handlers = state.eventHandlers.get(eventName);
362
+ if (handlers) {
363
+ handlers.delete(callback);
364
+ if (handlers.size === 0) {
365
+ state.eventHandlers.delete(eventName);
366
+ unsubscribeFromEvent(ipcRenderer, eventName);
367
+ }
368
+ }
369
+ };
370
+ },
371
+ off: (eventName, callback) => {
372
+ const handlers = state.eventHandlers.get(eventName);
373
+ if (!handlers) return;
374
+ if (callback) {
375
+ handlers.delete(callback);
376
+ if (handlers.size === 0) {
377
+ state.eventHandlers.delete(eventName);
378
+ unsubscribeFromEvent(ipcRenderer, eventName);
379
+ }
380
+ } else {
381
+ state.eventHandlers.delete(eventName);
382
+ unsubscribeFromEvent(ipcRenderer, eventName);
383
+ }
384
+ },
385
+ once: (eventName, callback) => {
386
+ const wrappedCallback = (data) => {
387
+ callback(data);
388
+ const handlers = state.eventHandlers.get(eventName);
389
+ if (handlers) {
390
+ handlers.delete(wrappedCallback);
391
+ if (handlers.size === 0) {
392
+ state.eventHandlers.delete(eventName);
393
+ unsubscribeFromEvent(ipcRenderer, eventName);
394
+ }
395
+ }
396
+ };
397
+ if (!state.eventHandlers.has(eventName)) {
398
+ state.eventHandlers.set(eventName, /* @__PURE__ */ new Set());
399
+ subscribeToEvent(ipcRenderer, eventName);
400
+ }
401
+ state.eventHandlers.get(eventName).add(wrappedCallback);
402
+ },
403
+ dispose: () => {
404
+ ipcRenderer.removeListener(STREAM_CHANNEL, handleStreamChunk);
405
+ ipcRenderer.removeListener(EVENT_CHANNEL, handleEventBusMessage);
406
+ for (const controller of state.activeStreams.values()) try {
407
+ controller.close();
408
+ } catch {}
409
+ state.activeStreams.clear();
410
+ state.eventHandlers.clear();
411
+ }
412
+ };
413
+ }
414
+
415
+ //#endregion
416
+ export { createPreloadClient, exposeRpcApi };
417
+ //# sourceMappingURL=preload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload.mjs","names":[],"sources":["../src/preload.ts"],"sourcesContent":["/**\n * JSON-RPC Preload Script for Electron\n *\n * Exposes a secure RPC API to the renderer process via contextBridge\n *\n * Architecture:\n * - Regular RPC calls go directly from renderer to main (no preload overhead)\n * - Streams and events are handled in preload (require state management)\n */\n\nimport type { ExposeRpcApiOptions, EventHandler } from \"./types.js\";\nimport { STREAM_CHANNEL, createStreamChunk } from \"./stream.js\";\nimport { EVENT_CHANNEL, type EventMessage } from \"./event.js\";\n\n/**\n * 订阅指定事件到主进程\n */\nfunction subscribeToEvent(\n ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"],\n eventName: string,\n): void {\n ipcRenderer.send(EVENT_CHANNEL, {\n type: \"subscribe\",\n eventName,\n } as EventMessage);\n}\n\n/**\n * 取消订阅指定事件\n */\nfunction unsubscribeFromEvent(\n ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"],\n eventName: string,\n): void {\n ipcRenderer.send(EVENT_CHANNEL, {\n type: \"unsubscribe\",\n eventName,\n } as EventMessage);\n}\n\n/**\n * IPC channel name for JSON-RPC communication\n */\nconst RPC_CHANNEL = \"json-rpc\";\n\n/**\n * Module-level state (not exposed to renderer, so can contain unclonable objects)\n */\nconst state = {\n /** Event handlers by event name (for exposed API and preload client) */\n eventHandlers: new Map<string, Set<EventHandler>>(),\n /** Active stream controllers */\n activeStreams: new Map<string, ReadableStreamDefaultController>(),\n\n /** The ipcRenderer instance (set during setup) */\n ipcRenderer: null as ExposeRpcApiOptions[\"ipcRenderer\"] | null,\n /** Request ID counter */\n requestIdCounter: 0,\n};\n\n/**\n * Setup IPC listeners for streams and events (only these need preload state)\n */\nfunction setupListeners(ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"]): void {\n // Listen for stream chunks\n ipcRenderer.on(STREAM_CHANNEL, handleStreamChunk as (event: unknown, ...args: unknown[]) => void);\n // Listen for event bus messages from main process\n ipcRenderer.on(\n EVENT_CHANNEL,\n handleEventBusMessage as (event: unknown, ...args: unknown[]) => void,\n );\n}\n\n/**\n * Handle incoming stream chunks\n */\nfunction handleStreamChunk(_event: unknown, ...args: unknown[]): void {\n const chunk = args[0] as {\n streamId: string | number;\n type: \"chunk\" | \"end\" | \"error\";\n data?: unknown;\n error?: { message?: string };\n };\n const { streamId, type, data, error } = chunk;\n const streamKey = String(streamId);\n const controller = state.activeStreams.get(streamKey);\n\n if (!controller) {\n return; // Stream not found or already closed\n }\n\n switch (type) {\n case \"chunk\":\n controller.enqueue(data);\n break;\n case \"end\":\n state.activeStreams.delete(streamKey);\n controller.close();\n break;\n case \"error\":\n state.activeStreams.delete(streamKey);\n const errorObj = new Error(error?.message ?? \"Stream error\");\n controller.error(errorObj);\n break;\n }\n}\n\n/**\n * Handle incoming event bus messages from main\n */\nfunction handleEventBusMessage(_event: unknown, ...args: unknown[]): void {\n const message = args[0] as { type: string; eventName: string; data?: unknown };\n const { type, eventName, data } = message;\n\n if (type === \"event\") {\n // Handle callbacks from createPreloadClient and exposed API\n const handlers = state.eventHandlers.get(eventName);\n if (handlers) {\n for (const callback of handlers) {\n try {\n callback(data);\n } catch (error) {\n console.error(`Error in event handler for \"${eventName}\":`, error);\n }\n }\n }\n }\n}\n\n/**\n * Generate a unique request ID\n */\nfunction generateRequestId(): string {\n return `rpc_${++state.requestIdCounter}_${Date.now()}`;\n}\n\n/**\n * Create the API object to expose to renderer\n *\n * The API object functions only capture primitive values and use ipcRenderer\n * which is available in renderer, making the object clonable by contextBridge.\n */\nfunction createApiObject(ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"]): Record<string, unknown> {\n return {\n /**\n * Call a RPC method - direct IPC to main, no preload overhead\n */\n call: (method: string, ...params: unknown[]) => {\n const id = generateRequestId();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n reject(new Error(`RPC timeout: ${method}`));\n }, 30000);\n\n const listener = (_event: unknown, ...args: unknown[]) => {\n const response = args[0] as {\n jsonrpc: \"2.0\";\n id: string | number;\n result?: unknown;\n error?: unknown;\n };\n if (response.id === id) {\n clearTimeout(timeout);\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n if (response.error) {\n const err = response.error as { message: string };\n reject(new Error(err.message));\n } else {\n resolve(response.result);\n }\n }\n };\n\n ipcRenderer.on(RPC_CHANNEL, listener);\n ipcRenderer.send(RPC_CHANNEL, { jsonrpc: \"2.0\", id, method, params });\n });\n },\n\n /**\n * Send a notification (one-way, no response) - direct IPC\n */\n notify: (method: string, ...params: unknown[]) => {\n ipcRenderer.send(RPC_CHANNEL, { jsonrpc: \"2.0\", method, params });\n },\n\n /**\n * Create a stream from a RPC method\n * Stream setup uses delegation, but chunks go direct via STREAM_CHANNEL\n */\n stream: (method: string, ...params: unknown[]) => {\n const callId = generateRequestId();\n const ipc = state.ipcRenderer || ipcRenderer;\n let streamKey: string | null = null;\n\n return new ReadableStream({\n start: async (controller) => {\n try {\n // First call the method to get stream ID\n const streamId = await new Promise<string>((resolve, reject) => {\n const timeout = setTimeout(() => {\n ipc.removeListener(RPC_CHANNEL, listener);\n reject(new Error(\"Stream timeout\"));\n }, 30000);\n\n const listener = (_event: unknown, ...args: unknown[]) => {\n const response = args[0] as {\n jsonrpc: \"2.0\";\n id: string;\n result?: { streamId?: string };\n };\n if (response.id === callId) {\n clearTimeout(timeout);\n ipc.removeListener(RPC_CHANNEL, listener);\n if (response.result?.streamId) {\n resolve(response.result.streamId);\n } else {\n reject(new Error(\"Not a stream method\"));\n }\n }\n };\n\n ipc.on(RPC_CHANNEL, listener);\n ipc.send(RPC_CHANNEL, { jsonrpc: \"2.0\", id: callId, method, params });\n });\n\n // Store controller for this stream\n streamKey = String(streamId);\n state.activeStreams.set(streamKey, controller);\n\n // Set up cleanup when stream closes\n const originalClose = controller.close.bind(controller);\n controller.close = () => {\n state.activeStreams.delete(streamKey!);\n originalClose();\n };\n } catch (error) {\n controller.error(error);\n }\n },\n cancel: () => {\n if (streamKey) {\n state.activeStreams.delete(streamKey);\n ipc.send(STREAM_CHANNEL, createStreamChunk(streamKey, \"end\"));\n }\n },\n });\n },\n\n /**\n * Subscribe to an event\n * @returns Unsubscribe function\n */\n on: (eventName: string, callback: (data?: unknown) => void) => {\n const ipc = state.ipcRenderer || ipcRenderer;\n\n if (!state.eventHandlers.has(eventName)) {\n state.eventHandlers.set(eventName, new Set());\n subscribeToEvent(ipc, eventName);\n }\n\n state.eventHandlers.get(eventName)!.add(callback as EventHandler);\n\n // Return unsubscribe function\n return () => {\n const handlers = state.eventHandlers.get(eventName);\n if (handlers) {\n handlers.delete(callback as EventHandler);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipc, eventName);\n }\n }\n };\n },\n\n /**\n * Unsubscribe from an event\n */\n off: (eventName: string, callback?: (data?: unknown) => void) => {\n const ipc = state.ipcRenderer || ipcRenderer;\n const handlers = state.eventHandlers.get(eventName);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n handlers.delete(callback as EventHandler);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipc, eventName);\n }\n } else {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipc, eventName);\n }\n },\n\n /**\n * Subscribe to an event once (auto-unsubscribe after first call)\n */\n once: (eventName: string, callback: (data?: unknown) => void) => {\n const ipc = state.ipcRenderer || ipcRenderer;\n\n const wrappedCallback: EventHandler = (data) => {\n try {\n callback(data);\n } finally {\n const handlers = state.eventHandlers.get(eventName);\n if (handlers) {\n handlers.delete(wrappedCallback);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipc, eventName);\n }\n }\n }\n };\n\n if (!state.eventHandlers.has(eventName)) {\n state.eventHandlers.set(eventName, new Set());\n subscribeToEvent(ipc, eventName);\n }\n\n state.eventHandlers.get(eventName)!.add(wrappedCallback);\n },\n };\n}\n\n/**\n * Create the API object for whitelist mode\n * Adds shortcut methods for whitelisted method names\n */\nfunction createWhitelistApi(\n methods: string[],\n ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"],\n): Record<string, unknown> {\n const api = createApiObject(ipcRenderer);\n\n // Add method shortcuts for whitelisted methods\n for (const method of methods) {\n Object.defineProperty(api, method, {\n value: (...args: unknown[]) => {\n const callFn = api.call as (method: string, ...params: unknown[]) => Promise<unknown>;\n return callFn(method, ...args);\n },\n writable: false,\n enumerable: true,\n });\n }\n\n return api;\n}\n\n/**\n * Initialize the preload module\n */\nfunction init(ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"]): void {\n if (state.ipcRenderer) {\n return; // Already initialized\n }\n\n state.ipcRenderer = ipcRenderer;\n setupListeners(ipcRenderer);\n}\n\n/**\n * Expose an RPC API to the renderer process via contextBridge\n */\nexport function exposeRpcApi(options: ExposeRpcApiOptions): void {\n const { contextBridge, ipcRenderer, methods, apiName = \"rpc\" } = options;\n\n // Initialize the module-level state\n init(ipcRenderer);\n\n // Build the API object to expose\n const api =\n methods && methods.length > 0\n ? createWhitelistApi(methods, ipcRenderer)\n : createApiObject(ipcRenderer);\n\n // Expose to renderer via contextBridge\n contextBridge.exposeInMainWorld(apiName, api);\n}\n\n/**\n * Create an RPC client for use in preload scripts\n * (without exposing to renderer)\n */\nexport function createPreloadClient(\n ipcRenderer: ExposeRpcApiOptions[\"ipcRenderer\"],\n timeout = 30000,\n): {\n call: (method: string, ...params: unknown[]) => Promise<unknown>;\n notify: (method: string, ...params: unknown[]) => void;\n stream: (method: string, ...params: unknown[]) => ReadableStream;\n on: (eventName: string, callback: (data?: unknown) => void) => () => void;\n off: (eventName: string, callback?: (data?: unknown) => void) => void;\n once: (eventName: string, callback: (data?: unknown) => void) => void;\n dispose: () => void;\n} {\n // Initialize the module-level state\n init(ipcRenderer);\n\n return {\n call: (method: string, ...params: unknown[]) => {\n const id = generateRequestId();\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n reject(new Error(`RPC timeout: ${method}`));\n }, timeout);\n\n const listener = (_event: unknown, ...args: unknown[]) => {\n const response = args[0] as {\n jsonrpc: \"2.0\";\n id: string | number;\n result?: unknown;\n error?: unknown;\n };\n if (response.id === id) {\n clearTimeout(timeoutId);\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n if (response.error) {\n const err = response.error as { message: string };\n reject(new Error(err.message));\n } else {\n resolve(response.result);\n }\n }\n };\n\n ipcRenderer.on(RPC_CHANNEL, listener);\n ipcRenderer.send(RPC_CHANNEL, { jsonrpc: \"2.0\", id, method, params });\n });\n },\n\n notify: (method: string, ...params: unknown[]) => {\n ipcRenderer.send(RPC_CHANNEL, { jsonrpc: \"2.0\", method, params });\n },\n\n stream: (method: string, ...params: unknown[]) => {\n const callId = generateRequestId();\n let streamKey: string | null = null;\n\n return new ReadableStream({\n start: async (controller) => {\n try {\n // First call the method to get stream ID\n const streamId = await new Promise<string>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n reject(new Error(\"Stream timeout\"));\n }, timeout);\n\n const listener = (_event: unknown, ...args: unknown[]) => {\n const response = args[0] as {\n jsonrpc: \"2.0\";\n id: string;\n result?: { streamId?: string };\n };\n if (response.id === callId) {\n clearTimeout(timeoutId);\n ipcRenderer.removeListener(RPC_CHANNEL, listener);\n if (response.result?.streamId) {\n resolve(response.result.streamId);\n } else {\n reject(new Error(\"Not a stream method\"));\n }\n }\n };\n\n ipcRenderer.on(RPC_CHANNEL, listener);\n ipcRenderer.send(RPC_CHANNEL, { jsonrpc: \"2.0\", id: callId, method, params });\n });\n\n // Store controller for this stream\n streamKey = String(streamId);\n state.activeStreams.set(streamKey, controller);\n\n // Set up cleanup when stream closes\n const originalClose = controller.close.bind(controller);\n controller.close = () => {\n state.activeStreams.delete(streamKey!);\n originalClose();\n };\n } catch (error) {\n controller.error(error);\n }\n },\n cancel: () => {\n if (streamKey) {\n state.activeStreams.delete(streamKey);\n ipcRenderer.send(STREAM_CHANNEL, createStreamChunk(streamKey, \"end\"));\n }\n },\n });\n },\n\n on: (eventName: string, callback: EventHandler) => {\n if (!state.eventHandlers.has(eventName)) {\n state.eventHandlers.set(eventName, new Set());\n // Send subscribe message to main process\n subscribeToEvent(ipcRenderer, eventName);\n }\n\n state.eventHandlers.get(eventName)!.add(callback);\n\n // Return unsubscribe function\n return () => {\n const handlers = state.eventHandlers.get(eventName);\n if (handlers) {\n handlers.delete(callback);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipcRenderer, eventName);\n }\n }\n };\n },\n\n off: (eventName: string, callback?: EventHandler) => {\n const handlers = state.eventHandlers.get(eventName);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n handlers.delete(callback);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipcRenderer, eventName);\n }\n } else {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipcRenderer, eventName);\n }\n },\n\n once: (eventName: string, callback: EventHandler) => {\n const wrappedCallback: EventHandler = (data) => {\n callback(data);\n const handlers = state.eventHandlers.get(eventName);\n if (handlers) {\n handlers.delete(wrappedCallback);\n if (handlers.size === 0) {\n state.eventHandlers.delete(eventName);\n unsubscribeFromEvent(ipcRenderer, eventName);\n }\n }\n };\n\n if (!state.eventHandlers.has(eventName)) {\n state.eventHandlers.set(eventName, new Set());\n subscribeToEvent(ipcRenderer, eventName);\n }\n\n state.eventHandlers.get(eventName)!.add(wrappedCallback);\n },\n\n dispose: () => {\n ipcRenderer.removeListener(STREAM_CHANNEL, handleStreamChunk as (...args: unknown[]) => void);\n ipcRenderer.removeListener(\n EVENT_CHANNEL,\n handleEventBusMessage as (...args: unknown[]) => void,\n );\n\n // Close all active streams\n for (const controller of state.activeStreams.values()) {\n try {\n controller.close();\n } catch {\n // Ignore errors during cleanup\n }\n }\n state.activeStreams.clear();\n\n // Clear all event handlers\n state.eventHandlers.clear();\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { d as RpcClientOptions, f as RpcDebugOptions } from "../types-BnGse9DF.mjs";
2
+
3
+ //#region src/renderer/builder.d.ts
4
+ /**
5
+ * RpcBuilder for fluent API definition
6
+ *
7
+ * Accumulates method definitions and builds a typed client.
8
+ */
9
+ declare class RpcBuilder {
10
+ private methods;
11
+ private options;
12
+ private apiName;
13
+ constructor(options?: RpcClientOptions & RpcDebugOptions, apiName?: string);
14
+ /**
15
+ * Add a regular RPC method
16
+ *
17
+ * @param name - Method name
18
+ * @param _handler - Handler function for type inference only (not executed)
19
+ * @returns Builder with method added
20
+ */
21
+ add<K extends string, P extends any[], R>(name: K, _handler: (...args: P) => R): RpcBuilderWithMethod<K, P, R>;
22
+ /**
23
+ * Add a stream RPC method
24
+ *
25
+ * @param name - Method name
26
+ * @param _handler - Handler function for type inference only (not executed)
27
+ * @returns Builder with stream method added
28
+ */
29
+ stream<K extends string, P extends any[], R>(name: K, _handler: (...args: P) => R): RpcBuilderWithMethod<K, P, R>;
30
+ /**
31
+ * Build the final typed API client
32
+ *
33
+ * @returns Typed RPC client
34
+ */
35
+ build(): Record<string, (...args: any[]) => any>;
36
+ }
37
+ /**
38
+ * Builder type with one method added
39
+ * Provides fluent chaining for building the API
40
+ */
41
+ type RpcBuilderWithMethod<K extends string, P extends any[], R> = { [M in K]: (...args: P) => R extends void ? void : Promise<Awaited<R>> } & Omit<RpcBuilderMethods, K>;
42
+ /**
43
+ * Available methods on the builder
44
+ */
45
+ type RpcBuilderMethods = {
46
+ add: <K extends string, P extends any[], R>(name: K, handler: (...args: P) => R) => RpcBuilderWithMethod<K, P, R>;
47
+ stream: <K extends string, P extends any[], R>(name: K, handler: (...args: P) => R) => RpcBuilderWithMethod<K, P, R>;
48
+ build: () => Record<string, (...args: any[]) => any>;
49
+ };
50
+ /**
51
+ * Create a fluent RPC API builder
52
+ *
53
+ * Build a typed RPC API by chaining method definitions.
54
+ * Types are inferred from the handler functions.
55
+ *
56
+ * @param options - Client options
57
+ * @returns Builder instance
58
+ */
59
+ declare function createRpc(options?: RpcClientOptions & RpcDebugOptions): Omit<RpcBuilder, "build"> & {
60
+ build: () => Record<string, (...args: any[]) => any>;
61
+ };
62
+ //#endregion
63
+ export { type RpcBuilder, type RpcBuilderMethods, type RpcBuilderWithMethod, createRpc };
64
+ //# sourceMappingURL=builder.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.mts","names":[],"sources":["../../src/renderer/builder.ts"],"mappings":";;;;AAOqE;;;;cAU/D,UAAA;EAAA,QAAA,OAAA;EAAA,QAAA,OAAA;EAAA,QAAA,OAAA;EAAA,YAAA,OAAA,GAMO,gBAAA,GAAmB,eAAA,EAAA,OAAA;EAAA;;;;;;;EAAA,GAAA,sCAAA,CAAA,IAAA,EAetB,CAAA,EAAA,QAAA,MAAA,IAAA,EACc,CAAA,KAAM,CAAA,GACzB,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA;EAAA;;;;;;;EAAA,MAAA,sCAAA,CAAA,IAAA,EAatB,CAAA,EAAA,QAAA,MAAA,IAAA,EACc,CAAA,KAAM,CAAA,GACzB,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA;EAAA;;;;AAUf;EAVe,MAAA,GAUrB,MAAA,aAAA,IAAA;AAAA;AAAA;;AAAM;;AAAN,KAsEN,oBAAA,iDACG,CAAA,OAAA,IAAA,EAAc,CAAA,KAAM,CAAA,uBAAwB,OAAA,CAAQ,OAAA,CAAQ,CAAA,OAChE,IAAA,CAAK,iBAAA,EAAmB,CAAA;AAAA;;;AAAA,KAKvB,iBAAA;EAAA,GAAA,yCAAA,IAAA,EAEK,CAAA,EAAA,OAAA,MAAA,IAAA,EACa,CAAA,KAAM,CAAA,KACtB,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA;EAAA,MAAA,yCAAA,IAAA,EAExB,CAAA,EAAA,OAAA,MAAA,IAAA,EACa,CAAA,KAAM,CAAA,KACtB,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA;EAAA,KAAA,QACnB,MAAA,aAAA,IAAA;AAAA;AAAA;;AAYf;;;;;;;AAZe,iBAYC,SAAA,CAAA,OAAA,GAAmB,gBAAA,GAAmB,eAAA,GAAuB,IAAA,CAC3E,UAAA;EAAA,KAAA,QAGa,MAAA,aAAA,IAAA;AAAA"}
@@ -0,0 +1,101 @@
1
+ import { i as RpcTimeoutError } from "../error-xVRu7Lxq.mjs";
2
+ import { getLogger } from "../debug.mjs";
3
+ import { n as createTracker, r as getExposedApi, t as DEFAULT_API_NAME } from "../internal-BZK_0O3n.mjs";
4
+
5
+ //#region src/renderer/builder.ts
6
+ /**
7
+ * RpcBuilder for fluent API definition
8
+ *
9
+ * Accumulates method definitions and builds a typed client.
10
+ */
11
+ var RpcBuilder = class {
12
+ methods = /* @__PURE__ */ new Map();
13
+ options;
14
+ apiName;
15
+ constructor(options = {}, apiName = DEFAULT_API_NAME) {
16
+ this.options = options;
17
+ this.apiName = apiName;
18
+ }
19
+ /**
20
+ * Add a regular RPC method
21
+ *
22
+ * @param name - Method name
23
+ * @param _handler - Handler function for type inference only (not executed)
24
+ * @returns Builder with method added
25
+ */
26
+ add(name, _handler) {
27
+ this.methods.set(name, { kind: "call" });
28
+ return this;
29
+ }
30
+ /**
31
+ * Add a stream RPC method
32
+ *
33
+ * @param name - Method name
34
+ * @param _handler - Handler function for type inference only (not executed)
35
+ * @returns Builder with stream method added
36
+ */
37
+ stream(name, _handler) {
38
+ this.methods.set(name, { kind: "stream" });
39
+ return this;
40
+ }
41
+ /**
42
+ * Build the final typed API client
43
+ *
44
+ * @returns Typed RPC client
45
+ */
46
+ build() {
47
+ const api = getExposedApi(this.apiName);
48
+ if (!api) throw new Error(`RPC API not found. Make sure exposeRpcApi() is called in your preload script with apiName='${this.apiName}'.`);
49
+ const { timeout = 3e4, debug, logger } = this.options;
50
+ const debugTracker = createTracker(debug, logger ?? getLogger());
51
+ const requestIdCounter = { value: 0 };
52
+ const methods = this.methods;
53
+ return new Proxy({}, { get(_target, prop) {
54
+ const methodInfo = methods.get(prop);
55
+ if (!methodInfo) throw new Error(`Method '${prop}' was not registered in the RPC builder`);
56
+ if (methodInfo.kind === "stream") return (...args) => {
57
+ debugTracker.onStream(prop, args);
58
+ return api.stream(prop, ...args);
59
+ };
60
+ return (...args) => {
61
+ const requestId = ++requestIdCounter.value;
62
+ const startTime = performance.now();
63
+ debugTracker.onRequest(prop, args, requestId);
64
+ return new Promise((resolve, reject) => {
65
+ const timeoutId = setTimeout(() => {
66
+ const duration = Math.round(performance.now() - startTime);
67
+ debugTracker.onError(prop, args, `Timeout after ${timeout}ms`, duration, requestId);
68
+ reject(new RpcTimeoutError(timeout));
69
+ }, timeout);
70
+ api.call(prop, ...args).then((result) => {
71
+ clearTimeout(timeoutId);
72
+ const duration = Math.round(performance.now() - startTime);
73
+ debugTracker.onResponse(prop, args, result, duration, requestId);
74
+ resolve(result);
75
+ }).catch((error) => {
76
+ clearTimeout(timeoutId);
77
+ const duration = Math.round(performance.now() - startTime);
78
+ debugTracker.onError(prop, args, error.message, duration, requestId);
79
+ reject(error);
80
+ });
81
+ });
82
+ };
83
+ } });
84
+ }
85
+ };
86
+ /**
87
+ * Create a fluent RPC API builder
88
+ *
89
+ * Build a typed RPC API by chaining method definitions.
90
+ * Types are inferred from the handler functions.
91
+ *
92
+ * @param options - Client options
93
+ * @returns Builder instance
94
+ */
95
+ function createRpc(options = {}) {
96
+ return new RpcBuilder(options);
97
+ }
98
+
99
+ //#endregion
100
+ export { createRpc };
101
+ //# sourceMappingURL=builder.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.mjs","names":[],"sources":["../../src/renderer/builder.ts"],"sourcesContent":["/**\n * RpcBuilder for fluent API definition\n *\n * Build a typed RPC API by chaining method definitions.\n * Types are inferred from the handler functions.\n */\n\nimport type { RpcClientOptions, RpcDebugOptions } from \"../types.js\";\nimport { RpcTimeoutError } from \"../error.js\";\nimport { getLogger } from \"../debug.js\";\nimport { DEFAULT_API_NAME, getExposedApi, createTracker } from \"./internal.js\";\n\n/**\n * RpcBuilder for fluent API definition\n *\n * Accumulates method definitions and builds a typed client.\n */\nclass RpcBuilder {\n private methods: Map<string, { kind: \"call\" | \"stream\" }> = new Map();\n private options: RpcClientOptions & RpcDebugOptions;\n private apiName: string;\n\n constructor(\n options: RpcClientOptions & RpcDebugOptions = {},\n apiName: string = DEFAULT_API_NAME,\n ) {\n this.options = options;\n this.apiName = apiName;\n }\n\n /**\n * Add a regular RPC method\n *\n * @param name - Method name\n * @param _handler - Handler function for type inference only (not executed)\n * @returns Builder with method added\n */\n add<K extends string, P extends any[], R>(\n name: K,\n _handler: (...args: P) => R,\n ): RpcBuilderWithMethod<K, P, R> {\n this.methods.set(name, { kind: \"call\" });\n return this as unknown as RpcBuilderWithMethod<K, P, R>;\n }\n\n /**\n * Add a stream RPC method\n *\n * @param name - Method name\n * @param _handler - Handler function for type inference only (not executed)\n * @returns Builder with stream method added\n */\n stream<K extends string, P extends any[], R>(\n name: K,\n _handler: (...args: P) => R,\n ): RpcBuilderWithMethod<K, P, R> {\n this.methods.set(name, { kind: \"stream\" });\n return this as unknown as RpcBuilderWithMethod<K, P, R>;\n }\n\n /**\n * Build the final typed API client\n *\n * @returns Typed RPC client\n */\n build(): Record<string, (...args: any[]) => any> {\n const api = getExposedApi(this.apiName);\n if (!api) {\n throw new Error(\n `RPC API not found. Make sure exposeRpcApi() is called in your preload script with apiName='${this.apiName}'.`,\n );\n }\n\n const { timeout = 30000, debug, logger } = this.options;\n\n const debugTracker = createTracker(debug, logger ?? getLogger());\n const requestIdCounter = { value: 0 };\n\n // Capture methods map for use in proxy\n const methods = this.methods;\n\n // Create a proxy that routes method calls to the underlying API\n return new Proxy({} as Record<string, (...args: any[]) => any>, {\n get(_target, prop: string) {\n const methodInfo = methods.get(prop);\n if (!methodInfo) {\n throw new Error(`Method '${prop}' was not registered in the RPC builder`);\n }\n\n if (methodInfo.kind === \"stream\") {\n return (...args: unknown[]) => {\n debugTracker.onStream(prop, args);\n return api.stream(prop, ...args);\n };\n }\n\n // Regular method - handle both calls and notifications\n return (...args: unknown[]) => {\n const requestId = ++requestIdCounter.value;\n const startTime = performance.now();\n\n debugTracker.onRequest(prop, args, requestId);\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n const duration = Math.round(performance.now() - startTime);\n debugTracker.onError(prop, args, `Timeout after ${timeout}ms`, duration, requestId);\n reject(new RpcTimeoutError(timeout));\n }, timeout);\n\n api\n .call(prop, ...args)\n .then((result: unknown) => {\n clearTimeout(timeoutId);\n const duration = Math.round(performance.now() - startTime);\n debugTracker.onResponse(prop, args, result, duration, requestId);\n resolve(result as any);\n })\n .catch((error: Error) => {\n clearTimeout(timeoutId);\n const duration = Math.round(performance.now() - startTime);\n debugTracker.onError(prop, args, error.message, duration, requestId);\n reject(error);\n });\n });\n };\n },\n });\n }\n}\n\n/**\n * Builder type with one method added\n * Provides fluent chaining for building the API\n */\ntype RpcBuilderWithMethod<K extends string, P extends any[], R> = {\n [M in K]: (...args: P) => R extends void ? void : Promise<Awaited<R>>;\n} & Omit<RpcBuilderMethods, K>;\n\n/**\n * Available methods on the builder\n */\ntype RpcBuilderMethods = {\n add: <K extends string, P extends any[], R>(\n name: K,\n handler: (...args: P) => R,\n ) => RpcBuilderWithMethod<K, P, R>;\n stream: <K extends string, P extends any[], R>(\n name: K,\n handler: (...args: P) => R,\n ) => RpcBuilderWithMethod<K, P, R>;\n build: () => Record<string, (...args: any[]) => any>;\n};\n\n/**\n * Create a fluent RPC API builder\n *\n * Build a typed RPC API by chaining method definitions.\n * Types are inferred from the handler functions.\n *\n * @param options - Client options\n * @returns Builder instance\n */\nexport function createRpc(options: RpcClientOptions & RpcDebugOptions = {}): Omit<\n RpcBuilder,\n \"build\"\n> & {\n build: () => Record<string, (...args: any[]) => any>;\n} {\n return new RpcBuilder(options);\n}\n\n/**\n * Re-export types for convenience\n */\nexport type { RpcBuilder, RpcBuilderMethods, RpcBuilderWithMethod };\n"],"mappings":";;;;;;;;;;AAiBA,IAAM,aAAN,MAAiB;CACf,AAAQ,0BAAoD,IAAI,KAAK;CACrE,AAAQ;CACR,AAAQ;CAER,YACE,UAA8C,EAAE,EAChD,UAAkB,kBAClB;AACA,OAAK,UAAU;AACf,OAAK,UAAU;;;;;;;;;CAUjB,IACE,MACA,UAC+B;AAC/B,OAAK,QAAQ,IAAI,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,SAAO;;;;;;;;;CAUT,OACE,MACA,UAC+B;AAC/B,OAAK,QAAQ,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC1C,SAAO;;;;;;;CAQT,QAAiD;EAC/C,MAAM,MAAM,cAAc,KAAK,QAAQ;AACvC,MAAI,CAAC,IACH,OAAM,IAAI,MACR,8FAA8F,KAAK,QAAQ,IAC5G;EAGH,MAAM,EAAE,UAAU,KAAO,OAAO,WAAW,KAAK;EAEhD,MAAM,eAAe,cAAc,OAAO,UAAU,WAAW,CAAC;EAChE,MAAM,mBAAmB,EAAE,OAAO,GAAG;EAGrC,MAAM,UAAU,KAAK;AAGrB,SAAO,IAAI,MAAM,EAAE,EAA6C,EAC9D,IAAI,SAAS,MAAc;GACzB,MAAM,aAAa,QAAQ,IAAI,KAAK;AACpC,OAAI,CAAC,WACH,OAAM,IAAI,MAAM,WAAW,KAAK,yCAAyC;AAG3E,OAAI,WAAW,SAAS,SACtB,SAAQ,GAAG,SAAoB;AAC7B,iBAAa,SAAS,MAAM,KAAK;AACjC,WAAO,IAAI,OAAO,MAAM,GAAG,KAAK;;AAKpC,WAAQ,GAAG,SAAoB;IAC7B,MAAM,YAAY,EAAE,iBAAiB;IACrC,MAAM,YAAY,YAAY,KAAK;AAEnC,iBAAa,UAAU,MAAM,MAAM,UAAU;AAE7C,WAAO,IAAI,SAAS,SAAS,WAAW;KACtC,MAAM,YAAY,iBAAiB;MACjC,MAAM,WAAW,KAAK,MAAM,YAAY,KAAK,GAAG,UAAU;AAC1D,mBAAa,QAAQ,MAAM,MAAM,iBAAiB,QAAQ,KAAK,UAAU,UAAU;AACnF,aAAO,IAAI,gBAAgB,QAAQ,CAAC;QACnC,QAAQ;AAEX,SACG,KAAK,MAAM,GAAG,KAAK,CACnB,MAAM,WAAoB;AACzB,mBAAa,UAAU;MACvB,MAAM,WAAW,KAAK,MAAM,YAAY,KAAK,GAAG,UAAU;AAC1D,mBAAa,WAAW,MAAM,MAAM,QAAQ,UAAU,UAAU;AAChE,cAAQ,OAAc;OACtB,CACD,OAAO,UAAiB;AACvB,mBAAa,UAAU;MACvB,MAAM,WAAW,KAAK,MAAM,YAAY,KAAK,GAAG,UAAU;AAC1D,mBAAa,QAAQ,MAAM,MAAM,MAAM,SAAS,UAAU,UAAU;AACpE,aAAO,MAAM;OACb;MACJ;;KAGP,CAAC;;;;;;;;;;;;AAoCN,SAAgB,UAAU,UAA8C,EAAE,EAKxE;AACA,QAAO,IAAI,WAAW,QAAQ"}