findRootNodeAncestor(obj, predicate = null) {
    let iterations = 0
    let ids = this._rootNodeMap.get(obj)
    if (!ids) {
      ids = new Set()
    }
    if (obj?.parent && typeof obj.parent === `string`) {
      ids.add(obj.parent)
    }
    let matchingRoot = null

    if (ids) {
      for (const id of ids) {
        let tracked = getNodeById(id)

        if (tracked) {
          const visited = new Set()

          while (iterations++ < 100) {
            if (predicate && predicate(tracked)) {
              return tracked
            }

            if (visited.has(tracked)) {
              reporter.error(
                `It looks like you have a node that's set its parent as itself:\n\n` +
                  tracked
              )
              break
            }
            visited.add(tracked)

            const parent = getNodeById(tracked.parent)

            if (!parent) {
              break
            }

            tracked = parent
          }

          if (tracked && !predicate) {
            matchingRoot = tracked
          }
        }
      }
    }

    return matchingRoot
  }
Finds top most ancestor of node that contains passed Object or Array
@param {(Object|Array)} obj Object/Array belonging to Node object or Node object
@param {nodePredicate} [predicate] Optional callback to check if ancestor meets defined conditions
@returns {Node} Top most ancestor if predicate is not specified
or first node that meet predicate conditions if predicate is specified