kkrpc 0.2.2 → 0.4.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 (36) hide show
  1. package/README.md +129 -33
  2. package/dist/browser-mod.cjs +254 -104
  3. package/dist/browser-mod.d.cts +4 -5
  4. package/dist/browser-mod.d.ts +4 -5
  5. package/dist/browser-mod.js +5 -7
  6. package/dist/{channel-C01VCxab.d.cts → channel-B27IYR-w.d.cts} +65 -9
  7. package/dist/{channel-C01VCxab.d.ts → channel-B27IYR-w.d.ts} +65 -9
  8. package/dist/{chrome.cjs → chrome-extension.cjs} +289 -69
  9. package/dist/chrome-extension.d.cts +31 -0
  10. package/dist/chrome-extension.d.ts +31 -0
  11. package/dist/chrome-extension.js +85 -0
  12. package/dist/{chunk-GRCUBSPR.js → chunk-4HW5GG4Z.js} +1 -2
  13. package/dist/{chunk-YIQVRWAJ.js → chunk-H4MEJ5S3.js} +244 -9
  14. package/dist/deno-mod.cjs +243 -11
  15. package/dist/deno-mod.d.cts +3 -4
  16. package/dist/deno-mod.d.ts +3 -4
  17. package/dist/deno-mod.js +2 -2
  18. package/dist/{http-D0k1TiAJ.d.cts → http-Bmw5laGn.d.cts} +1 -1
  19. package/dist/{http-D6N0U5-p.d.ts → http-Oe0LUTM7.d.ts} +1 -1
  20. package/dist/http.cjs +242 -9
  21. package/dist/http.d.cts +2 -3
  22. package/dist/http.d.ts +2 -3
  23. package/dist/http.js +1 -1
  24. package/dist/mod.cjs +255 -109
  25. package/dist/mod.d.cts +7 -13
  26. package/dist/mod.d.ts +7 -13
  27. package/dist/mod.js +10 -11
  28. package/dist/{tauri-ohph68oo.d.cts → tauri-Dw5KiKI3.d.cts} +1 -1
  29. package/dist/{tauri-pC0wuvjw.d.ts → tauri-hhlO_PlO.d.ts} +1 -1
  30. package/dist/utils-B1qZZBwh.d.cts +7 -0
  31. package/dist/utils-B1qZZBwh.d.ts +7 -0
  32. package/package.json +12 -12
  33. package/dist/chrome.d.cts +0 -43
  34. package/dist/chrome.d.ts +0 -43
  35. package/dist/chrome.js +0 -18
  36. package/dist/chunk-INKNKSKA.js +0 -84
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > This project is created for building extension system for a Tauri app (https://github.com/kunkunsh/kunkun).
4
4
  >
5
- > It's potential can be used in other types of apps, so I open sourced it as a standalone package.
5
+ > It can potentially be used in other types of apps, so I open sourced it as a standalone package.
6
6
 
7
7
  [![NPM Version](https://img.shields.io/npm/v/kkrpc)](https://www.npmjs.com/package/kkrpc)
8
8
  [![JSR Version](https://jsr.io/badges/@kunkun/kkrpc)](https://jsr.io/@kunkun/kkrpc)
@@ -23,6 +23,19 @@
23
23
  <img src="https://imgur.com/u728aVv.png" style="max-height: 400px;"/>
24
24
  <img src="https://i.imgur.com/Gu7jH1v.png" style="max-height: 300px;"/>
25
25
 
26
+ ## Features
27
+
28
+ - **Cross-runtime compatibility**: Works seamlessly across Node.js, Deno, Bun, browsers, and more
29
+ - **Type-safe remote calls**: Full TypeScript inference and IDE autocompletion support
30
+ - **Bidirectional communication**: Both endpoints can expose and call APIs simultaneously
31
+ - **Property access**: Remote property getters and setters with dot notation (`await api.prop`, `api.prop = value`)
32
+ - **Enhanced error preservation**: Complete error object preservation across RPC boundaries including stack traces, causes, and custom properties
33
+ - **Multiple transport protocols**: stdio, HTTP, WebSocket, postMessage, Chrome extensions
34
+ - **Callback support**: Remote functions can accept callback functions as parameters
35
+ - **Nested object calls**: Deep method chaining like `api.math.operations.calculate()`
36
+ - **Automatic serialization**: Intelligent detection between JSON and superjson formats
37
+ - **Zero configuration**: No schema files or code generation required
38
+
26
39
  ## Supported Environments
27
40
 
28
41
  - stdio: RPC over stdio between any combinations of Node.js, Deno, Bun processes
@@ -111,6 +124,81 @@ const api = parent.getAPI()
111
124
  expect(await api.add(1, 2)).toBe(3)
112
125
  ```
113
126
 
127
+ ### Property Access Example
128
+
129
+ kkrpc supports direct property access and mutation across RPC boundaries:
130
+
131
+ ```ts
132
+ // Define API with properties
133
+ interface API {
134
+ add(a: number, b: number): Promise<number>
135
+ counter: number
136
+ settings: {
137
+ theme: string
138
+ notifications: {
139
+ enabled: boolean
140
+ }
141
+ }
142
+ }
143
+
144
+ const api = rpc.getAPI<API>()
145
+
146
+ // Property getters (using await for remote access)
147
+ const currentCount = await api.counter
148
+ const theme = await api.settings.theme
149
+ const notificationsEnabled = await api.settings.notifications.enabled
150
+
151
+ // Property setters (direct assignment)
152
+ api.counter = 42
153
+ api.settings.theme = "dark"
154
+ api.settings.notifications.enabled = true
155
+
156
+ // Verify changes
157
+ console.log(await api.counter) // 42
158
+ console.log(await api.settings.theme) // "dark"
159
+ ```
160
+
161
+ ### Enhanced Error Preservation
162
+
163
+ kkrpc preserves complete error information across RPC boundaries:
164
+
165
+ ```ts
166
+ // Custom error class
167
+ class DatabaseError extends Error {
168
+ constructor(message: string, public code: number, public query: string) {
169
+ super(message)
170
+ this.name = 'DatabaseError'
171
+ }
172
+ }
173
+
174
+ // API with error-throwing method
175
+ const apiImplementation = {
176
+ async getUserById(id: string) {
177
+ if (!id) {
178
+ const error = new DatabaseError("Invalid user ID", 400, "SELECT * FROM users WHERE id = ?")
179
+ error.timestamp = new Date().toISOString()
180
+ error.requestId = generateRequestId()
181
+ throw error
182
+ }
183
+ // ... normal logic
184
+ }
185
+ }
186
+
187
+ // Error handling on client side
188
+ try {
189
+ await api.getUserById("")
190
+ } catch (error) {
191
+ // All error properties are preserved:
192
+ console.log(error.name) // "DatabaseError"
193
+ console.log(error.message) // "Invalid user ID"
194
+ console.log(error.code) // 400
195
+ console.log(error.query) // "SELECT * FROM users WHERE id = ?"
196
+ console.log(error.stack) // Full stack trace
197
+ console.log(error.timestamp) // ISO timestamp
198
+ console.log(error.requestId) // Request ID
199
+ }
200
+ ```
201
+
114
202
  ### Web Worker Example
115
203
 
116
204
  ```ts
@@ -208,52 +296,60 @@ console.log("Sum: ", sum)
208
296
 
209
297
  ### Chrome Extension Example
210
298
 
299
+ For Chrome extensions, use the dedicated `ChromePortIO` adapter for reliable, port-based communication.
300
+
211
301
  #### `background.ts`
212
302
 
213
303
  ```ts
214
- import { ChromeBackgroundIO, RPCChannel } from "kkrpc"
215
- import type { API } from "./api"
304
+ import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension";
305
+ import type { BackgroundAPI, ContentAPI } from "./types";
216
306
 
217
- // Store RPC channels for each tab
218
- const rpcChannels = new Map<number, RPCChannel<API, {}>>()
307
+ const backgroundAPI: BackgroundAPI = {
308
+ async getExtensionVersion() {
309
+ return chrome.runtime.getManifest().version;
310
+ },
311
+ };
219
312
 
220
- // Listen for tab connections
221
313
  chrome.runtime.onConnect.addListener((port) => {
222
- if (port.sender?.tab?.id) {
223
- const tabId = port.sender.tab.id
224
- const io = new ChromeBackgroundIO(tabId)
225
- const rpc = new RPCChannel(io, { expose: backgroundAPI })
226
- rpcChannels.set(tabId, rpc)
227
-
228
- port.onDisconnect.addListener(() => {
229
- rpcChannels.delete(tabId)
230
- })
231
- }
232
- })
314
+ if (port.name === "content-to-background") {
315
+ const io = new ChromePortIO(port);
316
+ const rpc = new RPCChannel(io, { expose: backgroundAPI });
317
+ // Handle disconnect
318
+ port.onDisconnect.addListener(() => io.destroy());
319
+ }
320
+ });
233
321
  ```
234
322
 
235
323
  #### `content.ts`
236
324
 
237
325
  ```ts
238
- import { ChromeContentIO, RPCChannel } from "kkrpc"
239
- import type { API } from "./api"
240
-
241
- const io = new ChromeContentIO()
242
- const rpc = new RPCChannel<API, API>(io, {
243
- expose: {
244
- updateUI: async (data) => {
245
- document.body.innerHTML = data.message
246
- return true
247
- }
248
- }
249
- })
326
+ import { ChromePortIO, RPCChannel } from "kkrpc/chrome-extension";
327
+ import type { BackgroundAPI, ContentAPI } from "./types";
250
328
 
251
- // Get API from background script
252
- const api = rpc.getAPI()
253
- const data = await api.getData()
254
- console.log(data) // { message: "Hello from background!" }
329
+ const contentAPI: ContentAPI = {
330
+ async getPageTitle() {
331
+ return document.title;
332
+ },
333
+ };
334
+
335
+ const port = chrome.runtime.connect({ name: "content-to-background" });
336
+ const io = new ChromePortIO(port);
337
+ const rpc = new RPCChannel<ContentAPI, BackgroundAPI>(io, { expose: contentAPI });
338
+
339
+ const backgroundAPI = rpc.getAPI();
340
+
341
+ // Example call
342
+ backgroundAPI.getExtensionVersion().then(version => {
343
+ console.log("Extension version:", version);
344
+ });
255
345
  ```
256
346
 
347
+ **Chrome Extension Features:**
348
+ - **Port-based**: Uses `chrome.runtime.Port` for stable, long-lived connections.
349
+ - **Bidirectional**: Both sides can expose and call APIs.
350
+ - **Type-safe**: Full TypeScript support for your APIs.
351
+ - **Reliable**: Handles connection lifecycle and cleanup.
352
+
257
353
  ### Tauri Example
258
354
 
259
355
  Call functions in bun/node/deno processes from Tauri app with JS/TS.