# What Is @escrcpy/electron-ipcx? Function-Friendly IPC for Electron Explained

> Discover @escrcpy/electron-ipcx a lightweight library enhancing Electron IPC for function-friendly communication. Proxy callbacks between renderer and main processes easily.

- Repository: [viarotel-org/escrcpy](https://github.com/viarotel-org/escrcpy)
- Tags: deep-dive
- Published: 2026-09-10

---

**@escrcpy/electron-ipcx is a lightweight library that extends Electron’s standard IPC to support function-friendly communication between renderer and main processes by proxying callbacks through temporary channels.**

Standard Electron IPC (`ipcRenderer.invoke` and `ipcMain.handle`) cannot serialize JavaScript functions across the process boundary. The `@escrcpy/electron-ipcx` package—developed as part of the [viarotel-org/escrcpy](https://github.com/viarotel-org/escrcpy) project—solves this limitation by transparently lifting function arguments into transferable descriptors and reconstructing them as proxies on the receiving side.

## How Function Proxying Works in @escrcpy/electron-ipcx

The library intercepts IPC calls to scan for function arguments, replacing them with metadata descriptors while registering temporary listeners that bridge the callback traffic back to the original renderer functions.

### Scanning and Serializing Callbacks on the Renderer

When you call `ipcxRenderer.invoke` in [`packages/electron-ipcx/renderer/index.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/electron-ipcx/renderer/index.ts), the library inspects the payload for any function values. Each discovered function is assigned a unique channel name, and a temporary listener is registered on that channel. The function is then replaced by a **function descriptor**—a plain object containing the channel identifier—allowing the payload to pass through Electron’s structured clone algorithm.

### Rehydrating Proxies on the Main Process

On the main side, `ipcxMain.handle` (exported from [`packages/electron-ipcx/main/index.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/electron-ipcx/main/index.ts)) receives the descriptors alongside regular arguments. Using logic defined in [`packages/electron-ipcx/shared/serialize.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/electron-ipcx/shared/serialize.ts), it reconstructs proxy functions that post messages back to the renderer via `event.sender.send(channel, …)`. From the handler’s perspective, these proxies behave exactly like local JavaScript callbacks.

## Lifecycle Management: Auto vs. Retained Mode

@escrcpy/electron-ipcx provides two strategies for managing the temporary channels that back proxied callbacks.

**Auto Mode (Default):** When using `ipcxRenderer.invoke`, the library automatically disposes of temporary listeners once the promise settles. This prevents memory leaks for one-shot operations like file reads or single response queries.

**Retained Mode:** For long-running tasks with multiple progress updates, `ipcxRenderer.invokeRetained` (also defined in [`renderer/index.ts`](https://github.com/viarotel-org/escrcpy/blob/main/renderer/index.ts)) returns a `{ promise, dispose }` pair. You retain full control over the channel lifecycle, calling `dispose()` explicitly when the callback is no longer needed.

```typescript
import { ipcxRenderer } from '@escrcpy/electron-ipcx/renderer'

const { promise, dispose } = ipcxRenderer.invokeRetained('task:start', {
  onProgress: (pct: number) => console.log('progress', pct)
})

await promise   // Wait for task completion
dispose()       // Explicitly clean up the temporary listeners

```

## Safety and Validation Mechanisms

The library implements defensive programming to protect against malformed IPC traffic. The [`shared/validators.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/validators.ts) module validates every incoming envelope, ensuring that payloads match the expected shape before processing. If validation fails—such as when a sender is missing in the main process—the library throws a typed `InvalidPayloadError` (defined in [`shared/errors.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/errors.ts)) rather than crashing silently. Errors occurring within renderer callbacks are logged without breaking the invoke chain, maintaining robustness during complex operations.

## Practical Usage Examples

The API mirrors native Electron IPC, allowing drop-in replacement of `ipcRenderer.invoke` with `ipcxRenderer.invoke` and `ipcMain.handle` with `ipcxMain.handle`.

### Renderer-Side Invocation with Callback

```typescript
import { ipcxRenderer } from '@escrcpy/electron-ipcx/renderer'

await ipcxRenderer.invoke('files:read', {
  path: '/tmp/demo',
  // The function will be proxied to the main process
  onChunk: (chunk: Uint8Array) => console.log('got', chunk.length)
})

```

### Main-Process Handler with Rehydrated Callback

```typescript
import { ipcxMain } from '@escrcpy/electron-ipcx/main'

ipcxMain.handle('files:read', async (_event, payload: {
  path: string,
  onChunk: (chunk: Uint8Array) => void
}) => {
  // The `onChunk` argument is a proxy back to the renderer
  payload.onChunk(new Uint8Array([1, 2, 3]))
  return 'done'
})

```

These patterns enable streaming data from the main process to the renderer or reporting progress on long-running computations without polling or complex event management.

## Summary

- **@escrcpy/electron-ipcx** transparently proxies JavaScript functions across Electron’s IPC boundary by serializing them into descriptors in [`renderer/index.ts`](https://github.com/viarotel-org/escrcpy/blob/main/renderer/index.ts) and reconstructing them in [`main/index.ts`](https://github.com/viarotel-org/escrcpy/blob/main/main/index.ts).
- The library supports both automatic lifecycle management (auto mode) and manual disposal (retained mode via `invokeRetained`) to suit one-shot and long-running operations.
- Built-in validation in [`shared/validators.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/validators.ts) and custom error types in [`shared/errors.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/errors.ts) ensure type safety and graceful error handling.
- Channel pooling logic in [`shared/channel-pool.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/channel-pool.ts) manages the temporary communication channels efficiently.

## Frequently Asked Questions

### What is the difference between `ipcRenderer.invoke` and `ipcxRenderer.invoke`?

Standard `ipcRenderer.invoke` cannot serialize function arguments because Electron’s IPC uses the structured clone algorithm, which does not support functions. `ipcxRenderer.invoke` wraps the native call to intercept function arguments, convert them to descriptors using logic in [`shared/serialize.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/serialize.ts), and register temporary listeners so the main process can call back into the renderer.

### How does @escrcpy/electron-ipcx prevent memory leaks?

In auto mode (the default), temporary channels are automatically cleaned up when the promise returned by `invoke` settles. For retained mode, the `dispose()` function returned by `invokeRetained` allows developers to explicitly release channels when callbacks are no longer needed. The [`shared/channel-pool.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/channel-pool.ts) module tracks these temporary channels to ensure proper cleanup.

### Can I pass multiple callbacks in a single IPC call?

Yes. The library scans the entire argument tree for functions, so you can include multiple callbacks (e.g., `onProgress`, `onComplete`, `onError`) in a single payload. Each function receives a unique channel descriptor managed by the channel pool, allowing independent communication streams back to the renderer.

### Is @escrcpy/electron-ipcx compatible with Electron's context isolation?

Yes. The library is designed to work with modern Electron security practices. The renderer module exports a safe interface that can be exposed through a preload script using `contextBridge`, while the main module operates within the privileged main process. The validation layer in [`shared/validators.ts`](https://github.com/viarotel-org/escrcpy/blob/main/shared/validators.ts) adds an additional security boundary by rejecting malformed payloads before they reach application logic.