var DEFAULT_SEPARATOR = '.';
var DEFAULT_TRAVERSAL_OPTS = {
  traversalType: 'depth-first',
  maxNodes: Number.POSITIVE_INFINITY,
  cycleHandling: true,
  maxDepth: Number.POSITIVE_INFINITY,
  haltOnTruthy: false,
  pathSeparator: DEFAULT_SEPARATOR
};

var _Queue = /*#__PURE__*/function () {
  function _Queue() {
    this.head = undefined;
    this.tail = undefined;
  }

  var _proto = _Queue.prototype;

  _proto.enqueue = function enqueue(v) {
    if (this.tail) {
      this.tail = this.tail.next = {
        value: v
      };
    } else {
      this.head = this.tail = {
        value: v
      };
    }
  };

  _proto.dequeue = function dequeue() {
    var previousHeadValue = this.head.value;
    this.head = this.head.next;

    if (!this.head) {
      this.tail = this.head;
    }

    return previousHeadValue;
  };

  _proto.isEmpty = function isEmpty() {
    return !this.head;
  };

  _proto.reset = function reset() {
    this.head = this.tail = undefined;
  };

  return _Queue;
}();
var _QueueToStackAdapter = /*#__PURE__*/function () {
  function _QueueToStackAdapter(queue) {
    this.queue = void 0;
    this.queue = queue;
  }

  var _proto2 = _QueueToStackAdapter.prototype;

  _proto2.push = function push(v) {
    this.queue.enqueue(v);
  };

  _proto2.pop = function pop() {
    return this.queue.dequeue();
  };

  _proto2.isEmpty = function isEmpty() {
    return this.queue.isEmpty();
  };

  _proto2.reset = function reset() {
    return this.queue.reset();
  };

  return _QueueToStackAdapter;
}();

var _Stack = /*#__PURE__*/function () {
  function _Stack() {
    this.tail = undefined;
  }

  var _proto = _Stack.prototype;

  _proto.push = function push(v) {
    this.tail = {
      value: v,
      prev: this.tail
    };
  };

  _proto.pop = function pop() {
    var node = this.tail;
    this.tail = this.tail.prev;
    return node.value;
  };

  _proto.isEmpty = function isEmpty() {
    return !this.tail;
  };

  _proto.reset = function reset() {
    this.tail = undefined;
  };

  return _Stack;
}();

/** Applies a given callback function to all properties of an object and its children */

var traverse = function traverse(root, callback, opts) {
  if (!(root instanceof Object)) {
    throw new Error('First argument must be an object');
  }

  var fullOpts = Object.assign({}, DEFAULT_TRAVERSAL_OPTS, opts);
  fullOpts.disablePathTracking = typeof fullOpts.pathSeparator !== 'string';
  var stackOrQueue;

  if (fullOpts.traversalType === 'depth-first') {
    stackOrQueue = new _Stack();
  } else {
    stackOrQueue = new _QueueToStackAdapter(new _Queue());
  }

  var traversalMeta = {
    visitedNodes: new WeakSet(),
    depth: 0
  };

  if (!fullOpts.disablePathTracking) {
    traversalMeta.nodePath = null;
  }

  stackOrQueue.push({
    parent: null,
    key: null,
    value: root,
    meta: traversalMeta
  });

  _traverse(callback, stackOrQueue, fullOpts);
};

var _traverse = function _traverse(callback, stackOrQueue, opts) {
  /**
   * Using a stack instead of a queue to preserve the natural depth-first traversal order. Using a queue or traversing an array
   *   in order would lead the depth-first to traverse the value.properties in reverse order.
   * Breadth-first traversal uses queues as usual.
   */
  var newNodesToVisit;

  if (opts.traversalType === 'depth-first') {
    newNodesToVisit = new _Stack();
  } else {
    newNodesToVisit = new _QueueToStackAdapter(new _Queue());
  }

  var maxNodes = opts.maxNodes,
      cycleHandling = opts.cycleHandling,
      maxDepth = opts.maxDepth,
      haltOnTruthy = opts.haltOnTruthy,
      pathSeparator = opts.pathSeparator;
  var visitedNodeCount = 0;

  while (!stackOrQueue.isEmpty() && maxNodes > visitedNodeCount) {
    var callbackContext = stackOrQueue.pop();
    var value = callbackContext.value,
        meta = callbackContext.meta;
    var visitedNodes = meta.visitedNodes;
    var nodeIsObject = value instanceof Object;
    var skipNode = cycleHandling && nodeIsObject && visitedNodes.has(value);

    if (skipNode) {
      continue;
    }

    if (callback(callbackContext) && haltOnTruthy) {
      break;
    }

    visitedNodeCount++;

    if (nodeIsObject) {
      visitedNodes.add(value);
      var depth = meta.depth,
          nodePath = meta.nodePath;
      var newDepth = depth + 1;

      if (newDepth > maxDepth) {
        continue;
      }

      newNodesToVisit.reset();
      var keys = Object.keys(value);

      for (var i = 0; i < keys.length; i++) {
        var property = keys[i];
        var traversalMeta = {
          visitedNodes: visitedNodes,
          depth: newDepth
        };
        var newPath = void 0;

        if (!opts.disablePathTracking) {
          if (!nodePath) {
            newPath = property;
          } else {
            newPath = "" + nodePath + pathSeparator + property;
          }

          traversalMeta.nodePath = newPath;
        }

        newNodesToVisit.push({
          value: value[property],
          meta: traversalMeta,
          key: property,
          parent: value
        });
      }

      while (!newNodesToVisit.isEmpty()) {
        stackOrQueue.push(newNodesToVisit.pop());
      }
    }
  }
};

function getNodeByPath(root, path, separator) {
  if (separator === void 0) {
    separator = DEFAULT_SEPARATOR;
  }

  var node = root;
  var segments = path.split(separator);
  var index = 0;
  var segment = segments[index];

  while (node && segment) {
    node = node[segment];
    segment = segments[++index];
  }

  return node;
}

export { _Queue, _QueueToStackAdapter, _Stack, getNodeByPath, traverse };
//# sourceMappingURL=object-traversal.esm.js.map
