@procedures/dag 0.0.0-stage → 0.2.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/README_NEW.md ADDED
@@ -0,0 +1,736 @@
1
+ # @procedures/dag
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@procedures/dag.svg)](https://www.npmjs.com/package/@procedures/dag)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Generic DAG (Directed Acyclic Graph) algorithms for dependency management, topological sorting, and parallel execution.
7
+
8
+ ## Overview
9
+
10
+ `client-dag` provides a robust, generic DAG implementation using Kahn's algorithm for topological sorting. It enables dependency-aware parallel execution with concurrency control, making it ideal for package build systems, task scheduling, and any scenario requiring ordered execution of dependent operations.
11
+
12
+ ### Key Features
13
+
14
+ - **Kahn's Algorithm**: Topological sort with cycle detection
15
+ - **Level Assignment**: Automatic grouping for parallel execution
16
+ - **Parallel Execution**: Configurable concurrency per level
17
+ - **Generic Types**: Type-safe with custom node data
18
+ - **Graph Utilities**: Filter, traverse, and analyze dependencies
19
+ - **Fail-Fast or Continue**: Flexible error handling
20
+ - **Progress Tracking**: Callbacks for monitoring
21
+ - **Visualization**: Helper functions for debugging
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install @procedures/dag
27
+ ```
28
+
29
+ ### Dependencies
30
+
31
+ - No runtime dependencies (pure TypeScript/JavaScript)
32
+ - Dev: `@mark1russell7/cue`, `vitest`, `@mark1russell7/test`
33
+
34
+ ## Architecture
35
+
36
+ ```mermaid
37
+ graph TB
38
+ subgraph "Input Layer"
39
+ Nodes[Node Definitions<br/>id + dependencies]
40
+ end
41
+
42
+ subgraph "Builder Module"
43
+ CreateNode[createNode<br/>Factory]
44
+ BuildMap[buildNodeMap<br/>Array to Map]
45
+ Filter[filterDAGFromRoot<br/>Subset extraction]
46
+ Ancestors[getAncestors<br/>Transitive deps]
47
+ Descendants[getDescendants<br/>Reverse deps]
48
+ end
49
+
50
+ subgraph "Traversal Module"
51
+ Kahn[Kahn's Algorithm<br/>Topological Sort]
52
+ BuildDAG[buildLeveledDAG<br/>Level assignment]
53
+ TopoOrder[getTopologicalOrder<br/>Flat list]
54
+ Visualize[visualizeDAG<br/>Debug output]
55
+ end
56
+
57
+ subgraph "Executor Module"
58
+ Processor[createProcessor<br/>Wrapper factory]
59
+ Execute[executeDAG<br/>Parallel execution]
60
+ Sequential[executeDAGSequential<br/>Sequential mode]
61
+ Concurrency[executeWithConcurrency<br/>Batch control]
62
+ end
63
+
64
+ subgraph "Output Layer"
65
+ DAG[DependencyDAG<br/>nodes + levels]
66
+ Results[DAGResult<br/>success + results]
67
+ end
68
+
69
+ Nodes --> CreateNode
70
+ CreateNode --> BuildMap
71
+ BuildMap --> BuildDAG
72
+ BuildDAG --> Kahn
73
+ Kahn --> DAG
74
+ DAG --> Filter
75
+ DAG --> Ancestors
76
+ DAG --> Descendants
77
+ DAG --> TopoOrder
78
+ DAG --> Visualize
79
+ DAG --> Execute
80
+ Execute --> Concurrency
81
+ Execute --> Results
82
+
83
+ Processor --> Execute
84
+ Sequential --> Execute
85
+
86
+ style BuildDAG fill:#e1f5ff
87
+ style Execute fill:#e8f5e9
88
+ style Kahn fill:#fff4e1
89
+ ```
90
+
91
+ ### Kahn's Algorithm Flow
92
+
93
+ ```mermaid
94
+ flowchart TD
95
+ Start[Start: Nodes with dependencies]
96
+ Init[Initialize in-degree counters]
97
+ Queue[Queue = nodes with in-degree 0]
98
+ Process{Queue empty?}
99
+ Take[Take all nodes from queue]
100
+ Level[Create level from queue nodes]
101
+ Reduce[Reduce in-degree of dependents]
102
+ Zero{Any in-degree = 0?}
103
+ AddQueue[Add to queue]
104
+ NextLevel[Move to next level]
105
+ Check{Processed all nodes?}
106
+ Success[Success: Return leveled DAG]
107
+ Cycle[Error: Cycle detected]
108
+
109
+ Start --> Init
110
+ Init --> Queue
111
+ Queue --> Process
112
+ Process -->|No| Take
113
+ Take --> Level
114
+ Level --> Reduce
115
+ Reduce --> Zero
116
+ Zero -->|Yes| AddQueue
117
+ AddQueue --> NextLevel
118
+ NextLevel --> Process
119
+ Zero -->|No| Process
120
+ Process -->|Yes| Check
121
+ Check -->|Yes| Success
122
+ Check -->|No| Cycle
123
+
124
+ style Kahn fill:#fff4e1
125
+ style Success fill:#e8f5e9
126
+ style Cycle fill:#ffe1e1
127
+ ```
128
+
129
+ ### Execution Flow
130
+
131
+ ```mermaid
132
+ sequenceDiagram
133
+ participant App as Application
134
+ participant Exec as executeDAG
135
+ participant Level as Level Processor
136
+ participant Conc as Concurrency Control
137
+ participant Proc as Node Processor
138
+
139
+ App->>Exec: executeDAG(dag, processor, {concurrency: 4})
140
+ Exec->>Exec: Initialize results map
141
+
142
+ loop For each level in dag.levels
143
+ Exec->>Level: Process level
144
+ Level->>Conc: executeWithConcurrency(nodes, fn, limit)
145
+
146
+ loop For each node in level
147
+ Conc->>Proc: processor(node)
148
+ Proc->>Proc: Execute node logic
149
+ Proc-->>Conc: NodeResult
150
+ Conc->>Conc: Track executing promises
151
+ Conc->>Conc: Enforce concurrency limit
152
+ end
153
+
154
+ Conc-->>Level: All node results
155
+ Level->>Level: Check for failures
156
+ Level->>Level: Call onNodeComplete callbacks
157
+ end
158
+
159
+ Exec->>Exec: Aggregate results
160
+ Exec-->>App: DAGResult
161
+ ```
162
+
163
+ ## Usage
164
+
165
+ ### Basic DAG Creation
166
+
167
+ ```typescript
168
+ import {
169
+ createNode,
170
+ buildNodeMap,
171
+ buildLeveledDAG,
172
+ type DAGNode
173
+ } from "@procedures/dag";
174
+
175
+ // Method 1: Manual node creation
176
+ const nodes: DAGNode[] = [
177
+ { id: "app", dependencies: ["lib-a", "lib-b"] },
178
+ { id: "lib-a", dependencies: ["core"] },
179
+ { id: "lib-b", dependencies: ["core"] },
180
+ { id: "core", dependencies: [] }
181
+ ];
182
+
183
+ // Method 2: Using factories
184
+ const nodesAlt = [
185
+ createNode("app", ["lib-a", "lib-b"]),
186
+ createNode("lib-a", ["core"]),
187
+ createNode("lib-b", ["core"]),
188
+ createNode("core")
189
+ ];
190
+
191
+ // Convert to Map
192
+ const nodeMap = buildNodeMap(nodes);
193
+
194
+ // Build leveled DAG
195
+ const dag = buildLeveledDAG(nodeMap);
196
+
197
+ console.log(dag.levels);
198
+ // [
199
+ // [{ id: "core", level: 0, ... }], // Level 0: no dependencies
200
+ // [{ id: "lib-a", level: 1 }, { id: "lib-b", level: 1 }], // Level 1
201
+ // [{ id: "app", level: 2, ... }] // Level 2: depends on all
202
+ // ]
203
+ ```
204
+
205
+ ### Parallel Execution
206
+
207
+ ```typescript
208
+ import { executeDAG, createProcessor } from "@procedures/dag";
209
+
210
+ // Simple processor
211
+ const processor = createProcessor(async (node) => {
212
+ console.log(`Building ${node.id}...`);
213
+ await build(node.id);
214
+ });
215
+
216
+ // Execute with parallelism
217
+ const result = await executeDAG(dag, processor, {
218
+ concurrency: 4, // Up to 4 nodes in parallel per level
219
+ failFast: true, // Stop on first error
220
+ onNodeStart: (node) => console.log(`Started: ${node.id}`),
221
+ onNodeComplete: (result) => {
222
+ if (result.success) {
223
+ console.log(`Completed: ${result.node.id} (${result.duration}ms)`);
224
+ } else {
225
+ console.error(`Failed: ${result.node.id}`, result.error);
226
+ }
227
+ }
228
+ });
229
+
230
+ if (!result.success) {
231
+ console.error("Failed nodes:", result.failedNodes);
232
+ process.exit(1);
233
+ }
234
+ ```
235
+
236
+ ### Custom Node Data
237
+
238
+ ```typescript
239
+ interface PackageNode extends DAGNode {
240
+ data: {
241
+ path: string;
242
+ version: string;
243
+ scripts: Record<string, string>;
244
+ };
245
+ }
246
+
247
+ const packageNodes: PackageNode[] = [
248
+ {
249
+ id: "@org/app",
250
+ dependencies: ["@org/ui"],
251
+ data: {
252
+ path: "/packages/app",
253
+ version: "1.0.0",
254
+ scripts: { build: "tsc" }
255
+ }
256
+ },
257
+ {
258
+ id: "@org/ui",
259
+ dependencies: [],
260
+ data: {
261
+ path: "/packages/ui",
262
+ version: "2.0.0",
263
+ scripts: { build: "vite build" }
264
+ }
265
+ }
266
+ ];
267
+
268
+ const dag = buildLeveledDAG(buildNodeMap(packageNodes));
269
+
270
+ const processor = createProcessor(async (node: PackageNode) => {
271
+ console.log(`Building ${node.id} at ${node.data.path}`);
272
+ await execSync(node.data.scripts.build, { cwd: node.data.path });
273
+ });
274
+
275
+ await executeDAG(dag, processor);
276
+ ```
277
+
278
+ ### Graph Filtering
279
+
280
+ ```typescript
281
+ import { filterDAGFromRoot, getAncestors, getDescendants } from "@procedures/dag";
282
+
283
+ // Filter to only nodes needed for a specific target
284
+ const appOnlyNodes = filterDAGFromRoot(nodeMap, "app");
285
+ const appDAG = buildLeveledDAG(appOnlyNodes);
286
+
287
+ // Get all dependencies of a node (transitive)
288
+ const appDeps = getAncestors(nodeMap, "app");
289
+ console.log(appDeps); // Set { "lib-a", "lib-b", "core" }
290
+
291
+ // Get all dependents of a node (reverse dependencies)
292
+ const coreDependents = getDescendants(nodeMap, "core");
293
+ console.log(coreDependents); // Set { "lib-a", "lib-b", "app" }
294
+ ```
295
+
296
+ ### Visualization
297
+
298
+ ```typescript
299
+ import { visualizeDAG } from "@procedures/dag";
300
+
301
+ const output = visualizeDAG(dag);
302
+ console.log(output);
303
+ // DAG Structure:
304
+ // Level 0:
305
+ // - core (leaf)
306
+ // Level 1:
307
+ // - lib-a -> [core]
308
+ // - lib-b -> [core]
309
+ // Level 2:
310
+ // - app -> [lib-a, lib-b]
311
+ ```
312
+
313
+ ### Sequential Execution
314
+
315
+ ```typescript
316
+ import { executeDAGSequential } from "@procedures/dag";
317
+
318
+ // Execute one node at a time (concurrency: 1)
319
+ const result = await executeDAGSequential(dag, processor, {
320
+ failFast: false // Continue even if some nodes fail
321
+ });
322
+ ```
323
+
324
+ ## API Reference
325
+
326
+ ### Types
327
+
328
+ #### `DAGNode<TData = unknown>`
329
+
330
+ ```typescript
331
+ interface DAGNode<TData = unknown> {
332
+ id: string; // Unique identifier
333
+ dependencies: string[]; // IDs this node depends on
334
+ level?: number; // Topological level (computed by buildLeveledDAG)
335
+ data?: TData | undefined; // Optional user data
336
+ }
337
+ ```
338
+
339
+ #### `DependencyDAG<TNode extends DAGNode = DAGNode>`
340
+
341
+ ```typescript
342
+ interface DependencyDAG<TNode extends DAGNode = DAGNode> {
343
+ nodes: Map<string, TNode>; // All nodes by ID
344
+ levels: TNode[][]; // Nodes grouped by level (0 = leaves)
345
+ roots: string[]; // Node IDs with no dependents
346
+ leaves: string[]; // Node IDs with no dependencies
347
+ }
348
+ ```
349
+
350
+ #### `DAGExecutionOptions<TNode extends DAGNode = DAGNode>`
351
+
352
+ ```typescript
353
+ interface DAGExecutionOptions<TNode extends DAGNode = DAGNode> {
354
+ concurrency?: number; // Max parallel per level (default: 4)
355
+ failFast?: boolean; // Stop on first error (default: true)
356
+ onNodeStart?: (node: TNode) => void; // Called when node starts
357
+ onNodeComplete?: (result: NodeResult<TNode>) => void; // Called when node completes
358
+ }
359
+ ```
360
+
361
+ #### `NodeResult<TNode extends DAGNode = DAGNode>`
362
+
363
+ ```typescript
364
+ interface NodeResult<TNode extends DAGNode = DAGNode> {
365
+ node: TNode; // The processed node
366
+ success: boolean; // Whether processing succeeded
367
+ error?: Error | undefined; // Error if failed
368
+ duration: number; // Processing time in milliseconds
369
+ logs: string[]; // Log messages
370
+ output?: unknown | undefined; // Optional output data
371
+ }
372
+ ```
373
+
374
+ #### `DAGResult<TNode extends DAGNode = DAGNode>`
375
+
376
+ ```typescript
377
+ interface DAGResult<TNode extends DAGNode = DAGNode> {
378
+ success: boolean; // Overall success
379
+ results: Map<string, NodeResult<TNode>>; // Results by node ID
380
+ failedNodes: string[]; // IDs of failed nodes
381
+ totalDuration: number; // Total execution time in ms
382
+ }
383
+ ```
384
+
385
+ #### `NodeProcessor<TNode extends DAGNode = DAGNode>`
386
+
387
+ ```typescript
388
+ type NodeProcessor<TNode extends DAGNode = DAGNode> = (
389
+ node: TNode
390
+ ) => Promise<NodeResult<TNode>>;
391
+ ```
392
+
393
+ ### Functions
394
+
395
+ #### Building
396
+
397
+ ##### `createNode<TData>(id: string, dependencies?: string[], data?: TData): DAGNode<TData>`
398
+
399
+ Create a DAG node from minimal input.
400
+
401
+ ```typescript
402
+ const node = createNode("my-package", ["dep-a", "dep-b"], {
403
+ path: "/packages/my-package"
404
+ });
405
+ ```
406
+
407
+ ##### `buildNodeMap<TNode extends DAGNode>(nodes: TNode[]): Map<string, TNode>`
408
+
409
+ Convert an array of nodes to a Map keyed by ID.
410
+
411
+ ```typescript
412
+ const nodeMap = buildNodeMap([
413
+ createNode("a", ["b"]),
414
+ createNode("b", [])
415
+ ]);
416
+ ```
417
+
418
+ #### Traversal
419
+
420
+ ##### `buildLeveledDAG<TNode extends DAGNode>(nodes: Map<string, TNode>): DependencyDAG<TNode>`
421
+
422
+ Build a leveled DAG using Kahn's algorithm. Assigns level to each node and groups by level.
423
+
424
+ **Throws:** Error if a cycle is detected.
425
+
426
+ ```typescript
427
+ const dag = buildLeveledDAG(nodeMap);
428
+ console.log(dag.levels.length); // Number of levels
429
+ ```
430
+
431
+ ##### `getTopologicalOrder<TNode extends DAGNode>(dag: DependencyDAG<TNode>): TNode[]`
432
+
433
+ Get a flat array of nodes in topological order (leaves first).
434
+
435
+ ```typescript
436
+ const order = getTopologicalOrder(dag);
437
+ // [core, lib-a, lib-b, app]
438
+ ```
439
+
440
+ ##### `visualizeDAG<TNode extends DAGNode>(dag: DependencyDAG<TNode>): string`
441
+
442
+ Generate a string visualization of the DAG structure.
443
+
444
+ ```typescript
445
+ console.log(visualizeDAG(dag));
446
+ ```
447
+
448
+ #### Filtering
449
+
450
+ ##### `filterDAGFromRoot<TNode extends DAGNode>(nodes: Map<string, TNode>, rootId: string): Map<string, TNode>`
451
+
452
+ Filter DAG to only include nodes reachable from a root (the root and all its dependencies).
453
+
454
+ ```typescript
455
+ const appOnly = filterDAGFromRoot(allNodes, "app");
456
+ ```
457
+
458
+ ##### `getAncestors<TNode extends DAGNode>(nodes: Map<string, TNode>, id: string): Set<string>`
459
+
460
+ Get all ancestors of a node (transitive dependencies).
461
+
462
+ ```typescript
463
+ const deps = getAncestors(nodes, "app");
464
+ // Set of all nodes that "app" depends on
465
+ ```
466
+
467
+ ##### `getDescendants<TNode extends DAGNode>(nodes: Map<string, TNode>, id: string): Set<string>`
468
+
469
+ Get all descendants of a node (reverse transitive dependencies).
470
+
471
+ ```typescript
472
+ const dependents = getDescendants(nodes, "core");
473
+ // Set of all nodes that depend on "core"
474
+ ```
475
+
476
+ #### Execution
477
+
478
+ ##### `executeDAG<TNode extends DAGNode>(dag: DependencyDAG<TNode>, processor: NodeProcessor<TNode>, options?: DAGExecutionOptions<TNode>): Promise<DAGResult<TNode>>`
479
+
480
+ Execute DAG with level-based parallelization.
481
+
482
+ - Levels are processed sequentially (level 0, then 1, etc.)
483
+ - Within each level, nodes are processed in parallel up to concurrency limit
484
+ - Returns aggregated results
485
+
486
+ ```typescript
487
+ const result = await executeDAG(dag, processor, {
488
+ concurrency: 4,
489
+ failFast: true,
490
+ onNodeStart: (node) => console.log(`Starting ${node.id}`),
491
+ onNodeComplete: (result) => console.log(`Done ${result.node.id}`)
492
+ });
493
+ ```
494
+
495
+ ##### `executeDAGSequential<TNode extends DAGNode>(dag: DependencyDAG<TNode>, processor: NodeProcessor<TNode>, options?: Omit<DAGExecutionOptions<TNode>, 'concurrency'>): Promise<DAGResult<TNode>>`
496
+
497
+ Execute DAG sequentially (concurrency: 1).
498
+
499
+ ```typescript
500
+ const result = await executeDAGSequential(dag, processor);
501
+ ```
502
+
503
+ ##### `createProcessor<TNode extends DAGNode>(fn: (node: TNode) => Promise<void>): NodeProcessor<TNode>`
504
+
505
+ Create a processor that wraps an async function, handling timing and error catching.
506
+
507
+ ```typescript
508
+ const processor = createProcessor(async (node) => {
509
+ await buildPackage(node.id);
510
+ });
511
+ ```
512
+
513
+ ## Examples
514
+
515
+ ### Example 1: Monorepo Package Build
516
+
517
+ ```typescript
518
+ import { buildLeveledDAG, executeDAG, createProcessor } from "@procedures/dag";
519
+
520
+ const packages = [
521
+ { id: "app", dependencies: ["ui", "api-client"] },
522
+ { id: "ui", dependencies: ["components", "utils"] },
523
+ { id: "api-client", dependencies: ["utils"] },
524
+ { id: "components", dependencies: ["utils"] },
525
+ { id: "utils", dependencies: [] }
526
+ ];
527
+
528
+ const dag = buildLeveledDAG(buildNodeMap(packages));
529
+
530
+ const processor = createProcessor(async (pkg) => {
531
+ console.log(`Building ${pkg.id}...`);
532
+ await exec(`npm run build`, { cwd: `/packages/${pkg.id}` });
533
+ });
534
+
535
+ const result = await executeDAG(dag, processor, {
536
+ concurrency: 3,
537
+ onNodeComplete: (r) => {
538
+ if (r.success) {
539
+ console.log(`✓ ${r.node.id} built in ${r.duration}ms`);
540
+ }
541
+ }
542
+ });
543
+ ```
544
+
545
+ ### Example 2: Task Pipeline
546
+
547
+ ```typescript
548
+ const tasks = [
549
+ { id: "deploy", dependencies: ["test", "build-docker"] },
550
+ { id: "test", dependencies: ["build"] },
551
+ { id: "build-docker", dependencies: ["build"] },
552
+ { id: "build", dependencies: ["lint", "typecheck"] },
553
+ { id: "lint", dependencies: [] },
554
+ { id: "typecheck", dependencies: [] }
555
+ ];
556
+
557
+ const dag = buildLeveledDAG(buildNodeMap(tasks));
558
+
559
+ const processor = createProcessor(async (task) => {
560
+ await runTask(task.id);
561
+ });
562
+
563
+ await executeDAG(dag, processor, { failFast: true });
564
+ ```
565
+
566
+ ### Example 3: Selective Execution
567
+
568
+ ```typescript
569
+ // Only build what's needed for "app"
570
+ const allPackages = buildNodeMap([...]);
571
+ const appDeps = filterDAGFromRoot(allPackages, "app");
572
+ const appDAG = buildLeveledDAG(appDeps);
573
+
574
+ await executeDAG(appDAG, buildProcessor);
575
+ ```
576
+
577
+ ### Example 4: Progress Tracking
578
+
579
+ ```typescript
580
+ let completed = 0;
581
+ const total = dag.nodes.size;
582
+
583
+ const result = await executeDAG(dag, processor, {
584
+ onNodeComplete: (result) => {
585
+ completed++;
586
+ const percent = ((completed / total) * 100).toFixed(1);
587
+ console.log(`[${percent}%] ${result.node.id}: ${result.success ? 'OK' : 'FAIL'}`);
588
+ }
589
+ });
590
+ ```
591
+
592
+ ### Example 5: Continue on Error
593
+
594
+ ```typescript
595
+ const result = await executeDAG(dag, processor, {
596
+ failFast: false // Process all nodes even if some fail
597
+ });
598
+
599
+ if (!result.success) {
600
+ console.error("Failed nodes:", result.failedNodes.join(", "));
601
+ for (const id of result.failedNodes) {
602
+ const nodeResult = result.results.get(id);
603
+ console.error(`${id}: ${nodeResult?.error?.message}`);
604
+ }
605
+ }
606
+ ```
607
+
608
+ ## Cycle Detection
609
+
610
+ If a cycle is detected, `buildLeveledDAG` throws an error:
611
+
612
+ ```typescript
613
+ const nodes = [
614
+ { id: "a", dependencies: ["b"] },
615
+ { id: "b", dependencies: ["c"] },
616
+ { id: "c", dependencies: ["a"] } // Cycle!
617
+ ];
618
+
619
+ try {
620
+ const dag = buildLeveledDAG(buildNodeMap(nodes));
621
+ } catch (error) {
622
+ console.error(error.message);
623
+ // "Circular dependency detected involving: a, b, c"
624
+ }
625
+ ```
626
+
627
+ ## Performance
628
+
629
+ ### Complexity
630
+
631
+ - **buildLeveledDAG**: O(V + E) where V = vertices, E = edges
632
+ - **executeDAG**: O(V * T) where T = average task time
633
+ - **getAncestors/getDescendants**: O(V + E) per call
634
+
635
+ ### Optimization Tips
636
+
637
+ 1. **Concurrency**: Tune based on CPU cores and I/O characteristics
638
+ 2. **Fail-Fast**: Enable for faster feedback in CI/CD
639
+ 3. **Level Inspection**: Use `dag.levels` to understand parallelism opportunities
640
+ 4. **Node Filtering**: Use `filterDAGFromRoot` to reduce work
641
+
642
+ ## Testing
643
+
644
+ The package includes comprehensive tests:
645
+
646
+ ```bash
647
+ npm test
648
+ npm run test:watch
649
+ npm run test:coverage
650
+ ```
651
+
652
+ Test files:
653
+ - `src/dag/__tests__/builder.test.ts` - Graph building and filtering
654
+ - `src/dag/__tests__/traversal.test.ts` - Kahn's algorithm and topological sort
655
+ - `src/dag/__tests__/executor.test.ts` - Parallel execution and concurrency
656
+
657
+ ## Package Structure
658
+
659
+ ```
660
+ client-dag/
661
+ ├── src/
662
+ │ ├── index.ts # Main exports
663
+ │ ├── types.ts # TypeScript type definitions
664
+ │ └── dag/
665
+ │ ├── index.ts # DAG module exports
666
+ │ ├── builder.ts # Graph building utilities
667
+ │ ├── traversal.ts # Kahn's algorithm + topological sort
668
+ │ ├── executor.ts # Parallel execution engine
669
+ │ └── __tests__/ # Test suite
670
+ ├── dist/ # Compiled output
671
+ ├── package.json
672
+ ├── tsconfig.json
673
+ └── README.md
674
+ ```
675
+
676
+ ## Development
677
+
678
+ ### Build
679
+
680
+ ```bash
681
+ npm run build
682
+ ```
683
+
684
+ ### Type Check
685
+
686
+ ```bash
687
+ npm run typecheck
688
+ ```
689
+
690
+ ### Clean
691
+
692
+ ```bash
693
+ npm run clean
694
+ ```
695
+
696
+ ### Run Tests
697
+
698
+ ```bash
699
+ npm test
700
+ npm run test:watch
701
+ npm run test:coverage
702
+ ```
703
+
704
+ ## Integration with Ecosystem
705
+
706
+ ### Used By
707
+
708
+ - **client-lib**: Package dependency resolution
709
+ - **CLI**: Build orchestration commands
710
+ - **CI/CD**: Multi-package pipelines
711
+
712
+ ### Dependency Graph
713
+
714
+ ```mermaid
715
+ graph LR
716
+ clientDag[client-dag]
717
+ cue[cue]
718
+ test[test]
719
+ vitest[vitest]
720
+
721
+ clientDag -.dev.-> cue
722
+ clientDag -.dev.-> test
723
+ clientDag -.dev.-> vitest
724
+
725
+ style clientDag fill:#e1f5ff
726
+ ```
727
+
728
+ ## License
729
+
730
+ MIT - Mark Russell
731
+
732
+ ## Related Packages
733
+
734
+ - `@procedures/lib` - Uses DAG for package builds
735
+ - `@mark1russell7/test` - Testing utilities
736
+ - `@mark1russell7/cue` - Build configuration