@api-client/core 0.3.4 → 0.3.7

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 (109) hide show
  1. package/build/browser.d.ts +6 -0
  2. package/build/browser.js +15 -0
  3. package/build/browser.js.map +1 -1
  4. package/build/index.d.ts +14 -2
  5. package/build/index.js +26 -2
  6. package/build/index.js.map +1 -1
  7. package/build/src/lib/calculators/DataCalculator.d.ts +27 -0
  8. package/build/src/lib/calculators/DataCalculator.js +88 -0
  9. package/build/src/lib/calculators/DataCalculator.js.map +1 -0
  10. package/build/src/lib/fs/Fs.d.ts +52 -0
  11. package/build/src/lib/fs/Fs.js +245 -0
  12. package/build/src/lib/fs/Fs.js.map +1 -0
  13. package/build/src/lib/parsers/UrlEncoder.d.ts +51 -0
  14. package/build/src/lib/parsers/UrlEncoder.js +74 -0
  15. package/build/src/lib/parsers/UrlEncoder.js.map +1 -0
  16. package/build/src/lib/parsers/UrlParser.d.ts +104 -0
  17. package/build/src/lib/parsers/UrlParser.js +189 -0
  18. package/build/src/lib/parsers/UrlParser.js.map +1 -0
  19. package/build/src/lib/parsers/UrlValueParser.d.ts +92 -0
  20. package/build/src/lib/parsers/UrlValueParser.js +172 -0
  21. package/build/src/lib/parsers/UrlValueParser.js.map +1 -0
  22. package/build/src/lib/timers/Timers.d.ts +5 -0
  23. package/build/src/lib/timers/Timers.js +10 -0
  24. package/build/src/lib/timers/Timers.js.map +1 -0
  25. package/build/src/mocking/ProjectMock.d.ts +13 -0
  26. package/build/src/mocking/ProjectMock.js +16 -0
  27. package/build/src/mocking/ProjectMock.js.map +1 -0
  28. package/build/src/mocking/lib/Request.d.ts +32 -0
  29. package/build/src/mocking/lib/Request.js +63 -0
  30. package/build/src/mocking/lib/Request.js.map +1 -0
  31. package/build/src/mocking/lib/Response.d.ts +33 -0
  32. package/build/src/mocking/lib/Response.js +79 -0
  33. package/build/src/mocking/lib/Response.js.map +1 -0
  34. package/build/src/models/ErrorResponse.d.ts +5 -4
  35. package/build/src/models/ErrorResponse.js +18 -5
  36. package/build/src/models/ErrorResponse.js.map +1 -1
  37. package/build/src/models/SerializableError.d.ts +30 -0
  38. package/build/src/models/SerializableError.js +63 -0
  39. package/build/src/models/SerializableError.js.map +1 -0
  40. package/build/src/runtime/http-engine/ArcEngine.js +8 -4
  41. package/build/src/runtime/http-engine/ArcEngine.js.map +1 -1
  42. package/build/src/runtime/http-engine/HttpEngine.d.ts +3 -3
  43. package/build/src/runtime/http-engine/HttpEngine.js +3 -3
  44. package/build/src/runtime/http-engine/HttpEngine.js.map +1 -1
  45. package/build/src/runtime/http-engine/NodeEngine.js +9 -4
  46. package/build/src/runtime/http-engine/NodeEngine.js.map +1 -1
  47. package/build/src/runtime/http-engine/NodeEngineDirect.js +8 -2
  48. package/build/src/runtime/http-engine/NodeEngineDirect.js.map +1 -1
  49. package/build/src/runtime/node/BaseRunner.d.ts +21 -0
  50. package/build/src/runtime/node/BaseRunner.js +27 -0
  51. package/build/src/runtime/node/BaseRunner.js.map +1 -0
  52. package/build/src/runtime/node/ProjectParallelRunner.d.ts +81 -0
  53. package/build/src/runtime/node/ProjectParallelRunner.js +173 -0
  54. package/build/src/runtime/node/ProjectParallelRunner.js.map +1 -0
  55. package/build/src/runtime/node/ProjectRequestRunner.d.ts +125 -0
  56. package/build/src/runtime/node/ProjectRequestRunner.js +185 -0
  57. package/build/src/runtime/node/ProjectRequestRunner.js.map +1 -0
  58. package/build/src/runtime/node/ProjectRunner.d.ts +166 -64
  59. package/build/src/runtime/node/ProjectRunner.js +191 -139
  60. package/build/src/runtime/node/ProjectRunner.js.map +1 -1
  61. package/build/src/runtime/node/ProjectRunnerWorker.d.ts +1 -0
  62. package/build/src/runtime/node/ProjectRunnerWorker.js +58 -0
  63. package/build/src/runtime/node/ProjectRunnerWorker.js.map +1 -0
  64. package/build/src/runtime/node/ProjectSerialRunner.d.ts +11 -0
  65. package/build/src/runtime/node/ProjectSerialRunner.js +34 -0
  66. package/build/src/runtime/node/ProjectSerialRunner.js.map +1 -0
  67. package/build/src/runtime/reporters/ProjectRunCliReporter.d.ts +7 -0
  68. package/build/src/runtime/reporters/ProjectRunCliReporter.js +73 -0
  69. package/build/src/runtime/reporters/ProjectRunCliReporter.js.map +1 -0
  70. package/build/src/runtime/reporters/Reporter.d.ts +62 -0
  71. package/build/src/runtime/reporters/Reporter.js +98 -0
  72. package/build/src/runtime/reporters/Reporter.js.map +1 -0
  73. package/build/src/testing/TestCliHelper.d.ts +29 -0
  74. package/build/src/testing/TestCliHelper.js +80 -0
  75. package/build/src/testing/TestCliHelper.js.map +1 -0
  76. package/build/src/testing/getPort.d.ts +52 -0
  77. package/build/src/testing/getPort.js +169 -0
  78. package/build/src/testing/getPort.js.map +1 -0
  79. package/package.json +2 -1
  80. package/src/lib/calculators/DataCalculator.ts +91 -0
  81. package/src/lib/fs/Fs.ts +258 -0
  82. package/src/lib/parsers/UrlEncoder.ts +74 -0
  83. package/src/lib/parsers/UrlParser.ts +201 -0
  84. package/src/lib/parsers/UrlValueParser.ts +211 -0
  85. package/src/lib/timers/Timers.ts +9 -0
  86. package/src/mocking/LegacyInterfaces.ts +1 -1
  87. package/src/mocking/ProjectMock.ts +20 -0
  88. package/src/mocking/lib/Request.ts +85 -0
  89. package/src/mocking/lib/Response.ts +101 -0
  90. package/src/models/ErrorResponse.ts +20 -8
  91. package/src/models/SerializableError.ts +80 -0
  92. package/src/runtime/http-engine/ArcEngine.ts +8 -4
  93. package/src/runtime/http-engine/HttpEngine.ts +5 -5
  94. package/src/runtime/http-engine/NodeEngine.ts +10 -5
  95. package/src/runtime/http-engine/NodeEngineDirect.ts +9 -3
  96. package/src/runtime/node/BaseRunner.ts +29 -0
  97. package/src/runtime/node/ProjectParallelRunner.ts +234 -0
  98. package/src/runtime/node/ProjectRequestRunner.ts +281 -0
  99. package/src/runtime/node/ProjectRunner.ts +281 -182
  100. package/src/runtime/node/ProjectRunnerWorker.ts +62 -0
  101. package/src/runtime/node/ProjectSerialRunner.ts +36 -0
  102. package/src/runtime/reporters/ProjectRunCliReporter.ts +79 -0
  103. package/src/runtime/reporters/Reporter.ts +142 -0
  104. package/src/testing/TestCliHelper.ts +87 -0
  105. package/src/testing/getPort.ts +212 -0
  106. package/build/src/runtime/http-engine/Errors.d.ts +0 -10
  107. package/build/src/runtime/http-engine/Errors.js +0 -14
  108. package/build/src/runtime/http-engine/Errors.js.map +0 -1
  109. package/src/runtime/http-engine/Errors.ts +0 -13
@@ -0,0 +1,80 @@
1
+ interface IErrorOptions {
2
+ cause?: Error;
3
+ }
4
+
5
+ export interface ISerializedError {
6
+ message: string;
7
+ stack?: string;
8
+ code?: string | number;
9
+ }
10
+
11
+ /**
12
+ * An error that serializes!
13
+ * It is a copy of the normal definition of an error but allows
14
+ * to serialize the value via the toJSON() function and restore previous values
15
+ * with the `new()` function.
16
+ */
17
+ export class SerializableError {
18
+ message: string;
19
+ code?: number | string;
20
+ private stackValue?: string;
21
+ get stack(): string | undefined {
22
+ return this.stackValue;
23
+ }
24
+
25
+ get name(): string {
26
+ return 'SerializableError';
27
+ }
28
+
29
+ constructor();
30
+ constructor(error: Error);
31
+ constructor(message: string);
32
+ constructor(message: string, options: IErrorOptions);
33
+ constructor(message: string, code?: number | string);
34
+
35
+ constructor(message?: string | Error, options: IErrorOptions | number | string = {}) {
36
+ if (typeof message === 'string') {
37
+ this.message = message;
38
+ } else if (message) {
39
+ this.message = message.message;
40
+ this.stackValue = message.stack;
41
+ } else {
42
+ this.message = '';
43
+ }
44
+ if (typeof options === 'string' || typeof options === 'number') {
45
+ this.code = options;
46
+ } else if (options.cause && options.cause.stack) {
47
+ this.stackValue = options.cause.stack;
48
+ }
49
+ }
50
+
51
+ new(values: ISerializedError): void {
52
+ if (values.message) {
53
+ this.message = values.message;
54
+ }
55
+ if (values.stack) {
56
+ this.stackValue = values.stack;
57
+ }
58
+ if (values.code || values.code === 0) {
59
+ this.code = values.code;
60
+ }
61
+ }
62
+
63
+ toJSON(): ISerializedError {
64
+ const { message, stackValue: stack, code } = this;
65
+ const result: ISerializedError = {
66
+ message,
67
+ };
68
+ if (stack) {
69
+ result.stack = stack;
70
+ }
71
+ if (code || code === 0) {
72
+ result.code = code;
73
+ }
74
+ return result;
75
+ }
76
+
77
+ toString(): string {
78
+ return this.message;
79
+ }
80
+ }
@@ -6,10 +6,10 @@ import { HttpEngine, HttpEngineOptions, HeadersReceivedDetail } from './HttpEngi
6
6
  import { IRequestLog } from 'src/models/RequestLog.js';
7
7
  import { IHttpRequest } from '../../models/HttpRequest.js';
8
8
  import { ArcResponse } from '../../models/ArcResponse.js';
9
+ import { SerializableError } from '../../models/SerializableError.js';
9
10
  import { Headers } from '../../lib/headers/Headers.js';
10
11
  import { PayloadSupport } from './PayloadSupport.js';
11
12
  import { addContentLength, getPort } from './RequestUtils.js';
12
- import { NetError } from './Errors.js';
13
13
  import { INtlmAuthorization } from '../../models/Authorization.js';
14
14
  import { NtlmAuth, INtlmAuthConfig } from './ntlm/NtlmAuth.js';
15
15
  import { PayloadSerializer } from '../../lib/transformers/PayloadSerializer.js';
@@ -80,7 +80,11 @@ export class ArcEngine extends HttpEngine {
80
80
  const message = await this.prepareMessage();
81
81
  await this.writeMessage(message);
82
82
  } catch (cause) {
83
- const err = cause as NetError;
83
+ const e = cause as any;
84
+ const err = new SerializableError(e.message, { cause: e });
85
+ if (e.code || e.code === 0) {
86
+ err.code = e.code as string;
87
+ }
84
88
  this.abort();
85
89
  this._errorRequest({
86
90
  message: err.message,
@@ -459,7 +463,7 @@ export class ArcEngine extends HttpEngine {
459
463
  if (!this.currentResponse) {
460
464
  this.logger.error(`Connection closed without receiving any data.`);
461
465
  // The parser haven't found the end of message so there was no message!
462
- const e = new NetError('Connection closed without receiving any data', 100);
466
+ const e = new SerializableError('Connection closed without receiving any data', 100);
463
467
  this._errorRequest(e);
464
468
  } else {
465
469
  // There is an issue with the response. Size mismatch? Anyway,
@@ -1012,7 +1016,7 @@ export class ArcEngine extends HttpEngine {
1012
1016
  } else if (res.statusCode !== 200) {
1013
1017
  this.logger.debug(`The proxy tunnel ended with ${res.statusCode} status code. Erroring request.`);
1014
1018
  connectRequest.destroy();
1015
- const e = new NetError('A tunnel connection through the proxy could not be established.', 111);
1019
+ const e = new SerializableError('A tunnel connection through the proxy could not be established', 111);
1016
1020
  reject(e);
1017
1021
  } else {
1018
1022
  this.logger.debug(`Established a proxy tunnel.`);
@@ -19,6 +19,7 @@ import { HttpResponse } from '../../models/HttpResponse.js';
19
19
  import { ResponseRedirect } from '../../models/ResponseRedirect.js';
20
20
  import { RequestLog, IRequestLog } from '../../models/RequestLog.js';
21
21
  import { RequestTime } from '../../models/RequestTime.js';
22
+ import { SerializableError } from '../../models/SerializableError.js';
22
23
  import { ResponseAuthorization } from '../../models/ResponseAuthorization.js';
23
24
  import { DefaultLogger } from '../../lib/logging/DefaultLogger.js';
24
25
  import { ILogger, Logger } from '../../lib/logging/Logger.js';
@@ -26,7 +27,6 @@ import { Headers } from '../../lib/headers/Headers.js';
26
27
  import * as RequestUtils from './RequestUtils.js';
27
28
  import { Cookies } from '../../lib/cookies/Cookies.js';
28
29
  import { HttpErrorCodes } from './HttpErrorCodes.js';
29
- import { NetError } from './Errors.js';
30
30
 
31
31
  export interface HttpEngineOptions extends IRequestBaseConfig {
32
32
  /**
@@ -188,7 +188,7 @@ export abstract class HttpEngine extends EventEmitter {
188
188
  auth?: IRequestAuthState;
189
189
 
190
190
  protected mainResolver?: (log: IRequestLog) => void;
191
- protected mainRejecter?: (err: NetError) => void;
191
+ protected mainRejecter?: (err: SerializableError) => void;
192
192
  [mainPromiseSymbol]?: Promise<IRequestLog>;
193
193
 
194
194
  constructor(request: IHttpRequest, opts: HttpEngineOptions = {}) {
@@ -562,7 +562,7 @@ Check your request parameters.`);
562
562
  message = opts.message;
563
563
  }
564
564
  message = message || 'Unknown error occurred';
565
- const error = new NetError(message, opts.code);
565
+ const error = new SerializableError(message, opts.code);
566
566
  const log = RequestLog.fromRequest(this.sentRequest);
567
567
  const response = ErrorResponse.fromError(error);
568
568
  log.response = response;
@@ -855,14 +855,14 @@ Check your request parameters.`);
855
855
  *
856
856
  * @param log Either the request execution log or an error.
857
857
  */
858
- protected finalizeRequest(log: RequestLog | NetError): void {
858
+ protected finalizeRequest(log: RequestLog | SerializableError): void {
859
859
  const { mainRejecter, mainResolver } = this;
860
860
  if (!mainRejecter || !mainResolver) {
861
861
  // console.error(`Trying to finalize the request but the main resolver is not set.`);
862
862
  return;
863
863
  }
864
864
 
865
- if (log instanceof Error) {
865
+ if (log instanceof SerializableError) {
866
866
  mainRejecter(log);
867
867
  } else {
868
868
  mainResolver(log.toJSON());
@@ -8,10 +8,10 @@ import { HttpEngine, HttpEngineOptions, ResponseErrorInit, HeadersReceivedDetail
8
8
  import { IHttpRequest } from '../../models/HttpRequest.js';
9
9
  import { ArcResponse } from '../../models/ArcResponse.js';
10
10
  import { IRequestLog } from '../../models/RequestLog.js';
11
+ import { SerializableError } from '../../models/SerializableError.js';
11
12
  import { Headers } from '../../lib/headers/Headers.js';
12
13
  import { PayloadSupport } from './PayloadSupport.js';
13
14
  import { addContentLength, getPort } from './RequestUtils.js';
14
- import { NetError } from './Errors.js';
15
15
  import { INtlmAuthorization } from '../../models/Authorization.js';
16
16
  import { NtlmAuth, INtlmAuthConfig } from './ntlm/NtlmAuth.js';
17
17
 
@@ -59,9 +59,14 @@ export class NodeEngine extends HttpEngine {
59
59
  request.setTimeout(timeout);
60
60
  }
61
61
  }
62
- } catch (e) {
63
- console.warn(e);
64
- this.finalizeRequest(e as Error);
62
+ } catch (cause) {
63
+ console.warn(cause);
64
+ const e = cause as any;
65
+ const err = new SerializableError(e.message, { cause: e });
66
+ if (e.code || e.code === 0) {
67
+ err.code = e.code as string;
68
+ }
69
+ this.finalizeRequest(e);
65
70
  }
66
71
  }
67
72
 
@@ -593,7 +598,7 @@ export class NodeEngine extends HttpEngine {
593
598
  });
594
599
  } else {
595
600
  connectRequest.destroy();
596
- const e = new NetError('A tunnel connection through the proxy could not be established.', 111);
601
+ const e = new SerializableError('A tunnel connection through the proxy could not be established', 111);
597
602
  reject(e);
598
603
  }
599
604
  });
@@ -9,6 +9,7 @@ import { IRequestLog } from 'src/models/RequestLog.js';
9
9
  import { IHttpRequest } from '../../models/HttpRequest.js';
10
10
  import { ArcResponse } from '../../models/ArcResponse.js';
11
11
  import { Headers } from '../../lib/headers/Headers.js';
12
+ import { SerializableError } from '../../models/SerializableError.js';
12
13
  import { PayloadSupport } from './PayloadSupport.js';
13
14
  import { addContentLength, getPort } from './RequestUtils.js';
14
15
 
@@ -85,9 +86,14 @@ export class NodeEngineDirect extends HttpEngine {
85
86
  if (timeout > 0) {
86
87
  request.setTimeout(timeout);
87
88
  }
88
- } catch (e) {
89
- console.warn(e);
90
- this.finalizeRequest(e as Error);
89
+ } catch (cause) {
90
+ console.warn(cause);
91
+ const e = cause as any;
92
+ const err = new SerializableError(e.message, { cause: e });
93
+ if (e.code || e.code === 0) {
94
+ err.code = e.code as string;
95
+ }
96
+ this.finalizeRequest(e);
91
97
  }
92
98
  }
93
99
 
@@ -0,0 +1,29 @@
1
+ import { EventEmitter } from 'events';
2
+ import { IProjectExecutionIteration, IProjectExecutionLog } from '../reporters/Reporter.js';
3
+
4
+ export abstract class BaseRunner extends EventEmitter {
5
+ /**
6
+ * Iteration start time.
7
+ */
8
+ protected startTime?: number;
9
+ /**
10
+ * Iteration end time.
11
+ */
12
+ protected endTime?: number;
13
+ /**
14
+ * A list of already executed iterations.
15
+ */
16
+ protected executed: IProjectExecutionIteration[] = [];
17
+
18
+ /**
19
+ * Creates the report of the execution.
20
+ */
21
+ protected async createReport(): Promise<IProjectExecutionLog> {
22
+ const log: IProjectExecutionLog = {
23
+ started: this.startTime as number,
24
+ ended: this.endTime as number,
25
+ iterations: this.executed,
26
+ };
27
+ return log;
28
+ }
29
+ }
@@ -0,0 +1,234 @@
1
+ import cluster, { Worker } from 'cluster';
2
+ import { cpus } from 'os';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { HttpProject, IHttpProject } from '../../models/HttpProject.js';
6
+ import { IProjectRunnerOptions } from './ProjectRunner.js';
7
+ import { IProjectExecutionLog, IProjectExecutionIteration } from '../reporters/Reporter.js';
8
+ import { BaseRunner } from './BaseRunner.js';
9
+
10
+ const numCPUs = cpus().length;
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ export type WorkerStatus = 'initializing' | 'ready' | 'running' | 'finished' | 'error';
14
+
15
+ export interface IWorkerInfo {
16
+ /**
17
+ * Whether the worker is online.
18
+ */
19
+ online: boolean;
20
+ /**
21
+ * The number of iterations the worker is performing.
22
+ */
23
+ iterations: number;
24
+ /**
25
+ * The current status of the worker.
26
+ */
27
+ status: WorkerStatus;
28
+ /**
29
+ * Optional error message received from the worker.
30
+ */
31
+ message?: string;
32
+ }
33
+
34
+ interface WorkerInfoInternal extends IWorkerInfo {
35
+ worker: Worker;
36
+ }
37
+
38
+ export interface IWorkerMessage {
39
+ cmd: string;
40
+ data?: unknown;
41
+ }
42
+
43
+ export interface IProjectParallelRunnerOptions extends IProjectRunnerOptions {
44
+ }
45
+
46
+ export interface IProjectParallelWorkerOptions extends IProjectRunnerOptions {
47
+ project: IHttpProject;
48
+ }
49
+
50
+ export interface ProjectParallelRunner {
51
+ /**
52
+ * Dispatched when a status of a worker change.
53
+ * This can be used to render the current status.
54
+ */
55
+ on(event: 'status', listener: (info: IWorkerInfo[]) => void): this;
56
+ /**
57
+ * Dispatched when a status of a worker change.
58
+ * This can be used to render the current status.
59
+ */
60
+ once(event: 'status', listener: (info: IWorkerInfo[]) => void): this;
61
+ }
62
+
63
+ /**
64
+ * Runs a project in parallel.
65
+ * It creates a number of workers determined by the number of CPUs available on the current machine
66
+ * and the number of iterations defined in the configuration options.
67
+ *
68
+ * When the number of iterations is greater then the number of CPUs then
69
+ * the program distributes the remaining iterations among created workers.
70
+ *
71
+ * The program dispatched the `status` event. It is dispatched each time when the worker status
72
+ * change. This event can be user to refresh the UI to reflect the newest state.
73
+ */
74
+ export class ProjectParallelRunner extends BaseRunner {
75
+ project: HttpProject;
76
+ options: IProjectParallelRunnerOptions;
77
+ workers: WorkerInfoInternal[] = [];
78
+ private mainResolver?: (report: IProjectExecutionLog) => void;
79
+ private mainRejecter?: (err: Error) => void;
80
+
81
+ constructor(project: HttpProject, opts: IProjectParallelRunnerOptions = {}) {
82
+ super();
83
+ this.project = project;
84
+ this.options = opts || {};
85
+
86
+ this._exitHandler = this._exitHandler.bind(this);
87
+ }
88
+
89
+ execute(): Promise<IProjectExecutionLog> {
90
+ return new Promise((resolve, reject) => {
91
+ this.mainResolver = resolve;
92
+ this.mainRejecter = reject;
93
+ this._execute();
94
+ });
95
+ }
96
+
97
+ private _execute(): void {
98
+ try {
99
+ cluster.setupPrimary({
100
+ exec: join(__dirname, 'ProjectRunnerWorker.js'),
101
+ silent: true,
102
+ });
103
+ const { iterations = 1 } = this.options;
104
+ const poolSize = Math.min(iterations, numCPUs);
105
+ for (let i = 0; i < poolSize; i++) {
106
+ const worker = cluster.fork();
107
+ this.setupWorker(worker);
108
+ }
109
+ this.distributeIterations();
110
+ this.emit('status', this.getStatusWorkers());
111
+ cluster.on('exit', this._exitHandler);
112
+ } catch (e) {
113
+ const cause = e as Error;
114
+ this.mainRejecter!(cause);
115
+ }
116
+ }
117
+
118
+ private getStatusWorkers(): IWorkerInfo[] {
119
+ const { workers } = this;
120
+ const result: IWorkerInfo[] = [];
121
+ workers.forEach((info) => {
122
+ const cp = { ...info } as any;
123
+ delete cp.worker;
124
+ result.push(cp);
125
+ });
126
+ return result;
127
+ }
128
+
129
+ private distributeIterations(): void {
130
+ const workers = this.workers.length;
131
+ const { iterations = 1 } = this.options;
132
+ let iterationsRemaining = iterations - workers;
133
+ let currentIndex = 0;
134
+ while (iterationsRemaining > 0) {
135
+ this.workers[currentIndex].iterations += 1;
136
+ iterationsRemaining--;
137
+ currentIndex++;
138
+ if (currentIndex + 1 === workers) {
139
+ currentIndex = 0;
140
+ }
141
+ }
142
+ }
143
+
144
+ private setupWorker(worker: Worker): void {
145
+ this.workers.push({
146
+ worker,
147
+ online: false,
148
+ iterations: 1,
149
+ status: 'initializing',
150
+ });
151
+ worker.on('message', this._messageHandler.bind(this, worker));
152
+ }
153
+
154
+ private _messageHandler(worker: Worker, message: IWorkerMessage): void {
155
+ switch (message.cmd) {
156
+ case 'online': this.setOnline(worker); break;
157
+ case 'result': this.setRunResult(worker, message); break;
158
+ case 'error': this.setRunError(worker, message); break;
159
+ }
160
+ }
161
+
162
+ private _exitHandler(worker: Worker): void {
163
+ const info = this.workers.find(i => i.worker === worker);
164
+ if (!info) {
165
+ return;
166
+ }
167
+ this.finishWhenReady();
168
+ }
169
+
170
+ private setOnline(worker: Worker): void {
171
+ const info = this.workers.find(i => i.worker === worker);
172
+ if (!info) {
173
+ return;
174
+ }
175
+ info.online = true;
176
+ info.status = 'ready';
177
+ this.runWhenReady();
178
+ this.emit('status', this.getStatusWorkers());
179
+ }
180
+
181
+ private setRunResult(worker: Worker, message: IWorkerMessage): void {
182
+ const reports = message.data as IProjectExecutionIteration[];
183
+ this.executed = this.executed.concat(reports);
184
+ worker.destroy();
185
+ const info = this.workers.find(i => i.worker === worker);
186
+ if (!info) {
187
+ return;
188
+ }
189
+ info.status = 'finished';
190
+ this.emit('status', this.getStatusWorkers());
191
+ }
192
+
193
+ private runWhenReady(): void {
194
+ const waiting = this.workers.some(i => !i.online);
195
+ if (waiting) {
196
+ return;
197
+ }
198
+ this.startTime = Date.now();
199
+ this.workers.forEach((info) => {
200
+ const opts: IProjectParallelWorkerOptions = { ...this.options, project: this.project.toJSON() };
201
+ opts.iterations = info.iterations;
202
+ info.status = 'running';
203
+ info.worker.send({
204
+ cmd: 'run',
205
+ data: opts,
206
+ });
207
+ });
208
+ }
209
+
210
+ private async finishWhenReady(): Promise<void> {
211
+ if (this.endTime) {
212
+ return;
213
+ }
214
+ const working = this.workers.some(i => !['finished', 'error'].includes(i.status));
215
+ if (working || !this.mainResolver) {
216
+ return;
217
+ }
218
+ this.endTime = Date.now();
219
+ const report = await this.createReport();
220
+ this.mainResolver(report);
221
+ cluster.off('exit', this._exitHandler);
222
+ }
223
+
224
+ private setRunError(worker: Worker, message: IWorkerMessage): void {
225
+ worker.destroy();
226
+ const info = this.workers.find(i => i.worker === worker);
227
+ if (!info) {
228
+ return;
229
+ }
230
+ info.status = 'error';
231
+ info.message = message.data as string;
232
+ this.emit('status', this.getStatusWorkers());
233
+ }
234
+ }