/** * 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. * * strict-local * @format */ 'use strict'; /** * Coordinates the concurrent modification of a `Store` due to optimistic and * non-revertable client updates and server payloads: * - Applies optimistic updates. * - Reverts optimistic updates, rebasing any subsequent updates. * - Commits client updates (typically for client schema extensions). * - Commits server updates: * - Normalizes query/mutation/subscription responses. * - Executes handlers for "handle" fields. * - Reverts and reapplies pending optimistic updates. */ var RelayPublishQueue = /*#__PURE__*/ function () { // A "negative" of all applied updaters. It can be published to the store to // undo them in order to re-apply some of them for a rebase. // True if the next `run()` should apply the backup and rerun all optimistic // updates performing a rebase. // Payloads to apply or Sources to publish to the store with the next `run()`. // Updaters to apply with the next `run()`. These mutate the store and should // typically only mutate client schema extensions. // Optimistic updaters to add with the next `run()`. // Optimistic updaters that are already added and might be rerun in order to // rebase them. // Garbage collection hold, should rerun gc on dispose function RelayPublishQueue(store, handlerProvider) { this._backup = new (require("./RelayInMemoryRecordSource"))(); this._handlerProvider = handlerProvider || null; this._pendingBackupRebase = false; this._pendingUpdaters = new Set(); this._pendingData = new Set(); this._pendingOptimisticUpdates = new Set(); this._store = store; this._appliedOptimisticUpdates = new Set(); this._gcHold = null; } /** * Schedule applying an optimistic updates on the next `run()`. */ var _proto = RelayPublishQueue.prototype; _proto.applyUpdate = function applyUpdate(updater) { !(!this._appliedOptimisticUpdates.has(updater) && !this._pendingOptimisticUpdates.has(updater)) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayPublishQueue: Cannot apply the same update function more than ' + 'once concurrently.') : require("fbjs/lib/invariant")(false) : void 0; this._pendingOptimisticUpdates.add(updater); }; /** * Schedule reverting an optimistic updates on the next `run()`. */ _proto.revertUpdate = function revertUpdate(updater) { if (this._pendingOptimisticUpdates.has(updater)) { // Reverted before it was applied this._pendingOptimisticUpdates["delete"](updater); } else if (this._appliedOptimisticUpdates.has(updater)) { this._pendingBackupRebase = true; this._appliedOptimisticUpdates["delete"](updater); } }; /** * Schedule a revert of all optimistic updates on the next `run()`. */ _proto.revertAll = function revertAll() { this._pendingBackupRebase = true; this._pendingOptimisticUpdates.clear(); this._appliedOptimisticUpdates.clear(); }; /** * Schedule applying a payload to the store on the next `run()`. */ _proto.commitPayload = function commitPayload(operation, _ref, updater) { var fieldPayloads = _ref.fieldPayloads, source = _ref.source; this._pendingBackupRebase = true; this._pendingData.add({ kind: 'payload', payload: { fieldPayloads: fieldPayloads, operation: operation, source: source, updater: updater } }); }; _proto.commitRelayPayload = function commitRelayPayload(_ref2) { var fieldPayloads = _ref2.fieldPayloads, source = _ref2.source; this._pendingBackupRebase = true; this._pendingData.add({ kind: 'payload', payload: { fieldPayloads: fieldPayloads, operation: null, source: source, updater: null } }); }; /** * Schedule an updater to mutate the store on the next `run()` typically to * update client schema fields. */ _proto.commitUpdate = function commitUpdate(updater) { this._pendingBackupRebase = true; this._pendingUpdaters.add(updater); }; /** * Schedule a publish to the store from the provided source on the next * `run()`. As an example, to update the store with substituted fields that * are missing in the store. */ _proto.commitSource = function commitSource(source) { this._pendingBackupRebase = true; this._pendingData.add({ kind: 'source', source: source }); }; /** * Execute all queued up operations from the other public methods. */ _proto.run = function run() { if (this._pendingBackupRebase && this._backup.size()) { this._store.publish(this._backup); this._backup = new (require("./RelayInMemoryRecordSource"))(); } this._commitData(); this._commitUpdaters(); this._applyUpdates(); this._pendingBackupRebase = false; if (this._appliedOptimisticUpdates.size > 0) { if (!this._gcHold) { this._gcHold = this._store.holdGC(); } } else { if (this._gcHold) { this._gcHold.dispose(); this._gcHold = null; } } this._store.notify(); }; _proto._getSourceFromPayload = function _getSourceFromPayload(payload) { var _this = this; var fieldPayloads = payload.fieldPayloads, operation = payload.operation, source = payload.source, updater = payload.updater; var mutator = new (require("./RelayRecordSourceMutator"))(this._store.getSource(), source); var store = new (require("./RelayRecordSourceProxy"))(mutator); if (fieldPayloads && fieldPayloads.length) { fieldPayloads.forEach(function (fieldPayload) { var handler = _this._handlerProvider && _this._handlerProvider(fieldPayload.handle); !handler ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernEnvironment: Expected a handler to be provided for ' + 'handle `%s`.', fieldPayload.handle) : require("fbjs/lib/invariant")(false) : void 0; handler.update(store, fieldPayload); }); } if (updater) { var selector = operation === null || operation === void 0 ? void 0 : operation.fragment; !(selector != null) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernEnvironment: Expected a selector to be provided with updater function.') : require("fbjs/lib/invariant")(false) : void 0; var selectorStore = new (require("./RelayRecordSourceSelectorProxy"))(store, selector); var selectorData = lookupSelector(source, selector, operation); updater(selectorStore, selectorData); } return source; }; _proto._commitData = function _commitData() { var _this2 = this; if (!this._pendingData.size) { return; } this._pendingData.forEach(function (data) { var source; if (data.kind === 'payload') { source = _this2._getSourceFromPayload(data.payload); } else { source = data.source; } _this2._store.publish(source); }); this._pendingData.clear(); }; _proto._commitUpdaters = function _commitUpdaters() { var _this3 = this; if (!this._pendingUpdaters.size) { return; } var sink = new (require("./RelayInMemoryRecordSource"))(); this._pendingUpdaters.forEach(function (updater) { var mutator = new (require("./RelayRecordSourceMutator"))(_this3._store.getSource(), sink); var store = new (require("./RelayRecordSourceProxy"))(mutator); require("fbjs/lib/ErrorUtils").applyWithGuard(updater, null, [store], null, 'RelayPublishQueue:commitUpdaters'); }); this._store.publish(sink); this._pendingUpdaters.clear(); }; _proto._applyUpdates = function _applyUpdates() { var _this4 = this; if (this._pendingOptimisticUpdates.size || this._pendingBackupRebase && this._appliedOptimisticUpdates.size) { var sink = new (require("./RelayInMemoryRecordSource"))(); var mutator = new (require("./RelayRecordSourceMutator"))(this._store.getSource(), sink, this._backup); var store = new (require("./RelayRecordSourceProxy"))(mutator, this._handlerProvider); // rerun all updaters in case we are running a rebase if (this._pendingBackupRebase && this._appliedOptimisticUpdates.size) { this._appliedOptimisticUpdates.forEach(function (optimisticUpdate) { if (optimisticUpdate.operation) { var selectorStoreUpdater = optimisticUpdate.selectorStoreUpdater, operation = optimisticUpdate.operation, response = optimisticUpdate.response; var selectorStore = store.commitPayload(operation, response); // TODO: Fix commitPayload so we don't have to run normalize twice var selectorData, source; if (response) { var _normalizeRelayPayloa = require("./normalizeRelayPayload")(operation.root, response); source = _normalizeRelayPayloa.source; selectorData = lookupSelector(source, operation.fragment, operation); } selectorStoreUpdater && require("fbjs/lib/ErrorUtils").applyWithGuard(selectorStoreUpdater, null, [selectorStore, selectorData], null, 'RelayPublishQueue:applyUpdates'); } else if (optimisticUpdate.storeUpdater) { var storeUpdater = optimisticUpdate.storeUpdater; require("fbjs/lib/ErrorUtils").applyWithGuard(storeUpdater, null, [store], null, 'RelayPublishQueue:applyUpdates'); } else { var _source = optimisticUpdate.source, fieldPayloads = optimisticUpdate.fieldPayloads; store.publishSource(_source, fieldPayloads); } }); } // apply any new updaters if (this._pendingOptimisticUpdates.size) { this._pendingOptimisticUpdates.forEach(function (optimisticUpdate) { if (optimisticUpdate.operation) { var selectorStoreUpdater = optimisticUpdate.selectorStoreUpdater, operation = optimisticUpdate.operation, response = optimisticUpdate.response; var selectorStore = store.commitPayload(operation, response); // TODO: Fix commitPayload so we don't have to run normalize twice var selectorData, source; if (response) { var _normalizeRelayPayloa2 = require("./normalizeRelayPayload")(operation.root, response); source = _normalizeRelayPayloa2.source; selectorData = lookupSelector(source, operation.fragment, operation); } selectorStoreUpdater && require("fbjs/lib/ErrorUtils").applyWithGuard(selectorStoreUpdater, null, [selectorStore, selectorData], null, 'RelayPublishQueue:applyUpdates'); } else if (optimisticUpdate.storeUpdater) { var storeUpdater = optimisticUpdate.storeUpdater; require("fbjs/lib/ErrorUtils").applyWithGuard(storeUpdater, null, [store], null, 'RelayPublishQueue:applyUpdates'); } else { var _source2 = optimisticUpdate.source, fieldPayloads = optimisticUpdate.fieldPayloads; store.publishSource(_source2, fieldPayloads); } _this4._appliedOptimisticUpdates.add(optimisticUpdate); }); this._pendingOptimisticUpdates.clear(); } this._store.publish(sink); } }; return RelayPublishQueue; }(); function lookupSelector(source, selector, owner) { var selectorData = require("./RelayReader").read(source, selector, owner).data; if (process.env.NODE_ENV !== "production") { var deepFreeze = require("./deepFreeze"); if (selectorData) { deepFreeze(selectorData); } } return selectorData; } module.exports = RelayPublishQueue;