@rushstack/operation-graph 0.2.40 → 0.3.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.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "@rushstack/operation-graph",
3
3
  "entries": [
4
+ {
5
+ "version": "0.3.0",
6
+ "tag": "@rushstack/operation-graph_v0.3.0",
7
+ "date": "Sat, 21 Jun 2025 00:13:15 GMT",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "comment": "(BREAKING CHANGE) The OperationExecutionManager `beforeExecute` and `afterExecute` hooks have been made async and renamed to `beforeExecuteAsync` and `afterExecuteAsync`. Operations now have an optional `metadata` field that can be used to store arbitrary data."
12
+ }
13
+ ]
14
+ }
15
+ },
16
+ {
17
+ "version": "0.2.41",
18
+ "tag": "@rushstack/operation-graph_v0.2.41",
19
+ "date": "Thu, 01 May 2025 00:11:12 GMT",
20
+ "comments": {
21
+ "dependency": [
22
+ {
23
+ "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`"
24
+ },
25
+ {
26
+ "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`"
27
+ }
28
+ ]
29
+ }
30
+ },
4
31
  {
5
32
  "version": "0.2.40",
6
33
  "tag": "@rushstack/operation-graph_v0.2.40",
package/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Change Log - @rushstack/operation-graph
2
2
 
3
- This log was last generated on Tue, 25 Mar 2025 15:11:15 GMT and should not be manually modified.
3
+ This log was last generated on Sat, 21 Jun 2025 00:13:15 GMT and should not be manually modified.
4
+
5
+ ## 0.3.0
6
+ Sat, 21 Jun 2025 00:13:15 GMT
7
+
8
+ ### Minor changes
9
+
10
+ - (BREAKING CHANGE) The OperationExecutionManager `beforeExecute` and `afterExecute` hooks have been made async and renamed to `beforeExecuteAsync` and `afterExecuteAsync`. Operations now have an optional `metadata` field that can be used to store arbitrary data.
11
+
12
+ ## 0.2.41
13
+ Thu, 01 May 2025 00:11:12 GMT
14
+
15
+ _Version update only_
4
16
 
5
17
  ## 0.2.40
6
18
  Tue, 25 Mar 2025 15:11:15 GMT
@@ -42,11 +42,11 @@ export declare interface IExecuteOperationContext extends Omit<IOperationRunnerC
42
42
  /**
43
43
  * Function to invoke before execution of an operation, for logging.
44
44
  */
45
- beforeExecute(operation: Operation, state: IOperationState): void;
45
+ beforeExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;
46
46
  /**
47
47
  * Function to invoke after execution of an operation, for logging.
48
48
  */
49
- afterExecute(operation: Operation, state: IOperationState): void;
49
+ afterExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;
50
50
  /**
51
51
  * Function used to schedule the concurrency-limited execution of an operation.
52
52
  *
@@ -78,18 +78,22 @@ export declare interface IExitCommandMessage {
78
78
  *
79
79
  * @beta
80
80
  */
81
- export declare interface IOperationExecutionOptions {
81
+ export declare interface IOperationExecutionOptions<TOperationMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
82
82
  abortSignal: AbortSignal;
83
83
  parallelism: number;
84
84
  terminal: ITerminal;
85
85
  requestRun?: (requestor?: string) => void;
86
+ beforeExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;
87
+ afterExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;
88
+ beforeExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;
89
+ afterExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;
86
90
  }
87
91
 
88
92
  /**
89
93
  * Options for constructing a new Operation.
90
94
  * @beta
91
95
  */
92
- export declare interface IOperationOptions {
96
+ export declare interface IOperationOptions<TMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
93
97
  /**
94
98
  * The name of this operation, for logging.
95
99
  */
@@ -97,7 +101,7 @@ export declare interface IOperationOptions {
97
101
  /**
98
102
  * The group that this operation belongs to. Will be used for logging and duration tracking.
99
103
  */
100
- groupName?: string | undefined;
104
+ group?: OperationGroupRecord<TGroupMetadata> | undefined;
101
105
  /**
102
106
  * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of
103
107
  * running the operation.
@@ -107,6 +111,10 @@ export declare interface IOperationOptions {
107
111
  * The weight used by the scheduler to determine order of execution.
108
112
  */
109
113
  weight?: number | undefined;
114
+ /**
115
+ * The metadata for this operation.
116
+ */
117
+ metadata?: TMetadata | undefined;
110
118
  }
111
119
 
112
120
  /**
@@ -282,19 +290,19 @@ export declare interface IWatchLoopState {
282
290
  *
283
291
  * @beta
284
292
  */
285
- export declare class Operation implements IOperationStates {
293
+ export declare class Operation<TMetadata extends {} = {}, TGroupMetadata extends {} = {}> implements IOperationStates {
286
294
  /**
287
295
  * A set of all dependencies which must be executed before this operation is complete.
288
296
  */
289
- readonly dependencies: Set<Operation>;
297
+ readonly dependencies: Set<Operation<TMetadata, TGroupMetadata>>;
290
298
  /**
291
299
  * A set of all operations that wait for this operation.
292
300
  */
293
- readonly consumers: Set<Operation>;
301
+ readonly consumers: Set<Operation<TMetadata, TGroupMetadata>>;
294
302
  /**
295
303
  * If specified, the name of a grouping to which this Operation belongs, for logging start and end times.
296
304
  */
297
- readonly groupName: string | undefined;
305
+ readonly group: OperationGroupRecord<TGroupMetadata> | undefined;
298
306
  /**
299
307
  * The name of this operation, for logging.
300
308
  */
@@ -363,9 +371,10 @@ export declare class Operation implements IOperationStates {
363
371
  * This is used to track state from the `requestRun` callback passed to the runner.
364
372
  */
365
373
  private _runPending;
366
- constructor(options?: IOperationOptions);
367
- addDependency(dependency: Operation): void;
368
- deleteDependency(dependency: Operation): void;
374
+ readonly metadata: TMetadata;
375
+ constructor(options?: IOperationOptions<TMetadata, TGroupMetadata>);
376
+ addDependency(dependency: Operation<TMetadata, TGroupMetadata>): void;
377
+ deleteDependency(dependency: Operation<TMetadata, TGroupMetadata>): void;
369
378
  reset(): void;
370
379
  /**
371
380
  * @internal
@@ -394,27 +403,23 @@ export declare class OperationError extends Error {
394
403
  *
395
404
  * @beta
396
405
  */
397
- export declare class OperationExecutionManager {
406
+ export declare class OperationExecutionManager<TOperationMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
398
407
  /**
399
408
  * The set of operations that will be executed
400
409
  */
401
410
  private readonly _operations;
402
- /**
403
- * Group records are metadata-only entities used for tracking the start and end of a set of related tasks.
404
- * This is the only extent to which the operation graph is aware of Heft phases.
405
- */
406
- private readonly _groupRecordByName;
407
411
  /**
408
412
  * The total number of non-silent operations in the graph.
409
413
  * Silent operations are generally used to simplify the construction of the graph.
410
414
  */
411
415
  private readonly _trackedOperationCount;
412
- constructor(operations: ReadonlySet<Operation>);
416
+ private readonly _groupRecords;
417
+ constructor(operations: ReadonlySet<Operation<TOperationMetadata, TGroupMetadata>>);
413
418
  /**
414
419
  * Executes all operations which have been registered, returning a promise which is resolved when all the
415
420
  * operations are completed successfully, or rejects when any operation fails.
416
421
  */
417
- executeAsync(executionOptions: IOperationExecutionOptions): Promise<OperationStatus>;
422
+ executeAsync(executionOptions: IOperationExecutionOptions<TOperationMetadata, TGroupMetadata>): Promise<OperationStatus>;
418
423
  }
419
424
 
420
425
  /**
@@ -422,18 +427,19 @@ export declare class OperationExecutionManager {
422
427
  *
423
428
  * @beta
424
429
  */
425
- export declare class OperationGroupRecord {
430
+ export declare class OperationGroupRecord<TMetadata extends {} = {}> {
426
431
  private readonly _operations;
427
432
  private _remainingOperations;
428
433
  private _groupStopwatch;
429
434
  private _hasCancellations;
430
435
  private _hasFailures;
431
436
  readonly name: string;
437
+ readonly metadata: TMetadata;
432
438
  get duration(): number;
433
439
  get finished(): boolean;
434
440
  get hasCancellations(): boolean;
435
441
  get hasFailures(): boolean;
436
- constructor(name: string);
442
+ constructor(name: string, metadata?: TMetadata);
437
443
  addOperation(operation: Operation): void;
438
444
  startTimer(): void;
439
445
  setOperationAsComplete(operation: Operation, state: IOperationState): void;
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.52.1"
8
+ "packageVersion": "7.52.5"
9
9
  }
10
10
  ]
11
11
  }
@@ -1,11 +1,12 @@
1
1
  import type { ITerminal } from '@rushstack/terminal';
2
2
  import type { IOperationRunner, IOperationRunnerContext, IOperationState, IOperationStates } from './IOperationRunner';
3
3
  import { OperationStatus } from './OperationStatus';
4
+ import type { OperationGroupRecord } from './OperationGroupRecord';
4
5
  /**
5
6
  * Options for constructing a new Operation.
6
7
  * @beta
7
8
  */
8
- export interface IOperationOptions {
9
+ export interface IOperationOptions<TMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
9
10
  /**
10
11
  * The name of this operation, for logging.
11
12
  */
@@ -13,7 +14,7 @@ export interface IOperationOptions {
13
14
  /**
14
15
  * The group that this operation belongs to. Will be used for logging and duration tracking.
15
16
  */
16
- groupName?: string | undefined;
17
+ group?: OperationGroupRecord<TGroupMetadata> | undefined;
17
18
  /**
18
19
  * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of
19
20
  * running the operation.
@@ -23,6 +24,10 @@ export interface IOperationOptions {
23
24
  * The weight used by the scheduler to determine order of execution.
24
25
  */
25
26
  weight?: number | undefined;
27
+ /**
28
+ * The metadata for this operation.
29
+ */
30
+ metadata?: TMetadata | undefined;
26
31
  }
27
32
  /**
28
33
  * Information provided to `executeAsync` by the `OperationExecutionManager`.
@@ -33,11 +38,11 @@ export interface IExecuteOperationContext extends Omit<IOperationRunnerContext,
33
38
  /**
34
39
  * Function to invoke before execution of an operation, for logging.
35
40
  */
36
- beforeExecute(operation: Operation, state: IOperationState): void;
41
+ beforeExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;
37
42
  /**
38
43
  * Function to invoke after execution of an operation, for logging.
39
44
  */
40
- afterExecute(operation: Operation, state: IOperationState): void;
45
+ afterExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;
41
46
  /**
42
47
  * Function used to schedule the concurrency-limited execution of an operation.
43
48
  *
@@ -63,19 +68,19 @@ export interface IExecuteOperationContext extends Omit<IOperationRunnerContext,
63
68
  *
64
69
  * @beta
65
70
  */
66
- export declare class Operation implements IOperationStates {
71
+ export declare class Operation<TMetadata extends {} = {}, TGroupMetadata extends {} = {}> implements IOperationStates {
67
72
  /**
68
73
  * A set of all dependencies which must be executed before this operation is complete.
69
74
  */
70
- readonly dependencies: Set<Operation>;
75
+ readonly dependencies: Set<Operation<TMetadata, TGroupMetadata>>;
71
76
  /**
72
77
  * A set of all operations that wait for this operation.
73
78
  */
74
- readonly consumers: Set<Operation>;
79
+ readonly consumers: Set<Operation<TMetadata, TGroupMetadata>>;
75
80
  /**
76
81
  * If specified, the name of a grouping to which this Operation belongs, for logging start and end times.
77
82
  */
78
- readonly groupName: string | undefined;
83
+ readonly group: OperationGroupRecord<TGroupMetadata> | undefined;
79
84
  /**
80
85
  * The name of this operation, for logging.
81
86
  */
@@ -144,9 +149,10 @@ export declare class Operation implements IOperationStates {
144
149
  * This is used to track state from the `requestRun` callback passed to the runner.
145
150
  */
146
151
  private _runPending;
147
- constructor(options?: IOperationOptions);
148
- addDependency(dependency: Operation): void;
149
- deleteDependency(dependency: Operation): void;
152
+ readonly metadata: TMetadata;
153
+ constructor(options?: IOperationOptions<TMetadata, TGroupMetadata>);
154
+ addDependency(dependency: Operation<TMetadata, TGroupMetadata>): void;
155
+ deleteDependency(dependency: Operation<TMetadata, TGroupMetadata>): void;
150
156
  reset(): void;
151
157
  /**
152
158
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"Operation.d.ts","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/B;;;OAGG;IACH,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAEtC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CAAC,uBAAuB,EAAE,YAAY,GAAG,YAAY,CAAC;IAC1G;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAElE;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAEjE;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAE9F;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,qBAAa,SAAU,YAAW,gBAAgB;IAChD;;OAEG;IACH,SAAgB,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAwB;IACpE;;OAEG;IACH,SAAgB,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAwB;IACjE;;OAEG;IACH,SAAgB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzC;;;OAGG;IACI,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAa;IAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACI,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAa;IAE1D;;;;;;;;;;OAUG;IACI,MAAM,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACI,SAAS,EAAE,eAAe,GAAG,SAAS,CAAa;IAE1D;;OAEG;IACI,KAAK,EAAE,eAAe,GAAG,SAAS,CAAa;IAEtD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAmD;IAEnE;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAiB;gBAEjB,OAAO,CAAC,EAAE,iBAAiB;IAOvC,aAAa,CAAC,UAAU,EAAE,SAAS,GAAG,IAAI;IAK1C,gBAAgB,CAAC,UAAU,EAAE,SAAS,GAAG,IAAI;IAK7C,KAAK,IAAI,IAAI;IAepB;;OAEG;IACU,aAAa,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,eAAe,CAAC;YAazE,kBAAkB;CA0HjC"}
1
+ {"version":3,"file":"Operation.d.ts","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":"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"}
package/lib/Operation.js CHANGED
@@ -77,10 +77,14 @@ class Operation {
77
77
  * This is used to track state from the `requestRun` callback passed to the runner.
78
78
  */
79
79
  this._runPending = true;
80
- this.groupName = options?.groupName;
80
+ this.group = options?.group;
81
81
  this.runner = options?.runner;
82
82
  this.weight = options?.weight || 1;
83
83
  this.name = options?.name;
84
+ this.metadata = options?.metadata || {};
85
+ if (this.group) {
86
+ this.group.addOperation(this);
87
+ }
84
88
  }
85
89
  addDependency(dependency) {
86
90
  this.dependencies.add(dependency);
@@ -175,7 +179,7 @@ class Operation {
175
179
  innerState.status = OperationStatus_1.OperationStatus.Aborted;
176
180
  return innerState.status;
177
181
  }
178
- context.beforeExecute(this, innerState);
182
+ await context.beforeExecuteAsync(this, innerState);
179
183
  innerState.stopwatch.start();
180
184
  innerState.status = OperationStatus_1.OperationStatus.Executing;
181
185
  // Mark that the operation has been started at least once.
@@ -208,7 +212,7 @@ class Operation {
208
212
  }
209
213
  }
210
214
  state.stopwatch.stop();
211
- context.afterExecute(this, state);
215
+ await context.afterExecuteAsync(this, state);
212
216
  return state.status;
213
217
  }, /* priority */ this.criticalPathLength ?? 0);
214
218
  return state.status;
@@ -1 +1 @@
1
- {"version":3,"file":"Operation.js","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { InternalError } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport { Stopwatch } from './Stopwatch';\nimport type {\n IOperationRunner,\n IOperationRunnerContext,\n IOperationState,\n IOperationStates\n} from './IOperationRunner';\nimport type { OperationError } from './OperationError';\nimport { OperationStatus } from './OperationStatus';\n\n/**\n * Options for constructing a new Operation.\n * @beta\n */\nexport interface IOperationOptions {\n /**\n * The name of this operation, for logging.\n */\n name?: string | undefined;\n\n /**\n * The group that this operation belongs to. Will be used for logging and duration tracking.\n */\n groupName?: string | undefined;\n\n /**\n * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of\n * running the operation.\n */\n runner?: IOperationRunner | undefined;\n\n /**\n * The weight used by the scheduler to determine order of execution.\n */\n weight?: number | undefined;\n}\n\n/**\n * Information provided to `executeAsync` by the `OperationExecutionManager`.\n *\n * @beta\n */\nexport interface IExecuteOperationContext extends Omit<IOperationRunnerContext, 'isFirstRun' | 'requestRun'> {\n /**\n * Function to invoke before execution of an operation, for logging.\n */\n beforeExecute(operation: Operation, state: IOperationState): void;\n\n /**\n * Function to invoke after execution of an operation, for logging.\n */\n afterExecute(operation: Operation, state: IOperationState): void;\n\n /**\n * Function used to schedule the concurrency-limited execution of an operation.\n *\n * Will return OperationStatus.Aborted if execution is aborted before the task executes.\n */\n queueWork(workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus>;\n\n /**\n * A callback to the overarching orchestrator to request that the operation be invoked again.\n * Used in watch mode to signal that inputs have changed.\n */\n requestRun?: (requestor?: string) => void;\n\n /**\n * Terminal to write output to.\n */\n terminal: ITerminal;\n}\n\n/**\n * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the\n * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose\n * implementation manages the actual process of running a single operation.\n *\n * The graph of `Operation` instances will be cloned into a separate execution graph after processing.\n *\n * @beta\n */\nexport class Operation implements IOperationStates {\n /**\n * A set of all dependencies which must be executed before this operation is complete.\n */\n public readonly dependencies: Set<Operation> = new Set<Operation>();\n /**\n * A set of all operations that wait for this operation.\n */\n public readonly consumers: Set<Operation> = new Set<Operation>();\n /**\n * If specified, the name of a grouping to which this Operation belongs, for logging start and end times.\n */\n public readonly groupName: string | undefined;\n /**\n * The name of this operation, for logging.\n */\n public readonly name: string | undefined;\n\n /**\n * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of\n * running the operation.\n */\n public runner: IOperationRunner | undefined = undefined;\n\n /**\n * This number represents how far away this Operation is from the furthest \"root\" operation (i.e.\n * an operation with no consumers). This helps us to calculate the critical path (i.e. the\n * longest chain of projects which must be executed in order, thereby limiting execution speed\n * of the entire operation tree.\n *\n * This number is calculated via a memoized depth-first search, and when choosing the next\n * operation to execute, the operation with the highest criticalPathLength is chosen.\n *\n * Example:\n * (0) A\n * \\\\\n * (1) B C (0) (applications)\n * \\\\ /|\\\\\n * \\\\ / | \\\\\n * (2) D | X (1) (utilities)\n * | / \\\\\n * |/ \\\\\n * (2) Y Z (2) (other utilities)\n *\n * All roots (A & C) have a criticalPathLength of 0.\n * B has a score of 1, since A depends on it.\n * D has a score of 2, since we look at the longest chain (e.g D-\\>B-\\>A is longer than D-\\>C)\n * X has a score of 1, since the only package which depends on it is A\n * Z has a score of 2, since only X depends on it, and X has a score of 1\n * Y has a score of 2, since the chain Y-\\>X-\\>C is longer than Y-\\>C\n *\n * The algorithm is implemented in AsyncOperationQueue.ts as calculateCriticalPathLength()\n */\n public criticalPathLength: number | undefined = undefined;\n\n /**\n * The weight for this operation. This scalar is the contribution of this operation to the\n * `criticalPathLength` calculation above. Modify to indicate the following:\n * - `weight` === 1: indicates that this operation has an average duration\n * - `weight` &gt; 1: indicates that this operation takes longer than average and so the scheduler\n * should try to favor starting it over other, shorter operations. An example might be an operation that\n * bundles an entire application and runs whole-program optimization.\n * - `weight` &lt; 1: indicates that this operation takes less time than average and so the scheduler\n * should favor other, longer operations over it. An example might be an operation to unpack a cached\n * output, or an operation using NullOperationRunner, which might use a value of 0.\n */\n public weight: number;\n\n /**\n * The state of this operation the previous time a manager was invoked.\n */\n public lastState: IOperationState | undefined = undefined;\n\n /**\n * The current state of this operation\n */\n public state: IOperationState | undefined = undefined;\n\n /**\n * A cached execution promise for the current OperationExecutionManager invocation of this operation.\n */\n private _promise: Promise<OperationStatus> | undefined = undefined;\n\n /**\n * If true, then a run of this operation is currently wanted.\n * This is used to track state from the `requestRun` callback passed to the runner.\n */\n private _runPending: boolean = true;\n\n public constructor(options?: IOperationOptions) {\n this.groupName = options?.groupName;\n this.runner = options?.runner;\n this.weight = options?.weight || 1;\n this.name = options?.name;\n }\n\n public addDependency(dependency: Operation): void {\n this.dependencies.add(dependency);\n dependency.consumers.add(this);\n }\n\n public deleteDependency(dependency: Operation): void {\n this.dependencies.delete(dependency);\n dependency.consumers.delete(this);\n }\n\n public reset(): void {\n // Reset operation state\n this.lastState = this.state;\n\n this.state = {\n status: this.dependencies.size > 0 ? OperationStatus.Waiting : OperationStatus.Ready,\n hasBeenRun: this.lastState?.hasBeenRun ?? false,\n error: undefined,\n stopwatch: new Stopwatch()\n };\n\n this._promise = undefined;\n this._runPending = true;\n }\n\n /**\n * @internal\n */\n public async _executeAsync(context: IExecuteOperationContext): Promise<OperationStatus> {\n const { state } = this;\n if (!state) {\n throw new Error(`Operation state has not been initialized.`);\n }\n\n if (!this._promise) {\n this._promise = this._executeInnerAsync(context, state);\n }\n\n return this._promise;\n }\n\n private async _executeInnerAsync(\n context: IExecuteOperationContext,\n rawState: IOperationState\n ): Promise<OperationStatus> {\n const state: IOperationState = rawState;\n const { runner } = this;\n\n const dependencyResults: PromiseSettledResult<OperationStatus>[] = await Promise.allSettled(\n Array.from(this.dependencies, (dependency: Operation) => dependency._executeAsync(context))\n );\n\n const { abortSignal, requestRun, queueWork } = context;\n\n if (abortSignal.aborted) {\n state.status = OperationStatus.Aborted;\n return state.status;\n }\n\n for (const result of dependencyResults) {\n if (\n result.status === 'rejected' ||\n result.value === OperationStatus.Blocked ||\n result.value === OperationStatus.Failure\n ) {\n state.status = OperationStatus.Blocked;\n return state.status;\n }\n }\n\n state.status = OperationStatus.Ready;\n\n const innerContext: IOperationRunnerContext = {\n abortSignal,\n isFirstRun: !state.hasBeenRun,\n requestRun: requestRun\n ? () => {\n switch (this.state?.status) {\n case OperationStatus.Waiting:\n case OperationStatus.Ready:\n case OperationStatus.Executing:\n // If current status has not yet resolved to a fixed value,\n // re-executing this operation does not require a full rerun\n // of the operation graph. Simply mark that a run is requested.\n\n // This variable is on the Operation instead of the\n // containing closure to deal with scenarios in which\n // the runner hangs on to an old copy of the callback.\n this._runPending = true;\n return;\n\n case OperationStatus.Blocked:\n case OperationStatus.Aborted:\n case OperationStatus.Failure:\n case OperationStatus.NoOp:\n case OperationStatus.Success:\n // The requestRun callback is assumed to remain constant\n // throughout the lifetime of the process, so it is safe\n // to capture here.\n return requestRun(this.name);\n default:\n // This line is here to enforce exhaustiveness\n const currentStatus: undefined = this.state?.status;\n throw new InternalError(`Unexpected status: ${currentStatus}`);\n }\n }\n : undefined\n };\n\n // eslint-disable-next-line require-atomic-updates\n state.status = await queueWork(async (): Promise<OperationStatus> => {\n // Redundant variable to satisfy require-atomic-updates\n const innerState: IOperationState = state;\n\n if (abortSignal.aborted) {\n innerState.status = OperationStatus.Aborted;\n return innerState.status;\n }\n\n context.beforeExecute(this, innerState);\n\n innerState.stopwatch.start();\n innerState.status = OperationStatus.Executing;\n // Mark that the operation has been started at least once.\n innerState.hasBeenRun = true;\n\n while (this._runPending) {\n this._runPending = false;\n try {\n // We don't support aborting in the middle of a runner's execution.\n innerState.status = runner ? await runner.executeAsync(innerContext) : OperationStatus.NoOp;\n } catch (error) {\n innerState.status = OperationStatus.Failure;\n innerState.error = error as OperationError;\n }\n\n // Since runner.executeAsync is async, a change could have occurred that requires re-execution\n // This operation is still active, so can re-execute immediately, rather than forcing a whole\n // new execution pass.\n\n // As currently written, this does mean that if a job is scheduled with higher priority while\n // this operation is still executing, it will still wait for this retry. This may not be desired\n // and if it becomes a problem, the retry loop will need to be moved outside of the `queueWork` call.\n // This introduces complexity regarding tracking of timing and start/end logging, however.\n\n if (this._runPending) {\n if (abortSignal.aborted) {\n innerState.status = OperationStatus.Aborted;\n break;\n } else {\n context.terminal.writeLine(`Immediate rerun requested. Executing.`);\n }\n }\n }\n\n state.stopwatch.stop();\n context.afterExecute(this, state);\n\n return state.status;\n }, /* priority */ this.criticalPathLength ?? 0);\n\n return state.status;\n }\n}\n"]}
1
+ {"version":3,"file":"Operation.js","sourceRoot":"","sources":["../src/Operation.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { InternalError } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport { Stopwatch } from './Stopwatch';\nimport type {\n IOperationRunner,\n IOperationRunnerContext,\n IOperationState,\n IOperationStates\n} from './IOperationRunner';\nimport type { OperationError } from './OperationError';\nimport { OperationStatus } from './OperationStatus';\nimport type { OperationGroupRecord } from './OperationGroupRecord';\n\n/**\n * Options for constructing a new Operation.\n * @beta\n */\nexport interface IOperationOptions<TMetadata extends {} = {}, TGroupMetadata extends {} = {}> {\n /**\n * The name of this operation, for logging.\n */\n name?: string | undefined;\n\n /**\n * The group that this operation belongs to. Will be used for logging and duration tracking.\n */\n group?: OperationGroupRecord<TGroupMetadata> | undefined;\n\n /**\n * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of\n * running the operation.\n */\n runner?: IOperationRunner | undefined;\n\n /**\n * The weight used by the scheduler to determine order of execution.\n */\n weight?: number | undefined;\n\n /**\n * The metadata for this operation.\n */\n metadata?: TMetadata | undefined;\n}\n\n/**\n * Information provided to `executeAsync` by the `OperationExecutionManager`.\n *\n * @beta\n */\nexport interface IExecuteOperationContext extends Omit<IOperationRunnerContext, 'isFirstRun' | 'requestRun'> {\n /**\n * Function to invoke before execution of an operation, for logging.\n */\n beforeExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;\n\n /**\n * Function to invoke after execution of an operation, for logging.\n */\n afterExecuteAsync(operation: Operation, state: IOperationState): Promise<void>;\n\n /**\n * Function used to schedule the concurrency-limited execution of an operation.\n *\n * Will return OperationStatus.Aborted if execution is aborted before the task executes.\n */\n queueWork(workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus>;\n\n /**\n * A callback to the overarching orchestrator to request that the operation be invoked again.\n * Used in watch mode to signal that inputs have changed.\n */\n requestRun?: (requestor?: string) => void;\n\n /**\n * Terminal to write output to.\n */\n terminal: ITerminal;\n}\n\n/**\n * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the\n * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose\n * implementation manages the actual process of running a single operation.\n *\n * The graph of `Operation` instances will be cloned into a separate execution graph after processing.\n *\n * @beta\n */\nexport class Operation<TMetadata extends {} = {}, TGroupMetadata extends {} = {}>\n implements IOperationStates\n{\n /**\n * A set of all dependencies which must be executed before this operation is complete.\n */\n public readonly dependencies: Set<Operation<TMetadata, TGroupMetadata>> = new Set<\n Operation<TMetadata, TGroupMetadata>\n >();\n /**\n * A set of all operations that wait for this operation.\n */\n public readonly consumers: Set<Operation<TMetadata, TGroupMetadata>> = new Set<\n Operation<TMetadata, TGroupMetadata>\n >();\n /**\n * If specified, the name of a grouping to which this Operation belongs, for logging start and end times.\n */\n public readonly group: OperationGroupRecord<TGroupMetadata> | undefined;\n /**\n * The name of this operation, for logging.\n */\n public readonly name: string | undefined;\n\n /**\n * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of\n * running the operation.\n */\n public runner: IOperationRunner | undefined = undefined;\n\n /**\n * This number represents how far away this Operation is from the furthest \"root\" operation (i.e.\n * an operation with no consumers). This helps us to calculate the critical path (i.e. the\n * longest chain of projects which must be executed in order, thereby limiting execution speed\n * of the entire operation tree.\n *\n * This number is calculated via a memoized depth-first search, and when choosing the next\n * operation to execute, the operation with the highest criticalPathLength is chosen.\n *\n * Example:\n * (0) A\n * \\\\\n * (1) B C (0) (applications)\n * \\\\ /|\\\\\n * \\\\ / | \\\\\n * (2) D | X (1) (utilities)\n * | / \\\\\n * |/ \\\\\n * (2) Y Z (2) (other utilities)\n *\n * All roots (A & C) have a criticalPathLength of 0.\n * B has a score of 1, since A depends on it.\n * D has a score of 2, since we look at the longest chain (e.g D-\\>B-\\>A is longer than D-\\>C)\n * X has a score of 1, since the only package which depends on it is A\n * Z has a score of 2, since only X depends on it, and X has a score of 1\n * Y has a score of 2, since the chain Y-\\>X-\\>C is longer than Y-\\>C\n *\n * The algorithm is implemented in AsyncOperationQueue.ts as calculateCriticalPathLength()\n */\n public criticalPathLength: number | undefined = undefined;\n\n /**\n * The weight for this operation. This scalar is the contribution of this operation to the\n * `criticalPathLength` calculation above. Modify to indicate the following:\n * - `weight` === 1: indicates that this operation has an average duration\n * - `weight` &gt; 1: indicates that this operation takes longer than average and so the scheduler\n * should try to favor starting it over other, shorter operations. An example might be an operation that\n * bundles an entire application and runs whole-program optimization.\n * - `weight` &lt; 1: indicates that this operation takes less time than average and so the scheduler\n * should favor other, longer operations over it. An example might be an operation to unpack a cached\n * output, or an operation using NullOperationRunner, which might use a value of 0.\n */\n public weight: number;\n\n /**\n * The state of this operation the previous time a manager was invoked.\n */\n public lastState: IOperationState | undefined = undefined;\n\n /**\n * The current state of this operation\n */\n public state: IOperationState | undefined = undefined;\n\n /**\n * A cached execution promise for the current OperationExecutionManager invocation of this operation.\n */\n private _promise: Promise<OperationStatus> | undefined = undefined;\n\n /**\n * If true, then a run of this operation is currently wanted.\n * This is used to track state from the `requestRun` callback passed to the runner.\n */\n private _runPending: boolean = true;\n\n public readonly metadata: TMetadata;\n\n public constructor(options?: IOperationOptions<TMetadata, TGroupMetadata>) {\n this.group = options?.group;\n this.runner = options?.runner;\n this.weight = options?.weight || 1;\n this.name = options?.name;\n this.metadata = options?.metadata || ({} as TMetadata);\n\n if (this.group) {\n this.group.addOperation(this);\n }\n }\n\n public addDependency(dependency: Operation<TMetadata, TGroupMetadata>): void {\n this.dependencies.add(dependency);\n dependency.consumers.add(this);\n }\n\n public deleteDependency(dependency: Operation<TMetadata, TGroupMetadata>): void {\n this.dependencies.delete(dependency);\n dependency.consumers.delete(this);\n }\n\n public reset(): void {\n // Reset operation state\n this.lastState = this.state;\n\n this.state = {\n status: this.dependencies.size > 0 ? OperationStatus.Waiting : OperationStatus.Ready,\n hasBeenRun: this.lastState?.hasBeenRun ?? false,\n error: undefined,\n stopwatch: new Stopwatch()\n };\n\n this._promise = undefined;\n this._runPending = true;\n }\n\n /**\n * @internal\n */\n public async _executeAsync(context: IExecuteOperationContext): Promise<OperationStatus> {\n const { state } = this;\n if (!state) {\n throw new Error(`Operation state has not been initialized.`);\n }\n\n if (!this._promise) {\n this._promise = this._executeInnerAsync(context, state);\n }\n\n return this._promise;\n }\n\n private async _executeInnerAsync(\n context: IExecuteOperationContext,\n rawState: IOperationState\n ): Promise<OperationStatus> {\n const state: IOperationState = rawState;\n const { runner } = this;\n\n const dependencyResults: PromiseSettledResult<OperationStatus>[] = await Promise.allSettled(\n Array.from(this.dependencies, (dependency: Operation) => dependency._executeAsync(context))\n );\n\n const { abortSignal, requestRun, queueWork } = context;\n\n if (abortSignal.aborted) {\n state.status = OperationStatus.Aborted;\n return state.status;\n }\n\n for (const result of dependencyResults) {\n if (\n result.status === 'rejected' ||\n result.value === OperationStatus.Blocked ||\n result.value === OperationStatus.Failure\n ) {\n state.status = OperationStatus.Blocked;\n return state.status;\n }\n }\n\n state.status = OperationStatus.Ready;\n\n const innerContext: IOperationRunnerContext = {\n abortSignal,\n isFirstRun: !state.hasBeenRun,\n requestRun: requestRun\n ? () => {\n switch (this.state?.status) {\n case OperationStatus.Waiting:\n case OperationStatus.Ready:\n case OperationStatus.Executing:\n // If current status has not yet resolved to a fixed value,\n // re-executing this operation does not require a full rerun\n // of the operation graph. Simply mark that a run is requested.\n\n // This variable is on the Operation instead of the\n // containing closure to deal with scenarios in which\n // the runner hangs on to an old copy of the callback.\n this._runPending = true;\n return;\n\n case OperationStatus.Blocked:\n case OperationStatus.Aborted:\n case OperationStatus.Failure:\n case OperationStatus.NoOp:\n case OperationStatus.Success:\n // The requestRun callback is assumed to remain constant\n // throughout the lifetime of the process, so it is safe\n // to capture here.\n return requestRun(this.name);\n default:\n // This line is here to enforce exhaustiveness\n const currentStatus: undefined = this.state?.status;\n throw new InternalError(`Unexpected status: ${currentStatus}`);\n }\n }\n : undefined\n };\n\n // eslint-disable-next-line require-atomic-updates\n state.status = await queueWork(async (): Promise<OperationStatus> => {\n // Redundant variable to satisfy require-atomic-updates\n const innerState: IOperationState = state;\n\n if (abortSignal.aborted) {\n innerState.status = OperationStatus.Aborted;\n return innerState.status;\n }\n\n await context.beforeExecuteAsync(this, innerState);\n\n innerState.stopwatch.start();\n innerState.status = OperationStatus.Executing;\n // Mark that the operation has been started at least once.\n innerState.hasBeenRun = true;\n\n while (this._runPending) {\n this._runPending = false;\n try {\n // We don't support aborting in the middle of a runner's execution.\n innerState.status = runner ? await runner.executeAsync(innerContext) : OperationStatus.NoOp;\n } catch (error) {\n innerState.status = OperationStatus.Failure;\n innerState.error = error as OperationError;\n }\n\n // Since runner.executeAsync is async, a change could have occurred that requires re-execution\n // This operation is still active, so can re-execute immediately, rather than forcing a whole\n // new execution pass.\n\n // As currently written, this does mean that if a job is scheduled with higher priority while\n // this operation is still executing, it will still wait for this retry. This may not be desired\n // and if it becomes a problem, the retry loop will need to be moved outside of the `queueWork` call.\n // This introduces complexity regarding tracking of timing and start/end logging, however.\n\n if (this._runPending) {\n if (abortSignal.aborted) {\n innerState.status = OperationStatus.Aborted;\n break;\n } else {\n context.terminal.writeLine(`Immediate rerun requested. Executing.`);\n }\n }\n }\n\n state.stopwatch.stop();\n await context.afterExecuteAsync(this, state);\n\n return state.status;\n }, /* priority */ this.criticalPathLength ?? 0);\n\n return state.status;\n }\n}\n"]}
@@ -1,16 +1,21 @@
1
1
  import type { ITerminal } from '@rushstack/terminal';
2
2
  import type { Operation } from './Operation';
3
+ import type { OperationGroupRecord } from './OperationGroupRecord';
3
4
  import { OperationStatus } from './OperationStatus';
4
5
  /**
5
6
  * Options for the current run.
6
7
  *
7
8
  * @beta
8
9
  */
9
- export interface IOperationExecutionOptions {
10
+ export interface IOperationExecutionOptions<TOperationMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
10
11
  abortSignal: AbortSignal;
11
12
  parallelism: number;
12
13
  terminal: ITerminal;
13
14
  requestRun?: (requestor?: string) => void;
15
+ beforeExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;
16
+ afterExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;
17
+ beforeExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;
18
+ afterExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;
14
19
  }
15
20
  /**
16
21
  * A class which manages the execution of a set of tasks with interdependencies.
@@ -20,26 +25,22 @@ export interface IOperationExecutionOptions {
20
25
  *
21
26
  * @beta
22
27
  */
23
- export declare class OperationExecutionManager {
28
+ export declare class OperationExecutionManager<TOperationMetadata extends {} = {}, TGroupMetadata extends {} = {}> {
24
29
  /**
25
30
  * The set of operations that will be executed
26
31
  */
27
32
  private readonly _operations;
28
- /**
29
- * Group records are metadata-only entities used for tracking the start and end of a set of related tasks.
30
- * This is the only extent to which the operation graph is aware of Heft phases.
31
- */
32
- private readonly _groupRecordByName;
33
33
  /**
34
34
  * The total number of non-silent operations in the graph.
35
35
  * Silent operations are generally used to simplify the construction of the graph.
36
36
  */
37
37
  private readonly _trackedOperationCount;
38
- constructor(operations: ReadonlySet<Operation>);
38
+ private readonly _groupRecords;
39
+ constructor(operations: ReadonlySet<Operation<TOperationMetadata, TGroupMetadata>>);
39
40
  /**
40
41
  * Executes all operations which have been registered, returning a promise which is resolved when all the
41
42
  * operations are completed successfully, or rejects when any operation fails.
42
43
  */
43
- executeAsync(executionOptions: IOperationExecutionOptions): Promise<OperationStatus>;
44
+ executeAsync(executionOptions: IOperationExecutionOptions<TOperationMetadata, TGroupMetadata>): Promise<OperationStatus>;
44
45
  }
45
46
  //# sourceMappingURL=OperationExecutionManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OperationExecutionManager.d.ts","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EAA4B,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IAEpB,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,qBAAa,yBAAyB;IACpC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IACvE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE7B,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC;IAqCrD;;;OAGG;IACU,YAAY,CAAC,gBAAgB,EAAE,0BAA0B,GAAG,OAAO,CAAC,eAAe,CAAC;CAqHlG"}
1
+ {"version":3,"file":"OperationExecutionManager.d.ts","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EAA4B,SAAS,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;GAIG;AACH,MAAM,WAAW,0BAA0B,CACzC,kBAAkB,SAAS,EAAE,GAAG,EAAE,EAClC,cAAc,SAAS,EAAE,GAAG,EAAE;IAE9B,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IAEpB,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C,2BAA2B,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,cAAc,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1G,0BAA0B,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,cAAc,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzG,gCAAgC,CAAC,EAAE,CAAC,cAAc,EAAE,oBAAoB,CAAC,cAAc,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3G,+BAA+B,CAAC,EAAE,CAAC,cAAc,EAAE,oBAAoB,CAAC,cAAc,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3G;AAED;;;;;;;GAOG;AACH,qBAAa,yBAAyB,CAAC,kBAAkB,SAAS,EAAE,GAAG,EAAE,EAAE,cAAc,SAAS,EAAE,GAAG,EAAE;IACvG;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkD;IAC9E;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAEhD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4C;gBAEvD,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;IA2BzF;;;OAGG;IACU,YAAY,CACvB,gBAAgB,EAAE,0BAA0B,CAAC,kBAAkB,EAAE,cAAc,CAAC,GAC/E,OAAO,CAAC,eAAe,CAAC;CAiI5B"}
@@ -4,7 +4,6 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.OperationExecutionManager = void 0;
6
6
  const node_core_library_1 = require("@rushstack/node-core-library");
7
- const OperationGroupRecord_1 = require("./OperationGroupRecord");
8
7
  const OperationStatus_1 = require("./OperationStatus");
9
8
  const calculateCriticalPath_1 = require("./calculateCriticalPath");
10
9
  const WorkQueue_1 = require("./WorkQueue");
@@ -18,17 +17,8 @@ const WorkQueue_1 = require("./WorkQueue");
18
17
  */
19
18
  class OperationExecutionManager {
20
19
  constructor(operations) {
21
- const groupRecordByName = new Map();
22
- this._groupRecordByName = groupRecordByName;
23
20
  let trackedOperationCount = 0;
24
21
  for (const operation of operations) {
25
- const { groupName } = operation;
26
- let group = undefined;
27
- if (groupName && !(group = groupRecordByName.get(groupName))) {
28
- group = new OperationGroupRecord_1.OperationGroupRecord(groupName);
29
- groupRecordByName.set(groupName, group);
30
- }
31
- group?.addOperation(operation);
32
22
  if (!operation.runner?.silent) {
33
23
  // Only count non-silent operations
34
24
  trackedOperationCount++;
@@ -36,6 +26,7 @@ class OperationExecutionManager {
36
26
  }
37
27
  this._trackedOperationCount = trackedOperationCount;
38
28
  this._operations = (0, calculateCriticalPath_1.calculateCriticalPathLengths)(operations);
29
+ this._groupRecords = new Set(Array.from(this._operations, (e) => e.group).filter((e) => e !== undefined));
39
30
  for (const consumer of operations) {
40
31
  for (const dependency of consumer.dependencies) {
41
32
  if (!operations.has(dependency)) {
@@ -58,8 +49,7 @@ class OperationExecutionManager {
58
49
  const startedGroups = new Set();
59
50
  const finishedGroups = new Set();
60
51
  const maxParallelism = Math.min(this._operations.length, parallelism);
61
- const groupRecords = this._groupRecordByName;
62
- for (const groupRecord of groupRecords.values()) {
52
+ for (const groupRecord of this._groupRecords) {
63
53
  groupRecord.reset();
64
54
  }
65
55
  for (const operation of this._operations) {
@@ -78,25 +68,25 @@ class OperationExecutionManager {
78
68
  queueWork: (workFn, priority) => {
79
69
  return workQueue.pushAsync(workFn, priority);
80
70
  },
81
- beforeExecute: (operation) => {
71
+ beforeExecuteAsync: async (operation) => {
82
72
  // Initialize group if uninitialized and log the group name
83
- const { groupName } = operation;
84
- const groupRecord = groupName
85
- ? groupRecords.get(groupName)
86
- : undefined;
87
- if (groupRecord && !startedGroups.has(groupRecord)) {
88
- startedGroups.add(groupRecord);
89
- groupRecord.startTimer();
90
- terminal.writeLine(` ---- ${groupRecord.name} started ---- `);
73
+ const { group, runner } = operation;
74
+ if (group) {
75
+ if (!startedGroups.has(group)) {
76
+ startedGroups.add(group);
77
+ group.startTimer();
78
+ terminal.writeLine(` ---- ${group.name} started ---- `);
79
+ await executionOptions.beforeExecuteOperationGroupAsync?.(group);
80
+ }
81
+ }
82
+ if (!runner?.silent) {
83
+ await executionOptions.beforeExecuteOperationAsync?.(operation);
91
84
  }
92
85
  },
93
- afterExecute: (operation, state) => {
94
- const { groupName } = operation;
95
- const groupRecord = groupName
96
- ? groupRecords.get(groupName)
97
- : undefined;
98
- if (groupRecord) {
99
- groupRecord.setOperationAsComplete(operation, state);
86
+ afterExecuteAsync: async (operation, state) => {
87
+ const { group, runner } = operation;
88
+ if (group) {
89
+ group.setOperationAsComplete(operation, state);
100
90
  }
101
91
  if (state.status === OperationStatus_1.OperationStatus.Failure) {
102
92
  // This operation failed. Mark it as such and all reachable dependents as blocked.
@@ -108,15 +98,21 @@ class OperationExecutionManager {
108
98
  }
109
99
  hasReportedFailures = true;
110
100
  }
111
- // Log out the group name and duration if it is the last operation in the group
112
- if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {
113
- finishedGroups.add(groupRecord);
114
- const finishedLoggingWord = groupRecord.hasFailures
115
- ? 'encountered an error'
116
- : groupRecord.hasCancellations
117
- ? 'cancelled'
118
- : 'finished';
119
- terminal.writeLine(` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `);
101
+ if (!runner?.silent) {
102
+ await executionOptions.afterExecuteOperationAsync?.(operation);
103
+ }
104
+ if (group) {
105
+ // Log out the group name and duration if it is the last operation in the group
106
+ if (group?.finished && !finishedGroups.has(group)) {
107
+ finishedGroups.add(group);
108
+ const finishedLoggingWord = group.hasFailures
109
+ ? 'encountered an error'
110
+ : group.hasCancellations
111
+ ? 'cancelled'
112
+ : 'finished';
113
+ terminal.writeLine(` ---- ${group.name} ${finishedLoggingWord} (${group.duration.toFixed(3)}s) ---- `);
114
+ await executionOptions.afterExecuteOperationGroupAsync?.(group);
115
+ }
120
116
  }
121
117
  }
122
118
  };
@@ -1 +1 @@
1
- {"version":3,"file":"OperationExecutionManager.js","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Async } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { IExecuteOperationContext, Operation } from './Operation';\nimport { OperationGroupRecord } from './OperationGroupRecord';\nimport { OperationStatus } from './OperationStatus';\nimport { calculateCriticalPathLengths } from './calculateCriticalPath';\nimport { WorkQueue } from './WorkQueue';\n\n/**\n * Options for the current run.\n *\n * @beta\n */\nexport interface IOperationExecutionOptions {\n abortSignal: AbortSignal;\n parallelism: number;\n terminal: ITerminal;\n\n requestRun?: (requestor?: string) => void;\n}\n\n/**\n * A class which manages the execution of a set of tasks with interdependencies.\n * Initially, and at the end of each task execution, all unblocked tasks\n * are added to a ready queue which is then executed. This is done continually until all\n * tasks are complete, or prematurely fails if any of the tasks fail.\n *\n * @beta\n */\nexport class OperationExecutionManager {\n /**\n * The set of operations that will be executed\n */\n private readonly _operations: Operation[];\n /**\n * Group records are metadata-only entities used for tracking the start and end of a set of related tasks.\n * This is the only extent to which the operation graph is aware of Heft phases.\n */\n private readonly _groupRecordByName: Map<string, OperationGroupRecord>;\n /**\n * The total number of non-silent operations in the graph.\n * Silent operations are generally used to simplify the construction of the graph.\n */\n private readonly _trackedOperationCount: number;\n\n public constructor(operations: ReadonlySet<Operation>) {\n const groupRecordByName: Map<string, OperationGroupRecord> = new Map();\n this._groupRecordByName = groupRecordByName;\n\n let trackedOperationCount: number = 0;\n for (const operation of operations) {\n const { groupName } = operation;\n let group: OperationGroupRecord | undefined = undefined;\n if (groupName && !(group = groupRecordByName.get(groupName))) {\n group = new OperationGroupRecord(groupName);\n groupRecordByName.set(groupName, group);\n }\n\n group?.addOperation(operation);\n\n if (!operation.runner?.silent) {\n // Only count non-silent operations\n trackedOperationCount++;\n }\n }\n\n this._trackedOperationCount = trackedOperationCount;\n\n this._operations = calculateCriticalPathLengths(operations);\n\n for (const consumer of operations) {\n for (const dependency of consumer.dependencies) {\n if (!operations.has(dependency)) {\n throw new Error(\n `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` +\n `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.`\n );\n }\n }\n }\n }\n\n /**\n * Executes all operations which have been registered, returning a promise which is resolved when all the\n * operations are completed successfully, or rejects when any operation fails.\n */\n public async executeAsync(executionOptions: IOperationExecutionOptions): Promise<OperationStatus> {\n let hasReportedFailures: boolean = false;\n\n const { abortSignal, parallelism, terminal, requestRun } = executionOptions;\n\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n const startedGroups: Set<OperationGroupRecord> = new Set();\n const finishedGroups: Set<OperationGroupRecord> = new Set();\n\n const maxParallelism: number = Math.min(this._operations.length, parallelism);\n const groupRecords: Map<string, OperationGroupRecord> = this._groupRecordByName;\n for (const groupRecord of groupRecords.values()) {\n groupRecord.reset();\n }\n\n for (const operation of this._operations) {\n operation.reset();\n }\n\n terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`);\n\n const workQueueAbortController: AbortController = new AbortController();\n const abortHandler: () => void = () => workQueueAbortController.abort();\n abortSignal.addEventListener('abort', abortHandler, { once: true });\n try {\n const workQueue: WorkQueue = new WorkQueue(workQueueAbortController.signal);\n\n const executionContext: IExecuteOperationContext = {\n terminal,\n abortSignal,\n\n requestRun,\n\n queueWork: (workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus> => {\n return workQueue.pushAsync(workFn, priority);\n },\n\n beforeExecute: (operation: Operation): void => {\n // Initialize group if uninitialized and log the group name\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord && !startedGroups.has(groupRecord)) {\n startedGroups.add(groupRecord);\n groupRecord.startTimer();\n terminal.writeLine(` ---- ${groupRecord.name} started ---- `);\n }\n },\n\n afterExecute: (operation: Operation, state: IOperationState): void => {\n const { groupName } = operation;\n const groupRecord: OperationGroupRecord | undefined = groupName\n ? groupRecords.get(groupName)\n : undefined;\n if (groupRecord) {\n groupRecord.setOperationAsComplete(operation, state);\n }\n\n if (state.status === OperationStatus.Failure) {\n // This operation failed. Mark it as such and all reachable dependents as blocked.\n // Failed operations get reported, even if silent.\n // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure.\n const message: string | undefined = state.error?.message;\n if (message) {\n terminal.writeErrorLine(message);\n }\n hasReportedFailures = true;\n }\n\n // Log out the group name and duration if it is the last operation in the group\n if (groupRecord?.finished && !finishedGroups.has(groupRecord)) {\n finishedGroups.add(groupRecord);\n const finishedLoggingWord: string = groupRecord.hasFailures\n ? 'encountered an error'\n : groupRecord.hasCancellations\n ? 'cancelled'\n : 'finished';\n terminal.writeLine(\n ` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- `\n );\n }\n }\n };\n\n const workQueuePromise: Promise<void> = Async.forEachAsync(\n workQueue,\n (workFn: () => Promise<void>) => workFn(),\n {\n concurrency: maxParallelism\n }\n );\n\n await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext)));\n\n // Terminate queue execution.\n workQueueAbortController.abort();\n await workQueuePromise;\n } finally {\n // Cleanup resources\n abortSignal.removeEventListener('abort', abortHandler);\n }\n\n const finalStatus: OperationStatus =\n this._trackedOperationCount === 0\n ? OperationStatus.NoOp\n : abortSignal.aborted\n ? OperationStatus.Aborted\n : hasReportedFailures\n ? OperationStatus.Failure\n : OperationStatus.Success;\n\n return finalStatus;\n }\n}\n"]}
1
+ {"version":3,"file":"OperationExecutionManager.js","sourceRoot":"","sources":["../src/OperationExecutionManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { Async } from '@rushstack/node-core-library';\nimport type { ITerminal } from '@rushstack/terminal';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { IExecuteOperationContext, Operation } from './Operation';\nimport type { OperationGroupRecord } from './OperationGroupRecord';\nimport { OperationStatus } from './OperationStatus';\nimport { calculateCriticalPathLengths } from './calculateCriticalPath';\nimport { WorkQueue } from './WorkQueue';\n\n/**\n * Options for the current run.\n *\n * @beta\n */\nexport interface IOperationExecutionOptions<\n TOperationMetadata extends {} = {},\n TGroupMetadata extends {} = {}\n> {\n abortSignal: AbortSignal;\n parallelism: number;\n terminal: ITerminal;\n\n requestRun?: (requestor?: string) => void;\n\n beforeExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;\n afterExecuteOperationAsync?: (operation: Operation<TOperationMetadata, TGroupMetadata>) => Promise<void>;\n beforeExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;\n afterExecuteOperationGroupAsync?: (operationGroup: OperationGroupRecord<TGroupMetadata>) => Promise<void>;\n}\n\n/**\n * A class which manages the execution of a set of tasks with interdependencies.\n * Initially, and at the end of each task execution, all unblocked tasks\n * are added to a ready queue which is then executed. This is done continually until all\n * tasks are complete, or prematurely fails if any of the tasks fail.\n *\n * @beta\n */\nexport class OperationExecutionManager<TOperationMetadata extends {} = {}, TGroupMetadata extends {} = {}> {\n /**\n * The set of operations that will be executed\n */\n private readonly _operations: Operation<TOperationMetadata, TGroupMetadata>[];\n /**\n * The total number of non-silent operations in the graph.\n * Silent operations are generally used to simplify the construction of the graph.\n */\n private readonly _trackedOperationCount: number;\n\n private readonly _groupRecords: Set<OperationGroupRecord<TGroupMetadata>>;\n\n public constructor(operations: ReadonlySet<Operation<TOperationMetadata, TGroupMetadata>>) {\n let trackedOperationCount: number = 0;\n for (const operation of operations) {\n if (!operation.runner?.silent) {\n // Only count non-silent operations\n trackedOperationCount++;\n }\n }\n\n this._trackedOperationCount = trackedOperationCount;\n\n this._operations = calculateCriticalPathLengths(operations);\n\n this._groupRecords = new Set(Array.from(this._operations, (e) => e.group).filter((e) => e !== undefined));\n\n for (const consumer of operations) {\n for (const dependency of consumer.dependencies) {\n if (!operations.has(dependency)) {\n throw new Error(\n `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` +\n `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.`\n );\n }\n }\n }\n }\n\n /**\n * Executes all operations which have been registered, returning a promise which is resolved when all the\n * operations are completed successfully, or rejects when any operation fails.\n */\n public async executeAsync(\n executionOptions: IOperationExecutionOptions<TOperationMetadata, TGroupMetadata>\n ): Promise<OperationStatus> {\n let hasReportedFailures: boolean = false;\n\n const { abortSignal, parallelism, terminal, requestRun } = executionOptions;\n\n if (abortSignal.aborted) {\n return OperationStatus.Aborted;\n }\n\n const startedGroups: Set<OperationGroupRecord> = new Set();\n const finishedGroups: Set<OperationGroupRecord> = new Set();\n\n const maxParallelism: number = Math.min(this._operations.length, parallelism);\n\n for (const groupRecord of this._groupRecords) {\n groupRecord.reset();\n }\n\n for (const operation of this._operations) {\n operation.reset();\n }\n\n terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`);\n\n const workQueueAbortController: AbortController = new AbortController();\n const abortHandler: () => void = () => workQueueAbortController.abort();\n abortSignal.addEventListener('abort', abortHandler, { once: true });\n try {\n const workQueue: WorkQueue = new WorkQueue(workQueueAbortController.signal);\n\n const executionContext: IExecuteOperationContext = {\n terminal,\n abortSignal,\n\n requestRun,\n\n queueWork: (workFn: () => Promise<OperationStatus>, priority: number): Promise<OperationStatus> => {\n return workQueue.pushAsync(workFn, priority);\n },\n\n beforeExecuteAsync: async (\n operation: Operation<TOperationMetadata, TGroupMetadata>\n ): Promise<void> => {\n // Initialize group if uninitialized and log the group name\n const { group, runner } = operation;\n if (group) {\n if (!startedGroups.has(group)) {\n startedGroups.add(group);\n group.startTimer();\n terminal.writeLine(` ---- ${group.name} started ---- `);\n await executionOptions.beforeExecuteOperationGroupAsync?.(group);\n }\n }\n if (!runner?.silent) {\n await executionOptions.beforeExecuteOperationAsync?.(operation);\n }\n },\n\n afterExecuteAsync: async (\n operation: Operation<TOperationMetadata, TGroupMetadata>,\n state: IOperationState\n ): Promise<void> => {\n const { group, runner } = operation;\n if (group) {\n group.setOperationAsComplete(operation, state);\n }\n\n if (state.status === OperationStatus.Failure) {\n // This operation failed. Mark it as such and all reachable dependents as blocked.\n // Failed operations get reported, even if silent.\n // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure.\n const message: string | undefined = state.error?.message;\n if (message) {\n terminal.writeErrorLine(message);\n }\n hasReportedFailures = true;\n }\n\n if (!runner?.silent) {\n await executionOptions.afterExecuteOperationAsync?.(operation);\n }\n\n if (group) {\n // Log out the group name and duration if it is the last operation in the group\n if (group?.finished && !finishedGroups.has(group)) {\n finishedGroups.add(group);\n const finishedLoggingWord: string = group.hasFailures\n ? 'encountered an error'\n : group.hasCancellations\n ? 'cancelled'\n : 'finished';\n terminal.writeLine(\n ` ---- ${group.name} ${finishedLoggingWord} (${group.duration.toFixed(3)}s) ---- `\n );\n await executionOptions.afterExecuteOperationGroupAsync?.(group);\n }\n }\n }\n };\n\n const workQueuePromise: Promise<void> = Async.forEachAsync(\n workQueue,\n (workFn: () => Promise<void>) => workFn(),\n {\n concurrency: maxParallelism\n }\n );\n\n await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext)));\n\n // Terminate queue execution.\n workQueueAbortController.abort();\n await workQueuePromise;\n } finally {\n // Cleanup resources\n abortSignal.removeEventListener('abort', abortHandler);\n }\n\n const finalStatus: OperationStatus =\n this._trackedOperationCount === 0\n ? OperationStatus.NoOp\n : abortSignal.aborted\n ? OperationStatus.Aborted\n : hasReportedFailures\n ? OperationStatus.Failure\n : OperationStatus.Success;\n\n return finalStatus;\n }\n}\n"]}
@@ -5,18 +5,19 @@ import type { Operation } from './Operation';
5
5
  *
6
6
  * @beta
7
7
  */
8
- export declare class OperationGroupRecord {
8
+ export declare class OperationGroupRecord<TMetadata extends {} = {}> {
9
9
  private readonly _operations;
10
10
  private _remainingOperations;
11
11
  private _groupStopwatch;
12
12
  private _hasCancellations;
13
13
  private _hasFailures;
14
14
  readonly name: string;
15
+ readonly metadata: TMetadata;
15
16
  get duration(): number;
16
17
  get finished(): boolean;
17
18
  get hasCancellations(): boolean;
18
19
  get hasFailures(): boolean;
19
- constructor(name: string);
20
+ constructor(name: string, metadata?: TMetadata);
20
21
  addOperation(operation: Operation): void;
21
22
  startTimer(): void;
22
23
  setOperationAsComplete(operation: Operation, state: IOperationState): void;
@@ -1 +1 @@
1
- {"version":3,"file":"OperationGroupRecord.d.ts","sourceRoot":"","sources":["../src/OperationGroupRecord.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C;;;;GAIG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,oBAAoB,CAA6B;IAEzD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,YAAY,CAAkB;IAEtC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED,IAAW,WAAW,IAAI,OAAO,CAEhC;gBAEkB,IAAI,EAAE,MAAM;IAIxB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAIxC,UAAU,IAAI,IAAI;IAKlB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAiB1E,KAAK,IAAI,IAAI;CAMrB"}
1
+ {"version":3,"file":"OperationGroupRecord.d.ts","sourceRoot":"","sources":["../src/OperationGroupRecord.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C;;;;GAIG;AACH,qBAAa,oBAAoB,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,oBAAoB,CAA6B;IAEzD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,YAAY,CAAkB;IAEtC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,EAAE,SAAS,CAAC;IAEpC,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED,IAAW,WAAW,IAAI,OAAO,CAEhC;gBAEkB,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,SAA2B;IAK/D,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAIxC,UAAU,IAAI,IAAI;IAKlB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAiB1E,KAAK,IAAI,IAAI;CAMrB"}
@@ -24,13 +24,14 @@ class OperationGroupRecord {
24
24
  get hasFailures() {
25
25
  return this._hasFailures;
26
26
  }
27
- constructor(name) {
27
+ constructor(name, metadata = {}) {
28
28
  this._operations = new Set();
29
29
  this._remainingOperations = new Set();
30
30
  this._groupStopwatch = new Stopwatch_1.Stopwatch();
31
31
  this._hasCancellations = false;
32
32
  this._hasFailures = false;
33
33
  this.name = name;
34
+ this.metadata = metadata;
34
35
  }
35
36
  addOperation(operation) {
36
37
  this._operations.add(operation);
@@ -1 +1 @@
1
- {"version":3,"file":"OperationGroupRecord.js","sourceRoot":"","sources":["../src/OperationGroupRecord.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,oEAA6D;AAI7D,uDAAoD;AACpD,2CAAwC;AAExC;;;;GAIG;AACH,MAAa,oBAAoB;IAU/B,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,IAAW,gBAAgB;QACzB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAED,IAAW,WAAW;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,YAAmB,IAAY;QAzBd,gBAAW,GAAmB,IAAI,GAAG,EAAE,CAAC;QACjD,yBAAoB,GAAmB,IAAI,GAAG,EAAE,CAAC;QAEjD,oBAAe,GAAc,IAAI,qBAAS,EAAE,CAAC;QAC7C,sBAAiB,GAAY,KAAK,CAAC;QACnC,iBAAY,GAAY,KAAK,CAAC;QAqBpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAEM,YAAY,CAAC,SAAoB;QACtC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAEM,UAAU;QACf,yFAAyF;QACzF,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;IAEM,sBAAsB,CAAC,SAAoB,EAAE,KAAsB;QACxE,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,iCAAa,CAAC,aAAa,SAAS,CAAC,IAAI,wBAAwB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1F,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE,CAAC;YAC7C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE,CAAC;YACpD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,oBAAoB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;CACF;AA9DD,oDA8DC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { InternalError } from '@rushstack/node-core-library';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { Operation } from './Operation';\nimport { OperationStatus } from './OperationStatus';\nimport { Stopwatch } from './Stopwatch';\n\n/**\n * Meta-entity that tracks information about a group of related operations.\n *\n * @beta\n */\nexport class OperationGroupRecord {\n private readonly _operations: Set<Operation> = new Set();\n private _remainingOperations: Set<Operation> = new Set();\n\n private _groupStopwatch: Stopwatch = new Stopwatch();\n private _hasCancellations: boolean = false;\n private _hasFailures: boolean = false;\n\n public readonly name: string;\n\n public get duration(): number {\n return this._groupStopwatch ? this._groupStopwatch.duration : 0;\n }\n\n public get finished(): boolean {\n return this._remainingOperations.size === 0;\n }\n\n public get hasCancellations(): boolean {\n return this._hasCancellations;\n }\n\n public get hasFailures(): boolean {\n return this._hasFailures;\n }\n\n public constructor(name: string) {\n this.name = name;\n }\n\n public addOperation(operation: Operation): void {\n this._operations.add(operation);\n }\n\n public startTimer(): void {\n // Keep this undefined until needed, then start to avoid subsequent calls to startTimer()\n this._groupStopwatch.start();\n }\n\n public setOperationAsComplete(operation: Operation, state: IOperationState): void {\n if (!this._remainingOperations.has(operation)) {\n throw new InternalError(`Operation ${operation.name} is not in the group ${this.name}`);\n }\n\n if (state.status === OperationStatus.Aborted) {\n this._hasCancellations = true;\n } else if (state.status === OperationStatus.Failure) {\n this._hasFailures = true;\n }\n\n this._remainingOperations.delete(operation);\n if (this._remainingOperations.size === 0) {\n this._groupStopwatch.stop();\n }\n }\n\n public reset(): void {\n this._remainingOperations = new Set(this._operations);\n this._groupStopwatch.reset();\n this._hasCancellations = false;\n this._hasFailures = false;\n }\n}\n"]}
1
+ {"version":3,"file":"OperationGroupRecord.js","sourceRoot":"","sources":["../src/OperationGroupRecord.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,oEAA6D;AAI7D,uDAAoD;AACpD,2CAAwC;AAExC;;;;GAIG;AACH,MAAa,oBAAoB;IAW/B,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,IAAW,gBAAgB;QACzB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAED,IAAW,WAAW;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,YAAmB,IAAY,EAAE,WAAsB,EAAe;QA1BrD,gBAAW,GAAmB,IAAI,GAAG,EAAE,CAAC;QACjD,yBAAoB,GAAmB,IAAI,GAAG,EAAE,CAAC;QAEjD,oBAAe,GAAc,IAAI,qBAAS,EAAE,CAAC;QAC7C,sBAAiB,GAAY,KAAK,CAAC;QACnC,iBAAY,GAAY,KAAK,CAAC;QAsBpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEM,YAAY,CAAC,SAAoB;QACtC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAEM,UAAU;QACf,yFAAyF;QACzF,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;IAEM,sBAAsB,CAAC,SAAoB,EAAE,KAAsB;QACxE,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,iCAAa,CAAC,aAAa,SAAS,CAAC,IAAI,wBAAwB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1F,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE,CAAC;YAC7C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE,CAAC;YACpD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,oBAAoB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;CACF;AAhED,oDAgEC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { InternalError } from '@rushstack/node-core-library';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { Operation } from './Operation';\nimport { OperationStatus } from './OperationStatus';\nimport { Stopwatch } from './Stopwatch';\n\n/**\n * Meta-entity that tracks information about a group of related operations.\n *\n * @beta\n */\nexport class OperationGroupRecord<TMetadata extends {} = {}> {\n private readonly _operations: Set<Operation> = new Set();\n private _remainingOperations: Set<Operation> = new Set();\n\n private _groupStopwatch: Stopwatch = new Stopwatch();\n private _hasCancellations: boolean = false;\n private _hasFailures: boolean = false;\n\n public readonly name: string;\n public readonly metadata: TMetadata;\n\n public get duration(): number {\n return this._groupStopwatch ? this._groupStopwatch.duration : 0;\n }\n\n public get finished(): boolean {\n return this._remainingOperations.size === 0;\n }\n\n public get hasCancellations(): boolean {\n return this._hasCancellations;\n }\n\n public get hasFailures(): boolean {\n return this._hasFailures;\n }\n\n public constructor(name: string, metadata: TMetadata = {} as TMetadata) {\n this.name = name;\n this.metadata = metadata;\n }\n\n public addOperation(operation: Operation): void {\n this._operations.add(operation);\n }\n\n public startTimer(): void {\n // Keep this undefined until needed, then start to avoid subsequent calls to startTimer()\n this._groupStopwatch.start();\n }\n\n public setOperationAsComplete(operation: Operation, state: IOperationState): void {\n if (!this._remainingOperations.has(operation)) {\n throw new InternalError(`Operation ${operation.name} is not in the group ${this.name}`);\n }\n\n if (state.status === OperationStatus.Aborted) {\n this._hasCancellations = true;\n } else if (state.status === OperationStatus.Failure) {\n this._hasFailures = true;\n }\n\n this._remainingOperations.delete(operation);\n if (this._remainingOperations.size === 0) {\n this._groupStopwatch.stop();\n }\n }\n\n public reset(): void {\n this._remainingOperations = new Set(this._operations);\n this._groupStopwatch.reset();\n this._hasCancellations = false;\n this._hasFailures = false;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rushstack/operation-graph",
3
- "version": "0.2.40",
3
+ "version": "0.3.0",
4
4
  "description": "Library for managing and executing operations in a directed acyclic graph.",
5
5
  "main": "lib/index.js",
6
6
  "typings": "dist/operation-graph.d.ts",
@@ -11,14 +11,12 @@
11
11
  "directory": "libraries/operation-graph"
12
12
  },
13
13
  "dependencies": {
14
- "@rushstack/node-core-library": "5.13.0",
15
- "@rushstack/terminal": "0.15.2"
14
+ "@rushstack/node-core-library": "5.13.1",
15
+ "@rushstack/terminal": "0.15.3"
16
16
  },
17
17
  "devDependencies": {
18
- "@rushstack/heft": "0.70.0",
19
- "@rushstack/heft-node-rig": "2.8.0",
20
- "@types/heft-jest": "1.0.1",
21
- "@types/node": "20.17.19",
18
+ "@rushstack/heft": "0.73.2",
19
+ "decoupled-local-node-rig": "1.0.0",
22
20
  "local-eslint-config": "1.0.0"
23
21
  },
24
22
  "peerDependencies": {