@procedures/dag 0.47.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/README.md +2 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +3 -3
- package/dist/graph/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/procedures/traverse.js +1 -1
- package/dist/procedures/traverse.js.map +1 -1
- package/dist/procedures/types.d.ts +1 -1
- package/dist/procedures/types.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/graph/index.ts +3 -3
- package/src/index.ts +3 -3
- package/src/procedures/traverse.ts +1 -1
- package/src/procedures/types.ts +1 -1
- package/dist/dag/builder.d.ts +0 -25
- package/dist/dag/builder.d.ts.map +0 -1
- package/dist/dag/builder.js +0 -95
- package/dist/dag/builder.js.map +0 -1
- package/dist/dag/executor.d.ts +0 -28
- package/dist/dag/executor.d.ts.map +0 -1
- package/dist/dag/executor.js +0 -147
- package/dist/dag/executor.js.map +0 -1
- package/dist/dag/index.d.ts +0 -7
- package/dist/dag/index.d.ts.map +0 -1
- package/dist/dag/index.js +0 -7
- package/dist/dag/index.js.map +0 -1
- package/dist/dag/traversal.d.ts +0 -28
- package/dist/dag/traversal.d.ts.map +0 -1
- package/dist/dag/traversal.js +0 -103
- package/dist/dag/traversal.js.map +0 -1
- package/dist/graph/hub-order.d.ts +0 -19
- package/dist/graph/hub-order.d.ts.map +0 -1
- package/dist/graph/hub-order.js +0 -52
- package/dist/graph/hub-order.js.map +0 -1
- package/dist/types.d.ts +0 -80
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -8
- package/dist/types.js.map +0 -1
- package/src/dag/builder.ts +0 -124
- package/src/dag/executor.ts +0 -190
- package/src/dag/index.ts +0 -23
- package/src/dag/traversal.ts +0 -129
- package/src/graph/hub-order.ts +0 -53
- package/src/types.ts +0 -87
package/src/dag/traversal.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DAG Traversal using Kahn's Algorithm
|
|
3
|
-
*
|
|
4
|
-
* Implements topological sort with level assignment for parallel execution.
|
|
5
|
-
* Level 0 = leaves (no dependencies), higher levels depend on lower levels.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { DAGNode, DependencyDAG } from "../types.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Build a leveled DAG from nodes using Kahn's algorithm
|
|
12
|
-
*
|
|
13
|
-
* This assigns levels to each node where:
|
|
14
|
-
* - Level 0: Nodes with no dependencies (leaves)
|
|
15
|
-
* - Level N: Nodes whose dependencies are all at level < N
|
|
16
|
-
*
|
|
17
|
-
* Nodes at the same level can be processed in parallel.
|
|
18
|
-
*
|
|
19
|
-
* @throws Error if a cycle is detected
|
|
20
|
-
*/
|
|
21
|
-
export function buildLeveledDAG<TNode extends DAGNode>(
|
|
22
|
-
nodes: Map<string, TNode>
|
|
23
|
-
): DependencyDAG<TNode> {
|
|
24
|
-
const inDegree = new Map<string, number>();
|
|
25
|
-
const dependents = new Map<string, Set<string>>();
|
|
26
|
-
|
|
27
|
-
for (const [name, node] of nodes) {
|
|
28
|
-
// A set: a dependency that a node lists twice counts once. (`dependents` is a set, so it
|
|
29
|
-
// takes the in-degree down once: a repeated dependency was "Circular": deep dive DATA-21.)
|
|
30
|
-
const depsInDag = new Set(node.dependencies.filter((dep) => nodes.has(dep)));
|
|
31
|
-
inDegree.set(name, depsInDag.size);
|
|
32
|
-
dependents.set(name, new Set());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
for (const [name, node] of nodes) {
|
|
36
|
-
for (const dep of node.dependencies) {
|
|
37
|
-
if (dependents.has(dep)) {
|
|
38
|
-
dependents.get(dep)!.add(name);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let queue: string[] = [];
|
|
44
|
-
for (const [name, degree] of inDegree) {
|
|
45
|
-
if (degree === 0) {
|
|
46
|
-
queue.push(name);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const levels: TNode[][] = [];
|
|
51
|
-
const nodeLevel = new Map<string, number>();
|
|
52
|
-
let processedCount = 0;
|
|
53
|
-
|
|
54
|
-
while (queue.length > 0) {
|
|
55
|
-
const currentLevel = levels.length;
|
|
56
|
-
const levelNodes: TNode[] = [];
|
|
57
|
-
const nextQueue: string[] = [];
|
|
58
|
-
|
|
59
|
-
for (const name of queue) {
|
|
60
|
-
const node = nodes.get(name)!;
|
|
61
|
-
node.level = currentLevel;
|
|
62
|
-
nodeLevel.set(name, currentLevel);
|
|
63
|
-
levelNodes.push(node);
|
|
64
|
-
processedCount++;
|
|
65
|
-
|
|
66
|
-
for (const dependent of dependents.get(name) ?? []) {
|
|
67
|
-
const newDegree = inDegree.get(dependent)! - 1;
|
|
68
|
-
inDegree.set(dependent, newDegree);
|
|
69
|
-
|
|
70
|
-
if (newDegree === 0) {
|
|
71
|
-
nextQueue.push(dependent);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
levels.push(levelNodes);
|
|
77
|
-
queue = nextQueue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (processedCount !== nodes.size) {
|
|
81
|
-
const cycleNodes = [...nodes.keys()].filter((n) => !nodeLevel.has(n));
|
|
82
|
-
throw new Error("Circular dependency detected involving: " + cycleNodes.join(", "));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const roots = [...nodes.keys()].filter((name) => {
|
|
86
|
-
const deps = dependents.get(name);
|
|
87
|
-
return !deps || deps.size === 0;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const leaves = [...nodes.keys()].filter((name) => {
|
|
91
|
-
const node = nodes.get(name)!;
|
|
92
|
-
const depsInDag = node.dependencies.filter((dep) => nodes.has(dep));
|
|
93
|
-
return depsInDag.length === 0;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return { nodes, levels, roots, leaves };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Get the topological order (flat list, leaves first)
|
|
101
|
-
*/
|
|
102
|
-
export function getTopologicalOrder<TNode extends DAGNode>(
|
|
103
|
-
dag: DependencyDAG<TNode>
|
|
104
|
-
): TNode[] {
|
|
105
|
-
return dag.levels.flat();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Visualize the DAG structure (for debugging)
|
|
110
|
-
*/
|
|
111
|
-
export function visualizeDAG<TNode extends DAGNode>(
|
|
112
|
-
dag: DependencyDAG<TNode>
|
|
113
|
-
): string {
|
|
114
|
-
const lines = ["DAG Structure:"];
|
|
115
|
-
|
|
116
|
-
for (let i = 0; i < dag.levels.length; i++) {
|
|
117
|
-
const level = dag.levels[i]!;
|
|
118
|
-
lines.push(" Level " + i + ":");
|
|
119
|
-
for (const node of level) {
|
|
120
|
-
const deps =
|
|
121
|
-
node.dependencies.length > 0
|
|
122
|
-
? " -> [" + node.dependencies.join(", ") + "]"
|
|
123
|
-
: " (leaf)";
|
|
124
|
-
lines.push(" - " + node.id + deps);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return lines.join("\n");
|
|
129
|
-
}
|
package/src/graph/hub-order.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/** An undirected edge between the nodes `a` and `b`. The default weight is 1. */
|
|
2
|
-
export interface WeightedEdge {
|
|
3
|
-
readonly a: number;
|
|
4
|
-
readonly b: number;
|
|
5
|
-
readonly weight?: number | undefined;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* This function gives a breadth-first order of the nodes `0` to `n - 1` of an undirected weighted graph.
|
|
10
|
-
*
|
|
11
|
-
* - The hub of a component is the node with the largest sum of edge weights. `compare`, then the lower index, breaks a tie.
|
|
12
|
-
* - The order starts at the hub of each component, and goes breadth-first. `compare`, then the lower index, orders the neighbours of a node.
|
|
13
|
-
* - Without edges, each node is a component, so the order is the order of `compare`.
|
|
14
|
-
* - An edge from a node to itself adds no weight.
|
|
15
|
-
* - A bad `n`, an index outside the nodes, or a weight that is not a finite number above 0, throws a `RangeError`.
|
|
16
|
-
*
|
|
17
|
-
* The function is pure. It keeps no state, and it does not change its arguments.
|
|
18
|
-
*/
|
|
19
|
-
export function hubBreadthFirstOrder(n: number, edges: readonly WeightedEdge[] = [], compare: (i: number, j: number) => number = () => 0): number[] {
|
|
20
|
-
if (!Number.isInteger(n) || n < 0) throw new RangeError(`The count of the nodes is a whole number from 0, not ${n}.`);
|
|
21
|
-
const degree = new Float64Array(n);
|
|
22
|
-
const next: number[][] = Array.from({ length: n }, () => []);
|
|
23
|
-
for (const { a, b, weight = 1 } of edges) {
|
|
24
|
-
if (!Number.isInteger(a) || !Number.isInteger(b) || a < 0 || b < 0 || a >= n || b >= n) {
|
|
25
|
-
throw new RangeError(`An edge joins the nodes ${a} and ${b}, but the indexes go from 0 to ${n - 1}.`);
|
|
26
|
-
}
|
|
27
|
-
if (!Number.isFinite(weight) || weight <= 0) throw new RangeError(`The weight of an edge is a finite number above 0, not ${weight}.`);
|
|
28
|
-
if (a === b) continue;
|
|
29
|
-
degree[a] = degree[a]! + weight;
|
|
30
|
-
degree[b] = degree[b]! + weight;
|
|
31
|
-
next[a]!.push(b);
|
|
32
|
-
next[b]!.push(a);
|
|
33
|
-
}
|
|
34
|
-
const order = (i: number, j: number): number => compare(i, j) || i - j;
|
|
35
|
-
const hubs = Array.from({ length: n }, (_, i) => i).sort((i, j) => degree[j]! - degree[i]! || order(i, j));
|
|
36
|
-
const seen = new Uint8Array(n);
|
|
37
|
-
const out: number[] = [];
|
|
38
|
-
for (const hub of hubs) {
|
|
39
|
-
if (seen[hub] === 1) continue;
|
|
40
|
-
seen[hub] = 1;
|
|
41
|
-
const queue = [hub];
|
|
42
|
-
for (let k = 0; k < queue.length; k++) {
|
|
43
|
-
const i = queue[k]!;
|
|
44
|
-
out.push(i);
|
|
45
|
-
for (const j of next[i]!.toSorted(order)) {
|
|
46
|
-
if (seen[j] === 1) continue;
|
|
47
|
-
seen[j] = 1;
|
|
48
|
-
queue.push(j);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return out;
|
|
53
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic DAG Types
|
|
3
|
-
*
|
|
4
|
-
* These types are designed to be reusable across different use cases.
|
|
5
|
-
* For ecosystem-specific DAG building, see client-lib.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A node in a dependency DAG
|
|
10
|
-
*/
|
|
11
|
-
export interface DAGNode<TData = unknown> {
|
|
12
|
-
/** Unique identifier for this node */
|
|
13
|
-
id: string;
|
|
14
|
-
/** Names/IDs of nodes this node depends on */
|
|
15
|
-
dependencies: string[];
|
|
16
|
-
/** Topological level (0 = leaves, computed by Kahn's algorithm) */
|
|
17
|
-
level?: number;
|
|
18
|
-
/** Optional user data associated with this node */
|
|
19
|
-
data?: TData | undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* A dependency DAG with level information for parallel execution
|
|
24
|
-
*/
|
|
25
|
-
export interface DependencyDAG<TNode extends DAGNode = DAGNode> {
|
|
26
|
-
/** All nodes in the DAG */
|
|
27
|
-
nodes: Map<string, TNode>;
|
|
28
|
-
/** Nodes grouped by level for parallel execution */
|
|
29
|
-
levels: TNode[][];
|
|
30
|
-
/** Root nodes (no dependents in the DAG) */
|
|
31
|
-
roots: string[];
|
|
32
|
-
/** Leaf nodes (no dependencies in the DAG) */
|
|
33
|
-
leaves: string[];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Options for DAG execution
|
|
38
|
-
*/
|
|
39
|
-
export interface DAGExecutionOptions<TNode extends DAGNode = DAGNode> {
|
|
40
|
-
/** Max parallel operations per level (default: 4) */
|
|
41
|
-
concurrency?: number;
|
|
42
|
-
/** Stop on first error vs continue (default: true) */
|
|
43
|
-
failFast?: boolean;
|
|
44
|
-
/** Callback for progress reporting */
|
|
45
|
-
onNodeStart?: (node: TNode) => void;
|
|
46
|
-
/** Callback when a node completes */
|
|
47
|
-
onNodeComplete?: (result: NodeResult<TNode>) => void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Result of processing a single node
|
|
52
|
-
*/
|
|
53
|
-
export interface NodeResult<TNode extends DAGNode = DAGNode> {
|
|
54
|
-
/** The node that was processed */
|
|
55
|
-
node: TNode;
|
|
56
|
-
/** Whether processing succeeded */
|
|
57
|
-
success: boolean;
|
|
58
|
-
/** Error if failed */
|
|
59
|
-
error?: Error | undefined;
|
|
60
|
-
/** Duration in milliseconds */
|
|
61
|
-
duration: number;
|
|
62
|
-
/** Logs from processing */
|
|
63
|
-
logs: string[];
|
|
64
|
-
/** Optional output from the processor */
|
|
65
|
-
output?: unknown | undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Result of executing an entire DAG
|
|
70
|
-
*/
|
|
71
|
-
export interface DAGResult<TNode extends DAGNode = DAGNode> {
|
|
72
|
-
/** Overall success */
|
|
73
|
-
success: boolean;
|
|
74
|
-
/** Results for each node */
|
|
75
|
-
results: Map<string, NodeResult<TNode>>;
|
|
76
|
-
/** Names of failed nodes */
|
|
77
|
-
failedNodes: string[];
|
|
78
|
-
/** Total duration in milliseconds */
|
|
79
|
-
totalDuration: number;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Processor function type
|
|
84
|
-
*/
|
|
85
|
-
export type NodeProcessor<TNode extends DAGNode = DAGNode> = (
|
|
86
|
-
node: TNode
|
|
87
|
-
) => Promise<NodeResult<TNode>>;
|