npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

spanan

v2.1.0

Published

postMessage made easy

Downloads

2

Readme

Spanan Build Status

Spanan is simple library for building Promise-based remote function invocation. It wraps one way messaging APIs (like window.postMessage, or chrome.runtime.sendMessage) with a Proxy object that returns a promise.

Usage

Given a iframe object:

const iframeWrapper = new Spanan((message) => {
  iframe.contentWindow.postMessage(JSON.stringify(message), '*');
});
const iframeProxy = iframeWrapper.createProxy();
const echoResponse = iframeProxy.echo(1);
echoResponse.then(response => console.log(response));

Spanan will send a message to iframe in a form:

{
  "action": "echo",
  "args": [ 1 ],
  "uuid": <some uuid>
}

To resolve echoPromise, iframeWrapper must handle the incoming message:

iframe.contentWindow.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);
  iframeWrapper.handleMessage({
    uuid: message.uuid,
    response: message.response,
  });
});

Exposing API

Given a object that all own properties are function:

const actions = {
  echo(text) {
    return text;
  },
};

Spanan can automatically respond to upcoming messages that matches the desired shape. By default upcoming messages must have 3 properties: uuid, action and args.

const spanan = new Spanan();
spanan.export(actions, {
  respond(response, request) {
    window.postMessage(JSON.stringify({
      type: 'response',
      uuid: request.uuid,
      response: response,
    }));
  },
});

Now, Spanan need to listen to upcoming messages:

window.addEventListener('message' (ev) => {
  const message = JSON.parse(ev.data);
  spanan.handleMessage(message);
});

Second argument for export is an options object that can configure default Spanan behavior, it has following properties:

  • respond(response, request) - being called for every messsage that was successfully handled
  • filter(request) - called for every message, if returns true, the matching action will be called
  • transform(request) - called for every positively filtered message. It must return an object with action and args properties. action is being used to match the name of the function that should be called, args are the arguments passed to the function.

Handling errors

Similarly to the way successful calls are handles, if a server action handler throws, a message can be send.

spanan.export(actions, {
  respondWithError(error, request) {
    window.postMessage(JSON.stringify({
      type: 'response',
      uuid: request.uuid,
      error: response,
    }));
  },
});

On a client side, the message containg the error is handled in a same way as normal response is, with the difference that response propery is replaced with error property.

window.addEventListener('message' (ev) => {
  const message = JSON.parse(ev.data);
  spanan.handleMessage({
    uuid: message.uuid,
    error: message.error,
  });
});

Note that if the message passed to handleMessage has both response and error properties the response will take precedence.

Requirements

Spanan uses Javascript Proxy API so require will work in Firefox >= 40 or Chromium >= 49.

Reasons behind Spanan

  • postMessage API is one way communication. Spanan makes it two way: a method call, return a value (via promise object).
  • Cross website communication tends to happen between server-server or client-server. Spanan works in client-client environment.

Development

Test runner (testem) is integratrated with development server:

raureif test

Tests

Run tests in CI mode by:

raureif test --ci

Building for distribution

raureif build

Contribution

Before submiting a pull request, please first create an issue describing what and why you would like to change. This workflow will ensure the development direction.

Migration from 1.x to 2.x

In Spanan 1.x exposing an API was done with static method Spanan.export, also messages were handled with Spanan.dispatch. This approach worked well in simple scenariors, but fail in cases of multiple exports or more then one import.

In Spanan 2.x those problematic static methods were replaced with instance methods.

Migration should be fairly easy. Lets consider following Spanan 1.x code sample:

Spanan.export({
  echo(e) {
    return e;
  },
});
window.addEventListener('message', (ev) => {
  const message = JSON.parse(ev.data);
  Spanan.dispatch(message);
});

It can be replaced with this 2.x version:

const api = new Spanan();
api.export({
  echo(e) {
    return e;
  },
});
window.addEventListener('message', (ev) => {
  const message = JSON.parse(ev.data);
  api.handleMessage(message);
});
```[<65;12;29M[<65;12;29M