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

cross-storage-with-events

v1.1.2

Published

Cross domain local storage

Downloads

3

Readme

Note: This is a fork of original version with merged events functionality. If you need this functionality and this MR is merged, please, consider using original version.

cross-storage

Cross domain local storage, with permissions. Enables multiple browser windows/tabs, across a variety of domains, to share a single localStorage. Features an API using ES6 promises.

Build Status

Overview

The library is a convenient alternative to sharing a root domain cookie. Unlike cookies, your client-side data isn't limited to a few kilobytes - you get up to 2.49M chars. For a client-heavy application, you can potentially shave a few KB off your request headers by avoiding cookies. This is all thanks to LocalStorage, which is available in IE 8+, FF 3.5+, Chrome 4+, as well as a majority of mobile browsers. For a list of compatible browsers, refer to caniuse.

How does it work? The library is divided into two types of components: hubs and clients. The hubs reside on a host of choice and interact directly with the LocalStorage API. The clients then load said hub over an embedded iframe and post messages, requesting data to be stored, retrieved, and deleted. This allows multiple clients to access and share the data located in a single store.

Care should be made to limit the origins of the bidirectional communication. As such, when initializing the hub, an array of permissions objects is passed. Any messages from clients whose origin does not match the pattern are ignored, as well as those not within the allowed set of methods. The set of permissions are enforced thanks to the same-origin policy. However, keep in mind that any user has full control of their local storage data - it's still client data. This only restricts access on a per-domain or web app level.

Hub

// Config s.t. subdomains can get, but only the root domain can set and del
CrossStorageHub.init([
  {origin: /\.example.com$/,            allow: ['get']},
  {origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);

Note the $ for matching the end of the string. The RegExps in the above example will match origins such as valid.example.com, but not invalid.example.com.malicious.com.

Client

var storage = new CrossStorageClient('https://store.example.com/hub.html');

storage.onConnect().then(function() {
  return storage.set('newKey', 'foobar');
}).then(function() {
  return storage.get('existingKey', 'newKey');
}).then(function(res) {
  console.log(res.length); // 2
}).catch(function(err) {
  // Handle error
});

Installation

The library can be installed via bower:

bower install cross-storage

Or using npm:

npm install cross-storage

along with browserify:

var CrossStorageClient = require('cross-storage').CrossStorageClient;
var CrossStorageHub    = require('cross-storage').CrossStorageHub;

When serving the hub, you may want to set the CORS and CSP headers for your server depending on client/hub location. For example:

{
  'Access-Control-Allow-Origin':  '*',
  'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
  'Access-Control-Allow-Headers': 'X-Requested-With',
  'Content-Security-Policy':      "default-src 'unsafe-inline' *",
  'X-Content-Security-Policy':    "default-src 'unsafe-inline' *",
  'X-WebKit-CSP':                 "default-src 'unsafe-inline' *",
}

If using inline JS to create the hub, you'll need to specify unsafe-inline for the CSP headers. Otherwise, it can be left out if simply including the init code via another resource.

API

CrossStorageHub.init(permissions)

Accepts an array of objects with two keys: origin and allow. The value of origin is expected to be a RegExp, and allow, an array of strings. The cross storage hub is then initialized to accept requests from any of the matching origins, allowing access to the associated lists of methods. Methods may include any of: get, set, del, getKeys, clear and listen. A 'ready' message is sent to the parent window once complete.

CrossStorageHub.init([
  {origin: /localhost:3000$/, allow: ['get', 'set', 'del', 'getKeys', 'clear', 'listen']}
]);

new CrossStorageClient(url, [opts])

Constructs a new cross storage client given the url to a hub. By default, an iframe is created within the document body that points to the url. It also accepts an options object, which may include a timeout, frameId, and promise. The timeout, in milliseconds, is applied to each request and defaults to 5000ms. The options object may also include a frameId, identifying an existing frame on which to install its listeners. If the promise key is supplied the constructor for a Promise, that Promise library will be used instead of the default window.Promise.

var storage = new CrossStorageClient('http://localhost:3000/hub.html');

var storage = new CrossStorageClient('http://localhost:3000/hub.html', {
  timeout: 5000,
  frameId: 'storageFrame'
});

CrossStorageClient.prototype.onConnect()

Returns a promise that is fulfilled when a connection has been established with the cross storage hub. Its use is required to avoid sending any requests prior to initialization being complete.

storage.onConnect().then(function() {
  // ready!
});

CrossStorageClient.prototype.set(key, value)

Sets a key to the specified value. Returns a promise that is fulfilled on success, or rejected if any errors setting the key occurred, or the request timed out.

storage.onConnect().then(function() {
  return storage.set('key', JSON.stringify({foo: 'bar'}));
});

CrossStorageClient.prototype.get(key1, [key2], [...])

Accepts one or more keys for which to retrieve their values. Returns a promise that is settled on hub response or timeout. On success, it is fulfilled with the value of the key if only passed a single argument. Otherwise it's resolved with an array of values. On failure, it is rejected with the corresponding error message.

storage.onConnect().then(function() {
  return storage.get('key1');
}).then(function(res) {
  return storage.get('key1', 'key2', 'key3');
}).then(function(res) {
  // ...
});

CrossStorageClient.prototype.del(key1, [key2], [...])

Accepts one or more keys for deletion. Returns a promise that is settled on hub response or timeout.

storage.onConnect().then(function() {
  return storage.del('key1', 'key2');
});

CrossStorageClient.prototype.getKeys()

Returns a promise that, when resolved, passes an array of keys currently in storage.

storage.onConnect().then(function() {
  return storage.getKeys();
}).then(function(keys) {
  // ['key1', 'key2', ...]
});

CrossStorageClient.prototype.clear()

Returns a promise that, when resolved, indicates that all localStorage data has been cleared.

storage.onConnect().then(function() {
  return storage.clear();
});

CrossStorageClient.prototype.listen(fn)

Adds an event listener to the storage event in the hub. The callback will be invoked on any storage event not originating from that client. The callback will be invoked with an object containing the following keys taken from the original event: key, newValue, oldValue and url. Returns a promise that resolves to a listener id that can be used to unregister the listener.

storage.onConnect().then(function() {
  return storage.listen(function(event) {
    console.log(event);
  });
}).then(function(id) {
  // id can be passed to storage.unlisten
});

CrossStorageClient.prototype.unlisten(id)

Removes the registered listener with the supplied id. Returns a promise that resolves on completion.

storage.unlisten(id);

CrossStorageClient.prototype.close()

Deletes the iframe and sets the connected state to false. The client can no longer be used after being invoked.

storage.onConnect().then(function() {
  return storage.set('key1', 'key2');
}).catch(function(err) {
  // Handle error
}).then(function() {
  storage.close();
});

Compatibility

For compatibility with older browsers, simply load a Promise polyfill such as es6-promise.

You can also use RSVP or any other ES6 compliant promise library. Supports IE8 and up using the above polyfill. A JSON polyfill is also required for IE8 in Compatibility View. Also note that catch is a reserved word in IE8, and so error handling with promises can be done as:

storage.onConnect().then(function() {
  return storage.get('key1');
}).then(function(res) {
  // ... on success
})['catch'](function(err) {
  // ... on error
});

Breaking Changes

API breaking changes were introduced in both 0.6 and 1.0. Refer to releases for details.

Notes on Safari 7+ (OSX, iOS)

All cross-domain local storage access is disabled by default with Safari 7+. This is a result of the "Block cookies and other website data" privacy setting being set to "From third parties and advertisers". Any cross-storage client code will not crash, however, it will only have access to a sandboxed, isolated local storage instance. As such, none of the data previously set by other origins will be accessible. If an option, one could fall back to using root cookies for those user agents, or requesting the data from a server-side store.

Compression

Most localStorage-compatible browsers offer at least ~5Mb of storage. But keys and values are defined as DOMStrings, which are UTF-8 encoded using single 16-bit sequences. That means a string of ~2.5 million ASCII characters will use up ~5Mb, since they're 2 bytes per char.

If you need to maximize your storage space, consider using lz-string. For smaller strings, it's not uncommon to see a 50% reduction in size when compressed, which will bring you a lot closer to 5 million characters. At that point, you're only limited by the average compression rate of your strings.

Building

The minified, production JavaScript can be generated with gulp by running gulp dist. If not already on your system, gulp can be installed using npm install -g gulp

Tests

Tests can be ran locally using npm test. Tests are ran using Zuul, and the Travis CI build uses Sauce Labs for multi-browser testing as well.

Copyright and license

Copyright 2016 Zendesk

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.