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,978 @@
1
+ # electron-json-rpc
2
+
3
+ [English](./README.md) | [简体中文](./README.zh-CN.md)
4
+
5
+ 基于 JSON-RPC 2.0 协议的 Electron 类型安全 IPC 通信库。定义一次 API,即可在主进程、preload 脚本和渲染进程中获得完整的类型推断。支持流式传输、事件总线、带重试的请求队列和参数验证。
6
+
7
+ > **状态**: 本项目目前处于 **beta** 阶段。API 可能会发生变化。欢迎反馈和贡献!
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ bun add electron-json-rpc
13
+ # 或
14
+ npm install electron-json-rpc
15
+ ```
16
+
17
+ ## 特性
18
+
19
+ - **符合 JSON-RPC 2.0 标准** - 标准的请求/响应协议
20
+ - **类型安全** - 完整的 TypeScript 支持,支持类型化方法定义
21
+ - **事件总线** - 内置发布-订阅模式,支持实时事件
22
+ - **参数验证** - 通用验证器接口,兼容任何验证库
23
+ - **流式传输** - 支持 Web 标准 `ReadableStream` 的服务端推送流
24
+ - **通知** - 无需响应的单向调用
25
+ - **超时处理** - 可配置的 RPC 调用超时
26
+ - **批量请求** - 支持单次调用发送多个请求
27
+
28
+ ## 快速开始
29
+
30
+ ### 主进程
31
+
32
+ ```typescript
33
+ import { app, BrowserWindow } from "electron";
34
+ import { RpcServer } from "electron-json-rpc/main";
35
+
36
+ const rpc = new RpcServer();
37
+
38
+ // 注册方法
39
+ rpc.register("add", (a: number, b: number) => a + b);
40
+ rpc.register("greet", async (name: string) => {
41
+ return `你好,${name}!`;
42
+ });
43
+
44
+ // 开始监听
45
+ rpc.listen();
46
+ ```
47
+
48
+ ### Preload 脚本
49
+
50
+ 现在支持两种模式:**自动代理模式**(推荐)和**白名单模式**。
51
+
52
+ #### 自动代理模式(推荐)
53
+
54
+ 无需定义方法名,直接调用主进程注册的任何方法:
55
+
56
+ ```typescript
57
+ import { exposeRpcApi } from "electron-json-rpc/preload";
58
+ import { contextBridge, ipcRenderer } from "electron";
59
+
60
+ exposeRpcApi({
61
+ contextBridge,
62
+ ipcRenderer,
63
+ });
64
+ ```
65
+
66
+ 在渲染进程中可以直接调用任何方法:
67
+
68
+ ```typescript
69
+ // 直接调用,无需预定义
70
+ const sum = await window.rpc.add(1, 2);
71
+ const greeting = await window.rpc.greet("世界");
72
+
73
+ // 也可以使用通用 call 方法
74
+ const result = await window.rpc.call("methodName", arg1, arg2);
75
+
76
+ // 发送通知(无需响应)
77
+ window.rpc.log("来自渲染进程的问候!");
78
+ ```
79
+
80
+ #### 白名单模式(安全性更高)
81
+
82
+ 只暴露指定的方法:
83
+
84
+ ```typescript
85
+ import { exposeRpcApi } from "electron-json-rpc/preload";
86
+ import { contextBridge, ipcRenderer } from "electron";
87
+
88
+ exposeRpcApi({
89
+ contextBridge,
90
+ ipcRenderer,
91
+ methods: ["add", "greet"], // 只暴露这些方法
92
+ });
93
+ ```
94
+
95
+ 在渲染进程中:
96
+
97
+ ```typescript
98
+ // 白名单方法可以直接调用
99
+ const sum = await window.rpc.add(1, 2);
100
+ const greeting = await window.rpc.greet("世界");
101
+
102
+ // 非白名单方法需要使用 call 方法
103
+ const result = await window.rpc.call("otherMethod", arg1, arg2);
104
+ ```
105
+
106
+ ### 渲染进程
107
+
108
+ ```typescript
109
+ import { createRpcClient } from "electron-json-rpc/renderer";
110
+
111
+ const rpc = createRpcClient();
112
+
113
+ // 调用方法
114
+ const result = await rpc.call("add", 1, 2);
115
+ console.log(result); // 3
116
+
117
+ // 发送通知(无需响应)
118
+ rpc.notify("log", "来自渲染进程的问候!");
119
+ ```
120
+
121
+ ## 通信模式对比
122
+
123
+ 本库支持三种主要的通信模式,可根据安全性和类型安全需求选择:
124
+
125
+ | 模式 | Preload 定义 | Renderer 使用 | 安全性 | 类型安全 | 适用场景 |
126
+ | ---------------- | ---------------------------------------------------------------- | ----------------------------------- | ----------------- | ----------- | ------------ |
127
+ | **自动代理** | `exposeRpcApi({ contextBridge, ipcRenderer })` | `await window.rpc.anyMethod()` | ⚠️ 任意方法可调用 | ❌ 无类型 | 快速原型开发 |
128
+ | **白名单** | `exposeRpcApi({ contextBridge, ipcRenderer, methods: ["add"] })` | `await window.rpc.add()` | ✅ 仅允许指定方法 | ❌ 无类型 | 生产环境推荐 |
129
+ | **类型化客户端** | 任意模式 | `const api = defineRpcApi<MyApi>()` | ✅ 取决于 preload | ✅ 完整类型 | 大型项目首选 |
130
+
131
+ ### 自动代理模式(最简洁)
132
+
133
+ ```typescript
134
+ // Preload - 无需定义方法名
135
+ exposeRpcApi({ contextBridge, ipcRenderer });
136
+
137
+ // Renderer - 直接调用任意方法
138
+ await window.rpc.add(1, 2);
139
+ ```
140
+
141
+ ### 白名单模式(推荐生产使用)
142
+
143
+ ```typescript
144
+ // Preload - 只暴露指定方法
145
+ exposeRpcApi({ contextBridge, ipcRenderer, methods: ["add", "greet"] });
146
+
147
+ // Renderer - 白名单方法直接调用
148
+ await window.rpc.add(1, 2);
149
+ ```
150
+
151
+ ### 类型化客户端(完整类型安全)
152
+
153
+ ```typescript
154
+ // Renderer - 定义接口获得完整类型
155
+ const api = defineRpcApi<{
156
+ add: (a: number, b: number) => number;
157
+ log: (msg: string) => void;
158
+ }>();
159
+
160
+ await api.add(1, 2); // 完全类型化!
161
+ ```
162
+
163
+ ## 类型化 API
164
+
165
+ 定义你的 API 接口以获得完整的类型安全:
166
+
167
+ ```typescript
168
+ // 定义 API 类型
169
+ type AppApi = {
170
+ add: (a: number, b: number) => number;
171
+ greet: (name: string) => Promise<string>;
172
+ log: (message: string) => void; // 通知
173
+ };
174
+
175
+ // 创建类型化客户端
176
+ import { createTypedRpcClient } from "electron-json-rpc/renderer";
177
+
178
+ const rpc = createTypedRpcClient<AppApi>();
179
+
180
+ // 完全类型化!
181
+ const sum = await rpc.add(1, 2);
182
+ const greeting = await rpc.greet("世界");
183
+ rpc.log("这是一个通知");
184
+ ```
185
+
186
+ ### 接口风格 API (defineRpcApi)
187
+
188
+ 使用 `defineRpcApi` 可以更语义化地定义 API:
189
+
190
+ ```typescript
191
+ import { defineRpcApi } from "electron-json-rpc/renderer";
192
+
193
+ // 定义 API 接口
194
+ interface AppApi {
195
+ getUserList(): Promise<{ id: string; name: string }[]>;
196
+ getUser(id: string): Promise<{ id: string; name: string }>;
197
+ deleteUser(id: string): Promise<void>;
198
+ log(message: string): void; // 通知
199
+ dataStream(count: number): ReadableStream<number>;
200
+ }
201
+
202
+ const api = defineRpcApi<AppApi>({ timeout: 10000 });
203
+
204
+ // 完全类型化的使用方式
205
+ const users = await api.getUserList();
206
+ const user = await api.getUser("123");
207
+ await api.deleteUser("123");
208
+ api.log("完成"); // 通知(void 返回)
209
+
210
+ // 流支持
211
+ for await (const n of api.dataStream(10)) {
212
+ console.log(n);
213
+ }
214
+ ```
215
+
216
+ ### 构建器模式 (createRpc)
217
+
218
+ 使用流式构建器模式构建 API,支持类型推断:
219
+
220
+ ```typescript
221
+ import { createRpc } from "electron-json-rpc/renderer";
222
+
223
+ interface User {
224
+ id: string;
225
+ name: string;
226
+ }
227
+
228
+ const api = createRpc({ timeout: 10000 })
229
+ .add("getUserList", () => Promise<User[]>())
230
+ .add("getUser", (id: string) => Promise<User>())
231
+ .add("deleteUser", (id: string) => Promise<void>())
232
+ .add("log", (message: string) => {}) // void 返回 = 通知
233
+ .stream("dataStream", (count: number) => new ReadableStream<number>())
234
+ .build();
235
+
236
+ // 完全类型化的使用 - 类型从处理函数签名推断
237
+ const users = await api.getUserList();
238
+ const user = await api.getUser("123");
239
+ await api.deleteUser("123");
240
+ api.log("完成"); // 通知(void)
241
+
242
+ // 流支持
243
+ for await (const n of api.dataStream(10)) {
244
+ console.log(n);
245
+ }
246
+ ```
247
+
248
+ ## 请求队列
249
+
250
+ 对于需要处理不可靠连接或繁忙主进程的应用,队列 RPC 客户端提供自动请求排队和重试逻辑。
251
+
252
+ ### 基本用法
253
+
254
+ ```typescript
255
+ import { createQueuedRpcClient } from "electron-json-rpc/renderer";
256
+
257
+ const rpc = createQueuedRpcClient({
258
+ maxSize: 50, // 最大队列大小
259
+ fullBehavior: "evictOldest", // 队列满时的行为
260
+ timeout: 10000, // 请求超时时间(毫秒)
261
+ });
262
+
263
+ // 调用方法 - 如果主进程繁忙,请求将被排队
264
+ const result = await rpc.call("getData", id);
265
+
266
+ // 发送通知(不排队 - 发后即忘)
267
+ rpc.notify("log", "来自渲染进程的问候!");
268
+
269
+ // 获取队列状态
270
+ console.log(rpc.getQueueStatus());
271
+ // { pending: 2, active: 1, maxSize: 50, isPaused: false, isConnected: true }
272
+ ```
273
+
274
+ ### 队列配置
275
+
276
+ ```typescript
277
+ const rpc = createQueuedRpcClient({
278
+ // 队列大小设置
279
+ maxSize: 100,
280
+ fullBehavior: "reject", // 'reject' | 'evict' | 'evictOldest'
281
+
282
+ // 重试设置
283
+ retry: {
284
+ maxAttempts: 3, // 最大重试次数
285
+ backoff: "exponential", // 'fixed' | 'exponential'
286
+ initialDelay: 1000, // 初始延迟(毫秒)
287
+ maxDelay: 10000, // 最大延迟(毫秒)
288
+ },
289
+
290
+ // 连接健康检查设置
291
+ healthCheck: true, // 启用连接健康检查
292
+ healthCheckInterval: 5000, // 健康检查间隔(毫秒)
293
+ });
294
+ ```
295
+
296
+ ### 队列满时的行为
297
+
298
+ 当队列达到最大大小时:
299
+
300
+ - **`"reject"`**(默认):为新请求抛出 `RpcQueueFullError`
301
+ - **`"evict"`**: 驱逐当前正在添加的请求
302
+ - **`"evictOldest"`**: 从队列中移除最旧的请求
303
+
304
+ ```typescript
305
+ // 拒绝模式(默认)
306
+ const rpc = createQueuedRpcClient({
307
+ maxSize: 10,
308
+ fullBehavior: "reject",
309
+ });
310
+
311
+ try {
312
+ await rpc.call("someMethod");
313
+ } catch (error) {
314
+ if (error.name === "RpcQueueFullError") {
315
+ console.log("队列已满!");
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### 队列控制方法
321
+
322
+ ```typescript
323
+ const rpc = createQueuedRpcClient();
324
+
325
+ // 检查队列是否健康(已连接且未暂停)
326
+ if (rpc.isQueueHealthy()) {
327
+ await rpc.call("someMethod");
328
+ }
329
+
330
+ // 获取详细的队列状态
331
+ const status = rpc.getQueueStatus();
332
+ console.log(`待处理: ${status.pending}, 活跃: ${status.active}`);
333
+
334
+ // 暂停队列处理(新请求将排队)
335
+ rpc.pauseQueue();
336
+
337
+ // 恢复队列处理
338
+ rpc.resumeQueue();
339
+
340
+ // 清除所有待处理的请求
341
+ rpc.clearQueue();
342
+ ```
343
+
344
+ ### 重试策略
345
+
346
+ 队列会根据配置的策略自动重试失败的请求:
347
+
348
+ ```typescript
349
+ const rpc = createQueuedRpcClient({
350
+ retry: {
351
+ maxAttempts: 3,
352
+ backoff: "exponential", // 或 "fixed"
353
+ initialDelay: 1000,
354
+ maxDelay: 10000,
355
+ },
356
+ });
357
+
358
+ // 指数退避: 1000ms, 2000ms, 4000ms, ...
359
+ // 固定延迟: 1000ms, 1000ms, 1000ms, ...
360
+ ```
361
+
362
+ 以下情况的请求会被重试:
363
+
364
+ - 超时错误 (`RpcTimeoutError`)
365
+ - 连接错误 (`RpcConnectionError`)
366
+
367
+ ### 错误处理
368
+
369
+ ```typescript
370
+ import {
371
+ RpcQueueFullError,
372
+ RpcConnectionError,
373
+ RpcQueueEvictedError,
374
+ isQueueFullError,
375
+ isConnectionError,
376
+ isQueueEvictedError,
377
+ } from "electron-json-rpc/renderer";
378
+
379
+ const rpc = createQueuedRpcClient();
380
+
381
+ try {
382
+ await rpc.call("someMethod");
383
+ } catch (error) {
384
+ if (isQueueFullError(error)) {
385
+ console.log(`队列已满: ${error.currentSize}/${error.maxSize}`);
386
+ } else if (isConnectionError(error)) {
387
+ console.log(`连接丢失: ${error.message}`);
388
+ } else if (isQueueEvictedError(error)) {
389
+ console.log(`请求被驱逐: ${error.reason}`);
390
+ }
391
+ }
392
+ ```
393
+
394
+ ## 调试日志
395
+
396
+ 渲染端客户端提供内置的调试日志功能,用于监控 RPC 请求和响应。这对于开发和故障排查非常有用。
397
+
398
+ ### 全局调试模式
399
+
400
+ 为所有 RPC 客户端全局启用调试日志:
401
+
402
+ ```typescript
403
+ import { setRpcDebug, isRpcDebug } from "electron-json-rpc/renderer";
404
+
405
+ // 为所有客户端启用调试日志
406
+ setRpcDebug(true);
407
+
408
+ // 检查是否启用了调试
409
+ if (isRpcDebug()) {
410
+ console.log("调试模式已激活");
411
+ }
412
+
413
+ // 禁用调试日志
414
+ setRpcDebug(false);
415
+ ```
416
+
417
+ ### 单客户端调试选项
418
+
419
+ 为特定客户端启用调试日志:
420
+
421
+ ```typescript
422
+ import { createRpcClient } from "electron-json-rpc/renderer";
423
+
424
+ const rpc = createRpcClient({
425
+ debug: true, // 为此客户端启用调试日志
426
+ timeout: 10000,
427
+ });
428
+
429
+ // 适用于所有客户端类型
430
+ const api = createTypedRpcClient<MyApi>({ debug: true });
431
+ const events = createEventBus<AppEvents>({ debug: true });
432
+ ```
433
+
434
+ ### 自定义日志记录器
435
+
436
+ 提供自定义日志记录函数以完全控制调试输出:
437
+
438
+ ```typescript
439
+ import { createRpcClient, type RpcLogger } from "electron-json-rpc/renderer";
440
+
441
+ const customLogger: RpcLogger = (entry) => {
442
+ const { type, method, params, result, error, duration, requestId } = entry;
443
+
444
+ console.log(`[RPC ${type.toUpperCase()}] ${method}`, {
445
+ params,
446
+ result,
447
+ error,
448
+ duration,
449
+ requestId,
450
+ });
451
+ };
452
+
453
+ const rpc = createRpcClient({
454
+ logger: customLogger,
455
+ });
456
+ ```
457
+
458
+ ### 默认日志记录器输出
459
+
460
+ 使用默认日志记录器(启用 `debug: true`)时,您将看到格式化的控制台输出:
461
+
462
+ ```
463
+ [RPC] 10:30:15.123 → request add [#1]
464
+ params: [1, 2]
465
+
466
+ [RPC] 10:30:15.145 ← response add [#1] (22ms)
467
+ params: [1, 2]
468
+ result: 3
469
+
470
+ [RPC] 10:30:16.234 → notify log
471
+ params: ['Hello']
472
+
473
+ [RPC] 10:30:17.456 → request getUser [#2]
474
+ params: ['123']
475
+
476
+ [RPC] 10:30:17.567 ← error getUser [#2] (111ms)
477
+ params: ['123']
478
+ error: Method not found
479
+ ```
480
+
481
+ ### 日志条目类型
482
+
483
+ 日志记录器接收以下类型的条目:
484
+
485
+ - **`request`** - 发出的 RPC 请求
486
+ - **`response`** - 成功的 RPC 响应
487
+ - **`error`** - 失败的 RPC 请求
488
+ - **`notify`** - 单向通知
489
+ - **`stream`** - 流请求
490
+ - **`event`** - 事件总线接收到的事件
491
+
492
+ ## 事件总线
493
+
494
+ 事件总线使用发布-订阅模式实现从主进程到渲染进程的实时通信。
495
+
496
+ ### 主进程
497
+
498
+ ```typescript
499
+ import { RpcServer } from "electron-json-rpc/main";
500
+
501
+ const rpc = new RpcServer();
502
+ rpc.listen();
503
+
504
+ // 向所有已订阅的渲染进程发布事件
505
+ rpc.publish("user-updated", { id: "123", name: "张三" });
506
+ rpc.publish("data-changed", { items: [1, 2, 3] });
507
+
508
+ // 检查订阅者数量
509
+ console.log(rpc.getEventSubscribers());
510
+ // { "user-updated": 2, "data-changed": 1 }
511
+ ```
512
+
513
+ ### 渲染进程
514
+
515
+ ```typescript
516
+ // 使用暴露的 API(通过 preload)
517
+ const unsubscribe = window.rpc.on("user-updated", (data) => {
518
+ console.log("用户已更新:", data);
519
+ });
520
+
521
+ // 使用返回的取消订阅函数
522
+ unsubscribe();
523
+
524
+ // 或手动取消订阅
525
+ window.rpc.off("user-updated");
526
+
527
+ // 单次订阅(首次事件后自动取消订阅)
528
+ window.rpc.once("notification", (data) => {
529
+ console.log("收到通知:", data);
530
+ });
531
+ ```
532
+
533
+ ### 类型化事件总线
534
+
535
+ 使用 `createEventBus` 和事件定义实现完整的类型安全:
536
+
537
+ ```typescript
538
+ import { createEventBus } from "electron-json-rpc/renderer";
539
+
540
+ // 定义事件
541
+ interface AppEvents {
542
+ "user-updated": { id: string; name: string };
543
+ "data-changed": { items: number[] };
544
+ notification: { message: string; type: "info" | "warning" };
545
+ }
546
+
547
+ const events = createEventBus<AppEvents>();
548
+
549
+ // 完全类型化!
550
+ const unsubscribe = events.on("user-updated", (data) => {
551
+ console.log(data.name); // TypeScript 知道这是 string
552
+ });
553
+
554
+ // 单次订阅
555
+ events.once("data-changed", (data) => {
556
+ console.log(data.items); // number[]
557
+ });
558
+
559
+ // 取消订阅
560
+ unsubscribe();
561
+ ```
562
+
563
+ ### 事件总线方法
564
+
565
+ **主进程 (`RpcServer`):**
566
+
567
+ - `publish(eventName, data?)` - 向所有已订阅的渲染进程发布事件
568
+ - `getEventSubscribers(): Record<string, number>` - 获取每个事件的订阅者数量
569
+
570
+ **渲染进程:**
571
+
572
+ - `on(eventName, callback)` - 订阅事件,返回取消订阅函数
573
+ - `off(eventName, callback?)` - 取消订阅事件(或事件的所有回调)
574
+ - `once(eventName, callback)` - 单次订阅,首次事件后自动取消订阅
575
+
576
+ ## 流式传输
577
+
578
+ 使用 Web 标准 `ReadableStream` 从主进程向渲染进程传输数据:
579
+
580
+ ### 主进程
581
+
582
+ ```typescript
583
+ import { RpcServer } from "electron-json-rpc/main";
584
+
585
+ const rpc = new RpcServer();
586
+
587
+ // 注册流方法
588
+ rpc.registerStream("counter", async (count: number) => {
589
+ return new ReadableStream({
590
+ async start(controller) {
591
+ for (let i = 0; i < count; i++) {
592
+ controller.enqueue({ index: i, value: i * 2 });
593
+ // 模拟异步操作
594
+ await new Promise((r) => setTimeout(r, 100));
595
+ }
596
+ controller.close();
597
+ },
598
+ });
599
+ });
600
+
601
+ // 从 fetch 或其他异步源传输流
602
+ rpc.registerStream("fetchData", async (url: string) => {
603
+ const response = await fetch(url);
604
+ return response.body!;
605
+ });
606
+
607
+ rpc.listen();
608
+ ```
609
+
610
+ ### 渲染进程
611
+
612
+ ```typescript
613
+ import { createRpcClient } from "electron-json-rpc/renderer";
614
+
615
+ const rpc = createRpcClient();
616
+
617
+ // 使用 for-await-of(推荐)
618
+ for await (const chunk of rpc.stream("counter", 10)) {
619
+ console.log(chunk); // { index: 0, value: 0 }, { index: 1, value: 2 }, ...
620
+ }
621
+
622
+ // 直接使用 reader
623
+ const stream = rpc.stream("counter", 5);
624
+ const reader = stream.getReader();
625
+ while (true) {
626
+ const { done, value } = await reader.read();
627
+ if (done) break;
628
+ console.log(value);
629
+ }
630
+ reader.release();
631
+
632
+ // 通过 Response 管道传输
633
+ const response = new Response(rpc.stream("fetchData", "https://api.example.com/data"));
634
+ const blob = await response.blob();
635
+ ```
636
+
637
+ ### 流工具
638
+
639
+ ```typescript
640
+ import { asyncGeneratorToStream, iterableToStream } from "electron-json-rpc/stream";
641
+
642
+ // 将异步生成器转换为流
643
+ rpc.registerStream("numbers", () => {
644
+ return asyncGeneratorToStream(async function* () {
645
+ for (let i = 0; i < 10; i++) {
646
+ yield i;
647
+ await new Promise((r) => setTimeout(r, 100));
648
+ }
649
+ });
650
+ });
651
+
652
+ // 将数组/可迭代对象转换为流
653
+ rpc.registerStream("items", () => {
654
+ return iterableToStream([1, 2, 3, 4, 5]);
655
+ });
656
+ ```
657
+
658
+ ## 参数验证
659
+
660
+ 你可以为任何 RPC 方法添加参数验证。该库提供了通用的验证器接口,可与任何验证库配合使用。
661
+
662
+ ### 自定义验证器
663
+
664
+ ```typescript
665
+ import { RpcServer } from "electron-json-rpc/main";
666
+
667
+ const rpc = new RpcServer();
668
+
669
+ // 简单的自定义验证器
670
+ rpc.register("divide", (a: number, b: number) => a / b, {
671
+ validate: (params) => {
672
+ const [, divisor] = params as [number, number];
673
+ if (divisor === 0) {
674
+ throw new Error("不能除以零");
675
+ }
676
+ },
677
+ });
678
+ ```
679
+
680
+ ### 使用 Zod
681
+
682
+ ```typescript
683
+ import { z } from "zod";
684
+ import { RpcServer } from "electron-json-rpc/main";
685
+
686
+ const rpc = new RpcServer();
687
+
688
+ const userSchema = z.object({
689
+ name: z.string().min(1),
690
+ age: z.number().min(0).max(150),
691
+ });
692
+
693
+ rpc.register(
694
+ "createUser",
695
+ async (user: unknown) => {
696
+ // user 已经验证
697
+ return db.users.create(user);
698
+ },
699
+ {
700
+ validate: (params) => {
701
+ const result = userSchema.safeParse(params[0]);
702
+ if (!result.success) {
703
+ throw new Error(result.error.errors[0].message);
704
+ }
705
+ },
706
+ description: "创建新用户",
707
+ },
708
+ );
709
+ ```
710
+
711
+ ### 使用 TypeBox
712
+
713
+ ```typescript
714
+ import { Type, type Static } from "@sinclair/typebox";
715
+ import { Value } from "@sinclair/typebox/value";
716
+ import { RpcServer } from "electron-json-rpc/main";
717
+
718
+ const rpc = new RpcServer();
719
+
720
+ const UserSchema = Type.Object({
721
+ name: Type.String({ minLength: 1 }),
722
+ age: Type.Number({ minimum: 0, maximum: 150 }),
723
+ });
724
+
725
+ type User = Static<typeof UserSchema>;
726
+
727
+ rpc.register(
728
+ "createUser",
729
+ async (user: User) => {
730
+ return db.users.create(user);
731
+ },
732
+ {
733
+ validate: (params) => {
734
+ const errors = Value.Errors(UserSchema, params[0]);
735
+ if (errors.length > 0) {
736
+ throw new Error([...errors][0].message);
737
+ }
738
+ },
739
+ },
740
+ );
741
+ ```
742
+
743
+ ### 使用 Ajv
744
+
745
+ ```typescript
746
+ import Ajv from "ajv";
747
+ import { RpcServer } from "electron-json-rpc/main";
748
+
749
+ const rpc = new RpcServer();
750
+ const ajv = new Ajv();
751
+
752
+ const validateUser = ajv.compile({
753
+ type: "object",
754
+ properties: {
755
+ name: { type: "string", minLength: 1 },
756
+ age: { type: "number", minimum: 0, maximum: 150 },
757
+ },
758
+ required: ["name", "age"],
759
+ additionalProperties: false,
760
+ });
761
+
762
+ rpc.register(
763
+ "createUser",
764
+ async (user) => {
765
+ return db.users.create(user);
766
+ },
767
+ {
768
+ validate: (params) => {
769
+ if (!validateUser(params[0])) {
770
+ throw new Error(ajv.errorsText(validateUser.errors));
771
+ }
772
+ },
773
+ },
774
+ );
775
+ ```
776
+
777
+ ## API 参考
778
+
779
+ ### 主进程 (`electron-json-rpc/main`)
780
+
781
+ #### `RpcServer`
782
+
783
+ ```typescript
784
+ const rpc = new RpcServer();
785
+ ```
786
+
787
+ **方法:**
788
+
789
+ - `register(name: string, handler: Function, options?)` - 注册 RPC 方法
790
+ - `options.validate?: (params: unknown[]) => void | Promise<void>` - 验证函数
791
+ - `options.description?: string` - 方法描述
792
+ - `registerStream(name: string, handler: Function, options?)` - 注册流方法
793
+ - 处理函数应返回 `ReadableStream`
794
+ - `publish(eventName: string, data?)` - 向所有已订阅的渲染进程发布事件
795
+ - `getEventSubscribers(): Record<string, number>` - 获取每个事件的订阅者数量
796
+ - `unregister(name: string)` - 注销方法
797
+ - `has(name: string): boolean` - 检查方法是否存在
798
+ - `getMethodNames(): string[]` - 获取所有已注册的方法名
799
+ - `listen()` - 开始监听 IPC 消息
800
+ - `dispose()` - 停止监听并清理
801
+
802
+ ### Preload (`electron-json-rpc/preload`)
803
+
804
+ #### `exposeRpcApi(options)`
805
+
806
+ 向渲染进程暴露 RPC API。
807
+
808
+ ```typescript
809
+ exposeRpcApi({
810
+ contextBridge,
811
+ ipcRenderer,
812
+ methods: ["method1", "method2"], // 可选白名单
813
+ apiName: "rpc", // 默认: 'rpc'
814
+ });
815
+ ```
816
+
817
+ #### `createPreloadClient(ipcRenderer, timeout?)`
818
+
819
+ 创建用于 preload 脚本的客户端(不向渲染进程暴露)。
820
+
821
+ ### 渲染进程 (`electron-json-rpc/renderer`)
822
+
823
+ #### `createRpcClient(options?)`
824
+
825
+ 创建无类型 RPC 客户端。
826
+
827
+ ```typescript
828
+ const rpc = createRpcClient({
829
+ timeout: 10000, // 默认: 30000ms
830
+ apiName: "rpc", // 默认: 'rpc'
831
+ });
832
+
833
+ await rpc.call("methodName", arg1, arg2);
834
+ rpc.notify("notificationMethod", arg1);
835
+ rpc.stream("streamMethod", arg1); // 返回 ReadableStream
836
+ ```
837
+
838
+ #### `createTypedRpcClient<T>(options?)`
839
+
840
+ 创建具有完整类型安全的类型化 RPC 客户端。
841
+
842
+ ```typescript
843
+ type MyApi = {
844
+ foo: (x: number) => string;
845
+ };
846
+
847
+ const rpc = createTypedRpcClient<MyApi>();
848
+ await rpc.foo(42);
849
+ ```
850
+
851
+ #### `defineRpcApi<T>(options?)`
852
+
853
+ 从接口定义类型化 RPC API(`createTypedRpcClient` 的语义化别名)。
854
+
855
+ ```typescript
856
+ interface AppApi {
857
+ getUser(id: string): Promise<User>;
858
+ log(msg: string): void;
859
+ }
860
+
861
+ const api = defineRpcApi<AppApi>();
862
+ await api.getUser("123");
863
+ api.log("你好");
864
+ ```
865
+
866
+ #### `createRpc(options?)`
867
+
868
+ 创建支持类型推断的流式 API 构建器。
869
+
870
+ ```typescript
871
+ const api = createRpc()
872
+ .add("getUser", (id: string) => Promise<User>())
873
+ .add("log", (msg: string) => {})
874
+ .stream("dataStream", (n: number) => new ReadableStream<number>())
875
+ .build();
876
+
877
+ await api.getUser("123");
878
+ api.log("你好");
879
+ ```
880
+
881
+ #### `useRpcProxy(options?)`
882
+
883
+ 使用 preload 暴露的代理(如果提供了方法白名单)。
884
+
885
+ #### `createQueuedRpcClient(options?)`
886
+
887
+ 创建具有自动重试和连接健康检查的队列 RPC 客户端。
888
+
889
+ ```typescript
890
+ const rpc = createQueuedRpcClient({
891
+ maxSize: 100, // 最大队列大小(默认: 100)
892
+ fullBehavior: "reject", // 'reject' | 'evict' | 'evictOldest'
893
+ timeout: 30000, // 请求超时时间(毫秒,默认: 30000)
894
+ retry: {
895
+ maxAttempts: 3, // 最大重试次数(默认: 3)
896
+ backoff: "exponential", // 'fixed' | 'exponential'
897
+ initialDelay: 1000, // 初始延迟(毫秒,默认: 1000)
898
+ maxDelay: 10000, // 最大延迟(毫秒,默认: 10000)
899
+ },
900
+ healthCheck: true, // 启用健康检查(默认: true)
901
+ healthCheckInterval: 5000, // 健康检查间隔(毫秒,默认: 5000)
902
+ apiName: "rpc", // API 名称(默认: 'rpc')
903
+ });
904
+
905
+ // 队列控制方法
906
+ rpc.getQueueStatus(); // 返回 QueueStatus
907
+ rpc.clearQueue(); // 清除所有待处理的请求
908
+ rpc.pauseQueue(); // 暂停队列处理
909
+ rpc.resumeQueue(); // 恢复队列处理
910
+ rpc.isQueueHealthy(); // 如果已连接且未暂停则返回 true
911
+ ```
912
+
913
+ #### `createEventBus<T>(options?)`
914
+
915
+ 创建用于主进程实时事件的类型化事件总线。
916
+
917
+ ```typescript
918
+ interface AppEvents {
919
+ "user-updated": { id: string; name: string };
920
+ "data-changed": { items: number[] };
921
+ }
922
+
923
+ const events = createEventBus<AppEvents>();
924
+
925
+ // 使用完整的类型安全订阅
926
+ const unsubscribe = events.on("user-updated", (data) => {
927
+ console.log(data.name); // TypeScript 知道这是 string
928
+ });
929
+
930
+ // 取消订阅
931
+ unsubscribe();
932
+
933
+ // 单次订阅
934
+ events.once("data-changed", (data) => {
935
+ console.log(data.items);
936
+ });
937
+ ```
938
+
939
+ ## 错误处理
940
+
941
+ JSON-RPC 错误返回标准错误码:
942
+
943
+ | Code | Message |
944
+ | ------ | ---------------- |
945
+ | -32700 | Parse error |
946
+ | -32600 | Invalid Request |
947
+ | -32601 | Method not found |
948
+ | -32602 | Invalid params |
949
+ | -32603 | Internal error |
950
+
951
+ 超时错误使用自定义的 `RpcTimeoutError` 类。
952
+
953
+ ## 包大小
954
+
955
+ ESM 打包大小:
956
+
957
+ | Package | gzip |
958
+ | ---------------- | ------- |
959
+ | Preload | 3.95 kB |
960
+ | Main | 2.97 kB |
961
+ | Queue | 1.99 kB |
962
+ | Debug | 1.50 kB |
963
+ | Renderer/client | 1.14 kB |
964
+ | Renderer/builder | 1.21 kB |
965
+ | Renderer/event | 1.15 kB |
966
+ | Renderer/queue | 0.93 kB |
967
+ | Stream | 0.72 kB |
968
+ | Event | 0.43 kB |
969
+
970
+ ## 系统要求
971
+
972
+ - **Electron**: >= 18.0.0(推荐 >= 32.0.0)
973
+ - **Node.js**: >= 16.9.0
974
+ - **TypeScript**: >= 5.0.0(如果使用 TypeScript)
975
+
976
+ ## 许可证
977
+
978
+ MIT