UNPKG

recoil

Version:

Recoil - A state management library for React

1,948 lines (1,556 loc) 227 kB
import reactNative from 'react-native'; import react from 'react'; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ // Split declaration and implementation to allow this function to pretend to // check for actual instance of Promise instead of something with a `then` // method. // eslint-disable-next-line no-redeclare function isPromise(p) { return !!p && typeof p.then === 'function'; } var Recoil_isPromise = isPromise; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ function nullthrows(x, message) { if (x != null) { return x; } throw new Error(message !== null && message !== void 0 ? message : 'Got unexpected null or undefined'); } var Recoil_nullthrows = nullthrows; // TODO Convert Loadable to a Class to allow for runtime type detection. // Containing static factories of withValue(), withError(), withPromise(), and all() class Canceled {} const CANCELED = new Canceled(); const loadableAccessors = { valueMaybe() { return undefined; }, valueOrThrow() { const error = new Error(`Loadable expected value, but in "${this.state}" state`); // V8 keeps closures alive until stack is accessed, this prevents a memory leak throw error; }, errorMaybe() { return undefined; }, errorOrThrow() { const error = new Error(`Loadable expected error, but in "${this.state}" state`); // V8 keeps closures alive until stack is accessed, this prevents a memory leak throw error; }, promiseMaybe() { return undefined; }, promiseOrThrow() { const error = new Error(`Loadable expected promise, but in "${this.state}" state`); // V8 keeps closures alive until stack is accessed, this prevents a memory leak throw error; }, is(other) { return other.state === this.state && other.contents === this.contents; }, // TODO Unit tests // TODO Convert Loadable to a Class to better support chaining // by returning a Loadable from a map function map(map) { if (this.state === 'hasError') { return this; } if (this.state === 'hasValue') { try { const next = map(this.contents); // TODO if next instanceof Loadable, then return next return Recoil_isPromise(next) ? loadableWithPromise(next) : loadableWithValue(next); } catch (e) { return Recoil_isPromise(e) ? // If we "suspended", then try again. // errors and subsequent retries will be handled in 'loading' case loadableWithPromise(e.next(() => map(this.contents))) : loadableWithError(e); } } if (this.state === 'loading') { return loadableWithPromise(this.contents // TODO if map returns a loadable, then return the value or promise or throw the error .then(map).catch(e => { if (Recoil_isPromise(e)) { // we were "suspended," try again return e.then(() => map(this.contents)); } throw e; })); } const error = new Error('Invalid Loadable state'); // V8 keeps closures alive until stack is accessed, this prevents a memory leak throw error; } }; function loadableWithValue(value) { // Build objects this way since Flow doesn't support disjoint unions for class properties return Object.freeze({ state: 'hasValue', contents: value, ...loadableAccessors, getValue() { return this.contents; }, toPromise() { return Promise.resolve(this.contents); }, valueMaybe() { return this.contents; }, valueOrThrow() { return this.contents; } }); } function loadableWithError(error) { return Object.freeze({ state: 'hasError', contents: error, ...loadableAccessors, getValue() { throw this.contents; }, toPromise() { return Promise.reject(this.contents); }, errorMaybe() { return this.contents; }, errorOrThrow() { return this.contents; } }); } function loadableWithPromise(promise) { return Object.freeze({ state: 'loading', contents: promise, ...loadableAccessors, getValue() { throw this.contents.then(({ __value }) => __value); }, toPromise() { return this.contents.then(({ __value }) => __value); }, promiseMaybe() { return this.contents.then(({ __value }) => __value); }, promiseOrThrow() { return this.contents.then(({ __value }) => __value); } }); } function loadableLoading() { return loadableWithPromise(new Promise(() => {})); } function loadableAll(inputs) { return inputs.every(i => i.state === 'hasValue') ? loadableWithValue(inputs.map(i => i.contents)) : inputs.some(i => i.state === 'hasError') ? loadableWithError(Recoil_nullthrows(inputs.find(i => i.state === 'hasError'), 'Invalid loadable passed to loadableAll').contents) : loadableWithPromise(Promise.all(inputs.map(i => i.contents)).then(value => ({ __value: value }))); } var Recoil_Loadable = { loadableWithValue, loadableWithError, loadableWithPromise, loadableLoading, loadableAll, Canceled, CANCELED }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ function recoverableViolation(message, projectName, { error } = {}) { if (process.env.NODE_ENV !== "production") { console.error(message, error); } return null; } var recoverableViolation_1 = recoverableViolation; // @oss-only var Recoil_recoverableViolation = recoverableViolation_1; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * Utilities for working with built-in Maps and Sets without mutating them. * * @emails oncall+recoil * * @format */ function setByAddingToSet(set, v) { const next = new Set(set); next.add(v); return next; } function setByDeletingFromSet(set, v) { const next = new Set(set); next.delete(v); return next; } function mapBySettingInMap(map, k, v) { const next = new Map(map); next.set(k, v); return next; } function mapByUpdatingInMap(map, k, updater) { const next = new Map(map); next.set(k, updater(next.get(k))); return next; } function mapByDeletingFromMap(map, k) { const next = new Map(map); next.delete(k); return next; } function mapByDeletingMultipleFromMap(map, ks) { const next = new Map(map); ks.forEach(k => next.delete(k)); return next; } var Recoil_CopyOnWrite = { setByAddingToSet, setByDeletingFromSet, mapBySettingInMap, mapByUpdatingInMap, mapByDeletingFromMap, mapByDeletingMultipleFromMap }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ /** * Creates a new iterable whose output is generated by passing the input * iterable's values through the filter function. */ function* filterIterable(iterable, predicate) { // Use generator to create iterable/iterator let index = 0; for (const value of iterable) { if (predicate(value, index++)) { yield value; } } } var Recoil_filterIterable = filterIterable; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ const gks = new Map().set('recoil_hamt_2020', true).set('recoil_memory_managament_2020', true); function Recoil_gkx(gk) { var _gks$get; return (_gks$get = gks.get(gk)) !== null && _gks$get !== void 0 ? _gks$get : false; } Recoil_gkx.setPass = gk => { gks.set(gk, true); }; Recoil_gkx.setFail = gk => { gks.set(gk, false); }; var Recoil_gkx_1 = Recoil_gkx; // @oss-only /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ /** * Creates a new iterable whose output is generated by passing the input * iterable's values through the mapper function. */ function mapIterable(iterable, callback) { // Use generator to create iterable/iterator return function* () { let index = 0; for (const value of iterable) { yield callback(value, index++); } }(); } var Recoil_mapIterable = mapIterable; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ function sprintf(format, ...args) { let index = 0; return format.replace(/%s/g, () => String(args[index++])); } var sprintf_1 = sprintf; function expectationViolation(format, ...args) { if (process.env.NODE_ENV !== "production") { const message = sprintf_1.call(null, format, ...args); const error = new Error(message); error.name = 'Expectation Violation'; console.error(error); } } var expectationViolation_1 = expectationViolation; // @oss-only var Recoil_expectationViolation = expectationViolation_1; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ // eslint-disable-next-line no-unused-vars class AbstractRecoilValue { constructor(newKey) { _defineProperty(this, "key", void 0); this.key = newKey; } } class RecoilState extends AbstractRecoilValue {} class RecoilValueReadOnly extends AbstractRecoilValue {} function isRecoilValue(x) { return x instanceof RecoilState || x instanceof RecoilValueReadOnly; } var Recoil_RecoilValue = { AbstractRecoilValue, RecoilState, RecoilValueReadOnly, isRecoilValue }; var Recoil_RecoilValue_1 = Recoil_RecoilValue.AbstractRecoilValue; var Recoil_RecoilValue_2 = Recoil_RecoilValue.RecoilState; var Recoil_RecoilValue_3 = Recoil_RecoilValue.RecoilValueReadOnly; var Recoil_RecoilValue_4 = Recoil_RecoilValue.isRecoilValue; var Recoil_RecoilValue$1 = /*#__PURE__*/Object.freeze({ __proto__: null, AbstractRecoilValue: Recoil_RecoilValue_1, RecoilState: Recoil_RecoilValue_2, RecoilValueReadOnly: Recoil_RecoilValue_3, isRecoilValue: Recoil_RecoilValue_4 }); class DefaultValue {} const DEFAULT_VALUE = new DefaultValue(); class RecoilValueNotReady extends Error { constructor(key) { super(`Tried to set the value of Recoil selector ${key} using an updater function, but it is an async selector in a pending or error state; this is not supported.`); } } // flowlint-next-line unclear-type:off const nodes = new Map(); // flowlint-next-line unclear-type:off const recoilValues = new Map(); /* eslint-disable no-redeclare */ function recoilValuesForKeys(keys) { return Recoil_mapIterable(keys, key => Recoil_nullthrows(recoilValues.get(key))); } function registerNode(node) { if (nodes.has(node.key)) { const message = `Duplicate atom key "${node.key}". This is a FATAL ERROR in production. But it is safe to ignore this warning if it occurred because of hot module replacement.`; // TODO Need to figure out if there is a standard/open-source equivalent to see if hot module replacement is happening: // prettier-ignore // @fb-only: if (__DEV__) { // @fb-only: const isAcceptingUpdate = require('__debug').isAcceptingUpdate; // prettier-ignore // @fb-only: if (typeof isAcceptingUpdate !== 'function' || !isAcceptingUpdate()) { // @fb-only: expectationViolation(message, 'recoil'); // @fb-only: } // prettier-ignore // @fb-only: } else { // @fb-only: recoverableViolation(message, 'recoil'); // @fb-only: } console.warn(message); // @oss-only } nodes.set(node.key, node); const recoilValue = node.set == null ? new Recoil_RecoilValue$1.RecoilValueReadOnly(node.key) : new Recoil_RecoilValue$1.RecoilState(node.key); recoilValues.set(node.key, recoilValue); return recoilValue; } /* eslint-enable no-redeclare */ class NodeMissingError extends Error {} // flowlint-next-line unclear-type:off function getNode(key) { const node = nodes.get(key); if (node == null) { throw new NodeMissingError(`Missing definition for RecoilValue: "${key}""`); } return node; } // flowlint-next-line unclear-type:off function getNodeMaybe(key) { return nodes.get(key); } const configDeletionHandlers = new Map(); function deleteNodeConfigIfPossible(key) { var _node$shouldDeleteCon; if (!Recoil_gkx_1('recoil_memory_managament_2020')) { return; } const node = nodes.get(key); if (node === null || node === void 0 ? void 0 : (_node$shouldDeleteCon = node.shouldDeleteConfigOnRelease) === null || _node$shouldDeleteCon === void 0 ? void 0 : _node$shouldDeleteCon.call(node)) { var _getConfigDeletionHan; nodes.delete(key); (_getConfigDeletionHan = getConfigDeletionHandler(key)) === null || _getConfigDeletionHan === void 0 ? void 0 : _getConfigDeletionHan(); configDeletionHandlers.delete(key); } } function setConfigDeletionHandler(key, fn) { if (!Recoil_gkx_1('recoil_memory_managament_2020')) { return; } if (fn === undefined) { configDeletionHandlers.delete(key); } else { configDeletionHandlers.set(key, fn); } } function getConfigDeletionHandler(key) { return configDeletionHandlers.get(key); } var Recoil_Node = { nodes, recoilValues, registerNode, getNode, getNodeMaybe, deleteNodeConfigIfPossible, setConfigDeletionHandler, getConfigDeletionHandler, recoilValuesForKeys, NodeMissingError, DefaultValue, DEFAULT_VALUE, RecoilValueNotReady }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ class RetentionZone {} function retentionZone() { return new RetentionZone(); } var Recoil_RetentionZone = { RetentionZone, retentionZone }; const { setByAddingToSet: setByAddingToSet$1 } = Recoil_CopyOnWrite; const { getNode: getNode$1, getNodeMaybe: getNodeMaybe$1, recoilValuesForKeys: recoilValuesForKeys$1 } = Recoil_Node; const { RetentionZone: RetentionZone$1 } = Recoil_RetentionZone; // flowlint-next-line unclear-type:off const emptySet = Object.freeze(new Set()); class ReadOnlyRecoilValueError extends Error {} function initializeRetentionForNode(store, nodeKey, retainedBy) { if (!Recoil_gkx_1('recoil_memory_managament_2020')) { return () => undefined; } const { nodesRetainedByZone } = store.getState().retention; function addToZone(zone) { let set = nodesRetainedByZone.get(zone); if (!set) { nodesRetainedByZone.set(zone, set = new Set()); } set.add(nodeKey); } if (retainedBy instanceof RetentionZone$1) { addToZone(retainedBy); } else if (Array.isArray(retainedBy)) { for (const zone of retainedBy) { addToZone(zone); } } return () => { if (!Recoil_gkx_1('recoil_memory_managament_2020')) { return; } const nodesRetainedByZone = store.getState().retention.nodesRetainedByZone; function deleteFromZone(zone) { const set = nodesRetainedByZone.get(zone); if (set) { set.delete(nodeKey); } if (set && set.size === 0) { nodesRetainedByZone.delete(zone); } } if (retainedBy instanceof RetentionZone$1) { deleteFromZone(retainedBy); } else if (Array.isArray(retainedBy)) { for (const zone of retainedBy) { deleteFromZone(zone); } } }; } function initializeNodeIfNewToStore(store, treeState, key, trigger) { const storeState = store.getState(); if (storeState.nodeCleanupFunctions.has(key)) { return; } const config = getNode$1(key); const retentionCleanup = initializeRetentionForNode(store, key, config.retainedBy); const nodeCleanup = config.init(store, treeState, trigger); storeState.nodeCleanupFunctions.set(key, () => { nodeCleanup(); retentionCleanup(); }); } function cleanUpNode(store, key) { var _state$nodeCleanupFun; const state = store.getState(); (_state$nodeCleanupFun = state.nodeCleanupFunctions.get(key)) === null || _state$nodeCleanupFun === void 0 ? void 0 : _state$nodeCleanupFun(); state.nodeCleanupFunctions.delete(key); } // Get the current value loadable of a node and update the state. // Update dependencies and subscriptions for selectors. // Update saved value validation for atoms. function getNodeLoadable(store, state, key) { initializeNodeIfNewToStore(store, state, key, 'get'); return getNode$1(key).get(store, state); } // Peek at the current value loadable for a node without any evaluation or state change function peekNodeLoadable(store, state, key) { return getNode$1(key).peek(store, state); } // Write value directly to state bypassing the Node interface as the node // definitions may not have been loaded yet when processing the initial snapshot. function setUnvalidatedAtomValue_DEPRECATED(state, key, newValue) { var _node$invalidate; const node = getNodeMaybe$1(key); node === null || node === void 0 ? void 0 : (_node$invalidate = node.invalidate) === null || _node$invalidate === void 0 ? void 0 : _node$invalidate.call(node, state); return { ...state, atomValues: state.atomValues.clone().delete(key), nonvalidatedAtoms: state.nonvalidatedAtoms.clone().set(key, newValue), dirtyAtoms: setByAddingToSet$1(state.dirtyAtoms, key) }; } // Return the discovered dependencies and values to be written by setting // a node value. (Multiple values may be written due to selectors getting to // set upstreams; deps may be discovered because of reads in updater functions.) function setNodeValue(store, state, key, newValue) { const node = getNode$1(key); if (node.set == null) { throw new ReadOnlyRecoilValueError(`Attempt to set read-only RecoilValue: ${key}`); } const set = node.set; // so flow doesn't lose the above refinement. initializeNodeIfNewToStore(store, state, key, 'set'); return set(store, state, newValue); } function peekNodeInfo(store, state, key) { var _graph$nodeDeps$get, _storeState$nodeToCom, _storeState$nodeToCom2; const storeState = store.getState(); const graph = store.getGraph(state.version); const type = storeState.knownAtoms.has(key) ? 'atom' : storeState.knownSelectors.has(key) ? 'selector' : undefined; const downstreamNodes = Recoil_filterIterable(getDownstreamNodes(store, state, new Set([key])), nodeKey => nodeKey !== key); return { loadable: peekNodeLoadable(store, state, key), isActive: storeState.knownAtoms.has(key) || storeState.knownSelectors.has(key), isSet: type === 'selector' ? false : state.atomValues.has(key), isModified: state.dirtyAtoms.has(key), type, // Report current dependencies. If the node hasn't been evaluated, then // dependencies may be missing based on the current state. deps: recoilValuesForKeys$1((_graph$nodeDeps$get = graph.nodeDeps.get(key)) !== null && _graph$nodeDeps$get !== void 0 ? _graph$nodeDeps$get : []), // Reportsall "current" subscribers. Evaluating other nodes or // previous in-progress async evaluations may introduce new subscribers. subscribers: { nodes: recoilValuesForKeys$1(downstreamNodes), components: Recoil_mapIterable((_storeState$nodeToCom = (_storeState$nodeToCom2 = storeState.nodeToComponentSubscriptions.get(key)) === null || _storeState$nodeToCom2 === void 0 ? void 0 : _storeState$nodeToCom2.values()) !== null && _storeState$nodeToCom !== void 0 ? _storeState$nodeToCom : [], ([name]) => ({ name })) } }; } // Find all of the recursively dependent nodes function getDownstreamNodes(store, state, keys) { const visitedNodes = new Set(); const visitingNodes = Array.from(keys); const graph = store.getGraph(state.version); for (let key = visitingNodes.pop(); key; key = visitingNodes.pop()) { var _graph$nodeToNodeSubs; visitedNodes.add(key); const subscribedNodes = (_graph$nodeToNodeSubs = graph.nodeToNodeSubscriptions.get(key)) !== null && _graph$nodeToNodeSubs !== void 0 ? _graph$nodeToNodeSubs : emptySet; for (const downstreamNode of subscribedNodes) { if (!visitedNodes.has(downstreamNode)) { visitingNodes.push(downstreamNode); } } } return visitedNodes; } var Recoil_FunctionalCore = { getNodeLoadable, peekNodeLoadable, setNodeValue, cleanUpNode, setUnvalidatedAtomValue_DEPRECATED, peekNodeInfo, getDownstreamNodes, initializeNodeIfNewToStore }; const { CANCELED: CANCELED$1 } = Recoil_Loadable; const { getDownstreamNodes: getDownstreamNodes$1, getNodeLoadable: getNodeLoadable$1, setNodeValue: setNodeValue$1 } = Recoil_FunctionalCore; const { getNodeMaybe: getNodeMaybe$2 } = Recoil_Node; const { DefaultValue: DefaultValue$1, RecoilValueNotReady: RecoilValueNotReady$1 } = Recoil_Node; const { AbstractRecoilValue: AbstractRecoilValue$1, RecoilState: RecoilState$1, RecoilValueReadOnly: RecoilValueReadOnly$1, isRecoilValue: isRecoilValue$1 } = Recoil_RecoilValue$1; function getRecoilValueAsLoadable(store, { key }, treeState = store.getState().currentTree) { var _storeState$nextTree, _storeState$previousT; // Reading from an older tree can cause bugs because the dependencies that we // discover during the read are lost. const storeState = store.getState(); if (!(treeState.version === storeState.currentTree.version || treeState.version === ((_storeState$nextTree = storeState.nextTree) === null || _storeState$nextTree === void 0 ? void 0 : _storeState$nextTree.version) || treeState.version === ((_storeState$previousT = storeState.previousTree) === null || _storeState$previousT === void 0 ? void 0 : _storeState$previousT.version))) { Recoil_recoverableViolation('Tried to read from a discarded tree'); } const loadable = getNodeLoadable$1(store, treeState, key); if (loadable.state === 'loading') { loadable.contents.catch(() => { /** * HACK: intercept thrown error here to prevent an uncaught promise exception. Ideally this would happen closer to selector * execution (perhaps introducing a new ERROR class to be resolved by async selectors that are in an error state) */ return CANCELED$1; }); } return loadable; } function applyAtomValueWrites(atomValues, writes) { const result = atomValues.clone(); writes.forEach((v, k) => { if (v.state === 'hasValue' && v.contents instanceof DefaultValue$1) { result.delete(k); } else { result.set(k, v); } }); return result; } function valueFromValueOrUpdater(store, state, { key }, valueOrUpdater) { if (typeof valueOrUpdater === 'function') { // Updater form: pass in the current value. Throw if the current value // is unavailable (namely when updating an async selector that's // pending or errored): const current = getNodeLoadable$1(store, state, key); if (current.state === 'loading') { throw new RecoilValueNotReady$1(key); } else if (current.state === 'hasError') { throw current.contents; } // T itself may be a function, so our refinement is not sufficient: return valueOrUpdater(current.contents); // flowlint-line unclear-type:off } else { return valueOrUpdater; } } function applyAction(store, state, action) { if (action.type === 'set') { const { recoilValue, valueOrUpdater } = action; const newValue = valueFromValueOrUpdater(store, state, recoilValue, valueOrUpdater); const writes = setNodeValue$1(store, state, recoilValue.key, newValue); for (const [key, loadable] of writes.entries()) { writeLoadableToTreeState(state, key, loadable); } } else if (action.type === 'setLoadable') { const { recoilValue: { key }, loadable } = action; writeLoadableToTreeState(state, key, loadable); } else if (action.type === 'markModified') { const { recoilValue: { key } } = action; state.dirtyAtoms.add(key); } else if (action.type === 'setUnvalidated') { var _node$invalidate; // Write value directly to state bypassing the Node interface as the node // definitions may not have been loaded yet when processing the initial snapshot. const { recoilValue: { key }, unvalidatedValue } = action; const node = getNodeMaybe$2(key); node === null || node === void 0 ? void 0 : (_node$invalidate = node.invalidate) === null || _node$invalidate === void 0 ? void 0 : _node$invalidate.call(node, state); state.atomValues.delete(key); state.nonvalidatedAtoms.set(key, unvalidatedValue); state.dirtyAtoms.add(key); } else { Recoil_recoverableViolation(`Unknown action ${action.type}`); } } function writeLoadableToTreeState(state, key, loadable) { if (loadable.state === 'hasValue' && loadable.contents instanceof DefaultValue$1) { state.atomValues.delete(key); } else { state.atomValues.set(key, loadable); } state.dirtyAtoms.add(key); state.nonvalidatedAtoms.delete(key); } function applyActionsToStore(store, actions) { store.replaceState(state => { const newState = copyTreeState(state); for (const action of actions) { applyAction(store, newState, action); } invalidateDownstreams(store, newState); return newState; }); } function queueOrPerformStateUpdate(store, action) { if (batchStack.length) { const actionsByStore = batchStack[batchStack.length - 1]; let actions = actionsByStore.get(store); if (!actions) { actionsByStore.set(store, actions = []); } actions.push(action); } else { applyActionsToStore(store, [action]); } } const batchStack = []; function batchStart() { const actionsByStore = new Map(); batchStack.push(actionsByStore); return () => { for (const [store, actions] of actionsByStore) { applyActionsToStore(store, actions); } const popped = batchStack.pop(); if (popped !== actionsByStore) { Recoil_recoverableViolation('Incorrect order of batch popping'); } }; } function copyTreeState(state) { return { ...state, atomValues: state.atomValues.clone(), nonvalidatedAtoms: state.nonvalidatedAtoms.clone(), dirtyAtoms: new Set(state.dirtyAtoms) }; } function invalidateDownstreams(store, state) { // Inform any nodes that were changed or downstream of changes so that they // can clear out any caches as needed due to the update: const downstreams = getDownstreamNodes$1(store, state, state.dirtyAtoms); for (const key of downstreams) { var _getNodeMaybe, _getNodeMaybe$invalid; (_getNodeMaybe = getNodeMaybe$2(key)) === null || _getNodeMaybe === void 0 ? void 0 : (_getNodeMaybe$invalid = _getNodeMaybe.invalidate) === null || _getNodeMaybe$invalid === void 0 ? void 0 : _getNodeMaybe$invalid.call(_getNodeMaybe, state); } } function setRecoilValue(store, recoilValue, valueOrUpdater) { queueOrPerformStateUpdate(store, { type: 'set', recoilValue, valueOrUpdater }); } function setRecoilValueLoadable(store, recoilValue, loadable) { if (loadable instanceof DefaultValue$1) { return setRecoilValue(store, recoilValue, loadable); } queueOrPerformStateUpdate(store, { type: 'setLoadable', recoilValue, loadable }); } function markRecoilValueModified(store, recoilValue) { queueOrPerformStateUpdate(store, { type: 'markModified', recoilValue }); } function setUnvalidatedRecoilValue(store, recoilValue, unvalidatedValue) { queueOrPerformStateUpdate(store, { type: 'setUnvalidated', recoilValue, unvalidatedValue }); } let subscriptionID = 0; function subscribeToRecoilValue(store, { key }, callback, componentDebugName = null) { const subID = subscriptionID++; const storeState = store.getState(); if (!storeState.nodeToComponentSubscriptions.has(key)) { storeState.nodeToComponentSubscriptions.set(key, new Map()); } Recoil_nullthrows(storeState.nodeToComponentSubscriptions.get(key)).set(subID, [componentDebugName !== null && componentDebugName !== void 0 ? componentDebugName : '<not captured>', callback]); return { release: () => { const storeState = store.getState(); const subs = storeState.nodeToComponentSubscriptions.get(key); if (subs === undefined || !subs.has(subID)) { Recoil_recoverableViolation(`Subscription missing at release time for atom ${key}. This is a bug in Recoil.`); return; } subs.delete(subID); if (subs.size === 0) { storeState.nodeToComponentSubscriptions.delete(key); } } }; } var Recoil_RecoilValueInterface = { RecoilValueReadOnly: RecoilValueReadOnly$1, AbstractRecoilValue: AbstractRecoilValue$1, RecoilState: RecoilState$1, getRecoilValueAsLoadable, setRecoilValue, setRecoilValueLoadable, markRecoilValueModified, setUnvalidatedRecoilValue, subscribeToRecoilValue, isRecoilValue: isRecoilValue$1, applyAtomValueWrites, // TODO Remove export when deprecating initialStoreState_DEPRECATED in RecoilRoot batchStart, invalidateDownstreams_FOR_TESTING: invalidateDownstreams }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format * * This is to export esstiential functions from react-dom * for our react-native build */ // $FlowExpectedError[cannot-resolve-module] const { unstable_batchedUpdates } = reactNative; var ReactBatchedUpdates_native = { unstable_batchedUpdates }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format * * This is to export esstiential functions from react-dom * for our web build */ // @fb-only: const {unstable_batchedUpdates} = require('ReactDOMComet'); const { unstable_batchedUpdates: unstable_batchedUpdates$1 } = ReactBatchedUpdates_native; // @oss-only var Recoil_ReactBatchedUpdates = { unstable_batchedUpdates: unstable_batchedUpdates$1 }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ const { batchStart: batchStart$1 } = Recoil_RecoilValueInterface; const { unstable_batchedUpdates: unstable_batchedUpdates$2 } = Recoil_ReactBatchedUpdates; let batcher = unstable_batchedUpdates$2; // flowlint-next-line unclear-type:off /** * Sets the provided batcher function as the batcher function used by Recoil. * * Set the batcher to a custom batcher for your renderer, * if you use a renderer other than React DOM or React Native. */ const setBatcher = newBatcher => { batcher = newBatcher; }; /** * Returns the current batcher function. */ const getBatcher = () => batcher; /** * Calls the current batcher function and passes the * provided callback function. */ const batchUpdates = callback => { batcher(() => { let batchEnd = () => undefined; try { batchEnd = batchStart$1(); callback(); } finally { batchEnd(); } }); }; var Recoil_Batching = { getBatcher, setBatcher, batchUpdates }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ function enqueueExecution(s, f) { f(); } var Recoil_Queue = { enqueueExecution }; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ /** * Returns a set containing all of the values from the first set that are not * present in any of the subsequent sets. * * Note: this is written procedurally (i.e., without filterSet) for performant * use in tight loops. */ function differenceSets(set, ...setsWithValuesToRemove) { const ret = new Set(); FIRST: for (const value of set) { for (const otherSet of setsWithValuesToRemove) { if (otherSet.has(value)) { continue FIRST; } } ret.add(value); } return ret; } var Recoil_differenceSets = differenceSets; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * * @format */ /** * Returns a new Map object with the same keys as the original, but with the * values replaced with the output of the given callback function. */ function mapMap(map, callback) { const result = new Map(); map.forEach((value, key) => { result.set(key, callback(value, key)); }); return result; } var Recoil_mapMap = mapMap; function graph() { return { nodeDeps: new Map(), nodeToNodeSubscriptions: new Map() }; } function cloneGraph(graph) { return { nodeDeps: Recoil_mapMap(graph.nodeDeps, s => new Set(s)), nodeToNodeSubscriptions: Recoil_mapMap(graph.nodeToNodeSubscriptions, s => new Set(s)) }; } // Note that this overwrites the deps of existing nodes, rather than unioning // the new deps with the old deps. function mergeDependencyMapIntoGraph(deps, graph, // If olderGraph is given then we will not overwrite changes made to the given // graph compared with olderGraph: olderGraph) { const { nodeDeps, nodeToNodeSubscriptions } = graph; deps.forEach((upstreams, downstream) => { const existingUpstreams = nodeDeps.get(downstream); if (existingUpstreams && olderGraph && existingUpstreams !== olderGraph.nodeDeps.get(downstream)) { return; } // Update nodeDeps: nodeDeps.set(downstream, new Set(upstreams)); // Add new deps to nodeToNodeSubscriptions: const addedUpstreams = existingUpstreams == null ? upstreams : Recoil_differenceSets(upstreams, existingUpstreams); addedUpstreams.forEach(upstream => { if (!nodeToNodeSubscriptions.has(upstream)) { nodeToNodeSubscriptions.set(upstream, new Set()); } const existing = Recoil_nullthrows(nodeToNodeSubscriptions.get(upstream)); existing.add(downstream); }); // Remove removed deps from nodeToNodeSubscriptions: if (existingUpstreams) { const removedUpstreams = Recoil_differenceSets(existingUpstreams, upstreams); removedUpstreams.forEach(upstream => { if (!nodeToNodeSubscriptions.has(upstream)) { return; } const existing = Recoil_nullthrows(nodeToNodeSubscriptions.get(upstream)); existing.delete(downstream); if (existing.size === 0) { nodeToNodeSubscriptions.delete(upstream); } }); } }); } function saveDependencyMapToStore(dependencyMap, store, version) { var _storeState$nextTree, _storeState$previousT, _storeState$previousT2, _storeState$previousT3; const storeState = store.getState(); if (!(version === storeState.currentTree.version || version === ((_storeState$nextTree = storeState.nextTree) === null || _storeState$nextTree === void 0 ? void 0 : _storeState$nextTree.version) || version === ((_storeState$previousT = storeState.previousTree) === null || _storeState$previousT === void 0 ? void 0 : _storeState$previousT.version))) { Recoil_recoverableViolation('Tried to save dependencies to a discarded tree'); } // Merge the dependencies discovered into the store's dependency map // for the version that was read: const graph = store.getGraph(version); mergeDependencyMapIntoGraph(dependencyMap, graph); // If this version is not the latest version, also write these dependencies // into later versions if they don't already have their own: if (version === ((_storeState$previousT2 = storeState.previousTree) === null || _storeState$previousT2 === void 0 ? void 0 : _storeState$previousT2.version)) { const currentGraph = store.getGraph(storeState.currentTree.version); mergeDependencyMapIntoGraph(dependencyMap, currentGraph, graph); } if (version === ((_storeState$previousT3 = storeState.previousTree) === null || _storeState$previousT3 === void 0 ? void 0 : _storeState$previousT3.version) || version === storeState.currentTree.version) { var _storeState$nextTree2; const nextVersion = (_storeState$nextTree2 = storeState.nextTree) === null || _storeState$nextTree2 === void 0 ? void 0 : _storeState$nextTree2.version; if (nextVersion !== undefined) { const nextGraph = store.getGraph(nextVersion); mergeDependencyMapIntoGraph(dependencyMap, nextGraph, graph); } } } function mergeDepsIntoDependencyMap(from, into) { from.forEach((upstreamDeps, downstreamNode) => { if (!into.has(downstreamNode)) { into.set(downstreamNode, new Set()); } const deps = Recoil_nullthrows(into.get(downstreamNode)); upstreamDeps.forEach(dep => deps.add(dep)); }); } function addToDependencyMap(downstream, upstream, dependencyMap) { if (!dependencyMap.has(downstream)) { dependencyMap.set(downstream, new Set()); } Recoil_nullthrows(dependencyMap.get(downstream)).add(upstream); } var Recoil_Graph = { addToDependencyMap, cloneGraph, graph, mergeDepsIntoDependencyMap, saveDependencyMapToStore }; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var hamt_1 = createCommonjsModule(function (module) { var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** @fileOverview Hash Array Mapped Trie. Code based on: https://github.com/exclipy/pdata */ var hamt = {}; // export /* Configuration ******************************************************************************/ var SIZE = 5; var BUCKET_SIZE = Math.pow(2, SIZE); var MASK = BUCKET_SIZE - 1; var MAX_INDEX_NODE = BUCKET_SIZE / 2; var MIN_ARRAY_NODE = BUCKET_SIZE / 4; /* ******************************************************************************/ var nothing = {}; var constant = function constant(x) { return function () { return x; }; }; /** Get 32 bit hash of string. Based on: http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery */ var hash = hamt.hash = function (str) { var type = typeof str === 'undefined' ? 'undefined' : _typeof(str); if (type === 'number') return str; if (type !== 'string') str += ''; var hash = 0; for (var i = 0, len = str.length; i < len; ++i) { var c = str.charCodeAt(i); hash = (hash << 5) - hash + c | 0; } return hash; }; /* Bit Ops ******************************************************************************/ /** Hamming weight. Taken from: http://jsperf.com/hamming-weight */ var popcount = function popcount(x) { x -= x >> 1 & 0x55555555; x = (x & 0x33333333) + (x >> 2 & 0x33333333); x = x + (x >> 4) & 0x0f0f0f0f; x += x >> 8; x += x >> 16; return x & 0x7f; }; var hashFragment = function hashFragment(shift, h) { return h >>> shift & MASK; }; var toBitmap = function toBitmap(x) { return 1 << x; }; var fromBitmap = function fromBitmap(bitmap, bit) { return popcount(bitmap & bit - 1); }; /* Array Ops ******************************************************************************/ /** Set a value in an array. @param mutate Should the input array be mutated? @param at Index to change. @param v New value @param arr Array. */ var arrayUpdate = function arrayUpdate(mutate, at, v, arr) { var out = arr; if (!mutate) { var len = arr.length; out = new Array(len); for (var i = 0; i < len; ++i) { out[i] = arr[i]; } } out[at] = v; return out; }; /** Remove a value from an array. @param mutate Should the input array be mutated? @param at Index to remove. @param arr Array. */ var arraySpliceOut = function arraySpliceOut(mutate, at, arr) { var newLen = arr.length - 1; var i = 0; var g = 0; var out = arr; if (mutate) { i = g = at; } else { out = new Array(newLen); while (i < at) { out[g++] = arr[i++]; } } ++i; while (i <= newLen) { out[g++] = arr[i++]; } if (mutate) { out.length = newLen; } return out; }; /** Insert a value into an array. @param mutate Should the input array be mutated? @param at Index to insert at. @param v Value to insert, @param arr Array. */ var arraySpliceIn = function arraySpliceIn(mutate, at, v, arr) { var len = arr.length; if (mutate) { var _i = len; while (_i >= at) { arr[_i--] = arr[_i]; } arr[at] = v; return arr; } var i = 0, g = 0; var out = new Array(len + 1); while (i < at) { out[g++] = arr[i++]; } out[at] = v; while (i < len) { out[++g] = arr[i++]; } return out; }; /* Node Structures ******************************************************************************/ var LEAF = 1; var COLLISION = 2; var INDEX = 3; var ARRAY = 4; /** Empty node. */ var empty = { __hamt_isEmpty: true }; var isEmptyNode = function isEmptyNode(x) { return x === empty || x && x.__hamt_isEmpty; }; /** Leaf holding a value. @member edit Edit of the node. @member hash Hash of key. @member key Key. @member value Value stored. */ var Leaf = function Leaf(edit, hash, key, value) { return { type: LEAF, edit: edit, hash: hash, key: key, value: value, _modify: Leaf__modify }; }; /** Leaf holding multiple values with the same hash but different keys. @member edit Edit of the node. @member hash Hash of key. @member children Array of collision children node. */ var Collision = function Collision(edit, hash, children) { return { type: COLLISION, edit: edit, hash: hash, children: children, _modify: Collision__modify }; }; /** Internal node with a sparse set of children. Uses a bitmap and array to pack children. @member edit Edit of the node. @member mask Bitmap that encode the positions of children in the array. @member children Array of child nodes. */ var IndexedNode = function IndexedNode(edit, mask, children) { return { type: INDEX, edit: edit, mask: mask, children: children, _modify: IndexedNode__modify }; }; /** Internal node with many children. @member edit Edit of the node. @member size Number of children. @member children Array of child nodes. */ var ArrayNode = function ArrayNode(edit, size, children) { return { type: ARRAY, edit: edit, size: size, children: children, _modify: ArrayNode__modify }; }; /** Is `node` a leaf node? */ var isLeaf = function isLeaf(node) { return node === empty || node.type === LEAF || node.type === COLLISION; }; /* Internal node operations. ******************************************************************************/ /** Expand an indexed node into an array node. @param edit Current edit. @param frag Index of added child. @param child Added child. @param mask Index node mask before child added. @param subNodes Index node children before child added. */ var expand = function expand(edit, frag, child, bitmap, subNodes) { var arr = []; var bit = bitmap; var count = 0; for (var i = 0; bit; ++i) { if (bit & 1) arr[i] = subNodes[count++]; bit >>>= 1; } arr[frag] = child; return ArrayNode(edit, count + 1, arr); }; /** Collapse an array node into a indexed node. @param edit Current edit. @param count Number of elements in new array. @param removed Index of removed element. @param elements Array node children before remove. */ var pack = function pack(edit, count, removed, elements) { var children = new Array(count - 1); var g = 0; var bitmap = 0; for (var i = 0, len = elements.length; i < len; ++i) { if (i !== removed) { var elem = elements[i]; if (elem && !isEmptyNode(elem)) { children[g++] = elem; bitmap |= 1 << i; } } } return IndexedNode(edit, bitmap, children); }; /** Merge two leaf nodes. @param shift Current shift. @param h1 Node 1 hash. @param n1 Node 1. @param h2 Node 2 hash. @param n2 Node 2. */ var mergeLeaves = function mergeLeaves(edit, shift, h1, n1, h2, n2) { if (h1 === h2) return Collision(edit, h1, [n2, n1]); var subH1 = hashFragment(shift, h1); var subH2 = hashFragment(shift, h2); return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), subH1 === subH2 ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)] : subH1 < subH2 ? [n1, n2] : [n2, n1]); }; /** Update an entry in a collision list. @param mutate Should mutation be used? @param edit Current edit. @param keyEq Key compare function. @param hash Hash of collision. @param list Collision list. @param f Update function. @param k Key to update. @param size Size ref. */ var updateCollisionList = function updateCollisionList(mutate, edit, keyEq, h, list, f, k, size) { var len = list.length; for (var i = 0; i < len; ++i) { var child = list[i]; if (keyEq(k, child.key)) { var value = child.value; var _newValue = f(value); if (_newValue === value) return list; if (_newValue === nothing) { --size.value; return arraySpliceOut(mutate, i, list); } return arrayUpdate(mutate, i, Leaf(edit, h, k, _newValue), list); } } var newValue = f(); if (newValue === nothing) return list; ++size.value; return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list); }; var canEditNode = function canEditNode(edit, node) { return edit === node.edit; }; /* Editing ******************************************************************************/ var Leaf__modify = function Leaf__modify(edit, keyEq, shift, f, h, k, size) { if (keyEq(k, this.key)) { var _v = f(this.value); if (_v === this.value) return this;else if (_v === nothing) { --size.value; return empty; } if (canEditNode(edit, this)) { this.value = _v; return this; } return Leaf(edit, h, k, _v); } var v = f(); if (v === nothing) return this; ++size.value; return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); }; var Collision__modify = function Collision__modify(edit, keyEq, shift, f, h, k, size) { if (h === this.hash) { var canEdit = canEditNode(edit, this); var list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size); if (list === this.children) return this; return list.length > 1 ? Collision(edit, this.hash, list) : list[0]; // collapse single element collision list } var v = f(); if (v === nothing) return this; ++size.value; return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); }; var IndexedNode__modify = function IndexedNode__modify(edit, keyEq, shift, f, h, k, size) { var mask = this.mask; var children = this.children; var frag = hashFragment(shift, h); var bit = toBitmap(frag); var indx = fromBitmap(mask, bit); var exists = mask & bit; var current = exists ? children[indx] : empty; var child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size); if (current === child) return this; var canEdit = canEditNode(edit, this); var bitmap = mask; var newChildren = void 0; if (exists && isEmptyNode(child)) { // remove bitmap &= ~bit; if (!bitmap) return empty; if (children.length <= 2 && isLeaf(children[indx ^ 1])) return children[indx ^ 1]; // collapse newChildren = arraySpliceOut(canEdit, indx, children); } else if (!exists && !isEmptyNode(child)) { // add if (children.length >= MAX_INDEX_NODE) return expand(edit, frag, child, mask, children); bitmap |= bit; newChildren = arraySpliceIn(canEdit, indx, child, children); } else { // modify newChildren = arrayUpdate(canEdit, indx, child, children); } if (canEdit) { this.mask = bitmap; this.children = newChildren; return this; } return IndexedNode(edit, bitmap, newChildren); }; var ArrayNode__modify = function ArrayN