text: /**
   * 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.
   *
   * 
   * @format
   */
  // flowlint ambiguous-object-type:error
  'use strict';
  
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  
  var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
  
  var RelayFeatureFlags = require('../util/RelayFeatureFlags');
  
  var areEqual = require("fbjs/lib/areEqual");
  
  var invariant = require('invariant');
  
  var isScalarAndEqual = require('../util/isScalarAndEqual');
  
  var reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
  
  var warning = require("fbjs/lib/warning");
  
  var _require = require('../query/fetchQueryInternal'),
      getPromiseForActiveRequest = _require.getPromiseForActiveRequest;
  
  var _require2 = require('./RelayModernOperationDescriptor'),
      createRequestDescriptor = _require2.createRequestDescriptor;
  
  var _require3 = require('./RelayModernSelector'),
      areEqualSelectors = _require3.areEqualSelectors,
      createReaderSelector = _require3.createReaderSelector,
      getSelectorsFromObject = _require3.getSelectorsFromObject;
  
  /**
   * A utility for resolving and subscribing to the results of a fragment spec
   * (key -> fragment mapping) given some "props" that determine the root ID
   * and variables to use when reading each fragment. When props are changed via
   * `setProps()`, the resolver will update its results and subscriptions
   * accordingly. Internally, the resolver:
   * - Converts the fragment map & props map into a map of `Selector`s.
   * - Removes any resolvers for any props that became null.
   * - Creates resolvers for any props that became non-null.
   * - Updates resolvers with the latest props.
   *
   * This utility is implemented as an imperative, stateful API for performance
   * reasons: reusing previous resolvers, callback functions, and subscriptions
   * all helps to reduce object allocation and thereby decrease GC time.
   *
   * The `resolve()` function is also lazy and memoized: changes in the store mark
   * the resolver as stale and notify the caller, and the actual results are
   * recomputed the first time `resolve()` is called.
   */
  var RelayModernFragmentSpecResolver = /*#__PURE__*/function () {
    function RelayModernFragmentSpecResolver(context, fragments, props, callback, rootIsQueryRenderer) {
      var _this = this;
  
      (0, _defineProperty2["default"])(this, "_onChange", function () {
        _this._stale = true;
  
        if (typeof _this._callback === 'function') {
          _this._callback();
        }
      });
      this._callback = callback;
      this._context = context;
      this._data = {};
      this._fragments = fragments;
      this._props = {};
      this._resolvers = {};
      this._stale = false;
      this._rootIsQueryRenderer = rootIsQueryRenderer;
      this.setProps(props);
    }
  
    var _proto = RelayModernFragmentSpecResolver.prototype;
  
    _proto.dispose = function dispose() {
      for (var _key in this._resolvers) {
        if (this._resolvers.hasOwnProperty(_key)) {
          disposeCallback(this._resolvers[_key]);
        }
      }
    };
  
    _proto.resolve = function resolve() {
      if (this._stale) {
        // Avoid mapping the object multiple times, which could occur if data for
        // multiple keys changes in the same event loop.
        var prevData = this._data;
        var nextData;
  
        for (var _key2 in this._resolvers) {
          if (this._resolvers.hasOwnProperty(_key2)) {
            var resolver = this._resolvers[_key2];
            var prevItem = prevData[_key2];
  
            if (resolver) {
              var nextItem = resolver.resolve();
  
              if (nextData || nextItem !== prevItem) {
                nextData = nextData || (0, _objectSpread2["default"])({}, prevData);
                nextData[_key2] = nextItem;
              }
            } else {
              var prop = this._props[_key2];
  
              var _nextItem = prop !== undefined ? prop : null;
  
              if (nextData || !isScalarAndEqual(_nextItem, prevItem)) {
                nextData = nextData || (0, _objectSpread2["default"])({}, prevData);
                nextData[_key2] = _nextItem;
              }
            }
          }
        }
  
        this._data = nextData || prevData;
        this._stale = false;
      }
  
      return this._data;
    };
  
    _proto.setCallback = function setCallback(callback) {
      this._callback = callback;
    };
  
    _proto.setProps = function setProps(props) {
      var ownedSelectors = getSelectorsFromObject(this._fragments, props);
      this._props = {};
  
      for (var _key3 in ownedSelectors) {
        if (ownedSelectors.hasOwnProperty(_key3)) {
          var ownedSelector = ownedSelectors[_key3];
          var resolver = this._resolvers[_key3];
  
          if (ownedSelector == null) {
            if (resolver != null) {
              resolver.dispose();
            }
  
            resolver = null;
          } else if (ownedSelector.kind === 'PluralReaderSelector') {
            if (resolver == null) {
              resolver = new SelectorListResolver(this._context.environment, this._rootIsQueryRenderer, ownedSelector, this._onChange);
            } else {
              !(resolver instanceof SelectorListResolver) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an array.', _key3) : invariant(false) : void 0;
              resolver.setSelector(ownedSelector);
            }
          } else {
            if (resolver == null) {
              resolver = new SelectorResolver(this._context.environment, this._rootIsQueryRenderer, ownedSelector, this._onChange);
            } else {
              !(resolver instanceof SelectorResolver) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an object.', _key3) : invariant(false) : void 0;
              resolver.setSelector(ownedSelector);
            }
          }
  
          this._props[_key3] = props[_key3];
          this._resolvers[_key3] = resolver;
        }
      }
  
      this._stale = true;
    };
  
    _proto.setVariables = function setVariables(variables, request) {
      for (var _key4 in this._resolvers) {
        if (this._resolvers.hasOwnProperty(_key4)) {
          var resolver = this._resolvers[_key4];
  
          if (resolver) {
            resolver.setVariables(variables, request);
          }
        }
      }
  
      this._stale = true;
    };
  
    return RelayModernFragmentSpecResolver;
  }();
  /**
   * A resolver for a single Selector.
   */
  
  
  var SelectorResolver = /*#__PURE__*/function () {
    function SelectorResolver(environment, rootIsQueryRenderer, selector, callback) {
      var _this2 = this;
  
      (0, _defineProperty2["default"])(this, "_onChange", function (snapshot) {
        _this2._data = snapshot.data;
        _this2._isMissingData = snapshot.isMissingData;
        _this2._missingRequiredFields = snapshot.missingRequiredFields;
  
        _this2._callback();
      });
  
      var _snapshot = environment.lookup(selector);
  
      this._callback = callback;
      this._data = _snapshot.data;
      this._isMissingData = _snapshot.isMissingData;
      this._missingRequiredFields = _snapshot.missingRequiredFields;
      this._environment = environment;
      this._rootIsQueryRenderer = rootIsQueryRenderer;
      this._selector = selector;
      this._subscription = environment.subscribe(_snapshot, this._onChange);
    }
  
    var _proto2 = SelectorResolver.prototype;
  
    _proto2.dispose = function dispose() {
      if (this._subscription) {
        this._subscription.dispose();
  
        this._subscription = null;
      }
    };
  
    _proto2.resolve = function resolve() {
      if (RelayFeatureFlags.ENABLE_RELAY_CONTAINERS_SUSPENSE === true && this._isMissingData === true) {
        var _getPromiseForActiveR;
  
        // NOTE: This branch exists to handle the case in which:
        // - A RelayModern container is rendered as a descendant of a Relay Hook
        //   root using a "partial" renderPolicy (this means that eargerly
        //   reading any cached data that is available instead of blocking
        //   at the root until the whole query is fetched).
        // - A parent Relay Hook didnt' suspend earlier on data being fetched,
        //   either because the fragment data for the parent was available, or
        //   the parent fragment didn't have any data dependencies.
        // Even though our Flow types reflect the possiblity of null data, there
        // might still be cases where it's not handled at runtime becuase the
        // Flow types are being ignored, or simply not being used (for example,
        // the case reported here: https://fburl.com/srnbucf8, was due to
        // misuse of Flow types here: https://fburl.com/g3m0mqqh).
        // Additionally, even though the null data might be handled without a
        // runtime error, we might not suspend when we intended to if a parent
        // Relay Hook (e.g. that is using @defer) decided not to suspend becuase
        // it's immediate data was already available (even if it was deferred),
        // or it didn't actually need any data (was just spreading other fragments).
        // This should eventually go away with something like @optional, where we only
        // suspend at specific boundaries depending on whether the boundary
        // can be fulfilled or not.
        var promise = (_getPromiseForActiveR = getPromiseForActiveRequest(this._environment, this._selector.owner)) !== null && _getPromiseForActiveR !== void 0 ? _getPromiseForActiveR : this._environment.getOperationTracker().getPromiseForPendingOperationsAffectingOwner(this._selector.owner);
  
        if (promise != null) {
          if (this._rootIsQueryRenderer) {
            process.env.NODE_ENV !== "production" ? warning(false, 'Relay: Relay Container for fragment `%s` has missing data and ' + 'would suspend. When using features such as @defer or @module, ' + 'use `useFragment` instead of a Relay Container.', this._selector.node.name) : void 0;
          } else {
            process.env.NODE_ENV !== "production" ? warning(false, 'Relay: Relay Container for fragment `%s` suspended. When using ' + 'features such as @defer or @module, use `useFragment` instead ' + 'of a Relay Container.', this._selector.node.name) : void 0;
            throw promise;
          }
        }
      }
  
      if (this._missingRequiredFields != null) {
        reportMissingRequiredFields(this._environment, this._missingRequiredFields);
      }
  
      return this._data;
    };
  
    _proto2.setSelector = function setSelector(selector) {
      if (this._subscription != null && areEqualSelectors(selector, this._selector)) {
        return;
      }
  
      this.dispose();
  
      var snapshot = this._environment.lookup(selector);
  
      this._data = snapshot.data;
      this._isMissingData = snapshot.isMissingData;
      this._missingRequiredFields = snapshot.missingRequiredFields;
      this._selector = selector;
      this._subscription = this._environment.subscribe(snapshot, this._onChange);
    };
  
    _proto2.setVariables = function setVariables(variables, request) {
      if (areEqual(variables, this._selector.variables)) {
        // If we're not actually setting new variables, we don't actually want
        // to create a new fragment owner, since areEqualSelectors relies on
        // owner identity.
        // In fact, we don't even need to try to attempt to set a new selector.
        // When fragment ownership is not enabled, setSelector will also bail
        // out since the selector doesn't really change, so we're doing it here
        // earlier.
        return;
      } // NOTE: We manually create the request descriptor here instead of
      // calling createOperationDescriptor() because we want to set a
      // descriptor with *unaltered* variables as the fragment owner.
      // This is a hack that allows us to preserve existing (broken)
      // behavior of RelayModern containers while using fragment ownership
      // to propagate variables instead of Context.
      // For more details, see the summary of D13999308
  
  
      var requestDescriptor = createRequestDescriptor(request, variables);
      var selector = createReaderSelector(this._selector.node, this._selector.dataID, variables, requestDescriptor);
      this.setSelector(selector);
    };
  
    return SelectorResolver;
  }();
  /**
   * A resolver for an array of Selectors.
   */
  
  
  var SelectorListResolver = /*#__PURE__*/function () {
    function SelectorListResolver(environment, rootIsQueryRenderer, selector, callback) {
      var _this3 = this;
  
      (0, _defineProperty2["default"])(this, "_onChange", function (data) {
        _this3._stale = true;
  
        _this3._callback();
      });
      this._callback = callback;
      this._data = [];
      this._environment = environment;
      this._resolvers = [];
      this._stale = true;
      this._rootIsQueryRenderer = rootIsQueryRenderer;
      this.setSelector(selector);
    }
  
    var _proto3 = SelectorListResolver.prototype;
  
    _proto3.dispose = function dispose() {
      this._resolvers.forEach(disposeCallback);
    };
  
    _proto3.resolve = function resolve() {
      if (this._stale) {
        // Avoid mapping the array multiple times, which could occur if data for
        // multiple indices changes in the same event loop.
        var prevData = this._data;
        var nextData;
  
        for (var ii = 0; ii < this._resolvers.length; ii++) {
          var prevItem = prevData[ii];
  
          var nextItem = this._resolvers[ii].resolve();
  
          if (nextData || nextItem !== prevItem) {
            nextData = nextData || prevData.slice(0, ii);
            nextData.push(nextItem);
          }
        }
  
        if (!nextData && this._resolvers.length !== prevData.length) {
          nextData = prevData.slice(0, this._resolvers.length);
        }
  
        this._data = nextData || prevData;
        this._stale = false;
      }
  
      return this._data;
    };
  
    _proto3.setSelector = function setSelector(selector) {
      var selectors = selector.selectors;
  
      while (this._resolvers.length > selectors.length) {
        var resolver = this._resolvers.pop();
  
        resolver.dispose();
      }
  
      for (var ii = 0; ii < selectors.length; ii++) {
        if (ii < this._resolvers.length) {
          this._resolvers[ii].setSelector(selectors[ii]);
        } else {
          this._resolvers[ii] = new SelectorResolver(this._environment, this._rootIsQueryRenderer, selectors[ii], this._onChange);
        }
      }
  
      this._stale = true;
    };
  
    _proto3.setVariables = function setVariables(variables, request) {
      this._resolvers.forEach(function (resolver) {
        return resolver.setVariables(variables, request);
      });
  
      this._stale = true;
    };
  
    return SelectorListResolver;
  }();
  
  function disposeCallback(disposable) {
    disposable && disposable.dispose();
  }
  
  module.exports = RelayModernFragmentSpecResolver;