A simple JavaScript library providing an asynchronous method call interface for Workers, Iframes and cross-window contexts using postMessage.
npm install shared-ipcimport {
makeIpcRequest,
sendIpcMessage,
addMessageHandler,
handleMessage
} from "shared-ipc";
// In both frames/workers:
window.addEventListener("message", handleMessage);
// Add message handlers
addMessageHandler("greet", (name) => {
return `Hello ${name}!`;
});
// In parent frame communicating with iframe:
const iframe = document.getElementById("my-iframe");
await makeIpcRequest(iframe, "greet", "World"); // Returns "Hello World!"
// Or send a message without waiting for response
sendIpcMessage(iframe, "notification", { type: "alert" });Makes an IPC request and returns a promise that resolves with the response.
Sends an IPC message without waiting for a response.
Adds a handler function for a specific message type.
The message event handler that processes incoming messages.
Utility class used internally for managing promise resolution.
createStrictIpcEndpoint(port, options) creates an opt-in endpoint bound to one
MessagePort. It uses construction-time incoming/outgoing command registries,
consumer validators, exact envelopes, sequenced traffic, random request IDs,
timeouts, abort cleanup and explicit disposal. It never uses the legacy global
handler registry or global-function fallback.
import { createStrictIpcEndpoint } from "shared-ipc";
const endpoint = createStrictIpcEndpoint(port, {
channelId: crypto.randomUUID(),
incoming: new Map([
["notify", {
kind: "message",
validate: value => typeof value === "string",
handler: value => console.log(value)
}]
]),
outgoing: new Map([
["lookup", {
kind: "request",
validate: value => typeof value === "string",
validateResult: value => typeof value === "string"
}]
])
});
const value = await endpoint.request("lookup", "key", {
timeoutMs: 1000,
signal: abortController.signal
});
endpoint.dispose();Application command names, schemas and lifecycle policy remain the consumer's responsibility. Strict endpoints are separate from the existing API, whose default behavior remains unchanged.
Incoming and outgoing registries are copied and retained only inside the local endpoint. They are never transmitted or exposed through an enumeration API. Each peer must construct its own validators; applications should not share one bidirectional schema object merely for convenience. Commands may use non-empty strings or non-negative safe integers, allowing consumers to use compact or transformed identifiers.
Remote handler exception details are redacted as Remote handler failed by
default. Tests or development tools may set exposeRemoteErrors: true on the
receiving endpoint, but production receivers should retain the safe default.
Detailed onReject diagnostics are local to the endpoint and are never sent to
its peer.
| Type | Window (Browser) | Worker (Browser) | MessagePort (Node) |
|---|---|---|---|
| Window (Browser) | ✅ Yes | ✅ Yes | ❌ No |
| Worker (Browser) | ✅ Yes | ✅ Yes | ❌ No |
| Iframe (Browser) | ✅ Yes | ✅ Yes | ❌ No |
| MessagePort (Node) | ❌ No | ❌ No | ✅ Yes |
| Worker (Node) | ❌ No | ❌ No | ✅ Yes |
MIT