UNPKG

recoil

Version:

Recoil - A state management library for React

2,168 lines (1,669 loc) 275 kB
import react from 'react'; import reactDom from 'react-dom'; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ function err(message) { const error = new Error(message); // In V8, Error objects keep the closure scope chain alive until the // err.stack property is accessed. if (error.stack === undefined) { // IE sets the stack only if error is thrown try { throw error; } catch (_) {} // eslint-disable-line fb-www/no-unused-catch-bindings, no-empty } return error; } var err_1 = err; // @oss-only var Recoil_err = err_1; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ // 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; function nullthrows(x, message) { if (x != null) { return x; } throw Recoil_err(message !== null && message !== void 0 ? message : 'Got unexpected null or undefined'); } var Recoil_nullthrows = nullthrows; 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; } class BaseLoadable { getValue() { throw Recoil_err('BaseLoadable'); } toPromise() { throw Recoil_err('BaseLoadable'); } valueMaybe() { throw Recoil_err('BaseLoadable'); } valueOrThrow() { // $FlowFixMe[prop-missing] throw Recoil_err(`Loadable expected value, but in "${this.state}" state`); } promiseMaybe() { throw Recoil_err('BaseLoadable'); } promiseOrThrow() { // $FlowFixMe[prop-missing] throw Recoil_err(`Loadable expected promise, but in "${this.state}" state`); } errorMaybe() { throw Recoil_err('BaseLoadable'); } errorOrThrow() { // $FlowFixMe[prop-missing] throw Recoil_err(`Loadable expected error, but in "${this.state}" state`); } is(other) { // $FlowFixMe[prop-missing] return other.state === this.state && other.contents === this.contents; } map(_map) { throw Recoil_err('BaseLoadable'); } } class ValueLoadable extends BaseLoadable { constructor(value) { super(); _defineProperty(this, "state", 'hasValue'); _defineProperty(this, "contents", void 0); this.contents = value; } getValue() { return this.contents; } toPromise() { return Promise.resolve(this.contents); } valueMaybe() { return this.contents; } valueOrThrow() { return this.contents; } promiseMaybe() { return undefined; } errorMaybe() { return undefined; } map(map) { try { const next = map(this.contents); return Recoil_isPromise(next) ? loadableWithPromise(next) : isLoadable(next) ? 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 // $FlowFixMe[prop-missing] loadableWithPromise(e.next(() => this.map(map))) : loadableWithError(e); } } } class ErrorLoadable extends BaseLoadable { constructor(error) { super(); _defineProperty(this, "state", 'hasError'); _defineProperty(this, "contents", void 0); this.contents = error; } getValue() { throw this.contents; } toPromise() { return Promise.reject(this.contents); } valueMaybe() { return undefined; } promiseMaybe() { return undefined; } errorMaybe() { return this.contents; } errorOrThrow() { return this.contents; } map(_map) { // $FlowIssue[incompatible-return] return this; } } class LoadingLoadable extends BaseLoadable { constructor(promise) { super(); _defineProperty(this, "state", 'loading'); _defineProperty(this, "contents", void 0); this.contents = promise; } getValue() { throw this.contents; } toPromise() { return this.contents; } valueMaybe() { return undefined; } promiseMaybe() { return this.contents; } promiseOrThrow() { return this.contents; } errorMaybe() { return undefined; } map(map) { return loadableWithPromise(this.contents.then(value => { const next = map(value); if (isLoadable(next)) { const nextLoadable = next; switch (nextLoadable.state) { case 'hasValue': return nextLoadable.contents; case 'hasError': throw nextLoadable.contents; case 'loading': return nextLoadable.contents; } } // $FlowIssue[incompatible-return] return next; }) // $FlowFixMe[incompatible-call] .catch(e => { if (Recoil_isPromise(e)) { // we were "suspended," try again return e.then(() => this.map(map).contents); } throw e; })); } } function loadableWithValue(value) { return Object.freeze(new ValueLoadable(value)); } function loadableWithError(error) { return Object.freeze(new ErrorLoadable(error)); } function loadableWithPromise(promise) { return Object.freeze(new LoadingLoadable(promise)); } function loadableLoading() { return Object.freeze(new LoadingLoadable(new Promise(() => {}))); } function loadableAllArray(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))); } function loadableAll(inputs) { const unwrapedInputs = Array.isArray(inputs) ? inputs : Object.getOwnPropertyNames(inputs).map(key => inputs[key]); const normalizedInputs = unwrapedInputs.map(x => isLoadable(x) ? x : Recoil_isPromise(x) ? loadableWithPromise(x) : loadableWithValue(x)); const output = loadableAllArray(normalizedInputs); return Array.isArray(inputs) ? // $FlowIssue[incompatible-return] output : // Object.getOwnPropertyNames() has consistent key ordering with ES6 // $FlowIssue[incompatible-call] output.map(outputs => Object.getOwnPropertyNames(inputs).reduce( // $FlowFixMe[invalid-computed-prop] (out, key, idx) => ({ ...out, [key]: outputs[idx] }), {})); } function isLoadable(x) { return x instanceof BaseLoadable; } const LoadableStaticInterface = { of: value => Recoil_isPromise(value) ? loadableWithPromise(value) : isLoadable(value) ? value : loadableWithValue(value), error: error => loadableWithError(error), // $FlowIssue[incompatible-return] loading: () => loadableLoading(), // $FlowIssue[unclear-type] all: loadableAll, isLoadable }; var Recoil_Loadable = { loadableWithValue, loadableWithError, loadableWithPromise, loadableLoading, loadableAll, isLoadable, RecoilLoadable: LoadableStaticInterface }; var Recoil_Loadable_1 = Recoil_Loadable.loadableWithValue; var Recoil_Loadable_2 = Recoil_Loadable.loadableWithError; var Recoil_Loadable_3 = Recoil_Loadable.loadableWithPromise; var Recoil_Loadable_4 = Recoil_Loadable.loadableLoading; var Recoil_Loadable_5 = Recoil_Loadable.loadableAll; var Recoil_Loadable_6 = Recoil_Loadable.isLoadable; var Recoil_Loadable_7 = Recoil_Loadable.RecoilLoadable; var Recoil_Loadable$1 = /*#__PURE__*/Object.freeze({ __proto__: null, loadableWithValue: Recoil_Loadable_1, loadableWithError: Recoil_Loadable_2, loadableWithPromise: Recoil_Loadable_3, loadableLoading: Recoil_Loadable_4, loadableAll: Recoil_Loadable_5, isLoadable: Recoil_Loadable_6, RecoilLoadable: Recoil_Loadable_7 }); const env = { RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED: true, // Note: RECOIL_GKS_ENABLED settings will only be honored in OSS builds of Recoil RECOIL_GKS_ENABLED: new Set(['recoil_hamt_2020', 'recoil_sync_external_store', 'recoil_suppress_rerender_in_callback', 'recoil_memory_managament_2020']) }; function readProcessEnvBooleanFlag(name, set) { var _process$env$name, _process$env$name$toL; const sanitizedValue = (_process$env$name = process.env[name]) === null || _process$env$name === void 0 ? void 0 : (_process$env$name$toL = _process$env$name.toLowerCase()) === null || _process$env$name$toL === void 0 ? void 0 : _process$env$name$toL.trim(); if (sanitizedValue == null || sanitizedValue === '') { return; } const allowedValues = ['true', 'false']; if (!allowedValues.includes(sanitizedValue)) { throw Recoil_err(`process.env.${name} value must be 'true', 'false', or empty: ${sanitizedValue}`); } set(sanitizedValue === 'true'); } function readProcessEnvStringArrayFlag(name, set) { var _process$env$name2; const sanitizedValue = (_process$env$name2 = process.env[name]) === null || _process$env$name2 === void 0 ? void 0 : _process$env$name2.trim(); if (sanitizedValue == null || sanitizedValue === '') { return; } set(sanitizedValue.split(/\s*,\s*|\s+/)); } /** * Allow NodeJS/NextJS/etc to set the initial state through process.env variable * Note: we don't assume 'process' is available in all runtime environments * * @see https://github.com/facebookexperimental/Recoil/issues/733 */ function applyProcessEnvFlagOverrides() { var _process; // note: this check is needed in addition to the check below, runtime error will occur without it! // eslint-disable-next-line fb-www/typeof-undefined if (typeof process === 'undefined') { return; } if (((_process = process) === null || _process === void 0 ? void 0 : _process.env) == null) { return; } readProcessEnvBooleanFlag('RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED', value => { env.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = value; }); readProcessEnvStringArrayFlag('RECOIL_GKS_ENABLED', value => { value.forEach(gk => { env.RECOIL_GKS_ENABLED.add(gk); }); }); } applyProcessEnvFlagOverrides(); var Recoil_RecoilEnv = env; function Recoil_gkx_OSS(gk) { return Recoil_RecoilEnv.RECOIL_GKS_ENABLED.has(gk); } Recoil_gkx_OSS.setPass = gk => { Recoil_RecoilEnv.RECOIL_GKS_ENABLED.add(gk); }; Recoil_gkx_OSS.setFail = gk => { Recoil_RecoilEnv.RECOIL_GKS_ENABLED.delete(gk); }; Recoil_gkx_OSS.clear = () => { Recoil_RecoilEnv.RECOIL_GKS_ENABLED.clear(); }; var Recoil_gkx = Recoil_gkx_OSS; // @oss-only /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ 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; var _createMutableSource, _useMutableSource, _useSyncExternalStore; const createMutableSource = // flowlint-next-line unclear-type:off (_createMutableSource = react.createMutableSource) !== null && _createMutableSource !== void 0 ? _createMutableSource : react.unstable_createMutableSource; const useMutableSource = // flowlint-next-line unclear-type:off (_useMutableSource = react.useMutableSource) !== null && _useMutableSource !== void 0 ? _useMutableSource : react.unstable_useMutableSource; // https://github.com/reactwg/react-18/discussions/86 const useSyncExternalStore = // flowlint-next-line unclear-type:off (_useSyncExternalStore = react.useSyncExternalStore) !== null && _useSyncExternalStore !== void 0 ? _useSyncExternalStore : // flowlint-next-line unclear-type:off react.unstable_useSyncExternalStore; let ReactRendererVersionMismatchWarnOnce = false; // Check if the current renderer supports `useSyncExternalStore()`. // Since React goes through a proxy dispatcher and the current renderer can // change we can't simply check if `React.useSyncExternalStore()` is defined. function currentRendererSupportsUseSyncExternalStore() { var _ReactCurrentDispatch; // $FlowFixMe[incompatible-use] const { ReactCurrentDispatcher, ReactCurrentOwner } = /* $FlowFixMe[prop-missing] This workaround was approved as a safer mechanism * to detect if the current renderer supports useSyncExternalStore() * https://fb.workplace.com/groups/reactjs/posts/9558682330846963/ */ react.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; const dispatcher = (_ReactCurrentDispatch = ReactCurrentDispatcher === null || ReactCurrentDispatcher === void 0 ? void 0 : ReactCurrentDispatcher.current) !== null && _ReactCurrentDispatch !== void 0 ? _ReactCurrentDispatch : ReactCurrentOwner.currentDispatcher; const isUseSyncExternalStoreSupported = dispatcher.useSyncExternalStore != null; if (useSyncExternalStore && !isUseSyncExternalStoreSupported && !ReactRendererVersionMismatchWarnOnce) { ReactRendererVersionMismatchWarnOnce = true; Recoil_recoverableViolation('A React renderer without React 18+ API support is being used with React 18+.'); } return isUseSyncExternalStoreSupported; } /** * mode: The React API and approach to use for syncing state with React * early: Re-renders from Recoil updates occur: * 1) earlier * 2) in sync with React updates in the same batch * 3) before transaction observers instead of after. * concurrent: Is the current mode compatible with Concurrent Mode and useTransition() */ function reactMode() { // NOTE: This mode is currently broken with some Suspense cases // see Recoil_selector-test.js if (Recoil_gkx('recoil_transition_support')) { return { mode: 'TRANSITION_SUPPORT', early: true, concurrent: true }; } if (Recoil_gkx('recoil_sync_external_store') && useSyncExternalStore != null) { return { mode: 'SYNC_EXTERNAL_STORE', early: true, concurrent: false }; } if (Recoil_gkx('recoil_mutable_source') && useMutableSource != null && typeof window !== 'undefined' && !window.$disableRecoilValueMutableSource_TEMP_HACK_DO_NOT_USE) { return Recoil_gkx('recoil_suppress_rerender_in_callback') ? { mode: 'MUTABLE_SOURCE', early: true, concurrent: true } : { mode: 'MUTABLE_SOURCE', early: false, concurrent: false }; } return Recoil_gkx('recoil_suppress_rerender_in_callback') ? { mode: 'LEGACY', early: true, concurrent: false } : { mode: 'LEGACY', early: false, concurrent: false }; } // TODO Need to figure out if there is a standard/open-source equivalent to see if hot module replacement is happening: function isFastRefreshEnabled() { // @fb-only: const {isAcceptingUpdate} = require('__debug'); // @fb-only: return typeof isAcceptingUpdate === 'function' && isAcceptingUpdate(); return false; // @oss-only } var Recoil_ReactMode = { createMutableSource, useMutableSource, useSyncExternalStore, currentRendererSupportsUseSyncExternalStore, reactMode, isFastRefreshEnabled }; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ // eslint-disable-next-line no-unused-vars class AbstractRecoilValue { constructor(newKey) { _defineProperty(this, "key", void 0); this.key = newKey; } toJSON() { return { key: this.key }; } } 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 }); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ 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; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ /** * 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; const { isFastRefreshEnabled: isFastRefreshEnabled$1 } = Recoil_ReactMode; class DefaultValue {} const DEFAULT_VALUE = new DefaultValue(); // 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 checkForDuplicateAtomKey(key) { if (nodes.has(key)) { const message = `Duplicate atom key "${key}". This is a FATAL ERROR in production. But it is safe to ignore this warning if it occurred because of hot module replacement.`; if (process.env.NODE_ENV !== "production") { // TODO Figure this out for open-source if (!isFastRefreshEnabled$1()) { Recoil_expectationViolation(message, 'recoil'); } } else { // @fb-only: recoverableViolation(message, 'recoil'); console.warn(message); // @oss-only } } } function registerNode(node) { if (Recoil_RecoilEnv.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED) { checkForDuplicateAtomKey(node.key); } 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('recoil_memory_managament_2020')) { return; } const node = nodes.get(key); if (node !== null && node !== void 0 && (_node$shouldDeleteCon = node.shouldDeleteConfigOnRelease) !== null && _node$shouldDeleteCon !== 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('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 }; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ function enqueueExecution(s, f) { f(); } var Recoil_Queue = { enqueueExecution }; 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 ArrayNode__modify(edit, keyEq, shift, f, h, k, size) { var count = this.size; var children = this.children; var frag = hashFragment(shift, h); var child = children[frag]; var newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size); if (child === newChild) return this; var canEdit = canEditNode(edit, this); var newChildren = void 0; if (isEmptyNode(child) && !isEmptyNode(newChild)) { // add ++count; newChildren = arrayUpdate(canEdit, frag, newChild, children); } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { // remove --count; if (count <= MIN_ARRAY_NODE) return pack(edit, count, frag, children); newChildren = arrayUpdate(canEdit, frag, empty, children); } else { // modify newChildren = arrayUpdate(canEdit, frag, newChild, children); } if (canEdit) { this.size = count; this.children = newChildren; return this; } return ArrayNode(edit, count, newChildren); }; empty._modify = function (edit, keyEq, shift, f, h, k, size) { var v = f(); if (v === nothing) return empty; ++size.value; return Leaf(edit, h, k, v); }; /* ******************************************************************************/ function Map(editable, edit, config, root, size) { this._editable = editable; this._edit = edit; this._config = config; this._root = root; this._size = size; } Map.prototype.setTree = function (newRoot, newSize) { if (this._editable) { this._root = newRoot; this._size = newSize; return this; } return newRoot === this._root ? this : new Map(this._editable, this._edit, this._config, newRoot, newSize); }; /* Queries ******************************************************************************/ /** Lookup the value for `key` in `map` using a custom `hash`. Returns the value or `alt` if none. */ var tryGetHash = hamt.tryGetHash = function (alt, hash, key, map) { var node = map._root; var shift = 0; var keyEq = map._config.keyEq; while (true) { switch (node.type) { case LEAF: { return keyEq(key, node.key) ? node.value : alt; } case COLLISION: { if (hash === node.hash) { var children = node.children; for (var i = 0, len = children.length; i < len; ++i) { var child = children[i]; if (keyEq(key, child.key)) return child.value; } } return alt; } case INDEX: { var frag = hashFragment(shift, hash); var bit = toBitmap(frag); if (node.mask & bit) { node = node.children[fromBitmap(node.mask, bit)]; shift += SIZE; break; } return alt; } case ARRAY: { node = node.children[hashFragment(shift, hash)]; if (node) { shift += SIZE; break; } return alt; } default: return alt; } } }; Map.prototype.tryGetHash = function (alt, hash, key) { return tryGetHash(alt, hash, key, this); }; /** Lookup the value for `key` in `map` using internal hash function. @see `tryGetHash` */ var tryGet = hamt.tryGet = function (alt, key, map) { return tryGetHash(alt, map._config.hash(key), key, map); }; Map.prototype.tryGet = function (alt, key) { return tryGet(alt, key, this); }; /** Lookup the value for `key` in `map` using a custom `hash`. Returns the value or `undefined` if none. */ var getHash = hamt.getHash = function (hash, key, map) { return tryGetHash(undefined, hash, key, map); }; Map.prototype.getHash = function (hash, key) { return getHash(hash, key, this); }; /** Lookup the value for `key` in `map` using internal hash function. @see `get` */ var get = hamt.get = function (key, map) { return tryGetHash(undefined, map._config.hash(key), key, map); }; Map.prototype.get = function (key, alt) { return tryGet(alt, key, this); }; /** Does an entry exist for `key` in `map`? Uses custom `hash`. */ var hasHash = hamt.has = function (hash, key, map) { return tryGetHash(nothing, hash, key, map) !== nothing; }; Map.prototype.hasHash = function (hash, key) { return hasHash(hash, key, this); }; /** Does an entry exist for `key` in `map`? Uses internal hash function. */ var has = hamt.has = function (key, map) { return hasHash(map._config.hash(key), key, map); }; Map.prototype.has = function (key) { return has(key, this); }; var defKeyCompare = function defKeyCompare(x, y) { return x === y; }; /** Create an empty map. @param config Configuration. */ hamt.make = function (config) { return new Map(0, 0, { keyEq: config && config.keyEq || defKeyCompare, hash: config && config.hash || hash }, empty, 0); }; /** Empty map. */ hamt.empty = hamt.make(); /** Does `map` contain any elements? */ var isEmpty = hamt.isEmpty = function (map) { return map && !!isEmptyNode(map._root); }; Map.prototype.isEmpty = function () { return isEmpty(this); }; /* Updates ******************************************************************************/ /** Alter the value stored for `key` in `map` using function `f` using custom hash. `f` is invoked with the current value for `k` if it exists, or no arguments if no such value exists. `modify` will always either update or insert a value into the map. Returns a map with the modified value. Does not alter `map`. */ var modifyHash = hamt.modifyHash = function (f, hash, key, map) { var size = { value: map._size }; var newRoot = map._root._modify(map._editable ? map._edit : NaN, map._config.keyEq, 0, f, hash, key, size); return map.setTree(newRoot, size.value); }; Map.prototype.modifyHash = function (hash, key, f) { return modifyHash(f, hash, key, this); }; /** Alter the value stored for `key` in `map` using function `f` using internal hash function. @see `modifyHash` */ var modify = hamt.modify = function (f, key, map) { return modifyHash(f, map._config.hash(key), key, map); }; Map.prototype.modify = function (key, f) { return modify(f, key, this); }; /** Store `value` for `key` in `map` using custom `hash`. Returns a map with the modified value. Does not alter `map`. */ var setHash = hamt.setHash = function (hash, key, value, map) { return modifyHash(constant(value), hash, key, map); }; Map.prototype.setHash = function (hash, key, value) { return setHash(hash, key, value, this); }; /** Store `value` for `key` in `map` using internal hash function. @see `setHash` */ var set = hamt.set = function (key, value, map) { return setHash(map._config.hash(key), key, value, map); }; Map.prototype.set = function (key, value) { return set(key, value, this); }; /** Remove the entry for `key` in `map`. Returns a map with the value removed. Does not alter `map`. */ var del = constant(nothing); var removeHash = hamt.removeHash = function (hash, key, map) { return modifyHash(del, hash, key, map); }; Map.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) { return removeHash(hash, key, this); }; /** Remove the entry for `key` in `map` using internal hash function. @see `removeHash` */ var remove = hamt.remove = function (key, map) { return removeHash(map._config.hash(key), key, map); }; Map.prototype.remove = Map.prototype.delete = function (key) { return remove(key, this); }; /* Mutation ******************************************************************************/ /** Mark `map` as mutable. */ var beginMutation = hamt.beginMutation = function (map) { return new Map(map._editable + 1, map._edit + 1, map._config, map._root, map._size); }; Map.prototype.beginMutation = function () { return beginMutation(this); }; /** Mark `map` as immutable. */ var endMutation = hamt.endMutation = function (map) { map._editable = map._editable && map._editable - 1; return map; }; Map.prototype.endMutation = function () { return endMutation(this); }; /** Mutate `map` within the context of `f`. @param f @param map HAMT */ var mutate = hamt.mutate = function (f, map) { var transient = beginMutation(map); f(transient); return endMutation(transient); }; Map.prototype.mutate = function (f) { return mutate(f, this); }; /* Traversal ******************************************************************************/ /** Apply a continuation. */ var appk = function appk(k) { return k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]); }; /** Recursively visit all values stored in an array of nodes lazily. */ var lazyVisitChildren = function lazyVisitChildren(len, children, i, f, k) { while (i < len) { var child = children[i++]; if (child && !isEmptyNode(child)) return lazyVisit(child, f, [len, children, i, f, k]); } return appk(k); }; /** Recursively visit all values stored in `node` lazily. */ var lazyVisit = function lazyVisit(node, f, k) { switch (node.type) { case LEAF: return { value: f(node), rest: k }; case COLLISION: case ARRAY: case INDEX: var children = node.children; return lazyVisitChildren(children.length, children, 0, f, k); default: return appk(k); } }; var DONE = { done: true }; /** Javascript iterator over a map. */ function MapIterator(v) { this.v = v; } MapIterator.prototype.next = function () { if (!this.v) return DONE; var v0 = this.v; this.v = appk(v0.rest); return v0; }; MapIterator.prototype[Symbol.iterator] = function () { return this; }; /** Lazily visit each value in map with function `f`. */ var visit = function visit(map, f) { return new MapIterator(lazyVisit(map._root, f)); }; /** Get a Javascsript iterator of `map`. Iterates over `[key, value]` arrays. */ var buildPairs = function buildPairs(x) { return [x.key, x.value]; }; var entries = hamt.entries = function (map) { return visit(map, buildPairs); }; Map.prototype.entries = Map.prototype[Symbol.iterator] = function () { return entries(this); }; /** Get array of all keys in `map`. Order is not guaranteed. */ var buildKeys = function buildKeys(x) { return x.key; }; var keys = hamt.keys = function (map) { return visit(map, buildKeys); }; Map.prototype.keys = function () { return keys(this); }; /** Get array of all values in `map`. Order is not guaranteed, duplicates are preserved. */ var buildValues = function buildValues(x) { return x.value; }; var values = hamt.values = Map.prototype.values = function (map) { return visit(map, buildValues); }; Map.prototype.values = function () { return values(this); }; /* Fold ******************************************************************************/ /** Visit every entry in the map, aggregating data. Order of nodes is not guaranteed. @param f Function mapping accumulated value, value, and key to new value. @param z Starting value. @param m HAMT */ var fold = hamt.fold = function (f, z, m) { var root = m._root; if (root.type === LEAF) return f(z, root.value, root.key); var toVisit = [root.children]; var children = void 0; while (children = toVisit.pop()) { for (var i = 0, len = children.length; i < len;) { var child = children[i++]; if (child && child.type) { if (child.type === LEAF) z = f(z, child.value, child.key);else toVisit.push(child.children); } } } return z; }; Map.prototype.fold = function (f, z) { return fold(f, z, this); }; /** Visit every entry in the map, aggregating data. Order of nodes is not guaranteed. @param f Function invoked with value and key @param map HAMT */ var forEach = hamt.forEach = function (f, map) { return fold(function (_, value, key) { return f(value, key, map); }, null, map); }; Map.prototype.forEach = function (f) { return forEach(f, this); }; /* Aggregate ******************************************************************************/ /** Get the number of entries in `map`. */ var count = hamt.count = function (map) { return map._size; }; Map.prototype.count = function () { return count(this); }; Object.defineProperty(Map.prototype, 'size', { get: Map.prototype.count }); /* Export ******************************************************************************/ if ( module.exports) { module.exports = hamt; } else { undefined.hamt = hamt; } }); class BuiltInMap { constructor(existing) { _defineProperty(this, "_map", void 0); this._map = new Map(existing === null || existing === void 0 ? void 0 : existing.entries()); } keys() { return this._map.keys(); } entries() { return this._map.entries(); } get(k) { return this._map.get(k); } has(k) { return this._map.has(k); } set(k, v) { this._map.set(k, v); return this; } delete(k) { this._map.delete(k); return this; } clone() { return persistentMap(this); } toMap() { return new Map(this._map); } } class HashArrayMappedTrieMap { // Because hamt.empty is not a function there is no way to introduce type // parameters on it, so empty is typed as HAMTPlusMap<string, mixed>. // $FlowIssue constructor(existing) { _defineProperty(this, "_hamt", hamt_1.empty.beginMutation()); if (existing instanceof HashArrayMappedTrieMap) { const h = existing._hamt.endMutation(); existing._hamt = h.beginMutation(); this._hamt = h.beginMutation(); } else if (existing) { for (const [k, v] of existing.entries()) { this._hamt.set(k, v); } } } keys() { return this._hamt.keys(); } entries() { return this._hamt.entries(); } get(k) { return this._hamt.get(k); } has(k) { return this._hamt.has(k); } set(k, v) { this._hamt.set(k, v); return this; } delete(k) { this._hamt.delete(k); return this; } clone() { return persistentMap(this); } toMap() { return new Map(this._hamt); } } function persistentMap(existing) { if (Recoil_gkx('recoil_hamt_2020')) { return new HashArrayMappedTrieMap(existing); } else { return new BuiltInMap(existing); } } var Recoil_PersistentMap = { persistentMap }; var Recoil_PersistentMap_1 = Recoil_PersistentMap.persistentMap; var Recoil_PersistentMap$1 = /*#__PURE__*/Object.freeze({ __proto__: null, persistentMap: Recoil_PersistentMap_1 }); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ /** * 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) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format * @oncall recoil */ /** * 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 makeGraph() { 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 mergeDepsIntoGraph(key, newDeps, graph, // If olderGraph is given then we will not overwrite changes made to the given // graph compared with olderGraph: olderGraph) { const { nodeDeps, nodeToNodeSubscriptions } = graph; const oldDeps = nodeDeps.get(key); if (oldDeps && olderGraph && oldDeps !== olderGraph.nodeDeps.get(key)) { return; } // Update nodeDeps: nodeDeps.set(key, newDeps); // Add new deps to nodeToNodeSubscriptions: const addedDeps = oldDeps == null ? newDeps : Recoil_differenceSets(newDeps, oldDeps); for (const dep of addedDeps) { if (!nodeToNodeSubscriptions.has(dep)) { nodeToNodeSubscriptions.set(dep, new Set()); } const existing = Recoil_nullthrows(nodeToNodeSubscriptions.get(dep)); existing.add(