@vltpkg/graph-run 0.0.0 → 1.0.0-rc.11

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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (c) vlt technology, Inc.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7
+ Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
8
+
9
+ (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
10
+ (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
11
+ Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
12
+
13
+ DISCLAIMER
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,219 @@
1
+ # @vltpkg/graph-run
2
+
3
+ Figure out which items in a dependency graph can be handled in
4
+ parallel, and which must be deferred until their dependencies are
5
+ completed. Handles cyclic and acyclic graphs, but if cycles are
6
+ allowed, then dependency order may not be guaranteed.
7
+
8
+ For example, given the following directed graph:
9
+
10
+ ```mermaid
11
+ flowchart TD
12
+ A --> B
13
+ A --> C
14
+ B --> E
15
+ B --> D
16
+ C --> D
17
+ C --> G
18
+ D --> F
19
+ F --> E
20
+ ```
21
+
22
+ it will allow you to operate in this order:
23
+
24
+ - E and G in parallel (since they are leaf nodes)
25
+ - F when E is complete
26
+ - D when F is complete
27
+ - B when D and E are complete (possibly in parallel with C)
28
+ - C when D and G are complete (possibly in parallel with B)
29
+ - A when B and C are complete
30
+
31
+ Operation will be maximally parallelized, so that each step in the
32
+ graph only waits until its dependencies are completed.
33
+
34
+ ## Cycles
35
+
36
+ If a cycle exists in the graph, then at least one node in the cycle
37
+ will by necessity be operated upon _before_ its dependencies are
38
+ complete.
39
+
40
+ Cycles may be disallowed by throwing in the `onCycle` method provided.
41
+
42
+ ## Contract
43
+
44
+ - Each node reachable from the initial entry points provided will be
45
+ visited exactly once.
46
+ - Except in the case of cycles, each node's dependencies will be
47
+ visited before itself.
48
+ - In the default async forms of the methods provided:
49
+ - All methods will be awaited before continuing.
50
+ - Any operations that can be done in parallel will be.
51
+ - All pending visits and dependency lookups will be skipped if a
52
+ provided `AbortSignal` fires.
53
+
54
+ ## Difference between this and typical topological sort
55
+
56
+ This is not just a topological sort, and there are better libraries
57
+ available that implement Kahn's Topological Sort very efficiently.
58
+
59
+ However, topological sorting is insufficient if you wish to know which
60
+ items can be operated on _in parallel_, and Kahn's algorithm only
61
+ works when the graph is guaranteed to be acyclic and all nodes are
62
+ known ahead of time.
63
+
64
+ This implementation does not require that the graph be entirely loaded
65
+ ahead of time, and provides a mechanism to treat cycles as a warning
66
+ rather than an error, with the caveat that if cycles are allowed then
67
+ topological ordering is of course not guaranteed.
68
+
69
+ ## Caveat
70
+
71
+ While this _can_ be used in theory to explore any infinitely large
72
+ graph, note that the result set will be stored in memory.
73
+
74
+ So, if you were to use it to try to crawl all the links in Wikipedia
75
+ or something, you're going to have a Bad Time if the result set gets
76
+ too big.
77
+
78
+ ## USAGE
79
+
80
+ ```js
81
+ import {
82
+ // returns Promise<Map<Node, Result>>
83
+ graphRun,
84
+ // returns Promise<Map<Node, PromiseSettledResult<Result>>>
85
+ // like Promise.allSettled
86
+ // Implies failFast:false
87
+ allSettled,
88
+ // resolves to first successful result, or rejects with AggregateError
89
+ // like Promise.any()
90
+ any,
91
+ // returns or rejects on the first visit to complete
92
+ // like Promise.race()
93
+ race,
94
+
95
+ // options provided to sync methods MUST be sychronous
96
+
97
+ // returns Map<Node, Result>
98
+ graphRunSync,
99
+ // returns Map<Node, PromiseSettledResult<Result>>
100
+ allSettledSync,
101
+ // returns first successful result, or throws AggregateError
102
+ anySync,
103
+ // returns or throws on the first visit
104
+ // this is somewhat useless, since you can just call the method
105
+ // on the first node in the options.graph list, but is included
106
+ // for symmetry reasons.
107
+ raceSync,
108
+ } from '@vltpk/graph-run'
109
+
110
+ // optionally used to trigger an abort of the walk at any time
111
+ const ac = new AbortController()
112
+
113
+ // return value will be a Promise<Map<Node, Result>>
114
+ // when using allSettled, returns Promise<Map<Node, PromiseSettledResult>>
115
+ const results = await graphRun/*<Node, Result>*/({
116
+ // provide one or more node to serve as entry points.
117
+ // this MAY be the entire graph, but doesn't have to be.
118
+ // Node[]
119
+ graph: [some, nodes, known, at, the, start],
120
+
121
+ // a sync or async method for providing nodes that are
122
+ // dependents of the node being considered.
123
+ // Return [] if node has no dependencies.
124
+ // (n: Node) => Node[] | Promise<Node[]>
125
+ getDeps: async (n) => {
126
+ // if a promise is returned, then it must resolve to an
127
+ // array of nodes.
128
+ return [the, dependency, node, objects]
129
+ },
130
+
131
+ // sync or async method that will be executed on each node in the graph
132
+ // node is the data object being operated on, signal is an
133
+ // AbortSignal that will fire if the operation should halt.
134
+ // (node: Node, signal: AbortSignal, path: Node[]) => Result|Promise<Result>
135
+ visit: async (node, signal, path) => {
136
+ await doSomething(n)
137
+ },
138
+
139
+ // optional: if this method throws, then the traversal will
140
+ // of course fail when cycles are encountered. If not provided,
141
+ // then cycles are silently detected and skipped.
142
+ // first argument is the dependency that would cause a cycle
143
+ // second argument is the cycle from that node back to itself
144
+ // third is the path to the dependent that wanted to load it,
145
+ // but instead will skip it because of the cycle.
146
+ // (node: Node, cycle: Node[], path: Node[]) => void | Promise<void>
147
+ onCycle: (node, cycle, path) => {
148
+ console.error(
149
+ `warning: while evaluating ${
150
+ node
151
+ } at path ${
152
+ path.join('->')
153
+ } encountered cycle: ${
154
+ cycle.join('->')
155
+ }. Proceeding without this dependency.`
156
+ )
157
+ },
158
+
159
+ // Whether to abort the entire traversal on the first error
160
+ // if true, then this will reject with an error and trigger an abort
161
+ // on all in-progress operations.
162
+ // if false, then the rejection value will be an AggregateError of all
163
+ // failures encountered. (For consistency, an AggregateError is
164
+ // raised if failFast is false, even if only one failure is countered.)
165
+ // default: true
166
+ failFast: true
167
+
168
+ // Optional: pass along a signal that you can use externally to signal
169
+ // the graph traversal should end prematurely.
170
+ signal: ac.signal
171
+ })
172
+
173
+ // Another example, detect whether it's a directed acyclic graph:
174
+ const isDAG = (graph) => {
175
+ try {
176
+ graphRunSync({
177
+ graph: [graph],
178
+ getDeps: n => n.children ?? [],
179
+ onCycle: () => { throw 'cycle detected' },
180
+ visit: () => {}
181
+ })
182
+ return true
183
+ } catch {
184
+ return false
185
+ }
186
+ }
187
+
188
+ const g = {
189
+ children: [
190
+ {
191
+ name: 'a',
192
+ children: [{ name: 'b' }],
193
+ }
194
+ ]
195
+ }
196
+ console.log(isDAG(g)) // true
197
+ // create a cycle
198
+ g.children[0].children[0].children = g.children
199
+ console.log(isDAG(g)) // false
200
+ ```
201
+
202
+ ## Other Options
203
+
204
+ - [topological-sort](https://www.npmjs.com/package/topological-sort)
205
+ - [@hapi/topo](https://www.npmjs.com/package/@hapi/topo)
206
+ - [toposort](https://www.npmjs.com/package/toposort)
207
+ - [graph-data-structure](https://www.npmjs.com/package/graph-data-structure)
208
+ - [js-graph-algorithms](https://www.npmjs.com/package/js-graph-algorithms)
209
+ - [fast-graph](https://www.npmjs.com/package/fast-graph)
210
+ - [treeverse](https://www.npmjs.com/package/treeverse)
211
+
212
+ ## How This Is Different
213
+
214
+ - More flexible than most as far as the data structures used. Ie,
215
+ `Node` can be any value type.
216
+ - Dependency edges are resolved on-demand, and may be calculated
217
+ asynchronously, making it efficient in cases where loading the
218
+ entire graph may be expensive.
219
+ - Both cyclic and acyclic graphs are supported.
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Codes indicating the type of error that was encountered.
3
+ * These are found on the `Error.cause.code` field.
4
+ *
5
+ * They are:
6
+ *
7
+ * - `'GRAPHRUN_TRAVERSAL'` The command run on a given node has failed, either
8
+ * by throwing an error, or by returning a rejected promise.
9
+ * - `'GRAPHRUN_NO_NODES'` An empty list of initial nodes was provided to the
10
+ * graph run operation. At least one starting node must be present in the
11
+ * list.
12
+ * - `'GRAPHRUN_CYCLE_WITHOUT_PATH'` - A cycle in the graph was detected, but
13
+ * the path *to* the node where the cycle was detected could not be
14
+ * determined. This is impossible, and cannot ever happen.
15
+ */
16
+ export type ErrorCode = 'GRAPHRUN_NO_NODES' | 'GRAPHRUN_CYCLE_WITHOUT_PATH' | 'GRAPHRUN_TRAVERSAL';
17
+ export type ErrorCause<Node> = {
18
+ code: ErrorCode;
19
+ } & ({
20
+ code: 'GRAPHRUN_NO_NODES';
21
+ found: unknown;
22
+ wanted: string;
23
+ } | {
24
+ code: 'GRAPHRUN_CYCLE_WITHOUT_PATH';
25
+ } | {
26
+ code: 'GRAPHRUN_TRAVERSAL';
27
+ cause: Error;
28
+ node: Node;
29
+ path: Node[];
30
+ });
31
+ export declare const isGraphRunError: <Node>(er: unknown) => er is Error & {
32
+ cause: ErrorCause<Node>;
33
+ };
34
+ /**
35
+ * Options that define the graph and how to traverse it
36
+ */
37
+ export interface RunnerOptions<Node, Result = void> {
38
+ /** Array of one or more entry nodes. */
39
+ graph: [node: Node, ...rest: Node[]];
40
+ /** get the dependencies of a given node */
41
+ getDeps: (node: Node) => Node[] | Promise<Node[]>;
42
+ /** action to take on each node */
43
+ visit: (node: Node, signal: AbortSignal, path: Node[], depResults: DepResults<Node, Result>) => Result | Promise<Result>;
44
+ /**
45
+ * Called when a cycle is encountered.
46
+ * Throw in this method to enforce a DAG graph.
47
+ * If left undefined, then cycles are silently ignored and skipped.
48
+ *
49
+ * `node` parameter is the dependency that is being skipped.
50
+ *
51
+ * `cycle` is the route from the dependent back to itself via
52
+ * the parent.
53
+ *
54
+ * `path` is the path to the dependent who wanted this dep to be
55
+ * loaded.
56
+ */
57
+ onCycle?: (node: Node, cycle: Node[], path: Node[]) => void | Promise<void>;
58
+ /**
59
+ * Set to `false` to continue operations even if errors occur.
60
+ * If set to false, then an AggregateError will be raised on failure
61
+ * containing all failures (even if only one). If true, then a normal
62
+ * Error will be raised on failure.
63
+ * @default true
64
+ */
65
+ failFast?: boolean;
66
+ /** a signal that will trigger the graph traversal to end prematurely */
67
+ signal?: AbortSignal;
68
+ }
69
+ export type DepResults<Node, Result> = Map<Node, Result | undefined>;
70
+ /**
71
+ * Options that can define a synchronous graph traversal.
72
+ *
73
+ * Note that if the visit() method is async, then the *promises themselves*
74
+ * will be used as the `Result` type, which is likely not what you want!
75
+ */
76
+ export interface RunnerOptionsSync<Node, Result = void> extends RunnerOptions<Node, Result> {
77
+ /** Get a set of dependency nodes synchronously */
78
+ getDeps: (node: Node) => Node[];
79
+ /** Visit a node synchronously */
80
+ visit: (node: Node, signal: AbortSignal, path: Node[], depResults: DepResults<Node, Result>) => Result;
81
+ /** Handle cycles synchronously */
82
+ onCycle?: (node: Node, cycle: Node[], path: Node[]) => void;
83
+ }
84
+ /** A map of nodes to their PromiseSettledResult value */
85
+ export type SettledMap<Node, Result = void> = Map<Node, PromiseSettledResult<Result>>;
86
+ /** Any function or class. Used for Error.captureStackTrace */
87
+ export type Callable = Function | ((...a: unknown[]) => unknown) | (new (...a: unknown[]) => unknown);
88
+ /** Base class of Runner and RunnerSync */
89
+ export declare abstract class RunnerBase<
90
+ /** The type of thing to be found in this graph */
91
+ Node,
92
+ /** Type returned by the visit() method */
93
+ Result = void, Sync extends boolean = false, O extends Sync extends true ? RunnerOptionsSync<Node, Result> : RunnerOptions<Node, Result> = Sync extends true ? RunnerOptionsSync<Node, Result> : RunnerOptions<Node, Result>> {
94
+ /** The map of traversal results */
95
+ readonly results: Map<Node, Result>;
96
+ /** The map of PromiseSettledResult objects */
97
+ readonly settled: SettledMap<Node, Result>;
98
+ /** Set of dependents (direct & transitive) on each node */
99
+ readonly dependents: Map<Node, Set<Node>>;
100
+ /** Set of direct dependents on each node */
101
+ readonly directDependents: Map<Node, Set<Node>>;
102
+ /** Options provided to constructor */
103
+ readonly options: O;
104
+ /**
105
+ * AbortController used internally to abort the process.
106
+ *
107
+ * This is internal only, and triggering it at the wrong time may cause
108
+ * undefined and unsupported behavior. Do not use!
109
+ *
110
+ * Instead, if you want to be able to abort the walk at any time, provide
111
+ * your own AbortSignal in the opions.
112
+ * @internal
113
+ */
114
+ readonly abortController: AbortController;
115
+ /** True if we are in failFast mode */
116
+ readonly failFast: boolean;
117
+ /** Rejections and Errors encountered in the traversal */
118
+ readonly errors: unknown[];
119
+ /**
120
+ * Function defining the callsite where the traversal was initiated,
121
+ * used for Error.captureStackTrace.
122
+ */
123
+ readonly from: Callable;
124
+ constructor(options: O, from?: Callable);
125
+ /** Initiate the graph traversal, resolving/returning when complete */
126
+ abstract run(): Sync extends true ? void : Promise<void>;
127
+ /** Get the dependencies of a given node */
128
+ abstract getDeps(n: Node): Sync extends true ? Node[] : Promise<Node[]>;
129
+ /** Visit a node. Calls `options.visit()` */
130
+ abstract visit(n: Node, path: Node[], depResults: DepResults<Node, Result>): Sync extends true ? Result : Promise<Result>;
131
+ /**
132
+ * Calls the `options.onCycle()` method when a cycle is detected.
133
+ */
134
+ abstract onCycle(n: Node, cycle: Node[], path: Node[]): Sync extends true ? void : void | Promise<void>;
135
+ /**
136
+ * For a Node `n` that depends directly or transitively on Node `d`, find the
137
+ * shortest known dependency path from `n` to `d`. This is done by walking
138
+ * backwards breadth-first up the dependency relations from `d` until `n` is
139
+ * found.
140
+ *
141
+ * If no known path can be found, then `undefined` is returned. Otherwise,
142
+ * a path array is returned that starts with `n` and ends with `d`.
143
+ *
144
+ * Note that self-referential links are never considered, since they're
145
+ * by definition cyclical.
146
+ */
147
+ route(n: Node, d: Node): undefined | [n: Node, ...path: Node[]];
148
+ /**
149
+ * If the dependency from `n -> d` at the specified path would
150
+ * cause a cycle, then call the onCycle method and return true.
151
+ * Oherwise, assign the appropriate entries in the dependency
152
+ * tracking sets, and return false.
153
+ * @internal
154
+ */
155
+ cycleCheck(n: Node, path: Node[], d: Node): boolean;
156
+ /**
157
+ * Method that handles errors raised by visits.
158
+ * @internal
159
+ */
160
+ handleError(er: unknown, n: Node, path: Node[]): void;
161
+ /**
162
+ * Method that handles successful visit results
163
+ * @internal
164
+ */
165
+ handleValue(value: Result, n: Node): void;
166
+ }
167
+ /** Asynchronous graph runner */
168
+ export declare class Runner<Node, Result> extends RunnerBase<Node, Result, false, RunnerOptions<Node, Result>> {
169
+ #private;
170
+ /** Map of nodes currently awaiting completion */
171
+ readonly running: Map<Node, Promise<void>>;
172
+ /** Track which node's promise is waiting for which other nodes */
173
+ readonly promiseWaiting: Map<Node, Set<Node>>;
174
+ getDeps(n: Node): Promise<Node[]>;
175
+ visit(n: Node, path: Node[], depResults: DepResults<Node, Result>): Promise<Result>;
176
+ onCycle(n: Node, cycle: Node[], path: Node[]): Promise<void>;
177
+ run(): Promise<void>;
178
+ }
179
+ /** Synchronous graph runner */
180
+ export declare class RunnerSync<Node, Result> extends RunnerBase<Node, Result, true, RunnerOptionsSync<Node, Result>> {
181
+ #private;
182
+ getDeps(n: Node): Node[];
183
+ visit(n: Node, path: Node[], depResults: DepResults<Node, Result>): Result;
184
+ onCycle(n: Node, cycle: Node[], path: Node[]): void;
185
+ run(): Map<Node, Result>;
186
+ }
187
+ /**
188
+ * Asynchronous graph traversal method
189
+ *
190
+ * If `failFast:false` is set in the options, then an AggregateError
191
+ * will be raised if there were any failures. Otherwise, a normal Error
192
+ * is raised on failure.
193
+ */
194
+ export declare const graphRun: <Node, Result>(options: RunnerOptions<Node, Result>) => Promise<Map<Node, Result>>;
195
+ /**
196
+ * Synchronous graph traversal method
197
+ *
198
+ * If `failFast:false` is set in the options, then an AggregateError
199
+ * will be thrown if there were any failures. Otherwise, a normal Error
200
+ * is thrown on failure.
201
+ */
202
+ export declare const graphRunSync: <Node, Result>(options: RunnerOptionsSync<Node, Result>) => Map<Node, Result>;
203
+ /**
204
+ * Asynchronous graph traversal, capturing all error/result
205
+ * statuses.
206
+ */
207
+ export declare const allSettled: <Node, Result>(options: RunnerOptions<Node, Result>) => Promise<SettledMap<Node, Result>>;
208
+ /**
209
+ * Synchronous graph traversal, capturing all error/result
210
+ * statuses.
211
+ */
212
+ export declare const allSettledSync: <Node, Result>(options: RunnerOptionsSync<Node, Result>) => SettledMap<Node, Result>;
213
+ /**
214
+ * Asynchronous graph traversal, returning the first successful visit.
215
+ * If all visits fail, then an AggregateError is raised with all errors
216
+ * encountered.
217
+ */
218
+ export declare const any: <Node, Result>(options: RunnerOptions<Node, Result>) => Promise<Result>;
219
+ /**
220
+ * Synchronous graph traversal, returning the first successful visit.
221
+ * If all visits fail, then an AggregateError is thrown with all errors
222
+ * encountered.
223
+ */
224
+ export declare const anySync: <Node, Result>(options: RunnerOptionsSync<Node, Result>) => Result;
225
+ /**
226
+ * Asynchronous graph traversal, resolving or rejecting when the first visit
227
+ * resolves or rejects.
228
+ */
229
+ export declare const race: <Node, Result>(options: RunnerOptions<Node, Result>) => Promise<Result>;
230
+ /**
231
+ * Synchronous graph traversal, returning or throwing when the first visit
232
+ * is completed.
233
+ */
234
+ export declare const raceSync: <Node, Result>(options: RunnerOptionsSync<Node, Result>) => Result;
235
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,540 @@
1
+ import { setMaxListeners } from 'node:events';
2
+ export const isGraphRunError = (er) => !!er &&
3
+ typeof er === 'object' &&
4
+ 'cause' in er &&
5
+ !!er.cause &&
6
+ typeof er.cause === 'object' &&
7
+ 'code' in er.cause &&
8
+ ((er.cause.code === 'GRAPHRUN_NO_NODES' &&
9
+ 'found' in er.cause &&
10
+ 'wanted' in er.cause &&
11
+ typeof er.cause.wanted === 'string') ||
12
+ (er.cause.code === 'GRAPHRUN_TRAVERSAL' &&
13
+ 'path' in er.cause &&
14
+ Array.isArray(er.cause.path) &&
15
+ 'node' in er.cause &&
16
+ 'cause' in er.cause) ||
17
+ er.cause.code === 'GRAPHRUN_CYCLE_WITHOUT_PATH');
18
+ /** Base class of Runner and RunnerSync */
19
+ export class RunnerBase {
20
+ /** The map of traversal results */
21
+ results = new Map();
22
+ /** The map of PromiseSettledResult objects */
23
+ settled = new Map();
24
+ /** Set of dependents (direct & transitive) on each node */
25
+ dependents = new Map();
26
+ /** Set of direct dependents on each node */
27
+ directDependents = new Map();
28
+ /** Options provided to constructor */
29
+ options;
30
+ /**
31
+ * AbortController used internally to abort the process.
32
+ *
33
+ * This is internal only, and triggering it at the wrong time may cause
34
+ * undefined and unsupported behavior. Do not use!
35
+ *
36
+ * Instead, if you want to be able to abort the walk at any time, provide
37
+ * your own AbortSignal in the opions.
38
+ * @internal
39
+ */
40
+ abortController;
41
+ /** True if we are in failFast mode */
42
+ failFast;
43
+ /** Rejections and Errors encountered in the traversal */
44
+ errors = [];
45
+ /**
46
+ * Function defining the callsite where the traversal was initiated,
47
+ * used for Error.captureStackTrace.
48
+ */
49
+ from;
50
+ constructor(options, from) {
51
+ const ac = new AbortController();
52
+ this.from = from ?? this.constructor;
53
+ this.abortController = ac;
54
+ setMaxListeners(Infinity, ac.signal);
55
+ this.options = options;
56
+ if (!options.graph.length) {
57
+ const er = new Error('no nodes provided to graph traversal', {
58
+ cause: {
59
+ code: 'GRAPHRUN_NO_NODES',
60
+ found: options.graph,
61
+ wanted: '[first: Node, ...rest: Node[]]',
62
+ },
63
+ });
64
+ Error.captureStackTrace(er, from);
65
+ throw er;
66
+ }
67
+ this.failFast = options.failFast !== false;
68
+ const { signal } = options;
69
+ if (signal !== undefined) {
70
+ signal.addEventListener('abort', reason => ac.abort(reason), {
71
+ once: true,
72
+ signal: ac.signal,
73
+ });
74
+ }
75
+ }
76
+ /**
77
+ * For a Node `n` that depends directly or transitively on Node `d`, find the
78
+ * shortest known dependency path from `n` to `d`. This is done by walking
79
+ * backwards breadth-first up the dependency relations from `d` until `n` is
80
+ * found.
81
+ *
82
+ * If no known path can be found, then `undefined` is returned. Otherwise,
83
+ * a path array is returned that starts with `n` and ends with `d`.
84
+ *
85
+ * Note that self-referential links are never considered, since they're
86
+ * by definition cyclical.
87
+ */
88
+ route(n, d) {
89
+ const dependents = this.dependents.get(d);
90
+ if (!dependents?.has(n))
91
+ return undefined;
92
+ const directDependents = this.directDependents.get(d);
93
+ /* c8 ignore next */
94
+ if (!directDependents)
95
+ return undefined;
96
+ if (directDependents.has(n)) {
97
+ return [n, d];
98
+ }
99
+ const queue = [
100
+ ...directDependents,
101
+ ].map(dd => [dd, d]);
102
+ let step = undefined;
103
+ while (undefined !== (step = queue.shift())) {
104
+ /* c8 ignore next */
105
+ if (!dependents.has(step[0]))
106
+ continue;
107
+ if (step[0] === n) {
108
+ return step;
109
+ }
110
+ const ddd = this.directDependents.get(step[0]);
111
+ if (ddd) {
112
+ for (const d of ddd) {
113
+ queue.push([d, ...step]);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * If the dependency from `n -> d` at the specified path would
120
+ * cause a cycle, then call the onCycle method and return true.
121
+ * Oherwise, assign the appropriate entries in the dependency
122
+ * tracking sets, and return false.
123
+ * @internal
124
+ */
125
+ cycleCheck(n, path, d) {
126
+ /* c8 ignore next */
127
+ const dependents = this.dependents.get(n) ?? new Set();
128
+ this.dependents.set(n, dependents);
129
+ const isCycle = dependents.has(d);
130
+ if (isCycle) {
131
+ const cycle = this.route(d, n);
132
+ /* c8 ignore start - impossible */
133
+ if (!cycle) {
134
+ throw new Error('cycle detected, but cycle route not found', {
135
+ cause: { code: 'GRAPHRUN_CYCLE_WITHOUT_PATH' },
136
+ });
137
+ }
138
+ /* c8 ignore stop */
139
+ cycle.unshift(n);
140
+ void this.onCycle(d, cycle, path);
141
+ return true;
142
+ }
143
+ const depDD = this.directDependents.get(d) ?? new Set();
144
+ this.directDependents.set(d, depDD);
145
+ depDD.add(n);
146
+ const depDependents = this.dependents.get(d) ?? new Set();
147
+ this.dependents.set(d, depDependents);
148
+ for (const n of dependents) {
149
+ depDependents.add(n);
150
+ }
151
+ depDependents.add(n);
152
+ return false;
153
+ }
154
+ /**
155
+ * Method that handles errors raised by visits.
156
+ * @internal
157
+ */
158
+ handleError(er, n, path) {
159
+ this.errors.push(er);
160
+ this.settled.set(n, {
161
+ status: 'rejected',
162
+ reason: er,
163
+ });
164
+ if (this.failFast) {
165
+ this.abortController.abort(er);
166
+ const e = new Error('failed graph traversal', {
167
+ cause: {
168
+ code: 'GRAPHRUN_TRAVERSAL',
169
+ node: n,
170
+ path,
171
+ cause: er,
172
+ },
173
+ });
174
+ Error.captureStackTrace(e, this.from);
175
+ throw e;
176
+ }
177
+ }
178
+ /**
179
+ * Method that handles successful visit results
180
+ * @internal
181
+ */
182
+ handleValue(value, n) {
183
+ this.results.set(n, value);
184
+ this.settled.set(n, {
185
+ status: 'fulfilled',
186
+ value,
187
+ });
188
+ }
189
+ }
190
+ /** Asynchronous graph runner */
191
+ export class Runner extends RunnerBase {
192
+ /** Map of nodes currently awaiting completion */
193
+ running = new Map();
194
+ /** Track which node's promise is waiting for which other nodes */
195
+ promiseWaiting = new Map();
196
+ async getDeps(n) {
197
+ /* c8 ignore next */
198
+ if (this.abortController.signal.aborted)
199
+ return [];
200
+ const deps = await this.options.getDeps(n);
201
+ for (const d of deps) {
202
+ const dependents = this.dependents.get(d) ?? new Set();
203
+ this.dependents.set(d, dependents);
204
+ dependents.add(n);
205
+ const depDD = this.directDependents.get(d) ?? new Set();
206
+ this.directDependents.set(d, depDD);
207
+ depDD.add(n);
208
+ }
209
+ return deps;
210
+ }
211
+ async visit(n, path, depResults) {
212
+ const { signal } = this.abortController;
213
+ return this.options.visit(n, signal, path, depResults);
214
+ }
215
+ async onCycle(n, cycle, path) {
216
+ /* c8 ignore next */
217
+ if (this.abortController.signal.aborted)
218
+ return;
219
+ await this.options.onCycle?.(n, cycle, path);
220
+ }
221
+ /**
222
+ * Check if node `target` is waiting (directly or transitively) for node `n`.
223
+ * If so, making `n` wait for `target` would create a deadlock.
224
+ */
225
+ #isWaitingFor(target, n, visited = new Set()) {
226
+ if (visited.has(target))
227
+ return false;
228
+ visited.add(target);
229
+ const waiting = this.promiseWaiting.get(target);
230
+ if (!waiting)
231
+ return false;
232
+ if (waiting.has(n))
233
+ return true;
234
+ for (const w of waiting) {
235
+ if (this.#isWaitingFor(w, n, visited))
236
+ return true;
237
+ }
238
+ return false;
239
+ }
240
+ async #walk(n, path) {
241
+ const r = this.running.get(n);
242
+ /* c8 ignore next */
243
+ if (r)
244
+ return r;
245
+ /* c8 ignore start */
246
+ if (this.settled.get(n))
247
+ return;
248
+ /* c8 ignore stop */
249
+ const p = this.#step(n, path).then(() => {
250
+ this.running.delete(n);
251
+ this.promiseWaiting.delete(n);
252
+ },
253
+ /* c8 ignore start - handled deeper in the chain */
254
+ (er) => {
255
+ this.running.delete(n);
256
+ this.promiseWaiting.delete(n);
257
+ throw er;
258
+ });
259
+ this.running.set(n, p);
260
+ return p;
261
+ }
262
+ async #step(n, path) {
263
+ const dependents = this.dependents.get(n) ?? new Set();
264
+ this.dependents.set(n, dependents);
265
+ const deps = await this.getDeps(n);
266
+ const awaiting = [];
267
+ const depPath = [...path, n];
268
+ // Initialize waiting set for this node
269
+ const waitingOn = new Set();
270
+ this.promiseWaiting.set(n, waitingOn);
271
+ for (const d of deps) {
272
+ /* c8 ignore next */
273
+ if (this.abortController.signal.aborted)
274
+ return;
275
+ // self-link, skip
276
+ if (d === n)
277
+ continue;
278
+ if (this.cycleCheck(n, depPath, d))
279
+ continue;
280
+ /* c8 ignore next */
281
+ if (this.settled.get(d))
282
+ continue;
283
+ const runningPromise = this.running.get(d);
284
+ if (runningPromise) {
285
+ // Check if d is already waiting for n (promise-level cycle)
286
+ if (this.#isWaitingFor(d, n)) {
287
+ // Treat as cycle to prevent deadlock
288
+ void this.onCycle(d, [n, d], depPath);
289
+ continue;
290
+ }
291
+ }
292
+ // Record that n is waiting for d
293
+ waitingOn.add(d);
294
+ awaiting.push(runningPromise ?? this.#walk(d, depPath));
295
+ }
296
+ /* c8 ignore next */
297
+ if (this.abortController.signal.aborted)
298
+ return;
299
+ await Promise.all(awaiting);
300
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
301
+ if (this.abortController.signal.aborted)
302
+ return;
303
+ const depRes = new Map(deps.map(d => [d, this.results.get(d)]));
304
+ try {
305
+ this.handleValue(await this.visit(n, path, depRes), n);
306
+ }
307
+ catch (er) {
308
+ this.handleError(er, n, path);
309
+ }
310
+ }
311
+ async run() {
312
+ const promises = [];
313
+ for (const n of this.options.graph) {
314
+ promises.push(this.#walk(n, []));
315
+ }
316
+ await Promise.all(promises);
317
+ }
318
+ }
319
+ /** Synchronous graph runner */
320
+ export class RunnerSync extends RunnerBase {
321
+ getDeps(n) {
322
+ if (this.abortController.signal.aborted)
323
+ return [];
324
+ return this.options.getDeps(n);
325
+ }
326
+ visit(n, path, depResults) {
327
+ const { signal } = this.abortController;
328
+ return this.options.visit(n, signal, path, depResults);
329
+ }
330
+ onCycle(n, cycle, path) {
331
+ /* c8 ignore next */
332
+ if (this.abortController.signal.aborted)
333
+ return;
334
+ this.options.onCycle?.(n, cycle, path);
335
+ }
336
+ #walk(n, path) {
337
+ /* c8 ignore start */
338
+ if (this.settled.get(n))
339
+ return;
340
+ /* c8 ignore stop */
341
+ this.#step(n, path);
342
+ }
343
+ #step(n, path) {
344
+ const dependents = this.dependents.get(n) ?? new Set();
345
+ this.dependents.set(n, dependents);
346
+ const deps = this.getDeps(n);
347
+ const depPath = [...path, n];
348
+ for (const d of deps) {
349
+ if (this.abortController.signal.aborted)
350
+ return;
351
+ /* c8 ignore next */
352
+ if (d === n)
353
+ continue;
354
+ if (this.cycleCheck(n, depPath, d))
355
+ continue;
356
+ if (!this.settled.get(d))
357
+ this.#walk(d, depPath);
358
+ }
359
+ if (this.abortController.signal.aborted)
360
+ return;
361
+ const depRes = new Map(deps.map(d => [d, this.results.get(d)]));
362
+ try {
363
+ this.handleValue(this.visit(n, path, depRes), n);
364
+ }
365
+ catch (er) {
366
+ this.handleError(er, n, path);
367
+ }
368
+ }
369
+ run() {
370
+ for (const n of this.options.graph) {
371
+ this.#walk(n, []);
372
+ }
373
+ return this.results;
374
+ }
375
+ }
376
+ /**
377
+ * Asynchronous graph traversal method
378
+ *
379
+ * If `failFast:false` is set in the options, then an AggregateError
380
+ * will be raised if there were any failures. Otherwise, a normal Error
381
+ * is raised on failure.
382
+ */
383
+ export const graphRun = async (options) => {
384
+ const runner = new Runner(options, graphRun);
385
+ await runner.run();
386
+ if (runner.errors.length) {
387
+ const e = new AggregateError(runner.errors, 'failed graph traversal');
388
+ Error.captureStackTrace(e, graphRun);
389
+ throw e;
390
+ }
391
+ return runner.results;
392
+ };
393
+ /**
394
+ * Synchronous graph traversal method
395
+ *
396
+ * If `failFast:false` is set in the options, then an AggregateError
397
+ * will be thrown if there were any failures. Otherwise, a normal Error
398
+ * is thrown on failure.
399
+ */
400
+ export const graphRunSync = (options) => {
401
+ const runner = new RunnerSync(options, graphRunSync);
402
+ runner.run();
403
+ if (runner.errors.length) {
404
+ const e = new AggregateError(runner.errors, 'failed graph traversal');
405
+ Error.captureStackTrace(e, graphRunSync);
406
+ throw e;
407
+ }
408
+ return runner.results;
409
+ };
410
+ /**
411
+ * Asynchronous graph traversal, capturing all error/result
412
+ * statuses.
413
+ */
414
+ export const allSettled = async (options) => {
415
+ const runner = new Runner({ ...options, failFast: false }, allSettled);
416
+ await runner.run();
417
+ return runner.settled;
418
+ };
419
+ /**
420
+ * Synchronous graph traversal, capturing all error/result
421
+ * statuses.
422
+ */
423
+ export const allSettledSync = (options) => {
424
+ const runner = new RunnerSync({ ...options, failFast: false }, allSettledSync);
425
+ runner.run();
426
+ return runner.settled;
427
+ };
428
+ /**
429
+ * Asynchronous graph traversal, returning the first successful visit.
430
+ * If all visits fail, then an AggregateError is raised with all errors
431
+ * encountered.
432
+ */
433
+ export const any = async (options) => {
434
+ const ac = new AbortController();
435
+ let result;
436
+ let found = false;
437
+ const runner = new Runner({
438
+ ...options,
439
+ failFast: false,
440
+ signal: ac.signal,
441
+ visit: async (node, signal, path, depResults) => {
442
+ try {
443
+ result = await options.visit(node, signal, path, depResults);
444
+ found = true;
445
+ ac.abort('found');
446
+ }
447
+ catch { }
448
+ return result;
449
+ },
450
+ }, any);
451
+ await runner.run();
452
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
453
+ if (!found) {
454
+ const e = new AggregateError(runner.errors, 'failed graph traversal');
455
+ Error.captureStackTrace(e, any);
456
+ throw e;
457
+ }
458
+ return result;
459
+ };
460
+ /**
461
+ * Synchronous graph traversal, returning the first successful visit.
462
+ * If all visits fail, then an AggregateError is thrown with all errors
463
+ * encountered.
464
+ */
465
+ export const anySync = (options) => {
466
+ const ac = new AbortController();
467
+ let result;
468
+ let found = false;
469
+ const runner = new RunnerSync({
470
+ ...options,
471
+ failFast: false,
472
+ signal: ac.signal,
473
+ visit: (node, signal, path, depResults) => {
474
+ try {
475
+ result = options.visit(node, signal, path, depResults);
476
+ found = true;
477
+ ac.abort('found');
478
+ }
479
+ catch { }
480
+ return result;
481
+ },
482
+ }, anySync);
483
+ runner.run();
484
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
485
+ if (!found) {
486
+ const e = new AggregateError(runner.errors, 'failed graph traversal');
487
+ Error.captureStackTrace(e, anySync);
488
+ throw e;
489
+ }
490
+ return result;
491
+ };
492
+ /**
493
+ * Asynchronous graph traversal, resolving or rejecting when the first visit
494
+ * resolves or rejects.
495
+ */
496
+ export const race = async (options) => {
497
+ const ac = new AbortController();
498
+ let result;
499
+ const runner = new Runner({
500
+ ...options,
501
+ failFast: true,
502
+ signal: ac.signal,
503
+ visit: async (node, signal, path, depResults) => {
504
+ try {
505
+ result = await options.visit(node, signal, path, depResults);
506
+ ac.abort('found');
507
+ /* c8 ignore next */
508
+ }
509
+ catch { }
510
+ return result;
511
+ },
512
+ }, race);
513
+ await runner.run();
514
+ return result;
515
+ };
516
+ /**
517
+ * Synchronous graph traversal, returning or throwing when the first visit
518
+ * is completed.
519
+ */
520
+ export const raceSync = (options) => {
521
+ const ac = new AbortController();
522
+ let result;
523
+ const runner = new RunnerSync({
524
+ ...options,
525
+ failFast: true,
526
+ signal: ac.signal,
527
+ visit: (node, signal, path, depResults) => {
528
+ try {
529
+ result = options.visit(node, signal, path, depResults);
530
+ ac.abort('found');
531
+ /* c8 ignore next */
532
+ }
533
+ catch { }
534
+ return result;
535
+ },
536
+ }, race);
537
+ runner.run();
538
+ return result;
539
+ };
540
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { setMaxListeners } from 'node:events'\n\n/**\n * Codes indicating the type of error that was encountered.\n * These are found on the `Error.cause.code` field.\n *\n * They are:\n *\n * - `'GRAPHRUN_TRAVERSAL'` The command run on a given node has failed, either\n * by throwing an error, or by returning a rejected promise.\n * - `'GRAPHRUN_NO_NODES'` An empty list of initial nodes was provided to the\n * graph run operation. At least one starting node must be present in the\n * list.\n * - `'GRAPHRUN_CYCLE_WITHOUT_PATH'` - A cycle in the graph was detected, but\n * the path *to* the node where the cycle was detected could not be\n * determined. This is impossible, and cannot ever happen.\n */\nexport type ErrorCode =\n | 'GRAPHRUN_NO_NODES'\n | 'GRAPHRUN_CYCLE_WITHOUT_PATH'\n | 'GRAPHRUN_TRAVERSAL'\n\nexport type ErrorCause<Node> = {\n code: ErrorCode\n} & (\n | {\n code: 'GRAPHRUN_NO_NODES'\n found: unknown\n wanted: string\n }\n | { code: 'GRAPHRUN_CYCLE_WITHOUT_PATH' }\n | {\n code: 'GRAPHRUN_TRAVERSAL'\n cause: Error\n node: Node\n path: Node[]\n }\n)\n\nexport const isGraphRunError = <Node>(\n er: unknown,\n): er is Error & { cause: ErrorCause<Node> } =>\n !!er &&\n typeof er === 'object' &&\n 'cause' in er &&\n !!er.cause &&\n typeof er.cause === 'object' &&\n 'code' in er.cause &&\n ((er.cause.code === 'GRAPHRUN_NO_NODES' &&\n 'found' in er.cause &&\n 'wanted' in er.cause &&\n typeof er.cause.wanted === 'string') ||\n (er.cause.code === 'GRAPHRUN_TRAVERSAL' &&\n 'path' in er.cause &&\n Array.isArray(er.cause.path) &&\n 'node' in er.cause &&\n 'cause' in er.cause) ||\n er.cause.code === 'GRAPHRUN_CYCLE_WITHOUT_PATH')\n\n/**\n * Options that define the graph and how to traverse it\n */\nexport interface RunnerOptions<Node, Result = void> {\n /** Array of one or more entry nodes. */\n graph: [node: Node, ...rest: Node[]]\n\n /** get the dependencies of a given node */\n getDeps: (node: Node) => Node[] | Promise<Node[]>\n\n /** action to take on each node */\n visit: (\n node: Node,\n signal: AbortSignal,\n path: Node[],\n depResults: DepResults<Node, Result>,\n ) => Result | Promise<Result>\n\n /**\n * Called when a cycle is encountered.\n * Throw in this method to enforce a DAG graph.\n * If left undefined, then cycles are silently ignored and skipped.\n *\n * `node` parameter is the dependency that is being skipped.\n *\n * `cycle` is the route from the dependent back to itself via\n * the parent.\n *\n * `path` is the path to the dependent who wanted this dep to be\n * loaded.\n */\n onCycle?: (\n node: Node,\n cycle: Node[],\n path: Node[],\n ) => void | Promise<void>\n\n /**\n * Set to `false` to continue operations even if errors occur.\n * If set to false, then an AggregateError will be raised on failure\n * containing all failures (even if only one). If true, then a normal\n * Error will be raised on failure.\n * @default true\n */\n failFast?: boolean\n\n /** a signal that will trigger the graph traversal to end prematurely */\n signal?: AbortSignal\n}\n\nexport type DepResults<Node, Result> = Map<Node, Result | undefined>\n\n/**\n * Options that can define a synchronous graph traversal.\n *\n * Note that if the visit() method is async, then the *promises themselves*\n * will be used as the `Result` type, which is likely not what you want!\n */\nexport interface RunnerOptionsSync<\n Node,\n Result = void,\n> extends RunnerOptions<Node, Result> {\n /** Get a set of dependency nodes synchronously */\n getDeps: (node: Node) => Node[]\n\n /** Visit a node synchronously */\n visit: (\n node: Node,\n signal: AbortSignal,\n path: Node[],\n depResults: DepResults<Node, Result>,\n ) => Result\n\n /** Handle cycles synchronously */\n onCycle?: (node: Node, cycle: Node[], path: Node[]) => void\n}\n\n/** A map of nodes to their PromiseSettledResult value */\nexport type SettledMap<Node, Result = void> = Map<\n Node,\n PromiseSettledResult<Result>\n>\n\n/** Any function or class. Used for Error.captureStackTrace */\nexport type Callable =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n | Function\n | ((...a: unknown[]) => unknown)\n | (new (...a: unknown[]) => unknown)\n\n/** Base class of Runner and RunnerSync */\nexport abstract class RunnerBase<\n /** The type of thing to be found in this graph */\n Node,\n /** Type returned by the visit() method */\n Result = void,\n Sync extends boolean = false,\n O extends Sync extends true ? RunnerOptionsSync<Node, Result>\n : RunnerOptions<Node, Result> = Sync extends true ?\n RunnerOptionsSync<Node, Result>\n : RunnerOptions<Node, Result>,\n> {\n /** The map of traversal results */\n readonly results = new Map<Node, Result>()\n\n /** The map of PromiseSettledResult objects */\n readonly settled: SettledMap<Node, Result> = new Map()\n\n /** Set of dependents (direct & transitive) on each node */\n readonly dependents = new Map<Node, Set<Node>>()\n\n /** Set of direct dependents on each node */\n readonly directDependents = new Map<Node, Set<Node>>()\n\n /** Options provided to constructor */\n readonly options: O\n\n /**\n * AbortController used internally to abort the process.\n *\n * This is internal only, and triggering it at the wrong time may cause\n * undefined and unsupported behavior. Do not use!\n *\n * Instead, if you want to be able to abort the walk at any time, provide\n * your own AbortSignal in the opions.\n * @internal\n */\n readonly abortController: AbortController\n\n /** True if we are in failFast mode */\n readonly failFast: boolean\n\n /** Rejections and Errors encountered in the traversal */\n readonly errors: unknown[] = []\n\n /**\n * Function defining the callsite where the traversal was initiated,\n * used for Error.captureStackTrace.\n */\n readonly from: Callable\n\n constructor(options: O, from?: Callable) {\n const ac = new AbortController()\n this.from = from ?? this.constructor\n this.abortController = ac\n setMaxListeners(Infinity, ac.signal)\n this.options = options\n if (!options.graph.length) {\n const er = new Error('no nodes provided to graph traversal', {\n cause: {\n code: 'GRAPHRUN_NO_NODES',\n found: options.graph,\n wanted: '[first: Node, ...rest: Node[]]',\n },\n })\n Error.captureStackTrace(er, from)\n throw er\n }\n this.failFast = options.failFast !== false\n const { signal } = options\n if (signal !== undefined) {\n signal.addEventListener('abort', reason => ac.abort(reason), {\n once: true,\n signal: ac.signal,\n })\n }\n }\n\n /** Initiate the graph traversal, resolving/returning when complete */\n abstract run(): Sync extends true ? void : Promise<void>\n\n /** Get the dependencies of a given node */\n abstract getDeps(\n n: Node,\n ): Sync extends true ? Node[] : Promise<Node[]>\n\n /** Visit a node. Calls `options.visit()` */\n abstract visit(\n n: Node,\n path: Node[],\n depResults: DepResults<Node, Result>,\n ): Sync extends true ? Result : Promise<Result>\n\n /**\n * Calls the `options.onCycle()` method when a cycle is detected.\n */\n abstract onCycle(\n n: Node,\n cycle: Node[],\n path: Node[],\n ): Sync extends true ? void : void | Promise<void>\n\n /**\n * For a Node `n` that depends directly or transitively on Node `d`, find the\n * shortest known dependency path from `n` to `d`. This is done by walking\n * backwards breadth-first up the dependency relations from `d` until `n` is\n * found.\n *\n * If no known path can be found, then `undefined` is returned. Otherwise,\n * a path array is returned that starts with `n` and ends with `d`.\n *\n * Note that self-referential links are never considered, since they're\n * by definition cyclical.\n */\n route(n: Node, d: Node): undefined | [n: Node, ...path: Node[]] {\n const dependents = this.dependents.get(d)\n if (!dependents?.has(n)) return undefined\n const directDependents = this.directDependents.get(d)\n /* c8 ignore next */\n if (!directDependents) return undefined\n if (directDependents.has(n)) {\n return [n, d]\n }\n const queue: [n: Node, ...r: Node[]][] = [\n ...directDependents,\n ].map(dd => [dd, d])\n let step: undefined | [n: Node, ...r: Node[]] = undefined\n while (undefined !== (step = queue.shift())) {\n /* c8 ignore next */\n if (!dependents.has(step[0])) continue\n if (step[0] === n) {\n return step\n }\n const ddd = this.directDependents.get(step[0])\n if (ddd) {\n for (const d of ddd) {\n queue.push([d, ...step])\n }\n }\n }\n }\n\n /**\n * If the dependency from `n -> d` at the specified path would\n * cause a cycle, then call the onCycle method and return true.\n * Oherwise, assign the appropriate entries in the dependency\n * tracking sets, and return false.\n * @internal\n */\n cycleCheck(n: Node, path: Node[], d: Node) {\n /* c8 ignore next */\n const dependents = this.dependents.get(n) ?? new Set()\n this.dependents.set(n, dependents)\n const isCycle = dependents.has(d)\n if (isCycle) {\n const cycle = this.route(d, n)\n /* c8 ignore start - impossible */\n if (!cycle) {\n throw new Error('cycle detected, but cycle route not found', {\n cause: { code: 'GRAPHRUN_CYCLE_WITHOUT_PATH' },\n })\n }\n /* c8 ignore stop */\n cycle.unshift(n)\n void this.onCycle(d, cycle, path)\n return true\n }\n\n const depDD = this.directDependents.get(d) ?? new Set()\n this.directDependents.set(d, depDD)\n depDD.add(n)\n\n const depDependents = this.dependents.get(d) ?? new Set()\n this.dependents.set(d, depDependents)\n for (const n of dependents) {\n depDependents.add(n)\n }\n depDependents.add(n)\n return false\n }\n\n /**\n * Method that handles errors raised by visits.\n * @internal\n */\n handleError(er: unknown, n: Node, path: Node[]) {\n this.errors.push(er)\n this.settled.set(n, {\n status: 'rejected',\n reason: er,\n })\n if (this.failFast) {\n this.abortController.abort(er)\n const e = new Error('failed graph traversal', {\n cause: {\n code: 'GRAPHRUN_TRAVERSAL',\n node: n,\n path,\n cause: er as Error,\n },\n })\n Error.captureStackTrace(e, this.from)\n throw e\n }\n }\n\n /**\n * Method that handles successful visit results\n * @internal\n */\n handleValue(value: Result, n: Node) {\n this.results.set(n, value)\n this.settled.set(n, {\n status: 'fulfilled',\n value,\n })\n }\n}\n\n/** Asynchronous graph runner */\nexport class Runner<Node, Result> extends RunnerBase<\n Node,\n Result,\n false,\n RunnerOptions<Node, Result>\n> {\n /** Map of nodes currently awaiting completion */\n readonly running = new Map<Node, Promise<void>>()\n\n /** Track which node's promise is waiting for which other nodes */\n readonly promiseWaiting = new Map<Node, Set<Node>>()\n\n async getDeps(n: Node) {\n /* c8 ignore next */\n if (this.abortController.signal.aborted) return []\n const deps = await this.options.getDeps(n)\n for (const d of deps) {\n const dependents = this.dependents.get(d) ?? new Set()\n this.dependents.set(d, dependents)\n dependents.add(n)\n const depDD = this.directDependents.get(d) ?? new Set()\n this.directDependents.set(d, depDD)\n depDD.add(n)\n }\n return deps\n }\n\n async visit(\n n: Node,\n path: Node[],\n depResults: DepResults<Node, Result>,\n ) {\n const { signal } = this.abortController\n return this.options.visit(n, signal, path, depResults)\n }\n\n async onCycle(n: Node, cycle: Node[], path: Node[]) {\n /* c8 ignore next */\n if (this.abortController.signal.aborted) return\n await this.options.onCycle?.(n, cycle, path)\n }\n\n /**\n * Check if node `target` is waiting (directly or transitively) for node `n`.\n * If so, making `n` wait for `target` would create a deadlock.\n */\n #isWaitingFor(\n target: Node,\n n: Node,\n visited = new Set<Node>(),\n ): boolean {\n if (visited.has(target)) return false\n visited.add(target)\n const waiting = this.promiseWaiting.get(target)\n if (!waiting) return false\n if (waiting.has(n)) return true\n for (const w of waiting) {\n if (this.#isWaitingFor(w, n, visited)) return true\n }\n return false\n }\n\n async #walk(n: Node, path: Node[]) {\n const r = this.running.get(n)\n /* c8 ignore next */\n if (r) return r\n /* c8 ignore start */\n if (this.settled.get(n)) return\n /* c8 ignore stop */\n const p = this.#step(n, path).then(\n () => {\n this.running.delete(n)\n this.promiseWaiting.delete(n)\n },\n /* c8 ignore start - handled deeper in the chain */\n (er: unknown) => {\n this.running.delete(n)\n this.promiseWaiting.delete(n)\n throw er\n },\n /* c8 ignore stop */\n )\n this.running.set(n, p)\n return p\n }\n\n async #step(n: Node, path: Node[]) {\n const dependents = this.dependents.get(n) ?? new Set()\n this.dependents.set(n, dependents)\n\n const deps = await this.getDeps(n)\n const awaiting: Promise<void>[] = []\n const depPath = [...path, n]\n\n // Initialize waiting set for this node\n const waitingOn = new Set<Node>()\n this.promiseWaiting.set(n, waitingOn)\n\n for (const d of deps) {\n /* c8 ignore next */\n if (this.abortController.signal.aborted) return\n // self-link, skip\n if (d === n) continue\n if (this.cycleCheck(n, depPath, d)) continue\n /* c8 ignore next */\n if (this.settled.get(d)) continue\n\n const runningPromise = this.running.get(d)\n if (runningPromise) {\n // Check if d is already waiting for n (promise-level cycle)\n if (this.#isWaitingFor(d, n)) {\n // Treat as cycle to prevent deadlock\n void this.onCycle(d, [n, d], depPath)\n continue\n }\n }\n\n // Record that n is waiting for d\n waitingOn.add(d)\n awaiting.push(runningPromise ?? this.#walk(d, depPath))\n }\n\n /* c8 ignore next */\n if (this.abortController.signal.aborted) return\n await Promise.all(awaiting)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.abortController.signal.aborted) return\n const depRes = new Map<Node, Result | undefined>(\n deps.map(d => [d, this.results.get(d)]),\n )\n try {\n this.handleValue(await this.visit(n, path, depRes), n)\n } catch (er) {\n this.handleError(er, n, path)\n }\n }\n\n async run(): Promise<void> {\n const promises: Promise<void>[] = []\n for (const n of this.options.graph) {\n promises.push(this.#walk(n, []))\n }\n await Promise.all(promises)\n }\n}\n\n/** Synchronous graph runner */\nexport class RunnerSync<Node, Result> extends RunnerBase<\n Node,\n Result,\n true,\n RunnerOptionsSync<Node, Result>\n> {\n getDeps(n: Node) {\n if (this.abortController.signal.aborted) return []\n return this.options.getDeps(n)\n }\n\n visit(n: Node, path: Node[], depResults: DepResults<Node, Result>) {\n const { signal } = this.abortController\n return this.options.visit(n, signal, path, depResults)\n }\n\n onCycle(n: Node, cycle: Node[], path: Node[]) {\n /* c8 ignore next */\n if (this.abortController.signal.aborted) return\n this.options.onCycle?.(n, cycle, path)\n }\n\n #walk(n: Node, path: Node[]) {\n /* c8 ignore start */\n if (this.settled.get(n)) return\n /* c8 ignore stop */\n this.#step(n, path)\n }\n\n #step(n: Node, path: Node[]) {\n const dependents = this.dependents.get(n) ?? new Set()\n this.dependents.set(n, dependents)\n\n const deps = this.getDeps(n)\n const depPath = [...path, n]\n for (const d of deps) {\n if (this.abortController.signal.aborted) return\n /* c8 ignore next */\n if (d === n) continue\n if (this.cycleCheck(n, depPath, d)) continue\n if (!this.settled.get(d)) this.#walk(d, depPath)\n }\n\n if (this.abortController.signal.aborted) return\n const depRes = new Map<Node, Result | undefined>(\n deps.map(d => [d, this.results.get(d)]),\n )\n try {\n this.handleValue(this.visit(n, path, depRes), n)\n } catch (er) {\n this.handleError(er, n, path)\n }\n }\n\n run(): Map<Node, Result> {\n for (const n of this.options.graph) {\n this.#walk(n, [])\n }\n return this.results\n }\n}\n\n/**\n * Asynchronous graph traversal method\n *\n * If `failFast:false` is set in the options, then an AggregateError\n * will be raised if there were any failures. Otherwise, a normal Error\n * is raised on failure.\n */\nexport const graphRun = async <Node, Result>(\n options: RunnerOptions<Node, Result>,\n): Promise<Map<Node, Result>> => {\n const runner = new Runner(options, graphRun)\n await runner.run()\n if (runner.errors.length) {\n const e = new AggregateError(\n runner.errors,\n 'failed graph traversal',\n )\n Error.captureStackTrace(e, graphRun)\n throw e\n }\n return runner.results\n}\n\n/**\n * Synchronous graph traversal method\n *\n * If `failFast:false` is set in the options, then an AggregateError\n * will be thrown if there were any failures. Otherwise, a normal Error\n * is thrown on failure.\n */\nexport const graphRunSync = <Node, Result>(\n options: RunnerOptionsSync<Node, Result>,\n): Map<Node, Result> => {\n const runner = new RunnerSync(options, graphRunSync)\n runner.run()\n if (runner.errors.length) {\n const e = new AggregateError(\n runner.errors,\n 'failed graph traversal',\n )\n Error.captureStackTrace(e, graphRunSync)\n throw e\n }\n return runner.results\n}\n\n/**\n * Asynchronous graph traversal, capturing all error/result\n * statuses.\n */\nexport const allSettled = async <Node, Result>(\n options: RunnerOptions<Node, Result>,\n): Promise<SettledMap<Node, Result>> => {\n const runner = new Runner(\n { ...options, failFast: false },\n allSettled,\n )\n await runner.run()\n return runner.settled\n}\n\n/**\n * Synchronous graph traversal, capturing all error/result\n * statuses.\n */\nexport const allSettledSync = <Node, Result>(\n options: RunnerOptionsSync<Node, Result>,\n): SettledMap<Node, Result> => {\n const runner = new RunnerSync(\n { ...options, failFast: false },\n allSettledSync,\n )\n runner.run()\n return runner.settled\n}\n\n/**\n * Asynchronous graph traversal, returning the first successful visit.\n * If all visits fail, then an AggregateError is raised with all errors\n * encountered.\n */\nexport const any = async <Node, Result>(\n options: RunnerOptions<Node, Result>,\n): Promise<Result> => {\n const ac = new AbortController()\n let result!: Result\n let found = false\n const runner = new Runner<Node, Result>(\n {\n ...options,\n failFast: false,\n signal: ac.signal,\n visit: async (node, signal, path, depResults) => {\n try {\n result = await options.visit(node, signal, path, depResults)\n found = true\n ac.abort('found')\n } catch {}\n return result\n },\n },\n any,\n )\n await runner.run()\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!found) {\n const e = new AggregateError(\n runner.errors,\n 'failed graph traversal',\n )\n Error.captureStackTrace(e, any)\n throw e\n }\n return result\n}\n\n/**\n * Synchronous graph traversal, returning the first successful visit.\n * If all visits fail, then an AggregateError is thrown with all errors\n * encountered.\n */\nexport const anySync = <Node, Result>(\n options: RunnerOptionsSync<Node, Result>,\n): Result => {\n const ac = new AbortController()\n let result!: Result\n let found = false\n const runner = new RunnerSync<Node, Result>(\n {\n ...options,\n failFast: false,\n signal: ac.signal,\n visit: (node, signal, path, depResults) => {\n try {\n result = options.visit(node, signal, path, depResults)\n found = true\n ac.abort('found')\n } catch {}\n return result\n },\n },\n anySync,\n )\n runner.run()\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!found) {\n const e = new AggregateError(\n runner.errors,\n 'failed graph traversal',\n )\n Error.captureStackTrace(e, anySync)\n throw e\n }\n return result\n}\n\n/**\n * Asynchronous graph traversal, resolving or rejecting when the first visit\n * resolves or rejects.\n */\nexport const race = async <Node, Result>(\n options: RunnerOptions<Node, Result>,\n): Promise<Result> => {\n const ac = new AbortController()\n let result!: Result\n const runner = new Runner<Node, Result>(\n {\n ...options,\n failFast: true,\n signal: ac.signal,\n visit: async (node, signal, path, depResults) => {\n try {\n result = await options.visit(node, signal, path, depResults)\n ac.abort('found')\n /* c8 ignore next */\n } catch {}\n return result\n },\n },\n race,\n )\n await runner.run()\n return result\n}\n\n/**\n * Synchronous graph traversal, returning or throwing when the first visit\n * is completed.\n */\nexport const raceSync = <Node, Result>(\n options: RunnerOptionsSync<Node, Result>,\n): Result => {\n const ac = new AbortController()\n let result!: Result\n const runner = new RunnerSync<Node, Result>(\n {\n ...options,\n failFast: true,\n signal: ac.signal,\n visit: (node, signal, path, depResults) => {\n try {\n result = options.visit(node, signal, path, depResults)\n ac.abort('found')\n /* c8 ignore next */\n } catch {}\n return result\n },\n },\n race,\n )\n runner.run()\n return result\n}\n"]}
package/package.json CHANGED
@@ -1,4 +1,53 @@
1
1
  {
2
2
  "name": "@vltpkg/graph-run",
3
- "version": "0.0.0"
4
- }
3
+ "description": "Run operations on a graph, maximizing parallelism",
4
+ "version": "1.0.0-rc.11",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/vltpkg/vltpkg.git",
8
+ "directory": "src/graph-run"
9
+ },
10
+ "author": "vlt technology inc. <support@vlt.sh> (http://vlt.sh)",
11
+ "dependencies": {},
12
+ "devDependencies": {
13
+ "@eslint/js": "^9.39.1",
14
+ "@types/node": "^22.19.2",
15
+ "eslint": "^9.39.1",
16
+ "prettier": "^3.7.4",
17
+ "tap": "^21.5.0",
18
+ "typedoc": "~0.27.9",
19
+ "typescript": "5.7.3",
20
+ "typescript-eslint": "^8.49.0"
21
+ },
22
+ "license": "BSD-2-Clause-Patent",
23
+ "engines": {
24
+ "node": ">=22.9.0"
25
+ },
26
+ "tap": {
27
+ "extends": "../../tap-config.yaml"
28
+ },
29
+ "prettier": "../../.prettierrc.js",
30
+ "module": "./dist/index.js",
31
+ "type": "module",
32
+ "exports": {
33
+ "./package.json": "./package.json",
34
+ ".": {
35
+ "import": {
36
+ "default": "./dist/index.js"
37
+ }
38
+ }
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "scripts": {
44
+ "format": "prettier --write . --log-level warn --ignore-path ../../.prettierignore --cache",
45
+ "format:check": "prettier --check . --ignore-path ../../.prettierignore --cache",
46
+ "lint": "eslint . --fix",
47
+ "lint:check": "eslint .",
48
+ "snap": "tap",
49
+ "test": "tap",
50
+ "posttest": "tsc --noEmit",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }