@mrjacket/ahko 0.6.0 → 1.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.
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/errors/ahko.error.ts
2
9
  var AhkoError = class extends Error {
3
10
  /**
@@ -28,6 +35,72 @@ var AhkoConfigurationError = class extends AhkoError {
28
35
  }
29
36
  };
30
37
 
38
+ // src/config/config-loader.ts
39
+ var activeConfig;
40
+ function loadConfig(config) {
41
+ activeConfig = { ...config };
42
+ }
43
+ function resetConfig() {
44
+ activeConfig = void 0;
45
+ }
46
+ async function loadConfigFile(filePath = "config.ahko.json") {
47
+ if (typeof process === "undefined" || !process.versions?.node) {
48
+ return void 0;
49
+ }
50
+ try {
51
+ const { readFile } = await import("fs/promises");
52
+ const { resolve } = await import("path");
53
+ const resolvedPath = resolve(process.cwd(), filePath);
54
+ const content = await readFile(resolvedPath, "utf-8");
55
+ const parsed = JSON.parse(content);
56
+ activeConfig = parsed;
57
+ return parsed;
58
+ } catch {
59
+ return void 0;
60
+ }
61
+ }
62
+ function tryAutoDiscoverSync() {
63
+ if (activeConfig !== void 0 || typeof process === "undefined" || !process.versions?.node) {
64
+ return;
65
+ }
66
+ try {
67
+ let fs = null;
68
+ let path = null;
69
+ if (typeof process.getBuiltinModule === "function") {
70
+ const getBuiltin = process.getBuiltinModule;
71
+ fs = getBuiltin("node:fs");
72
+ path = getBuiltin("node:path");
73
+ } else if (typeof __require === "function") {
74
+ fs = __require("fs");
75
+ path = __require("path");
76
+ }
77
+ if (fs && path) {
78
+ const configPath = path.resolve(process.cwd(), "config.ahko.json");
79
+ if (fs.existsSync(configPath)) {
80
+ const raw = fs.readFileSync(configPath, "utf-8");
81
+ activeConfig = JSON.parse(raw);
82
+ }
83
+ }
84
+ } catch {
85
+ }
86
+ }
87
+ function getActiveConfig() {
88
+ if (activeConfig === void 0) {
89
+ tryAutoDiscoverSync();
90
+ }
91
+ return activeConfig;
92
+ }
93
+ function getProfileConfig(profileName) {
94
+ const config = getActiveConfig();
95
+ if (!config) {
96
+ return void 0;
97
+ }
98
+ if (profileName) {
99
+ return config.profiles?.[profileName];
100
+ }
101
+ return config.default;
102
+ }
103
+
31
104
  // src/models/strategy.model.ts
32
105
  var EScheduleStrategy = /* @__PURE__ */ ((EScheduleStrategy2) => {
33
106
  EScheduleStrategy2["IMMEDIATE"] = "immediate";
@@ -38,6 +111,23 @@ var EScheduleStrategy = /* @__PURE__ */ ((EScheduleStrategy2) => {
38
111
  return EScheduleStrategy2;
39
112
  })(EScheduleStrategy || {});
40
113
 
114
+ // src/errors/circuit-breaker.error.ts
115
+ var AhkoCircuitBreakerOpenError = class extends AhkoError {
116
+ /** Time remaining in milliseconds before trial execution is allowed */
117
+ resetTimeoutMs;
118
+ /** Timestamp when the circuit tripped open */
119
+ trippedAt;
120
+ /** Total consecutive failures that caused the trip */
121
+ consecutiveFailures;
122
+ constructor(message = "Circuit breaker is open. Fast-failing task execution to protect downstream resources.", options) {
123
+ super(message);
124
+ this.name = "AhkoCircuitBreakerOpenError";
125
+ this.resetTimeoutMs = options?.resetTimeoutMs;
126
+ this.trippedAt = options?.trippedAt;
127
+ this.consecutiveFailures = options?.consecutiveFailures;
128
+ }
129
+ };
130
+
41
131
  // src/errors/timeout.error.ts
42
132
  var AhkoTimeoutError = class extends AhkoError {
43
133
  /**
@@ -58,6 +148,28 @@ var AhkoTimeoutError = class extends AhkoError {
58
148
  }
59
149
  };
60
150
 
151
+ // src/models/priority.model.ts
152
+ var TASK_PRIORITY_WEIGHTS = {
153
+ high: 10,
154
+ normal: 0,
155
+ low: -10
156
+ };
157
+ function resolvePriorityWeight(priority) {
158
+ if (priority === void 0) {
159
+ return TASK_PRIORITY_WEIGHTS.normal;
160
+ }
161
+ if (typeof priority === "number") {
162
+ return Number.isFinite(priority) ? priority : TASK_PRIORITY_WEIGHTS.normal;
163
+ }
164
+ if (priority === "high") {
165
+ return TASK_PRIORITY_WEIGHTS.high;
166
+ }
167
+ if (priority === "low") {
168
+ return TASK_PRIORITY_WEIGHTS.low;
169
+ }
170
+ return TASK_PRIORITY_WEIGHTS.normal;
171
+ }
172
+
61
173
  // src/models/state.model.ts
62
174
  var ETaskState = /* @__PURE__ */ ((ETaskState2) => {
63
175
  ETaskState2["PENDING"] = "pending";
@@ -94,6 +206,123 @@ function calculateBackoff(attempt, options, randomFn = Math.random) {
94
206
  return Math.floor(cappedDelay);
95
207
  }
96
208
 
209
+ // src/models/circuit-breaker.model.ts
210
+ var ECircuitState = /* @__PURE__ */ ((ECircuitState2) => {
211
+ ECircuitState2["CLOSED"] = "closed";
212
+ ECircuitState2["OPEN"] = "open";
213
+ ECircuitState2["HALF_OPEN"] = "half_open";
214
+ return ECircuitState2;
215
+ })(ECircuitState || {});
216
+
217
+ // src/scheduler/circuit-breaker.ts
218
+ var CircuitBreakerCoordinator = class {
219
+ _state = "closed" /* CLOSED */;
220
+ _consecutiveFailures = 0;
221
+ _lastFailureTime;
222
+ failureThreshold;
223
+ resetTimeoutMs;
224
+ /**
225
+ * Initializes a new CircuitBreakerCoordinator.
226
+ *
227
+ * @param options - Configuration options for threshold and cool-down window.
228
+ * @throws {AhkoConfigurationError} If options are invalid.
229
+ */
230
+ constructor(options) {
231
+ if (typeof options.failureThreshold !== "number" || Number.isNaN(options.failureThreshold) || !Number.isInteger(options.failureThreshold) || options.failureThreshold < 1) {
232
+ throw new AhkoConfigurationError(
233
+ `Invalid failureThreshold "${options.failureThreshold}". failureThreshold must be an integer greater than or equal to 1.`
234
+ );
235
+ }
236
+ if (typeof options.resetTimeoutMs !== "number" || Number.isNaN(options.resetTimeoutMs) || !Number.isFinite(options.resetTimeoutMs) || options.resetTimeoutMs <= 0) {
237
+ throw new AhkoConfigurationError(
238
+ `Invalid resetTimeoutMs "${options.resetTimeoutMs}". resetTimeoutMs must be a positive finite number greater than 0.`
239
+ );
240
+ }
241
+ this.failureThreshold = options.failureThreshold;
242
+ this.resetTimeoutMs = options.resetTimeoutMs;
243
+ }
244
+ /** Current state of the circuit breaker */
245
+ get state() {
246
+ this.refreshState();
247
+ return this._state;
248
+ }
249
+ /**
250
+ * Checks whether an execution is currently allowed.
251
+ * If the circuit is OPEN and cool-down has not elapsed, fast-fails immediately.
252
+ *
253
+ * @throws {AhkoCircuitBreakerOpenError} If the circuit is currently OPEN.
254
+ */
255
+ checkAllowed() {
256
+ this.refreshState();
257
+ if (this._state === "open" /* OPEN */) {
258
+ const remainingMs = this._lastFailureTime ? Math.max(0, this.resetTimeoutMs - (Date.now() - this._lastFailureTime)) : this.resetTimeoutMs;
259
+ throw new AhkoCircuitBreakerOpenError(
260
+ `Circuit breaker is open. Fast-failing task execution. Remaining cool-down: ${remainingMs}ms.`,
261
+ {
262
+ resetTimeoutMs: remainingMs,
263
+ trippedAt: this._lastFailureTime,
264
+ consecutiveFailures: this._consecutiveFailures
265
+ }
266
+ );
267
+ }
268
+ }
269
+ /**
270
+ * Records a successful task execution.
271
+ * Heals HALF_OPEN state back to CLOSED and resets consecutive failure counters.
272
+ */
273
+ recordSuccess() {
274
+ this._consecutiveFailures = 0;
275
+ this._state = "closed" /* CLOSED */;
276
+ }
277
+ /**
278
+ * Records a failed task execution.
279
+ * Trips CLOSED to OPEN when threshold is met, or re-trips HALF_OPEN immediately.
280
+ *
281
+ * @param _error - Optional error that caused the failure.
282
+ */
283
+ recordFailure(_error) {
284
+ this._consecutiveFailures++;
285
+ this._lastFailureTime = Date.now();
286
+ if (this._state === "half_open" /* HALF_OPEN */) {
287
+ this._state = "open" /* OPEN */;
288
+ return;
289
+ }
290
+ if (this._consecutiveFailures >= this.failureThreshold) {
291
+ this._state = "open" /* OPEN */;
292
+ }
293
+ }
294
+ /**
295
+ * Evaluates if enough time has passed to transition from OPEN to HALF_OPEN.
296
+ */
297
+ refreshState() {
298
+ if (this._state === "open" /* OPEN */ && this._lastFailureTime !== void 0) {
299
+ const elapsed = Date.now() - this._lastFailureTime;
300
+ if (elapsed >= this.resetTimeoutMs) {
301
+ this._state = "half_open" /* HALF_OPEN */;
302
+ }
303
+ }
304
+ }
305
+ /**
306
+ * Resets the circuit breaker back to initial CLOSED state.
307
+ */
308
+ reset() {
309
+ this._state = "closed" /* CLOSED */;
310
+ this._consecutiveFailures = 0;
311
+ this._lastFailureTime = void 0;
312
+ }
313
+ /**
314
+ * Returns a snapshot of circuit breaker telemetry.
315
+ */
316
+ getStats() {
317
+ this.refreshState();
318
+ return {
319
+ state: this._state,
320
+ consecutiveFailures: this._consecutiveFailures,
321
+ lastFailureTime: this._lastFailureTime
322
+ };
323
+ }
324
+ };
325
+
97
326
  // src/errors/cancellation.error.ts
98
327
  var AhkoCancellationError = class extends AhkoError {
99
328
  /**
@@ -112,6 +341,8 @@ var AhkoCancellationError = class extends AhkoError {
112
341
  // src/scheduler/debounce-coordinator.ts
113
342
  var DebounceCoordinator = class {
114
343
  entries = /* @__PURE__ */ new Map();
344
+ /** Optional callback invoked whenever entries are settled or removed from coordinator */
345
+ onSettled;
115
346
  /**
116
347
  * Schedules a task under the debounce strategy.
117
348
  *
@@ -198,6 +429,7 @@ var DebounceCoordinator = class {
198
429
  return;
199
430
  }
200
431
  this.entries.delete(key);
432
+ this.onSettled?.();
201
433
  if (entry.options?.signal && entry.abortListener) {
202
434
  entry.options.signal.removeEventListener("abort", entry.abortListener);
203
435
  }
@@ -221,6 +453,7 @@ var DebounceCoordinator = class {
221
453
  }
222
454
  clearTimeout(entry.timerId);
223
455
  this.entries.delete(key);
456
+ this.onSettled?.();
224
457
  if (entry.options?.signal && entry.abortListener) {
225
458
  entry.options.signal.removeEventListener("abort", entry.abortListener);
226
459
  }
@@ -248,6 +481,7 @@ var DebounceCoordinator = class {
248
481
  entry.reject(new AhkoCancellationError("Debounced tasks cleared"));
249
482
  }
250
483
  this.entries.clear();
484
+ this.onSettled?.();
251
485
  }
252
486
  };
253
487
 
@@ -293,6 +527,8 @@ var IdleScheduler = class {
293
527
  // src/scheduler/throttle-coordinator.ts
294
528
  var ThrottleCoordinator = class {
295
529
  entries = /* @__PURE__ */ new Map();
530
+ /** Optional callback invoked whenever entries are settled or removed from coordinator */
531
+ onSettled;
296
532
  /**
297
533
  * Schedules a task under the throttle strategy.
298
534
  *
@@ -378,6 +614,7 @@ var ThrottleCoordinator = class {
378
614
  return;
379
615
  }
380
616
  this.entries.delete(key);
617
+ this.onSettled?.();
381
618
  }
382
619
  /**
383
620
  * Cancels any pending trailing throttled task for a given key.
@@ -394,6 +631,7 @@ var ThrottleCoordinator = class {
394
631
  clearTimeout(entry.windowTimerId);
395
632
  }
396
633
  this.entries.delete(key);
634
+ this.onSettled?.();
397
635
  if (entry.trailingReject) {
398
636
  const cancelError = new AhkoCancellationError(
399
637
  typeof reason === "string" ? reason : "Throttled task was cancelled",
@@ -421,6 +659,7 @@ var ThrottleCoordinator = class {
421
659
  }
422
660
  }
423
661
  this.entries.clear();
662
+ this.onSettled?.();
424
663
  }
425
664
  };
426
665
 
@@ -518,6 +757,10 @@ var TaskQueue = class {
518
757
  throttleCoordinator = new ThrottleCoordinator();
519
758
  /** Lifecycle event emitter for task and scheduler events */
520
759
  emitter = new AhkoEventEmitter();
760
+ /** Circuit breaker coordinator if configured */
761
+ circuitBreakerCoordinator;
762
+ /** Pause state flag */
763
+ _isPaused = false;
521
764
  /** Set of pending resolvers awaiting scheduler idle transition */
522
765
  idleResolvers = /* @__PURE__ */ new Set();
523
766
  /** WeakMap associating task runners with their scheduling options */
@@ -539,9 +782,10 @@ var TaskQueue = class {
539
782
  *
540
783
  * @param concurrency - Maximum concurrent tasks (defaults to Infinity).
541
784
  * @param minIntervalMs - Minimum interval in milliseconds between task dispatches.
785
+ * @param circuitBreakerOptions - Optional circuit breaker policy configuration.
542
786
  * @throws {AhkoConfigurationError} If concurrency is less than 1 or minIntervalMs is invalid.
543
787
  */
544
- constructor(concurrency = Infinity, minIntervalMs = 0) {
788
+ constructor(concurrency = Infinity, minIntervalMs = 0, circuitBreakerOptions) {
545
789
  if (Number.isNaN(concurrency) || concurrency < 1) {
546
790
  throw new AhkoConfigurationError(
547
791
  `Invalid concurrency "${concurrency}". Must be a number greater than or equal to 1.`
@@ -554,6 +798,50 @@ var TaskQueue = class {
554
798
  }
555
799
  this.concurrency = concurrency;
556
800
  this.minIntervalMs = minIntervalMs;
801
+ if (circuitBreakerOptions) {
802
+ this.circuitBreakerCoordinator = new CircuitBreakerCoordinator(circuitBreakerOptions);
803
+ }
804
+ this.debounceCoordinator.onSettled = () => this.checkIdle();
805
+ this.throttleCoordinator.onSettled = () => this.checkIdle();
806
+ }
807
+ /**
808
+ * Pauses queue execution. Running tasks will complete normally, but no new pending tasks will be dispatched.
809
+ */
810
+ pause() {
811
+ this._isPaused = true;
812
+ }
813
+ /**
814
+ * Resumes queue execution, immediately dispatching waiting tasks up to available concurrency.
815
+ */
816
+ resume() {
817
+ if (this._isPaused) {
818
+ this._isPaused = false;
819
+ this.pump();
820
+ }
821
+ }
822
+ /**
823
+ * Checks whether the task queue is currently paused.
824
+ */
825
+ isPaused() {
826
+ return this._isPaused;
827
+ }
828
+ /**
829
+ * Inserts a task runner into the queue based on priority weight (descending).
830
+ * Preserves FIFO ordering among tasks with identical priority.
831
+ */
832
+ insertIntoQueue(runner) {
833
+ const options = this.runnerOptions.get(runner);
834
+ const targetWeight = resolvePriorityWeight(options?.priority);
835
+ let insertIndex = this.queue.length;
836
+ for (let i = 0; i < this.queue.length; i++) {
837
+ const existingOptions = this.runnerOptions.get(this.queue[i]);
838
+ const existingWeight = resolvePriorityWeight(existingOptions?.priority);
839
+ if (existingWeight < targetWeight) {
840
+ insertIndex = i;
841
+ break;
842
+ }
843
+ }
844
+ this.queue.splice(insertIndex, 0, runner);
557
845
  }
558
846
  /**
559
847
  * Enqueues a task runner according to the specified schedule options.
@@ -608,6 +896,13 @@ var TaskQueue = class {
608
896
  );
609
897
  }
610
898
  }
899
+ if (options?.totalTimeoutMs !== void 0) {
900
+ if (typeof options.totalTimeoutMs !== "number" || Number.isNaN(options.totalTimeoutMs) || !Number.isFinite(options.totalTimeoutMs) || options.totalTimeoutMs <= 0) {
901
+ throw new AhkoConfigurationError(
902
+ `Invalid totalTimeoutMs "${options.totalTimeoutMs}". totalTimeoutMs must be a positive finite number greater than 0.`
903
+ );
904
+ }
905
+ }
611
906
  if (options) {
612
907
  this.runnerOptions.set(runner, options);
613
908
  }
@@ -615,6 +910,16 @@ var TaskQueue = class {
615
910
  this.cancelledTasks++;
616
911
  return runner.promise;
617
912
  }
913
+ if (options?.totalTimeoutMs !== void 0) {
914
+ const budgetMs = options.totalTimeoutMs;
915
+ const totalTimerId = setTimeout(() => {
916
+ runner.timeout(budgetMs, `Task total execution deadline exceeded after ${budgetMs}ms`);
917
+ }, budgetMs);
918
+ runner.promise.finally(() => {
919
+ clearTimeout(totalTimerId);
920
+ }).catch(() => {
921
+ });
922
+ }
618
923
  if (strategy === "delay" /* DELAY */) {
619
924
  const delayMs = options?.delay ?? 0;
620
925
  if (typeof delayMs !== "number" || Number.isNaN(delayMs) || delayMs < 0) {
@@ -638,15 +943,23 @@ var TaskQueue = class {
638
943
  const index = this.queue.indexOf(runner);
639
944
  if (index !== -1) {
640
945
  this.queue.splice(index, 1);
641
- this.cancelledTasks++;
642
- this.emitter.emit("task:cancel", {
643
- taskId: runner.taskId,
644
- reason: "Task cancelled while queued"
645
- });
946
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
947
+ this.timedOutTasks++;
948
+ this.emitter.emit("task:timeout", {
949
+ taskId: runner.taskId,
950
+ timeoutMs: options?.totalTimeoutMs ?? runner.timeoutMs
951
+ });
952
+ } else {
953
+ this.cancelledTasks++;
954
+ this.emitter.emit("task:cancel", {
955
+ taskId: runner.taskId,
956
+ reason: "Task cancelled while queued"
957
+ });
958
+ }
646
959
  this.checkIdle();
647
960
  }
648
961
  };
649
- this.queue.push(runner);
962
+ this.insertIntoQueue(runner);
650
963
  this.pump();
651
964
  return runner.promise;
652
965
  }
@@ -659,22 +972,30 @@ var TaskQueue = class {
659
972
  runner,
660
973
  timerId: setTimeout(() => {
661
974
  this.delayedEntries.delete(delayedEntry);
662
- if (runner.state === "cancelled" /* CANCELLED */) {
975
+ if (runner.state === "cancelled" /* CANCELLED */ || runner.state === "timed_out" /* TIMED_OUT */) {
663
976
  return;
664
977
  }
665
978
  runner.onCancel = () => {
666
979
  const index = this.queue.indexOf(runner);
667
980
  if (index !== -1) {
668
981
  this.queue.splice(index, 1);
669
- this.cancelledTasks++;
670
- this.emitter.emit("task:cancel", {
671
- taskId: runner.taskId,
672
- reason: "Task cancelled while queued"
673
- });
982
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
983
+ this.timedOutTasks++;
984
+ this.emitter.emit("task:timeout", {
985
+ taskId: runner.taskId,
986
+ timeoutMs: this.runnerOptions.get(runner)?.totalTimeoutMs ?? runner.timeoutMs
987
+ });
988
+ } else {
989
+ this.cancelledTasks++;
990
+ this.emitter.emit("task:cancel", {
991
+ taskId: runner.taskId,
992
+ reason: "Task cancelled while queued"
993
+ });
994
+ }
674
995
  this.checkIdle();
675
996
  }
676
997
  };
677
- this.queue.push(runner);
998
+ this.insertIntoQueue(runner);
678
999
  this.pump();
679
1000
  }, delayMs)
680
1001
  };
@@ -683,11 +1004,19 @@ var TaskQueue = class {
683
1004
  if (this.delayedEntries.has(delayedEntry)) {
684
1005
  clearTimeout(delayedEntry.timerId);
685
1006
  this.delayedEntries.delete(delayedEntry);
686
- this.cancelledTasks++;
687
- this.emitter.emit("task:cancel", {
688
- taskId: runner.taskId,
689
- reason: "Task cancelled while waiting in delay"
690
- });
1007
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1008
+ this.timedOutTasks++;
1009
+ this.emitter.emit("task:timeout", {
1010
+ taskId: runner.taskId,
1011
+ timeoutMs: this.runnerOptions.get(runner)?.totalTimeoutMs ?? runner.timeoutMs
1012
+ });
1013
+ } else {
1014
+ this.cancelledTasks++;
1015
+ this.emitter.emit("task:cancel", {
1016
+ taskId: runner.taskId,
1017
+ reason: "Task cancelled while waiting in delay"
1018
+ });
1019
+ }
691
1020
  this.checkIdle();
692
1021
  }
693
1022
  };
@@ -700,22 +1029,30 @@ var TaskQueue = class {
700
1029
  let idleEntry;
701
1030
  const handle = IdleScheduler.schedule(() => {
702
1031
  this.idleEntries.delete(idleEntry);
703
- if (runner.state === "cancelled" /* CANCELLED */) {
1032
+ if (runner.state === "cancelled" /* CANCELLED */ || runner.state === "timed_out" /* TIMED_OUT */) {
704
1033
  return;
705
1034
  }
706
1035
  runner.onCancel = () => {
707
1036
  const index = this.queue.indexOf(runner);
708
1037
  if (index !== -1) {
709
1038
  this.queue.splice(index, 1);
710
- this.cancelledTasks++;
711
- this.emitter.emit("task:cancel", {
712
- taskId: runner.taskId,
713
- reason: "Task cancelled while queued"
714
- });
1039
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1040
+ this.timedOutTasks++;
1041
+ this.emitter.emit("task:timeout", {
1042
+ taskId: runner.taskId,
1043
+ timeoutMs: this.runnerOptions.get(runner)?.totalTimeoutMs ?? runner.timeoutMs
1044
+ });
1045
+ } else {
1046
+ this.cancelledTasks++;
1047
+ this.emitter.emit("task:cancel", {
1048
+ taskId: runner.taskId,
1049
+ reason: "Task cancelled while queued"
1050
+ });
1051
+ }
715
1052
  this.checkIdle();
716
1053
  }
717
1054
  };
718
- this.queue.push(runner);
1055
+ this.insertIntoQueue(runner);
719
1056
  this.pump();
720
1057
  }, idleTimeout);
721
1058
  idleEntry = { runner, handle };
@@ -724,21 +1061,30 @@ var TaskQueue = class {
724
1061
  if (this.idleEntries.has(idleEntry)) {
725
1062
  handle.cancel();
726
1063
  this.idleEntries.delete(idleEntry);
727
- this.cancelledTasks++;
728
- this.emitter.emit("task:cancel", {
729
- taskId: runner.taskId,
730
- reason: "Task cancelled while waiting for idle"
731
- });
1064
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1065
+ this.timedOutTasks++;
1066
+ this.emitter.emit("task:timeout", {
1067
+ taskId: runner.taskId,
1068
+ timeoutMs: this.runnerOptions.get(runner)?.totalTimeoutMs ?? runner.timeoutMs
1069
+ });
1070
+ } else {
1071
+ this.cancelledTasks++;
1072
+ this.emitter.emit("task:cancel", {
1073
+ taskId: runner.taskId,
1074
+ reason: "Task cancelled while waiting for idle"
1075
+ });
1076
+ }
732
1077
  this.checkIdle();
733
1078
  }
734
1079
  };
735
1080
  }
736
1081
  /**
737
1082
  * Pumps the queue by picking pending tasks and executing them
738
- * as long as concurrency capacity is available and minIntervalMs is respected.
1083
+ * as long as concurrency capacity is available, minIntervalMs is respected,
1084
+ * and queue is not paused.
739
1085
  */
740
1086
  pump() {
741
- if (this.queue.length === 0 || this.activeRunners.size >= this.concurrency) {
1087
+ if (this._isPaused || this.queue.length === 0 || this.activeRunners.size >= this.concurrency) {
742
1088
  return;
743
1089
  }
744
1090
  if (this.minIntervalMs > 0 && this.lastTaskStartTime > 0) {
@@ -755,7 +1101,7 @@ var TaskQueue = class {
755
1101
  return;
756
1102
  }
757
1103
  }
758
- while (this.activeRunners.size < this.concurrency && this.queue.length > 0) {
1104
+ while (!this._isPaused && this.activeRunners.size < this.concurrency && this.queue.length > 0) {
759
1105
  if (this.minIntervalMs > 0 && this.lastTaskStartTime > 0) {
760
1106
  const now = Date.now();
761
1107
  const elapsed = now - this.lastTaskStartTime;
@@ -774,9 +1120,25 @@ var TaskQueue = class {
774
1120
  if (!runner) {
775
1121
  break;
776
1122
  }
777
- if (runner.state === "cancelled" /* CANCELLED */) {
1123
+ if (runner.state === "cancelled" /* CANCELLED */ || runner.state === "timed_out" /* TIMED_OUT */) {
778
1124
  continue;
779
1125
  }
1126
+ if (this.circuitBreakerCoordinator) {
1127
+ try {
1128
+ this.circuitBreakerCoordinator.checkAllowed();
1129
+ } catch (cbError) {
1130
+ this.failedTasks++;
1131
+ this.runnerOptions.delete(runner);
1132
+ this.emitter.emit("task:fail", {
1133
+ taskId: runner.taskId,
1134
+ attempt: runner.attempt,
1135
+ error: cbError,
1136
+ willRetry: false
1137
+ });
1138
+ runner.reject(cbError);
1139
+ continue;
1140
+ }
1141
+ }
780
1142
  this.activeRunners.add(runner);
781
1143
  this.lastTaskStartTime = Date.now();
782
1144
  void this.executeRunner(runner);
@@ -805,6 +1167,7 @@ var TaskQueue = class {
805
1167
  });
806
1168
  try {
807
1169
  const result = await runner.run();
1170
+ this.circuitBreakerCoordinator?.recordSuccess();
808
1171
  this.completedTasks++;
809
1172
  this.activeRunners.delete(runner);
810
1173
  this.runnerOptions.delete(runner);
@@ -840,11 +1203,14 @@ var TaskQueue = class {
840
1203
  this.scheduleRetry(runner, options);
841
1204
  return;
842
1205
  }
1206
+ if (this.circuitBreakerCoordinator && !(error instanceof AhkoCircuitBreakerOpenError)) {
1207
+ this.circuitBreakerCoordinator.recordFailure(error);
1208
+ }
843
1209
  if (runner.state === "timed_out" /* TIMED_OUT */ || error instanceof AhkoTimeoutError) {
844
1210
  this.timedOutTasks++;
845
1211
  this.emitter.emit("task:timeout", {
846
1212
  taskId: runner.taskId,
847
- timeoutMs: runner.timeoutMs
1213
+ timeoutMs: options?.totalTimeoutMs ?? runner.timeoutMs
848
1214
  });
849
1215
  } else {
850
1216
  this.failedTasks++;
@@ -874,15 +1240,23 @@ var TaskQueue = class {
874
1240
  const index = this.queue.indexOf(runner);
875
1241
  if (index !== -1) {
876
1242
  this.queue.splice(index, 1);
877
- this.cancelledTasks++;
878
- this.emitter.emit("task:cancel", {
879
- taskId: runner.taskId,
880
- reason: "Task cancelled while queued"
881
- });
1243
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1244
+ this.timedOutTasks++;
1245
+ this.emitter.emit("task:timeout", {
1246
+ taskId: runner.taskId,
1247
+ timeoutMs: options?.totalTimeoutMs ?? runner.timeoutMs
1248
+ });
1249
+ } else {
1250
+ this.cancelledTasks++;
1251
+ this.emitter.emit("task:cancel", {
1252
+ taskId: runner.taskId,
1253
+ reason: "Task cancelled while queued"
1254
+ });
1255
+ }
882
1256
  this.checkIdle();
883
1257
  }
884
1258
  };
885
- this.queue.push(runner);
1259
+ this.insertIntoQueue(runner);
886
1260
  this.pump();
887
1261
  return;
888
1262
  }
@@ -890,22 +1264,30 @@ var TaskQueue = class {
890
1264
  runner,
891
1265
  timerId: setTimeout(() => {
892
1266
  this.retryEntries.delete(retryEntry);
893
- if (runner.state === "cancelled" /* CANCELLED */) {
1267
+ if (runner.state === "cancelled" /* CANCELLED */ || runner.state === "timed_out" /* TIMED_OUT */) {
894
1268
  return;
895
1269
  }
896
1270
  runner.onCancel = () => {
897
1271
  const index = this.queue.indexOf(runner);
898
1272
  if (index !== -1) {
899
1273
  this.queue.splice(index, 1);
900
- this.cancelledTasks++;
901
- this.emitter.emit("task:cancel", {
902
- taskId: runner.taskId,
903
- reason: "Task cancelled while queued"
904
- });
1274
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1275
+ this.timedOutTasks++;
1276
+ this.emitter.emit("task:timeout", {
1277
+ taskId: runner.taskId,
1278
+ timeoutMs: options?.totalTimeoutMs ?? runner.timeoutMs
1279
+ });
1280
+ } else {
1281
+ this.cancelledTasks++;
1282
+ this.emitter.emit("task:cancel", {
1283
+ taskId: runner.taskId,
1284
+ reason: "Task cancelled while queued"
1285
+ });
1286
+ }
905
1287
  this.checkIdle();
906
1288
  }
907
1289
  };
908
- this.queue.push(runner);
1290
+ this.insertIntoQueue(runner);
909
1291
  this.pump();
910
1292
  }, backoffDelay)
911
1293
  };
@@ -914,11 +1296,19 @@ var TaskQueue = class {
914
1296
  if (this.retryEntries.has(retryEntry)) {
915
1297
  clearTimeout(retryEntry.timerId);
916
1298
  this.retryEntries.delete(retryEntry);
917
- this.cancelledTasks++;
918
- this.emitter.emit("task:cancel", {
919
- taskId: runner.taskId,
920
- reason: "Task cancelled during retry backoff"
921
- });
1299
+ if (runner.totalTimedOut || runner.state === "timed_out" /* TIMED_OUT */) {
1300
+ this.timedOutTasks++;
1301
+ this.emitter.emit("task:timeout", {
1302
+ taskId: runner.taskId,
1303
+ timeoutMs: options?.totalTimeoutMs ?? runner.timeoutMs
1304
+ });
1305
+ } else {
1306
+ this.cancelledTasks++;
1307
+ this.emitter.emit("task:cancel", {
1308
+ taskId: runner.taskId,
1309
+ reason: "Task cancelled during retry backoff"
1310
+ });
1311
+ }
922
1312
  this.checkIdle();
923
1313
  }
924
1314
  };
@@ -965,7 +1355,7 @@ var TaskQueue = class {
965
1355
  clear() {
966
1356
  while (this.queue.length > 0) {
967
1357
  const runner = this.queue.shift();
968
- if (runner && runner.state !== "cancelled" /* CANCELLED */) {
1358
+ if (runner && runner.state !== "cancelled" /* CANCELLED */ && runner.state !== "timed_out" /* TIMED_OUT */) {
969
1359
  runner.cancel("Scheduler cleared");
970
1360
  this.cancelledTasks++;
971
1361
  this.emitter.emit("task:cancel", { taskId: runner.taskId, reason: "Scheduler cleared" });
@@ -1015,7 +1405,9 @@ var TaskQueue = class {
1015
1405
  timedOutTasks: this.timedOutTasks,
1016
1406
  retriedTasks: this.retriedTasks,
1017
1407
  totalDispatched: this.totalDispatched,
1018
- capacity: this.concurrency
1408
+ capacity: this.concurrency,
1409
+ isPaused: this._isPaused,
1410
+ circuitState: this.circuitBreakerCoordinator?.state
1019
1411
  });
1020
1412
  }
1021
1413
  };
@@ -1086,6 +1478,8 @@ var TaskRunner = class {
1086
1478
  }
1087
1479
  }
1088
1480
  }
1481
+ /** Flag indicating if runner was aborted by an overall total timeout deadline */
1482
+ totalTimedOut = false;
1089
1483
  /**
1090
1484
  * Gets the current lifecycle state of the task.
1091
1485
  */
@@ -1118,7 +1512,7 @@ var TaskRunner = class {
1118
1512
  * @returns A promise resolving to true if retry should proceed, false otherwise.
1119
1513
  */
1120
1514
  async canRetry(error, retryOptions) {
1121
- if (this._state === "cancelled" /* CANCELLED */ || (this.externalSignal?.aborted ?? false)) {
1515
+ if (this.totalTimedOut || this._state === "cancelled" /* CANCELLED */ || (this.externalSignal?.aborted ?? false)) {
1122
1516
  return false;
1123
1517
  }
1124
1518
  if (!retryOptions || typeof retryOptions.attempts !== "number") {
@@ -1159,15 +1553,16 @@ var TaskRunner = class {
1159
1553
  let abortListener;
1160
1554
  const abortPromise = new Promise((_, reject) => {
1161
1555
  abortListener = () => {
1162
- if (this._state === "timed_out" /* TIMED_OUT */) {
1556
+ const reason = this.abortController.signal.reason;
1557
+ if (this._state === "timed_out" /* TIMED_OUT */ || reason instanceof AhkoTimeoutError) {
1558
+ this._state = "timed_out" /* TIMED_OUT */;
1163
1559
  reject(
1164
- new AhkoTimeoutError(
1560
+ reason instanceof AhkoTimeoutError ? reason : new AhkoTimeoutError(
1165
1561
  `Task execution timed out after ${this.timeoutMs}ms`,
1166
1562
  { timeoutMs: this.timeoutMs }
1167
1563
  )
1168
1564
  );
1169
1565
  } else {
1170
- const reason = this.abortController.signal.reason;
1171
1566
  reject(
1172
1567
  new AhkoCancellationError("Task was cancelled during execution", {
1173
1568
  cause: reason instanceof Error ? reason : void 0
@@ -1202,6 +1597,10 @@ var TaskRunner = class {
1202
1597
  }
1203
1598
  taskExecutionPromise.catch(() => {
1204
1599
  });
1600
+ abortPromise.catch(() => {
1601
+ });
1602
+ timeoutPromise?.catch(() => {
1603
+ });
1205
1604
  const racePromises = [
1206
1605
  taskExecutionPromise,
1207
1606
  abortPromise
@@ -1293,6 +1692,31 @@ var TaskRunner = class {
1293
1692
  this.onCancel?.(this);
1294
1693
  }
1295
1694
  }
1695
+ /**
1696
+ * Times out the task, aborting pending or running execution with AhkoTimeoutError.
1697
+ *
1698
+ * @param timeoutMs - Timeout duration in milliseconds.
1699
+ * @param message - Optional custom timeout message.
1700
+ */
1701
+ timeout(timeoutMs, message) {
1702
+ if (this._state === "completed" /* COMPLETED */ || this._state === "failed" /* FAILED */ || this._state === "cancelled" /* CANCELLED */ || this._state === "timed_out" /* TIMED_OUT */) {
1703
+ return;
1704
+ }
1705
+ const wasPending = this._state === "pending" /* PENDING */;
1706
+ this._state = "timed_out" /* TIMED_OUT */;
1707
+ this.totalTimedOut = true;
1708
+ this.clearTimeoutTimer();
1709
+ const timeoutError = new AhkoTimeoutError(
1710
+ message ?? `Task execution timed out after ${timeoutMs}ms`,
1711
+ { timeoutMs }
1712
+ );
1713
+ this.abortController.abort(timeoutError);
1714
+ this.cleanup();
1715
+ if (wasPending) {
1716
+ this.rejectPromise(timeoutError);
1717
+ this.onCancel?.(this);
1718
+ }
1719
+ }
1296
1720
  /**
1297
1721
  * Handles external AbortSignal trigger.
1298
1722
  */
@@ -1311,9 +1735,55 @@ var TaskRunner = class {
1311
1735
  };
1312
1736
 
1313
1737
  // src/ahko.ts
1314
- var Ahko = class {
1738
+ var Ahko = class _Ahko {
1315
1739
  /** Internal queue and concurrency manager */
1316
1740
  queue;
1741
+ /** Default schedule options inherited from profile if configured */
1742
+ defaultScheduleOptions;
1743
+ /**
1744
+ * Programmatically loads a declarative configuration into memory.
1745
+ * Works universally across Node.js, browsers, and edge runtimes.
1746
+ *
1747
+ * @param config - File configuration object containing default and named profiles.
1748
+ */
1749
+ static loadConfig(config) {
1750
+ loadConfig(config);
1751
+ }
1752
+ /**
1753
+ * Asynchronously loads a configuration file from disk (Node.js).
1754
+ *
1755
+ * @param filePath - Path to configuration file (default: "config.ahko.json").
1756
+ */
1757
+ static async loadConfigFile(filePath) {
1758
+ return loadConfigFile(filePath);
1759
+ }
1760
+ /**
1761
+ * Resets the active declarative configuration.
1762
+ */
1763
+ static resetConfig() {
1764
+ resetConfig();
1765
+ }
1766
+ /**
1767
+ * Retrieves the currently active declarative configuration.
1768
+ */
1769
+ static getActiveConfig() {
1770
+ return getActiveConfig();
1771
+ }
1772
+ /**
1773
+ * Instantiates an Ahko scheduler initialized with settings from a declarative profile.
1774
+ *
1775
+ * @param profileName - Optional name of the profile (e.g. "api", "background").
1776
+ * @param overrides - Optional scheduler options overriding profile values.
1777
+ * @returns A new configured Ahko instance.
1778
+ */
1779
+ static fromProfile(profileName, overrides) {
1780
+ const profile = getProfileConfig(profileName);
1781
+ return new _Ahko({
1782
+ ...profile,
1783
+ ...overrides,
1784
+ circuitBreaker: overrides?.circuitBreaker ?? profile?.circuitBreaker
1785
+ });
1786
+ }
1317
1787
  /**
1318
1788
  * Initializes a new Ahko scheduler instance.
1319
1789
  *
@@ -1326,79 +1796,152 @@ var Ahko = class {
1326
1796
  * ```
1327
1797
  */
1328
1798
  constructor(options) {
1329
- this.queue = new TaskQueue(options?.concurrency, options?.minIntervalMs);
1799
+ const profile = options?.profile ? getProfileConfig(options.profile) : getProfileConfig();
1800
+ const mergedOptions = {
1801
+ ...profile,
1802
+ ...options,
1803
+ circuitBreaker: options?.circuitBreaker ?? profile?.circuitBreaker
1804
+ };
1805
+ if (profile) {
1806
+ this.defaultScheduleOptions = {
1807
+ priority: profile.priority,
1808
+ retry: profile.retry,
1809
+ timeoutMs: profile.timeoutMs,
1810
+ totalTimeoutMs: profile.totalTimeoutMs
1811
+ };
1812
+ }
1813
+ this.queue = new TaskQueue(
1814
+ mergedOptions.concurrency,
1815
+ mergedOptions.minIntervalMs,
1816
+ mergedOptions.circuitBreaker
1817
+ );
1818
+ }
1819
+ /**
1820
+ * Pauses scheduler dispatch. In-flight tasks run to completion, but pending tasks remain queued.
1821
+ */
1822
+ pause() {
1823
+ this.queue.pause();
1824
+ }
1825
+ /**
1826
+ * Resumes scheduler dispatch, immediately executing waiting tasks up to available concurrency.
1827
+ */
1828
+ resume() {
1829
+ this.queue.resume();
1830
+ }
1831
+ /**
1832
+ * Checks whether the scheduler is currently paused.
1833
+ */
1834
+ isPaused() {
1835
+ return this.queue.isPaused();
1836
+ }
1837
+ /**
1838
+ * Current circuit breaker state if circuit breaker protection is configured.
1839
+ */
1840
+ get circuitState() {
1841
+ return this.queue.circuitBreakerCoordinator?.state;
1842
+ }
1843
+ /**
1844
+ * Access to the underlying circuit breaker coordinator instance if configured.
1845
+ */
1846
+ get circuitBreaker() {
1847
+ return this.queue.circuitBreakerCoordinator;
1848
+ }
1849
+ /**
1850
+ * Wraps an async function so every execution is automatically routed through this Ahko scheduler.
1851
+ *
1852
+ * @template TArgs - Parameter types of the wrapped function.
1853
+ * @template TReturn - Return type of the wrapped function.
1854
+ * @param fn - The function to wrap.
1855
+ * @param options - Optional scheduling options applied to every wrapped call.
1856
+ * @returns A wrapped function returning a Promise.
1857
+ *
1858
+ * @example
1859
+ * ```typescript
1860
+ * const fetchUser = ahko.wrap(async (id: string) => api.getUser(id), { priority: "high" });
1861
+ * const user = await fetchUser("usr_123");
1862
+ * ```
1863
+ */
1864
+ wrap(fn, options) {
1865
+ if (typeof fn !== "function") {
1866
+ throw new AhkoConfigurationError("Target to wrap must be a valid function.");
1867
+ }
1868
+ return (...args) => {
1869
+ return this.schedule(() => fn(...args), options);
1870
+ };
1330
1871
  }
1331
1872
  /**
1332
1873
  * Schedules a task for execution with full return type inference.
1333
1874
  *
1334
1875
  * @template T - Inferred return type of the task.
1335
1876
  * @param task - Asynchronous or synchronous task function accepting an {@link ITaskContext}.
1336
- * @param options - Task-specific scheduling options such as strategy, delay, and cancellation signal.
1877
+ * @param options - Task-specific scheduling options such as strategy, priority, delay, and cancellation signal.
1337
1878
  * @returns A promise that resolves with the task's return value.
1338
1879
  *
1339
1880
  * @throws {AhkoConfigurationError} If the task is not a function or options are invalid.
1340
1881
  * @throws {AhkoCancellationError} If the task is cancelled prior to or during execution.
1341
- * @throws {AhkoTimeoutError} If task execution exceeds timeoutMs.
1882
+ * @throws {AhkoTimeoutError} If task execution exceeds timeoutMs or totalTimeoutMs.
1883
+ * @throws {AhkoCircuitBreakerOpenError} If the circuit breaker is OPEN and rejects the execution.
1342
1884
  *
1343
1885
  * @example
1344
1886
  * ```typescript
1345
1887
  * // Immediate execution (subject to concurrency)
1346
1888
  * const count = await ahko.schedule(async () => 42);
1347
1889
  *
1348
- * // Delayed execution
1349
- * await ahko.schedule(
1350
- * async ({ signal }) => doWork({ signal }),
1351
- * { strategy: "delay", delay: 1000 }
1352
- * );
1890
+ * // High priority task
1891
+ * await ahko.schedule(doUrgentWork, { priority: "high" });
1353
1892
  * ```
1354
1893
  */
1355
1894
  schedule(task, options) {
1356
1895
  if (typeof task !== "function") {
1357
1896
  throw new AhkoConfigurationError("Task must be a valid function.");
1358
1897
  }
1359
- const strategy = options?.strategy ?? "immediate" /* IMMEDIATE */;
1898
+ const mergedOptions = {
1899
+ ...this.defaultScheduleOptions,
1900
+ ...options
1901
+ };
1902
+ const strategy = mergedOptions.strategy ?? "immediate" /* IMMEDIATE */;
1360
1903
  if (strategy === "debounce" /* DEBOUNCE */) {
1361
- if (!options?.key || typeof options.key !== "string" && typeof options.key !== "symbol") {
1904
+ if (!mergedOptions.key || typeof mergedOptions.key !== "string" && typeof mergedOptions.key !== "symbol") {
1362
1905
  throw new AhkoConfigurationError(
1363
1906
  `Strategy "debounce" requires a valid "key" of type string or symbol.`
1364
1907
  );
1365
1908
  }
1366
- const waitMs = options.waitMs ?? options.delay;
1909
+ const waitMs = mergedOptions.waitMs ?? mergedOptions.delay;
1367
1910
  if (typeof waitMs !== "number" || Number.isNaN(waitMs) || !Number.isFinite(waitMs) || waitMs < 0) {
1368
1911
  throw new AhkoConfigurationError(
1369
1912
  `Strategy "debounce" requires a non-negative finite "waitMs" or "delay" in milliseconds.`
1370
1913
  );
1371
1914
  }
1372
1915
  return this.queue.debounceCoordinator.schedule(
1373
- options.key,
1916
+ mergedOptions.key,
1374
1917
  task,
1375
1918
  waitMs,
1376
- options,
1919
+ mergedOptions,
1377
1920
  (t, opts) => this.schedule(t, { ...opts, strategy: "immediate" /* IMMEDIATE */ })
1378
1921
  );
1379
1922
  }
1380
1923
  if (strategy === "throttle" /* THROTTLE */) {
1381
- if (!options?.key || typeof options.key !== "string" && typeof options.key !== "symbol") {
1924
+ if (!mergedOptions.key || typeof mergedOptions.key !== "string" && typeof mergedOptions.key !== "symbol") {
1382
1925
  throw new AhkoConfigurationError(
1383
1926
  `Strategy "throttle" requires a valid "key" of type string or symbol.`
1384
1927
  );
1385
1928
  }
1386
- const waitMs = options.waitMs ?? options.delay;
1929
+ const waitMs = mergedOptions.waitMs ?? mergedOptions.delay;
1387
1930
  if (typeof waitMs !== "number" || Number.isNaN(waitMs) || !Number.isFinite(waitMs) || waitMs < 0) {
1388
1931
  throw new AhkoConfigurationError(
1389
1932
  `Strategy "throttle" requires a non-negative finite "waitMs" or "delay" in milliseconds.`
1390
1933
  );
1391
1934
  }
1392
1935
  return this.queue.throttleCoordinator.schedule(
1393
- options.key,
1936
+ mergedOptions.key,
1394
1937
  task,
1395
1938
  waitMs,
1396
- options,
1939
+ mergedOptions,
1397
1940
  (t, opts) => this.schedule(t, { ...opts, strategy: "immediate" /* IMMEDIATE */ })
1398
1941
  );
1399
1942
  }
1400
- const runner = new TaskRunner(task, options?.signal, options?.timeoutMs);
1401
- return this.queue.enqueue(runner, options);
1943
+ const runner = new TaskRunner(task, mergedOptions.signal, mergedOptions.timeoutMs);
1944
+ return this.queue.enqueue(runner, mergedOptions);
1402
1945
  }
1403
1946
  /**
1404
1947
  * Convenience method to schedule a task during platform idle opportunities.
@@ -1461,12 +2004,12 @@ var Ahko = class {
1461
2004
  /**
1462
2005
  * Retrieves real-time telemetry metrics from the scheduler.
1463
2006
  *
1464
- * @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled, and timed out tasks.
2007
+ * @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled, timed out tasks, pause status, and circuit state.
1465
2008
  *
1466
2009
  * @example
1467
2010
  * ```typescript
1468
2011
  * const stats = ahko.stats();
1469
- * console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}`);
2012
+ * console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}, Paused: ${stats.isPaused}`);
1470
2013
  * ```
1471
2014
  */
1472
2015
  stats() {
@@ -1552,7 +2095,7 @@ var Ahko = class {
1552
2095
  };
1553
2096
 
1554
2097
  // src/version.ts
1555
- var VERSION = "0.6.0";
2098
+ var VERSION = "1.1.0";
1556
2099
 
1557
2100
  // src/errors/queue.error.ts
1558
2101
  var AhkoQueueError = class extends AhkoError {
@@ -1626,16 +2169,26 @@ function combineSignals(signals) {
1626
2169
  export {
1627
2170
  Ahko,
1628
2171
  AhkoCancellationError,
2172
+ AhkoCircuitBreakerOpenError,
1629
2173
  AhkoConfigurationError,
1630
2174
  AhkoError,
1631
2175
  AhkoQueueError,
1632
2176
  AhkoTimeoutError,
2177
+ CircuitBreakerCoordinator,
1633
2178
  DEFAULT_BASE_DELAY,
1634
2179
  DEFAULT_MAX_DELAY,
2180
+ ECircuitState,
1635
2181
  EScheduleStrategy,
1636
2182
  ETaskState,
2183
+ TASK_PRIORITY_WEIGHTS,
1637
2184
  VERSION,
1638
2185
  calculateBackoff,
1639
- combineSignals
2186
+ combineSignals,
2187
+ getActiveConfig,
2188
+ getProfileConfig,
2189
+ loadConfig,
2190
+ loadConfigFile,
2191
+ resetConfig,
2192
+ resolvePriorityWeight
1640
2193
  };
1641
2194
  //# sourceMappingURL=index.js.map