lib/index.js
/**
 * A Redux store enhancer that connects to a Redux store in a background page
 * and listens for state updates, then updates the enhanced store to match.
 *
 * @param {string} [name] - A name that identifies the connecting component,
 *     useful for debugging.
 * @param {PortFactory<ReduxMessage} [portFactory] - A function that takes a
 *     callback which will be invoked with a new browser.runtime port. The
 *     default value here will suffice in almost all cases.
 *
 * @returns {Function} A Redux store enhancer that conforms to the Redux API,
 *     with the following caveats:
 *
 *     * The reducer passed in to createStore should only be used to narrow the
 *       state if necessary, e.g. a content script only keeping in sync a
 *       smaller part of a large state tree. The reducer will only be called if
 *       the store receives a state tree sync, not for other actions. You
 *       probably only need an identity (`(i) => i`) function as a reducer, for
 *       most purposes.
 *     * The `preloadedState` argument is ignored. Rely on the connected
 *       store's preloaded state instead.
 *     * The returned store slightly non-conforms with the Redux API in that it
 *       adds an extra property, `connected`. This is a `Promise` that resolves
 *       to once the state has received a first sync. The `dispatch`/
 *       `subscribe`/`getState` functions will fail until this resolve has
 *       happened.
 *     * `dispatch` always returns a Promise that resolves or rejects depending
 *       on whether the other end resolved or rejected.
 */
A Redux store enhancer that connects to a Redux store in a background page
and listens for state updates, then updates the enhanced store to match.

@param {string} [name] - A name that identifies the connecting component,
useful for debugging.
@param {PortFactory<ReduxMessage} [portFactory] - A function that takes a
callback which will be invoked with a new browser.runtime port. The
default value here will suffice in almost all cases.

@returns {Function} A Redux store enhancer that conforms to the Redux API,
with the following caveats.

The reducer passed in to createStore should only be used to narrow the
state if necessary, e.g. a content script only keeping in sync a
smaller part of a large state tree. The reducer will only be called if
the store receives a state tree sync, not for other actions. You
probably only need an identity (`(i) => i`) function as a reducer, for
most purposes.
The `preloadedState` argument is ignored. Rely on the connected
store's preloaded state instead.
The returned store slightly non-conforms with the Redux API in that it
adds an extra property, `connected`. This is a `Promise` that resolves
to once the state has received a first sync.
A Redux store enhancer that connects to a Redux store in a background page and listens for state updates then updates the enhanced store to match . @param { string } [ name ] - A name that identifies the connecting component useful for debugging . @param { PortFactory<ReduxMessage } [ portFactory ] - A function that takes a callback which will be invoked with a new browser . runtime port . The default value here will suffice in almost all cases . @returns { Function } A Redux store enhancer that conforms to the Redux API with the following caveats . The reducer passed in to createStore should only be used to narrow the state if necessary e . g . a content script only keeping in sync a smaller part of a large state tree . The reducer will only be called if the store receives a state tree sync not for other actions . You probably only need an identity ( ` ( i ) = > i ` ) function as a reducer for most purposes . The ` preloadedState ` argument is ignored . Rely on the connected store ' s preloaded state instead . The returned store slightly non - conforms with the Redux API in that it adds an extra property ` connected ` . This is a ` Promise ` that resolves to once the state has received a first sync .
function connectedStore(name = "default", portFactory = defaultPortFactory) {
  return cs => (reducer, preloadedState) => {
    let wrappedReducer = (state, action) => {
      return action.type == SYNC_ACTION ? reducer(action.state) : state;
    };
    let store = cs(wrappedReducer, undefined);
    let port = portFactory(name);
    let pendingPromises = {};
    let resolved = false;
    let guardResolve = () => {
      if (!resolved) {
        throw new Error("Store not yet ready");
      }
      return true;
    };
    let dispatch = action => {
      guardResolve();
      let id = randomString();
      return new Promise((resolve, reject) => {
        pendingPromises[id] = [() => {
          delete pendingPromises[id];
          resolve();
        }, () => {
          delete pendingPromises[id];
          reject();
        }];
        port.postMessage({
          id,
          type: "dispatch",
          payload: action
        });
      });
    };
    let getState = () => guardResolve() && store.getState();
    let subscribe = (...args) => guardResolve() && store.subscribe(...args);
    let replaceReducer = () => {
      throw new Error("replaceReducer doesn't exist in connected stores");
    };
    function executor(resolve, reject) {
      let unsubscribe = store.subscribe(() => {
        unsubscribe();
        resolved = true;
        resolve();
      });
      port.onMessage.addListener(message => {
        switch (message.type) {
          case "stateSync":
            store.dispatch({ type: SYNC_ACTION, state: message.payload });
            break;
          case "dispatchResolved":
            if (message.id) {
              pendingPromises[message.id][0]();
            }
            break;
          case "dispatchRejected":
            if (message.id) {
              pendingPromises[message.id][1]();
            }
            break;
          default:
            throw new Error(`Unknown message type: ${message.type}`);
        }
      });
      port.postMessage({ type: "requestStateSync" });
    }
    let newStore = {
      connected: new Promise(executor),
      dispatch,
      getState,
      subscribe,
      replaceReducer
    };
    return newStore;
  };
}
function connectedStore ( name = "default" , portFactory = defaultPortFactory ) { return cs => ( reducer , preloadedState ) => { let wrappedReducer = ( state , action ) => { return action . type == SYNC_ACTION ? reducer ( action . state ) : state ; } ; let store = cs ( wrappedReducer , undefined ) ; let port = portFactory ( name ) ; let pendingPromises = { } ; let resolved = false ; let guardResolve = ( ) => { if ( ! resolved ) { throw new Error ( "Store not yet ready" ) ; } return true ; } ; let dispatch = action => { guardResolve ( ) ; let id = randomString ( ) ; return new Promise ( ( resolve , reject ) => { pendingPromises [ id ] = [ ( ) => { delete pendingPromises [ id ] ; resolve ( ) ; } , ( ) => { delete pendingPromises [ id ] ; reject ( ) ; } ] ; port . postMessage ( { id , type : "dispatch" , payload : action } ) ; } ) ; } ; let getState = ( ) => guardResolve ( ) && store . getState ( ) ; let subscribe = ( ... args ) => guardResolve ( ) && store . subscribe ( ... args ) ; let replaceReducer = ( ) => { throw new Error ( "replaceReducer doesn't exist in connected stores" ) ; } ; function executor ( resolve , reject ) { let unsubscribe = store . subscribe ( ( ) => { unsubscribe ( ) ; resolved = true ; resolve ( ) ; } ) ; port . onMessage . addListener ( message => { switch ( message . type ) { case "stateSync" : store . dispatch ( { type : SYNC_ACTION , state : message . payload } ) ; break ; case "dispatchResolved" : if ( message . id ) { pendingPromises [ message . id ] [ 0 ] ( ) ; } break ; case "dispatchRejected" : if ( message . id ) { pendingPromises [ message . id ] [ 1 ] ( ) ; } break ; default : throw new Error ( ` ${ message . type } ` ) ; } } ) ; port . postMessage ( { type : "requestStateSync" } ) ; } let newStore = { connected : new Promise ( executor ) , dispatch , getState , subscribe , replaceReducer } ; return newStore ; } ; }
A Redux store enhancer that connects to a Redux store in a background page
 and listens for state updates, then updates the enhanced store to match.
A Redux store enhancer that connects to a Redux store in a background page and listens for state updates then updates the enhanced store to match .