Promise-based RPC for iframes and Web Workers, with progress feedback. The browser library shares one API across window messages, BroadcastChannel, storage events and page-script events. An Electron adapter uses the same request lifecycle.
Use it for file processing in Workers, embedded editor tasks, or other calls that need a result and progress updates. Typed clients check methods/arguments; optional deadlines and AbortSignal cancellation bound local waiting.
| Package | Use it for | Documentation |
|---|---|---|
| duplex-message | Iframes, Workers and same-origin browser tabs; no runtime dependencies | Browser guide |
| simple-electron-ipc | Bidirectional main ↔ isolated preload RPC | Electron guide |
pnpm add duplex-message
# or: npm install duplex-messageworker.ts
import { PostMessageHub, READY_METHOD } from 'duplex-message'
const hub = new PostMessageHub()
hub.on(self, 'add', (a: number, b: number) => a + b)
hub.on(self, READY_METHOD, () => true)main.ts
import { PostMessageHub, createRpcClient, waitForPeer } from 'duplex-message'
interface WorkerApi { add(a: number, b: number): number }
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })
const hub = new PostMessageHub({ requestTimeout: 5000 })
const rpc = createRpcClient<WorkerApi>((method, ...args) => hub.emit(worker, method, ...args))
try {
await waitForPeer(method => hub.emit(worker, method))
console.log(await rpc.call('add', 2, 3)) // 5
} finally {
hub.destroy()
worker.terminate()
}This example uses module Workers with Vite. See the browser guide for progress and cancellation, iframe origin restrictions, transport selection and comparisons.
For Electron, keep Node integration disabled and context isolation enabled. The runnable Electron demo uses a bundled sandboxed preload and exposes specific application methods through contextBridge.
Use Node.js 22.12+, 24, or 26+ and pnpm 12.8.1. The workspace pins TypeScript 7.0.2, Vite 8.3.2 and Vitest 5.0.3.
npm install -g pnpm@12.8.1
pnpm install --frozen-lockfile
pnpm --filter duplex-message exec playwright install --with-deps chromium firefox webkit
pnpm lint
pnpm build
pnpm typecheck
pnpm test
pnpm check:packagepnpm test:unit runs Node and mocked Electron IPC tests without a browser. pnpm test also exercises Chromium, Firefox and WebKit Workers, real cross-origin iframes, proxies, page-script events and storage messaging. CI checks Node 22 and 24, builds both demos, and validates packed CJS/ESM imports, declarations, UMD globals and tree shaking. Packed type checks include invalid methods/arguments and inferred return types.
pnpm --filter duplex-message dev
pnpm --filter duplex-message build:demo
pnpm --filter simple-electron-ipc build:demo
pnpm --filter simple-electron-ipc dev
pnpm check:electron
# Headless Linux: xvfb-run -a pnpm check:electronThe separate Electron smoke test launches an actual main/preload/renderer application and checks context isolation, restricted bridge methods, bidirectional RPC, progress, local cancellation and renderer controls. CI runs Electron 31 and 44 on Linux, and Electron 44 on Windows and macOS. Set ELECTRON_SKIP_BINARY_DOWNLOAD=1 during installation if you only need library builds/unit tests; leave it unset for the Electron demo/smoke test.
Library output targets ES2018; packed JavaScript is checked against that syntax level. Transport APIs must exist in the runtime. waitForPeer and cancellation require AbortController/AbortSignal; ordinary calls do not. Legacy browser distribution filenames and the UMD global remain available; package imports use explicit .mjs/CommonJS entries. TypeScript emits declarations directly. Existing emit APIs remain available; new typed clients and ready endpoints are additive. Origin/sender filters are opt-in to preserve existing integrations; configure them explicitly for trusted peers.
pnpm changeset
pnpm changeset version
pnpm publish -rReview changesets, validation and release versions before publishing. Cancellation ends local waiting; it does not stop peer-side work or undo effects.
MIT. See LICENSE.