@mrjacket/ahko 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +116 -28
- package/dist/ahko.d.ts +25 -1
- package/dist/index.cjs +273 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +269 -15
- package/dist/index.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/options.model.d.ts +11 -0
- package/dist/models/retry.model.d.ts +52 -0
- package/dist/models/strategy.model.d.ts +4 -2
- package/dist/retry/backoff.d.ts +18 -0
- package/dist/retry/index.d.ts +1 -0
- package/dist/scheduler/idle-scheduler.d.ts +28 -0
- package/dist/scheduler/task-queue.d.ts +16 -1
- package/dist/scheduler/task-runner.d.ts +11 -0
- package/dist/version.d.ts +1 -1
- package/package.json +24 -5
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported backoff algorithms for retry attempts.
|
|
3
|
+
*/
|
|
4
|
+
export type TRetryBackoff = "exponential" | "linear" | "none";
|
|
5
|
+
/**
|
|
6
|
+
* Predicate function determining whether a specific error warrants a retry attempt.
|
|
7
|
+
*
|
|
8
|
+
* @param error - The error thrown by the failed attempt.
|
|
9
|
+
* @param attempt - The 1-based index of the attempt that just failed.
|
|
10
|
+
* @returns Boolean or Promise<boolean> indicating whether to retry.
|
|
11
|
+
*/
|
|
12
|
+
export type TRetryPredicate = (error: unknown, attempt: number) => boolean | Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Options configuring automatic retry behavior for a scheduled task.
|
|
15
|
+
*/
|
|
16
|
+
export interface IRetryOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Total number of execution attempts allowed (initial attempt + retries).
|
|
19
|
+
* For example, `attempts: 3` means 1 initial execution plus up to 2 retries.
|
|
20
|
+
* Must be an integer greater than or equal to 1.
|
|
21
|
+
* @default 1
|
|
22
|
+
*/
|
|
23
|
+
attempts: number;
|
|
24
|
+
/**
|
|
25
|
+
* Backoff algorithm to apply between failed attempts.
|
|
26
|
+
* @default "exponential"
|
|
27
|
+
*/
|
|
28
|
+
backoff?: TRetryBackoff;
|
|
29
|
+
/**
|
|
30
|
+
* Base delay in milliseconds used as the starting multiplier for backoff.
|
|
31
|
+
* Must be a non-negative number.
|
|
32
|
+
* @default 250
|
|
33
|
+
*/
|
|
34
|
+
baseDelay?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum backoff delay cap in milliseconds to prevent unbounded growth.
|
|
37
|
+
* Must be a non-negative number greater than or equal to baseDelay.
|
|
38
|
+
* @default 10000
|
|
39
|
+
*/
|
|
40
|
+
maxDelay?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to apply full jitter randomization to the calculated delay
|
|
43
|
+
* to distribute retry waves across concurrent tasks.
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
jitter?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Optional predicate filter to evaluate whether an error is retryable.
|
|
49
|
+
* If not provided, all errors (except cancellations) trigger a retry.
|
|
50
|
+
*/
|
|
51
|
+
shouldRetry?: TRetryPredicate;
|
|
52
|
+
}
|
|
@@ -5,9 +5,11 @@ export declare enum EScheduleStrategy {
|
|
|
5
5
|
/** Execute as soon as a concurrency slot is available */
|
|
6
6
|
IMMEDIATE = "immediate",
|
|
7
7
|
/** Delay execution for a designated duration before queuing */
|
|
8
|
-
DELAY = "delay"
|
|
8
|
+
DELAY = "delay",
|
|
9
|
+
/** Execute during platform idle opportunities (requestIdleCallback in browser, setImmediate in Node.js) */
|
|
10
|
+
IDLE = "idle"
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Union type representing valid scheduling strategy identifiers.
|
|
12
14
|
*/
|
|
13
|
-
export type TScheduleStrategy = EScheduleStrategy | "immediate" | "delay";
|
|
15
|
+
export type TScheduleStrategy = EScheduleStrategy | "immediate" | "delay" | "idle";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IRetryOptions } from "../models/retry.model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default base delay for backoff calculations in milliseconds.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_BASE_DELAY = 250;
|
|
6
|
+
/**
|
|
7
|
+
* Default maximum delay ceiling for backoff calculations in milliseconds.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_MAX_DELAY = 10000;
|
|
10
|
+
/**
|
|
11
|
+
* Computes backoff delay in milliseconds for a retry attempt based on configured policy.
|
|
12
|
+
*
|
|
13
|
+
* @param attempt - 1-based index of the attempt that failed (1 for first failure, 2 for second, etc.).
|
|
14
|
+
* @param options - Retry configuration options.
|
|
15
|
+
* @param randomFn - Injectable random generator function (defaults to Math.random) for deterministic testing.
|
|
16
|
+
* @returns Delay duration in milliseconds before next attempt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function calculateBackoff(attempt: number, options?: IRetryOptions, randomFn?: () => number): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./backoff.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle returned by the IdleScheduler allowing cancellation of an idle request.
|
|
3
|
+
*/
|
|
4
|
+
export interface IIdleHandle {
|
|
5
|
+
/**
|
|
6
|
+
* Cancels the scheduled idle callback and cleans up platform resources.
|
|
7
|
+
*/
|
|
8
|
+
cancel(): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Platform-agnostic scheduler for opportunistic idle task execution.
|
|
12
|
+
*
|
|
13
|
+
* Automatically detects and selects platform capabilities:
|
|
14
|
+
* 1. Browser: `requestIdleCallback` / `cancelIdleCallback` (with optional timeout)
|
|
15
|
+
* 2. Node.js: `setImmediate` / `clearImmediate` as low-priority primitive
|
|
16
|
+
* 3. Fallback: `setTimeout(..., 0)` / `clearTimeout`
|
|
17
|
+
*/
|
|
18
|
+
export declare class IdleScheduler {
|
|
19
|
+
/**
|
|
20
|
+
* Schedules a callback to execute during the next idle opportunity.
|
|
21
|
+
*
|
|
22
|
+
* @param callback - Function to invoke when idle opportunity arises.
|
|
23
|
+
* @param timeout - Optional max deadline in milliseconds to wait before invoking (browser only).
|
|
24
|
+
* @param runtime - Target runtime scope providing scheduling primitives (defaults to globalThis).
|
|
25
|
+
* @returns An {@link IIdleHandle} with a `cancel()` method for cleanup.
|
|
26
|
+
*/
|
|
27
|
+
static schedule(callback: () => void, timeout?: number, runtime?: typeof globalThis): IIdleHandle;
|
|
28
|
+
}
|
|
@@ -14,6 +14,12 @@ export declare class TaskQueue {
|
|
|
14
14
|
private readonly activeRunners;
|
|
15
15
|
/** Set of tasks currently in delay phase */
|
|
16
16
|
private readonly delayedEntries;
|
|
17
|
+
/** Set of tasks currently awaiting an idle opportunity */
|
|
18
|
+
private readonly idleEntries;
|
|
19
|
+
/** Set of tasks currently awaiting a retry backoff timer */
|
|
20
|
+
private readonly retryEntries;
|
|
21
|
+
/** WeakMap associating task runners with their scheduling options */
|
|
22
|
+
private readonly runnerOptions;
|
|
17
23
|
/** Cumulative completed tasks counter */
|
|
18
24
|
private completedTasks;
|
|
19
25
|
/** Cumulative failed tasks counter */
|
|
@@ -44,6 +50,11 @@ export declare class TaskQueue {
|
|
|
44
50
|
* handling early cancellation safely.
|
|
45
51
|
*/
|
|
46
52
|
private scheduleDelayed;
|
|
53
|
+
/**
|
|
54
|
+
* Schedules a task to be placed into the queue during an idle opportunity,
|
|
55
|
+
* handling early cancellation safely.
|
|
56
|
+
*/
|
|
57
|
+
private scheduleIdle;
|
|
47
58
|
/**
|
|
48
59
|
* Pumps the queue by picking pending tasks and executing them
|
|
49
60
|
* as long as concurrency capacity is available.
|
|
@@ -51,9 +62,13 @@ export declare class TaskQueue {
|
|
|
51
62
|
private pump;
|
|
52
63
|
/**
|
|
53
64
|
* Internal execution of an active task runner.
|
|
54
|
-
* Settle caller promise strictly after stats and active status are updated.
|
|
55
65
|
*/
|
|
56
66
|
private executeRunner;
|
|
67
|
+
/**
|
|
68
|
+
* Schedules a retry attempt following backoff delay,
|
|
69
|
+
* without holding a concurrency slot.
|
|
70
|
+
*/
|
|
71
|
+
private scheduleRetry;
|
|
57
72
|
/**
|
|
58
73
|
* Returns telemetry snapshot for the scheduler.
|
|
59
74
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IRetryOptions } from "../models/retry.model.js";
|
|
1
2
|
import { ETaskState } from "../models/state.model.js";
|
|
2
3
|
import type { ITask } from "../models/task.model.js";
|
|
3
4
|
/**
|
|
@@ -38,6 +39,8 @@ export declare class TaskRunner<T> {
|
|
|
38
39
|
* Gets the current lifecycle state of the task.
|
|
39
40
|
*/
|
|
40
41
|
get state(): ETaskState;
|
|
42
|
+
/** Current execution attempt count (1-indexed) */
|
|
43
|
+
attempt: number;
|
|
41
44
|
/**
|
|
42
45
|
* Resolves the deferred promise.
|
|
43
46
|
*
|
|
@@ -50,6 +53,14 @@ export declare class TaskRunner<T> {
|
|
|
50
53
|
* @param reason - Reason to reject with.
|
|
51
54
|
*/
|
|
52
55
|
reject(reason: unknown): void;
|
|
56
|
+
/**
|
|
57
|
+
* Evaluates if the task should be retried following an execution failure.
|
|
58
|
+
*
|
|
59
|
+
* @param error - The error encountered during the attempt.
|
|
60
|
+
* @param retryOptions - Configured retry policy.
|
|
61
|
+
* @returns A promise resolving to true if retry should proceed, false otherwise.
|
|
62
|
+
*/
|
|
63
|
+
canRetry(error: unknown, retryOptions?: IRetryOptions): Promise<boolean>;
|
|
53
64
|
/**
|
|
54
65
|
* Executes the task within an allocated concurrency slot.
|
|
55
66
|
*
|
package/dist/version.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrjacket/ahko",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -47,16 +47,35 @@
|
|
|
47
47
|
},
|
|
48
48
|
"keywords": [
|
|
49
49
|
"scheduler",
|
|
50
|
+
"task-scheduler",
|
|
50
51
|
"queue",
|
|
51
52
|
"concurrency",
|
|
53
|
+
"concurrency-control",
|
|
52
54
|
"delay",
|
|
55
|
+
"idle",
|
|
56
|
+
"requestidlecallback",
|
|
57
|
+
"retry",
|
|
58
|
+
"backoff",
|
|
59
|
+
"exponential-backoff",
|
|
60
|
+
"jitter",
|
|
61
|
+
"throttle",
|
|
62
|
+
"debounce",
|
|
63
|
+
"rate-limit",
|
|
53
64
|
"abortsignal",
|
|
54
|
-
"
|
|
65
|
+
"abortcontroller",
|
|
66
|
+
"async",
|
|
67
|
+
"flow-control",
|
|
68
|
+
"task-runner",
|
|
69
|
+
"zero-dependencies",
|
|
70
|
+
"cooperative-scheduling",
|
|
71
|
+
"low-energy",
|
|
72
|
+
"typescript",
|
|
73
|
+
"100 kanojo"
|
|
55
74
|
],
|
|
56
75
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^
|
|
76
|
+
"@types/node": "^26.6.2",
|
|
58
77
|
"tsup": "^8.3.6",
|
|
59
|
-
"typescript": "^
|
|
60
|
-
"vitest": "^
|
|
78
|
+
"typescript": "^7.0.2",
|
|
79
|
+
"vitest": "^5.0.1"
|
|
61
80
|
}
|
|
62
81
|
}
|