text: "use strict";
  
  exports.__esModule = true;
  exports.postIndexingMetaSetup = postIndexingMetaSetup;
  exports.getNodesFromCacheByValue = exports.ensureIndexByElemMatch = exports.ensureIndexByQuery = exports.addResolvedNodes = exports.getResolvedNode = exports.saveResolvedNodes = exports.getNodeAndSavePathDependency = exports.hasNodeChanged = exports.getTypes = exports.getNodesByType = exports.getNode = exports.getNodes = void 0;
  
  var _ = require("./");
  
  var _addPageDependency = require("./actions/add-page-dependency");
  
  /**
   * Get all nodes from redux store.
   */
  const getNodes = () => {
    const nodes = _.store.getState().nodes;
  
    if (nodes) {
      return Array.from(nodes.values());
    } else {
      return [];
    }
  };
  /**
   * Get node by id from store.
   */
  
  
  exports.getNodes = getNodes;
  
  const getNode = id => _.store.getState().nodes.get(id);
  /**
   * Get all nodes of type from redux store.
   */
  
  
  exports.getNode = getNode;
  
  const getNodesByType = type => {
    const nodes = _.store.getState().nodesByType.get(type);
  
    if (nodes) {
      return Array.from(nodes.values());
    } else {
      return [];
    }
  };
  /**
   * Get all type names from redux store.
   */
  
  
  exports.getNodesByType = getNodesByType;
  
  const getTypes = () => Array.from(_.store.getState().nodesByType.keys());
  /**
   * Determine if node has changed.
   */
  
  
  exports.getTypes = getTypes;
  
  const hasNodeChanged = (id, digest) => {
    const node = _.store.getState().nodes.get(id);
  
    if (!node) {
      return true;
    } else {
      return node.internal.contentDigest !== digest;
    }
  };
  /**
   * Get node and save path dependency.
   */
  
  
  exports.hasNodeChanged = hasNodeChanged;
  
  const getNodeAndSavePathDependency = (id, path) => {
    const node = getNode(id);
  
    if (!node) {
      console.error(`getNodeAndSavePathDependency failed for node id: ${id} as it was not found in cache`);
      return undefined;
    }
  
    (0, _addPageDependency.createPageDependency)({
      path,
      nodeId: id
    });
    return node;
  };
  
  exports.getNodeAndSavePathDependency = getNodeAndSavePathDependency;
  
  // TODO
  const saveResolvedNodes = async (nodeTypeNames, resolver) => {
    for (const typeName of nodeTypeNames) {
      const nodes = _.store.getState().nodesByType.get(typeName);
  
      if (!nodes) return;
      const resolvedNodes = new Map();
  
      for (const node of nodes.values()) {
        const resolved = await resolver(node);
        resolvedNodes.set(node.id, resolved);
      }
  
      _.store.dispatch({
        type: `SET_RESOLVED_NODES`,
        payload: {
          key: typeName,
          nodes: resolvedNodes
        }
      });
    }
  };
  /**
   * Get node and save path dependency.
   */
  
  
  exports.saveResolvedNodes = saveResolvedNodes;
  
  const getResolvedNode = (typeName, id) => {
    const {
      nodesByType,
      resolvedNodesCache
    } = _.store.getState();
  
    const nodes = nodesByType.get(typeName);
  
    if (!nodes) {
      return null;
    }
  
    const node = nodes.get(id);
  
    if (!node) {
      return null;
    }
  
    const resolvedNodes = resolvedNodesCache.get(typeName);
  
    if (resolvedNodes) {
      node.__gatsby_resolved = resolvedNodes.get(id);
    }
  
    return node;
  };
  
  exports.getResolvedNode = getResolvedNode;
  
  const addResolvedNodes = (typeName, resolvedNodes = []) => {
    const {
      nodesByType,
      resolvedNodesCache
    } = _.store.getState();
  
    const nodes = nodesByType.get(typeName);
  
    if (!nodes) {
      return [];
    }
  
    const resolvedNodesFromCache = resolvedNodesCache.get(typeName);
    nodes.forEach(node => {
      if (resolvedNodesFromCache) {
        node.__gatsby_resolved = resolvedNodesFromCache.get(node.id);
      }
  
      resolvedNodes.push(node);
    });
    return resolvedNodes;
  };
  
  exports.addResolvedNodes = addResolvedNodes;
  
  function postIndexingMetaSetup(filterCache, op) {
    if (op === `$ne` || op === `$nin`) {
      postIndexingMetaSetupNeNin(filterCache);
    } else if ([`$lt`, `$lte`, `$gt`, `$gte`].includes(op)) {
      postIndexingMetaSetupLtLteGtGte(filterCache, op);
    }
  }
  
  function postIndexingMetaSetupNeNin(filterCache) {
    // Note: edge cases regarding `null` and `undefined`. Here `undefined` signals
    // that the property did not exist as sift does not support actual `undefined`
    // values.
    // For $ne, `null` only returns nodes that actually have the property
    // and in that case the property cannot be `null` either. For any other value,
    // $ne will return all nodes where the value is not actually the needle,
    // including nodes where the value is null.
    // A $nin does the same as an $ne except it filters multiple values instead
    // of just one.
    // For `$ne` we will take the list of all targeted nodes and eliminate the
    // bucket of nodes with a particular value, if it exists at all. So for that
    // reason we construct a flat list here to create new Set instances from.
    const arr = [];
    filterCache.meta.nodesUnordered = arr;
    filterCache.byValue.forEach(v => {
      v.forEach(node => {
        arr.push(node);
      });
    });
  }
  
  function postIndexingMetaSetupLtLteGtGte(filterCache, op) {
    // Create an ordered array of individual nodes, ordered (grouped) by the
    // value to which the filter resolves. Nodes are not ordered per value.
    // This way non-eq ops can simply slice the array to get a range.
    const entriesNullable = [...filterCache.byValue.entries()]; // These range checks never return `null` or `undefined` so filter those out
    // By filtering them out early, the sort should be faster. Could be ...
  
    const entries = entriesNullable.filter(([v]) => v != null); // Sort all sets by its value, asc. Ignore/allow potential type casting.
    // Note: while `<` is the inverse of `>=`, the ordering might coerce values.
    // This coercion makes the op no longer idempotent (normally the result of
    // `a < b` is the opposite of `b >= a` for any a or b of the same type). The
    // exception is a number that is `NaN`, which we're ignoring here as it's most
    // likely a bug in the user code. However, when coercing the ops may end up
    // comparing against `NaN`, too. For example: `("abc" <= 12) !== (12 > "abc")`
    // which ends up doing `NaN <= 12` and `NaN > "abc"`, which will both yield
    // false.
    // So instead we potentially track two ordered lists; ascending and descending
    // and the only difference when comparing the inverse of one to the other
    // should be how these `NaN` cases end up getting ordered.
    // It's fine for `lt` and `lte` to use the same ordered set. Same for gt/gte.
  
    if (op === `$lt` || op === `$lte`) {
      // Order ascending; first value is lowest
      entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
    } else if (op === `$gt` || op === `$gte`) {
      // Order descending; first value is highest
      entries.sort(([a], [b]) => a > b ? -1 : a < b ? 1 : 0);
    }
  
    const orderedNodes = [];
    const orderedValues = [];
    const offsets = new Map();
    entries.forEach(([v, bucket]) => {
      // Record the range containing all nodes with as filter value v
      // The last value of the range should be the offset of the next value
      // (So you should be able to do `nodes.slice(start, stop)` to get them)
      offsets.set(v, [orderedNodes.length, orderedNodes.length + bucket.size]); // We could do `arr.push(...bucket)` here but that's not safe with very
      // large sets, so we use a regular loop
  
      bucket.forEach(node => orderedNodes.push(node));
      orderedValues.push(v);
    });
  
    if (op === `$lt` || op === `$lte`) {
      filterCache.meta.valuesAsc = orderedValues;
      filterCache.meta.nodesByValueAsc = orderedNodes; // The nodesByValueAsc is ordered by value, but multiple nodes per value are
      // not ordered. To make lt as fast as lte, we must know the start and stop
      // index for each value. Similarly useful for for `ne`.
  
      filterCache.meta.valueRangesAsc = offsets;
    } else if (op === `$gt` || op === `$gte`) {
      filterCache.meta.valuesDesc = orderedValues;
      filterCache.meta.nodesByValueDesc = orderedNodes; // The nodesByValueDesc is ordered by value, but multiple nodes per value are
      // not ordered. To make gt as fast as gte, we must know the start and stop
      // index for each value. Similarly useful for for `ne`.
  
      filterCache.meta.valueRangesDesc = offsets;
    }
  }
  /**
   * Given a single non-elemMatch filter path, a set of node types, and a
   * cache, create a cache that for each resulting value of the filter contains
   * all the Nodes in a Set.
   * This cache is used for applying the filter and is a massive improvement over
   * looping over all the nodes, when the number of pages (/nodes) scales up.
   */
  
  
  const ensureIndexByQuery = (op, filterCacheKey, filterPath, nodeTypeNames, filtersCache) => {
    const state = _.store.getState();
  
    const resolvedNodesCache = state.resolvedNodesCache;
    const filterCache = {
      op,
      byValue: new Map(),
      meta: {}
    };
    filtersCache.set(filterCacheKey, filterCache); // We cache the subsets of nodes by type, but only one type. So if searching
    // through one node type we can prevent a search through all nodes, otherwise
    // it's probably faster to loop through all nodes. Perhaps. Maybe.
  
    if (nodeTypeNames.length === 1) {
      getNodesByType(nodeTypeNames[0]).forEach(node => {
        addNodeToFilterCache(node, filterPath, filterCache, resolvedNodesCache);
      });
    } else {
      // Here we must first filter for the node type
      // This loop is expensive at scale (!)
      state.nodes.forEach(node => {
        if (!nodeTypeNames.includes(node.internal.type)) {
          return;
        }
  
        addNodeToFilterCache(node, filterPath, filterCache, resolvedNodesCache);
      });
    }
  
    postIndexingMetaSetup(filterCache, op);
  };
  
  exports.ensureIndexByQuery = ensureIndexByQuery;
  
  function addNodeToFilterCache(node, chain, filterCache, resolvedNodesCache, valueOffset = node) {
    // There can be a filter that targets `__gatsby_resolved` so fix that first
    if (!node.__gatsby_resolved) {
      const typeName = node.internal.type;
      const resolvedNodes = resolvedNodesCache.get(typeName);
      node.__gatsby_resolved = resolvedNodes === null || resolvedNodes === void 0 ? void 0 : resolvedNodes.get(node.id);
    } // - for plain query, valueOffset === node
    // - for elemMatch, valueOffset is sub-tree of the node to continue matching
  
  
    let v = valueOffset;
    let i = 0;
  
    while (i < chain.length && v) {
      const nextProp = chain[i++];
      v = v[nextProp];
    }
  
    if (typeof v !== `string` && typeof v !== `number` && typeof v !== `boolean` && v !== null || i !== chain.length) {
      if (i === chain.length && Array.isArray(v)) {
        // The op resolved to an array
        // Add an entry for each element of the array. This would work for ops
        // like eq and ne, but not sure about range ops like lt,lte,gt,gte.
        v.forEach(v => markNodeForValue(filterCache, node, v));
        return;
      } // This means that either
      // - The filter resolved to `undefined`, or
      // - The filter resolved to something other than a primitive
      // Set the value to `undefined` to mark "path does not (fully) exist"
  
  
      v = undefined;
    }
  
    markNodeForValue(filterCache, node, v);
  }
  
  function markNodeForValue(filterCache, node, value) {
    let set = filterCache.byValue.get(value);
  
    if (!set) {
      set = new Set();
      filterCache.byValue.set(value, set);
    }
  
    set.add(node);
  }
  
  const ensureIndexByElemMatch = (op, filterCacheKey, filter, nodeTypeNames, filtersCache) => {
    // Given an elemMatch filter, generate the cache that contains all nodes that
    // matches a given value for that sub-query
    const state = _.store.getState();
  
    const {
      resolvedNodesCache
    } = state;
    const filterCache = {
      op,
      byValue: new Map(),
      meta: {}
    };
    filtersCache.set(filterCacheKey, filterCache);
  
    if (nodeTypeNames.length === 1) {
      getNodesByType(nodeTypeNames[0]).forEach(node => {
        addNodeToBucketWithElemMatch(node, node, filter, filterCache, resolvedNodesCache);
      });
    } else {
      // Expensive at scale
      state.nodes.forEach(node => {
        if (!nodeTypeNames.includes(node.internal.type)) {
          return;
        }
  
        addNodeToBucketWithElemMatch(node, node, filter, filterCache, resolvedNodesCache);
      });
    }
  
    postIndexingMetaSetup(filterCache, op);
  };
  
  exports.ensureIndexByElemMatch = ensureIndexByElemMatch;
  
  function addNodeToBucketWithElemMatch(node, valueAtCurrentStep, // Arbitrary step on the path inside the node
  filter, filterCache, resolvedNodesCache) {
    // There can be a filter that targets `__gatsby_resolved` so fix that first
    if (!node.__gatsby_resolved) {
      const typeName = node.internal.type;
      const resolvedNodes = resolvedNodesCache.get(typeName);
      node.__gatsby_resolved = resolvedNodes === null || resolvedNodes === void 0 ? void 0 : resolvedNodes.get(node.id);
    }
  
    const {
      path,
      nestedQuery
    } = filter; // Find the value to apply elemMatch to
  
    let i = 0;
  
    while (i < path.length && valueAtCurrentStep) {
      const nextProp = path[i++];
      valueAtCurrentStep = valueAtCurrentStep[nextProp];
    }
  
    if (path.length !== i) {
      // Found undefined before the end of the path, so let Sift take over
      return;
    } // `v` should now be an elemMatch target, probably an array (but maybe not)
  
  
    if (!Array.isArray(valueAtCurrentStep)) {
      // It's possible to `elemMatch` on a non-array so let's support that too
      valueAtCurrentStep = [valueAtCurrentStep];
    } // Note: We need to check all elements because the node may need to be added
    // to multiple buckets (`{a:[{b:3},{b:4}]}`, for `a.elemMatch.b/eq` that
    // node ends up in buckets for value 3 and 4. This may lead to duplicate
    // work when elements resolve to the same value, but that can't be helped.
  
  
    valueAtCurrentStep.forEach(elem => {
      if (nestedQuery.type === `elemMatch`) {
        addNodeToBucketWithElemMatch(node, elem, nestedQuery, filterCache, resolvedNodesCache);
      } else {
        // Now take same route as non-elemMatch filters would take
        addNodeToFilterCache(node, nestedQuery.path, filterCache, resolvedNodesCache, elem);
      }
    });
  }
  
  const binarySearchAsc = (values, needle) => {
    let min = 0;
    let max = values.length - 1;
    let pivot = Math.floor(values.length / 2);
  
    while (min <= max) {
      const value = values[pivot];
  
      if (needle < value) {
        // Move pivot to middle of nodes left of current pivot
        // assert pivot < max
        max = pivot;
      } else if (needle > value) {
        // Move pivot to middle of nodes right of current pivot
        // assert pivot > min
        min = pivot;
      } else {
        // This means needle === value
        // TODO: except for NaN ... and potentially certain type casting cases
        return [pivot, pivot];
      }
  
      if (max - min <= 1) {
        // End of search. Needle not found (as expected). Use pivot as index.
        // If the needle was not found, max-min==1 and max is returned.
        return [min, max];
      }
  
      pivot = min + Math.floor((max - min) / 2);
    } // Shouldn't be reachable, but just in case, fall back to Sift if so.
  
  
    return undefined;
  };
  
  const binarySearchDesc = (values, needle) => {
    let min = 0;
    let max = values.length - 1;
    let pivot = Math.floor(values.length / 2);
  
    while (min <= max) {
      const value = values[pivot];
  
      if (needle < value) {
        // Move pivot to middle of nodes right of current pivot
        // assert pivot < min
        min = pivot;
      } else if (needle > value) {
        // Move pivot to middle of nodes left of current pivot
        // assert pivot > max
        max = pivot;
      } else {
        // This means needle === value
        // TODO: except for NaN ... and potentially certain type casting cases
        return [pivot, pivot];
      }
  
      if (max - min <= 1) {
        // End of search. Needle not found (as expected). Use pivot as index.
        // If the needle was not found, max-min==1 and max is returned.
        return [min, max];
      }
  
      pivot = min + Math.floor((max - min) / 2);
    } // Shouldn't be reachable, but just in case, fall back to Sift if so.
  
  
    return undefined;
  };
  /**
   * Given the cache key for a filter and a target value return the set of nodes
   * that resolve to this value.
   * This returns `undefined` if there is no such node
   *
   * Basically if the filter was {a: {b: {slug: {eq: "foo/bar"}}}} then it will
   * return all the nodes that have `node.slug === "foo/bar"`. That usually (but
   * not always) at most one node for slug, but this filter can apply to anything.
   */
  
  
  const getNodesFromCacheByValue = (filterCacheKey, filterValue, filtersCache) => {
    const filterCache = filtersCache === null || filtersCache === void 0 ? void 0 : filtersCache.get(filterCacheKey);
  
    if (!filterCache) {
      return undefined;
    }
  
    const op = filterCache.op;
  
    if (op === `$eq`) {
      if (filterValue == null) {
        var _filterCache$byValue$, _filterCache$byValue$2;
  
        // Edge case; fetch all nodes for `null` and `undefined` because `$eq`
        // also returns nodes without the path when searching for `null`. Not
        // ops do so, so we map non-existing paths to `undefined`.
        return new Set([...((_filterCache$byValue$ = filterCache.byValue.get(null)) !== null && _filterCache$byValue$ !== void 0 ? _filterCache$byValue$ : []), ...((_filterCache$byValue$2 = filterCache.byValue.get(undefined)) !== null && _filterCache$byValue$2 !== void 0 ? _filterCache$byValue$2 : [])]);
      }
  
      return filterCache.byValue.get(filterValue);
    }
  
    if (op === `$in`) {
      if (!Array.isArray(filterValue)) {
        // Sift assumes the value has an `indexOf` property. By this fluke,
        // string args would work, but I don't think that's intentional/expected.
        throw new Error("The argument to the `in` comparator should be an array");
      }
  
      const filterValueArr = filterValue;
      const set = new Set();
  
      if (filterValueArr.includes(null)) {
        // Like all other ops, `in: [null]` behaves weirdly, allowing all nodes
        // that do not actually have a (complete) path (v=undefined)
        const nodes = filterCache.byValue.get(undefined);
  
        if (nodes) {
          nodes.forEach(v => set.add(v));
        }
      } // For every value in the needle array, find the bucket of nodes for
      // that value, add this bucket of nodes to one set, return the set.
  
  
      filterValueArr.slice(0) // Sort is inline so slice the original array
      .sort((a, b) => {
        if (a == null || b == null) return 0;
        return a < b ? -1 : a > b ? 1 : 0;
      }) // Just sort to preserve legacy order as much as possible.
      .forEach(v => {
        var _filterCache$byValue$3;
  
        return (_filterCache$byValue$3 = filterCache.byValue.get(v)) === null || _filterCache$byValue$3 === void 0 ? void 0 : _filterCache$byValue$3.forEach(v => set.add(v));
      });
      return set;
    }
  
    if (op === `$nin`) {
      // This is essentially the same as the $ne operator, just with multiple
      // values to exclude.
      if (!Array.isArray(filterValue)) {
        throw new Error(`The $nin operator expects an array as value`);
      }
  
      const values = new Set(filterValue);
      const set = new Set(filterCache.meta.nodesUnordered); // Do the action for "$ne" for each element in the set of values
  
      values.forEach(filterValue => {
        if (filterValue === null) {
          // Edge case: $nin with `null` returns only the nodes that contain the
          // full path and that don't resolve to null, so drop `undefined` as well
          let cache = filterCache.byValue.get(undefined);
          if (cache) cache.forEach(node => set.delete(node));
          cache = filterCache.byValue.get(null);
          if (cache) cache.forEach(node => set.delete(node));
        } else {
          // Not excluding null so it should include undefined leafs or leafs
          // where only the partial path exists for whatever reason.
          const cache = filterCache.byValue.get(filterValue);
          if (cache) cache.forEach(node => set.delete(node));
        }
      });
      return set;
    }
  
    if (op === `$ne`) {
      const set = new Set(filterCache.meta.nodesUnordered);
  
      if (filterValue === null) {
        // Edge case: $ne with `null` returns only the nodes that contain the full
        // path and that don't resolve to null, so drop `undefined` as well.
        let cache = filterCache.byValue.get(undefined);
        if (cache) cache.forEach(node => set.delete(node));
        cache = filterCache.byValue.get(null);
        if (cache) cache.forEach(node => set.delete(node));
      } else {
        // Not excluding null so it should include undefined leafs or leafs where
        // only the partial path exists for whatever reason.
        const cache = filterCache.byValue.get(filterValue);
        if (cache) cache.forEach(node => set.delete(node));
      }
  
      return set;
    }
  
    if (filterValue == null) {
      if (op === `$lt` || op === `$gt`) {
        // Nothing is lt/gt null
        return undefined;
      } // This is an edge case and this value should be directly indexed
      // For `lte`/`gte` this should only return nodes for `null`, not a "range"
  
  
      return filterCache.byValue.get(filterValue);
    }
  
    if (Array.isArray(filterValue)) {
      throw new Error("Array is an invalid filter value for the `" + op + "` comparator");
    }
  
    if (op === `$lt`) {
      // First try a direct approach. If a value is queried that also exists then
      // we can prevent a binary search through the whole set, O(1) vs O(log n)
      const ranges = filterCache.meta.valueRangesAsc;
      const nodes = filterCache.meta.nodesByValueAsc;
      const range = ranges.get(filterValue);
  
      if (range) {
        return new Set(nodes.slice(0, range[0]));
      } // Query may ask for a value that doesn't appear in the set, like if the
      // set is [1, 2, 5, 6] and the query is <= 3. In that case we have to
      // apply a search (we'll do binary) to determine the offset to slice from.
      // Note: for lte, the valueAsc array must be set at this point
  
  
      const values = filterCache.meta.valuesAsc; // It shouldn't find the targetValue (but it might) and return the index of
      // the two value between which targetValue sits, or first/last element.
  
      const point = binarySearchAsc(values, filterValue);
  
      if (!point) {
        return undefined;
      }
  
      const [pivotMin, pivotMax] = point; // Each pivot index must have a value and a range
      // The returned min/max index may include the lower/upper bound, so we still
      // have to do lte checks for both values.
  
      let pivotValue = values[pivotMax];
  
      if (pivotValue > filterValue) {
        pivotValue = values[pivotMin];
      } // Note: the pivot value _shouldnt_ match the filter value because that
      // means the value was actually found, but those should have been indexed
      // so should have yielded a result in the .get() above.
  
  
      const [exclPivot, inclPivot] = ranges.get(pivotValue); // Note: technically, `5 <= "5" === true` but `5` would not be cached.
      // So we have to consider weak comparison and may have to include the pivot
  
      const until = pivotValue < filterValue ? inclPivot : exclPivot;
      return new Set(nodes.slice(0, until));
    }
  
    if (op === `$lte`) {
      // First try a direct approach. If a value is queried that also exists then
      // we can prevent a binary search through the whole set, O(1) vs O(log n)
      const ranges = filterCache.meta.valueRangesAsc;
      const nodes = filterCache.meta.nodesByValueAsc;
      const range = ranges.get(filterValue);
  
      if (range) {
        return new Set(nodes.slice(0, range[1]));
      } // Query may ask for a value that doesn't appear in the set, like if the
      // set is [1, 2, 5, 6] and the query is <= 3. In that case we have to
      // apply a search (we'll do binary) to determine the offset to slice from.
      // Note: for lte, the valueAsc array must be set at this point
  
  
      const values = filterCache.meta.valuesAsc; // It shouldn't find the targetValue (but it might) and return the index of
      // the two value between which targetValue sits, or first/last element.
  
      const point = binarySearchAsc(values, filterValue);
  
      if (!point) {
        return undefined;
      }
  
      const [pivotMin, pivotMax] = point; // Each pivot index must have a value and a range
      // The returned min/max index may include the lower/upper bound, so we still
      // have to do lte checks for both values.
  
      let pivotValue = values[pivotMax];
  
      if (pivotValue > filterValue) {
        pivotValue = values[pivotMin];
      } // Note: the pivot value _shouldnt_ match the filter value because that
      // means the value was actually found, but those should have been indexed
      // so should have yielded a result in the .get() above.
  
  
      const [exclPivot, inclPivot] = ranges.get(pivotValue); // Note: technically, `5 <= "5" === true` but `5` would not be cached.
      // So we have to consider weak comparison and may have to include the pivot
  
      const until = pivotValue <= filterValue ? inclPivot : exclPivot;
      return new Set(nodes.slice(0, until));
    }
  
    if (op === `$gt`) {
      // First try a direct approach. If a value is queried that also exists then
      // we can prevent a binary search through the whole set, O(1) vs O(log n)
      const ranges = filterCache.meta.valueRangesDesc;
      const nodes = filterCache.meta.nodesByValueDesc;
      const range = ranges.get(filterValue);
  
      if (range) {
        return new Set(nodes.slice(0, range[0]));
      } // Query may ask for a value that doesn't appear in the set, like if the
      // set is [1, 2, 5, 6] and the query is <= 3. In that case we have to
      // apply a search (we'll do binary) to determine the offset to slice from.
      // Note: for gte, the valueDesc array must be set at this point
  
  
      const values = filterCache.meta.valuesDesc; // It shouldn't find the targetValue (but it might) and return the index of
      // the two value between which targetValue sits, or first/last element.
  
      const point = binarySearchDesc(values, filterValue);
  
      if (!point) {
        return undefined;
      }
  
      const [pivotMin, pivotMax] = point; // Each pivot index must have a value and a range
      // The returned min/max index may include the lower/upper bound, so we still
      // have to do gte checks for both values.
  
      let pivotValue = values[pivotMax];
  
      if (pivotValue < filterValue) {
        pivotValue = values[pivotMin];
      } // Note: the pivot value _shouldnt_ match the filter value because that
      // means the value was actually found, but those should have been indexed
      // so should have yielded a result in the .get() above.
  
  
      const [exclPivot, inclPivot] = ranges.get(pivotValue); // Note: technically, `5 >= "5" === true` but `5` would not be cached.
      // So we have to consider weak comparison and may have to include the pivot
  
      const until = pivotValue > filterValue ? inclPivot : exclPivot;
      return new Set(nodes.slice(0, until));
    }
  
    if (op === `$gte`) {
      // First try a direct approach. If a value is queried that also exists then
      // we can prevent a binary search through the whole set, O(1) vs O(log n)
      const ranges = filterCache.meta.valueRangesDesc;
      const nodes = filterCache.meta.nodesByValueDesc;
      const range = ranges.get(filterValue);
  
      if (range) {
        return new Set(nodes.slice(0, range[1]));
      } // Query may ask for a value that doesn't appear in the set, like if the
      // set is [1, 2, 5, 6] and the query is <= 3. In that case we have to
      // apply a search (we'll do binary) to determine the offset to slice from.
      // Note: for gte, the valueDesc array must be set at this point
  
  
      const values = filterCache.meta.valuesDesc; // It shouldn't find the targetValue (but it might) and return the index of
      // the two value between which targetValue sits, or first/last element.
  
      const point = binarySearchDesc(values, filterValue);
  
      if (!point) {
        return undefined;
      }
  
      const [pivotMin, pivotMax] = point; // Each pivot index must have a value and a range
      // The returned min/max index may include the lower/upper bound, so we still
      // have to do gte checks for both values.
  
      let pivotValue = values[pivotMax];
  
      if (pivotValue < filterValue) {
        pivotValue = values[pivotMin];
      } // Note: the pivot value _shouldnt_ match the filter value because that
      // means the value was actually found, but those should have been indexed
      // so should have yielded a result in the .get() above.
  
  
      const [exclPivot, inclPivot] = ranges.get(pivotValue); // Note: technically, `5 >= "5" === true` but `5` would not be cached.
      // So we have to consider weak comparison and may have to include the pivot
  
      const until = pivotValue >= filterValue ? inclPivot : exclPivot;
      return new Set(nodes.slice(0, until));
    } // Unreachable because we checked all values of FilterOp (which op is)
  
  
    return undefined;
  };
  
  exports.getNodesFromCacheByValue = getNodesFromCacheByValue;
  //# sourceMappingURL=nodes.js.map