kkrpc 0.5.0 → 0.6.1

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 (160) hide show
  1. package/README.md +782 -110
  2. package/dist/browser-mod.cjs +1 -194
  3. package/dist/browser-mod.d.cts +15 -7
  4. package/dist/browser-mod.d.cts.map +1 -1
  5. package/dist/browser-mod.d.ts +16 -8
  6. package/dist/browser-mod.d.ts.map +1 -1
  7. package/dist/browser-mod.js +1 -174
  8. package/dist/browser-mod.js.map +1 -1
  9. package/dist/{channel-C92Q3vK-.d.ts → channel-ChGarSnI.d.cts} +6 -2
  10. package/dist/channel-ChGarSnI.d.cts.map +1 -0
  11. package/dist/{channel-QTVxXCE5.d.cts → channel-K9w_2vmv.d.ts} +6 -2
  12. package/dist/channel-K9w_2vmv.d.ts.map +1 -0
  13. package/dist/channel-LGw9tl8f.js +7 -0
  14. package/dist/channel-LGw9tl8f.js.map +1 -0
  15. package/dist/channel-OGNDLfsd.cjs +6 -0
  16. package/dist/chrome-extension.cjs +1 -83
  17. package/dist/chrome-extension.d.cts +8 -4
  18. package/dist/chrome-extension.d.cts.map +1 -1
  19. package/dist/chrome-extension.d.ts +9 -5
  20. package/dist/chrome-extension.d.ts.map +1 -1
  21. package/dist/chrome-extension.js +1 -72
  22. package/dist/chrome-extension.js.map +1 -1
  23. package/dist/chunk-DjWAcSYV.cjs +1 -0
  24. package/dist/deno-B319Kxci.js +2 -0
  25. package/dist/deno-B319Kxci.js.map +1 -0
  26. package/dist/{deno-BzNLlX8S.d.ts → deno-BNN8LLrd.d.cts} +6 -2
  27. package/dist/deno-BNN8LLrd.d.cts.map +1 -0
  28. package/dist/deno-Cl_O9sLW.cjs +1 -0
  29. package/dist/{deno-noilmrf7.d.cts → deno-DS3TcO_u.d.ts} +6 -2
  30. package/dist/deno-DS3TcO_u.d.ts.map +1 -0
  31. package/dist/deno-mod.cjs +1 -10
  32. package/dist/deno-mod.d.cts +4 -4
  33. package/dist/deno-mod.d.ts +4 -4
  34. package/dist/deno-mod.js +1 -4
  35. package/dist/electron-ipc.cjs +1 -0
  36. package/dist/electron-ipc.d.cts +142 -0
  37. package/dist/electron-ipc.d.cts.map +1 -0
  38. package/dist/electron-ipc.d.ts +142 -0
  39. package/dist/electron-ipc.d.ts.map +1 -0
  40. package/dist/electron-ipc.js +2 -0
  41. package/dist/electron-ipc.js.map +1 -0
  42. package/dist/electron.cjs +1 -0
  43. package/dist/electron.d.cts +69 -0
  44. package/dist/electron.d.cts.map +1 -0
  45. package/dist/electron.d.ts +69 -0
  46. package/dist/electron.d.ts.map +1 -0
  47. package/dist/electron.js +2 -0
  48. package/dist/electron.js.map +1 -0
  49. package/dist/http-BEp3rEW8.js +2 -0
  50. package/dist/http-BEp3rEW8.js.map +1 -0
  51. package/dist/{http-Cvnz7K2R.d.cts → http-CcLOuQmc.d.ts} +12 -2
  52. package/dist/http-CcLOuQmc.d.ts.map +1 -0
  53. package/dist/{http-xFcyKRFw.d.ts → http-DWq6Ez_h.d.cts} +12 -2
  54. package/dist/http-DWq6Ez_h.d.cts.map +1 -0
  55. package/dist/http-DoUu8nzZ.cjs +1 -0
  56. package/dist/http.cjs +1 -23
  57. package/dist/http.d.cts +3 -3
  58. package/dist/http.d.ts +3 -3
  59. package/dist/http.js +1 -19
  60. package/dist/http.js.map +1 -1
  61. package/dist/{interface-DIWzRMw1.d.cts → interface-DPtHJBBS.d.cts} +9 -10
  62. package/dist/interface-DPtHJBBS.d.cts.map +1 -0
  63. package/dist/{interface-Bt8kzlyu.d.ts → interface-DRqrAKo-.d.ts} +9 -10
  64. package/dist/interface-DRqrAKo-.d.ts.map +1 -0
  65. package/dist/kafka-BMOHF0lZ.d.ts +98 -0
  66. package/dist/kafka-BMOHF0lZ.d.ts.map +1 -0
  67. package/dist/kafka-DrhWN0Wx.js +2 -0
  68. package/dist/kafka-DrhWN0Wx.js.map +1 -0
  69. package/dist/kafka-_Fcc7erL.d.cts +98 -0
  70. package/dist/kafka-_Fcc7erL.d.cts.map +1 -0
  71. package/dist/kafka-x0zuIRyy.cjs +1 -0
  72. package/dist/kafka.cjs +1 -0
  73. package/dist/kafka.d.cts +3 -0
  74. package/dist/kafka.d.ts +3 -0
  75. package/dist/kafka.js +1 -0
  76. package/dist/mod.cjs +3 -222
  77. package/dist/mod.d.cts +292 -14
  78. package/dist/mod.d.cts.map +1 -1
  79. package/dist/mod.d.ts +293 -15
  80. package/dist/mod.d.ts.map +1 -1
  81. package/dist/mod.js +3 -197
  82. package/dist/mod.js.map +1 -1
  83. package/dist/nats-Ba4bXoY5.js +2 -0
  84. package/dist/nats-Ba4bXoY5.js.map +1 -0
  85. package/dist/nats-Bkr44tQB.d.cts +78 -0
  86. package/dist/nats-Bkr44tQB.d.cts.map +1 -0
  87. package/dist/nats-CMglzmZn.cjs +1 -0
  88. package/dist/nats-D3XOZAGl.d.ts +78 -0
  89. package/dist/nats-D3XOZAGl.d.ts.map +1 -0
  90. package/dist/nats.cjs +1 -0
  91. package/dist/nats.d.cts +3 -0
  92. package/dist/nats.d.ts +3 -0
  93. package/dist/nats.js +1 -0
  94. package/dist/rabbitmq-B5NVNJ3X.cjs +1 -0
  95. package/dist/rabbitmq-CpeO6XdQ.d.ts +61 -0
  96. package/dist/rabbitmq-CpeO6XdQ.d.ts.map +1 -0
  97. package/dist/rabbitmq-DAUXsuvL.d.cts +61 -0
  98. package/dist/rabbitmq-DAUXsuvL.d.cts.map +1 -0
  99. package/dist/rabbitmq-DbxfLKQj.js +2 -0
  100. package/dist/rabbitmq-DbxfLKQj.js.map +1 -0
  101. package/dist/rabbitmq.cjs +1 -0
  102. package/dist/rabbitmq.d.cts +5 -0
  103. package/dist/rabbitmq.d.ts +5 -0
  104. package/dist/rabbitmq.js +1 -0
  105. package/dist/redis-streams-BV472jY5.js +2 -0
  106. package/dist/redis-streams-BV472jY5.js.map +1 -0
  107. package/dist/redis-streams-CGnDQYMH.cjs +1 -0
  108. package/dist/redis-streams-DpbNc20y.d.cts +97 -0
  109. package/dist/redis-streams-DpbNc20y.d.cts.map +1 -0
  110. package/dist/redis-streams-avvO_U2r.d.ts +97 -0
  111. package/dist/redis-streams-avvO_U2r.d.ts.map +1 -0
  112. package/dist/redis-streams.cjs +1 -0
  113. package/dist/redis-streams.d.cts +3 -0
  114. package/dist/redis-streams.d.ts +3 -0
  115. package/dist/redis-streams.js +1 -0
  116. package/dist/socketio.cjs +1 -123
  117. package/dist/socketio.d.cts +12 -4
  118. package/dist/socketio.d.cts.map +1 -1
  119. package/dist/socketio.d.ts +12 -4
  120. package/dist/socketio.d.ts.map +1 -1
  121. package/dist/socketio.js +1 -120
  122. package/dist/socketio.js.map +1 -1
  123. package/dist/tauri-CSoj53la.js +2 -0
  124. package/dist/tauri-CSoj53la.js.map +1 -0
  125. package/dist/{tauri-DwFX963h.d.cts → tauri-DIJzjZwG.d.ts} +16 -4
  126. package/dist/tauri-DIJzjZwG.d.ts.map +1 -0
  127. package/dist/tauri-DvS2Czwp.cjs +1 -0
  128. package/dist/{tauri-BAorUr9n.d.ts → tauri-d0yDsUnV.d.cts} +16 -4
  129. package/dist/tauri-d0yDsUnV.d.cts.map +1 -0
  130. package/dist/{transfer-handlers-BbGAQoCs.d.ts → transfer-handlers-3wBZpi5F.d.ts} +2 -2
  131. package/dist/{transfer-handlers-BbGAQoCs.d.ts.map → transfer-handlers-3wBZpi5F.d.ts.map} +1 -1
  132. package/dist/{transfer-handlers-ZiPAV3RZ.d.cts → transfer-handlers-zy5mMRny.d.cts} +2 -2
  133. package/dist/{transfer-handlers-ZiPAV3RZ.d.cts.map → transfer-handlers-zy5mMRny.d.cts.map} +1 -1
  134. package/dist/{utils-BQ7rl7lY.d.ts → utils-BEgiHk89.d.ts} +1 -1
  135. package/dist/utils-BEgiHk89.d.ts.map +1 -0
  136. package/package.json +85 -5
  137. package/dist/channel-BYtPphRu.js +0 -743
  138. package/dist/channel-BYtPphRu.js.map +0 -1
  139. package/dist/channel-C92Q3vK-.d.ts.map +0 -1
  140. package/dist/channel-CgIjXyNX.cjs +0 -839
  141. package/dist/channel-QTVxXCE5.d.cts.map +0 -1
  142. package/dist/chunk-CUT6urMc.cjs +0 -30
  143. package/dist/deno-BzNLlX8S.d.ts.map +0 -1
  144. package/dist/deno-DAHG1vvV.js +0 -33
  145. package/dist/deno-DAHG1vvV.js.map +0 -1
  146. package/dist/deno-DyicMPIa.cjs +0 -38
  147. package/dist/deno-noilmrf7.d.cts.map +0 -1
  148. package/dist/http-5KkjLA6X.js +0 -97
  149. package/dist/http-5KkjLA6X.js.map +0 -1
  150. package/dist/http-Bq1OqmJP.cjs +0 -109
  151. package/dist/http-Cvnz7K2R.d.cts.map +0 -1
  152. package/dist/http-xFcyKRFw.d.ts.map +0 -1
  153. package/dist/interface-Bt8kzlyu.d.ts.map +0 -1
  154. package/dist/interface-DIWzRMw1.d.cts.map +0 -1
  155. package/dist/tauri-BAorUr9n.d.ts.map +0 -1
  156. package/dist/tauri-C7MVgjRt.js +0 -160
  157. package/dist/tauri-C7MVgjRt.js.map +0 -1
  158. package/dist/tauri-DwFX963h.d.cts.map +0 -1
  159. package/dist/tauri-JjaZEH0s.cjs +0 -177
  160. package/dist/utils-BQ7rl7lY.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  <div align="center">
2
2
 
3
-
4
3
  # 🚀 kkrpc
5
4
 
6
5
  ## TypeScript-First RPC Library
7
6
 
7
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/kunkunsh/kkrpc)
8
+ [![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=for-the-badge&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/kunkunsh/kkrpc)
8
9
  [![NPM Version](https://img.shields.io/npm/v/kkrpc?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/kkrpc)
9
10
  [![JSR Version](https://img.shields.io/jsr/v/@kunkun/kkrpc?style=for-the-badge&logo=deno)](https://jsr.io/@kunkun/kkrpc)
10
11
  [![License](https://img.shields.io/npm/l/kkrpc?style=for-the-badge)](https://github.com/kunkunsh/kkrpc/blob/main/LICENSE)
@@ -12,6 +13,7 @@
12
13
  [![GitHub stars](https://img.shields.io/github/stars/kunkunsh/kkrpc?style=for-the-badge&logo=github)](https://github.com/kunkunsh/kkrpc)
13
14
  [![Typedoc Documentation](https://img.shields.io/badge/Docs-Typedoc-blue?style=for-the-badge&logo=typescript)](https://kunkunsh.github.io/kkrpc/)
14
15
  [![Excalidraw Diagrams](https://img.shields.io/badge/Diagrams-Excalidraw-orange?style=for-the-badge&logo=drawio)](https://excalidraw.com/#json=xp6GbAJVAx3nU-h3PhaxW,oYBNvYmCRsQ2XR3MQo73Ug)
16
+ [![LLM Docs](https://img.shields.io/badge/LLM-Docs-green?style=for-the-badge&logo=openai)](https://docs.kkrpc.kunkun.sh/llms.txt)
15
17
 
16
18
  </div>
17
19
 
@@ -25,10 +27,11 @@ Call remote functions as if they were local, with full TypeScript type safety an
25
27
 
26
28
  **Similar to Comlink but with bidirectional communication** and support for multiple environments - both client and server can expose functions for the other to call across Node.js, Deno, Bun, and browser environments.
27
29
 
28
- [**Quick Start**](#-quick-start) • [**Documentation**](https://kunkunsh.github.io/kkrpc/) • [**Examples**](#-examples) • [**API Reference**](https://jsr.io/@kunkun/kkrpc/doc)
30
+ [**Quick Start**](#-quick-start) • [**Documentation**](https://kunkunsh.github.io/kkrpc/) • [**Examples**](#-examples) • [**API Reference**](https://jsr.io/@kunkun/kkrpc/doc) • [**LLM Docs**](https://docs.kkrpc.kunkun.sh/llms.txt) • [**中文文档**](./README.zh.md)
29
31
 
30
32
  <div align="center">
31
33
 
34
+ <img src="https://imgur.com/19XswxO.jpg" style="max-width: 800px; width: 100%; margin-bottom: 20px;"/>
32
35
  <img src="https://imgur.com/vR3Lmv0.png" style="max-height: 200px; margin: 10px;"/>
33
36
  <img src="https://i.imgur.com/zmOHNfu.png" style="max-height: 250px; margin: 10px;"/>
34
37
  <img src="https://imgur.com/u728aVv.png" style="max-height: 400px; margin: 10px;"/>
@@ -46,19 +49,19 @@ kkrpc stands out in the crowded RPC landscape by offering **true cross-runtime c
46
49
 
47
50
  <div align="center">
48
51
 
49
- | Feature | Description |
50
- |---------|-------------|
51
- | **🔄 Cross-runtime** | Works seamlessly across Node.js, Deno, Bun, browsers, and more |
52
- | **🛡️ Type-safe** | Full TypeScript inference and IDE autocompletion support |
53
- | **↔️ Bidirectional** | Both endpoints can expose and call APIs simultaneously |
54
- | **🏠 Property Access** | Remote getters/setters with dot notation (`await api.prop`) |
55
- | **💥 Error Preservation** | Complete error objects across RPC boundaries |
56
- | **🌐 Multiple Transports** | stdio, HTTP, WebSocket, postMessage, Chrome extensions |
57
- | **📞 Callback Support** | Remote functions can accept callback functions |
58
- | **🔗 Nested Calls** | Deep method chaining like `api.math.operations.calculate()` |
59
- | **📦 Auto Serialization** | Intelligent JSON/superjson detection |
60
- | **⚡ Zero Config** | No schema files or code generation required |
61
- | **🚀 Transferable Objects** | Zero-copy transfers for large data (40-100x faster) |
52
+ | Feature | Description |
53
+ | --------------------------- | -------------------------------------------------------------- |
54
+ | **🔄 Cross-runtime** | Works seamlessly across Node.js, Deno, Bun, browsers, and more |
55
+ | **🛡️ Type-safe** | Full TypeScript inference and IDE autocompletion support |
56
+ | **↔️ Bidirectional** | Both endpoints can expose and call APIs simultaneously |
57
+ | **🏠 Property Access** | Remote getters/setters with dot notation (`await api.prop`) |
58
+ | **💥 Error Preservation** | Complete error objects across RPC boundaries |
59
+ | **🌐 Multiple Transports** | stdio, HTTP, WebSocket, postMessage, Chrome extensions |
60
+ | **📞 Callback Support** | Remote functions can accept callback functions |
61
+ | **🔗 Nested Calls** | Deep method chaining like `api.math.operations.calculate()` |
62
+ | **📦 Auto Serialization** | Intelligent JSON/superjson detection |
63
+ | **⚡ Zero Config** | No schema files or code generation required |
64
+ | **🚀 Transferable Objects** | Zero-copy transfers for large data (40-100x faster) |
62
65
 
63
66
  </div>
64
67
 
@@ -68,40 +71,47 @@ kkrpc stands out in the crowded RPC landscape by offering **true cross-runtime c
68
71
 
69
72
  ```mermaid
70
73
  graph LR
71
- A[kkrpc] --> B[Node.js]
72
- A --> C[Deno]
73
- A --> D[Bun]
74
- A --> E[Browser]
75
- A --> F[Chrome Extension]
76
- A --> G[Tauri]
77
-
78
- B -.-> H[stdio]
79
- C -.-> H
80
- D -.-> H
81
-
82
- E -.-> I[postMessage]
83
- E -.-> J[Web Workers]
84
- E -.-> K[iframes]
85
-
86
- F -.-> L[Chrome Ports]
87
-
88
- G -.-> M[Shell Plugin]
89
-
90
- style A fill:#ff6b6b,stroke:#333,stroke-width:2px
74
+ A[kkrpc] --> B[Node.js]
75
+ A --> C[Deno]
76
+ A --> D[Bun]
77
+ A --> E[Browser]
78
+ A --> F[Chrome Extension]
79
+ A --> G[Tauri]
80
+
81
+ B -.-> H[stdio]
82
+ C -.-> H
83
+ D -.-> H
84
+
85
+ E -.-> I[postMessage]
86
+ E -.-> J[Web Workers]
87
+ E -.-> K[iframes]
88
+
89
+ F -.-> L[Chrome Ports]
90
+
91
+ G -.-> M[Shell Plugin]
92
+
93
+ style A fill:#ff6b6b,stroke:#333,stroke-width:2px
91
94
  ```
92
95
 
93
96
  </div>
94
97
 
95
98
  ### 📡 Transport Protocols
96
99
 
97
- | Transport | Use Case | Supported Runtimes |
98
- |-----------|----------|-------------------|
99
- | **stdio** | Process-to-process communication | Node.js ↔ Deno ↔ Bun |
100
- | **postMessage** | Browser context communication | Browser ↔ Web Workers ↔ iframes |
101
- | **HTTP** | Web API communication | All runtimes |
102
- | **WebSocket** | Real-time communication | All runtimes |
103
- | **Socket.IO** | Enhanced real-time with rooms/namespaces | All runtimes |
104
- | **Chrome Extension** | Extension component communication | Chrome Extension contexts |
100
+ | Transport | Use Case | Supported Runtimes |
101
+ | -------------------- | ----------------------------------------------------- | -------------------------------------- |
102
+ | **stdio** | Process-to-process communication | Node.js ↔ Deno ↔ Bun |
103
+ | **postMessage** | Browser context communication | Browser ↔ Web Workers ↔ iframes |
104
+ | **HTTP** | Web API communication | All runtimes |
105
+ | **WebSocket** | Real-time communication | All runtimes |
106
+ | **Hono WebSocket** | High-performance WebSocket with Hono framework | Node.js, Deno, Bun, Cloudflare Workers |
107
+ | **Socket.IO** | Enhanced real-time with rooms/namespaces | All runtimes |
108
+ | **Elysia WebSocket** | Modern TypeScript framework WebSocket integration | Bun, Node.js, Deno |
109
+ | **Chrome Extension** | Extension component communication | Chrome Extension contexts |
110
+ | **RabbitMQ** | Message queue communication | Node.js, Deno, Bun |
111
+ | **Redis Streams** | Stream-based messaging with persistence | Node.js, Deno, Bun |
112
+ | **Kafka** | Distributed streaming platform | Node.js, Deno, Bun |
113
+ | **NATS** | High-performance messaging system | Node.js, Deno, Bun |
114
+ | **Electron** | Desktop app IPC (Renderer ↔ Main ↔ Utility Process) | Electron |
105
115
 
106
116
  The core of **kkrpc** design is in `RPCChannel` and `IoInterface`.
107
117
 
@@ -161,7 +171,7 @@ For backward compatibility, the receiving side will automatically detect the ser
161
171
 
162
172
  ### Installation
163
173
 
164
- <div align="center">
174
+
165
175
 
166
176
  ```bash
167
177
  # npm
@@ -177,41 +187,35 @@ pnpm add kkrpc
177
187
  import { RPCChannel } from "jsr:@kunkun/kkrpc"
178
188
  ```
179
189
 
180
- </div>
181
-
182
190
  ### Basic Example
183
191
 
184
- <div align="center">
185
-
186
192
  ```typescript
187
193
  // server.ts
188
- import { RPCChannel, NodeIo } from "kkrpc"
194
+ import { NodeIo, RPCChannel } from "kkrpc"
189
195
 
190
196
  const api = {
191
- greet: (name: string) => `Hello, ${name}!`,
192
- add: (a: number, b: number) => a + b
197
+ greet: (name: string) => `Hello, ${name}!`,
198
+ add: (a: number, b: number) => a + b
193
199
  }
194
200
 
195
201
  const rpc = new RPCChannel(new NodeIo(process.stdin, process.stdout), {
196
- expose: api
202
+ expose: api
197
203
  })
198
204
  ```
199
205
 
200
206
  ```typescript
201
207
  // client.ts
202
- import { RPCChannel, NodeIo } from "kkrpc"
203
208
  import { spawn } from "child_process"
209
+ import { NodeIo, RPCChannel } from "kkrpc"
204
210
 
205
211
  const worker = spawn("deno", ["run", "server.ts"])
206
212
  const rpc = new RPCChannel(new NodeIo(worker.stdout, worker.stdin))
207
213
  const api = rpc.getAPI<typeof api>()
208
214
 
209
215
  console.log(await api.greet("World")) // "Hello, World!"
210
- console.log(await api.add(5, 3)) // 8
216
+ console.log(await api.add(5, 3)) // 8
211
217
  ```
212
218
 
213
- </div>
214
-
215
219
  ## 📚 Examples
216
220
 
217
221
  Below are simple examples to get you started quickly.
@@ -278,9 +282,13 @@ kkrpc preserves complete error information across RPC boundaries:
278
282
  ```ts
279
283
  // Custom error class
280
284
  class DatabaseError extends Error {
281
- constructor(message: string, public code: number, public query: string) {
285
+ constructor(
286
+ message: string,
287
+ public code: number,
288
+ public query: string
289
+ ) {
282
290
  super(message)
283
- this.name = 'DatabaseError'
291
+ this.name = "DatabaseError"
284
292
  }
285
293
  }
286
294
 
@@ -302,11 +310,11 @@ try {
302
310
  await api.getUserById("")
303
311
  } catch (error) {
304
312
  // All error properties are preserved:
305
- console.log(error.name) // "DatabaseError"
306
- console.log(error.message) // "Invalid user ID"
307
- console.log(error.code) // 400
308
- console.log(error.query) // "SELECT * FROM users WHERE id = ?"
309
- console.log(error.stack) // Full stack trace
313
+ console.log(error.name) // "DatabaseError"
314
+ console.log(error.message) // "Invalid user ID"
315
+ console.log(error.code) // 400
316
+ console.log(error.query) // "SELECT * FROM users WHERE id = ?"
317
+ console.log(error.stack) // Full stack trace
310
318
  console.log(error.timestamp) // ISO timestamp
311
319
  console.log(error.requestId) // Request ID
312
320
  }
@@ -341,14 +349,14 @@ expect(sum).toBe(3)
341
349
  kkrpc supports zero-copy transfer of large data structures using browser's native transferable objects. This provides 40-100x performance improvement for large binary data transfers.
342
350
 
343
351
  ```ts
344
- import { RPCChannel, WorkerParentIO, transfer } from "kkrpc/browser"
352
+ import { RPCChannel, transfer, WorkerParentIO } from "kkrpc/browser"
345
353
 
346
354
  const worker = new Worker("worker.js")
347
355
  const io = new WorkerParentIO(worker)
348
356
  const rpc = new RPCChannel(io)
349
357
  const api = rpc.getAPI<{
350
- processBuffer(buffer: ArrayBuffer): Promise<number>
351
- generateData(size: number): Promise<ArrayBuffer>
358
+ processBuffer(buffer: ArrayBuffer): Promise<number>
359
+ generateData(size: number): Promise<ArrayBuffer>
352
360
  }>()
353
361
 
354
362
  // Create a large buffer (10MB)
@@ -367,13 +375,174 @@ const newBuffer = await api.generateData(5 * 1024 * 1024)
367
375
  console.log("Received from worker:", newBuffer.byteLength) // 5242880
368
376
  ```
369
377
 
378
+ ### Hono WebSocket Example
379
+
380
+ Hono WebSocket adapter provides seamless integration with the Hono framework's high-performance WebSocket support.
381
+
382
+ #### `server.ts`
383
+
384
+ ```ts
385
+ import { Hono } from "hono"
386
+ import { upgradeWebSocket, websocket } from "hono/bun"
387
+ import { createHonoWebSocketHandler } from "kkrpc"
388
+ import { apiMethods, type API } from "./api"
389
+
390
+ const app = new Hono()
391
+
392
+ app.get(
393
+ "/ws",
394
+ upgradeWebSocket(() => {
395
+ return createHonoWebSocketHandler<API>({
396
+ expose: apiMethods
397
+ })
398
+ })
399
+ )
400
+
401
+ const server = Bun.serve({
402
+ port: 3000,
403
+ fetch: app.fetch,
404
+ websocket
405
+ })
406
+
407
+ console.log(`Server running on port ${server.port}`)
408
+ ```
409
+
410
+ #### `client.ts`
411
+
412
+ ```ts
413
+ import { RPCChannel, WebSocketClientIO } from "kkrpc"
414
+ import { apiMethods, type API } from "./api"
415
+
416
+ const clientIO = new WebSocketClientIO({
417
+ url: "ws://localhost:3000/ws"
418
+ })
419
+
420
+ const clientRPC = new RPCChannel<API, API>(clientIO, {
421
+ expose: apiMethods
422
+ })
423
+
424
+ const api = clientRPC.getAPI()
425
+
426
+ // Test basic RPC calls
427
+ console.log(await api.add(5, 3)) // 8
428
+ console.log(await api.echo("Hello from Hono!")) // "Hello from Hono!"
429
+
430
+ // Test nested API calls
431
+ console.log(await api.math.grade2.multiply(4, 6)) // 24
432
+
433
+ // Test property access
434
+ console.log(await api.counter) // 42
435
+ console.log(await api.nested.value) // "hello world"
436
+
437
+ clientIO.destroy()
438
+ ```
439
+
440
+ **Hono WebSocket Features:**
441
+
442
+ - **High Performance**: Built on Hono's ultra-fast WebSocket implementation
443
+ - **Cross-runtime**: Works across Bun, Deno, Node.js, and Cloudflare Workers
444
+ - **Type-safe**: Full TypeScript support with Hono integration
445
+ - **Bidirectional**: Both client and server can expose APIs
446
+ - **Framework Integration**: Seamless integration with Hono's middleware ecosystem
447
+
448
+ **Learn more:** [Hono WebSocket Documentation](https://hono.dev/docs/helpers/websocket)
449
+
450
+ ### Elysia WebSocket Example
451
+
452
+ Elysia WebSocket adapter provides seamless integration with the modern TypeScript-first Elysia framework and its uWebSocket-powered WebSocket support.
453
+
454
+ #### `server.ts`
455
+
456
+ ```ts
457
+ import { Elysia } from "elysia"
458
+ import { ElysiaWebSocketServerIO, RPCChannel } from "kkrpc"
459
+ import { apiMethods, type API } from "./api"
460
+
461
+ // Extend API for Elysia-specific features
462
+ interface ElysiaAPI extends API {
463
+ getConnectionInfo(): Promise<{
464
+ remoteAddress: string | undefined
465
+ query: Record<string, string>
466
+ headers: Record<string, string>
467
+ }>
468
+ }
469
+
470
+ const app = new Elysia()
471
+ .ws("/rpc", {
472
+ open(ws) {
473
+ const io = new ElysiaWebSocketServerIO(ws)
474
+ const elysiaApiMethods: ElysiaAPI = {
475
+ ...apiMethods,
476
+ getConnectionInfo: async () => ({
477
+ remoteAddress: io.getRemoteAddress(),
478
+ query: io.getQuery(),
479
+ headers: io.getHeaders()
480
+ })
481
+ }
482
+
483
+ const rpc = new RPCChannel<ElysiaAPI, ElysiaAPI>(io, {
484
+ expose: elysiaApiMethods
485
+ })
486
+ },
487
+ message(ws, message) {
488
+ ElysiaWebSocketServerIO.feedMessage(ws, message)
489
+ }
490
+ })
491
+ .listen(3000)
492
+
493
+ console.log("Elysia server running on port 3000")
494
+ ```
495
+
496
+ #### `client.ts`
497
+
498
+ ```ts
499
+ import { ElysiaWebSocketClientIO, RPCChannel } from "kkrpc"
500
+ import { apiMethods, type API } from "./api"
501
+
502
+ const clientIO = new ElysiaWebSocketClientIO("ws://localhost:3000/rpc")
503
+ const clientRPC = new RPCChannel<API, any>(clientIO, {
504
+ expose: apiMethods
505
+ })
506
+
507
+ const api = clientRPC.getAPI()
508
+
509
+ // Test basic RPC calls
510
+ console.log(await api.add(5, 3)) // 8
511
+ console.log(await api.echo("Hello from Elysia!")) // "Hello from Elysia!"
512
+
513
+ // Test nested API calls
514
+ console.log(await api.math.grade1.add(10, 20)) // 30
515
+ console.log(await api.math.grade3.divide(20, 4)) // 5
516
+
517
+ // Test Elysia-specific features
518
+ const connInfo = await api.getConnectionInfo()
519
+ console.log("Connected from:", connInfo.remoteAddress)
520
+ console.log("Query params:", connInfo.query)
521
+ console.log("Headers:", connInfo.headers)
522
+
523
+ clientIO.destroy()
524
+ ```
525
+
526
+ **Elysia WebSocket Features:**
527
+
528
+ - **Modern Framework**: Built on Elysia's TypeScript-first design
529
+ - **Ultra-fast**: Powered by uWebSocket for maximum performance
530
+ - **Rich Metadata**: Access to connection info, query params, and headers
531
+ - **Type-safe**: Full TypeScript inference and autocompletion
532
+ - **Runtime Flexible**: Works across Bun, Node.js, and Deno
533
+ - **Developer Experience**: Clean API with factory functions
534
+
535
+ **Learn more:** [Elysia WebSocket Documentation](https://elysiajs.com/patterns/websocket)
536
+
370
537
  **Key Benefits:**
538
+
371
539
  - **Zero-copy performance**: No serialization/deserialization overhead
372
540
  - **Memory efficient**: Ownership transfers without copying
373
541
  - **Automatic fallback**: Graceful degradation for non-transferable transports
374
542
  - **Type-safe**: Full TypeScript support
375
543
 
376
544
  **Supported Transferable Types:**
545
+
377
546
  - `ArrayBuffer` - Binary data buffers
378
547
  - `MessagePort` - Communication channels
379
548
  - `ImageBitmap` - Decoded image data
@@ -459,55 +628,334 @@ For Chrome extensions, use the dedicated `ChromePortIO` adapter for reliable, po
459
628
  #### `background.ts`
460
629
 
461
630
  ```ts
462
- import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension";
463
- import type { BackgroundAPI, ContentAPI } from "./types";
631
+ import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension"
632
+ import type { BackgroundAPI, ContentAPI } from "./types"
464
633
 
465
634
  const backgroundAPI: BackgroundAPI = {
466
- async getExtensionVersion() {
467
- return chrome.runtime.getManifest().version;
468
- },
469
- };
635
+ async getExtensionVersion() {
636
+ return chrome.runtime.getManifest().version
637
+ }
638
+ }
470
639
 
471
640
  chrome.runtime.onConnect.addListener((port) => {
472
- if (port.name === "content-to-background") {
473
- const io = new ChromePortIO(port);
474
- const rpc = new RPCChannel(io, { expose: backgroundAPI });
475
- // Handle disconnect
476
- port.onDisconnect.addListener(() => io.destroy());
477
- }
478
- });
641
+ if (port.name === "content-to-background") {
642
+ const io = new ChromePortIO(port)
643
+ const rpc = new RPCChannel(io, { expose: backgroundAPI })
644
+ // Handle disconnect
645
+ port.onDisconnect.addListener(() => io.destroy())
646
+ }
647
+ })
479
648
  ```
480
649
 
481
650
  #### `content.ts`
482
651
 
483
652
  ```ts
484
- import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension";
485
- import type { BackgroundAPI, ContentAPI } from "./types";
653
+ import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension"
654
+ import type { BackgroundAPI, ContentAPI } from "./types"
486
655
 
487
656
  const contentAPI: ContentAPI = {
488
- async getPageTitle() {
489
- return document.title;
490
- },
491
- };
657
+ async getPageTitle() {
658
+ return document.title
659
+ }
660
+ }
492
661
 
493
- const port = chrome.runtime.connect({ name: "content-to-background" });
494
- const io = new ChromePortIO(port);
495
- const rpc = new RPCChannel<ContentAPI, BackgroundAPI>(io, { expose: contentAPI });
662
+ const port = chrome.runtime.connect({ name: "content-to-background" })
663
+ const io = new ChromePortIO(port)
664
+ const rpc = new RPCChannel<ContentAPI, BackgroundAPI>(io, { expose: contentAPI })
496
665
 
497
- const backgroundAPI = rpc.getAPI();
666
+ const backgroundAPI = rpc.getAPI()
498
667
 
499
668
  // Example call
500
- backgroundAPI.getExtensionVersion().then(version => {
501
- console.log("Extension version:", version);
502
- });
669
+ backgroundAPI.getExtensionVersion().then((version) => {
670
+ console.log("Extension version:", version)
671
+ })
503
672
  ```
504
673
 
505
674
  **Chrome Extension Features:**
675
+
506
676
  - **Port-based**: Uses `chrome.runtime.Port` for stable, long-lived connections.
507
677
  - **Bidirectional**: Both sides can expose and call APIs.
508
678
  - **Type-safe**: Full TypeScript support for your APIs.
509
679
  - **Reliable**: Handles connection lifecycle and cleanup.
510
680
 
681
+ ### RabbitMQ Example
682
+
683
+ RabbitMQ adapter provides reliable message queue communication with support for topic exchanges and durable messaging.
684
+
685
+ #### `producer.ts`
686
+
687
+ ```ts
688
+ import { RabbitMQIO, RPCChannel } from "kkrpc"
689
+ import { apiMethods, type API } from "./api"
690
+
691
+ const rabbitmqIO = new RabbitMQIO({
692
+ url: "amqp://localhost",
693
+ exchange: "kkrpc-exchange",
694
+ exchangeType: "topic",
695
+ durable: true
696
+ })
697
+
698
+ const producerRPC = new RPCChannel<API, API>(rabbitmqIO, {
699
+ expose: apiMethods
700
+ })
701
+
702
+ const api = producerRPC.getAPI()
703
+
704
+ // Test basic RPC calls
705
+ console.log(await api.add(5, 3)) // 8
706
+ console.log(await api.echo("Hello from RabbitMQ!")) // "Hello from RabbitMQ!"
707
+
708
+ rabbitmqIO.destroy()
709
+ ```
710
+
711
+ #### `consumer.ts`
712
+
713
+ ```ts
714
+ import { RabbitMQIO, RPCChannel } from "kkrpc"
715
+ import { apiMethods, type API } from "./api"
716
+
717
+ const rabbitmqIO = new RabbitMQIO({
718
+ url: "amqp://localhost",
719
+ exchange: "kkrpc-exchange",
720
+ exchangeType: "topic",
721
+ durable: true,
722
+ sessionId: "consumer-session"
723
+ })
724
+
725
+ const consumerRPC = new RPCChannel<API, API>(rabbitmqIO, {
726
+ expose: apiMethods
727
+ })
728
+
729
+ const api = consumerRPC.getAPI()
730
+
731
+ // Process messages from producer
732
+ console.log(await api.add(10, 20)) // 30
733
+ console.log(await api.echo("Hello from consumer!")) // "Hello from consumer!"
734
+
735
+ rabbitmqIO.destroy()
736
+ ```
737
+
738
+ **RabbitMQ Features:**
739
+
740
+ - **Topic Exchange**: Flexible routing with wildcard patterns
741
+ - **Durable Messaging**: Messages survive broker restarts
742
+ - **Load Balancing**: Multiple consumers can share workload
743
+ - **Reliable Delivery**: Acknowledgments and redelivery support
744
+ - **Session Management**: Unique sessions prevent message conflicts
745
+
746
+ ### Redis Streams Example
747
+
748
+ Redis Streams adapter provides high-performance stream-based messaging with persistence and consumer group support.
749
+
750
+ #### `publisher.ts`
751
+
752
+ ```ts
753
+ import { RedisStreamsIO, RPCChannel } from "kkrpc"
754
+ import { apiMethods, type API } from "./api"
755
+
756
+ const redisIO = new RedisStreamsIO({
757
+ url: "redis://localhost:6379",
758
+ stream: "kkrpc-stream",
759
+ maxLen: 10000, // Keep only last 10k messages
760
+ maxQueueSize: 1000
761
+ })
762
+
763
+ const publisherRPC = new RPCChannel<API, API>(redisIO, {
764
+ expose: apiMethods
765
+ })
766
+
767
+ const api = publisherRPC.getAPI()
768
+
769
+ // Test basic RPC calls
770
+ console.log(await api.add(7, 8)) // 15
771
+ console.log(await api.echo("Hello from Redis Streams!")) // "Hello from Redis Streams!"
772
+
773
+ // Get stream information
774
+ const streamInfo = await redisIO.getStreamInfo()
775
+ console.log("Stream length:", streamInfo.length)
776
+
777
+ redisIO.destroy()
778
+ ```
779
+
780
+ #### `subscriber.ts`
781
+
782
+ ```ts
783
+ import { RedisStreamsIO, RPCChannel } from "kkrpc"
784
+ import { apiMethods, type API } from "./api"
785
+
786
+ // Using consumer group for load balancing
787
+ const redisIO = new RedisStreamsIO({
788
+ url: "redis://localhost:6379",
789
+ stream: "kkrpc-stream",
790
+ consumerGroup: "kkrpc-group",
791
+ consumerName: "worker-1",
792
+ useConsumerGroup: true, // Enable load balancing
793
+ maxQueueSize: 1000
794
+ })
795
+
796
+ const subscriberRPC = new RPCChannel<API, API>(redisIO, {
797
+ expose: apiMethods
798
+ })
799
+
800
+ const api = subscriberRPC.getAPI()
801
+
802
+ // Process messages with load balancing
803
+ console.log(await api.multiply(4, 6)) // 24
804
+ console.log(await api.echo("Hello from subscriber!")) // "Hello from subscriber!"
805
+
806
+ redisIO.destroy()
807
+ ```
808
+
809
+ **Redis Streams Features:**
810
+
811
+ - **Two Modes**: Pub/Sub (all consumers) or Consumer Groups (load balancing)
812
+ - **Persistence**: Messages stored in Redis with configurable retention
813
+ - **Memory Protection**: Queue size limits prevent memory issues
814
+ - **Consumer Groups**: Load balancing with message acknowledgment
815
+ - **Stream Management**: Built-in tools for monitoring and trimming streams
816
+
817
+ ### Kafka Example
818
+
819
+ Kafka adapter provides distributed streaming with high throughput and fault tolerance for large-scale systems.
820
+
821
+ #### `producer.ts`
822
+
823
+ ```ts
824
+ import { KafkaIO, RPCChannel } from "kkrpc"
825
+ import { apiMethods, type API } from "./api"
826
+
827
+ const kafkaIO = new KafkaIO({
828
+ brokers: ["localhost:9092"],
829
+ topic: "kkrpc-topic",
830
+ clientId: "kkrpc-producer",
831
+ numPartitions: 3,
832
+ replicationFactor: 1,
833
+ maxQueueSize: 1000
834
+ })
835
+
836
+ const producerRPC = new RPCChannel<API, API>(kafkaIO, {
837
+ expose: apiMethods
838
+ })
839
+
840
+ const api = producerRPC.getAPI()
841
+
842
+ // Test basic RPC calls
843
+ console.log(await api.add(12, 18)) // 30
844
+ console.log(await api.echo("Hello from Kafka!")) // "Hello from Kafka!"
845
+
846
+ console.log("Topic:", kafkaIO.getTopic())
847
+ console.log("Session ID:", kafkaIO.getSessionId())
848
+
849
+ kafkaIO.destroy()
850
+ ```
851
+
852
+ #### `consumer.ts`
853
+
854
+ ```ts
855
+ import { KafkaIO, RPCChannel } from "kkrpc"
856
+ import { apiMethods, type API } from "./api"
857
+
858
+ const kafkaIO = new KafkaIO({
859
+ brokers: ["localhost:9092"],
860
+ topic: "kkrpc-topic",
861
+ clientId: "kkrpc-consumer",
862
+ groupId: "kkrpc-consumer-group",
863
+ fromBeginning: false, // Only read new messages
864
+ maxQueueSize: 1000
865
+ })
866
+
867
+ const consumerRPC = new RPCChannel<API, API>(kafkaIO, {
868
+ expose: apiMethods
869
+ })
870
+
871
+ const api = consumerRPC.getAPI()
872
+
873
+ // Process messages from Kafka
874
+ console.log(await api.divide(100, 4)) // 25
875
+ console.log(await api.echo("Hello from Kafka consumer!")) // "Hello from Kafka consumer!"
876
+
877
+ console.log("Topic:", kafkaIO.getTopic())
878
+ console.log("Group ID:", kafkaIO.getGroupId())
879
+
880
+ kafkaIO.destroy()
881
+ ```
882
+
883
+ **Kafka Features:**
884
+
885
+ - **Distributed**: Built-in replication and partitioning
886
+ - **High Throughput**: Optimized for high-volume message streaming
887
+ - **Fault Tolerant**: Replication and automatic failover
888
+ - **Scalable**: Horizontal scaling with partitions
889
+ - **Persistent**: Durable message storage with configurable retention
890
+ - **Consumer Groups**: Load balancing across consumer instances
891
+
892
+ ### NATS Example
893
+
894
+ NATS adapter provides high-performance messaging with publish/subscribe patterns and optional queue groups for load balancing.
895
+
896
+ #### `publisher.ts`
897
+
898
+ ```ts
899
+ import { NatsIO, RPCChannel } from "kkrpc"
900
+ import { apiMethods, type API } from "./api"
901
+
902
+ const natsIO = new NatsIO({
903
+ servers: "nats://localhost:4222",
904
+ subject: "kkrpc-messages",
905
+ queueGroup: "kkrpc-group" // Optional: enables load balancing
906
+ })
907
+
908
+ const publisherRPC = new RPCChannel<API, API>(natsIO, {
909
+ expose: apiMethods
910
+ })
911
+
912
+ const api = publisherRPC.getAPI()
913
+
914
+ // Test basic RPC calls
915
+ console.log(await api.add(5, 3)) // 8
916
+ console.log(await api.echo("Hello from NATS!")) // "Hello from NATS!"
917
+
918
+ console.log("Subject:", natsIO.getSubject())
919
+ console.log("Session ID:", natsIO.getSessionId())
920
+
921
+ natsIO.destroy()
922
+ ```
923
+
924
+ #### `subscriber.ts`
925
+
926
+ ```ts
927
+ import { NatsIO, RPCChannel } from "kkrpc"
928
+ import { apiMethods, type API } from "./api"
929
+
930
+ const natsIO = new NatsIO({
931
+ servers: "nats://localhost:4222",
932
+ subject: "kkrpc-messages",
933
+ queueGroup: "kkrpc-group", // Optional: enables load balancing
934
+ sessionId: "subscriber-session"
935
+ })
936
+
937
+ const subscriberRPC = new RPCChannel<API, API>(natsIO, {
938
+ expose: apiMethods
939
+ })
940
+
941
+ const api = subscriberRPC.getAPI()
942
+
943
+ // Process messages from publisher
944
+ console.log(await api.add(10, 20)) // 30
945
+ console.log(await api.echo("Hello from subscriber!")) // "Hello from subscriber!"
946
+
947
+ natsIO.destroy()
948
+ ```
949
+
950
+ **NATS Features:**
951
+
952
+ - **High Performance**: Ultra-low latency messaging system
953
+ - **Subject-Based**: Flexible subject hierarchy for routing
954
+ - **Queue Groups**: Optional load balancing across subscribers
955
+ - **Simple Model**: Pub/Sub with request/reply support
956
+ - **Cross-Platform**: Works across Node.js, Deno, and Bun
957
+ - **No Schema Required**: Dynamic message routing without upfront configuration
958
+
511
959
  ### Tauri Example
512
960
 
513
961
  Call functions in bun/node/deno processes from Tauri app with JS/TS.
@@ -578,21 +1026,245 @@ I provided a sample tauri app in `examples/tauri-demo`.
578
1026
 
579
1027
  ![Sample Tauri App](https://i.imgur.com/nkDwRHk.png)
580
1028
 
1029
+ ### Electron Example
1030
+
1031
+ Electron adapter provides type-safe bidirectional RPC communication between Renderer process, Main process, and Utility Process.
1032
+
1033
+ There are two sets of adapters for Electron:
1034
+
1035
+ 1. **Renderer ↔ Main IPC**: `ElectronIpcMainIO` (Main side) + `ElectronIpcRendererIO` (Renderer side)
1036
+ 2. **Main ↔ Utility Process**: `ElectronUtilityProcessIO` (Main side) + `ElectronUtilityProcessChildIO` (Utility Process side)
1037
+
1038
+ #### Preload Script Setup
1039
+
1040
+ Use `createSecureIpcBridge` to create a secured `ipcRenderer` with channel whitelisting:
1041
+
1042
+ ```ts title="preload.ts"
1043
+ import { contextBridge, ipcRenderer } from "electron"
1044
+ import { createSecureIpcBridge } from "kkrpc/electron-ipc"
1045
+
1046
+ const securedIpcRenderer = createSecureIpcBridge({
1047
+ ipcRenderer,
1048
+ channelPrefix: "kkrpc-"
1049
+ })
1050
+
1051
+ contextBridge.exposeInMainWorld("electron", {
1052
+ ipcRenderer: securedIpcRenderer
1053
+ })
1054
+ ```
1055
+
1056
+ This automatically whitelists only channels starting with `"kkrpc-"`. You can also whitelist specific channels:
1057
+
1058
+ ```ts title="preload.ts"
1059
+ import { contextBridge, ipcRenderer } from "electron"
1060
+ import { createSecureIpcBridge } from "kkrpc/electron-ipc"
1061
+
1062
+ const securedIpcRenderer = createSecureIpcBridge({
1063
+ ipcRenderer,
1064
+ allowedChannels: ["kkrpc-ipc", "kkrpc-worker-relay"]
1065
+ })
1066
+
1067
+ contextBridge.exposeInMainWorld("electron", {
1068
+ ipcRenderer: securedIpcRenderer
1069
+ })
1070
+ ```
1071
+
1072
+ This approach avoids direct Electron dependencies in kkrpc, making it compatible with any Electron version.
1073
+
1074
+ #### Main Process
1075
+
1076
+ ```ts title="main.ts"
1077
+ import { app, BrowserWindow, ipcMain, utilityProcess } from "electron"
1078
+ import { ElectronUtilityProcessIO, RPCChannel } from "kkrpc/electron"
1079
+ import { ElectronIpcMainIO } from "kkrpc/electron-ipc"
1080
+
1081
+ interface MainAPI {
1082
+ showNotification(message: string): Promise<void>
1083
+ getAppVersion(): Promise<string>
1084
+ }
1085
+
1086
+ interface WorkerAPI {
1087
+ add(a: number, b: number): Promise<number>
1088
+ multiply(a: number, b: number): Promise<number>
1089
+ }
1090
+
1091
+ const mainAPI: MainAPI = {
1092
+ showNotification: async (message: string) => {
1093
+ console.log(`[Main] Notification: ${message}`)
1094
+ },
1095
+ getAppVersion: async () => app.getVersion()
1096
+ }
1097
+
1098
+ // 1. Setup Renderer ↔ Main IPC
1099
+ const win = new BrowserWindow({
1100
+ webPreferences: {
1101
+ preload: path.join(__dirname, "preload.js"),
1102
+ contextIsolation: true,
1103
+ nodeIntegration: false
1104
+ }
1105
+ })
1106
+
1107
+ const ipcIO = new ElectronIpcMainIO(ipcMain, win.webContents)
1108
+ const ipcRPC = new RPCChannel<MainAPI, object>(ipcIO, { expose: mainAPI })
1109
+
1110
+ // 2. Setup Main ↔ Utility Process
1111
+ const workerPath = path.join(__dirname, "./worker.js")
1112
+ const workerProcess = utilityProcess.fork(workerPath)
1113
+ const workerIO = new ElectronUtilityProcessIO(workerProcess)
1114
+ const workerRPC = new RPCChannel<MainAPI, WorkerAPI>(workerIO, { expose: mainAPI })
1115
+ const workerAPI = workerRPC.getAPI()
1116
+
1117
+ // Now you can call worker methods from main
1118
+ const result = await workerAPI.add(2, 3) // 5
1119
+ ```
1120
+
1121
+ #### Renderer Process
1122
+
1123
+ ```ts title="renderer.ts"
1124
+ import { ElectronIpcRendererIO, RPCChannel } from "kkrpc/electron-ipc"
1125
+
1126
+ interface MainAPI {
1127
+ showNotification(message: string): Promise<void>
1128
+ getAppVersion(): Promise<string>
1129
+ }
1130
+
1131
+ const ipcIO = new ElectronIpcRendererIO()
1132
+ const ipcRPC = new RPCChannel<object, MainAPI>(ipcIO, { expose: {} })
1133
+ const mainAPI = ipcRPC.getAPI()
1134
+
1135
+ // Call main process methods from renderer
1136
+ await mainAPI.showNotification("Hello from renderer!")
1137
+ const version = await mainAPI.getAppVersion()
1138
+ ```
1139
+
1140
+ #### Utility Process (Worker)
1141
+
1142
+ ```ts title="worker.ts"
1143
+ import { ElectronUtilityProcessChildIO, RPCChannel } from "kkrpc/electron"
1144
+
1145
+ interface MainAPI {
1146
+ showNotification(message: string): Promise<void>
1147
+ }
1148
+
1149
+ const io = new ElectronUtilityProcessChildIO()
1150
+
1151
+ const workerMethods = {
1152
+ add: async (a: number, b: number) => a + b,
1153
+ multiply: async (a: number, b: number) => a * b
1154
+ }
1155
+
1156
+ const rpc = new RPCChannel<typeof workerMethods, MainAPI>(io, {
1157
+ expose: workerMethods
1158
+ })
1159
+
1160
+ const mainAPI = rpc.getAPI()
1161
+
1162
+ // Call back to main process
1163
+ await mainAPI.showNotification("Hello from worker!")
1164
+ ```
1165
+
1166
+ **Electron Features:**
1167
+
1168
+ - **Type-safe IPC**: Full TypeScript support across Renderer ↔ Main ↔ Utility Process
1169
+ - **Bidirectional**: All processes can expose and call APIs
1170
+ - **Secure**: Works with `contextIsolation: true` (recommended)
1171
+ - **Multiple Patterns**: Supports both IPC and Utility Process communication
1172
+ - **Nested API Support**: Full support for nested method calls like `api.math.add()`
1173
+
1174
+ **Learn more:** [Electron Documentation](https://www.electronjs.org/docs/latest/)
1175
+
1176
+ ### Relay Example
1177
+
1178
+ The `createRelay` function creates a transparent bidirectional relay between two IoInterfaces. This is useful when you want to connect two different transport layers without the intermediary process knowing the API details.
1179
+
1180
+ A common use case is connecting a Renderer process to an external Node.js process through Electron's Main process:
1181
+
1182
+ ```
1183
+ Renderer (IPC) → Main (relay) → External Node Process (stdio)
1184
+ ```
1185
+
1186
+ With relay, Main acts as a transparent byte pipe - it forwards messages without parsing them.
1187
+
1188
+ #### Main Process (with relay)
1189
+
1190
+ ```ts title="main.ts"
1191
+ import { spawn } from "child_process"
1192
+ import { createRelay, NodeIo } from "kkrpc"
1193
+ import { ElectronIpcMainIO } from "kkrpc/electron-ipc"
1194
+
1195
+ // Spawn external Node.js process
1196
+ const worker = spawn("node", ["./worker.js"])
1197
+
1198
+ // Create relay: IPC channel "worker-relay" <-> stdio
1199
+ const relay = createRelay(
1200
+ new ElectronIpcMainIO(ipcMain, webContents, "worker-relay"),
1201
+ new NodeIo(worker.stdout, worker.stdin)
1202
+ )
1203
+
1204
+ // Cleanup when done
1205
+ app.on("window-all-closed", () => {
1206
+ relay.destroy()
1207
+ worker.kill()
1208
+ })
1209
+ ```
1210
+
1211
+ #### Renderer Process
1212
+
1213
+ ```ts title="renderer.ts"
1214
+ import { ElectronIpcRendererIO, RPCChannel } from "kkrpc/electron-ipc"
1215
+
1216
+ // Connect via the relay channel (not the default "kkrpc-ipc" channel)
1217
+ const io = new ElectronIpcRendererIO("worker-relay")
1218
+ const rpc = new RPCChannel<{}, WorkerAPI>(io)
1219
+ const workerAPI = rpc.getAPI()
1220
+
1221
+ // Calls go directly to the external worker process
1222
+ const result = await workerAPI.calculate(42)
1223
+ ```
1224
+
1225
+ #### External Worker Process
1226
+
1227
+ ```ts title="worker.ts"
1228
+ import { NodeIo, RPCChannel } from "kkrpc"
1229
+
1230
+ const io = new NodeIo(process.stdin, process.stdout)
1231
+ const rpc = new RPCChannel<WorkerAPI, {}>(io, {
1232
+ expose: {
1233
+ calculate: async (n: number) => n * 2
1234
+ }
1235
+ })
1236
+ ```
1237
+
1238
+ **Relay Scenarios:**
1239
+
1240
+ | Scenario | From | Through | To | Use Case |
1241
+ | ------------------------------- | ----------------------- | ---------------------- | -------- | -------------------------------------- |
1242
+ | **Renderer → External Process** | `ElectronIpcRendererIO` | Main (`createRelay`) | `NodeIo` | Call external Node.js/Bun/Deno scripts |
1243
+ | **Browser → Server Process** | `WebSocketClientIO` | Server (`createRelay`) | `NodeIo` | Browser to shell process via WebSocket |
1244
+ | **Worker → External Process** | `WorkerChildIO` | Main (`createRelay`) | `NodeIo` | Web Worker to external script |
1245
+
1246
+ **Benefits:**
1247
+
1248
+ - **Transparent**: Intermediary doesn't need to know the API
1249
+ - **Clean separation**: Main doesn't expose worker methods
1250
+ - **Multiple channels**: Can create multiple relays on different IPC channels
1251
+ - **Composable**: Can chain relays through multiple processes
1252
+
581
1253
  ## 🆚 Comparison with Alternatives
582
1254
 
583
1255
  <div align="center">
584
1256
 
585
- | Feature | kkrpc | tRPC | Comlink |
586
- |---------|-------|------|---------|
587
- | **Cross-runtime** | ✅ Node.js, Deno, Bun, Browser | ❌ Node.js/Browser only | ❌ Browser only |
588
- | **Bidirectional** | ✅ Both sides can call APIs | ❌ Client calls server only | ✅ Both sides can call APIs |
589
- | **Type Safety** | ✅ Full TypeScript support | ✅ Full TypeScript support | ✅ TypeScript support |
590
- | **Transport Layers** | ✅ stdio, HTTP, WebSocket, postMessage, Chrome Extension | ❌ HTTP only | ❌ postMessage only |
591
- | **Error Preservation** | ✅ Complete error objects | ⚠️ Limited error serialization | ⚠️ Limited error serialization |
592
- | **Property Access** | ✅ Remote getters/setters | ❌ Methods only | ❌ Methods only |
593
- | **Zero Config** | ✅ No code generation | ✅ No code generation | ✅ No code generation |
594
- | **Callbacks** | ✅ Function parameters | ❌ No callbacks | ✅ Function parameters |
595
- | **Transferable Objects** | ✅ Zero-copy transfers (40-100x faster) | ❌ Not supported | ✅ Basic support |
1257
+ | Feature | kkrpc | tRPC | Comlink |
1258
+ | ------------------------ | ------------------------------------------------------------------ | ------------------------------ | ------------------------------ |
1259
+ | **Cross-runtime** | ✅ Node.js, Deno, Bun, Browser | ❌ Node.js/Browser only | ❌ Browser only |
1260
+ | **Bidirectional** | ✅ Both sides can call APIs | ❌ Client calls server only | ✅ Both sides can call APIs |
1261
+ | **Type Safety** | ✅ Full TypeScript support | ✅ Full TypeScript support | ✅ TypeScript support |
1262
+ | **Transport Layers** | ✅ stdio, HTTP, WebSocket, postMessage, Chrome Extension, Electron | ❌ HTTP only | ❌ postMessage only |
1263
+ | **Error Preservation** | ✅ Complete error objects | ⚠️ Limited error serialization | ⚠️ Limited error serialization |
1264
+ | **Property Access** | ✅ Remote getters/setters | ❌ Methods only | ❌ Methods only |
1265
+ | **Zero Config** | ✅ No code generation | ✅ No code generation | ✅ No code generation |
1266
+ | **Callbacks** | ✅ Function parameters | ❌ No callbacks | ✅ Function parameters |
1267
+ | **Transferable Objects** | ✅ Zero-copy transfers (40-100x faster) | ❌ Not supported | ✅ Basic support |
596
1268
 
597
1269
  </div>
598
1270
 
@@ -631,13 +1303,13 @@ I provided a sample tauri app in `examples/tauri-demo`.
631
1303
 
632
1304
  <div align="center">
633
1305
 
634
- | Platform | Package | Link |
635
- |----------|---------|------|
636
- | **NPM** | `kkrpc` | [![NPM](https://img.shields.io/badge/npm-kkrpc-red?style=flat-square&logo=npm)](https://www.npmjs.com/package/kkrpc) |
637
- | **JSR** | `@kunkun/kkrpc` | [![JSR](https://img.shields.io/badge/jsr-@kunkun/kkrpc-blue?style=flat-square&logo=deno)](https://jsr.io/@kunkun/kkrpc) |
638
- | **GitHub** | Repository | [![GitHub](https://img.shields.io/badge/github-kkrpc-black?style=flat-square&logo=github)](https://github.com/kunkunsh/kkrpc) |
639
- | **Docs** | Typedoc | [![Docs](https://img.shields.io/badge/docs-typedoc-blue?style=flat-square&logo=typescript)](https://kunkunsh.github.io/kkrpc/) |
640
- | **Examples** | Code Samples | [![Examples](https://img.shields.io/badge/examples-code-green?style=flat-square&logo=github)](https://github.com/kunkunsh/kkrpc/tree/main/examples) |
1306
+ | Platform | Package | Link |
1307
+ | ------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
1308
+ | **NPM** | `kkrpc` | [![NPM](https://img.shields.io/badge/npm-kkrpc-red?style=flat-square&logo=npm)](https://www.npmjs.com/package/kkrpc) |
1309
+ | **JSR** | `@kunkun/kkrpc` | [![JSR](https://img.shields.io/badge/jsr-@kunkun/kkrpc-blue?style=flat-square&logo=deno)](https://jsr.io/@kunkun/kkrpc) |
1310
+ | **GitHub** | Repository | [![GitHub](https://img.shields.io/badge/github-kkrpc-black?style=flat-square&logo=github)](https://github.com/kunkunsh/kkrpc) |
1311
+ | **Docs** | Typedoc | [![Docs](https://img.shields.io/badge/docs-typedoc-blue?style=flat-square&logo=typescript)](https://kunkunsh.github.io/kkrpc/) |
1312
+ | **Examples** | Code Samples | [![Examples](https://img.shields.io/badge/examples-code-green?style=flat-square&logo=github)](https://github.com/kunkunsh/kkrpc/tree/main/examples) |
641
1313
 
642
1314
  </div>
643
1315