Cross-Domain Post-Messaging
post-robot is a JavaScript library that simplifies secure cross-domain communication between browser windows (e.g., parent/iframe, opener/popup) using the native HTML5 `postMessage` API. It provides a robust, promise-based listener/sender pattern, abstracting away the complexities of serialization and asynchronous communication. A key differentiator is its ability to automatically serialize and deserialize complex data types, including functions, Promises (wrapped in `ZalgoPromise`), and Error objects, enabling advanced inter-window interactions. The library ensures reliable messaging with built-in error handling, timeouts, and options for securing channels by specifying target windows or domains. The current stable version is 8.0.32, and while a strict release cadence isn't explicitly stated, it is actively maintained by KrakenJS. This library is crucial for applications requiring seamless interaction between different origins.
Common errors
-
TypeError: postRobot.send is not a function
cause The `postRobot` object was not correctly imported or is not available in the global scope.fixEnsure `import postRobot from 'post-robot';` (ESM) or `const postRobot = require('post-robot');` (CommonJS) is at the top of your file where `postRobot` is used. Verify your bundler configuration if using a module system. -
Error: Message timeout: No response from window
cause The target window or listener did not send a response within the configured timeout period.fixCheck the listening window for errors or long-running operations. Increase the `timeout` option in the `postRobot.send` call if a longer processing time is expected on the receiver side (e.g., `{ timeout: 10000 }`). Ensure the listener function actually returns a value or a Promise that resolves. -
SecurityError: Blocked a frame from accessing a cross-origin frame.
cause The browser's same-origin policy is preventing direct JavaScript access to another window's content. This error typically occurs when attempting to directly manipulate an iframe's `contentWindow` or `contentDocument` from a different origin, and not directly related to `post-robot`'s message passing.fix`post-robot` is designed to *circumvent* this exact problem for message passing. Ensure you are using `postRobot.send()` and `postRobot.on()` correctly for communication, and not attempting direct DOM manipulation or property access across origins. Also, verify that the `domain` option in `postRobot.on` and `postRobot.send` is correctly configured. -
Error: Can not accept message from https://evil-domain.com
cause The `postRobot.on` listener was configured with a specific `domain` option (or `window` option), and the incoming message's origin did not match the allowed source.fixReview the `domain` (and `window`) option provided to `postRobot.on`. Ensure it correctly specifies the expected origin(s) from which messages should be accepted. If messages are expected from multiple specific origins, the `domain` option can often accept an array of strings.
Warnings
- gotcha When functions or Promises are passed across domains, post-robot wraps them with its internal `ZalgoPromise` implementation. If you are expecting native `Promise` instances for `instanceof` checks or specific promise library interoperability, this could lead to unexpected behavior.
- breaking Older versions (prior to v7) might have different API signatures or behaviors, especially regarding error handling and serialization. While the core `on`/`send` pattern remains, internal mechanics and advanced options have evolved.
- gotcha Improper configuration of the `domain` option (e.g., `postRobot.on('event', { domain: 'http://specific-domain.com' }, handler)`) can lead to messages being ignored or `SecurityError` exceptions if the message's origin does not match the specified domain. Conversely, omitting a specific domain can open up security vulnerabilities if not intended.
- gotcha Messages sent via `post-robot` have a default timeout. If the listening window does not respond within this period, the sending promise will reject with a timeout error. This is by design to prevent hanging calls, but can be unexpected if not accounted for.
Install
-
npm install post-robot -
yarn add post-robot -
pnpm add post-robot
Imports
- postRobot
const postRobot = require('post-robot');import postRobot from 'post-robot';
- send
import { send } from 'post-robot'; // Incorrect destructuringimport postRobot from 'post-robot'; postRobot.send(someWindow, 'messageType', data);
- on
import { on } from 'post-robot'; // Incorrect destructuringimport postRobot from 'post-robot'; postRobot.on('messageType', handler);
Quickstart
import postRobot from 'post-robot';
// --- Listener Window (e.g., in an iframe) ---
postRobot.on('getUserDetails', function(event) {
const userId = event.data.id;
console.log(`Listener received request for user ID: ${userId} from ${event.origin}`);
// Simulate fetching user data asynchronously
return new Promise(resolve => {
setTimeout(() => {
if (userId === 123) {
resolve({
id: userId,
name: 'Alice Smith',
email: 'alice@example.com',
// Functions can also be returned and invoked remotely
logStatus: (status) => console.log(`[Remote Log] Alice's status: ${status}`)
});
} else {
resolve(null); // User not found
}
}, 500);
});
});
console.log('post-robot listener initialized for getUserDetails.');
// --- Sender Window (e.g., parent window) ---
// This would typically be in a *different* window context, e.g., the parent window calling the iframe.
// For demonstration, we'll simulate the call.
// In a real scenario, 'someWindow' would be window.frames[0] or a popup window reference.
const someWindow = window.opener || window.parent; // Placeholder, adjust for actual target window
if (someWindow && someWindow !== window) {
postRobot.send(someWindow, 'getUserDetails', { id: 123 }, { timeout: 3000 })
.then(function(event) {
const user = event.data;
if (user) {
console.log(`Sender received user: ${user.name} from ${event.origin}`);
user.logStatus('active'); // Call a function passed from the other window
} else {
console.log('Sender: User not found.');
}
})
.catch(function(err) {
console.error('Sender error:', err.message);
});
} else {
console.warn('Sender simulation: Target window not found. This code needs to run in a separate context to function.');
}