Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
A library to facilitate communication between windows (iframe, worker, popup, ...)
Communicate with web Workers
and other Windows
using a simple Promise
based API
With post-me it is easy for a parent (for example the main app) and a child (for example a worker or an iframe) to expose methods and custom events to each other.
Main features:
In this live demo a parent window achieves two-way communication with its 5 children (4 iframes and 1 web worker).
To establish a connection between two windows follow the steps below:
Messenger
, an object implementing the low level communication (WindowMessenger
and WorkerMessenger
already provided).ParentHandshake()
and ChildHandshake()
methods respectively.methods
parameter contains the methods that each window will expose to the other.Promise<Connection>
to the two windows.connection.getRemoteHandle()
method.
remoteHandle.call(methodName, ...args)
to call methods on the other window. It returns a Promise
of the result.remoteHandle.addEventListener(eventName, callback)
to listen to specific events dispatched by the other window.remoteHandle.removeEventListener(eventName, callback)
to remove listeners.connection.getLocalHandle()
method.
localHandle.emit(eventName, payload)
to emit a specific event with the given payload.Refer to the code snippet below as an example of these steps.
import { ParentHandshake, WindowMessenger } from 'post-me';
// Create the child window any way you like (iframe here, but could be popup or tab too)
const childFrame = document.createElement('iframe');
childFrame.src = './child.html';
const childWindow = childFrame.contentWindow;
// Define the methods you want to expose to the other window.
// Methods can either return values or Promises
const methods = {
foo: (s, x) => s.length * x,
bar: (x) => Promise.resolve(x * 2),
}
// Start the handshake
// For safety it is strongly adviced to pass the explicit child origin instead of '*'
const messenger = new WindowMessenger({
remoteWindow: childWindow,
remoteOrigin: '*'
});
ParentHandshake(messenger, methods)
.then((connection) => {
const localHandle = connection.localHandle();
const remoteHandle = connection.remoteHandle();
// Call a method on the child
remoteHandle.call('baz', 3)
.then((value) => {
console.log(value); // 9
})
// Listen for an event emitted by the child
remoteHandle.addEventListener('some-child-event', (payload) => {
console.log(payload) // 'Hi from child'
});
// Emit an evevent
localHandle.emit('some-parent-event', 'Hi from parent');
})
import { ChildHandshake, WindowMessenger } from 'post-me';
// Define the methods you want to expose to the other window.
// Methods can either return values or Promises
const methods = {
baz: (x) => x * 3,
}
// Start the handshake
// For safety it is strongly adviced to pass the explicit parent origin instead of '*'
const messenger = new WindowMessenger({
remoteWindow: window.parent,
remoteOrigin: '*'
});
const messenger = new WindowMessenger({ remoteOrigin: '*' });
ChildHandshake(messenger, methods)
.then((connection) => {
const localHandle = connection.localHandle();
const remoteHandle = connection.remoteHandle();
// Call a method on the parent
remoteHandle.call('foo', 'ciao', 2)
.then((value) => {
console.log(value); // 8
})
// Listen for an event emitted by the child
remoteHandle.addEventListener('some-parent-event', (payload) => {
console.log(payload) // 'Hi from parent'
});
// Emit an evevent
localHandle.emit('some-child-event', 'Hi from child');
})
Thanks to post-me extensive typescript support, the correctness of the following items can be statically checked during development:
Ideally methods and events types should be defined in a place accessible to the code of both parent and child, so that they can both import the same type definition. This way, it will be ensured that the contract between the parties will be enforced.
Below a modified version of the previous example using typescript.
// common.ts
export type ParentMethods = {
foo: (s: string, x: number) => number;
bar: (x: number) => Promise<number>;
};
export type ParentEvents = {
'some-parent-event': string;
}
export type ChildMethods = {
baz: (x: number) => number;
};
export type ChildEvents = {
'some-child-event': string;
}
import { ParentHandshake, WindowMessenger, LocalHandle, RemoteHandle } from 'post-me';
import { ParentMethods, ParentEvents, ChildMethods, ChildEvents} from '/path/to/common';
// Create the child window any way you like (iframe here, but could be popup or tab too)
const childFrame = document.createElement('iframe');
childFrame.src = './child.html';
const childWindow = childFrame.contentWindow;
// Define the methods you want to expose to the other window.
// Methods can either return values or Promises
const methods: ParentMethods = {
foo: (s, x) => s.length * x,
bar: (x) => Promise.resolve(x * 2),
}
// Start the handshake
// For safety it is strongly adviced to pass the explicit child origin instead of '*'
const messenger = new WindowMessenger({
remoteWindow: childWindow,
remoteOrigin: '*'
});
ParentHandshake(messenger, methods)
.then((connection) => {
const localHandle: LocalHandle<ParentEvents> = connection.localHandle();
const remoteHandle: RemoteHandle<ChildMethods, ChildEvents> = connection.remoteHandle();
// Call a method on the child
remoteHandle.call('baz', 3)
.then((value) => {
console.log(value); // 9
})
// Listen for an event emitted by the child
remoteHandle.addEventListener('some-child-event', (payload) => {
console.log(payload) // 'Hi from child'
});
// Emit an evevent
localHandle.emit('some-parent-event', 'Hi from parent');
})
import { ChildHandshake, WindowMessenger, LocalHandle, RemoteHandle } from 'post-me';
import { ParentMethods, ParentEvents, ChildMethods, ChildEvents} from '/path/to/common';
// Define the methods you want to expose to the other window.
// Methods can either return values or Promises
const methods: ChildMethods = {
baz: (x) => x * 3,
}
// Start the handshake
// For safety it is strongly adviced to pass the explicit parent origin instead of '*'
const messenger = new WindowMessenger({
remoteWindow: window.parent,
remoteOrigin: '*'
});
ChildHandshake(messenger, methods)
.then((connection) => {
const localHandle: LocalHandle<ChildEvents> = connection.localHandle();
const remoteHandle: RemoteHandle<ParentMethods, ParentEvents> = connection.remoteHandle();
// Call a method on the parent
remoteHandle.call('foo', 'ciao', 2)
.then((value) => {
console.log(value); // 8
})
// Listen for an event emitted by the child
remoteHandle.addEventListener('some-parent-event', (payload) => {
console.log(payload) // 'Hi from parent'
});
// Emit an evevent
localHandle.emit('some-child-event', 'Hi from child');
})
A minimal example of using post-me with a web worker can be found in the demo source code.
import { ParentHandshake, WorkerMessenger } from 'post-me';
// Create a dedicated web worker.
const worker = new Worker('./worker.js');
// Start the handshake
const messenger = new WorkerMessenger({ worker });
ParentHandshake(messenger).then((connection) => {
const remoteHandle = connection.remoteHandle();
// Call a method on the worker
remoteHandle.call('sum', 3, 4)
.then((value) => {
console.log(value); // 7
});
remoteHandle.call('mul', 3, 4)
.then((value) => {
console.log(value); // 12
});
})
importScripts('./post-me.js');
const PostMe = self['post-me'];
const methods = {
sum: (x, y) => x + y,
mul: (x, y) => x * y,
};
const messenger = new PostMe.WorkerMessenger({ worker: self });
PostMe.ChildHandshake(messenger, methods).then((_connection) => {
console.log('Worker successfully connected');
});
post-me provides facilities to inspect each low level message exchanged between the two ends.
To enable debugging, simply decorate any Messenger
instance with the provided DebugMessenger
decorator.
You can optionally pass to the decorator your own logging function (a glorified console.log
by default), which can be useful to make the output more readable, or to inspect messages in automated tests.
import { ParentHandshake, WindowMessenger, DebugMessenger } from 'post-me';
import debug from 'debug'; // Use the full feature logger from the debug library
// import { debug } from 'post-me'; // Or the lightweight implementation provided
let messenger = new WindowMessenger({
localWindow: window,
remoteWindow: childWindow,
remoteOrigin: '*'
});
// To enable debugging of each message exchange, decorate the messenger with DebugMessenger
const log = debug('post-me:parent'); // optional
messenger = DebugMessenger(messenger, log);
ParentHandshake(messenger).then((connection) => {
// ...
});
The post-me API is loosely inspired by postmate, with several major improvements and fixes to outstanding issues:
FAQs
Use web Workers and other Windows through a simple Promise API
The npm package post-me receives a total of 1,741 weekly downloads. As such, post-me popularity was classified as popular.
We found that post-me demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.