@rushstack/operation-graph 0.1.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.
Files changed (49) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +12 -0
  3. package/dist/operation-graph.d.ts +572 -0
  4. package/dist/tsdoc-metadata.json +11 -0
  5. package/lib/IOperationRunner.d.ts +85 -0
  6. package/lib/IOperationRunner.d.ts.map +1 -0
  7. package/lib/IOperationRunner.js +5 -0
  8. package/lib/IOperationRunner.js.map +1 -0
  9. package/lib/Operation.d.ts +155 -0
  10. package/lib/Operation.d.ts.map +1 -0
  11. package/lib/Operation.js +213 -0
  12. package/lib/Operation.js.map +1 -0
  13. package/lib/OperationError.d.ts +12 -0
  14. package/lib/OperationError.d.ts.map +1 -0
  15. package/lib/OperationError.js +29 -0
  16. package/lib/OperationError.js.map +1 -0
  17. package/lib/OperationExecutionManager.d.ts +46 -0
  18. package/lib/OperationExecutionManager.d.ts.map +1 -0
  19. package/lib/OperationExecutionManager.js +127 -0
  20. package/lib/OperationExecutionManager.js.map +1 -0
  21. package/lib/OperationGroupRecord.d.ts +25 -0
  22. package/lib/OperationGroupRecord.d.ts.map +1 -0
  23. package/lib/OperationGroupRecord.js +65 -0
  24. package/lib/OperationGroupRecord.js.map +1 -0
  25. package/lib/OperationStatus.d.ts +39 -0
  26. package/lib/OperationStatus.d.ts.map +1 -0
  27. package/lib/OperationStatus.js +45 -0
  28. package/lib/OperationStatus.js.map +1 -0
  29. package/lib/Stopwatch.d.ts +47 -0
  30. package/lib/Stopwatch.d.ts.map +1 -0
  31. package/lib/Stopwatch.js +97 -0
  32. package/lib/Stopwatch.js.map +1 -0
  33. package/lib/WatchLoop.d.ts +80 -0
  34. package/lib/WatchLoop.d.ts.map +1 -0
  35. package/lib/WatchLoop.js +191 -0
  36. package/lib/WatchLoop.js.map +1 -0
  37. package/lib/calculateCriticalPath.d.ts +22 -0
  38. package/lib/calculateCriticalPath.d.ts.map +1 -0
  39. package/lib/calculateCriticalPath.js +89 -0
  40. package/lib/calculateCriticalPath.js.map +1 -0
  41. package/lib/index.d.ts +10 -0
  42. package/lib/index.d.ts.map +1 -0
  43. package/lib/index.js +20 -0
  44. package/lib/index.js.map +1 -0
  45. package/lib/protocol.types.d.ts +80 -0
  46. package/lib/protocol.types.d.ts.map +1 -0
  47. package/lib/protocol.types.js +5 -0
  48. package/lib/protocol.types.js.map +1 -0
  49. package/package.json +36 -0
@@ -0,0 +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 type { ITerminal } from '@rushstack/node-core-library';\n\nimport type { IOperationState } from './IOperationRunner';\nimport type { IExecuteOperationContext, Operation } from './Operation';\nimport { OperationGroupRecord } from './OperationGroupRecord';\nimport { OperationStatus } from './OperationStatus';\nimport { calculateCriticalPathLengths } from './calculateCriticalPath';\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 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 executionContext: IExecuteOperationContext = {\n terminal,\n abortSignal,\n\n requestRun,\n\n queueWork: <T>(workFn: () => Promise<T>, priority: number): Promise<T> => {\n // TODO: Update to throttle parallelism\n // Can just be a standard priority queue from async\n return workFn();\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 await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext)));\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"]}
@@ -0,0 +1,25 @@
1
+ import type { IOperationState } from './IOperationRunner';
2
+ import type { Operation } from './Operation';
3
+ /**
4
+ * Meta-entity that tracks information about a group of related operations.
5
+ *
6
+ * @beta
7
+ */
8
+ export declare class OperationGroupRecord {
9
+ private readonly _operations;
10
+ private _remainingOperations;
11
+ private _groupStopwatch;
12
+ private _hasCancellations;
13
+ private _hasFailures;
14
+ readonly name: string;
15
+ get duration(): number;
16
+ get finished(): boolean;
17
+ get hasCancellations(): boolean;
18
+ get hasFailures(): boolean;
19
+ constructor(name: string);
20
+ addOperation(operation: Operation): void;
21
+ startTimer(): void;
22
+ setOperationAsComplete(operation: Operation, state: IOperationState): void;
23
+ reset(): void;
24
+ }
25
+ //# sourceMappingURL=OperationGroupRecord.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.OperationGroupRecord = void 0;
6
+ const node_core_library_1 = require("@rushstack/node-core-library");
7
+ const OperationStatus_1 = require("./OperationStatus");
8
+ const Stopwatch_1 = require("./Stopwatch");
9
+ /**
10
+ * Meta-entity that tracks information about a group of related operations.
11
+ *
12
+ * @beta
13
+ */
14
+ class OperationGroupRecord {
15
+ get duration() {
16
+ return this._groupStopwatch ? this._groupStopwatch.duration : 0;
17
+ }
18
+ get finished() {
19
+ return this._remainingOperations.size === 0;
20
+ }
21
+ get hasCancellations() {
22
+ return this._hasCancellations;
23
+ }
24
+ get hasFailures() {
25
+ return this._hasFailures;
26
+ }
27
+ constructor(name) {
28
+ this._operations = new Set();
29
+ this._remainingOperations = new Set();
30
+ this._groupStopwatch = new Stopwatch_1.Stopwatch();
31
+ this._hasCancellations = false;
32
+ this._hasFailures = false;
33
+ this.name = name;
34
+ }
35
+ addOperation(operation) {
36
+ this._operations.add(operation);
37
+ }
38
+ startTimer() {
39
+ // Keep this undefined until needed, then start to avoid subsequent calls to startTimer()
40
+ this._groupStopwatch.start();
41
+ }
42
+ setOperationAsComplete(operation, state) {
43
+ if (!this._remainingOperations.has(operation)) {
44
+ throw new node_core_library_1.InternalError(`Operation ${operation.name} is not in the group ${this.name}`);
45
+ }
46
+ if (state.status === OperationStatus_1.OperationStatus.Aborted) {
47
+ this._hasCancellations = true;
48
+ }
49
+ else if (state.status === OperationStatus_1.OperationStatus.Failure) {
50
+ this._hasFailures = true;
51
+ }
52
+ this._remainingOperations.delete(operation);
53
+ if (this._remainingOperations.size === 0) {
54
+ this._groupStopwatch.stop();
55
+ }
56
+ }
57
+ reset() {
58
+ this._remainingOperations = new Set(this._operations);
59
+ this._groupStopwatch.reset();
60
+ this._hasCancellations = false;
61
+ this._hasFailures = false;
62
+ }
63
+ }
64
+ exports.OperationGroupRecord = OperationGroupRecord;
65
+ //# sourceMappingURL=OperationGroupRecord.js.map
@@ -0,0 +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;YAC7C,MAAM,IAAI,iCAAa,CAAC,aAAa,SAAS,CAAC,IAAI,wBAAwB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;SACzF;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE;YAC5C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;SAC/B;aAAM,IAAI,KAAK,CAAC,MAAM,KAAK,iCAAe,CAAC,OAAO,EAAE;YACnD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;SAC1B;QAED,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,EAAE;YACxC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;SAC7B;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"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Enumeration defining potential states of an operation
3
+ * @beta
4
+ */
5
+ export declare enum OperationStatus {
6
+ /**
7
+ * The Operation is on the queue, ready to execute
8
+ */
9
+ Ready = "READY",
10
+ /**
11
+ * The Operation is on the queue, waiting for one or more depencies
12
+ */
13
+ Waiting = "WAITING",
14
+ /**
15
+ * The Operation is currently executing
16
+ */
17
+ Executing = "EXECUTING",
18
+ /**
19
+ * The Operation completed successfully and did not write to standard output
20
+ */
21
+ Success = "SUCCESS",
22
+ /**
23
+ * The Operation failed
24
+ */
25
+ Failure = "FAILURE",
26
+ /**
27
+ * The operation was aborted
28
+ */
29
+ Aborted = "ABORTED",
30
+ /**
31
+ * The Operation could not be executed because one or more of its dependencies failed
32
+ */
33
+ Blocked = "BLOCKED",
34
+ /**
35
+ * The operation performed no meaningful work.
36
+ */
37
+ NoOp = "NO OP"
38
+ }
39
+ //# sourceMappingURL=OperationStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperationStatus.d.ts","sourceRoot":"","sources":["../src/OperationStatus.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,oBAAY,eAAe;IACzB;;OAEG;IACH,KAAK,UAAU;IACf;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,IAAI,UAAU;CACf"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.OperationStatus = void 0;
6
+ /**
7
+ * Enumeration defining potential states of an operation
8
+ * @beta
9
+ */
10
+ var OperationStatus;
11
+ (function (OperationStatus) {
12
+ /**
13
+ * The Operation is on the queue, ready to execute
14
+ */
15
+ OperationStatus["Ready"] = "READY";
16
+ /**
17
+ * The Operation is on the queue, waiting for one or more depencies
18
+ */
19
+ OperationStatus["Waiting"] = "WAITING";
20
+ /**
21
+ * The Operation is currently executing
22
+ */
23
+ OperationStatus["Executing"] = "EXECUTING";
24
+ /**
25
+ * The Operation completed successfully and did not write to standard output
26
+ */
27
+ OperationStatus["Success"] = "SUCCESS";
28
+ /**
29
+ * The Operation failed
30
+ */
31
+ OperationStatus["Failure"] = "FAILURE";
32
+ /**
33
+ * The operation was aborted
34
+ */
35
+ OperationStatus["Aborted"] = "ABORTED";
36
+ /**
37
+ * The Operation could not be executed because one or more of its dependencies failed
38
+ */
39
+ OperationStatus["Blocked"] = "BLOCKED";
40
+ /**
41
+ * The operation performed no meaningful work.
42
+ */
43
+ OperationStatus["NoOp"] = "NO OP";
44
+ })(OperationStatus = exports.OperationStatus || (exports.OperationStatus = {}));
45
+ //# sourceMappingURL=OperationStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperationStatus.js","sourceRoot":"","sources":["../src/OperationStatus.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;GAGG;AACH,IAAY,eAiCX;AAjCD,WAAY,eAAe;IACzB;;OAEG;IACH,kCAAe,CAAA;IACf;;OAEG;IACH,sCAAmB,CAAA;IACnB;;OAEG;IACH,0CAAuB,CAAA;IACvB;;OAEG;IACH,sCAAmB,CAAA;IACnB;;OAEG;IACH,sCAAmB,CAAA;IACnB;;OAEG;IACH,sCAAmB,CAAA;IACnB;;OAEG;IACH,sCAAmB,CAAA;IACnB;;OAEG;IACH,iCAAc,CAAA;AAChB,CAAC,EAjCW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAiC1B","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Enumeration defining potential states of an operation\n * @beta\n */\nexport enum OperationStatus {\n /**\n * The Operation is on the queue, ready to execute\n */\n Ready = 'READY',\n /**\n * The Operation is on the queue, waiting for one or more depencies\n */\n Waiting = 'WAITING',\n /**\n * The Operation is currently executing\n */\n Executing = 'EXECUTING',\n /**\n * The Operation completed successfully and did not write to standard output\n */\n Success = 'SUCCESS',\n /**\n * The Operation failed\n */\n Failure = 'FAILURE',\n /**\n * The operation was aborted\n */\n Aborted = 'ABORTED',\n /**\n * The Operation could not be executed because one or more of its dependencies failed\n */\n Blocked = 'BLOCKED',\n /**\n * The operation performed no meaningful work.\n */\n NoOp = 'NO OP'\n}\n"]}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Represents a typical timer/stopwatch which keeps track
3
+ * of elapsed time in between two events.
4
+ *
5
+ * @public
6
+ */
7
+ export declare class Stopwatch {
8
+ private _startTime;
9
+ private _endTime;
10
+ private _running;
11
+ constructor();
12
+ /**
13
+ * Static helper function which creates a stopwatch which is immediately started
14
+ */
15
+ static start(): Stopwatch;
16
+ get isRunning(): boolean;
17
+ /**
18
+ * Starts the stopwatch. Note that if end() has been called,
19
+ * reset() should be called before calling start() again.
20
+ */
21
+ start(): Stopwatch;
22
+ /**
23
+ * Stops executing the stopwatch and saves the current timestamp
24
+ */
25
+ stop(): Stopwatch;
26
+ /**
27
+ * Resets all values of the stopwatch back to the original
28
+ */
29
+ reset(): Stopwatch;
30
+ /**
31
+ * Displays how long the stopwatch has been executing in a human readable format.
32
+ */
33
+ toString(): string;
34
+ /**
35
+ * Get the duration in seconds.
36
+ */
37
+ get duration(): number;
38
+ /**
39
+ * Return the start time of the most recent stopwatch run.
40
+ */
41
+ get startTime(): number | undefined;
42
+ /**
43
+ * Return the end time of the most recent stopwatch run.
44
+ */
45
+ get endTime(): number | undefined;
46
+ }
47
+ //# sourceMappingURL=Stopwatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stopwatch.d.ts","sourceRoot":"","sources":["../src/Stopwatch.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAU;;IAQ1B;;OAEG;WACW,KAAK,IAAI,SAAS;IAIhC,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;OAGG;IACI,KAAK,IAAI,SAAS;IAUzB;;OAEG;IACI,IAAI,IAAI,SAAS;IAMxB;;OAEG;IACI,KAAK,IAAI,SAAS;IAMzB;;OAEG;IACI,QAAQ,IAAI,MAAM;IAgBzB;;OAEG;IACH,IAAW,QAAQ,IAAI,MAAM,CAO5B;IAED;;OAEG;IACH,IAAW,SAAS,IAAI,MAAM,GAAG,SAAS,CAEzC;IAED;;OAEG;IACH,IAAW,OAAO,IAAI,MAAM,GAAG,SAAS,CAEvC;CACF"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.Stopwatch = void 0;
6
+ /**
7
+ * Represents a typical timer/stopwatch which keeps track
8
+ * of elapsed time in between two events.
9
+ *
10
+ * @public
11
+ */
12
+ class Stopwatch {
13
+ constructor() {
14
+ this._startTime = undefined;
15
+ this._endTime = undefined;
16
+ this._running = false;
17
+ }
18
+ /**
19
+ * Static helper function which creates a stopwatch which is immediately started
20
+ */
21
+ static start() {
22
+ return new Stopwatch().start();
23
+ }
24
+ get isRunning() {
25
+ return this._running;
26
+ }
27
+ /**
28
+ * Starts the stopwatch. Note that if end() has been called,
29
+ * reset() should be called before calling start() again.
30
+ */
31
+ start() {
32
+ if (this._startTime !== undefined) {
33
+ throw new Error('Call reset() before starting the Stopwatch');
34
+ }
35
+ this._startTime = performance.now();
36
+ this._endTime = undefined;
37
+ this._running = true;
38
+ return this;
39
+ }
40
+ /**
41
+ * Stops executing the stopwatch and saves the current timestamp
42
+ */
43
+ stop() {
44
+ this._endTime = this._startTime !== undefined ? performance.now() : undefined;
45
+ this._running = false;
46
+ return this;
47
+ }
48
+ /**
49
+ * Resets all values of the stopwatch back to the original
50
+ */
51
+ reset() {
52
+ this._endTime = this._startTime = undefined;
53
+ this._running = false;
54
+ return this;
55
+ }
56
+ /**
57
+ * Displays how long the stopwatch has been executing in a human readable format.
58
+ */
59
+ toString() {
60
+ if (!this._running && this._startTime === undefined) {
61
+ return '0.00 seconds (stopped)';
62
+ }
63
+ const totalSeconds = this.duration;
64
+ if (totalSeconds > 60) {
65
+ const minutes = Math.floor(totalSeconds / 60);
66
+ const seconds = totalSeconds % 60.0;
67
+ return `${minutes.toFixed(0)} minute${minutes === 1 ? '' : 's'} ${seconds.toFixed(1)} seconds`;
68
+ }
69
+ else {
70
+ return `${totalSeconds.toFixed(2)} seconds`;
71
+ }
72
+ }
73
+ /**
74
+ * Get the duration in seconds.
75
+ */
76
+ get duration() {
77
+ if (this._startTime === undefined) {
78
+ return 0;
79
+ }
80
+ const curTime = this._endTime !== undefined ? this._endTime : performance.now();
81
+ return (curTime - this._startTime) / 1000.0;
82
+ }
83
+ /**
84
+ * Return the start time of the most recent stopwatch run.
85
+ */
86
+ get startTime() {
87
+ return this._startTime;
88
+ }
89
+ /**
90
+ * Return the end time of the most recent stopwatch run.
91
+ */
92
+ get endTime() {
93
+ return this._endTime;
94
+ }
95
+ }
96
+ exports.Stopwatch = Stopwatch;
97
+ //# sourceMappingURL=Stopwatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stopwatch.js","sourceRoot":"","sources":["../src/Stopwatch.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;;GAKG;AACH,MAAa,SAAS;IAKpB;QACE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;IACxB,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,KAAK;QACjB,OAAO,IAAI,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;IAED,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;OAGG;IACI,KAAK;QACV,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE;YACjC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;SAC/D;QACD,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,IAAI;QACT,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,KAAK;QACV,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE;YACnD,OAAO,wBAAwB,CAAC;SACjC;QACD,MAAM,YAAY,GAAW,IAAI,CAAC,QAAQ,CAAC;QAE3C,IAAI,YAAY,GAAG,EAAE,EAAE;YACrB,MAAM,OAAO,GAAW,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;YACtD,MAAM,OAAO,GAAW,YAAY,GAAG,IAAI,CAAC;YAE5C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;SAChG;aAAM;YACL,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;SAC7C;IACH,CAAC;IAED;;OAEG;IACH,IAAW,QAAQ;QACjB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE;YACjC,OAAO,CAAC,CAAC;SACV;QACD,MAAM,OAAO,GAAW,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;QAExF,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AAlGD,8BAkGC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Represents a typical timer/stopwatch which keeps track\n * of elapsed time in between two events.\n *\n * @public\n */\nexport class Stopwatch {\n private _startTime: number | undefined;\n private _endTime: number | undefined;\n private _running: boolean;\n\n public constructor() {\n this._startTime = undefined;\n this._endTime = undefined;\n this._running = false;\n }\n\n /**\n * Static helper function which creates a stopwatch which is immediately started\n */\n public static start(): Stopwatch {\n return new Stopwatch().start();\n }\n\n public get isRunning(): boolean {\n return this._running;\n }\n\n /**\n * Starts the stopwatch. Note that if end() has been called,\n * reset() should be called before calling start() again.\n */\n public start(): Stopwatch {\n if (this._startTime !== undefined) {\n throw new Error('Call reset() before starting the Stopwatch');\n }\n this._startTime = performance.now();\n this._endTime = undefined;\n this._running = true;\n return this;\n }\n\n /**\n * Stops executing the stopwatch and saves the current timestamp\n */\n public stop(): Stopwatch {\n this._endTime = this._startTime !== undefined ? performance.now() : undefined;\n this._running = false;\n return this;\n }\n\n /**\n * Resets all values of the stopwatch back to the original\n */\n public reset(): Stopwatch {\n this._endTime = this._startTime = undefined;\n this._running = false;\n return this;\n }\n\n /**\n * Displays how long the stopwatch has been executing in a human readable format.\n */\n public toString(): string {\n if (!this._running && this._startTime === undefined) {\n return '0.00 seconds (stopped)';\n }\n const totalSeconds: number = this.duration;\n\n if (totalSeconds > 60) {\n const minutes: number = Math.floor(totalSeconds / 60);\n const seconds: number = totalSeconds % 60.0;\n\n return `${minutes.toFixed(0)} minute${minutes === 1 ? '' : 's'} ${seconds.toFixed(1)} seconds`;\n } else {\n return `${totalSeconds.toFixed(2)} seconds`;\n }\n }\n\n /**\n * Get the duration in seconds.\n */\n public get duration(): number {\n if (this._startTime === undefined) {\n return 0;\n }\n const curTime: number = this._endTime !== undefined ? this._endTime : performance.now();\n\n return (curTime - this._startTime) / 1000.0;\n }\n\n /**\n * Return the start time of the most recent stopwatch run.\n */\n public get startTime(): number | undefined {\n return this._startTime;\n }\n\n /**\n * Return the end time of the most recent stopwatch run.\n */\n public get endTime(): number | undefined {\n return this._endTime;\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ /// <reference types="node" />
2
+ import { OperationStatus } from './OperationStatus';
3
+ import type { IPCHost } from './protocol.types';
4
+ /**
5
+ * Callbacks for the watch loop.
6
+ *
7
+ * @beta
8
+ */
9
+ export interface IWatchLoopOptions {
10
+ /**
11
+ * Callback that performs the core work of a single iteration.
12
+ */
13
+ executeAsync: (state: IWatchLoopState) => Promise<OperationStatus>;
14
+ /**
15
+ * Logging callback immediately before execution occurs.
16
+ */
17
+ onBeforeExecute: () => void;
18
+ /**
19
+ * Logging callback when a run is requested (and hasn't already been).
20
+ */
21
+ onRequestRun: (requestor?: string) => void;
22
+ /**
23
+ * Logging callback when a run is aborted.
24
+ */
25
+ onAbort: () => void;
26
+ }
27
+ /**
28
+ * The public API surface of the watch loop, for use in the `executeAsync` callback.
29
+ *
30
+ * @beta
31
+ */
32
+ export interface IWatchLoopState {
33
+ get abortSignal(): AbortSignal;
34
+ requestRun: (requestor?: string) => void;
35
+ }
36
+ /**
37
+ * This class implements a watch loop.
38
+ *
39
+ * @beta
40
+ */
41
+ export declare class WatchLoop implements IWatchLoopState {
42
+ private readonly _options;
43
+ private readonly _outerSignals;
44
+ private _abortController;
45
+ private _isRunning;
46
+ private _runRequested;
47
+ private _requestRunPromise;
48
+ private _resolveRequestRun;
49
+ constructor(options: IWatchLoopOptions);
50
+ /**
51
+ * Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested.
52
+ */
53
+ runUntilStableAsync(abortSignal: AbortSignal): Promise<OperationStatus>;
54
+ /**
55
+ * Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested.
56
+ */
57
+ runUntilAbortedAsync(abortSignal: AbortSignal, onWaiting: () => void): Promise<void>;
58
+ /**
59
+ * Sets up an IPC handler that will run the inner loop when it receives a "run" message from the host.
60
+ * Runs until receiving an "exit" message from the host, or aborts early if an unhandled error is thrown.
61
+ */
62
+ runIPCAsync(host?: IPCHost): Promise<void>;
63
+ /**
64
+ * Requests that a new run occur.
65
+ */
66
+ requestRun: (requestor?: string) => void;
67
+ /**
68
+ * The abort signal for the current iteration.
69
+ */
70
+ get abortSignal(): AbortSignal;
71
+ /**
72
+ * Cancels the current iteration (if possible).
73
+ */
74
+ private _abortCurrent;
75
+ /**
76
+ * Resets the abort signal and run request state.
77
+ */
78
+ private _reset;
79
+ }
80
+ //# sourceMappingURL=WatchLoop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WatchLoop.d.ts","sourceRoot":"","sources":["../src/WatchLoop.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAEV,OAAO,EAIR,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACnE;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B;;OAEG;IACH,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,WAAW,IAAI,WAAW,CAAC;IAC/B,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED;;;;GAIG;AACH,qBAAa,SAAU,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IACvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuB;IAErD,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,kBAAkB,CAA8B;IACxD,OAAO,CAAC,kBAAkB,CAAgC;gBAEvC,OAAO,EAAE,iBAAiB;IAc7C;;OAEG;IACU,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAsCpF;;OAEG;IACU,oBAAoB,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBjG;;;OAGG;IACU,WAAW,CAAC,IAAI,GAAE,OAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgFhE;;OAEG;IACI,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAU7C;IAEF;;OAEG;IACH,IAAW,WAAW,IAAI,WAAW,CAEpC;IAED;;OAEG;IACH,OAAO,CAAC,aAAa,CAEnB;IAEF;;OAEG;IACH,OAAO,CAAC,MAAM;CAYf"}
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.WatchLoop = void 0;
6
+ const node_events_1 = require("node:events");
7
+ const node_core_library_1 = require("@rushstack/node-core-library");
8
+ const OperationStatus_1 = require("./OperationStatus");
9
+ /**
10
+ * This class implements a watch loop.
11
+ *
12
+ * @beta
13
+ */
14
+ class WatchLoop {
15
+ constructor(options) {
16
+ /**
17
+ * Requests that a new run occur.
18
+ */
19
+ this.requestRun = (requestor) => {
20
+ if (!this._runRequested) {
21
+ this._options.onRequestRun(requestor);
22
+ this._runRequested = true;
23
+ if (this._isRunning) {
24
+ this._options.onAbort();
25
+ this._abortCurrent();
26
+ }
27
+ }
28
+ this._resolveRequestRun(requestor);
29
+ };
30
+ /**
31
+ * Cancels the current iteration (if possible).
32
+ */
33
+ this._abortCurrent = () => {
34
+ this._abortController.abort();
35
+ };
36
+ this._options = options;
37
+ this._abortController = new AbortController();
38
+ this._isRunning = false;
39
+ // Always start as true, so that any requests prior to first run are silenced.
40
+ this._runRequested = true;
41
+ this._requestRunPromise = new Promise((resolve) => {
42
+ this._resolveRequestRun = resolve;
43
+ });
44
+ this._outerSignals = new WeakSet();
45
+ }
46
+ /**
47
+ * Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested.
48
+ */
49
+ async runUntilStableAsync(abortSignal) {
50
+ if (abortSignal.aborted) {
51
+ return OperationStatus_1.OperationStatus.Aborted;
52
+ }
53
+ if (!this._outerSignals.has(abortSignal)) {
54
+ (0, node_events_1.once)(abortSignal, 'abort').finally(this._abortCurrent);
55
+ }
56
+ let result = OperationStatus_1.OperationStatus.Ready;
57
+ do {
58
+ // Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call.
59
+ if (abortSignal.aborted) {
60
+ return OperationStatus_1.OperationStatus.Aborted;
61
+ }
62
+ this._reset();
63
+ this._options.onBeforeExecute();
64
+ try {
65
+ this._isRunning = true;
66
+ result = await this._options.executeAsync(this);
67
+ }
68
+ catch (err) {
69
+ if (!(err instanceof node_core_library_1.AlreadyReportedError)) {
70
+ throw err;
71
+ }
72
+ else {
73
+ result = OperationStatus_1.OperationStatus.Failure;
74
+ }
75
+ }
76
+ finally {
77
+ this._isRunning = false;
78
+ }
79
+ } while (this._runRequested);
80
+ // Even if the run has finished, if the abort signal was aborted, we should return `Aborted` just in case.
81
+ return abortSignal.aborted ? OperationStatus_1.OperationStatus.Aborted : result;
82
+ }
83
+ /**
84
+ * Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested.
85
+ */
86
+ async runUntilAbortedAsync(abortSignal, onWaiting) {
87
+ if (abortSignal.aborted) {
88
+ return;
89
+ }
90
+ const abortPromise = (0, node_events_1.once)(abortSignal, 'abort');
91
+ // eslint-disable-next-line no-constant-condition
92
+ while (!abortSignal.aborted) {
93
+ await this.runUntilStableAsync(abortSignal);
94
+ onWaiting();
95
+ await Promise.race([this._requestRunPromise, abortPromise]);
96
+ }
97
+ }
98
+ /**
99
+ * Sets up an IPC handler that will run the inner loop when it receives a "run" message from the host.
100
+ * Runs until receiving an "exit" message from the host, or aborts early if an unhandled error is thrown.
101
+ */
102
+ async runIPCAsync(host = process) {
103
+ await new Promise((resolve, reject) => {
104
+ let abortController = new AbortController();
105
+ let runRequestedFromHost = true;
106
+ let status = OperationStatus_1.OperationStatus.Ready;
107
+ function requestRunFromHost(requestor) {
108
+ if (runRequestedFromHost) {
109
+ return;
110
+ }
111
+ runRequestedFromHost = true;
112
+ const requestRunMessage = {
113
+ event: 'requestRun',
114
+ requestor
115
+ };
116
+ host.send(requestRunMessage);
117
+ }
118
+ function sendSync() {
119
+ const syncMessage = {
120
+ event: 'sync',
121
+ status
122
+ };
123
+ host.send(syncMessage);
124
+ }
125
+ host.on('message', async (message) => {
126
+ switch (message.command) {
127
+ case 'exit': {
128
+ return resolve();
129
+ }
130
+ case 'cancel': {
131
+ if (this._isRunning) {
132
+ abortController.abort();
133
+ abortController = new AbortController();
134
+ // This will terminate the currently executing `runUntilStableAsync` call.
135
+ }
136
+ return;
137
+ }
138
+ case 'run': {
139
+ runRequestedFromHost = false;
140
+ status = OperationStatus_1.OperationStatus.Executing;
141
+ try {
142
+ status = await this.runUntilStableAsync(abortController.signal);
143
+ this._requestRunPromise.finally(requestRunFromHost);
144
+ }
145
+ catch (err) {
146
+ status = OperationStatus_1.OperationStatus.Failure;
147
+ return reject(err);
148
+ }
149
+ finally {
150
+ const afterExecuteMessage = {
151
+ event: 'after-execute',
152
+ status
153
+ };
154
+ host.send(afterExecuteMessage);
155
+ }
156
+ return;
157
+ }
158
+ case 'sync': {
159
+ return sendSync();
160
+ }
161
+ default: {
162
+ return reject(new Error(`Unexpected command from host: ${message}`));
163
+ }
164
+ }
165
+ });
166
+ sendSync();
167
+ });
168
+ }
169
+ /**
170
+ * The abort signal for the current iteration.
171
+ */
172
+ get abortSignal() {
173
+ return this._abortController.signal;
174
+ }
175
+ /**
176
+ * Resets the abort signal and run request state.
177
+ */
178
+ _reset() {
179
+ if (this._abortController.signal.aborted) {
180
+ this._abortController = new AbortController();
181
+ }
182
+ if (this._runRequested) {
183
+ this._runRequested = false;
184
+ this._requestRunPromise = new Promise((resolve) => {
185
+ this._resolveRequestRun = resolve;
186
+ });
187
+ }
188
+ }
189
+ }
190
+ exports.WatchLoop = WatchLoop;
191
+ //# sourceMappingURL=WatchLoop.js.map