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

hookstate-plugin-web-extension

v2.0.0-rc.1

Published

Sync and persist state between background page and content_script using browser.storage

Downloads

9

Readme

hookstate-plugin-web-extension

Install

npm i hookstate-plugin-web-extension
# or
yarn add hookstate-plugin-web-extension

Features

  • Compatible with chrome.storage.* and browser.storage.*
  • State synced between background page and all content scripts through browser/chrome.storage
  • Configurable persistence. Choose which keys of the storage you want to load at init
  • Initial synchronous state. It will be erased upon init if persisted data is found in storage
  • Use an internal __state_update key upon each storage update to pass metadata between processes. This allows processes receiving the changes to apply only necessary updates on part of their tree to avoid unnecessary rerendering. See details below
  • TODO: Migrations between state versions

Usage

Background page

import { BrowserExtensionStorage } from 'hookstate-plugin-web-extension';

import { GlobalState } from './mystate/global/types';
import { initialState } from './mystate/global/initial';

const state = createState<GlobalState>(initialState);

state.attach(
  BrowserExtensionStorage({
    // either browser.storage or chrome.storage
    storage: browser.storage,
    // either 'local' or 'sync'
    areaName: 'local',
    // id of the plugin instance, must be different for each process
    id: 'background',
    // your initial state. All keys represented in your GlobalState type MUST be present in this one
    initialState: initialState,
    // true for background page, false for all others
    leader: true,
    // which persisted keys of GlobalState do you want to reload upon start?
    persistedKeys: [],
    // what is the version of your state. This needs to be updated if you need to run a migration script (TODO)
    version: 1,
    // called whenever the plugin encounters an Error
    onError: notify,
  }),
);

Other processes (content scripts, popups)

import { BrowserExtensionStorage } from 'hookstate-plugin-web-extension';

// same type and initial state as for the background page
import { GlobalState } from './mystate/global/types';
import { initialState } from './mystate/global/initial';

const state = createState<GlobalState>(initialState);

state.attach(
  BrowserExtensionStorage({
    storage: browser.storage,
    areaName: 'local',
    // MUST be different for each process
    id: 'someRandomId',
    initialState: initialState,
    leader: false,
    onError: notify,
  }),
);

Forward partial updates of the tree through browser storage

The Storage API only allows us to update root values. Here is an example:

browser.storage.local.set({
  a: 'a1',
  b: {
    c: 1,
    d: 2,
  }
})

If I want to update b.c, with this API, I must set the whole b tree:

browser.storage.local.set({
  b: {
    c: 2,
    d: 2,
  }
})

browser.storage.onChanged.addListener(changes => {
  // here we will have the whole `changes.b` tree, so the simple solution would be:
  hookstateState.b.set(changes.b.newValue)
});

And this is not good, because it means that we get a new b.d object, and it will be considerer as updated by hookstate. In this case it is not particularly a problem, by imagine a list with hundreds of items updated whenever you update one, you'd loose all the optimisations of hookstate.

In this extension we are smarter than this. As a matter of fact we can abuse the storage API to use a custom key (__state_update) upon each update of the state, which value contains necessary metadata to only apply necessary changes onto the target states.

Upon changes, this __state_update is computed and sent along the real state update through the storage.set method. When other processes receive those changes, they know that:

  • If this key is present, it means the update comes from this plugin
  • We can use this computed metadata to apply more precise state.merge or state.set on the state, so that it reflects what has been done in the originating process.

License

MIT