UNPKG

@known-as-bmf/store

Version:
217 lines (210 loc) 5.26 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var immer = require('immer'); var hookable = require('@known-as-bmf/hookable'); /** * Map of error messages that can be thrown. * * @internal */ const errors = { invalidStore: 'Provided value is not a valid store instance.', }; /** * Current subscriptions to store changes. * * @internal */ const SUBSCRIPTIONS = Symbol(); /** * Current state of the store. * * @internal */ const STATE = Symbol(); /** * State transformation (middleware). * * @internal */ const SET_STATE = Symbol(); /** * The store class. * * @internal */ class StoreImpl { } /** * Check if a value is a Store instance. * * @param input - The value to check. * * @internal */ function isStore(input) { return input instanceof StoreImpl; } /** * Function to assert that the store is a valid store instance. * * @param store - The store to assert. * * @internal */ function assertStore(store) { if (!isStore(store)) { throw new TypeError(errors.invalidStore); } } /** * Get the current value of a store. * * @param store - The store. * * @internal */ function getState(store) { return store[STATE]; } /** * Set the current value of a store. * * @param store - The store. * @param state - The new state. * * @internal */ function setState(store, state) { store[SET_STATE](state); } /** * Notify all subscribers to the store that the state has changed. * If a selector was provided when subscribing, * only notify if the desired value has changed. * * @param store - The store instance. * @param event - The change event. * * @internal */ function notifySubscribers(store, event) { store[SUBSCRIPTIONS].forEach(([selector, callback]) => { if (selector(event.previous) !== selector(event.current)) { callback(event); } }); } /** * Create a store. * * @param initialState - The initial value of the state. * @param middleware - Middleware to use for this store. You can compose multiple * middlewares with `composeMiddlewares` and `pipeMiddlewares`. * * @public */ function of(initialState, middleware) { const store = Object.defineProperties(Object.create(StoreImpl.prototype), { [SUBSCRIPTIONS]: { value: new Set(), writable: true, }, [STATE]: { value: undefined, writable: true }, }); const setStateHook = hookable.createHookable((s) => { store[STATE] = s; return s; }); Object.defineProperty(store, SET_STATE, { value: setStateHook }); const hooks = { stateDidChange: setStateHook.leave, stateWillChange: setStateHook.enter, transformState: setStateHook.transformInput, }; if (middleware) { middleware(store, hooks); } setState(store, initialState); return store; } /** * Return the current state of a store. * * @param store - The store you want to get the current state from. * * @throws `TypeError` if the store is not a correct `Store` instance. * * @public */ function deref(store) { assertStore(store); return getState(store); } /** * Change the state of a store using a function. * * @param store - The store of which you want to change the state. * @param mutationFn - The function used to compute the value of the future state. * * @throws `TypeError` if the store is not a correct `Store` instance. * * @public */ function swap(store, mutationFn) { assertStore(store); const previous = getState(store); const current = immer.produce(previous, mutationFn); setState(store, current); notifySubscribers(store, { previous, current }); } /** * Change the state of a store with a new one. * * @param store - The store of which you want to change the state. * @param current - The new state. * * @throws `TypeError` if the store is not a correct `Store` instance. * * @public */ function set(store, current) { assertStore(store); const previous = getState(store); setState(store, current); notifySubscribers(store, { previous, current }); } function subscribe(store, callback, selector = (s) => s) { assertStore(store); const subscription = [selector, callback]; store[SUBSCRIPTIONS].add(subscription); return () => { store[SUBSCRIPTIONS].delete(subscription); }; } /** * Compose middlewares from right to left. * * @param middlewares - Middlewares to compose. * * @public */ const pipeMiddlewares = (...middlewares) => (store, hooks) => middlewares.map((m) => m(store, hooks)); /** * Compose middlewares from left to right. * * @param middlewares - Middlewares to compose. * * @public */ const composeMiddlewares = (...middlewares) => { return pipeMiddlewares(...middlewares.reverse()); }; exports.composeMiddlewares = composeMiddlewares; exports.deref = deref; exports.of = of; exports.pipeMiddlewares = pipeMiddlewares; exports.set = set; exports.subscribe = subscribe; exports.swap = swap; //# sourceMappingURL=index.cjs.js.map