kkrpc 0.2.0-beta.2 → 0.2.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.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # kkrpc
2
2
 
3
+ > This project is created for building extension system for a Tauri app (https://github.com/kunkunsh/kunkun).
4
+ >
5
+ > It's potential can be used in other types of apps, so I open sourced it as a standalone package.
6
+
3
7
  [![NPM Version](https://img.shields.io/npm/v/kkrpc)](https://www.npmjs.com/package/kkrpc)
4
8
  [![JSR Version](https://jsr.io/badges/@kunkun/kkrpc)](https://jsr.io/@kunkun/kkrpc)
5
9
  ![GitHub last commit](https://img.shields.io/github/last-commit/kunkunsh/kkrpc)
@@ -12,11 +16,12 @@
12
16
  - [Documentation by JSR](https://jsr.io/@kunkun/kkrpc/doc)
13
17
  - [Typedoc Documentation](https://kunkunsh.github.io/kkrpc/)
14
18
 
15
- [Excalidraw Diagrams](https://excalidraw.com/#json=otqFU25B2sSjweA4Sbq9l,7-eY_bzFrGAXLNkOVpQ2Tg)
19
+ [Excalidraw Diagrams](https://excalidraw.com/#json=xp6GbAJVAx3nU-h3PhaxW,oYBNvYmCRsQ2XR3MQo73Ug)
16
20
 
17
- ![](https://imgur.com/vR3Lmv0.png)
18
- ![](https://imgur.com/u728aVv.png)
19
- ![](https://imgur.com/2ycWgVQ.png)
21
+ <img src="https://imgur.com/vR3Lmv0.png" style="max-height: 200px;"/>
22
+ <img src="https://i.imgur.com/zmOHNfu.png" style="max-height: 250px;"/>
23
+ <img src="https://imgur.com/u728aVv.png" style="max-height: 400px;"/>
24
+ <img src="https://i.imgur.com/Gu7jH1v.png" style="max-height: 300px;"/>
20
25
 
21
26
  ## Supported Environments
22
27
 
@@ -220,3 +225,73 @@ const api = rpc.getAPI()
220
225
  const data = await api.getData()
221
226
  console.log(data) // { message: "Hello from background!" }
222
227
  ```
228
+
229
+ ### Tauri Example
230
+
231
+ Call functions in bun/node/deno processes from Tauri app with JS/TS.
232
+
233
+ It allows you to call any JS/TS code in Deno/Bun/Node processes from Tauri app, just like using Electron.
234
+
235
+ Seamless integration with Tauri's official shell plugin and [unlocked shellx plugin](https://github.com/HuakunShen/tauri-plugin-shellx).
236
+
237
+ ```ts
238
+ import { RPCChannel, TauriShellStdio } from "kkrpc/browser"
239
+ import { Child, Command } from "@tauri-apps/plugin-shell"
240
+
241
+ const localAPIImplementation = {
242
+ add: (a: number, b: number) => Promise.resolve(a + b)
243
+ }
244
+
245
+ async function spawnCmd(runtime: "deno" | "bun" | "node") {
246
+ let cmd: Command<string>
247
+ let process = Child | null = null
248
+
249
+ if (runtime === "deno") {
250
+ cmd = Command.create("deno", ["run", "-A", scriptPath])
251
+ process = await cmd.spawn()
252
+ } else if (runtime === "bun") {
253
+ cmd = Command.create("bun", [scriptPath])
254
+ process = await cmd.spawn()
255
+ } else if (runtime === "node") {
256
+ cmd = Command.create("node", [scriptPath])
257
+ process = await cmd.spawn()
258
+ } else {
259
+ throw new Error(`Invalid runtime: ${runtime}, pick either deno or bun`)
260
+ }
261
+
262
+ // monitor stdout/stderr/close/error for debugging and error handling
263
+ cmd.stdout.on("data", (data) => {
264
+ console.log("stdout", data)
265
+ })
266
+ cmd.stderr.on("data", (data) => {
267
+ console.warn("stderr", data)
268
+ })
269
+ cmd.on("close", (code) => {
270
+ console.log("close", code)
271
+ })
272
+ cmd.on("error", (err) => {
273
+ console.error("error", err)
274
+ })
275
+
276
+ const stdio = new TauriShellStdio(cmd.stdout, process)
277
+ const stdioRPC = new RPCChannel<typeof localAPIImplementation, RemoteAPI>(stdio, {
278
+ expose: localAPIImplementation
279
+ })
280
+
281
+ const api = stdioRPC.getAPI();
282
+ await api
283
+ .add(1, 2)
284
+ .then((result) => {
285
+ console.log("result", result)
286
+ })
287
+ .catch((err) => {
288
+ console.error(err)
289
+ })
290
+
291
+ process?.kill()
292
+ }
293
+ ```
294
+
295
+ I provided a sample tauri app in `examples/tauri-demo`.
296
+
297
+ ![Sample Tauri App](https://i.imgur.com/nkDwRHk.png)
@@ -35,6 +35,7 @@ __export(browser_mod_exports, {
35
35
  IframeChildIO: () => IframeChildIO,
36
36
  IframeParentIO: () => IframeParentIO,
37
37
  RPCChannel: () => RPCChannel,
38
+ TauriShellStdio: () => TauriShellStdio,
38
39
  WorkerChildIO: () => WorkerChildIO,
39
40
  WorkerParentIO: () => WorkerParentIO,
40
41
  deserializeMessage: () => deserializeMessage,
@@ -343,6 +344,26 @@ var IframeChildIO = class {
343
344
  }
344
345
  };
345
346
 
347
+ // src/adapters/tauri.ts
348
+ var import_plugin_shell = require("@tauri-apps/plugin-shell");
349
+ var TauriShellStdio = class {
350
+ constructor(readStream, childProcess) {
351
+ this.readStream = readStream;
352
+ this.childProcess = childProcess;
353
+ }
354
+ name = "tauri-shell-stdio";
355
+ read() {
356
+ return new Promise((resolve, reject) => {
357
+ this.readStream.on("data", (chunk) => {
358
+ resolve(chunk);
359
+ });
360
+ });
361
+ }
362
+ async write(data) {
363
+ return this.childProcess.write(data + "\n");
364
+ }
365
+ };
366
+
346
367
  // src/interface.ts
347
368
  var import_node_buffer = require("buffer");
348
369
 
@@ -656,6 +677,7 @@ var RPCChannel = class {
656
677
  IframeChildIO,
657
678
  IframeParentIO,
658
679
  RPCChannel,
680
+ TauriShellStdio,
659
681
  WorkerChildIO,
660
682
  WorkerParentIO,
661
683
  deserializeMessage,
@@ -1,7 +1,8 @@
1
- export { a as WorkerChildIO, W as WorkerParentIO } from './worker-72MNjMgj.cjs';
1
+ export { T as TauriShellStdio, a as WorkerChildIO, W as WorkerParentIO } from './tauri-CRNRbu3f.cjs';
2
2
  export { ChromeBackgroundIO, ChromeContentIO, Message, Response, deserializeMessage, deserializeResponse, generateUUID, serializeMessage, serializeResponse } from './chrome.cjs';
3
3
  import { D as DestroyableIoInterface } from './channel-D6ZClufP.cjs';
4
4
  export { I as IoInterface, R as RPCChannel } from './channel-D6ZClufP.cjs';
5
+ import '@tauri-apps/plugin-shell';
5
6
  import 'node:buffer';
6
7
 
7
8
  /**
@@ -1,7 +1,8 @@
1
- export { a as WorkerChildIO, W as WorkerParentIO } from './worker-DiYJxxHW.js';
1
+ export { T as TauriShellStdio, a as WorkerChildIO, W as WorkerParentIO } from './tauri-PdcZTVUI.js';
2
2
  export { ChromeBackgroundIO, ChromeContentIO, Message, Response, deserializeMessage, deserializeResponse, generateUUID, serializeMessage, serializeResponse } from './chrome.js';
3
3
  import { D as DestroyableIoInterface } from './channel-D6ZClufP.js';
4
4
  export { I as IoInterface, R as RPCChannel } from './channel-D6ZClufP.js';
5
+ import '@tauri-apps/plugin-shell';
5
6
  import 'node:buffer';
6
7
 
7
8
  /**
@@ -1,7 +1,8 @@
1
1
  import {
2
+ TauriShellStdio,
2
3
  WorkerChildIO,
3
4
  WorkerParentIO
4
- } from "./chunk-XU7DWWSJ.js";
5
+ } from "./chunk-KZFPA2BM.js";
5
6
  import {
6
7
  ChromeBackgroundIO,
7
8
  ChromeContentIO
@@ -154,6 +155,7 @@ export {
154
155
  IframeChildIO,
155
156
  IframeParentIO,
156
157
  RPCChannel,
158
+ TauriShellStdio,
157
159
  WorkerChildIO,
158
160
  WorkerParentIO,
159
161
  deserializeMessage,
@@ -0,0 +1,27 @@
1
+ // src/adapters/deno.ts
2
+ import { Buffer } from "node:buffer";
3
+ var DenoIo = class {
4
+ constructor(readStream) {
5
+ this.readStream = readStream;
6
+ this.reader = this.readStream.getReader();
7
+ }
8
+ reader;
9
+ name = "deno-io";
10
+ async read() {
11
+ const { value, done } = await this.reader.read();
12
+ if (done) {
13
+ return null;
14
+ }
15
+ return Buffer.from(value);
16
+ }
17
+ write(data) {
18
+ const encoder = new TextEncoder();
19
+ const encodedData = encoder.encode(data + "\n");
20
+ Deno.stdout.writeSync(encodedData);
21
+ return Promise.resolve();
22
+ }
23
+ };
24
+
25
+ export {
26
+ DenoIo
27
+ };
@@ -82,7 +82,28 @@ var WorkerChildIO = class {
82
82
  }
83
83
  };
84
84
 
85
+ // src/adapters/tauri.ts
86
+ import "@tauri-apps/plugin-shell";
87
+ var TauriShellStdio = class {
88
+ constructor(readStream, childProcess) {
89
+ this.readStream = readStream;
90
+ this.childProcess = childProcess;
91
+ }
92
+ name = "tauri-shell-stdio";
93
+ read() {
94
+ return new Promise((resolve, reject) => {
95
+ this.readStream.on("data", (chunk) => {
96
+ resolve(chunk);
97
+ });
98
+ });
99
+ }
100
+ async write(data) {
101
+ return this.childProcess.write(data + "\n");
102
+ }
103
+ };
104
+
85
105
  export {
86
106
  WorkerParentIO,
87
- WorkerChildIO
107
+ WorkerChildIO,
108
+ TauriShellStdio
88
109
  };
@@ -0,0 +1,355 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // deno-mod.ts
31
+ var deno_mod_exports = {};
32
+ __export(deno_mod_exports, {
33
+ DenoIo: () => DenoIo,
34
+ RPCChannel: () => RPCChannel
35
+ });
36
+ module.exports = __toCommonJS(deno_mod_exports);
37
+
38
+ // src/adapters/deno.ts
39
+ var import_node_buffer = require("buffer");
40
+ var DenoIo = class {
41
+ constructor(readStream) {
42
+ this.readStream = readStream;
43
+ this.reader = this.readStream.getReader();
44
+ }
45
+ reader;
46
+ name = "deno-io";
47
+ async read() {
48
+ const { value, done } = await this.reader.read();
49
+ if (done) {
50
+ return null;
51
+ }
52
+ return import_node_buffer.Buffer.from(value);
53
+ }
54
+ write(data) {
55
+ const encoder = new TextEncoder();
56
+ const encodedData = encoder.encode(data + "\n");
57
+ Deno.stdout.writeSync(encodedData);
58
+ return Promise.resolve();
59
+ }
60
+ };
61
+
62
+ // src/serialization.ts
63
+ var import_superjson = __toESM(require("superjson"), 1);
64
+ function serializeMessage(message) {
65
+ return import_superjson.default.stringify(message) + "\n";
66
+ }
67
+ function deserializeMessage(message) {
68
+ return new Promise((resolve, reject) => {
69
+ try {
70
+ const parsed = import_superjson.default.parse(message);
71
+ resolve(parsed);
72
+ } catch (error) {
73
+ console.error("failed to parse message", typeof message, message, error);
74
+ reject(error);
75
+ }
76
+ });
77
+ }
78
+
79
+ // src/utils.ts
80
+ function generateUUID() {
81
+ return new Array(4).fill(0).map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)).join("-");
82
+ }
83
+
84
+ // src/channel.ts
85
+ var RPCChannel = class {
86
+ constructor(io, options) {
87
+ this.io = io;
88
+ this.apiImplementation = options?.expose;
89
+ this.listen();
90
+ }
91
+ pendingRequests = {};
92
+ callbacks = {};
93
+ callbackCache = /* @__PURE__ */ new Map();
94
+ count = 0;
95
+ messageStr = "";
96
+ apiImplementation;
97
+ /**
98
+ * Exposes a local API implementation that can be called remotely
99
+ * @param api The local API implementation to expose
100
+ */
101
+ expose(api) {
102
+ this.apiImplementation = api;
103
+ }
104
+ /**
105
+ * Returns the IO interface used by this channel
106
+ * @returns The IO interface instance
107
+ */
108
+ getIO() {
109
+ return this.io;
110
+ }
111
+ /**
112
+ * Listens for incoming messages on the IO interface
113
+ * Handles message buffering and parsing
114
+ * @private
115
+ */
116
+ async listen() {
117
+ while (true) {
118
+ const buffer = await this.io.read();
119
+ if (!buffer) {
120
+ continue;
121
+ }
122
+ const bufferStr = buffer.toString("utf-8");
123
+ if (bufferStr.trim().length === 0) {
124
+ continue;
125
+ }
126
+ this.messageStr += bufferStr;
127
+ const lastChar = this.messageStr[this.messageStr.length - 1];
128
+ const msgsSplit = this.messageStr.split("\n");
129
+ const msgs = lastChar === "\n" ? msgsSplit : msgsSplit.slice(0, -1);
130
+ this.messageStr = lastChar === "\n" ? "" : msgsSplit.at(-1) ?? "";
131
+ for (const msgStr of msgs.map((msg) => msg.trim()).filter(Boolean)) {
132
+ if (msgStr.startsWith("{")) {
133
+ this.handleMessageStr(msgStr);
134
+ } else {
135
+ console.log(`(kkrpc stdout passthrough):`, msgStr);
136
+ }
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Handles a single message string by parsing and routing it
142
+ * @param messageStr The message string to handle
143
+ * @private
144
+ */
145
+ async handleMessageStr(messageStr) {
146
+ this.count++;
147
+ return deserializeMessage(messageStr).then((parsedMessage) => {
148
+ if (parsedMessage.type === "response") {
149
+ this.handleResponse(parsedMessage);
150
+ } else if (parsedMessage.type === "request") {
151
+ this.handleRequest(parsedMessage);
152
+ } else if (parsedMessage.type === "callback") {
153
+ this.handleCallback(parsedMessage);
154
+ } else {
155
+ console.error("received unknown message type", parsedMessage, typeof parsedMessage);
156
+ }
157
+ }).catch((err) => {
158
+ console.log(`(kkrpc stdout passthrough):`, messageStr);
159
+ });
160
+ }
161
+ /**
162
+ * Calls a method on the remote API
163
+ * @param method The name of the method to call
164
+ * @param args Arguments to pass to the remote method
165
+ * @returns Promise that resolves with the result of the remote call
166
+ */
167
+ callMethod(method, args) {
168
+ return new Promise((resolve, reject) => {
169
+ const messageId = generateUUID();
170
+ this.pendingRequests[messageId] = { resolve, reject };
171
+ const callbackIds = [];
172
+ const processedArgs = args.map((arg) => {
173
+ if (typeof arg === "function") {
174
+ let callbackId = this.callbackCache.get(arg);
175
+ if (!callbackId) {
176
+ callbackId = generateUUID();
177
+ this.callbacks[callbackId] = arg;
178
+ this.callbackCache.set(arg, callbackId);
179
+ } else {
180
+ }
181
+ callbackIds.push(callbackId);
182
+ return `__callback__${callbackId}`;
183
+ }
184
+ return arg;
185
+ });
186
+ const message = {
187
+ id: messageId,
188
+ method,
189
+ args: processedArgs,
190
+ type: "request",
191
+ callbackIds: callbackIds.length > 0 ? callbackIds : void 0
192
+ };
193
+ this.io.write(serializeMessage(message));
194
+ });
195
+ }
196
+ /**
197
+ * Handles responses received from remote method calls
198
+ * @param response The response message to handle
199
+ * @private
200
+ */
201
+ handleResponse(response) {
202
+ const { id } = response;
203
+ const { result, error } = response.args;
204
+ if (this.pendingRequests[id]) {
205
+ if (error) {
206
+ this.pendingRequests[id].reject(new Error(error));
207
+ } else {
208
+ this.pendingRequests[id].resolve(result);
209
+ }
210
+ delete this.pendingRequests[id];
211
+ }
212
+ }
213
+ /**
214
+ * Handles incoming method call requests from the remote endpoint
215
+ * @param request The request message to handle
216
+ * @private
217
+ */
218
+ handleRequest(request) {
219
+ const { id, method, args } = request;
220
+ const methodPath = method.split(".");
221
+ if (!this.apiImplementation) return;
222
+ let target = this.apiImplementation;
223
+ for (let i = 0; i < methodPath.length - 1; i++) {
224
+ target = target[methodPath[i]];
225
+ if (!target) {
226
+ this.sendError(id, `Method path ${method} not found at ${methodPath[i]}`);
227
+ return;
228
+ }
229
+ }
230
+ const finalMethod = methodPath[methodPath.length - 1];
231
+ const targetMethod = target[finalMethod];
232
+ if (typeof targetMethod !== "function") {
233
+ this.sendError(id, `Method ${method} is not a function`);
234
+ return;
235
+ }
236
+ const processedArgs = args.map((arg) => {
237
+ if (typeof arg === "string" && arg.startsWith("__callback__")) {
238
+ const callbackId = arg.slice(12);
239
+ return (...callbackArgs) => {
240
+ this.invokeCallback(callbackId, callbackArgs);
241
+ };
242
+ }
243
+ return arg;
244
+ });
245
+ try {
246
+ const result = targetMethod.apply(target, processedArgs);
247
+ Promise.resolve(result).then((res) => {
248
+ return this.sendResponse(id, res);
249
+ }).catch((err) => this.sendError(id, err.message));
250
+ } catch (error) {
251
+ this.sendError(id, error.message ?? error.toString());
252
+ }
253
+ }
254
+ /**
255
+ * Invokes a callback on the remote endpoint
256
+ * @param callbackId The ID of the callback to invoke
257
+ * @param args Arguments to pass to the callback
258
+ * @private
259
+ */
260
+ invokeCallback(callbackId, args) {
261
+ const message = {
262
+ id: generateUUID(),
263
+ method: callbackId,
264
+ args,
265
+ type: "callback"
266
+ };
267
+ this.io.write(serializeMessage(message));
268
+ }
269
+ /**
270
+ * Handles callback invocations received from the remote endpoint
271
+ * @param message The callback message to handle
272
+ * @private
273
+ */
274
+ handleCallback(message) {
275
+ const { method: callbackId, args } = message;
276
+ const callback = this.callbacks[callbackId];
277
+ if (callback) {
278
+ callback(...args);
279
+ } else {
280
+ console.error(`Callback with id ${callbackId} not found`);
281
+ }
282
+ }
283
+ /**
284
+ * Sends a successful response back to the remote endpoint
285
+ * @param id The ID of the request being responded to
286
+ * @param result The result to send back
287
+ * @private
288
+ */
289
+ sendResponse(id, result) {
290
+ const response = {
291
+ id,
292
+ method: "",
293
+ args: { result },
294
+ type: "response"
295
+ };
296
+ this.io.write(serializeMessage(response));
297
+ }
298
+ /**
299
+ * Sends an error response back to the remote endpoint
300
+ * @param id The ID of the request being responded to
301
+ * @param error The error message to send back
302
+ * @private
303
+ */
304
+ sendError(id, error) {
305
+ const response = {
306
+ id,
307
+ method: "",
308
+ args: { error },
309
+ type: "response"
310
+ };
311
+ this.io.write(serializeMessage(response));
312
+ }
313
+ /**
314
+ * Creates a nested proxy object for chaining remote method calls
315
+ * @param chain Array of method names in the chain
316
+ * @returns Proxy object that transforms property access into remote method calls
317
+ * @private
318
+ */
319
+ createNestedProxy(chain = []) {
320
+ return new Proxy(() => {
321
+ }, {
322
+ get: (_target, prop) => {
323
+ if (typeof prop === "string" && prop !== "then") {
324
+ return this.createNestedProxy([...chain, prop]);
325
+ }
326
+ return void 0;
327
+ },
328
+ apply: (_target, _thisArg, args) => {
329
+ const method = chain.join(".");
330
+ return this.callMethod(method, args);
331
+ }
332
+ });
333
+ }
334
+ /**
335
+ * Returns a proxy object that represents the remote API
336
+ * Methods called on this proxy will be executed on the remote endpoint
337
+ * @returns Proxy object representing the remote API
338
+ */
339
+ getAPI() {
340
+ return this.createNestedProxy();
341
+ }
342
+ /**
343
+ * Frees up memory by clearing stored callbacks and callback cache
344
+ * Useful when dealing with many anonymous callback functions to prevent memory leaks
345
+ */
346
+ freeCallbacks() {
347
+ this.callbacks = {};
348
+ this.callbackCache.clear();
349
+ }
350
+ };
351
+ // Annotate the CommonJS export names for ESM import in node:
352
+ 0 && (module.exports = {
353
+ DenoIo,
354
+ RPCChannel
355
+ });
@@ -0,0 +1,19 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { I as IoInterface } from './channel-D6ZClufP.cjs';
3
+ export { R as RPCChannel } from './channel-D6ZClufP.cjs';
4
+
5
+ /**
6
+ * Stdio implementation for Deno
7
+ * Deno doesn't have `process` object, and have a completely different stdio API,
8
+ * This implementation wrap Deno's `Deno.stdin` and `Deno.stdout` to follow StdioInterface
9
+ */
10
+ declare class DenoIo implements IoInterface {
11
+ private readStream;
12
+ private reader;
13
+ name: string;
14
+ constructor(readStream: ReadableStream<Uint8Array>);
15
+ read(): Promise<Buffer | null>;
16
+ write(data: string): Promise<void>;
17
+ }
18
+
19
+ export { DenoIo };
@@ -0,0 +1,19 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { I as IoInterface } from './channel-D6ZClufP.js';
3
+ export { R as RPCChannel } from './channel-D6ZClufP.js';
4
+
5
+ /**
6
+ * Stdio implementation for Deno
7
+ * Deno doesn't have `process` object, and have a completely different stdio API,
8
+ * This implementation wrap Deno's `Deno.stdin` and `Deno.stdout` to follow StdioInterface
9
+ */
10
+ declare class DenoIo implements IoInterface {
11
+ private readStream;
12
+ private reader;
13
+ name: string;
14
+ constructor(readStream: ReadableStream<Uint8Array>);
15
+ read(): Promise<Buffer | null>;
16
+ write(data: string): Promise<void>;
17
+ }
18
+
19
+ export { DenoIo };
@@ -0,0 +1,10 @@
1
+ import {
2
+ DenoIo
3
+ } from "./chunk-GRCUBSPR.js";
4
+ import {
5
+ RPCChannel
6
+ } from "./chunk-ZSSFWNSX.js";
7
+ export {
8
+ DenoIo,
9
+ RPCChannel
10
+ };
package/dist/mod.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // mod.ts
31
31
  var mod_exports = {};
32
32
  __export(mod_exports, {
33
+ BunIo: () => BunIo,
33
34
  ChromeBackgroundIO: () => ChromeBackgroundIO,
34
35
  ChromeContentIO: () => ChromeContentIO,
35
36
  DenoIo: () => DenoIo,
@@ -37,6 +38,7 @@ __export(mod_exports, {
37
38
  HTTPServerIO: () => HTTPServerIO,
38
39
  NodeIo: () => NodeIo,
39
40
  RPCChannel: () => RPCChannel,
41
+ TauriShellStdio: () => TauriShellStdio,
40
42
  WebSocketClientIO: () => WebSocketClientIO,
41
43
  WebSocketServerIO: () => WebSocketServerIO,
42
44
  WorkerChildIO: () => WorkerChildIO,
@@ -213,14 +215,36 @@ var ChromeContentIO = class {
213
215
  }
214
216
  };
215
217
 
216
- // src/adapters/node.ts
218
+ // src/adapters/bun.ts
217
219
  var import_node_buffer2 = require("buffer");
218
- var import_node_stream = require("stream");
219
220
 
220
221
  // src/interface.ts
221
222
  var import_node_buffer = require("buffer");
222
223
 
224
+ // src/adapters/bun.ts
225
+ var BunIo = class {
226
+ name = "bun-io";
227
+ readStream;
228
+ reader;
229
+ constructor(readStream) {
230
+ this.readStream = readStream;
231
+ this.reader = this.readStream.getReader();
232
+ }
233
+ async read() {
234
+ const { value, done } = await this.reader.read();
235
+ if (done) {
236
+ return null;
237
+ }
238
+ return import_node_buffer2.Buffer.from(value);
239
+ }
240
+ async write(data) {
241
+ return Bun.write(Bun.stdout, data).then(() => Promise.resolve());
242
+ }
243
+ };
244
+
223
245
  // src/adapters/node.ts
246
+ var import_node_buffer3 = require("buffer");
247
+ var import_node_stream = require("stream");
224
248
  var NodeIo = class {
225
249
  name = "node-io";
226
250
  readStream;
@@ -457,6 +481,26 @@ var HTTPServerIO = class {
457
481
  }
458
482
  };
459
483
 
484
+ // src/adapters/tauri.ts
485
+ var import_plugin_shell = require("@tauri-apps/plugin-shell");
486
+ var TauriShellStdio = class {
487
+ constructor(readStream, childProcess) {
488
+ this.readStream = readStream;
489
+ this.childProcess = childProcess;
490
+ }
491
+ name = "tauri-shell-stdio";
492
+ read() {
493
+ return new Promise((resolve, reject) => {
494
+ this.readStream.on("data", (chunk) => {
495
+ resolve(chunk);
496
+ });
497
+ });
498
+ }
499
+ async write(data) {
500
+ return this.childProcess.write(data + "\n");
501
+ }
502
+ };
503
+
460
504
  // src/serialization.ts
461
505
  var import_superjson2 = __toESM(require("superjson"), 1);
462
506
  function serializeMessage(message) {
@@ -762,11 +806,10 @@ var RPCChannel = class {
762
806
  };
763
807
 
764
808
  // src/adapters/deno.ts
765
- var import_node_buffer3 = require("buffer");
809
+ var import_node_buffer4 = require("buffer");
766
810
  var DenoIo = class {
767
- constructor(readStream, writeStream) {
811
+ constructor(readStream) {
768
812
  this.readStream = readStream;
769
- this.writeStream = writeStream;
770
813
  this.reader = this.readStream.getReader();
771
814
  }
772
815
  reader;
@@ -776,7 +819,7 @@ var DenoIo = class {
776
819
  if (done) {
777
820
  return null;
778
821
  }
779
- return import_node_buffer3.Buffer.from(value);
822
+ return import_node_buffer4.Buffer.from(value);
780
823
  }
781
824
  write(data) {
782
825
  const encoder = new TextEncoder();
@@ -787,6 +830,7 @@ var DenoIo = class {
787
830
  };
788
831
  // Annotate the CommonJS export names for ESM import in node:
789
832
  0 && (module.exports = {
833
+ BunIo,
790
834
  ChromeBackgroundIO,
791
835
  ChromeContentIO,
792
836
  DenoIo,
@@ -794,6 +838,7 @@ var DenoIo = class {
794
838
  HTTPServerIO,
795
839
  NodeIo,
796
840
  RPCChannel,
841
+ TauriShellStdio,
797
842
  WebSocketClientIO,
798
843
  WebSocketServerIO,
799
844
  WorkerChildIO,
package/dist/mod.d.cts CHANGED
@@ -1,10 +1,29 @@
1
- export { a as WorkerChildIO, W as WorkerParentIO } from './worker-72MNjMgj.cjs';
1
+ export { T as TauriShellStdio, a as WorkerChildIO, W as WorkerParentIO } from './tauri-CRNRbu3f.cjs';
2
2
  export { ChromeBackgroundIO, ChromeContentIO, Message, Response, deserializeMessage, deserializeResponse, generateUUID, serializeMessage, serializeResponse } from './chrome.cjs';
3
3
  import { Buffer } from 'node:buffer';
4
- import { Readable, Writable } from 'node:stream';
5
4
  import { I as IoInterface, D as DestroyableIoInterface } from './channel-D6ZClufP.cjs';
6
5
  export { R as RPCChannel } from './channel-D6ZClufP.cjs';
6
+ import { Readable, Writable } from 'node:stream';
7
7
  export { H as HTTPClientIO, a as HTTPServerIO } from './http-CvGfNM3D.cjs';
8
+ export { DenoIo } from './deno-mod.cjs';
9
+ import '@tauri-apps/plugin-shell';
10
+
11
+ /**
12
+ * Node.js implementation of IoInterface
13
+ * Should also work with Bun
14
+ */
15
+
16
+ /**
17
+ * Stdio implementation for Bun
18
+ */
19
+ declare class BunIo implements IoInterface {
20
+ name: string;
21
+ private readStream;
22
+ private reader;
23
+ constructor(readStream: ReadableStream<Uint8Array>);
24
+ read(): Promise<Buffer | null>;
25
+ write(data: string): Promise<void>;
26
+ }
8
27
 
9
28
  /**
10
29
  * Node.js implementation of IoInterface
@@ -62,19 +81,4 @@ declare class WebSocketServerIO implements DestroyableIoInterface {
62
81
  signalDestroy(): void;
63
82
  }
64
83
 
65
- /**
66
- * Stdio implementation for Deno
67
- * Deno doesn't have `process` object, and have a completely different stdio API,
68
- * This implementation wrap Deno's `Deno.stdin` and `Deno.stdout` to follow StdioInterface
69
- */
70
- declare class DenoIo implements IoInterface {
71
- private readStream;
72
- private writeStream;
73
- private reader;
74
- name: string;
75
- constructor(readStream: ReadableStream<Uint8Array>, writeStream: WritableStream<Uint8Array>);
76
- read(): Promise<Buffer | null>;
77
- write(data: string): Promise<void>;
78
- }
79
-
80
- export { DenoIo, DestroyableIoInterface, IoInterface, NodeIo, WebSocketClientIO, WebSocketServerIO };
84
+ export { BunIo, DestroyableIoInterface, IoInterface, NodeIo, WebSocketClientIO, WebSocketServerIO };
package/dist/mod.d.ts CHANGED
@@ -1,10 +1,29 @@
1
- export { a as WorkerChildIO, W as WorkerParentIO } from './worker-DiYJxxHW.js';
1
+ export { T as TauriShellStdio, a as WorkerChildIO, W as WorkerParentIO } from './tauri-PdcZTVUI.js';
2
2
  export { ChromeBackgroundIO, ChromeContentIO, Message, Response, deserializeMessage, deserializeResponse, generateUUID, serializeMessage, serializeResponse } from './chrome.js';
3
3
  import { Buffer } from 'node:buffer';
4
- import { Readable, Writable } from 'node:stream';
5
4
  import { I as IoInterface, D as DestroyableIoInterface } from './channel-D6ZClufP.js';
6
5
  export { R as RPCChannel } from './channel-D6ZClufP.js';
6
+ import { Readable, Writable } from 'node:stream';
7
7
  export { H as HTTPClientIO, a as HTTPServerIO } from './http-Bz7mwStC.js';
8
+ export { DenoIo } from './deno-mod.js';
9
+ import '@tauri-apps/plugin-shell';
10
+
11
+ /**
12
+ * Node.js implementation of IoInterface
13
+ * Should also work with Bun
14
+ */
15
+
16
+ /**
17
+ * Stdio implementation for Bun
18
+ */
19
+ declare class BunIo implements IoInterface {
20
+ name: string;
21
+ private readStream;
22
+ private reader;
23
+ constructor(readStream: ReadableStream<Uint8Array>);
24
+ read(): Promise<Buffer | null>;
25
+ write(data: string): Promise<void>;
26
+ }
8
27
 
9
28
  /**
10
29
  * Node.js implementation of IoInterface
@@ -62,19 +81,4 @@ declare class WebSocketServerIO implements DestroyableIoInterface {
62
81
  signalDestroy(): void;
63
82
  }
64
83
 
65
- /**
66
- * Stdio implementation for Deno
67
- * Deno doesn't have `process` object, and have a completely different stdio API,
68
- * This implementation wrap Deno's `Deno.stdin` and `Deno.stdout` to follow StdioInterface
69
- */
70
- declare class DenoIo implements IoInterface {
71
- private readStream;
72
- private writeStream;
73
- private reader;
74
- name: string;
75
- constructor(readStream: ReadableStream<Uint8Array>, writeStream: WritableStream<Uint8Array>);
76
- read(): Promise<Buffer | null>;
77
- write(data: string): Promise<void>;
78
- }
79
-
80
- export { DenoIo, DestroyableIoInterface, IoInterface, NodeIo, WebSocketClientIO, WebSocketServerIO };
84
+ export { BunIo, DestroyableIoInterface, IoInterface, NodeIo, WebSocketClientIO, WebSocketServerIO };
package/dist/mod.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import {
2
+ TauriShellStdio,
2
3
  WorkerChildIO,
3
4
  WorkerParentIO
4
- } from "./chunk-XU7DWWSJ.js";
5
+ } from "./chunk-KZFPA2BM.js";
5
6
  import {
6
7
  ChromeBackgroundIO,
7
8
  ChromeContentIO
8
9
  } from "./chunk-INKNKSKA.js";
10
+ import {
11
+ DenoIo
12
+ } from "./chunk-GRCUBSPR.js";
9
13
  import {
10
14
  HTTPClientIO,
11
15
  HTTPServerIO
@@ -19,6 +23,28 @@ import {
19
23
  serializeResponse
20
24
  } from "./chunk-ZSSFWNSX.js";
21
25
 
26
+ // src/adapters/bun.ts
27
+ import { Buffer } from "node:buffer";
28
+ var BunIo = class {
29
+ name = "bun-io";
30
+ readStream;
31
+ reader;
32
+ constructor(readStream) {
33
+ this.readStream = readStream;
34
+ this.reader = this.readStream.getReader();
35
+ }
36
+ async read() {
37
+ const { value, done } = await this.reader.read();
38
+ if (done) {
39
+ return null;
40
+ }
41
+ return Buffer.from(value);
42
+ }
43
+ async write(data) {
44
+ return Bun.write(Bun.stdout, data).then(() => Promise.resolve());
45
+ }
46
+ };
47
+
22
48
  // src/adapters/node.ts
23
49
  var NodeIo = class {
24
50
  name = "node-io";
@@ -165,32 +191,8 @@ var WebSocketServerIO = class {
165
191
  this.write(DESTROY_SIGNAL);
166
192
  }
167
193
  };
168
-
169
- // src/adapters/deno.ts
170
- import { Buffer } from "node:buffer";
171
- var DenoIo = class {
172
- constructor(readStream, writeStream) {
173
- this.readStream = readStream;
174
- this.writeStream = writeStream;
175
- this.reader = this.readStream.getReader();
176
- }
177
- reader;
178
- name = "deno-io";
179
- async read() {
180
- const { value, done } = await this.reader.read();
181
- if (done) {
182
- return null;
183
- }
184
- return Buffer.from(value);
185
- }
186
- write(data) {
187
- const encoder = new TextEncoder();
188
- const encodedData = encoder.encode(data + "\n");
189
- Deno.stdout.writeSync(encodedData);
190
- return Promise.resolve();
191
- }
192
- };
193
194
  export {
195
+ BunIo,
194
196
  ChromeBackgroundIO,
195
197
  ChromeContentIO,
196
198
  DenoIo,
@@ -198,6 +200,7 @@ export {
198
200
  HTTPServerIO,
199
201
  NodeIo,
200
202
  RPCChannel,
203
+ TauriShellStdio,
201
204
  WebSocketClientIO,
202
205
  WebSocketServerIO,
203
206
  WorkerChildIO,
@@ -1,4 +1,5 @@
1
- import { D as DestroyableIoInterface } from './channel-D6ZClufP.js';
1
+ import { D as DestroyableIoInterface, I as IoInterface } from './channel-D6ZClufP.cjs';
2
+ import { EventEmitter, OutputEvents, Child } from '@tauri-apps/plugin-shell';
2
3
 
3
4
  declare class WorkerParentIO implements DestroyableIoInterface {
4
5
  name: string;
@@ -24,4 +25,14 @@ declare class WorkerChildIO implements DestroyableIoInterface {
24
25
  signalDestroy(): void;
25
26
  }
26
27
 
27
- export { WorkerParentIO as W, WorkerChildIO as a };
28
+ declare class TauriShellStdio implements IoInterface {
29
+ private readStream;
30
+ private childProcess;
31
+ name: string;
32
+ constructor(readStream: EventEmitter<OutputEvents<string>>, // stdout of child process
33
+ childProcess: Child);
34
+ read(): Promise<string | Uint8Array | null>;
35
+ write(data: string): Promise<void>;
36
+ }
37
+
38
+ export { TauriShellStdio as T, WorkerParentIO as W, WorkerChildIO as a };
@@ -1,4 +1,5 @@
1
- import { D as DestroyableIoInterface } from './channel-D6ZClufP.cjs';
1
+ import { D as DestroyableIoInterface, I as IoInterface } from './channel-D6ZClufP.js';
2
+ import { EventEmitter, OutputEvents, Child } from '@tauri-apps/plugin-shell';
2
3
 
3
4
  declare class WorkerParentIO implements DestroyableIoInterface {
4
5
  name: string;
@@ -24,4 +25,14 @@ declare class WorkerChildIO implements DestroyableIoInterface {
24
25
  signalDestroy(): void;
25
26
  }
26
27
 
27
- export { WorkerParentIO as W, WorkerChildIO as a };
28
+ declare class TauriShellStdio implements IoInterface {
29
+ private readStream;
30
+ private childProcess;
31
+ name: string;
32
+ constructor(readStream: EventEmitter<OutputEvents<string>>, // stdout of child process
33
+ childProcess: Child);
34
+ read(): Promise<string | Uint8Array | null>;
35
+ write(data: string): Promise<void>;
36
+ }
37
+
38
+ export { TauriShellStdio as T, WorkerParentIO as W, WorkerChildIO as a };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kkrpc",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,6 +43,15 @@
43
43
  "import": "./dist/http.js",
44
44
  "require": "./dist/http.cjs"
45
45
  },
46
+ "./deno": {
47
+ "types": {
48
+ "import": "./dist/deno-mod.d.ts",
49
+ "require": "./dist/deno-mod.cjs",
50
+ "default": "./dist/deno-mod.js"
51
+ },
52
+ "import": "./dist/deno-mod.js",
53
+ "require": "./dist/deno-mod.cjs"
54
+ },
46
55
  "./chrome": {
47
56
  "types": {
48
57
  "import": "./dist/chrome.d.ts",
@@ -67,6 +76,7 @@
67
76
  "typescript": "^5.0.0"
68
77
  },
69
78
  "dependencies": {
79
+ "@tauri-apps/plugin-shell": "^2.2.0",
70
80
  "superjson": "^2.2.2",
71
81
  "ws": "^8.18.1"
72
82
  },