@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.
- package/CHANGELOG.md +63 -0
- package/README.md +230 -7
- package/dist/ahko.d.ts +155 -11
- package/dist/config/config-loader.d.ts +31 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/errors/circuit-breaker.error.d.ts +24 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/index.cjs +1116 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +1101 -97
- package/dist/index.js.map +1 -1
- package/dist/models/adaptive.model.d.ts +47 -0
- package/dist/models/batch.model.d.ts +22 -0
- package/dist/models/circuit-breaker.model.d.ts +39 -0
- package/dist/models/config.model.d.ts +23 -0
- package/dist/models/events.model.d.ts +6 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/models/options.model.d.ts +32 -0
- package/dist/models/priority.model.d.ts +18 -0
- package/dist/models/stats.model.d.ts +8 -0
- package/dist/scheduler/adaptive-coordinator.d.ts +45 -0
- package/dist/scheduler/circuit-breaker.d.ts +56 -0
- package/dist/scheduler/task-queue.d.ts +74 -5
- package/dist/scheduler/task-runner.d.ts +13 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,69 @@ 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
|
+
## [1.1.5] - 2026-09-24 — Batch Collections, Dynamic & Adaptive Concurrency, Task Tags
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Batch Collections API (`ahko.map` & `ahko.each`)**:
|
|
12
|
+
- `ahko.map<TItem, TResult>(items, fn, options?)`: Concurrently transforms any iterable sequence while strictly preserving original element order.
|
|
13
|
+
- `ahko.each<TItem>(items, fn, options?)`: Concurrently iterates over any sequence returning `Promise<void>`.
|
|
14
|
+
- Supports localized concurrency caps per-batch (`options.concurrency`), falling back to scheduler concurrency when omitted.
|
|
15
|
+
- Fail-fast flow control via `stopOnError: true` (aborts remaining tasks immediately and rejects) or settled error propagation via `stopOnError: false` (default).
|
|
16
|
+
- Native integration with `signal`, `retry`, `tags`, and priority options.
|
|
17
|
+
- **Dynamic & Adaptive Concurrency (AIMD Auto-Chill Mode)**:
|
|
18
|
+
- Runtime dynamic concurrency reconfiguration via `ahko.setConcurrency(n)` and getter `ahko.concurrency`.
|
|
19
|
+
- Additive Increase / Multiplicative Decrease (AIMD) algorithm (`AdaptiveCoordinator`) adjusting scheduler capacity based on real-time task latency (`options.adaptive`).
|
|
20
|
+
- Seamlessly handles network latency spikes by scaling down concurrency on congestion and recovering when latency drops.
|
|
21
|
+
- Lifecycle event `"concurrency:change"` emitting `previousConcurrency`, `currentConcurrency`, and human-readable `reason`.
|
|
22
|
+
- Telemetry metrics in `ahko.stats().adaptive`: `currentConcurrency`, `averageLatencyMs`, `samplesRecorded`.
|
|
23
|
+
- **Task Tags & Selective Cancellation**:
|
|
24
|
+
- Categorize tasks via `tags: string[]` in `schedule()`, `map()`, or declarative profiles.
|
|
25
|
+
- Selectively cancel related tasks via `ahko.cancelByTag(tag, reason?)` without interrupting other pending or active workloads.
|
|
26
|
+
- Inspect workload density per category via `ahko.statsByTag(tag)` (`activeTasks` and `pendingTasks`).
|
|
27
|
+
- Memory-safe automatic tag indexing and instant cleanup upon task runner settlement.
|
|
28
|
+
- **Declarative Configuration Schema Expansion**:
|
|
29
|
+
- Added `adaptive` policy and `tags` classification to `schema.json` and `config.ahko.example.json`.
|
|
30
|
+
- **New Runnable Examples**:
|
|
31
|
+
- `examples/09-batch-collections.mjs`: Concurrent mapping over iterables with strict index ordering.
|
|
32
|
+
- `examples/10-adaptive-concurrency.mjs`: AIMD Auto-Chill mode reacting to upstream latency.
|
|
33
|
+
- `examples/11-tag-cancellation.mjs`: Tagged workload telemetry and selective cancellation.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## [1.1.0] - 2026-09-23 — Declarative Config, Circuit Breaker & Priority Queue
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- Declarative configuration file support via `config.ahko.json` with multi-profile support (e.g. `default`, `crawler`, `critical-gateway`).
|
|
41
|
+
- Static configuration loaders: `Ahko.loadConfig(config)` for universal programmatic profile loading (Node.js & browser), `Ahko.loadConfigFile(path?)` for asynchronous filesystem loading, and `Ahko.fromProfile(name, overrides?)`.
|
|
42
|
+
- Martin Fowler Circuit Breaker pattern with `ECircuitState` (`CLOSED`, `OPEN`, `HALF_OPEN`), `CircuitBreakerCoordinator`, and `AhkoCircuitBreakerOpenError`:
|
|
43
|
+
- Protects downstream services by tracking consecutive failures against a `failureThreshold`.
|
|
44
|
+
- Fast-fails pending and incoming tasks without execution when OPEN.
|
|
45
|
+
- Automatically transitions to HALF_OPEN after `resetTimeoutMs` cooldown to allow a recovery trial.
|
|
46
|
+
- Heals to CLOSED on trial success or immediately re-trips to OPEN on trial failure.
|
|
47
|
+
- Priority queue scheduling with stable FIFO ordering:
|
|
48
|
+
- Supports named priorities (`"high"`, `"normal"`, `"low"`) and arbitrary numerical weights (e.g. `100`, `-5`).
|
|
49
|
+
- Tasks with higher priority preempt lower priority tasks in the queue; tasks with identical priority preserve strict FIFO ordering.
|
|
50
|
+
- Queue flow control via `ahko.pause()`, `ahko.resume()`, and `ahko.isPaused()`:
|
|
51
|
+
- Halts dispatching pending tasks without interrupting currently executing tasks.
|
|
52
|
+
- Immediately dispatches accumulated tasks upon resume up to concurrency limits.
|
|
53
|
+
- Total Timeout Budget (`totalTimeoutMs`):
|
|
54
|
+
- Sets an overarching execution deadline spanning queue wait time, execution, and retry delays.
|
|
55
|
+
- Cancels task runners cleanly with `AhkoTimeoutError` when the budget expires.
|
|
56
|
+
- Ergonomic function wrapping via `ahko.wrap(fn, options)`:
|
|
57
|
+
- Wraps any sync or async function returning a decorated function routed through the scheduler with pre-configured priorities and options.
|
|
58
|
+
- New runnable examples:
|
|
59
|
+
- `examples/07-circuit-breaker.mjs`: circuit breaker tripping, fast-failing, and cooldown recovery.
|
|
60
|
+
- `examples/08-priority-queue.mjs`: priority queue ordering and pause/resume flow control.
|
|
61
|
+
- `config.ahko.example.json`: reference declarative schema configuration file.
|
|
62
|
+
- Extended telemetry in `ahko.stats()`: `isPaused` boolean and `circuitState` indicator.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
- Remediated 2 CodeQL static analysis security alerts:
|
|
66
|
+
- Alert #4: Removed unused `externalController` declaration in `src/__tests__/e2e.test.ts`.
|
|
67
|
+
- Alert #3: Removed unused `sleep` helper declaration in `examples/04-retry-backoff-jitter.mjs`.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
8
71
|
## [1.0.0] - 2026-09-23 — Stable Scheduler Release
|
|
9
72
|
|
|
10
73
|
### Added
|
package/README.md
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
<a href="https://www.npmjs.com/package/@mrjacket/ahko">
|
|
16
16
|
<img src="https://img.shields.io/node/v/@mrjacket/ahko.svg" alt="node">
|
|
17
17
|
</a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@mrjacket/ahko">
|
|
19
|
+
<img src="https://img.shields.io/npm/dm/@mrjacket/ahko.svg" alt="npm downloads">
|
|
20
|
+
</a>
|
|
18
21
|
<a href="https://github.com/x-name15/ahko/actions/workflows/ci.yml">
|
|
19
22
|
<img src="https://github.com/x-name15/ahko/actions/workflows/ci.yml/badge.svg" alt="ci">
|
|
20
23
|
</a>
|
|
@@ -281,6 +284,182 @@ console.log(ahko.battery());
|
|
|
281
284
|
// { level: 3, chill: true, status: "low-energy", quote: "Mwee... my battery is low, but all your tasks are handled completely chill." }
|
|
282
285
|
```
|
|
283
286
|
|
|
287
|
+
### 12. Priority-Aware Scheduling
|
|
288
|
+
|
|
289
|
+
Ensure critical tasks jump ahead of normal or background work while preserving strict FIFO ordering among peers:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// Named priorities: "high" (10), "normal" (0), "low" (-10), or custom numeric weights
|
|
293
|
+
await ahko.schedule(criticalTask, { priority: "high" });
|
|
294
|
+
await ahko.schedule(backgroundSync, { priority: "low" });
|
|
295
|
+
await ahko.schedule(superUrgent, { priority: 100 });
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 13. Circuit Breaker Protection
|
|
299
|
+
|
|
300
|
+
Protect fragile downstream services and databases from cascading failures. When consecutive failures meet `failureThreshold`, the circuit trips to `OPEN` and fast-fails tasks immediately without execution:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
const ahko = new Ahko({
|
|
304
|
+
concurrency: 2,
|
|
305
|
+
circuitBreaker: {
|
|
306
|
+
failureThreshold: 3, // trip after 3 consecutive failures
|
|
307
|
+
resetTimeoutMs: 15000, // wait 15s before attempting recovery probe
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
await ahko.schedule(callFlakyService);
|
|
313
|
+
} catch (err) {
|
|
314
|
+
if (err instanceof AhkoCircuitBreakerOpenError) {
|
|
315
|
+
console.warn("Fast-failed: Circuit breaker is OPEN!");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 14. Pause & Resume Flow Control
|
|
321
|
+
|
|
322
|
+
Temporarily halt queue dispatching without aborting in-flight tasks. Resuming immediately pumps accumulated tasks up to capacity:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// Stop dispatching new tasks
|
|
326
|
+
ahko.pause();
|
|
327
|
+
console.log(ahko.isPaused()); // true
|
|
328
|
+
|
|
329
|
+
// In-flight tasks finish peacefully...
|
|
330
|
+
|
|
331
|
+
// Resume dispatching
|
|
332
|
+
ahko.resume();
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 15. Total Timeout Budget
|
|
336
|
+
|
|
337
|
+
Enforce an overarching deadline spanning queue wait time, execution, and retries:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// Task will abort with AhkoTimeoutError if total elapsed time exceeds 5000ms
|
|
341
|
+
await ahko.schedule(fetchWithRetries, {
|
|
342
|
+
totalTimeoutMs: 5000,
|
|
343
|
+
retry: { attempts: 3, baseDelay: 1000 },
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### 16. Function Wrapping (`ahko.wrap`)
|
|
348
|
+
|
|
349
|
+
Decorate any async function to automatically route every invocation through Ahko:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
const fetchUser = ahko.wrap(
|
|
353
|
+
async (userId: string) => {
|
|
354
|
+
const res = await fetch(`https://api.example.com/users/${userId}`);
|
|
355
|
+
return res.json();
|
|
356
|
+
},
|
|
357
|
+
{ priority: "high", retry: { attempts: 2 } }
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Seamlessly executed via scheduler
|
|
361
|
+
const user = await fetchUser("usr_42");
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 17. Declarative Configuration (`config.ahko.json`)
|
|
365
|
+
|
|
366
|
+
Define scheduler defaults and workload profiles cleanly in JSON:
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
370
|
+
"default": {
|
|
371
|
+
"concurrency": 2,
|
|
372
|
+
"minIntervalMs": 50,
|
|
373
|
+
"priority": "normal"
|
|
374
|
+
},
|
|
375
|
+
"profiles": {
|
|
376
|
+
"crawler": {
|
|
377
|
+
"concurrency": 4,
|
|
378
|
+
"minIntervalMs": 200,
|
|
379
|
+
"priority": "low"
|
|
380
|
+
},
|
|
381
|
+
"critical-gateway": {
|
|
382
|
+
"concurrency": 1,
|
|
383
|
+
"circuitBreaker": {
|
|
384
|
+
"failureThreshold": 3,
|
|
385
|
+
"resetTimeoutMs": 10000
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Programmatically or from config file:
|
|
394
|
+
Ahko.loadConfig(config);
|
|
395
|
+
|
|
396
|
+
// Instantiate configured scheduler from profile
|
|
397
|
+
const gateway = Ahko.fromProfile("critical-gateway");
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 18. Batch Collections API (`ahko.map` & `ahko.each`)
|
|
401
|
+
|
|
402
|
+
Transform collections concurrently with strict index order preservation and localized concurrency limits:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
const urls = ["/api/users", "/api/posts", "/api/comments"];
|
|
406
|
+
|
|
407
|
+
// Processes concurrently (max 2 at a time), guaranteed return in original order
|
|
408
|
+
const responses = await ahko.map(
|
|
409
|
+
urls,
|
|
410
|
+
async (url, index, { signal }) => {
|
|
411
|
+
const res = await fetch(url, { signal });
|
|
412
|
+
return res.json();
|
|
413
|
+
},
|
|
414
|
+
{ concurrency: 2, stopOnError: true }
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// Or iterate over items returning void:
|
|
418
|
+
await ahko.each(userIds, async (id) => syncUser(id), { concurrency: 5 });
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### 19. Dynamic & Adaptive Concurrency (AIMD Auto-Chill)
|
|
422
|
+
|
|
423
|
+
Adjust concurrency dynamically on the fly or let Ahko adapt automatically to network latency:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// Manually update concurrency at runtime:
|
|
427
|
+
ahko.setConcurrency(5);
|
|
428
|
+
console.log(ahko.concurrency); // 5
|
|
429
|
+
|
|
430
|
+
// Or enable AIMD (Additive Increase / Multiplicative Decrease) Auto-Chill mode:
|
|
431
|
+
const adaptiveAhko = new Ahko({
|
|
432
|
+
concurrency: 4,
|
|
433
|
+
adaptive: {
|
|
434
|
+
targetLatencyMs: 150, // if tasks take >150ms, cut concurrency in half
|
|
435
|
+
sampleWindowSize: 5, // adjust after every 5 completed tasks
|
|
436
|
+
minConcurrency: 1,
|
|
437
|
+
maxConcurrency: 10,
|
|
438
|
+
backoffFactor: 0.5,
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
adaptiveAhko.on("concurrency:change", ({ previousConcurrency, currentConcurrency, reason }) => {
|
|
443
|
+
console.log(`Capacity adapted: ${previousConcurrency} -> ${currentConcurrency} (${reason})`);
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### 20. Task Tags & Selective Cancellation
|
|
448
|
+
|
|
449
|
+
Classify tasks by tags, query categorical telemetry, and selectively cancel specific operations:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
// Tag tasks during scheduling:
|
|
453
|
+
ahko.schedule(generateReport, { tags: ["reports", "finance"] });
|
|
454
|
+
ahko.schedule(syncDatabase, { tags: ["sync"] });
|
|
455
|
+
|
|
456
|
+
// Check active and pending tasks by tag:
|
|
457
|
+
console.log(ahko.statsByTag("reports")); // { activeTasks: 1, pendingTasks: 0 }
|
|
458
|
+
|
|
459
|
+
// Cancel all tasks associated with a tag without affecting other tasks:
|
|
460
|
+
ahko.cancelByTag("reports", "User navigated away");
|
|
461
|
+
```
|
|
462
|
+
|
|
284
463
|
---
|
|
285
464
|
|
|
286
465
|
## Documentation
|
|
@@ -291,10 +470,11 @@ Comprehensive guides and technical documentation are available in the [`docs/`](
|
|
|
291
470
|
|---|---|
|
|
292
471
|
| [**Documentation Portal**](./docs/README.md) | Master overview and index of all guides and specifications. |
|
|
293
472
|
| [**Getting Started**](./docs/guides/getting-started.md) | Quickstart guide, installation, and fundamental usage patterns. |
|
|
473
|
+
| [**Declarative Configuration**](./docs/guides/configuration.md) | Centralizing limits in `config.ahko.json`, `$schema` validation, named profiles, and fallback rules. |
|
|
294
474
|
| [**Library API**](./docs/guides/library.md) | Complete programmatic API reference, TypeScript interfaces, and options. |
|
|
295
475
|
| [**Production Recipes**](./docs/guides/recipes.md) | Battle-tested recipes (paced API client, debounced search, throttled scroll, graceful shutdown). |
|
|
296
476
|
| [**Architecture**](./docs/architecture/ARCHITECTURE.md) | Architectural specifications, lifecycle state machine, and design decisions. |
|
|
297
|
-
| [**Roadmap**](./docs/architecture/ROADMAP.md) | Milestone progression from 0.1.0 through 1.
|
|
477
|
+
| [**Roadmap**](./docs/architecture/ROADMAP.md) | Milestone progression from 0.1.0 through 1.1.0. |
|
|
298
478
|
| [**Engineering Log**](./docs/architecture/LOG.md) | Chronological log of engineering decisions and ADRs. |
|
|
299
479
|
|
|
300
480
|
> **Runnable Examples:** A comprehensive suite of standalone, runnable Node.js scripts is available in the [`examples/`](./examples/) folder. See [`examples/README.md`](./examples/README.md) for details.
|
|
@@ -311,6 +491,33 @@ Creates an ahko scheduler instance.
|
|
|
311
491
|
|---|---|---|---|
|
|
312
492
|
| `concurrency` | `number` | `Infinity` | Maximum concurrent tasks allowed to run simultaneously. Must be $\ge 1$. |
|
|
313
493
|
| `minIntervalMs` | `number` | `0` | Minimum interval in milliseconds between consecutive task starts. Must be $\ge 0$. |
|
|
494
|
+
| `circuitBreaker` | `ICircuitBreakerOptions` | `undefined` | Optional failure threshold and cooldown reset configuration. |
|
|
495
|
+
| `adaptive` | `IAdaptiveConcurrencyOptions` | `undefined` | Optional AIMD adaptive concurrency options based on task execution latency. |
|
|
496
|
+
| `profile` | `string` | `undefined` | Name of declarative profile to inherit settings from. |
|
|
497
|
+
|
|
498
|
+
### `ahko.concurrency`
|
|
499
|
+
|
|
500
|
+
Getter returning the current concurrency capacity limit.
|
|
501
|
+
|
|
502
|
+
### `ahko.setConcurrency(newConcurrency: number): void`
|
|
503
|
+
|
|
504
|
+
Dynamically updates the concurrency limit of the scheduler at runtime.
|
|
505
|
+
|
|
506
|
+
### `ahko.map<TItem, TResult>(items, fn, options?): Promise<TResult[]>`
|
|
507
|
+
|
|
508
|
+
Concurrently transforms an iterable sequence into an array with strict index ordering. Supports localized `concurrency` limits and `stopOnError`.
|
|
509
|
+
|
|
510
|
+
### `ahko.each<TItem>(items, fn, options?): Promise<void>`
|
|
511
|
+
|
|
512
|
+
Iterates over an iterable sequence concurrently, executing the callback for each element.
|
|
513
|
+
|
|
514
|
+
### `ahko.cancelByTag(tag: string, reason?: unknown): number`
|
|
515
|
+
|
|
516
|
+
Cancels all pending, delayed, and active tasks marked with the specified tag. Returns the number of cancelled tasks.
|
|
517
|
+
|
|
518
|
+
### `ahko.statsByTag(tag: string): { activeTasks: number; pendingTasks: number }`
|
|
519
|
+
|
|
520
|
+
Returns active and pending task counts for a specific classification tag.
|
|
314
521
|
|
|
315
522
|
### `ahko.schedule<T>(task: ITask<T>, options?: IScheduleOptions): Promise<T>`
|
|
316
523
|
|
|
@@ -319,29 +526,44 @@ Schedules an asynchronous task with full return type inference.
|
|
|
319
526
|
| Option | Type | Default | Description |
|
|
320
527
|
|---|---|---|---|
|
|
321
528
|
| `strategy` | `"immediate" \| "delay" \| "idle" \| "throttle" \| "debounce"` | `"immediate"` | Scheduling execution strategy. |
|
|
529
|
+
| `priority` | `"high" \| "normal" \| "low" \| number` | `"normal"` | Task priority weight for queue ordering. |
|
|
322
530
|
| `delay` | `number` | `0` | Delay in milliseconds when strategy is `"delay"`. |
|
|
323
531
|
| `key` | `string \| symbol` | `undefined` | Explicit identity key for `"debounce"` and `"throttle"`. |
|
|
324
532
|
| `waitMs` | `number` | `undefined` | Window duration in ms for debounce quiet period or throttle interval. |
|
|
325
533
|
| `idleTimeout` | `number` | `undefined` | Maximum time to wait for idle window before forcing queue entry. |
|
|
326
534
|
| `retry` | `IRetryOptions` | `undefined` | Automatic retry policy (attempts, backoff, jitter, predicate). |
|
|
327
535
|
| `timeoutMs` | `number` | `undefined` | Maximum execution duration in milliseconds per attempt before aborting with `AhkoTimeoutError`. |
|
|
536
|
+
| `totalTimeoutMs` | `number` | `undefined` | Total execution budget across wait time, retries, and execution. |
|
|
328
537
|
| `signal` | `AbortSignal` | `undefined` | Optional external `AbortSignal` for cooperative cancellation. |
|
|
538
|
+
| `tags` | `string[]` | `undefined` | Classification tags for selective cancellation and metric grouping. |
|
|
539
|
+
|
|
540
|
+
### `ahko.wrap(fn, options?)`
|
|
541
|
+
|
|
542
|
+
Returns a wrapped version of `fn` routed through the scheduler.
|
|
543
|
+
|
|
544
|
+
### `ahko.pause() / ahko.resume() / ahko.isPaused()`
|
|
545
|
+
|
|
546
|
+
Pauses and resumes task dispatching.
|
|
547
|
+
|
|
548
|
+
### `ahko.circuitState`
|
|
549
|
+
|
|
550
|
+
Returns current circuit breaker state (`"closed" | "open" | "half_open"` or `undefined`).
|
|
329
551
|
|
|
330
|
-
### `ahko.debounce<T>(key
|
|
552
|
+
### `ahko.debounce<T>(key, task, waitMs, options?): Promise<T>`
|
|
331
553
|
|
|
332
554
|
Convenience method scheduling a debounced task with key-based Promise coalescing.
|
|
333
555
|
|
|
334
|
-
### `ahko.throttle<T>(key
|
|
556
|
+
### `ahko.throttle<T>(key, task, waitMs, options?): Promise<T>`
|
|
335
557
|
|
|
336
558
|
Convenience method scheduling a throttled task with leading execution and coalesced trailing run.
|
|
337
559
|
|
|
338
|
-
### `ahko.idle<T>(task
|
|
560
|
+
### `ahko.idle<T>(task, options?): Promise<T>`
|
|
339
561
|
|
|
340
562
|
Convenience method scheduling a task under `strategy: "idle"`.
|
|
341
563
|
|
|
342
564
|
### `ahko.on(event, handler)`
|
|
343
565
|
|
|
344
|
-
Subscribes to scheduler lifecycle events (`task:start`, `task:complete`, `task:fail`, `task:cancel`, `task:timeout`, `idle`). Returns an unsubscribe function.
|
|
566
|
+
Subscribes to scheduler lifecycle events (`task:start`, `task:complete`, `task:fail`, `task:cancel`, `task:timeout`, `idle`, `concurrency:change`). Returns an unsubscribe function.
|
|
345
567
|
|
|
346
568
|
### `ahko.off(event, handler)`
|
|
347
569
|
|
|
@@ -369,7 +591,7 @@ Returns mascot battery status and quote.
|
|
|
369
591
|
|
|
370
592
|
### `ahko.stats(): IAhkoStats`
|
|
371
593
|
|
|
372
|
-
Returns a snapshot of current task counters, retry counts, total dispatches, and queue capacity.
|
|
594
|
+
Returns a snapshot of current task counters, retry counts, total dispatches, pause status, circuit state, and queue capacity.
|
|
373
595
|
|
|
374
596
|
---
|
|
375
597
|
|
|
@@ -381,7 +603,8 @@ All scheduler errors inherit from `AhkoError`:
|
|
|
381
603
|
- `AhkoCancellationError`: Thrown when a task is aborted.
|
|
382
604
|
- `AhkoConfigurationError`: Thrown when invalid options are provided.
|
|
383
605
|
- `AhkoQueueError`: Thrown when queue constraints are violated.
|
|
384
|
-
- `AhkoTimeoutError`: Thrown when a task exceeds its configured duration.
|
|
606
|
+
- `AhkoTimeoutError`: Thrown when a task exceeds its configured duration or totalTimeoutMs.
|
|
607
|
+
- `AhkoCircuitBreakerOpenError`: Thrown when task execution is fast-failed because the circuit breaker is OPEN.
|
|
385
608
|
|
|
386
609
|
---
|
|
387
610
|
|
package/dist/ahko.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import type { IBatchOptions, IBatchMapOptions } from "./models/batch.model.js";
|
|
2
|
+
import type { ECircuitState } from "./models/circuit-breaker.model.js";
|
|
3
|
+
import type { IAhkoFileConfig } from "./models/config.model.js";
|
|
4
|
+
import type { ITaskContext } from "./models/context.model.js";
|
|
1
5
|
import type { TAhkoEventName, TAhkoEventHandler, TAhkoUnsubscribe } from "./models/events.model.js";
|
|
2
6
|
import type { IAhkoOptions, IScheduleOptions } from "./models/options.model.js";
|
|
3
7
|
import type { IAhkoStats } from "./models/stats.model.js";
|
|
4
8
|
import type { ITask } from "./models/task.model.js";
|
|
9
|
+
import type { CircuitBreakerCoordinator } from "./scheduler/circuit-breaker.js";
|
|
5
10
|
/**
|
|
6
11
|
* Ahko — Low-energy asynchronous task scheduler.
|
|
7
12
|
*
|
|
8
|
-
* Coordinates execution timing, enforces concurrency limits,
|
|
9
|
-
*
|
|
13
|
+
* Coordinates execution timing, enforces concurrency limits, manages priorities,
|
|
14
|
+
* provides circuit-breaker stability, supports pause/resume flow control,
|
|
15
|
+
* and cooperates natively with AbortSignal cancellation.
|
|
10
16
|
*
|
|
11
17
|
* @example
|
|
12
18
|
* ```typescript
|
|
@@ -23,6 +29,37 @@ import type { ITask } from "./models/task.model.js";
|
|
|
23
29
|
export declare class Ahko {
|
|
24
30
|
/** Internal queue and concurrency manager */
|
|
25
31
|
private readonly queue;
|
|
32
|
+
/** Default schedule options inherited from profile if configured */
|
|
33
|
+
private readonly defaultScheduleOptions?;
|
|
34
|
+
/**
|
|
35
|
+
* Programmatically loads a declarative configuration into memory.
|
|
36
|
+
* Works universally across Node.js, browsers, and edge runtimes.
|
|
37
|
+
*
|
|
38
|
+
* @param config - File configuration object containing default and named profiles.
|
|
39
|
+
*/
|
|
40
|
+
static loadConfig(config: IAhkoFileConfig): void;
|
|
41
|
+
/**
|
|
42
|
+
* Asynchronously loads a configuration file from disk (Node.js).
|
|
43
|
+
*
|
|
44
|
+
* @param filePath - Path to configuration file (default: "config.ahko.json").
|
|
45
|
+
*/
|
|
46
|
+
static loadConfigFile(filePath?: string): Promise<IAhkoFileConfig | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* Resets the active declarative configuration.
|
|
49
|
+
*/
|
|
50
|
+
static resetConfig(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves the currently active declarative configuration.
|
|
53
|
+
*/
|
|
54
|
+
static getActiveConfig(): IAhkoFileConfig | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Instantiates an Ahko scheduler initialized with settings from a declarative profile.
|
|
57
|
+
*
|
|
58
|
+
* @param profileName - Optional name of the profile (e.g. "api", "background").
|
|
59
|
+
* @param overrides - Optional scheduler options overriding profile values.
|
|
60
|
+
* @returns A new configured Ahko instance.
|
|
61
|
+
*/
|
|
62
|
+
static fromProfile(profileName?: string, overrides?: IAhkoOptions): Ahko;
|
|
26
63
|
/**
|
|
27
64
|
* Initializes a new Ahko scheduler instance.
|
|
28
65
|
*
|
|
@@ -35,28 +72,73 @@ export declare class Ahko {
|
|
|
35
72
|
* ```
|
|
36
73
|
*/
|
|
37
74
|
constructor(options?: IAhkoOptions);
|
|
75
|
+
/**
|
|
76
|
+
* Current concurrency limit.
|
|
77
|
+
*/
|
|
78
|
+
get concurrency(): number;
|
|
79
|
+
/**
|
|
80
|
+
* Dynamically updates the concurrency limit of the scheduler.
|
|
81
|
+
*
|
|
82
|
+
* @param concurrency - New maximum concurrency (must be >= 1).
|
|
83
|
+
* @throws {AhkoConfigurationError} If concurrency is invalid.
|
|
84
|
+
*/
|
|
85
|
+
setConcurrency(concurrency: number): void;
|
|
86
|
+
/**
|
|
87
|
+
* Pauses scheduler dispatch. In-flight tasks run to completion, but pending tasks remain queued.
|
|
88
|
+
*/
|
|
89
|
+
pause(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Resumes scheduler dispatch, immediately executing waiting tasks up to available concurrency.
|
|
92
|
+
*/
|
|
93
|
+
resume(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Checks whether the scheduler is currently paused.
|
|
96
|
+
*/
|
|
97
|
+
isPaused(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Current circuit breaker state if circuit breaker protection is configured.
|
|
100
|
+
*/
|
|
101
|
+
get circuitState(): ECircuitState | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Access to the underlying circuit breaker coordinator instance if configured.
|
|
104
|
+
*/
|
|
105
|
+
get circuitBreaker(): CircuitBreakerCoordinator | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Wraps an async function so every execution is automatically routed through this Ahko scheduler.
|
|
108
|
+
*
|
|
109
|
+
* @template TArgs - Parameter types of the wrapped function.
|
|
110
|
+
* @template TReturn - Return type of the wrapped function.
|
|
111
|
+
* @param fn - The function to wrap.
|
|
112
|
+
* @param options - Optional scheduling options applied to every wrapped call.
|
|
113
|
+
* @returns A wrapped function returning a Promise.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const fetchUser = ahko.wrap(async (id: string) => api.getUser(id), { priority: "high" });
|
|
118
|
+
* const user = await fetchUser("usr_123");
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
wrap<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => Promise<TReturn> | TReturn, options?: IScheduleOptions): (...args: TArgs) => Promise<TReturn>;
|
|
38
122
|
/**
|
|
39
123
|
* Schedules a task for execution with full return type inference.
|
|
40
124
|
*
|
|
41
125
|
* @template T - Inferred return type of the task.
|
|
42
126
|
* @param task - Asynchronous or synchronous task function accepting an {@link ITaskContext}.
|
|
43
|
-
* @param options - Task-specific scheduling options such as strategy, delay, and cancellation signal.
|
|
127
|
+
* @param options - Task-specific scheduling options such as strategy, priority, delay, and cancellation signal.
|
|
44
128
|
* @returns A promise that resolves with the task's return value.
|
|
45
129
|
*
|
|
46
130
|
* @throws {AhkoConfigurationError} If the task is not a function or options are invalid.
|
|
47
131
|
* @throws {AhkoCancellationError} If the task is cancelled prior to or during execution.
|
|
48
|
-
* @throws {AhkoTimeoutError} If task execution exceeds timeoutMs.
|
|
132
|
+
* @throws {AhkoTimeoutError} If task execution exceeds timeoutMs or totalTimeoutMs.
|
|
133
|
+
* @throws {AhkoCircuitBreakerOpenError} If the circuit breaker is OPEN and rejects the execution.
|
|
49
134
|
*
|
|
50
135
|
* @example
|
|
51
136
|
* ```typescript
|
|
52
137
|
* // Immediate execution (subject to concurrency)
|
|
53
138
|
* const count = await ahko.schedule(async () => 42);
|
|
54
139
|
*
|
|
55
|
-
* //
|
|
56
|
-
* await ahko.schedule(
|
|
57
|
-
* async ({ signal }) => doWork({ signal }),
|
|
58
|
-
* { strategy: "delay", delay: 1000 }
|
|
59
|
-
* );
|
|
140
|
+
* // High priority task
|
|
141
|
+
* await ahko.schedule(doUrgentWork, { priority: "high" });
|
|
60
142
|
* ```
|
|
61
143
|
*/
|
|
62
144
|
schedule<T>(task: ITask<T>, options?: IScheduleOptions): Promise<T>;
|
|
@@ -99,15 +181,77 @@ export declare class Ahko {
|
|
|
99
181
|
* @returns Promise resolving with the leading or coalesced trailing result.
|
|
100
182
|
*/
|
|
101
183
|
throttle<T>(key: string | symbol, task: ITask<T>, waitMs: number, options?: Omit<IScheduleOptions, "strategy" | "key" | "waitMs">): Promise<T>;
|
|
184
|
+
/**
|
|
185
|
+
* Transforms an iterable of items concurrently using an asynchronous mapping function.
|
|
186
|
+
*
|
|
187
|
+
* Results are guaranteed to be returned in the original index order.
|
|
188
|
+
* Concurrency can be capped per-batch or fall back to the scheduler's global limit.
|
|
189
|
+
*
|
|
190
|
+
* @template TItem - Type of input elements.
|
|
191
|
+
* @template TResult - Type of mapped elements.
|
|
192
|
+
* @param items - Iterable sequence of items to process.
|
|
193
|
+
* @param fn - Mapper callback receiving item, index, and task context.
|
|
194
|
+
* @param options - Batch execution options (concurrency, stopOnError, retry, signal, tags, etc.).
|
|
195
|
+
* @returns Array of transformed results in index order.
|
|
196
|
+
*
|
|
197
|
+
* @throws {AhkoConfigurationError} If fn is not a function or concurrency is invalid.
|
|
198
|
+
* @throws {AhkoCancellationError} If batch or item is cancelled.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const urls = ["/api/1", "/api/2", "/api/3"];
|
|
203
|
+
* const data = await ahko.map(urls, async (url, i, { signal }) => {
|
|
204
|
+
* const res = await fetch(url, { signal });
|
|
205
|
+
* return res.json();
|
|
206
|
+
* }, { concurrency: 2 });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
map<TItem, TResult>(items: Iterable<TItem>, fn: (item: TItem, index: number, context: ITaskContext) => Promise<TResult> | TResult, options?: IBatchMapOptions<TItem, TResult>): Promise<TResult[]>;
|
|
210
|
+
/**
|
|
211
|
+
* Iterates sequentially or concurrently over an iterable sequence of items,
|
|
212
|
+
* executing the callback function for each element.
|
|
213
|
+
*
|
|
214
|
+
* @template TItem - Type of input elements.
|
|
215
|
+
* @param items - Iterable sequence of items to process.
|
|
216
|
+
* @param fn - Callback receiving item, index, and task context.
|
|
217
|
+
* @param options - Batch execution options.
|
|
218
|
+
* @returns Promise resolving once all items have finished executing.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* await ahko.each(userQueue, async (user, index, { signal }) => {
|
|
223
|
+
* await sendWelcomeEmail(user, { signal });
|
|
224
|
+
* }, { concurrency: 5 });
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
each<TItem>(items: Iterable<TItem>, fn: (item: TItem, index: number, context: ITaskContext) => Promise<void> | void, options?: IBatchOptions): Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* Cancels all pending, delayed, and active tasks tagged with the given tag.
|
|
230
|
+
*
|
|
231
|
+
* @param tag - Tag identifier.
|
|
232
|
+
* @param reason - Optional cancellation reason.
|
|
233
|
+
* @returns Total number of tasks cancelled.
|
|
234
|
+
*/
|
|
235
|
+
cancelByTag(tag: string, reason?: unknown): number;
|
|
236
|
+
/**
|
|
237
|
+
* Retrieves active and pending task counts for a given tag.
|
|
238
|
+
*
|
|
239
|
+
* @param tag - Tag identifier.
|
|
240
|
+
* @returns Object with activeTasks and pendingTasks counts.
|
|
241
|
+
*/
|
|
242
|
+
statsByTag(tag: string): {
|
|
243
|
+
activeTasks: number;
|
|
244
|
+
pendingTasks: number;
|
|
245
|
+
};
|
|
102
246
|
/**
|
|
103
247
|
* Retrieves real-time telemetry metrics from the scheduler.
|
|
104
248
|
*
|
|
105
|
-
* @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled,
|
|
249
|
+
* @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled, timed out tasks, pause status, and circuit state.
|
|
106
250
|
*
|
|
107
251
|
* @example
|
|
108
252
|
* ```typescript
|
|
109
253
|
* const stats = ahko.stats();
|
|
110
|
-
* console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}`);
|
|
254
|
+
* console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}, Paused: ${stats.isPaused}`);
|
|
111
255
|
* ```
|
|
112
256
|
*/
|
|
113
257
|
stats(): IAhkoStats;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IAhkoFileConfig, IAhkoProfileConfig } from "../models/config.model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Programmatically loads and activates a declarative configuration.
|
|
4
|
+
* Universal across Node.js, browsers, and edge environments.
|
|
5
|
+
*
|
|
6
|
+
* @param config - Complete configuration object conforming to `IAhkoFileConfig`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadConfig(config: IAhkoFileConfig): void;
|
|
9
|
+
/**
|
|
10
|
+
* Resets the currently active configuration in memory to undefined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resetConfig(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Asynchronously loads a `config.ahko.json` or custom config file from the filesystem in Node.js.
|
|
15
|
+
* Sets the active configuration in memory upon successful read and parse.
|
|
16
|
+
*
|
|
17
|
+
* @param filePath - Optional relative or absolute path to the configuration file (default: "config.ahko.json").
|
|
18
|
+
* @returns The parsed configuration object, or `undefined` if not running in Node.js or if file cannot be read.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadConfigFile(filePath?: string): Promise<IAhkoFileConfig | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the currently active declarative configuration, attempting auto-discovery if in Node.js.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getActiveConfig(): IAhkoFileConfig | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves a specific profile configuration by name, or the default profile if no name is provided.
|
|
27
|
+
*
|
|
28
|
+
* @param profileName - Optional name of the profile (e.g. "api", "background").
|
|
29
|
+
* @returns The profile configuration if defined, or undefined.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getProfileConfig(profileName?: string): IAhkoProfileConfig | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { loadConfig, resetConfig, loadConfigFile, getActiveConfig, getProfileConfig, } from "./config-loader.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AhkoError } from "./ahko.error.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options describing the circuit breaker open state.
|
|
4
|
+
*/
|
|
5
|
+
export interface IAhkoCircuitBreakerErrorOptions {
|
|
6
|
+
/** Time remaining in milliseconds before the circuit attempts half-open trial */
|
|
7
|
+
resetTimeoutMs?: number;
|
|
8
|
+
/** Timestamp when the circuit tripped open */
|
|
9
|
+
trippedAt?: number;
|
|
10
|
+
/** Consecutive failures that caused the trip */
|
|
11
|
+
consecutiveFailures?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Thrown when attempting to execute a task while the scheduler's circuit breaker is in OPEN state.
|
|
15
|
+
*/
|
|
16
|
+
export declare class AhkoCircuitBreakerOpenError extends AhkoError {
|
|
17
|
+
/** Time remaining in milliseconds before trial execution is allowed */
|
|
18
|
+
readonly resetTimeoutMs?: number;
|
|
19
|
+
/** Timestamp when the circuit tripped open */
|
|
20
|
+
readonly trippedAt?: number;
|
|
21
|
+
/** Total consecutive failures that caused the trip */
|
|
22
|
+
readonly consecutiveFailures?: number;
|
|
23
|
+
constructor(message?: string, options?: IAhkoCircuitBreakerErrorOptions);
|
|
24
|
+
}
|
package/dist/errors/index.d.ts
CHANGED