@api-client/core 0.5.25 → 0.5.28

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 (68) hide show
  1. package/build/browser.d.ts +2 -1
  2. package/build/browser.js.map +1 -1
  3. package/build/index.d.ts +2 -1
  4. package/build/index.js.map +1 -1
  5. package/build/src/events/EventTypes.d.ts +16 -0
  6. package/build/src/events/EventTypes.js +2 -0
  7. package/build/src/events/EventTypes.js.map +1 -1
  8. package/build/src/events/Events.d.ts +16 -0
  9. package/build/src/events/Events.js +2 -0
  10. package/build/src/events/Events.js.map +1 -1
  11. package/build/src/events/transport/TransportEventTypes.d.ts +31 -0
  12. package/build/src/events/transport/TransportEventTypes.js +36 -0
  13. package/build/src/events/transport/TransportEventTypes.js.map +1 -0
  14. package/build/src/events/transport/TransportEvents.d.ts +77 -0
  15. package/build/src/events/transport/TransportEvents.js +91 -0
  16. package/build/src/events/transport/TransportEvents.js.map +1 -0
  17. package/build/src/mocking/ProjectMock.d.ts +2 -0
  18. package/build/src/mocking/ProjectMock.js +3 -0
  19. package/build/src/mocking/ProjectMock.js.map +1 -1
  20. package/build/src/mocking/lib/Url.d.ts +12 -0
  21. package/build/src/mocking/lib/Url.js +31 -0
  22. package/build/src/mocking/lib/Url.js.map +1 -0
  23. package/build/src/models/RequestConfig.d.ts +10 -0
  24. package/build/src/models/RequestConfig.js +13 -1
  25. package/build/src/models/RequestConfig.js.map +1 -1
  26. package/build/src/runtime/http-engine/CoreEngine.js +1 -1
  27. package/build/src/runtime/http-engine/CoreEngine.js.map +1 -1
  28. package/build/src/runtime/http-engine/HttpEngine.d.ts +12 -0
  29. package/build/src/runtime/http-engine/HttpEngine.js +34 -0
  30. package/build/src/runtime/http-engine/HttpEngine.js.map +1 -1
  31. package/build/src/runtime/node/InteropInterfaces.d.ts +5 -0
  32. package/build/src/runtime/node/ProjectParallelRunner.d.ts +20 -0
  33. package/build/src/runtime/node/ProjectParallelRunner.js +69 -1
  34. package/build/src/runtime/node/ProjectParallelRunner.js.map +1 -1
  35. package/build/src/runtime/node/ProjectRequestRunner.d.ts +8 -0
  36. package/build/src/runtime/node/ProjectRequestRunner.js +19 -0
  37. package/build/src/runtime/node/ProjectRequestRunner.js.map +1 -1
  38. package/build/src/runtime/node/ProjectRunner.d.ts +25 -0
  39. package/build/src/runtime/node/ProjectRunner.js +55 -0
  40. package/build/src/runtime/node/ProjectRunner.js.map +1 -1
  41. package/build/src/runtime/node/ProjectRunnerWorker.js +3 -0
  42. package/build/src/runtime/node/ProjectRunnerWorker.js.map +1 -1
  43. package/build/src/runtime/node/ProjectSerialRunner.js +3 -0
  44. package/build/src/runtime/node/ProjectSerialRunner.js.map +1 -1
  45. package/build/src/runtime/node/RequestFactory.d.ts +6 -0
  46. package/build/src/runtime/node/RequestFactory.js +10 -1
  47. package/build/src/runtime/node/RequestFactory.js.map +1 -1
  48. package/build/src/runtime/node/enums.d.ts +8 -0
  49. package/build/src/runtime/node/enums.js +10 -0
  50. package/build/src/runtime/node/enums.js.map +1 -0
  51. package/package.json +1 -1
  52. package/src/events/EventTypes.ts +2 -0
  53. package/src/events/Events.ts +2 -0
  54. package/src/events/transport/TransportEventTypes.ts +37 -0
  55. package/src/events/transport/TransportEvents.ts +116 -0
  56. package/src/mocking/ProjectMock.ts +3 -0
  57. package/src/mocking/lib/Url.ts +35 -0
  58. package/src/models/RequestConfig.ts +19 -1
  59. package/src/runtime/http-engine/CoreEngine.ts +1 -1
  60. package/src/runtime/http-engine/HttpEngine.ts +39 -0
  61. package/src/runtime/node/InteropInterfaces.ts +6 -0
  62. package/src/runtime/node/ProjectParallelRunner.ts +75 -1
  63. package/src/runtime/node/ProjectRequestRunner.ts +22 -0
  64. package/src/runtime/node/ProjectRunner.ts +63 -0
  65. package/src/runtime/node/ProjectRunnerWorker.ts +3 -0
  66. package/src/runtime/node/ProjectSerialRunner.ts +3 -2
  67. package/src/runtime/node/RequestFactory.ts +11 -1
  68. package/src/runtime/node/enums.ts +8 -0
@@ -59,6 +59,12 @@ export interface IRequestBaseConfig {
59
59
  * Whether the processor should validate certificates.
60
60
  */
61
61
  validateCertificates?: boolean;
62
+
63
+ /**
64
+ * Optional signal from an `AbortController`.
65
+ * This is populated only when executing a request. This value is opaque for the data store.
66
+ */
67
+ signal?: AbortSignal;
62
68
  }
63
69
 
64
70
  /**
@@ -150,6 +156,12 @@ export class RequestConfig {
150
156
  */
151
157
  sentMessageLimit?: number;
152
158
 
159
+ /**
160
+ * Optional signal from an `AbortController`.
161
+ * This is populated only when executing a request. This value is opaque for the data store.
162
+ */
163
+ signal?: AbortSignal;
164
+
153
165
  static withDefaults(): RequestConfig {
154
166
  return new RequestConfig({
155
167
  kind: Kind,
@@ -209,7 +221,7 @@ export class RequestConfig {
209
221
  new(init: IRequestConfig): void {
210
222
  const {
211
223
  enabled, followRedirects, ignoreSessionCookies, validateCertificates, defaultHeaders, timeout, hosts, variables,
212
- defaultAccept, defaultUserAgent, proxy, proxyPassword, proxyUsername, sentMessageLimit,
224
+ defaultAccept, defaultUserAgent, proxy, proxyPassword, proxyUsername, sentMessageLimit, signal,
213
225
  } = init;
214
226
  this.kind = Kind;
215
227
  if (typeof enabled === 'boolean') {
@@ -282,6 +294,11 @@ export class RequestConfig {
282
294
  } else {
283
295
  this.sentMessageLimit = undefined;
284
296
  }
297
+ if (signal) {
298
+ this.signal = signal;
299
+ } else {
300
+ this.signal = undefined;
301
+ }
285
302
  }
286
303
 
287
304
  toJSON(): IRequestConfig {
@@ -310,6 +327,7 @@ export class RequestConfig {
310
327
  if (Array.isArray(this.variables)) {
311
328
  result.variables = this.variables.map(i => i.toJSON());
312
329
  }
330
+ // DO NOT put the `signal` here.
313
331
  return result;
314
332
  }
315
333
  }
@@ -74,7 +74,7 @@ export class CoreEngine extends HttpEngine {
74
74
  } else {
75
75
  await this.connect();
76
76
  }
77
- if (!this.socket) {
77
+ if (!this.socket || this.aborted) {
78
78
  return;
79
79
  }
80
80
  const message = await this.prepareMessage();
@@ -191,6 +191,31 @@ export abstract class HttpEngine extends EventEmitter {
191
191
  protected mainRejecter?: (err: SerializableError) => void;
192
192
  [mainPromiseSymbol]?: Promise<IRequestLog>;
193
193
 
194
+ protected _signal?: AbortSignal;
195
+
196
+ /**
197
+ * The abort signal to set on this request.
198
+ * Aborts the request when the signal fires.
199
+ * @type {(AbortSignal | undefined)}
200
+ */
201
+ get signal(): AbortSignal | undefined {
202
+ return this._signal;
203
+ }
204
+
205
+ set signal(value: AbortSignal | undefined) {
206
+ const old = this._signal;
207
+ if (old === value) {
208
+ return;
209
+ }
210
+ this._signal = value;
211
+ if (old) {
212
+ old.removeEventListener('abort', this._abortHandler);
213
+ }
214
+ if (value) {
215
+ value.addEventListener('abort', this._abortHandler);
216
+ }
217
+ }
218
+
194
219
  constructor(request: IHttpRequest, opts: HttpEngineOptions = {}) {
195
220
  super();
196
221
  this.request = new HttpRequest({ ...request });
@@ -199,6 +224,11 @@ export abstract class HttpEngine extends EventEmitter {
199
224
  this.sentRequest = new SentRequest({ ...request, startTime: Date.now() });
200
225
  this.uri = this.readUrl(request.url);
201
226
  this.hostHeader = RequestUtils.getHostHeader(request.url);
227
+
228
+ this._abortHandler = this._abortHandler.bind(this);
229
+ if (opts.signal) {
230
+ this.signal = opts.signal;
231
+ }
202
232
  }
203
233
 
204
234
  /**
@@ -245,6 +275,15 @@ export abstract class HttpEngine extends EventEmitter {
245
275
  this.socket = undefined;
246
276
  }
247
277
 
278
+ /**
279
+ * Handler for the `abort` event on the `AbortSignal`.
280
+ */
281
+ protected _abortHandler(): void {
282
+ const e = new SerializableError('Request aborted', 3);
283
+ this._errorRequest(e);
284
+ this.abort();
285
+ }
286
+
248
287
  /**
249
288
  * Sends the request.
250
289
  */
@@ -118,4 +118,10 @@ export interface IProjectRunnerOptions {
118
118
  * When not set it does not read system variables.
119
119
  */
120
120
  variables?: boolean | string[] | Record<string, string>;
121
+
122
+ /**
123
+ * Optional signal from an `AbortController`.
124
+ * It aborts the execution when the ``abort` event is dispatched.
125
+ */
126
+ signal?: AbortSignal;
121
127
  }
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { HttpProject } from '../../models/HttpProject.js';
6
6
  import { IProjectExecutionLog, IProjectExecutionIteration } from '../reporters/Reporter.js';
7
7
  import { BaseRunner } from './BaseRunner.js';
8
+ import { State } from './enums.js';
8
9
  import { IProjectParallelRunnerOptions, IProjectParallelWorkerOptions } from './InteropInterfaces.js'
9
10
 
10
11
  const numCPUs = cpus().length;
@@ -74,9 +75,13 @@ export class ProjectParallelRunner extends BaseRunner {
74
75
  constructor(project: HttpProject, opts: IProjectParallelRunnerOptions = {}) {
75
76
  super();
76
77
  this.project = project;
77
- this.options = opts || {};
78
+ this.options = opts;
78
79
 
79
80
  this._exitHandler = this._exitHandler.bind(this);
81
+ this._abortHandler = this._abortHandler.bind(this);
82
+ if (opts.signal) {
83
+ this.signal = opts.signal;
84
+ }
80
85
  }
81
86
 
82
87
  execute(): Promise<IProjectExecutionLog> {
@@ -87,7 +92,70 @@ export class ProjectParallelRunner extends BaseRunner {
87
92
  });
88
93
  }
89
94
 
95
+ protected _state: State = State.Idle;
96
+
97
+ get state(): State {
98
+ return this._state;
99
+ }
100
+
101
+ protected _signal?: AbortSignal;
102
+
103
+ /**
104
+ * The abort signal to set on this request.
105
+ * Aborts the request when the signal fires.
106
+ * @type {(AbortSignal | undefined)}
107
+ */
108
+ get signal(): AbortSignal | undefined {
109
+ return this._signal;
110
+ }
111
+
112
+ set signal(value: AbortSignal | undefined) {
113
+ const old = this._signal;
114
+ if (old === value) {
115
+ return;
116
+ }
117
+ this._signal = value;
118
+ if (old) {
119
+ old.removeEventListener('abort', this._abortHandler);
120
+ }
121
+ if (value) {
122
+ value.addEventListener('abort', this._abortHandler);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Aborts the current run.
128
+ * The promise returned by the `execute()` method will reject if not yet resolved.
129
+ */
130
+ abort(): void {
131
+ this._state = State.Aborted;
132
+ const { workers } = this;
133
+ workers.forEach((info) => {
134
+ if (info.status !== 'error') {
135
+ try {
136
+ info.worker.destroy();
137
+ } catch (e) {
138
+ // ...
139
+ }
140
+ info.status = 'error';
141
+ }
142
+ });
143
+ if (this.mainRejecter) {
144
+ this.mainRejecter!(new Error(`The execution has been aborted.`));
145
+ this.mainRejecter = undefined;
146
+ this.mainResolver = undefined;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Handler for the `abort` event on the `AbortSignal`.
152
+ */
153
+ protected _abortHandler(): void {
154
+ this.abort();
155
+ }
156
+
90
157
  private _execute(): void {
158
+ this._state = State.Running as State;
91
159
  try {
92
160
  cluster.setupPrimary({
93
161
  exec: join(__dirname, 'ProjectRunnerWorker.js'),
@@ -105,6 +173,9 @@ export class ProjectParallelRunner extends BaseRunner {
105
173
  } catch (e) {
106
174
  const cause = e as Error;
107
175
  this.mainRejecter!(cause);
176
+ this.mainResolver = undefined
177
+ this.mainRejecter = undefined
178
+ this._state = State.Idle as State;
108
179
  }
109
180
  }
110
181
 
@@ -211,7 +282,10 @@ export class ProjectParallelRunner extends BaseRunner {
211
282
  this.endTime = Date.now();
212
283
  const report = await this.createReport();
213
284
  this.mainResolver(report);
285
+ this.mainResolver = undefined
286
+ this.mainRejecter = undefined
214
287
  cluster.off('exit', this._exitHandler);
288
+ this._state = State.Idle as State;
215
289
  }
216
290
 
217
291
  private setRunError(worker: Worker, message: IWorkerMessage): void {
@@ -14,6 +14,7 @@ import { VariablesProcessor } from '../variables/VariablesProcessor.js';
14
14
  import { RequestFactory } from './RequestFactory.js';
15
15
  import { EventTypes } from '../../events/EventTypes.js';
16
16
  import { ProjectRunnerOptions, ProjectRunnerRunOptions, RunResult } from './InteropInterfaces.js';
17
+ import { State } from './enums.js';
17
18
 
18
19
  export interface ProjectRequestRunner {
19
20
  /**
@@ -62,6 +63,12 @@ export class ProjectRequestRunner extends EventEmitter {
62
63
  */
63
64
  protected variablesProcessor = new VariablesProcessor();
64
65
 
66
+ protected _state: State = State.Idle;
67
+
68
+ get state(): State {
69
+ return this._state;
70
+ }
71
+
65
72
  constructor(project: HttpProject, opts: ProjectRunnerOptions = {}) {
66
73
  super();
67
74
  this.project = project;
@@ -77,15 +84,25 @@ export class ProjectRequestRunner extends EventEmitter {
77
84
  * @returns A promise with the run result.
78
85
  */
79
86
  async run(options?: ProjectRunnerRunOptions): Promise<RunResult[]> {
87
+ this._state = State.Running as State;
80
88
  const { project } = this;
81
89
  const executed: RunResult[] = [];
82
90
  for (const request of project.requestIterator(options)) {
83
91
  const info = await this._runItem(request);
84
92
  executed.push(info);
85
93
  }
94
+ this._state = State.Idle;
86
95
  return executed;
87
96
  }
88
97
 
98
+ /**
99
+ * Aborts the current run.
100
+ * The promise returned by the `run()` method will reject if not yet resolved.
101
+ */
102
+ abort(): void {
103
+ this._state = State.Aborted;
104
+ }
105
+
89
106
  /**
90
107
  * Allows to iterate over project requests recursively and execute each request
91
108
  * in order. The generator yields the `RunResult` for the request.
@@ -101,13 +118,18 @@ export class ProjectRequestRunner extends EventEmitter {
101
118
  */
102
119
  async* [Symbol.asyncIterator](): AsyncGenerator<RunResult> {
103
120
  const { project } = this;
121
+ this._state = State.Running as State;
104
122
  for (const request of project.requestIterator({ recursive: true })) {
105
123
  const info = await this._runItem(request);
106
124
  yield info;
107
125
  }
126
+ this._state = State.Idle;
108
127
  }
109
128
 
110
129
  private async _runItem(request: ProjectRequest): Promise<RunResult> {
130
+ if (this._state === State.Aborted) {
131
+ throw new Error(`The execution has been aborted.`);
132
+ }
111
133
  const folder = request.getParent();
112
134
  const parent = folder || this.project;
113
135
  let variables: Record<string, string>;
@@ -10,6 +10,7 @@ import { IProjectExecutionIteration, IProjectExecutionLog } from '../reporters/R
10
10
  import { pathExists, readJson } from '../../lib/fs/Fs.js';
11
11
  import { BaseRunner } from './BaseRunner.js';
12
12
  import { IProjectRunnerOptions } from './InteropInterfaces.js';
13
+ import { State } from './enums.js';
13
14
 
14
15
  type ProjectParent = HttpProject | ProjectFolder;
15
16
 
@@ -127,11 +128,48 @@ export abstract class ProjectRunner extends BaseRunner {
127
128
  */
128
129
  noEmit = false;
129
130
 
131
+ /**
132
+ * When executing, this is the last user request runner.
133
+ */
134
+ protected _runner?: ProjectRequestRunner;
135
+
136
+ protected _state: State = State.Idle;
137
+
138
+ get state(): State {
139
+ return this._state;
140
+ }
141
+
142
+ protected _signal?: AbortSignal;
143
+
144
+ /**
145
+ * The abort signal to set on this request.
146
+ * Aborts the request when the signal fires.
147
+ * @type {(AbortSignal | undefined)}
148
+ */
149
+ get signal(): AbortSignal | undefined {
150
+ return this._signal;
151
+ }
152
+
153
+ set signal(value: AbortSignal | undefined) {
154
+ const old = this._signal;
155
+ if (old === value) {
156
+ return;
157
+ }
158
+ this._signal = value;
159
+ if (old) {
160
+ old.removeEventListener('abort', this._abortHandler);
161
+ }
162
+ if (value) {
163
+ value.addEventListener('abort', this._abortHandler);
164
+ }
165
+ }
166
+
130
167
  constructor() {
131
168
  super();
132
169
  this._requestHandler = this._requestHandler.bind(this);
133
170
  this._responseHandler = this._responseHandler.bind(this);
134
171
  this._errorHandler = this._errorHandler.bind(this);
172
+ this._abortHandler = this._abortHandler.bind(this);
135
173
  }
136
174
 
137
175
  /**
@@ -152,6 +190,9 @@ export abstract class ProjectRunner extends BaseRunner {
152
190
  }
153
191
  this.root = root;
154
192
  this.environment = await this.getEnvironment();
193
+ if (opts.signal) {
194
+ this.signal = opts.signal;
195
+ }
155
196
  }
156
197
 
157
198
  /**
@@ -160,6 +201,24 @@ export abstract class ProjectRunner extends BaseRunner {
160
201
  */
161
202
  abstract execute(): Promise<IProjectExecutionLog>;
162
203
 
204
+ /**
205
+ * Aborts the current run.
206
+ * The promise returned by the `execute()` method will reject if not yet resolved.
207
+ */
208
+ abort(): void {
209
+ this._state = State.Aborted;
210
+ if (this._runner) {
211
+ this._runner.abort();
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Handler for the `abort` event on the `AbortSignal`.
217
+ */
218
+ protected _abortHandler(): void {
219
+ this.abort();
220
+ }
221
+
163
222
  /**
164
223
  * Creates the report of the execution.
165
224
  */
@@ -207,6 +266,9 @@ export abstract class ProjectRunner extends BaseRunner {
207
266
  * Runs the requests from the project as configured.
208
267
  */
209
268
  protected async executeIteration(): Promise<void> {
269
+ if (this._state === State.Aborted) {
270
+ throw new Error(`The execution has been aborted.`);
271
+ }
210
272
  const { environment, project, options, hasIterations, index, noEmit } = this;
211
273
  if (!options || !project) {
212
274
  throw new Error(`Run configure() first.`);
@@ -221,6 +283,7 @@ export abstract class ProjectRunner extends BaseRunner {
221
283
  eventTarget: this.target,
222
284
  variables: this.getSystemVariables(),
223
285
  });
286
+ this._runner = runner;
224
287
 
225
288
  runner.on('request', this._requestHandler);
226
289
  runner.on('response', this._responseHandler);
@@ -7,6 +7,7 @@ import { IWorkerMessage } from './ProjectParallelRunner.js';
7
7
  import { IProjectParallelWorkerOptions } from './InteropInterfaces.js';
8
8
  import { sleep } from '../../lib/timers/Timers.js';
9
9
  import { ProjectRunner } from './ProjectRunner.js';
10
+ import { State } from './enums.js';
10
11
 
11
12
  class ProjectExeWorker extends ProjectRunner {
12
13
  initialize(): void {
@@ -40,6 +41,7 @@ class ProjectExeWorker extends ProjectRunner {
40
41
  }
41
42
  function unhandledRejection(): void {}
42
43
  process.on('unhandledRejection', unhandledRejection);
44
+ this._state = State.Running as State;
43
45
  this.startTime = Date.now();
44
46
  while (this.remaining > 0) {
45
47
  this.remaining--;
@@ -54,6 +56,7 @@ class ProjectExeWorker extends ProjectRunner {
54
56
 
55
57
  const log = await this.createReport();
56
58
  process.send!({ cmd: 'result', data: log.iterations });
59
+ this._state = State.Idle as State;
57
60
  return log;
58
61
  }
59
62
  }
@@ -2,6 +2,7 @@ import { SerializableError } from '../../models/SerializableError.js';
2
2
  import { sleep } from '../../lib/timers/Timers.js';
3
3
  import { ProjectRunner } from './ProjectRunner.js';
4
4
  import { IProjectExecutionLog } from '../reporters/Reporter.js';
5
+ import { State } from './enums.js';
5
6
 
6
7
  /**
7
8
  * Project runner that runs the requests in the project one-by-one.
@@ -15,7 +16,7 @@ export class ProjectSerialRunner extends ProjectRunner {
15
16
  if (!root) {
16
17
  throw new SerializableError(`The project runner is not configured.`, 'ECONFIGURE');
17
18
  }
18
-
19
+ this._state = State.Running as State;
19
20
  this.startTime = Date.now();
20
21
  while (this.remaining > 0) {
21
22
  this.remaining--;
@@ -29,7 +30,7 @@ export class ProjectSerialRunner extends ProjectRunner {
29
30
  await sleep(0);
30
31
  }
31
32
  }
32
-
33
+ this._state = State.Idle as State;
33
34
  this.endTime = Date.now();
34
35
  return this.createReport();
35
36
  }
@@ -73,6 +73,13 @@ export class RequestFactory {
73
73
 
74
74
  logger?: Logger;
75
75
 
76
+ /**
77
+ * The abort signal to set on this request.
78
+ * Aborts the request when the signal fires.
79
+ * @type {(AbortSignal | undefined)}
80
+ */
81
+ signal?: AbortSignal;
82
+
76
83
  /**
77
84
  * Creates an instance from the IRequest object with setting the corresponding variables.
78
85
  * @param eventTarget The main events bus.
@@ -302,7 +309,7 @@ export class RequestFactory {
302
309
  * Creates a configuration options for the HTTP engine.
303
310
  */
304
311
  async prepareEngineConfig(): Promise<HttpEngineOptions> {
305
- const { certificates, logger } = this;
312
+ const { certificates, logger, signal } = this;
306
313
  const auth = await this.readAuthorization();
307
314
  const config = await this.readConfig();
308
315
  const opts: HttpEngineOptions = {};
@@ -315,6 +322,9 @@ export class RequestFactory {
315
322
  if (logger) {
316
323
  opts.logger = logger;
317
324
  }
325
+ if (signal) {
326
+ opts.signal = signal;
327
+ }
318
328
  if (!config || config.enabled === false) {
319
329
  return opts;
320
330
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The request execution state.
3
+ */
4
+ export enum State {
5
+ Idle,
6
+ Running,
7
+ Aborted,
8
+ }