Communicator is an experimental, Node.js package for inter-context messaging in Chromium-based browsers. The library is designed to work in main, isolated, background, and extension page contexts. Persistent data storage is available from any of these contexts via either direct IndexedDB access, or by an asynchronous messaging-based API.
npm install @rshaker/communicator
The CommMgr
singleton automatically detects the current browser context and registers listeners for incoming messages. Usage is identical in each extension context:
import { CommMgr, CommMsg } from "@rshaker/communicator";
import { detectContext } from "@rshaker/context-detect";
console.info(`Startup in context: ${detectContext()}`);
const commMgr = CommMgr.getInstance<CommMsg>();
commMgr.addListener((message) => {
console.log(`Handled message from ${message?.fromContext}`, message);
});
import { CommMgr, CommMsg } from "@rshaker/communicator";
import { detectContext, BrowserContextType } from "@rshaker/context-detect";
const commMgr = CommMgr.getInstance<CommMsg>();
const currentContext = detectContext();
if (currentContext === BrowserContextType.MAIN_WORLD) {
// Report mouse coordinates to the background context
document.addEventListener("mousemove", ({ clientX, clientY }) => {
const msg: CommMsg = {
type: "ping",
id: "pointer-coordinates",
toContext: BrowserContextType.BACKGROUND_WORKER,
payload: { entry: { clientX, clientY, timestamp: Date.now() } },
};
commMgr.sendMessage(msg);
});
} else if (currentContext === BrowserContextType.BACKGROUND_WORKER) {
commMgr.addListener((msg: CommMsg) => {
if (msg.type === "ping" && msg.id === "pointer-coordinates") {
console.log("Pointer coordinates received:", msg.payload.entry);
}
});
}
Suppose you want to allow content scripts or main-world pages to persist data, but only the background worker has direct access to IndexedDB. You can use MessagingProvider
in the main-world, and have the background worker act as a bridge to IndexedDB.
In the background worker:
import { CommMgr, MessagingProvider, IndexedDBProvider } from "@rshaker/communicator";
// Set up the physical IndexedDB provider
const idbStorage = new IndexedDBProvider("communicator", "test");
// Set up the comm manager and messaging provider
const commMgr = CommMgr.getInstance();
const _messagingProvider = new MessagingProvider(commMgr, { idbProvider: idbStorage });
In the main-world (or content script):
import { CommMgr, MessagingProvider } from "@rshaker/communicator";
// Set up the comm manager
const commMgr = CommMgr.getInstance();
// Use MessagingProvider to proxy storage requests to the background worker
const msgStorage = new MessagingProvider(commMgr);
// Add an entry
const id = await msgStorage.add({
data: {
message: "User action recorded",
timestamp: Date.now(),
level: "info",
},
});
// List all entries
const entries = await msgStorage.list();
console.log("All entries:", entries);
// Retrieve the entry by its actual ID
const entry = await msgStorage.get(id);
Communicator is organized into several core modules:
# Clone the repository
git clone https://github.com/rshaker/communicator.git
cd communicator
# Use the correct node version
nvm use
# Install dependencies
npm install
# Generate API documentation
npm run docs
# Build (development library)
npm run build:dev
# Build (web extension)
npm run build:webext
# Run all tests
npm test
Full API documentation (generated with TypeDoc) is available under the docs directory. Auto-generated docs contains class, interface, and type details for all modules.
MIT