@vodori/chatter
TypeScript icon, indicating that this package has built-in type declarations

0.0.31 • Public • Published

npm version Build Status MIT Licence

A network socket implementation for communicating across various browser contexts (nested iframes, extension background script, extension content script). Higher level abstractions like push, request/reply, and long-lived topic subscriptions are implemented on top of unidirectional messages that automatically propagate to all reachable contexts.


Why

We're maintaining a pretty sophisticated chrome extension. Turns out, this involves sending a lot of messages across a lot of contexts. We wanted a unified way to route messages from wherever we were to wherever they needed to go. Gossip protocol proved to be a great fit since we don't need to adjust the implementation depending on the context topology.


Install

npm install @vodori/chatter --save

Usage

The way you communicate stays the same regardless of the context you're in. Just name each location and start sending and/or handling messages. As an example:

From a chrome-extension background script.

import {Socket} from "@vodori/chatter";

const socket = new Socket("BACKGROUND");

socket.request("CONTENT_SCRIPT", "domNodeCount", {}).subscribe(response => {
    console.log(`The dom currently has ${response} nodes.`);
});

socket.subscription("MY_IFRAME", "serverPings", {url: "https://example.com/healthz"}).subscribe(response => {
    console.log(`The status code of example.com/healthz is ${response}`);
});

socket.handlePushes("SAY_HELLO", message => {
    console.log("Someone said hello to the background script!");
});

From a chrome-extension content script.

import {Socket} from "@vodori/chatter";
import {of} from "rxjs";

const socket = new Socket("CONTENT_SCRIPT");

socket.handleRequests("domNodeCount", message => {
   return of(Array.from(document.getElementsByTagName("*")).length);
});

From an iframe inside an iframe injected by a content script.

import {Socket} from "@vodori/chatter";
import {interval, map, switchMap, fromPromise} from "rxjs";

const socket = new Socket("MY_IFRAME");

socket.handleSubscriptions("serverPings", message => {
    return interval(5000).pipe(switchMap(_ => {
        return fromPromise(fetch(message.url, {method: 'get'})).pipe(map(response => {
            return response.status;
        }));
    }));
});

socket.push("BACKGROUND", "SAY_HELLO");

Security

Note that there are security concerns when sending messages between contexts in the browser. You don't want code listening in an untrusted frame to intercept traffic only intended for your application. You should define an originVerifier at each node to constrain the inbound and outbound messages.

import {Socket, socketSettings} from "@vodori/chatter";

function gossipSettings(): socketSettings {
    return {
        trustedOrigins: new Set(["https://example.com"])
    }
}

// now this node will only accept/send messages from example.com or other
// components of the same chrome extension
const backgroundNode = new Socket("BACKGROUND", gossipSettings());

FAQ

Q: Do I have to be building a chrome extension to use this?

A: No. It's useful when you're dealing with iframes too.

Q: Do I have to have iframes to use this?

A: No. You can create two nodes in the same frame if you want.


License

This project is licensed under MIT license.

Dependencies (2)

Dev Dependencies (6)

Package Sidebar

Install

npm i @vodori/chatter

Weekly Downloads

77

Version

0.0.31

License

MIT

Unpacked Size

49 kB

Total Files

13

Last publish

Collaborators

  • grantgochnauer
  • travisstom