function topologicalSort(list, getDeps) {
  const sorted = [];
  const NOT_VISITED = 0;
  const MID_VISIT = 1;
  const COMPLETELY_VISITED = 2;

  // Mark all objects as not visited.
  for (const object of list) {
    object.__mark = NOT_VISITED;
  }

  // Visit each object.
  for (const object of list) {
    visit(object);
  }

  // Return the sorted list.
  return sorted;

  /**
   * @param {T} object
   * @template T
   */
  function visit(object) {
    if (object.__mark == MID_VISIT) {
      assert.fail('Dependency cycle detected!');
    } else if (object.__mark == NOT_VISITED) {
      object.__mark = MID_VISIT;

      // Visit all dependencies.
      for (const dep of getDeps(object)) {
        visit(dep);
      }

      object.__mark = COMPLETELY_VISITED;

      // Push this object onto the list.  All transitive dependencies have
      // already been added to the list.
      sorted.push(object);
    }
  }
}
Topological sort of general objects using a DFS approach.
Will add a __mark field to each object as part of the sorting process.
@param {!Array<T>} list
@param {function(T): !Array<T>} getDeps
@return {!Array<T>}
@template T
@see https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search