Source: store/modules/store.js

/**
 * @summary store
 * @version 3.0.0
 * @since 3.0.0
 * @author Arian Khosravi <arian.khosravi@aofl.com>
 */
import {RegisterCallback} from '@aofl/register-callback';
import {deepFreeze} from '@aofl/object-utils';

const TICK = Symbol('tick');
const MICRO_TASK = Symbol('microtask');

/**
 * Simple yet powerful implementation of flux type data store.
 *
 * @memberof module:@aofl/store
 */
class Store {
  /**
   * Creates an instance of Store.
   * @param {Boolean} debug
   */
  constructor(debug = false) {
    this.debug = debug;
    this.state = {};
    this.namespaces = {};
    this.registerCallback = new RegisterCallback();
    this[TICK] = null;

    this[MICRO_TASK] = () => {
      this.registerCallback.next();
      this[TICK] = null;
    };

    if (debug === true || /* istanbul ignore next */typeof window.aoflDevtools !== 'undefined') {
      this.debug = true;
      this.state = deepFreeze(this.state);
      /* istanbul ignore next */
      if (typeof window.aoflDevtools === 'undefined') {
        window.aoflDevtools = {};
      }
      /* istanbul ignore next */
      if (!Array.isArray(window.aoflDevtools.storeInstances)) {
        window
          .aoflDevtools
          .storeInstances = [];
      }
      window.aoflDevtools.storeInstances.push(this);
    }
  }
  /**
   * subscribe() register the callback function with registerCallback and returns
   * the unsubscribe function.
   *
   * @param {Furtion} callback
   * @return {Function}
   */
  subscribe(callback) {
    return this.registerCallback.register(callback);
  }
  /**
   * getState() return the current state.
   *
   * @return {Object}
   */
  getState() {
    return this.state;
  }
  /**
   *
   * @param {SDO} sdo
   */
  addState(sdo) {
    /* istanbul ignore next */
    if (typeof this.namespaces[sdo.namespace] !== 'undefined') {
      throw new Error(`${this.constructor.name}: Cannot redefine existing namespace ${sdo.namespace}`);
    }

    this.namespaces[sdo.namespace] = sdo;
    this.commit(sdo.namespace, sdo.initialState || sdo.constructor.initialState);
  }
  /**
   * Copies staged changes to state and notifies subscribers
   */
  commit(namespace, subState) {
    const state = Object.assign({}, this.state, {
      [namespace]: subState
    });
    this.replaceState(state);
  }
  /**
   * replaceState() take a state object, replaces the state property and notifies subscribers.
   *
   * @param {Object} state
   */
  replaceState(state) {
    this.state = state;
    if (this.debug) {
      this.state = deepFreeze(this.state);
    }
    this.dispatch();
  }

  /**
   * Resets the state to the initial state of Sdos.
   */
  flushState() {
    const state = {};
    for (const key in this.namespaces) {
      /* istanbul ignore next */
      if (!Object.hasOwnProperty.call(this.namespaces, key)) continue;
      const sdo = this.namespaces[key];
      state[sdo.namespace] = Object.assign({}, sdo.constructor.initialState);
    }
    this.replaceState(state);
  }
  /**
   * Batches all calls to dispatch and notifies subscribers on next tick.
   */
  dispatch() {
    if (this[TICK]) {
      return;
    }
    this[TICK] = setTimeout(this[MICRO_TASK]);
  }
}

export {
  Store
};