@mukea/electron-callback-rpc
v0.2.0
Published
rpc for electron
Readme
electron-callback-rpc
- A lightweight RPC library built for Electron (Main ↔ Renderer), supporting
Cross-Process CallbacksandComplex Type Transmission.
⚠️ Pre-Release Notice
Current version < 1.0.0. The project is in the early stages of development. Portions of this code were generated with AI assistance. While all code has undergone manual review and validation via unit tests, unknown edge cases or instabilities may still exist. Prior to the v1.0.0 release, the API is subject to Breaking Changes at any time. Please use with caution in production environments and pin specific versions.
Core features focus on:
- Promise-based Service Calls: The Renderer calls Main process methods as native asynchronous functions, providing a seamless local-call experience.
- Cross-process Callback Support: Functions and objects with methods can be passed as arguments or return values across processes, enabling true bidirectional interaction.
- Rich Type Preservation: Supports native types like
Map,Set,Date,Error,BigInt, andBuffer, avoiding data distortion caused by the "Forced JSONification" typical in IPC communication.
Requirement:
electron >= 13.0.0
📖 Background & Motivation
This project is a fork of @ggworks/electron-rpc. While the original library established an excellent IPC method invocation model, complex real-world business scenarios often require greater flexibility:
- Callbacks as First-Class Citizens: We need to transmit not just data, but capabilities. This includes passing callback functions to the main process to report progress or handle asynchronous results.
- High-Fidelity Types: Electron IPC natively supports the Structured Clone algorithm. We fully leverage this to avoid unnecessary serialization overhead and type loss.
- Controllable Lifecycle: For cross-process references, we emphasize an explicit resource release (
Dispose) mechanism, complemented by an internal automatic garbage collection strategy to minimize the risk of memory leaks.
Core Differences from @ggworks/electron-rpc
- Focused: Exclusively focuses on the Request/Response + Callback model.
- Streamlined: Removed the event subscription model (no
on/once/offsemantics; explicit business-level callbacks are recommended instead). - Enhanced: Strengthened the ability to pass cross-process objects/functions and improved support for non-standard JSON types.
🧩 API Overview
Main Process
const server = new Server() // Create RPC Server
const service = createRpcService(instance) // Wrap object / instance
server.registerService('namespace', service) // Register serviceRenderer Process
const client = new Client(ipcRenderer) // Create Client
const proxy = createProxyService(client, 'namespace') // Create typed proxy
// Invoke
proxy.methodName(args)📦 Installation
npm i @mukea/electron-callback-rpc🚀 Quick Start
1. Define Type Interface (Shared)
It is recommended to define interfaces in a shared file to enable full TypeScript type inference.
export interface IDemoService {
echo(text: string): Promise<string>
run(taskName: string, onProgress: (percent: number) => void): Promise<string>
}2. Main Process: Implement and Register
import { Server, createRpcService } from '@mukea/electron-callback-rpc'
class DemoService implements IDemoService {
async echo(text: string) {
return text
}
async run(taskName: string, onProgress: (percent: number) => void) {
onProgress(0)
onProgress(50)
onProgress(100)
return `done:${taskName}`
}
}
const server = new Server()
// Wrap the instance as an RPC service and register it
server.registerService('demo', createRpcService(new DemoService()))3. Renderer Process: Create Proxy and Call
import { Client, createProxyService } from '@mukea/electron-callback-rpc'
// Initialize client (requires ipcRenderer)
const client = new Client(window.ipcRenderer)
const demo = createProxyService<IDemoService>(client, 'demo')
// Scenario 1: Standard async call
const echoed = await demo.echo('hello')
console.log(echoed)
// Scenario 2: Call with callback
const result = await demo.run('build', (percent) => {
console.log('Build progress:', percent)
})
console.log(result)⚠️ TypeScript Configuration Note
This library uses exports to distinguish between the Main process (CJS) and the Renderer process (ESM). If you encounter type inference errors in the Renderer process (e.g., VSCode pointing to the wrong dist/main path), ensure your Renderer's tsconfig.json is configured with a modern module resolution strategy:
// tsconfig.json (Renderer Process)
{
"compilerOptions": {
// Adapt for modern bundlers like Vite/Webpack (requires TS 5.0+)
"moduleResolution": "bundler",
// Explicitly specify browser environment to force hitting the "browser" condition in exports
"customConditions": ["browser"]
}
}Benchmarks
We conducted comprehensive performance benchmarking of electron-callback-rpc against native Electron IPC, covering throughput, latency, serialization, error handling, and memory usage.
This document cites a subset of the data. You can run the electron project within the example directory to generate full results.
Test Environment
- Platform: arm64 Darwin 25.1.0 / Apple M4
- Node.js: v24.11.0
- Electron: v39.2.7
Results
🚀 Throughput (Operations/sec)
| Test Scenario | RPC Package | Native IPC | Performance Ratio | |---------|-------------|------------|----------| | Batch Add (15,000 ops) | 5,500 ops/sec | 8,081 ops/sec | 0.68x | | Batch Process (6,000 ops) | 15,476 ops/sec | 15,940 ops/sec | 0.97x |
- Average Latency: RPC 0.12ms vs Native 0.09ms (+33.3% overhead)
- Overall Throughput: RPC maintains 87-97% of native performance in high-frequency call scenarios.
📊 Serialization Performance
| Data Type | RPC Package | Native IPC | Performance Ratio | |---------|-------------|------------|----------| | Simple Object (5,000 ops) | 22,222 ops/sec | 19,747 ops/sec | 1.13x | | Complex Object (2,500 ops) | 15,509 ops/sec | 17,643 ops/sec | 0.88x |
- Average Latency: RPC 0.10ms vs Native 0.09ms (+11.1% overhead)
- Serialization Advantage: For simple object handling, RPC is 13% faster than native IPC.
⚡ Latency (Round-trip time)
| Operation Type | RPC Package | Native IPC | Latency Overhead | |---------|-------------|------------|----------| | No-op (5,000 ops) | 0.041ms | 0.046ms | 0.0% | | Small Data Echo (5,000 ops) | 0.042ms | 0.042ms | 0.0% |
- Ultra-low Latency: In lightweight operations, RPC latency is virtually identical to native IPC.
- Throughput: 21,471 ops/sec vs 21,060 ops/sec (1.02x).
🛡️ Error Handling Performance
| Scenario | RPC Package | Native IPC | Performance Ratio | |------|-------------|------------|----------| | Success Path (5,000 ops) | 22,036 ops/sec | 23,753 ops/sec | 0.93x | | Error Path (5,000 ops) | 14,472 ops/sec | 11,593 ops/sec | 1.25x |
- Error Handling Advantage: RPC is 25% faster than native IPC in error scenarios due to optimized exception propagation.
- Average Latency: Virtually identical for both (0.06ms).
💾 Memory Usage
| Test Scenario | RPC Package | Native IPC | Performance Ratio | |---------|-------------|------------|----------| | Large Array (30 ops) | 36 ops/sec | 38 ops/sec | 0.95x | | 1MB Buffer (60 ops) | 632 ops/sec | 628 ops/sec | 1.01x |
- Large Data Processing: Performance loss is controlled within 5% when handling large data structures.
- Memory Efficiency: Slightly outperforms native IPC in 1MB buffer operations.
Key Conclusions
- Extremely Low Latency: In no-op and small data echo scenarios, the library's latency is on par with native IPC (~0.04ms), making the absolute overhead negligible.
- Superior Error Handling: Thanks to optimized exception propagation mechanisms, error path processing is 25% faster than native.
- Serialization Performance: Simple object transmission is 13% faster than native; complex objects show only ~12% performance loss.
- Throughput: In high-frequency call scenarios, it maintains 87%-97% of native IPC throughput.
Summary: While providing powerful type safety and callback capabilities, electron-callback-rpc introduces no significant performance bottlenecks and even outperforms native implementation in specific scenarios (e.g., error handling, simple objects).
⚠️ Best Practices
- Process Model: This library is designed for the real multi-process Electron environment.
- Event Subscription: We do not provide
EventEmitterstyle APIs. If you need to push events, please explicitly designsubscribe(callback)methods in your business interface. - Resource Management: Although the library includes an internal garbage collection mechanism, for long-lifecycle callbacks or objects, it is recommended to explicitly provide
dispose()orunsubscribe()methods in your interface to ensure absolute safety.
License
MIT
