Skip to content

Commit e9d8d50

Browse files
authored
Promote interop to libraries with stdio/WebSocket adapters and unit tests (#21)
* Promote interop code to libraries with stdio/ws tests * Add interop package readmes and fix Rust websocket feature * Add Rust interop gitignore * Getting interop packages ready for publish * update pnpm lcok file * refactor(interop): rename rust package to kkrpc BREAKING CHANGE: Package name changed from kkrpc-interop to kkrpc. Users will need to update their Cargo.toml dependencies. * fix(interop/python): resolve callback closure bug in RPC server Fix a Python closure issue in the RpcServer._wrap_callbacks method where multiple callbacks in a single RPC request would all route to the same callback ID. The original code captured callback_id by reference rather than by value, causing all generated callbacks to reference the last iteration's value. Uses default parameter binding (_callback_id: str = callback_id) to capture the correct value at function definition time. This is the most Pythonic solution requiring no additional imports or helper functions. Also normalizes code formatting from tabs to spaces. * fix(rust): handle websocket connection closure gracefully Introduce EOF signaling mechanism using `Option<String>` in the message queue. When the remote peer disconnects, the background reader pushes `None` to unblock waiting consumers instead of silently exiting. This prevents `Client::call()` from hanging on premature connection closure. Documentation and test imports now reference the renamed kkrpc package. * chore(rust): bump version to 0.6.1 * feat(interop/swift): add Swift interop package with JSON-RPC support Add a complete Swift implementation of kkrpc with stdio and WebSocket transport support. Features include: - Client and Server actors for async/await concurrency - JSON-mode protocol compatibility with kkrpc serialization - StdioTransport and WebSocketTransport implementations - Callback support using __callback__ tokens - Property get/set operations for remote object manipulation - Comprehensive unit tests This enables Swift applications to communicate with kkrpc services in TypeScript, JavaScript, Python, Rust, and Go. * feat(bench): add comprehensive benchmark suite for kkrpc performance Add benchmark tests to measure RPC throughput and data transfer performance across stdio and WebSocket transports for Node.js, Bun, and Deno runtimes. The suite includes: - Throughput tests measuring sequential/concurrent calls and batching - Large data transfer tests for upload/download scenarios - Runtime comparison with detailed metrics - Compiled test artifacts added to .gitignore Documentation added to README.md with benchmark design explanations, results tables, and key findings on performance characteristics across different runtimes and transports. * chore(node): update node version to 25 * test(bench): skip benchmarks in ci Skip stdio adapter throughput benchmarks when running in CI environments to reduce build time and avoid potential flakiness. The benchmarks are conditionally skipped by checking for CI or GITHUB_ACTIONS environment variables. * docs: add comprehensive agents documentation Add AGENTS.md knowledge base files for AI agents across the project: - Documentation site structure and commands - Language interop documentation (Go, Python, Rust, Swift, Node) - Demo API package documentation - Updated main project knowledge base with new interop info - Updated test and adapter documentation
1 parent e8f5dbf commit e9d8d50

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+8682
-218
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ yarn-error.log*
3838
*.pem
3939

4040
__pycache__/
41+
42+
# Compiled test scripts
43+
packages/kkrpc/__tests__/scripts/*.js

.journal/2026-02-05.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Development Journal - 2026-02-05
2+
3+
## Benchmark Suite Implementation for kkrpc
4+
5+
**Timestamp:** 2026-02-05 15:45
6+
7+
### Core Decision/Topic
8+
9+
Implemented comprehensive benchmark suite to measure kkrpc throughput and data transfer performance across stdio and WebSocket transports, with support for Node.js, Bun, and Deno runtimes.
10+
11+
### Options Considered
12+
13+
1. **Native TypeScript execution with Node 25+**
14+
- Attempted using `--experimental-strip-types` flag
15+
- **Rejected**: Node's strip-only mode doesn't support TypeScript parameter properties (`constructor(private options: ...)`) used extensively in kkrpc
16+
- Even Node 25.2.1 failed with `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`
17+
18+
2. **tsx/ts-node for TypeScript execution**
19+
- Would add dev dependencies
20+
- **Rejected**: Adds complexity and external dependencies
21+
22+
3. **Bun.build() compilation to JS (Chosen)**
23+
- Compiles TS to JS on-demand when JS file doesn't exist
24+
- No additional dependencies required
25+
- Works reliably across all Node versions
26+
- Compiled files added to `.gitignore`
27+
28+
### Final Decision & Rationale
29+
30+
Chose **Bun.build() compilation approach** because:
31+
- Zero additional dependencies
32+
- Works with all Node versions (tested on v24.10.0 and v25.2.1)
33+
- Simple implementation: checks if JS exists, compiles if needed
34+
- Compiled artifacts are gitignored
35+
36+
### Key Changes Made
37+
38+
**New Benchmark Files:**
39+
- `packages/kkrpc/__tests__/stdio-benchmark.test.ts` - Stdio RPC throughput (calls/sec)
40+
- `packages/kkrpc/__tests__/websocket-benchmark.test.ts` - WebSocket RPC throughput
41+
- `packages/kkrpc/__tests__/stdio-large-data-benchmark.test.ts` - Stdio data transfer (MB/s)
42+
- `packages/kkrpc/__tests__/websocket-large-data-benchmark.test.ts` - WebSocket data transfer
43+
44+
**API Scripts:**
45+
- `packages/kkrpc/__tests__/scripts/benchmark-api.ts` - Simple API for throughput testing
46+
- `packages/kkrpc/__tests__/scripts/large-data-api.ts` - API for data transfer testing
47+
- `packages/kkrpc/__tests__/scripts/deno-benchmark-api.ts` - Deno variant
48+
- `packages/kkrpc/__tests__/scripts/deno-large-data-api.ts` - Deno variant
49+
50+
**Documentation:**
51+
- Added comprehensive "Benchmarks" section to `packages/kkrpc/README.md`
52+
- Includes benchmark design explanation, results tables, and key findings
53+
54+
**Configuration:**
55+
- Added `packages/kkrpc/__tests__/scripts/*.js` to root `.gitignore`
56+
57+
### Benchmark Results (Sample)
58+
59+
| Runtime | Transport | Sequential Echo | Concurrent Echo | Latency (avg) |
60+
|---------|-----------|-----------------|-----------------|---------------|
61+
| Node.js | stdio | 25,256 calls/sec | 150,677 calls/sec | 0.038ms |
62+
| Bun | stdio | 20,034 calls/sec | 151,905 calls/sec | 0.050ms |
63+
| Deno | stdio | 19,185 calls/sec | 122,502 calls/sec | 0.044ms |
64+
| Bun | WebSocket | 22,314 calls/sec | 74,954 calls/sec | 0.040ms |
65+
66+
**Data Transfer (Bun stdio):**
67+
- Upload 1MB: ~1,010 MB/s
68+
- Download 1MB: ~134 MB/s
69+
- Echo 100KB: ~1,132 MB/s
70+
71+
### Key Findings
72+
73+
- Bun consistently outperforms Node.js and Deno for stdio communication
74+
- Stdio is 2-3x faster than WebSocket for local process communication
75+
- Concurrent operations achieve 6-7x higher throughput than sequential
76+
- Batching (100 ops/batch) achieves 400K-500K effective calls/sec
77+
- Upload is faster than download due to JSON serialization overhead on response path
78+
79+
### Future Considerations
80+
81+
1. **Node 26+**: Re-evaluate native TypeScript execution when parameter properties are supported
82+
2. **Additional Transports**: Add benchmarks for HTTP, Socket.IO, and message queue adapters
83+
3. **Memory Profiling**: Add memory usage metrics to benchmarks
84+
4. **CI Integration**: Consider running benchmarks in CI to detect performance regressions

.node-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
22
1+
25

AGENTS.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# kkrpc - PROJECT KNOWLEDGE BASE
22

3-
**Generated:** 2026-02-03
3+
**Generated:** 2026-02-05
44
**Commit:** (current)
55
**Branch:** main
66

77
## OVERVIEW
88

9-
TypeScript-first RPC library with bidirectional communication across Node.js, Deno, Bun, Browser, and Tauri. Supports 15+ transport protocols with full type safety and zero-copy transferable objects.
9+
TypeScript-first RPC library with bidirectional communication across Node.js, Deno, Bun, Browser, and Tauri. Supports 15+ transport protocols with full type safety and zero-copy transferable objects. Includes language interop for Go, Python, Rust, and Swift.
1010

1111
## STRUCTURE
1212

@@ -16,18 +16,20 @@ kkrpc/
1616
│ ├── src/ # Source code
1717
│ │ ├── channel.ts # RPCChannel core
1818
│ │ ├── interface.ts # IoInterface abstraction
19-
│ │ ├── adapters/ # Transport adapters (15 adapters)
19+
│ │ ├── adapters/ # Transport adapters (22 adapters)
2020
│ │ ├── transfer*.ts # Transferable objects support
2121
│ │ └── serialization.ts # JSON/superjson serialization
22-
│ ├── __tests__/ # Bun test suite (15 tests)
22+
│ ├── __tests__/ # Bun test suite (17+ tests)
2323
│ ├── __deno_tests__/ # Deno regression tests
2424
│ ├── mod.ts # Main entry (Node/Deno/Bun)
2525
│ ├── browser-mod.ts # Browser entry
26-
│ └── dist/ # Build output (do not edit)
26+
│ └── dist/ # Build output (do not edit)
2727
├── packages/demo-api/ # Sample API implementation
28-
├── examples/ # 10+ usage examples
29-
├── docs/ # Documentation site
30-
└── package.json # pnpm workspace config
28+
├── packages/slidev/ # Presentation slides
29+
├── examples/ # 10+ usage examples
30+
├── interop/ # Language interop (Go, Python, Rust, Swift)
31+
├── docs/ # Documentation site
32+
└── package.json # pnpm workspace config
3133
```
3234

3335
## WHERE TO LOOK
@@ -43,17 +45,23 @@ kkrpc/
4345

4446
## CODE MAP
4547

46-
| Symbol | Type | Location | Role |
47-
| --------------------- | --------- | -------------------- | ------------------------------------- |
48-
| RPCChannel | Class | src/channel.ts | Bidirectional RPC channel core |
49-
| IoInterface | Interface | src/interface.ts | Transport layer abstraction interface |
50-
| IoCapabilities | Interface | src/interface.ts | Adapter capability declarations |
51-
| serialize/deserialize | Function | src/serialization.ts | Message serialization |
52-
| transfer() | Function | src/transfer.ts | Mark zero-copy objects |
53-
| NodeIo | Class | adapters/node.ts | Node.js stdio |
54-
| DenoIo | Class | adapters/deno.ts | Deno stdio |
55-
| WorkerParentIO | Class | adapters/worker.ts | Web Worker parent side |
56-
| WorkerChildIO | Class | adapters/worker.ts | Web Worker child side |
48+
| Symbol | Type | Location | Role |
49+
| ----------------------------- | --------- | --------------------------------- | ------------------------------------- |
50+
| RPCChannel | Class | src/channel.ts | Bidirectional RPC channel core |
51+
| IoInterface | Interface | src/interface.ts | Transport layer abstraction interface |
52+
| IoCapabilities | Interface | src/interface.ts | Adapter capability declarations |
53+
| serialize/deserialize | Function | src/serialization.ts | Message serialization |
54+
| transfer() | Function | src/transfer.ts | Mark zero-copy objects |
55+
| NodeIo | Class | adapters/node.ts | Node.js stdio |
56+
| DenoIo | Class | adapters/deno.ts | Deno stdio |
57+
| BunIo | Class | adapters/bun.ts | Bun stdio |
58+
| WorkerParentIO | Class | adapters/worker.ts | Web Worker parent side |
59+
| WorkerChildIO | Class | adapters/worker.ts | Web Worker child side |
60+
| TauriShellStdio | Class | adapters/tauri.ts | Tauri shell plugin adapter |
61+
| ElectronIpcMainIO | Class | adapters/electron-ipc-main.ts | Electron main IPC |
62+
| ElectronIpcRendererIO | Class | adapters/electron-ipc-renderer.ts | Electron renderer IPC |
63+
| ElectronUtilityProcessIO | Class | adapters/electron.ts | Electron utility process (main) |
64+
| ElectronUtilityProcessChildIO | Class | adapters/electron-child.ts | Electron utility process (child) |
5765

5866
## CONVENTIONS
5967

README.md

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,6 @@ For backward compatibility, the receiving side will automatically detect the ser
171171

172172
### Installation
173173

174-
175-
176174
```bash
177175
# npm
178176
npm install kkrpc
@@ -1250,6 +1248,93 @@ const rpc = new RPCChannel<WorkerAPI, {}>(io, {
12501248
- **Multiple channels**: Can create multiple relays on different IPC channels
12511249
- **Composable**: Can chain relays through multiple processes
12521250
1251+
## 📊 Benchmarks
1252+
1253+
kkrpc includes comprehensive benchmarks to measure throughput and data transfer performance across different transports and runtimes.
1254+
1255+
### Running Benchmarks
1256+
1257+
```bash
1258+
# Run all benchmarks
1259+
bun test __tests__/stdio-benchmark.test.ts
1260+
bun test __tests__/websocket-benchmark.test.ts
1261+
bun test __tests__/stdio-large-data-benchmark.test.ts
1262+
bun test __tests__/websocket-large-data-benchmark.test.ts
1263+
1264+
# Or run all tests including benchmarks
1265+
bun test
1266+
```
1267+
1268+
### Benchmark Design
1269+
1270+
The benchmarks are designed to measure two key aspects of RPC performance:
1271+
1272+
1. **Call Throughput** (`stdio-benchmark.test.ts`, `websocket-benchmark.test.ts`)
1273+
1274+
- **Sequential Operations**: Measures latency per call when making blocking calls one after another
1275+
- **Concurrent Operations**: Measures throughput when making many calls in parallel using `Promise.all`
1276+
- **Batch Operations**: Tests batching multiple operations into a single RPC call
1277+
- **Latency Distribution**: Pings the server 1,000 times to calculate min/avg/p99/max latency
1278+
1279+
2. **Data Transfer Throughput** (`stdio-large-data-benchmark.test.ts`, `websocket-large-data-benchmark.test.ts`)
1280+
- **Upload**: Client sends large data payloads to the server
1281+
- **Download**: Server generates and sends large data to the client
1282+
- **Echo**: Bidirectional transfer (client sends, server echoes back)
1283+
- Tests various payload sizes: 1KB, 10KB, 100KB, 1MB, 10MB
1284+
1285+
### Benchmark Results
1286+
1287+
Results from running on a MacBook Pro (Apple Silicon):
1288+
1289+
#### Stdio Adapter (Process-to-Process)
1290+
1291+
| Runtime | Operation | Calls/sec | Latency (avg) |
1292+
| ----------- | --------------- | ----------------- | ------------- |
1293+
| **Bun** | Sequential Echo | 22,234 | 0.046ms |
1294+
| **Bun** | Concurrent Echo | 151,069 | - |
1295+
| **Bun** | Batch (100 ops) | 453,042 effective | - |
1296+
| **Node.js** | Sequential Echo | 23,985 | 0.038ms |
1297+
| **Node.js** | Concurrent Echo | 145,516 | - |
1298+
| **Deno** | Sequential Echo | 20,028 | 0.047ms |
1299+
| **Deno** | Concurrent Echo | 123,079 | - |
1300+
1301+
#### Stdio Large Data Transfer
1302+
1303+
| Runtime | Operation | 1MB Payload | 10MB Payload |
1304+
| ----------- | ------------ | ----------- | ------------ |
1305+
| **Bun** | Upload | ~1,010 MB/s | ~658 MB/s |
1306+
| **Bun** | Download | ~134 MB/s | ~132 MB/s |
1307+
| **Bun** | Echo (100KB) | ~1,132 MB/s | - |
1308+
| **Node.js** | Upload | ~382 MB/s | ~92 MB/s |
1309+
| **Node.js** | Download | ~75 MB/s | ~30 MB/s |
1310+
| **Deno** | Upload | ~358 MB/s | ~91 MB/s |
1311+
| **Deno** | Download | ~74 MB/s | ~33 MB/s |
1312+
1313+
#### WebSocket Adapter
1314+
1315+
| Operation | Calls/sec | Latency (avg) |
1316+
| --------------- | ----------------- | ------------- |
1317+
| Sequential Echo | 22,314 | 0.040ms |
1318+
| Concurrent Echo | 74,954 | - |
1319+
| Batch (100 ops) | 483,318 effective | - |
1320+
1321+
#### WebSocket Large Data Transfer
1322+
1323+
| Operation | 1MB Payload | 10MB Payload |
1324+
| ------------ | ----------- | ------------ |
1325+
| Upload | ~577 MB/s | ~927 MB/s |
1326+
| Download | ~137 MB/s | ~149 MB/s |
1327+
| Echo (100KB) | ~799 MB/s | - |
1328+
1329+
### Key Findings
1330+
1331+
- **Bun** consistently outperforms Node.js and Deno for stdio communication, especially for large data transfers
1332+
- **Stdio** is significantly faster than WebSocket for local process communication (2-3x higher throughput)
1333+
- **Concurrent operations** achieve 6-7x higher throughput than sequential operations
1334+
- **Batching** is highly effective - 100 operations per batch achieves 400K+ effective calls/sec
1335+
- **Upload** is faster than download due to JSON serialization overhead on the response path
1336+
- **WebSocket** performance is excellent for network communication, nearly matching stdio for small payloads
1337+
12531338
## 🆚 Comparison with Alternatives
12541339
12551340
<div align="center">

0 commit comments

Comments
 (0)