@mrjacket/ahko 1.0.0 → 1.1.5

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.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Configuration options for Additive Increase / Multiplicative Decrease (AIMD) Adaptive Concurrency.
3
+ */
4
+ export interface IAdaptiveConcurrencyOptions {
5
+ /**
6
+ * Target average task execution latency in milliseconds.
7
+ * When measured latency exceeds this threshold, concurrency decreases multiplicatively.
8
+ * When latency remains comfortably below, concurrency increases additively.
9
+ * Must be a positive finite number greater than 0.
10
+ */
11
+ targetLatencyMs: number;
12
+ /**
13
+ * Minimum concurrency floor allowed during backoff / chill mode.
14
+ * Must be an integer greater than or equal to 1.
15
+ * @default 1
16
+ */
17
+ minConcurrency?: number;
18
+ /**
19
+ * Maximum concurrency ceiling allowed during scaling.
20
+ * Must be greater than or equal to `minConcurrency`.
21
+ * Defaults to twice the initial scheduler concurrency, or 10 if initial is Infinity.
22
+ */
23
+ maxConcurrency?: number;
24
+ /**
25
+ * Number of task execution samples to accumulate before calculating moving average and adjusting capacity.
26
+ * Must be an integer greater than or equal to 1.
27
+ * @default 5
28
+ */
29
+ sampleWindowSize?: number;
30
+ /**
31
+ * Multiplicative factor applied to reduce concurrency when latency threshold is violated.
32
+ * Must be a number between 0.1 and 0.95.
33
+ * @default 0.7
34
+ */
35
+ backoffFactor?: number;
36
+ }
37
+ /**
38
+ * Real-time telemetry snapshot of the adaptive concurrency controller.
39
+ */
40
+ export interface IAdaptiveStats {
41
+ /** Current effective concurrency limit */
42
+ currentConcurrency: number;
43
+ /** Moving average execution latency in milliseconds across the latest sample window */
44
+ averageLatencyMs: number;
45
+ /** Total duration samples recorded in the current evaluation window */
46
+ samplesRecorded: number;
47
+ }
@@ -0,0 +1,22 @@
1
+ import type { IScheduleOptions } from "./options.model.js";
2
+ /**
3
+ * Common configuration options for batch processing operations (e.g. `ahko.map`, `ahko.each`).
4
+ */
5
+ export interface IBatchOptions extends Omit<IScheduleOptions, "strategy"> {
6
+ /**
7
+ * Optional localized concurrency cap specifically for this batch operation.
8
+ * If omitted, uses the scheduler's global concurrency limit.
9
+ */
10
+ concurrency?: number;
11
+ /**
12
+ * Whether to abort remaining items in the batch as soon as an item fails.
13
+ * If `true`, unstarted items are cancelled and the batch promise rejects immediately with the error.
14
+ * If `false`, all items run and errors are thrown/propagated once all items settle.
15
+ * @default false
16
+ */
17
+ stopOnError?: boolean;
18
+ }
19
+ /**
20
+ * Options specifically for `ahko.map()` collection operations.
21
+ */
22
+ export type IBatchMapOptions<_TItem = unknown, _TResult = unknown> = IBatchOptions;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Discrete lifecycle states of the circuit breaker.
3
+ */
4
+ export declare enum ECircuitState {
5
+ /** Normal operation: calls pass through to execution */
6
+ CLOSED = "closed",
7
+ /** Failure threshold exceeded: calls fast-fail immediately */
8
+ OPEN = "open",
9
+ /** Cool-down timer elapsed: trial call allowed to test recovery */
10
+ HALF_OPEN = "half_open"
11
+ }
12
+ /** Union type representing circuit breaker states */
13
+ export type TCircuitState = `${ECircuitState}`;
14
+ /**
15
+ * Configuration options for the circuit breaker policy.
16
+ */
17
+ export interface ICircuitBreakerOptions {
18
+ /**
19
+ * Number of consecutive task failures required to trip the circuit to OPEN state.
20
+ * Must be an integer greater than or equal to 1.
21
+ */
22
+ failureThreshold: number;
23
+ /**
24
+ * Time in milliseconds the circuit remains OPEN before transitioning to HALF_OPEN
25
+ * to attempt a recovery trial call. Must be a positive finite number.
26
+ */
27
+ resetTimeoutMs: number;
28
+ }
29
+ /**
30
+ * Telemetry snapshot of circuit breaker status.
31
+ */
32
+ export interface ICircuitBreakerStats {
33
+ /** Current state of the breaker */
34
+ state: ECircuitState;
35
+ /** Number of consecutive errors recorded */
36
+ consecutiveFailures: number;
37
+ /** Timestamp in ms when the breaker tripped to OPEN, if open */
38
+ lastFailureTime?: number;
39
+ }
@@ -0,0 +1,23 @@
1
+ import type { ICircuitBreakerOptions } from "./circuit-breaker.model.js";
2
+ import type { IAhkoOptions, IScheduleOptions } from "./options.model.js";
3
+ import type { TTaskPriority } from "./priority.model.js";
4
+ /**
5
+ * Pre-configured profile containing scheduler defaults and task scheduling policies.
6
+ */
7
+ export interface IAhkoProfileConfig extends IAhkoOptions, Partial<IScheduleOptions> {
8
+ /** Optional default task priority for tasks scheduled under this profile */
9
+ priority?: TTaskPriority;
10
+ /** Optional circuit breaker policy for the scheduler */
11
+ circuitBreaker?: ICircuitBreakerOptions;
12
+ }
13
+ /**
14
+ * Structure of `config.ahko.json` declarative configuration file.
15
+ */
16
+ export interface IAhkoFileConfig {
17
+ /** Optional JSON schema URL */
18
+ $schema?: string;
19
+ /** Default profile applied when no named profile is requested */
20
+ default?: IAhkoProfileConfig;
21
+ /** Named profiles for distinct workloads (e.g. "api", "background", "critical") */
22
+ profiles?: Record<string, IAhkoProfileConfig>;
23
+ }
@@ -35,6 +35,12 @@ export interface IAhkoEventMap {
35
35
  idle: {
36
36
  timestamp: number;
37
37
  };
38
+ /** Emitted when scheduler concurrency is dynamically or adaptively updated */
39
+ "concurrency:change": {
40
+ previousConcurrency: number;
41
+ currentConcurrency: number;
42
+ reason: string;
43
+ };
38
44
  }
39
45
  /**
40
46
  * Union of all valid event names emitted by the Ahko scheduler.
@@ -6,3 +6,8 @@ export * from "./stats.model.js";
6
6
  export * from "./strategy.model.js";
7
7
  export * from "./task.model.js";
8
8
  export * from "./events.model.js";
9
+ export * from "./circuit-breaker.model.js";
10
+ export * from "./priority.model.js";
11
+ export * from "./config.model.js";
12
+ export * from "./batch.model.js";
13
+ export * from "./adaptive.model.js";
@@ -1,3 +1,6 @@
1
+ import type { IAdaptiveConcurrencyOptions } from "./adaptive.model.js";
2
+ import type { ICircuitBreakerOptions } from "./circuit-breaker.model.js";
3
+ import type { TTaskPriority } from "./priority.model.js";
1
4
  import type { IRetryOptions } from "./retry.model.js";
2
5
  import type { TScheduleStrategy } from "./strategy.model.js";
3
6
  /**
@@ -31,6 +34,19 @@ export interface IScheduleOptions {
31
34
  * Must be a positive finite number greater than 0 if provided.
32
35
  */
33
36
  timeoutMs?: number;
37
+ /**
38
+ * Overall budget in milliseconds allowed for the entire task lifecycle,
39
+ * including queue wait times, execution durations, and retry backoffs.
40
+ * If exceeded, the task rejects with an AhkoTimeoutError.
41
+ */
42
+ totalTimeoutMs?: number;
43
+ /**
44
+ * Priority assigned to the task ("high", "normal", "low", or explicit numeric value).
45
+ * High priority tasks jump ahead of lower priority tasks in the pending queue.
46
+ * FIFO ordering is strictly preserved among tasks of equal priority.
47
+ * @default "normal" (0)
48
+ */
49
+ priority?: TTaskPriority;
34
50
  /**
35
51
  * Explicit identity key for "debounce" and "throttle" strategies.
36
52
  * Tasks sharing the same key coalesce into shared executions.
@@ -47,6 +63,10 @@ export interface IScheduleOptions {
47
63
  * If aborted while running, the abort event is propagated to the task context signal.
48
64
  */
49
65
  signal?: AbortSignal;
66
+ /**
67
+ * Optional tags for classifying tasks and enabling selective cancellation (e.g. `ahko.cancelByTag()`).
68
+ */
69
+ tags?: string[];
50
70
  }
51
71
  /**
52
72
  * Global configuration options for the Ahko scheduler instance.
@@ -65,4 +85,16 @@ export interface IAhkoOptions {
65
85
  * @default 0
66
86
  */
67
87
  minIntervalMs?: number;
88
+ /**
89
+ * Optional circuit breaker policy to guard against cascading failures.
90
+ */
91
+ circuitBreaker?: ICircuitBreakerOptions;
92
+ /**
93
+ * Optional adaptive concurrency policy (AIMD Auto-Chill mode) based on real-time task latency.
94
+ */
95
+ adaptive?: IAdaptiveConcurrencyOptions;
96
+ /**
97
+ * Optional named profile from `config.ahko.json` to inherit configuration defaults from.
98
+ */
99
+ profile?: string;
68
100
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Named priority level or explicit numeric priority for scheduled tasks.
3
+ * Higher numeric values indicate higher execution priority.
4
+ */
5
+ export type TTaskPriority = "high" | "normal" | "low" | number;
6
+ /** Default priority weight mappings */
7
+ export declare const TASK_PRIORITY_WEIGHTS: {
8
+ readonly high: 10;
9
+ readonly normal: 0;
10
+ readonly low: -10;
11
+ };
12
+ /**
13
+ * Resolves a task priority into a normalized numeric weight.
14
+ *
15
+ * @param priority - Named or numeric priority.
16
+ * @returns Numeric weight (default 0 for normal).
17
+ */
18
+ export declare function resolvePriorityWeight(priority?: TTaskPriority): number;
@@ -1,3 +1,5 @@
1
+ import type { IAdaptiveStats } from "./adaptive.model.js";
2
+ import type { ECircuitState } from "./circuit-breaker.model.js";
1
3
  /**
2
4
  * Telemetry snapshot of the Ahko scheduler.
3
5
  */
@@ -20,4 +22,10 @@ export interface IAhkoStats {
20
22
  totalDispatched: number;
21
23
  /** Maximum concurrent execution capacity */
22
24
  capacity: number;
25
+ /** Whether task dispatching is currently paused */
26
+ isPaused: boolean;
27
+ /** Current state of the scheduler circuit breaker, if configured */
28
+ circuitState?: ECircuitState;
29
+ /** Telemetry from the adaptive concurrency controller, if configured */
30
+ adaptive?: IAdaptiveStats;
23
31
  }
@@ -0,0 +1,45 @@
1
+ import type { IAdaptiveConcurrencyOptions, IAdaptiveStats } from "../models/adaptive.model.js";
2
+ /**
3
+ * Controller implementing Additive Increase / Multiplicative Decrease (AIMD)
4
+ * dynamic concurrency adjustment based on real-time task latency metrics.
5
+ */
6
+ export declare class AdaptiveCoordinator {
7
+ private _currentConcurrency;
8
+ readonly minConcurrency: number;
9
+ readonly maxConcurrency: number;
10
+ readonly targetLatencyMs: number;
11
+ readonly sampleWindowSize: number;
12
+ readonly backoffFactor: number;
13
+ private recentDurations;
14
+ private lastAverageLatencyMs;
15
+ private readonly onConcurrencyChange;
16
+ /**
17
+ * Initializes a new AdaptiveCoordinator instance.
18
+ *
19
+ * @param options - Adaptive concurrency configuration options.
20
+ * @param initialConcurrency - Starting scheduler concurrency limit.
21
+ * @param onConcurrencyChange - Callback invoked when concurrency changes.
22
+ * @throws {AhkoConfigurationError} If options are invalid.
23
+ */
24
+ constructor(options: IAdaptiveConcurrencyOptions, initialConcurrency: number, onConcurrencyChange: (previous: number, current: number, reason: string) => void);
25
+ /**
26
+ * Current effective concurrency limit dictated by the adaptive controller.
27
+ */
28
+ get currentConcurrency(): number;
29
+ /**
30
+ * Manually overrides the current concurrency within [minConcurrency, maxConcurrency].
31
+ *
32
+ * @param concurrency - New concurrency limit to set.
33
+ */
34
+ setConcurrency(concurrency: number): void;
35
+ /**
36
+ * Records a task execution duration sample and triggers AIMD adjustment if window is filled.
37
+ *
38
+ * @param durationMs - Execution duration in milliseconds of the completed task.
39
+ */
40
+ recordDuration(durationMs: number): void;
41
+ /**
42
+ * Returns a snapshot of adaptive telemetry metrics.
43
+ */
44
+ getStats(): IAdaptiveStats;
45
+ }
@@ -0,0 +1,56 @@
1
+ import { ECircuitState, type ICircuitBreakerOptions, type ICircuitBreakerStats } from "../models/circuit-breaker.model.js";
2
+ /**
3
+ * Manages circuit breaker failure tracking, state transitions, and fast-fail enforcement.
4
+ *
5
+ * Implements standard Martin Fowler Circuit Breaker state machine:
6
+ * - CLOSED: All operations execute normally.
7
+ * - OPEN: All operations fast-fail immediately with AhkoCircuitBreakerOpenError.
8
+ * - HALF_OPEN: Probe execution allowed to verify recovery.
9
+ */
10
+ export declare class CircuitBreakerCoordinator {
11
+ private _state;
12
+ private _consecutiveFailures;
13
+ private _lastFailureTime;
14
+ readonly failureThreshold: number;
15
+ readonly resetTimeoutMs: number;
16
+ /**
17
+ * Initializes a new CircuitBreakerCoordinator.
18
+ *
19
+ * @param options - Configuration options for threshold and cool-down window.
20
+ * @throws {AhkoConfigurationError} If options are invalid.
21
+ */
22
+ constructor(options: ICircuitBreakerOptions);
23
+ /** Current state of the circuit breaker */
24
+ get state(): ECircuitState;
25
+ /**
26
+ * Checks whether an execution is currently allowed.
27
+ * If the circuit is OPEN and cool-down has not elapsed, fast-fails immediately.
28
+ *
29
+ * @throws {AhkoCircuitBreakerOpenError} If the circuit is currently OPEN.
30
+ */
31
+ checkAllowed(): void;
32
+ /**
33
+ * Records a successful task execution.
34
+ * Heals HALF_OPEN state back to CLOSED and resets consecutive failure counters.
35
+ */
36
+ recordSuccess(): void;
37
+ /**
38
+ * Records a failed task execution.
39
+ * Trips CLOSED to OPEN when threshold is met, or re-trips HALF_OPEN immediately.
40
+ *
41
+ * @param _error - Optional error that caused the failure.
42
+ */
43
+ recordFailure(_error?: unknown): void;
44
+ /**
45
+ * Evaluates if enough time has passed to transition from OPEN to HALF_OPEN.
46
+ */
47
+ private refreshState;
48
+ /**
49
+ * Resets the circuit breaker back to initial CLOSED state.
50
+ */
51
+ reset(): void;
52
+ /**
53
+ * Returns a snapshot of circuit breaker telemetry.
54
+ */
55
+ getStats(): ICircuitBreakerStats;
56
+ }
@@ -1,16 +1,21 @@
1
+ import type { IAdaptiveConcurrencyOptions } from "../models/adaptive.model.js";
2
+ import type { ICircuitBreakerOptions } from "../models/circuit-breaker.model.js";
1
3
  import type { IScheduleOptions } from "../models/options.model.js";
2
4
  import type { IAhkoStats } from "../models/stats.model.js";
5
+ import { AdaptiveCoordinator } from "./adaptive-coordinator.js";
6
+ import { CircuitBreakerCoordinator } from "./circuit-breaker.js";
3
7
  import { DebounceCoordinator } from "./debounce-coordinator.js";
4
8
  import { TaskRunner } from "./task-runner.js";
5
9
  import { ThrottleCoordinator } from "./throttle-coordinator.js";
6
10
  import { AhkoEventEmitter } from "../events/event-emitter.js";
7
11
  /**
8
- * Memory-safe FIFO task queue managing concurrency allocation,
9
- * delayed scheduling, and task lifecycle counters.
12
+ * Memory-safe priority-aware task queue managing concurrency allocation,
13
+ * rate limiting, circuit breaker protection, dynamic & adaptive concurrency,
14
+ * tags, flow control (pause/resume), and task lifecycle counters.
10
15
  */
11
16
  export declare class TaskQueue {
12
17
  /** Maximum concurrent active tasks */
13
- readonly concurrency: number;
18
+ private _concurrency;
14
19
  /** Minimum interval in milliseconds between consecutive task starts */
15
20
  readonly minIntervalMs: number;
16
21
  /** Timestamp of the most recent task start */
@@ -27,12 +32,20 @@ export declare class TaskQueue {
27
32
  private readonly idleEntries;
28
33
  /** Set of tasks currently awaiting a retry backoff timer */
29
34
  private readonly retryEntries;
35
+ /** Tag index for selective cancellation and task classification */
36
+ private readonly tagIndex;
30
37
  /** Coordinator for debounced tasks with key coalescing */
31
38
  readonly debounceCoordinator: DebounceCoordinator;
32
39
  /** Coordinator for throttled tasks with leading/trailing coalescing */
33
40
  readonly throttleCoordinator: ThrottleCoordinator;
34
41
  /** Lifecycle event emitter for task and scheduler events */
35
42
  readonly emitter: AhkoEventEmitter;
43
+ /** Circuit breaker coordinator if configured */
44
+ readonly circuitBreakerCoordinator?: CircuitBreakerCoordinator;
45
+ /** Adaptive concurrency coordinator if configured */
46
+ readonly adaptiveCoordinator?: AdaptiveCoordinator;
47
+ /** Pause state flag */
48
+ private _isPaused;
36
49
  /** Set of pending resolvers awaiting scheduler idle transition */
37
50
  private readonly idleResolvers;
38
51
  /** WeakMap associating task runners with their scheduling options */
@@ -54,9 +67,64 @@ export declare class TaskQueue {
54
67
  *
55
68
  * @param concurrency - Maximum concurrent tasks (defaults to Infinity).
56
69
  * @param minIntervalMs - Minimum interval in milliseconds between task dispatches.
70
+ * @param circuitBreakerOptions - Optional circuit breaker policy configuration.
71
+ * @param adaptiveOptions - Optional adaptive concurrency policy configuration.
57
72
  * @throws {AhkoConfigurationError} If concurrency is less than 1 or minIntervalMs is invalid.
58
73
  */
59
- constructor(concurrency?: number, minIntervalMs?: number);
74
+ constructor(concurrency?: number, minIntervalMs?: number, circuitBreakerOptions?: ICircuitBreakerOptions, adaptiveOptions?: IAdaptiveConcurrencyOptions);
75
+ /**
76
+ * Current concurrency capacity limit.
77
+ */
78
+ get concurrency(): number;
79
+ /**
80
+ * Dynamically adjusts the concurrency limit at runtime.
81
+ *
82
+ * @param newConcurrency - New maximum concurrency (must be >= 1).
83
+ * @throws {AhkoConfigurationError} If newConcurrency is less than 1.
84
+ */
85
+ setConcurrency(newConcurrency: number): void;
86
+ /**
87
+ * Pauses queue execution. Running tasks will complete normally, but no new pending tasks will be dispatched.
88
+ */
89
+ pause(): void;
90
+ /**
91
+ * Resumes queue execution, immediately dispatching waiting tasks up to available concurrency.
92
+ */
93
+ resume(): void;
94
+ /**
95
+ * Checks whether the task queue is currently paused.
96
+ */
97
+ isPaused(): boolean;
98
+ /**
99
+ * Cancels all pending, delayed, and active tasks marked with the specified tag.
100
+ *
101
+ * @param tag - Tag identifier to match.
102
+ * @param reason - Optional cancellation reason.
103
+ * @returns Total count of tasks cancelled.
104
+ */
105
+ cancelByTag(tag: string, reason?: unknown): number;
106
+ /**
107
+ * Returns active and pending task counts for a given tag.
108
+ *
109
+ * @param tag - Tag identifier.
110
+ */
111
+ getStatsByTag(tag: string): {
112
+ activeTasks: number;
113
+ pendingTasks: number;
114
+ };
115
+ /**
116
+ * Indexes a runner under all its associated tags.
117
+ */
118
+ private indexTaskTags;
119
+ /**
120
+ * Removes a runner from the tag index upon settlement.
121
+ */
122
+ private cleanupTaskTags;
123
+ /**
124
+ * Inserts a task runner into the queue based on priority weight (descending).
125
+ * Preserves FIFO ordering among tasks with identical priority.
126
+ */
127
+ private insertIntoQueue;
60
128
  /**
61
129
  * Enqueues a task runner according to the specified schedule options.
62
130
  *
@@ -79,7 +147,8 @@ export declare class TaskQueue {
79
147
  private scheduleIdle;
80
148
  /**
81
149
  * Pumps the queue by picking pending tasks and executing them
82
- * as long as concurrency capacity is available and minIntervalMs is respected.
150
+ * as long as concurrency capacity is available, minIntervalMs is respected,
151
+ * and queue is not paused.
83
152
  */
84
153
  private pump;
85
154
  /**
@@ -36,14 +36,19 @@ export declare class TaskRunner<T> {
36
36
  attempt: number;
37
37
  /** Duration of the most recent execution attempt in milliseconds */
38
38
  lastDurationMs: number;
39
+ /** Set of classification tags associated with this task */
40
+ readonly tags: ReadonlySet<string>;
39
41
  /**
40
42
  * Creates a new TaskRunner instance.
41
43
  *
42
44
  * @param task - The asynchronous work unit to run.
43
45
  * @param externalSignal - Optional external AbortSignal to propagate.
44
46
  * @param timeoutMs - Optional maximum execution time in milliseconds.
47
+ * @param tags - Optional array of tags for classifying and selectively cancelling tasks.
45
48
  */
46
- constructor(task: ITask<T>, externalSignal?: AbortSignal, timeoutMs?: number);
49
+ constructor(task: ITask<T>, externalSignal?: AbortSignal, timeoutMs?: number, tags?: string[]);
50
+ /** Flag indicating if runner was aborted by an overall total timeout deadline */
51
+ totalTimedOut: boolean;
47
52
  /**
48
53
  * Gets the current lifecycle state of the task.
49
54
  */
@@ -84,6 +89,13 @@ export declare class TaskRunner<T> {
84
89
  * @param reason - Optional cancellation reason.
85
90
  */
86
91
  cancel(reason?: unknown): void;
92
+ /**
93
+ * Times out the task, aborting pending or running execution with AhkoTimeoutError.
94
+ *
95
+ * @param timeoutMs - Timeout duration in milliseconds.
96
+ * @param message - Optional custom timeout message.
97
+ */
98
+ timeout(timeoutMs: number, message?: string): void;
87
99
  /**
88
100
  * Handles external AbortSignal trigger.
89
101
  */
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Current version of @mrjacket/ahko package.
3
3
  */
4
- export declare const VERSION = "1.0.0";
4
+ export declare const VERSION = "1.1.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrjacket/ahko",
3
- "version": "1.0.0",
3
+ "version": "1.1.5",
4
4
  "description": "A low-energy task scheduler for JavaScript and TypeScript. Let your code chill.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",