@mrjacket/ahko 1.1.0 → 1.1.6

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;
@@ -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.
@@ -9,3 +9,5 @@ export * from "./events.model.js";
9
9
  export * from "./circuit-breaker.model.js";
10
10
  export * from "./priority.model.js";
11
11
  export * from "./config.model.js";
12
+ export * from "./batch.model.js";
13
+ export * from "./adaptive.model.js";
@@ -1,3 +1,4 @@
1
+ import type { IAdaptiveConcurrencyOptions } from "./adaptive.model.js";
1
2
  import type { ICircuitBreakerOptions } from "./circuit-breaker.model.js";
2
3
  import type { TTaskPriority } from "./priority.model.js";
3
4
  import type { IRetryOptions } from "./retry.model.js";
@@ -62,6 +63,10 @@ export interface IScheduleOptions {
62
63
  * If aborted while running, the abort event is propagated to the task context signal.
63
64
  */
64
65
  signal?: AbortSignal;
66
+ /**
67
+ * Optional tags for classifying tasks and enabling selective cancellation (e.g. `ahko.cancelByTag()`).
68
+ */
69
+ tags?: string[];
65
70
  }
66
71
  /**
67
72
  * Global configuration options for the Ahko scheduler instance.
@@ -84,6 +89,10 @@ export interface IAhkoOptions {
84
89
  * Optional circuit breaker policy to guard against cascading failures.
85
90
  */
86
91
  circuitBreaker?: ICircuitBreakerOptions;
92
+ /**
93
+ * Optional adaptive concurrency policy (AIMD Auto-Chill mode) based on real-time task latency.
94
+ */
95
+ adaptive?: IAdaptiveConcurrencyOptions;
87
96
  /**
88
97
  * Optional named profile from `config.ahko.json` to inherit configuration defaults from.
89
98
  */
@@ -1,3 +1,4 @@
1
+ import type { IAdaptiveStats } from "./adaptive.model.js";
1
2
  import type { ECircuitState } from "./circuit-breaker.model.js";
2
3
  /**
3
4
  * Telemetry snapshot of the Ahko scheduler.
@@ -25,4 +26,6 @@ export interface IAhkoStats {
25
26
  isPaused: boolean;
26
27
  /** Current state of the scheduler circuit breaker, if configured */
27
28
  circuitState?: ECircuitState;
29
+ /** Telemetry from the adaptive concurrency controller, if configured */
30
+ adaptive?: IAdaptiveStats;
28
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
+ }
@@ -1,6 +1,8 @@
1
+ import type { IAdaptiveConcurrencyOptions } from "../models/adaptive.model.js";
1
2
  import type { ICircuitBreakerOptions } from "../models/circuit-breaker.model.js";
2
3
  import type { IScheduleOptions } from "../models/options.model.js";
3
4
  import type { IAhkoStats } from "../models/stats.model.js";
5
+ import { AdaptiveCoordinator } from "./adaptive-coordinator.js";
4
6
  import { CircuitBreakerCoordinator } from "./circuit-breaker.js";
5
7
  import { DebounceCoordinator } from "./debounce-coordinator.js";
6
8
  import { TaskRunner } from "./task-runner.js";
@@ -8,11 +10,12 @@ import { ThrottleCoordinator } from "./throttle-coordinator.js";
8
10
  import { AhkoEventEmitter } from "../events/event-emitter.js";
9
11
  /**
10
12
  * Memory-safe priority-aware task queue managing concurrency allocation,
11
- * rate limiting, circuit breaker protection, flow control (pause/resume), and task lifecycle counters.
13
+ * rate limiting, circuit breaker protection, dynamic & adaptive concurrency,
14
+ * tags, flow control (pause/resume), and task lifecycle counters.
12
15
  */
13
16
  export declare class TaskQueue {
14
17
  /** Maximum concurrent active tasks */
15
- readonly concurrency: number;
18
+ private _concurrency;
16
19
  /** Minimum interval in milliseconds between consecutive task starts */
17
20
  readonly minIntervalMs: number;
18
21
  /** Timestamp of the most recent task start */
@@ -29,6 +32,8 @@ export declare class TaskQueue {
29
32
  private readonly idleEntries;
30
33
  /** Set of tasks currently awaiting a retry backoff timer */
31
34
  private readonly retryEntries;
35
+ /** Tag index for selective cancellation and task classification */
36
+ private readonly tagIndex;
32
37
  /** Coordinator for debounced tasks with key coalescing */
33
38
  readonly debounceCoordinator: DebounceCoordinator;
34
39
  /** Coordinator for throttled tasks with leading/trailing coalescing */
@@ -37,6 +42,8 @@ export declare class TaskQueue {
37
42
  readonly emitter: AhkoEventEmitter;
38
43
  /** Circuit breaker coordinator if configured */
39
44
  readonly circuitBreakerCoordinator?: CircuitBreakerCoordinator;
45
+ /** Adaptive concurrency coordinator if configured */
46
+ readonly adaptiveCoordinator?: AdaptiveCoordinator;
40
47
  /** Pause state flag */
41
48
  private _isPaused;
42
49
  /** Set of pending resolvers awaiting scheduler idle transition */
@@ -61,9 +68,21 @@ export declare class TaskQueue {
61
68
  * @param concurrency - Maximum concurrent tasks (defaults to Infinity).
62
69
  * @param minIntervalMs - Minimum interval in milliseconds between task dispatches.
63
70
  * @param circuitBreakerOptions - Optional circuit breaker policy configuration.
71
+ * @param adaptiveOptions - Optional adaptive concurrency policy configuration.
64
72
  * @throws {AhkoConfigurationError} If concurrency is less than 1 or minIntervalMs is invalid.
65
73
  */
66
- constructor(concurrency?: number, minIntervalMs?: number, circuitBreakerOptions?: ICircuitBreakerOptions);
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;
67
86
  /**
68
87
  * Pauses queue execution. Running tasks will complete normally, but no new pending tasks will be dispatched.
69
88
  */
@@ -76,6 +95,31 @@ export declare class TaskQueue {
76
95
  * Checks whether the task queue is currently paused.
77
96
  */
78
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;
79
123
  /**
80
124
  * Inserts a task runner into the queue based on priority weight (descending).
81
125
  * Preserves FIFO ordering among tasks with identical priority.
@@ -36,14 +36,17 @@ 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[]);
47
50
  /** Flag indicating if runner was aborted by an overall total timeout deadline */
48
51
  totalTimedOut: boolean;
49
52
  /**
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.1.0";
4
+ export declare const VERSION = "1.1.6";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrjacket/ahko",
3
- "version": "1.1.0",
3
+ "version": "1.1.6",
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",