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.
- package/LICENSE +21 -0
- package/README.md +978 -0
- package/README.zh-CN.md +978 -0
- package/dist/debug.d.mts +92 -0
- package/dist/debug.d.mts.map +1 -0
- package/dist/debug.mjs +206 -0
- package/dist/debug.mjs.map +1 -0
- package/dist/error-xVRu7Lxq.mjs +131 -0
- package/dist/error-xVRu7Lxq.mjs.map +1 -0
- package/dist/event.d.mts +71 -0
- package/dist/event.d.mts.map +1 -0
- package/dist/event.mjs +60 -0
- package/dist/event.mjs.map +1 -0
- package/dist/index.d.mts +78 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +4 -0
- package/dist/internal-BZK_0O3n.mjs +23 -0
- package/dist/internal-BZK_0O3n.mjs.map +1 -0
- package/dist/main.d.mts +151 -0
- package/dist/main.d.mts.map +1 -0
- package/dist/main.mjs +329 -0
- package/dist/main.mjs.map +1 -0
- package/dist/preload.d.mts +23 -0
- package/dist/preload.d.mts.map +1 -0
- package/dist/preload.mjs +417 -0
- package/dist/preload.mjs.map +1 -0
- package/dist/renderer/builder.d.mts +64 -0
- package/dist/renderer/builder.d.mts.map +1 -0
- package/dist/renderer/builder.mjs +101 -0
- package/dist/renderer/builder.mjs.map +1 -0
- package/dist/renderer/client.d.mts +42 -0
- package/dist/renderer/client.d.mts.map +1 -0
- package/dist/renderer/client.mjs +136 -0
- package/dist/renderer/client.mjs.map +1 -0
- package/dist/renderer/event.d.mts +17 -0
- package/dist/renderer/event.d.mts.map +1 -0
- package/dist/renderer/event.mjs +117 -0
- package/dist/renderer/event.mjs.map +1 -0
- package/dist/renderer/index.d.mts +6 -0
- package/dist/renderer/index.mjs +6 -0
- package/dist/stream.d.mts +38 -0
- package/dist/stream.d.mts.map +1 -0
- package/dist/stream.mjs +80 -0
- package/dist/stream.mjs.map +1 -0
- package/dist/types-BnGse9DF.d.mts +201 -0
- package/dist/types-BnGse9DF.d.mts.map +1 -0
- package/package.json +92 -0
package/dist/preload.mjs
ADDED
|
@@ -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"}
|