@loken/hierarchies
Version:
Library for working with hierarchies of identifiers and identifiable objects.
80 lines (65 loc) • 2.94 kB
text/typescript
import type { Some } from '@loken/utilities';
/**
* The type of traversal.
* - `breadth-first` (default): Breadth first processes each node at a given depth before it proceeds to the next depth.
* - `depth-first`: Depth first traverses as deep as it can at any given time only exploring the next branch once the previous one has been fully explored.
*/
export type TraversalType = 'breadth-first' | 'depth-first';
/**
* Options for graph traversal.
*
* You must either provide a delegate using a `signal` or a delegate simply providing the `next` nodes.
*/
export type GraphTraversal<TNode> = {
/** The roots may have parents, but they are treated as depth 0 nodes for the traversal. */
roots: Some<TNode>;
/**
* The type of traversal.
* - `breadth-first` (default): Breadth first processes each node at a given depth before it proceeds to the next depth.
* - `depth-first`: Depth first traverses as deep as it can at any given time only exploring the next branch once the previous one has been fully explored.
*/
type?: TraversalType;
/** Should we be looking for cycles in the graph (`true`) or is it an acyclic graph/tree and we don't need to (`false` by default)? */
detectCycles?: boolean;
} & ({
/** Describes how to get the the next nodes from a node visited while traversing a graph. */
next: NextNodes<TNode>;
/** Discriminated: Cannot pass a `signal` delegate when you've already passed a `next` delegate. */
signal?: never;
} | {
/** Describes how to traverse a graph when visiting a node using an `IGraphSignal`. */
signal: SignalNodes<TNode>;
/** Discriminated: Cannot pass a `next` delegate when you've already passed a `signal` delegate. */
next?: never;
});
/** Describes how to get the the next nodes from a node visited while traversing a graph. */
export type NextNodes<TNode> = (node: TNode) => TNode[] | Set<TNode> | undefined | void;
/** Describes how to traverse a graph when visiting a node using an `IGraphSignal`. */
export type SignalNodes<TNode> = (node: TNode, signal: IGraphSignal<TNode>) => void;
/**
* Use this to signal to the traversal what's `next`,
* what to `skip` and whether to `end`.
*/
export interface IGraphSignal<TNode> {
/** Depth of the current root relative to the traversal roots. */
get depth(): number;
/** The number of elements returned so far. */
get count(): number;
/** Call this when traversal should continue to a sub sequence of child roots. */
next(nodes: Some<TNode>): void;
/**
* Call this when you want to signal that the current root should be skipped,
* meaning it will not be part of the output.
*
* Traversal will still continue to whatever roots are passed to
* `next` irrespective of calling `skip`.
*/
skip(): void;
/**
* Call this when all traversal should end immediately.
*
* Ending traversal of a particular branch is controlled by not calling
* `next` for that branch.
*/
end(): void;
}