@mrjacket/ahko 0.3.0 → 0.4.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.md +14 -0
- package/README.md +22 -2
- package/dist/errors/timeout.error.d.ts +15 -2
- package/dist/index.cjs +200 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +198 -25
- package/dist/index.js.map +1 -1
- package/dist/models/options.model.d.ts +7 -0
- package/dist/scheduler/signal.d.ts +20 -0
- package/dist/scheduler/task-runner.d.ts +17 -8
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-09-22 — Timeout & Robust Cancellation
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Execution timeout control via `timeoutMs` in `IScheduleOptions`.
|
|
12
|
+
- Rejection with `AhkoTimeoutError` containing exceeded `timeoutMs` threshold and descriptive message.
|
|
13
|
+
- Immediate rejection and slot recovery for hanging, uncooperative tasks via `Promise.race`.
|
|
14
|
+
- Active execution timeout isolation (queued waiting time and backoff delays do not consume execution timeout).
|
|
15
|
+
- Fresh `timeoutMs` window allocation on retry attempts.
|
|
16
|
+
- Unified signal coordination via `combineSignals` utility with deterministic listener detachment.
|
|
17
|
+
- Strict configuration validation for `timeoutMs` (positive finite numbers).
|
|
18
|
+
- Comprehensive unit test suite covering execution timeouts, cancellation precedence, uncooperative tasks, retries, and signal combination.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
8
22
|
## [0.3.0] - 2026-09-22 — Retry & Backoff
|
|
9
23
|
|
|
10
24
|
### Added
|
package/README.md
CHANGED
|
@@ -160,7 +160,26 @@ const taskPromise = ahko.schedule(
|
|
|
160
160
|
controller.abort();
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
### 6.
|
|
163
|
+
### 6. Execution Deadlines & Timeouts (`timeoutMs`)
|
|
164
|
+
|
|
165
|
+
Enforce deadlines per attempt. If a task exceeds `timeoutMs`, its context signal is aborted and the task rejects with `AhkoTimeoutError`:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
try {
|
|
169
|
+
await ahko.schedule(
|
|
170
|
+
async ({ signal }) => {
|
|
171
|
+
return callLongRunningApi({ signal });
|
|
172
|
+
},
|
|
173
|
+
{ timeoutMs: 3000 } // aborts and rejects if running longer than 3 seconds
|
|
174
|
+
);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof AhkoTimeoutError) {
|
|
177
|
+
console.error(`Timed out after ${error.timeoutMs}ms`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 7. Telemetry (`stats`)
|
|
164
183
|
|
|
165
184
|
Inspect real-time scheduler state without synthetic metrics:
|
|
166
185
|
|
|
@@ -174,7 +193,7 @@ console.log(stats);
|
|
|
174
193
|
// completedTasks: 42,
|
|
175
194
|
// failedTasks: 1,
|
|
176
195
|
// cancelledTasks: 2,
|
|
177
|
-
// timedOutTasks:
|
|
196
|
+
// timedOutTasks: 1,
|
|
178
197
|
// capacity: 3
|
|
179
198
|
// }
|
|
180
199
|
```
|
|
@@ -214,6 +233,7 @@ Schedules an asynchronous task with full return type inference.
|
|
|
214
233
|
| `delay` | `number` | `0` | Delay in milliseconds when strategy is `"delay"`. |
|
|
215
234
|
| `idleTimeout` | `number` | `undefined` | Maximum time to wait for idle window before forcing queue entry. |
|
|
216
235
|
| `retry` | `IRetryOptions` | `undefined` | Automatic retry policy (attempts, backoff, jitter, predicate). |
|
|
236
|
+
| `timeoutMs` | `number` | `undefined` | Maximum execution duration in milliseconds per attempt before aborting with `AhkoTimeoutError`. |
|
|
217
237
|
| `signal` | `AbortSignal` | `undefined` | Optional external `AbortSignal` for cooperative cancellation. |
|
|
218
238
|
|
|
219
239
|
### `ahko.idle<T>(task: ITask<T>, options?: Omit<IScheduleOptions, "strategy">): Promise<T>`
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { AhkoError } from "./ahko.error.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for constructing an AhkoTimeoutError.
|
|
4
|
+
*/
|
|
5
|
+
export interface IAhkoTimeoutErrorOptions extends ErrorOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The timeout threshold in milliseconds that was exceeded.
|
|
8
|
+
*/
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
12
|
* Thrown when a task exceeds its allotted timeout duration.
|
|
4
13
|
*/
|
|
5
14
|
export declare class AhkoTimeoutError extends AhkoError {
|
|
15
|
+
/**
|
|
16
|
+
* The timeout threshold in milliseconds that was exceeded, if configured.
|
|
17
|
+
*/
|
|
18
|
+
readonly timeoutMs?: number;
|
|
6
19
|
/**
|
|
7
20
|
* Creates a new AhkoTimeoutError.
|
|
8
21
|
*
|
|
9
22
|
* @param message - Explanation of timeout expiry.
|
|
10
|
-
* @param options - Standard Error options including cause.
|
|
23
|
+
* @param options - Standard Error options including optional timeoutMs and cause.
|
|
11
24
|
*/
|
|
12
|
-
constructor(message?: string, options?:
|
|
25
|
+
constructor(message?: string, options?: IAhkoTimeoutErrorOptions);
|
|
13
26
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -31,7 +31,8 @@ __export(src_exports, {
|
|
|
31
31
|
EScheduleStrategy: () => EScheduleStrategy,
|
|
32
32
|
ETaskState: () => ETaskState,
|
|
33
33
|
VERSION: () => VERSION,
|
|
34
|
-
calculateBackoff: () => calculateBackoff
|
|
34
|
+
calculateBackoff: () => calculateBackoff,
|
|
35
|
+
combineSignals: () => combineSignals
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(src_exports);
|
|
37
38
|
|
|
@@ -73,6 +74,26 @@ var EScheduleStrategy = /* @__PURE__ */ ((EScheduleStrategy2) => {
|
|
|
73
74
|
return EScheduleStrategy2;
|
|
74
75
|
})(EScheduleStrategy || {});
|
|
75
76
|
|
|
77
|
+
// src/errors/timeout.error.ts
|
|
78
|
+
var AhkoTimeoutError = class extends AhkoError {
|
|
79
|
+
/**
|
|
80
|
+
* The timeout threshold in milliseconds that was exceeded, if configured.
|
|
81
|
+
*/
|
|
82
|
+
timeoutMs;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a new AhkoTimeoutError.
|
|
85
|
+
*
|
|
86
|
+
* @param message - Explanation of timeout expiry.
|
|
87
|
+
* @param options - Standard Error options including optional timeoutMs and cause.
|
|
88
|
+
*/
|
|
89
|
+
constructor(message = "Task execution timed out", options) {
|
|
90
|
+
super(message, options);
|
|
91
|
+
this.name = "AhkoTimeoutError";
|
|
92
|
+
this.timeoutMs = options?.timeoutMs;
|
|
93
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
76
97
|
// src/models/state.model.ts
|
|
77
98
|
var ETaskState = /* @__PURE__ */ ((ETaskState2) => {
|
|
78
99
|
ETaskState2["PENDING"] = "pending";
|
|
@@ -219,6 +240,13 @@ var TaskQueue = class {
|
|
|
219
240
|
);
|
|
220
241
|
}
|
|
221
242
|
}
|
|
243
|
+
if (options?.timeoutMs !== void 0) {
|
|
244
|
+
if (typeof options.timeoutMs !== "number" || Number.isNaN(options.timeoutMs) || !Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0) {
|
|
245
|
+
throw new AhkoConfigurationError(
|
|
246
|
+
`Invalid timeoutMs "${options.timeoutMs}". timeoutMs must be a positive finite number greater than 0.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
222
250
|
if (options) {
|
|
223
251
|
this.runnerOptions.set(runner, options);
|
|
224
252
|
}
|
|
@@ -361,7 +389,7 @@ var TaskQueue = class {
|
|
|
361
389
|
this.scheduleRetry(runner, options);
|
|
362
390
|
return;
|
|
363
391
|
}
|
|
364
|
-
if (runner.state === "timed_out" /* TIMED_OUT */) {
|
|
392
|
+
if (runner.state === "timed_out" /* TIMED_OUT */ || error instanceof AhkoTimeoutError) {
|
|
365
393
|
this.timedOutTasks++;
|
|
366
394
|
} else {
|
|
367
395
|
this.failedTasks++;
|
|
@@ -464,6 +492,10 @@ var TaskRunner = class {
|
|
|
464
492
|
task;
|
|
465
493
|
/** User-supplied AbortSignal for external cancellation */
|
|
466
494
|
externalSignal;
|
|
495
|
+
/** Maximum execution duration allowed in milliseconds */
|
|
496
|
+
timeoutMs;
|
|
497
|
+
/** Active timeout timer identifier */
|
|
498
|
+
timeoutTimerId;
|
|
467
499
|
/** Abort event listener reference for clean detachment */
|
|
468
500
|
abortListener;
|
|
469
501
|
/** Promise resolve handler */
|
|
@@ -474,16 +506,20 @@ var TaskRunner = class {
|
|
|
474
506
|
promise;
|
|
475
507
|
/** Callback invoked when runner is cancelled while pending */
|
|
476
508
|
onCancel;
|
|
509
|
+
/** Current execution attempt count (1-indexed) */
|
|
510
|
+
attempt = 1;
|
|
477
511
|
/**
|
|
478
512
|
* Creates a new TaskRunner instance.
|
|
479
513
|
*
|
|
480
514
|
* @param task - The asynchronous work unit to run.
|
|
481
515
|
* @param externalSignal - Optional external AbortSignal to propagate.
|
|
516
|
+
* @param timeoutMs - Optional maximum execution time in milliseconds.
|
|
482
517
|
*/
|
|
483
|
-
constructor(task, externalSignal) {
|
|
518
|
+
constructor(task, externalSignal, timeoutMs) {
|
|
484
519
|
this.taskId = `task_${Date.now().toString(36)}_${(++taskIdCounter).toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
485
520
|
this.task = task;
|
|
486
521
|
this.externalSignal = externalSignal;
|
|
522
|
+
this.timeoutMs = timeoutMs;
|
|
487
523
|
this.abortController = new AbortController();
|
|
488
524
|
this.promise = new Promise((resolve, reject) => {
|
|
489
525
|
this.resolvePromise = resolve;
|
|
@@ -497,6 +533,7 @@ var TaskRunner = class {
|
|
|
497
533
|
typeof reason === "string" ? reason : "Task was cancelled prior to execution",
|
|
498
534
|
{ cause: reason instanceof Error ? reason : void 0 }
|
|
499
535
|
);
|
|
536
|
+
this.abortController.abort(cancelError);
|
|
500
537
|
this.rejectPromise(cancelError);
|
|
501
538
|
} else {
|
|
502
539
|
this.abortListener = () => {
|
|
@@ -512,8 +549,6 @@ var TaskRunner = class {
|
|
|
512
549
|
get state() {
|
|
513
550
|
return this._state;
|
|
514
551
|
}
|
|
515
|
-
/** Current execution attempt count (1-indexed) */
|
|
516
|
-
attempt = 1;
|
|
517
552
|
/**
|
|
518
553
|
* Resolves the deferred promise.
|
|
519
554
|
*
|
|
@@ -533,14 +568,14 @@ var TaskRunner = class {
|
|
|
533
568
|
this.rejectPromise(reason);
|
|
534
569
|
}
|
|
535
570
|
/**
|
|
536
|
-
* Evaluates if the task should be retried following an execution failure.
|
|
571
|
+
* Evaluates if the task should be retried following an execution failure or timeout.
|
|
537
572
|
*
|
|
538
573
|
* @param error - The error encountered during the attempt.
|
|
539
574
|
* @param retryOptions - Configured retry policy.
|
|
540
575
|
* @returns A promise resolving to true if retry should proceed, false otherwise.
|
|
541
576
|
*/
|
|
542
577
|
async canRetry(error, retryOptions) {
|
|
543
|
-
if (this._state === "cancelled" /* CANCELLED */ || this.
|
|
578
|
+
if (this._state === "cancelled" /* CANCELLED */ || (this.externalSignal?.aborted ?? false)) {
|
|
544
579
|
return false;
|
|
545
580
|
}
|
|
546
581
|
if (!retryOptions || typeof retryOptions.attempts !== "number") {
|
|
@@ -561,12 +596,13 @@ var TaskRunner = class {
|
|
|
561
596
|
}
|
|
562
597
|
this.attempt++;
|
|
563
598
|
this._state = "pending" /* PENDING */;
|
|
599
|
+
this.abortController = new AbortController();
|
|
564
600
|
return true;
|
|
565
601
|
}
|
|
566
602
|
/**
|
|
567
603
|
* Executes the task within an allocated concurrency slot.
|
|
568
604
|
*
|
|
569
|
-
* @returns A promise resolving to the task result or rejecting on failure/cancellation.
|
|
605
|
+
* @returns A promise resolving to the task result or rejecting on failure/cancellation/timeout.
|
|
570
606
|
*/
|
|
571
607
|
async run() {
|
|
572
608
|
if (this._state === "cancelled" /* CANCELLED */) {
|
|
@@ -577,14 +613,100 @@ var TaskRunner = class {
|
|
|
577
613
|
signal: this.abortController.signal,
|
|
578
614
|
taskId: this.taskId
|
|
579
615
|
};
|
|
616
|
+
let abortListener;
|
|
617
|
+
const abortPromise = new Promise((_, reject) => {
|
|
618
|
+
abortListener = () => {
|
|
619
|
+
if (this._state === "timed_out" /* TIMED_OUT */) {
|
|
620
|
+
reject(
|
|
621
|
+
new AhkoTimeoutError(
|
|
622
|
+
`Task execution timed out after ${this.timeoutMs}ms`,
|
|
623
|
+
{ timeoutMs: this.timeoutMs }
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
} else {
|
|
627
|
+
const reason = this.abortController.signal.reason;
|
|
628
|
+
reject(
|
|
629
|
+
new AhkoCancellationError("Task was cancelled during execution", {
|
|
630
|
+
cause: reason instanceof Error ? reason : void 0
|
|
631
|
+
})
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
this.abortController.signal.addEventListener("abort", abortListener, { once: true });
|
|
636
|
+
});
|
|
637
|
+
let timeoutPromise;
|
|
638
|
+
if (this.timeoutMs !== void 0) {
|
|
639
|
+
timeoutPromise = new Promise((_, reject) => {
|
|
640
|
+
this.timeoutTimerId = setTimeout(() => {
|
|
641
|
+
if (this._state !== "running" /* RUNNING */) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
this._state = "timed_out" /* TIMED_OUT */;
|
|
645
|
+
const timeoutError = new AhkoTimeoutError(
|
|
646
|
+
`Task execution timed out after ${this.timeoutMs}ms`,
|
|
647
|
+
{ timeoutMs: this.timeoutMs }
|
|
648
|
+
);
|
|
649
|
+
this.abortController.abort(timeoutError);
|
|
650
|
+
reject(timeoutError);
|
|
651
|
+
}, this.timeoutMs);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
let taskExecutionPromise;
|
|
655
|
+
try {
|
|
656
|
+
taskExecutionPromise = Promise.resolve(this.task(context));
|
|
657
|
+
} catch (syncError) {
|
|
658
|
+
taskExecutionPromise = Promise.reject(syncError);
|
|
659
|
+
}
|
|
660
|
+
taskExecutionPromise.catch(() => {
|
|
661
|
+
});
|
|
662
|
+
const racePromises = [
|
|
663
|
+
taskExecutionPromise,
|
|
664
|
+
abortPromise
|
|
665
|
+
];
|
|
666
|
+
if (timeoutPromise) {
|
|
667
|
+
racePromises.push(timeoutPromise);
|
|
668
|
+
}
|
|
580
669
|
try {
|
|
581
|
-
const result = await
|
|
670
|
+
const result = await Promise.race(racePromises);
|
|
671
|
+
this.clearTimeoutTimer();
|
|
672
|
+
if (abortListener) {
|
|
673
|
+
this.abortController.signal.removeEventListener("abort", abortListener);
|
|
674
|
+
}
|
|
675
|
+
if (this._state === "timed_out" /* TIMED_OUT */) {
|
|
676
|
+
throw new AhkoTimeoutError(
|
|
677
|
+
`Task execution timed out after ${this.timeoutMs}ms`,
|
|
678
|
+
{ timeoutMs: this.timeoutMs }
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (this._state === "cancelled" /* CANCELLED */) {
|
|
682
|
+
throw new AhkoCancellationError("Task was cancelled during execution");
|
|
683
|
+
}
|
|
582
684
|
this._state = "completed" /* COMPLETED */;
|
|
583
685
|
return result;
|
|
584
686
|
} catch (error) {
|
|
585
|
-
|
|
687
|
+
this.clearTimeoutTimer();
|
|
688
|
+
if (abortListener) {
|
|
689
|
+
this.abortController.signal.removeEventListener("abort", abortListener);
|
|
690
|
+
}
|
|
691
|
+
if (this._state === "timed_out" /* TIMED_OUT */ || error instanceof AhkoTimeoutError) {
|
|
692
|
+
this._state = "timed_out" /* TIMED_OUT */;
|
|
693
|
+
if (error instanceof AhkoTimeoutError) {
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
throw new AhkoTimeoutError(
|
|
697
|
+
`Task execution timed out after ${this.timeoutMs}ms`,
|
|
698
|
+
{
|
|
699
|
+
timeoutMs: this.timeoutMs,
|
|
700
|
+
cause: error instanceof Error ? error : void 0
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
const isCancelled = this._state === "cancelled" /* CANCELLED */ || this.abortController.signal.aborted || (this.externalSignal?.aborted ?? false);
|
|
586
705
|
if (isCancelled) {
|
|
587
706
|
this._state = "cancelled" /* CANCELLED */;
|
|
707
|
+
if (error instanceof AhkoCancellationError) {
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
588
710
|
throw new AhkoCancellationError("Task was cancelled during execution", {
|
|
589
711
|
cause: error instanceof Error ? error : void 0
|
|
590
712
|
});
|
|
@@ -593,6 +715,15 @@ var TaskRunner = class {
|
|
|
593
715
|
throw error;
|
|
594
716
|
}
|
|
595
717
|
}
|
|
718
|
+
/**
|
|
719
|
+
* Clears the active timeout timer.
|
|
720
|
+
*/
|
|
721
|
+
clearTimeoutTimer() {
|
|
722
|
+
if (this.timeoutTimerId !== void 0) {
|
|
723
|
+
clearTimeout(this.timeoutTimerId);
|
|
724
|
+
this.timeoutTimerId = void 0;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
596
727
|
/**
|
|
597
728
|
* Cancels the task, aborting pending or running execution.
|
|
598
729
|
*
|
|
@@ -604,6 +735,7 @@ var TaskRunner = class {
|
|
|
604
735
|
}
|
|
605
736
|
const wasPending = this._state === "pending" /* PENDING */;
|
|
606
737
|
this._state = "cancelled" /* CANCELLED */;
|
|
738
|
+
this.clearTimeoutTimer();
|
|
607
739
|
this.abortController.abort(reason);
|
|
608
740
|
this.cleanup();
|
|
609
741
|
if (wasPending) {
|
|
@@ -625,6 +757,7 @@ var TaskRunner = class {
|
|
|
625
757
|
* Detaches event listeners from external signal to guarantee memory safety.
|
|
626
758
|
*/
|
|
627
759
|
cleanup() {
|
|
760
|
+
this.clearTimeoutTimer();
|
|
628
761
|
if (this.externalSignal && this.abortListener) {
|
|
629
762
|
this.externalSignal.removeEventListener("abort", this.abortListener);
|
|
630
763
|
}
|
|
@@ -676,7 +809,7 @@ var Ahko = class {
|
|
|
676
809
|
if (typeof task !== "function") {
|
|
677
810
|
throw new AhkoConfigurationError("Task must be a valid function.");
|
|
678
811
|
}
|
|
679
|
-
const runner = new TaskRunner(task, options?.signal);
|
|
812
|
+
const runner = new TaskRunner(task, options?.signal, options?.timeoutMs);
|
|
680
813
|
return this.queue.enqueue(runner, options);
|
|
681
814
|
}
|
|
682
815
|
/**
|
|
@@ -725,7 +858,7 @@ var Ahko = class {
|
|
|
725
858
|
};
|
|
726
859
|
|
|
727
860
|
// src/version.ts
|
|
728
|
-
var VERSION = "0.
|
|
861
|
+
var VERSION = "0.4.0";
|
|
729
862
|
|
|
730
863
|
// src/errors/queue.error.ts
|
|
731
864
|
var AhkoQueueError = class extends AhkoError {
|
|
@@ -742,20 +875,60 @@ var AhkoQueueError = class extends AhkoError {
|
|
|
742
875
|
}
|
|
743
876
|
};
|
|
744
877
|
|
|
745
|
-
// src/
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
878
|
+
// src/scheduler/signal.ts
|
|
879
|
+
function combineSignals(signals) {
|
|
880
|
+
const activeSignals = signals.filter(
|
|
881
|
+
(signal) => signal !== void 0
|
|
882
|
+
);
|
|
883
|
+
if (activeSignals.length === 0) {
|
|
884
|
+
const controller2 = new AbortController();
|
|
885
|
+
return {
|
|
886
|
+
signal: controller2.signal,
|
|
887
|
+
cleanup: () => {
|
|
888
|
+
}
|
|
889
|
+
};
|
|
757
890
|
}
|
|
758
|
-
|
|
891
|
+
const alreadyAborted = activeSignals.find((s) => s.aborted);
|
|
892
|
+
if (alreadyAborted) {
|
|
893
|
+
const controller2 = new AbortController();
|
|
894
|
+
controller2.abort(alreadyAborted.reason);
|
|
895
|
+
return {
|
|
896
|
+
signal: controller2.signal,
|
|
897
|
+
cleanup: () => {
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
if (activeSignals.length === 1) {
|
|
902
|
+
return {
|
|
903
|
+
signal: activeSignals[0],
|
|
904
|
+
cleanup: () => {
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
const controller = new AbortController();
|
|
909
|
+
const cleanupFns = [];
|
|
910
|
+
const onAbort = (event) => {
|
|
911
|
+
const target = event.target;
|
|
912
|
+
cleanup();
|
|
913
|
+
controller.abort(target.reason);
|
|
914
|
+
};
|
|
915
|
+
for (const sig of activeSignals) {
|
|
916
|
+
sig.addEventListener("abort", onAbort, { once: true });
|
|
917
|
+
cleanupFns.push(() => {
|
|
918
|
+
sig.removeEventListener("abort", onAbort);
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
const cleanup = () => {
|
|
922
|
+
for (const fn of cleanupFns) {
|
|
923
|
+
fn();
|
|
924
|
+
}
|
|
925
|
+
cleanupFns.length = 0;
|
|
926
|
+
};
|
|
927
|
+
return {
|
|
928
|
+
signal: controller.signal,
|
|
929
|
+
cleanup
|
|
930
|
+
};
|
|
931
|
+
}
|
|
759
932
|
// Annotate the CommonJS export names for ESM import in node:
|
|
760
933
|
0 && (module.exports = {
|
|
761
934
|
Ahko,
|
|
@@ -769,6 +942,7 @@ var AhkoTimeoutError = class extends AhkoError {
|
|
|
769
942
|
EScheduleStrategy,
|
|
770
943
|
ETaskState,
|
|
771
944
|
VERSION,
|
|
772
|
-
calculateBackoff
|
|
945
|
+
calculateBackoff,
|
|
946
|
+
combineSignals
|
|
773
947
|
});
|
|
774
948
|
//# sourceMappingURL=index.cjs.map
|