@mrjacket/ahko 0.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/CHANGELOG.md +19 -0
- package/LICENSE +674 -0
- package/README.md +157 -0
- package/dist/ahko.d.ts +73 -0
- package/dist/errors/ahko.error.d.ts +12 -0
- package/dist/errors/cancellation.error.d.ts +13 -0
- package/dist/errors/configuration.error.d.ts +13 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/queue.error.d.ts +13 -0
- package/dist/errors/timeout.error.d.ts +13 -0
- package/dist/index.cjs +517 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +482 -0
- package/dist/index.js.map +1 -0
- package/dist/models/context.model.d.ts +14 -0
- package/dist/models/index.d.ts +6 -0
- package/dist/models/options.model.d.ts +33 -0
- package/dist/models/state.model.d.ts +17 -0
- package/dist/models/stats.model.d.ts +19 -0
- package/dist/models/strategy.model.d.ts +13 -0
- package/dist/models/task.model.d.ts +9 -0
- package/dist/scheduler/index.d.ts +2 -0
- package/dist/scheduler/task-queue.d.ts +63 -0
- package/dist/scheduler/task-runner.d.ts +73 -0
- package/dist/version.d.ts +4 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
// src/errors/ahko.error.ts
|
|
2
|
+
var AhkoError = class extends Error {
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new AhkoError instance.
|
|
5
|
+
*
|
|
6
|
+
* @param message - Descriptive error message.
|
|
7
|
+
* @param options - Standard Error options including cause.
|
|
8
|
+
*/
|
|
9
|
+
constructor(message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.name = "AhkoError";
|
|
12
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/errors/configuration.error.ts
|
|
17
|
+
var AhkoConfigurationError = class extends AhkoError {
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new AhkoConfigurationError.
|
|
20
|
+
*
|
|
21
|
+
* @param message - Explanation of the invalid configuration parameter.
|
|
22
|
+
* @param options - Standard Error options including cause.
|
|
23
|
+
*/
|
|
24
|
+
constructor(message, options) {
|
|
25
|
+
super(message, options);
|
|
26
|
+
this.name = "AhkoConfigurationError";
|
|
27
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/models/state.model.ts
|
|
32
|
+
var ETaskState = /* @__PURE__ */ ((ETaskState2) => {
|
|
33
|
+
ETaskState2["PENDING"] = "pending";
|
|
34
|
+
ETaskState2["RUNNING"] = "running";
|
|
35
|
+
ETaskState2["COMPLETED"] = "completed";
|
|
36
|
+
ETaskState2["FAILED"] = "failed";
|
|
37
|
+
ETaskState2["CANCELLED"] = "cancelled";
|
|
38
|
+
ETaskState2["TIMED_OUT"] = "timed_out";
|
|
39
|
+
return ETaskState2;
|
|
40
|
+
})(ETaskState || {});
|
|
41
|
+
|
|
42
|
+
// src/models/strategy.model.ts
|
|
43
|
+
var EScheduleStrategy = /* @__PURE__ */ ((EScheduleStrategy2) => {
|
|
44
|
+
EScheduleStrategy2["IMMEDIATE"] = "immediate";
|
|
45
|
+
EScheduleStrategy2["DELAY"] = "delay";
|
|
46
|
+
return EScheduleStrategy2;
|
|
47
|
+
})(EScheduleStrategy || {});
|
|
48
|
+
|
|
49
|
+
// src/scheduler/task-queue.ts
|
|
50
|
+
var TaskQueue = class {
|
|
51
|
+
/** Maximum concurrent active tasks */
|
|
52
|
+
concurrency;
|
|
53
|
+
/** Queue of pending task runners waiting for a concurrency slot */
|
|
54
|
+
queue = [];
|
|
55
|
+
/** Set of task runners currently executing */
|
|
56
|
+
activeRunners = /* @__PURE__ */ new Set();
|
|
57
|
+
/** Set of tasks currently in delay phase */
|
|
58
|
+
delayedEntries = /* @__PURE__ */ new Set();
|
|
59
|
+
/** Cumulative completed tasks counter */
|
|
60
|
+
completedTasks = 0;
|
|
61
|
+
/** Cumulative failed tasks counter */
|
|
62
|
+
failedTasks = 0;
|
|
63
|
+
/** Cumulative cancelled tasks counter */
|
|
64
|
+
cancelledTasks = 0;
|
|
65
|
+
/** Cumulative timed out tasks counter */
|
|
66
|
+
timedOutTasks = 0;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new TaskQueue.
|
|
69
|
+
*
|
|
70
|
+
* @param concurrency - Maximum concurrent tasks (defaults to Infinity).
|
|
71
|
+
* @throws {AhkoConfigurationError} If concurrency is less than 1 or not a valid number.
|
|
72
|
+
*/
|
|
73
|
+
constructor(concurrency = Infinity) {
|
|
74
|
+
if (Number.isNaN(concurrency) || concurrency < 1) {
|
|
75
|
+
throw new AhkoConfigurationError(
|
|
76
|
+
`Invalid concurrency "${concurrency}". Must be a number greater than or equal to 1.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
this.concurrency = concurrency;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Enqueues a task runner according to the specified schedule options.
|
|
83
|
+
*
|
|
84
|
+
* @template T - The return type produced by the task.
|
|
85
|
+
* @param runner - The task runner instance.
|
|
86
|
+
* @param options - Scheduling options.
|
|
87
|
+
* @returns The deferred promise associated with the task runner.
|
|
88
|
+
* @throws {AhkoConfigurationError} If scheduling options are invalid.
|
|
89
|
+
*/
|
|
90
|
+
enqueue(runner, options) {
|
|
91
|
+
const strategy = options?.strategy ?? "immediate" /* IMMEDIATE */;
|
|
92
|
+
if (strategy !== "immediate" /* IMMEDIATE */ && strategy !== "delay" /* DELAY */) {
|
|
93
|
+
throw new AhkoConfigurationError(
|
|
94
|
+
`Unsupported schedule strategy "${String(strategy)}". Supported strategies in 0.1.0: "immediate", "delay".`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (runner.state === "cancelled" /* CANCELLED */) {
|
|
98
|
+
this.cancelledTasks++;
|
|
99
|
+
return runner.promise;
|
|
100
|
+
}
|
|
101
|
+
if (strategy === "delay" /* DELAY */) {
|
|
102
|
+
const delayMs = options?.delay ?? 0;
|
|
103
|
+
if (typeof delayMs !== "number" || Number.isNaN(delayMs) || delayMs < 0) {
|
|
104
|
+
throw new AhkoConfigurationError(
|
|
105
|
+
`Invalid delay "${delayMs}". Delay must be a non-negative number in milliseconds.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
this.scheduleDelayed(runner, delayMs);
|
|
109
|
+
return runner.promise;
|
|
110
|
+
}
|
|
111
|
+
runner.onCancel = () => {
|
|
112
|
+
const index = this.queue.indexOf(runner);
|
|
113
|
+
if (index !== -1) {
|
|
114
|
+
this.queue.splice(index, 1);
|
|
115
|
+
this.cancelledTasks++;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
this.queue.push(runner);
|
|
119
|
+
this.pump();
|
|
120
|
+
return runner.promise;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Schedules a task to be placed into the queue after a delay,
|
|
124
|
+
* handling early cancellation safely.
|
|
125
|
+
*/
|
|
126
|
+
scheduleDelayed(runner, delayMs) {
|
|
127
|
+
const delayedEntry = {
|
|
128
|
+
runner,
|
|
129
|
+
timerId: setTimeout(() => {
|
|
130
|
+
this.delayedEntries.delete(delayedEntry);
|
|
131
|
+
if (runner.state === "cancelled" /* CANCELLED */) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
runner.onCancel = () => {
|
|
135
|
+
const index = this.queue.indexOf(runner);
|
|
136
|
+
if (index !== -1) {
|
|
137
|
+
this.queue.splice(index, 1);
|
|
138
|
+
this.cancelledTasks++;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
this.queue.push(runner);
|
|
142
|
+
this.pump();
|
|
143
|
+
}, delayMs)
|
|
144
|
+
};
|
|
145
|
+
this.delayedEntries.add(delayedEntry);
|
|
146
|
+
runner.onCancel = () => {
|
|
147
|
+
if (this.delayedEntries.has(delayedEntry)) {
|
|
148
|
+
clearTimeout(delayedEntry.timerId);
|
|
149
|
+
this.delayedEntries.delete(delayedEntry);
|
|
150
|
+
this.cancelledTasks++;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Pumps the queue by picking pending tasks and executing them
|
|
156
|
+
* as long as concurrency capacity is available.
|
|
157
|
+
*/
|
|
158
|
+
pump() {
|
|
159
|
+
while (this.activeRunners.size < this.concurrency && this.queue.length > 0) {
|
|
160
|
+
const runner = this.queue.shift();
|
|
161
|
+
if (!runner) {
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
if (runner.state === "cancelled" /* CANCELLED */) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
this.activeRunners.add(runner);
|
|
168
|
+
void this.executeRunner(runner);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Internal execution of an active task runner.
|
|
173
|
+
* Settle caller promise strictly after stats and active status are updated.
|
|
174
|
+
*/
|
|
175
|
+
async executeRunner(runner) {
|
|
176
|
+
try {
|
|
177
|
+
const result = await runner.run();
|
|
178
|
+
this.completedTasks++;
|
|
179
|
+
this.activeRunners.delete(runner);
|
|
180
|
+
runner.resolve(result);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (runner.state === "cancelled" /* CANCELLED */) {
|
|
183
|
+
this.cancelledTasks++;
|
|
184
|
+
} else if (runner.state === "timed_out" /* TIMED_OUT */) {
|
|
185
|
+
this.timedOutTasks++;
|
|
186
|
+
} else {
|
|
187
|
+
this.failedTasks++;
|
|
188
|
+
}
|
|
189
|
+
this.activeRunners.delete(runner);
|
|
190
|
+
runner.reject(error);
|
|
191
|
+
} finally {
|
|
192
|
+
this.pump();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns telemetry snapshot for the scheduler.
|
|
197
|
+
*
|
|
198
|
+
* @returns Frozen snapshot of current task metrics.
|
|
199
|
+
*/
|
|
200
|
+
getStats() {
|
|
201
|
+
return Object.freeze({
|
|
202
|
+
activeTasks: this.activeRunners.size,
|
|
203
|
+
pendingTasks: this.queue.length + this.delayedEntries.size,
|
|
204
|
+
completedTasks: this.completedTasks,
|
|
205
|
+
failedTasks: this.failedTasks,
|
|
206
|
+
cancelledTasks: this.cancelledTasks,
|
|
207
|
+
timedOutTasks: this.timedOutTasks,
|
|
208
|
+
capacity: this.concurrency
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/errors/cancellation.error.ts
|
|
214
|
+
var AhkoCancellationError = class extends AhkoError {
|
|
215
|
+
/**
|
|
216
|
+
* Creates a new AhkoCancellationError.
|
|
217
|
+
*
|
|
218
|
+
* @param message - Reason for cancellation.
|
|
219
|
+
* @param options - Standard Error options including cause.
|
|
220
|
+
*/
|
|
221
|
+
constructor(message = "Task was cancelled", options) {
|
|
222
|
+
super(message, options);
|
|
223
|
+
this.name = "AhkoCancellationError";
|
|
224
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/scheduler/task-runner.ts
|
|
229
|
+
var taskIdCounter = 0;
|
|
230
|
+
var TaskRunner = class {
|
|
231
|
+
/** Unique task identifier */
|
|
232
|
+
taskId;
|
|
233
|
+
/** Current lifecycle state */
|
|
234
|
+
_state = "pending" /* PENDING */;
|
|
235
|
+
/** Internal AbortController whose signal is passed to the task context */
|
|
236
|
+
abortController;
|
|
237
|
+
/** The user task function to execute */
|
|
238
|
+
task;
|
|
239
|
+
/** User-supplied AbortSignal for external cancellation */
|
|
240
|
+
externalSignal;
|
|
241
|
+
/** Abort event listener reference for clean detachment */
|
|
242
|
+
abortListener;
|
|
243
|
+
/** Promise resolve handler */
|
|
244
|
+
resolvePromise;
|
|
245
|
+
/** Promise reject handler */
|
|
246
|
+
rejectPromise;
|
|
247
|
+
/** Deferred promise exposed to the caller */
|
|
248
|
+
promise;
|
|
249
|
+
/** Callback invoked when runner is cancelled while pending */
|
|
250
|
+
onCancel;
|
|
251
|
+
/**
|
|
252
|
+
* Creates a new TaskRunner instance.
|
|
253
|
+
*
|
|
254
|
+
* @param task - The asynchronous work unit to run.
|
|
255
|
+
* @param externalSignal - Optional external AbortSignal to propagate.
|
|
256
|
+
*/
|
|
257
|
+
constructor(task, externalSignal) {
|
|
258
|
+
this.taskId = `task_${Date.now().toString(36)}_${(++taskIdCounter).toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
259
|
+
this.task = task;
|
|
260
|
+
this.externalSignal = externalSignal;
|
|
261
|
+
this.abortController = new AbortController();
|
|
262
|
+
this.promise = new Promise((resolve, reject) => {
|
|
263
|
+
this.resolvePromise = resolve;
|
|
264
|
+
this.rejectPromise = reject;
|
|
265
|
+
});
|
|
266
|
+
if (this.externalSignal) {
|
|
267
|
+
if (this.externalSignal.aborted) {
|
|
268
|
+
this._state = "cancelled" /* CANCELLED */;
|
|
269
|
+
const reason = this.externalSignal.reason;
|
|
270
|
+
const cancelError = new AhkoCancellationError(
|
|
271
|
+
typeof reason === "string" ? reason : "Task was cancelled prior to execution",
|
|
272
|
+
{ cause: reason instanceof Error ? reason : void 0 }
|
|
273
|
+
);
|
|
274
|
+
this.rejectPromise(cancelError);
|
|
275
|
+
} else {
|
|
276
|
+
this.abortListener = () => {
|
|
277
|
+
this.handleExternalAbort();
|
|
278
|
+
};
|
|
279
|
+
this.externalSignal.addEventListener("abort", this.abortListener, { once: true });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Gets the current lifecycle state of the task.
|
|
285
|
+
*/
|
|
286
|
+
get state() {
|
|
287
|
+
return this._state;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Resolves the deferred promise.
|
|
291
|
+
*
|
|
292
|
+
* @param value - Value to resolve with.
|
|
293
|
+
*/
|
|
294
|
+
resolve(value) {
|
|
295
|
+
this.resolvePromise(value);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Rejects the deferred promise.
|
|
299
|
+
*
|
|
300
|
+
* @param reason - Reason to reject with.
|
|
301
|
+
*/
|
|
302
|
+
reject(reason) {
|
|
303
|
+
this.rejectPromise(reason);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Executes the task within an allocated concurrency slot.
|
|
307
|
+
*
|
|
308
|
+
* @returns A promise resolving to the task result or rejecting on failure/cancellation.
|
|
309
|
+
*/
|
|
310
|
+
async run() {
|
|
311
|
+
if (this._state === "cancelled" /* CANCELLED */) {
|
|
312
|
+
throw new AhkoCancellationError("Task was cancelled prior to execution");
|
|
313
|
+
}
|
|
314
|
+
this._state = "running" /* RUNNING */;
|
|
315
|
+
const context = {
|
|
316
|
+
signal: this.abortController.signal,
|
|
317
|
+
taskId: this.taskId
|
|
318
|
+
};
|
|
319
|
+
try {
|
|
320
|
+
const result = await this.task(context);
|
|
321
|
+
this._state = "completed" /* COMPLETED */;
|
|
322
|
+
this.cleanup();
|
|
323
|
+
return result;
|
|
324
|
+
} catch (error) {
|
|
325
|
+
this.cleanup();
|
|
326
|
+
const isCancelled = this._state === "cancelled" /* CANCELLED */ || this.abortController.signal.aborted;
|
|
327
|
+
if (isCancelled) {
|
|
328
|
+
this._state = "cancelled" /* CANCELLED */;
|
|
329
|
+
throw new AhkoCancellationError("Task was cancelled during execution", {
|
|
330
|
+
cause: error instanceof Error ? error : void 0
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
this._state = "failed" /* FAILED */;
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Cancels the task, aborting pending or running execution.
|
|
339
|
+
*
|
|
340
|
+
* @param reason - Optional cancellation reason.
|
|
341
|
+
*/
|
|
342
|
+
cancel(reason) {
|
|
343
|
+
if (this._state === "completed" /* COMPLETED */ || this._state === "failed" /* FAILED */ || this._state === "cancelled" /* CANCELLED */ || this._state === "timed_out" /* TIMED_OUT */) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const wasPending = this._state === "pending" /* PENDING */;
|
|
347
|
+
this._state = "cancelled" /* CANCELLED */;
|
|
348
|
+
this.abortController.abort(reason);
|
|
349
|
+
this.cleanup();
|
|
350
|
+
if (wasPending) {
|
|
351
|
+
const cancellationError = new AhkoCancellationError(
|
|
352
|
+
typeof reason === "string" ? reason : "Task was cancelled prior to execution",
|
|
353
|
+
{ cause: reason instanceof Error ? reason : void 0 }
|
|
354
|
+
);
|
|
355
|
+
this.rejectPromise(cancellationError);
|
|
356
|
+
this.onCancel?.(this);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Handles external AbortSignal trigger.
|
|
361
|
+
*/
|
|
362
|
+
handleExternalAbort() {
|
|
363
|
+
this.cancel(this.externalSignal?.reason);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Detaches event listeners from external signal to guarantee memory safety.
|
|
367
|
+
*/
|
|
368
|
+
cleanup() {
|
|
369
|
+
if (this.externalSignal && this.abortListener) {
|
|
370
|
+
this.externalSignal.removeEventListener("abort", this.abortListener);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/ahko.ts
|
|
376
|
+
var Ahko = class {
|
|
377
|
+
/** Internal queue and concurrency manager */
|
|
378
|
+
queue;
|
|
379
|
+
/**
|
|
380
|
+
* Initializes a new Ahko scheduler instance.
|
|
381
|
+
*
|
|
382
|
+
* @param options - Optional scheduler configuration.
|
|
383
|
+
* @throws {AhkoConfigurationError} If concurrency is invalid (less than 1 or NaN).
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```typescript
|
|
387
|
+
* const ahko = new Ahko({ concurrency: 4 });
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
constructor(options) {
|
|
391
|
+
this.queue = new TaskQueue(options?.concurrency);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Schedules a task for execution with full return type inference.
|
|
395
|
+
*
|
|
396
|
+
* @template T - Inferred return type of the task.
|
|
397
|
+
* @param task - Asynchronous or synchronous task function accepting an {@link ITaskContext}.
|
|
398
|
+
* @param options - Task-specific scheduling options such as strategy, delay, and cancellation signal.
|
|
399
|
+
* @returns A promise that resolves with the task's return value.
|
|
400
|
+
*
|
|
401
|
+
* @throws {AhkoConfigurationError} If the task is not a function or options are invalid.
|
|
402
|
+
* @throws {AhkoCancellationError} If the task is cancelled prior to or during execution.
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* // Immediate execution (subject to concurrency)
|
|
407
|
+
* const count = await ahko.schedule(async () => 42);
|
|
408
|
+
*
|
|
409
|
+
* // Delayed execution
|
|
410
|
+
* await ahko.schedule(
|
|
411
|
+
* async ({ signal }) => doWork({ signal }),
|
|
412
|
+
* { strategy: "delay", delay: 1000 }
|
|
413
|
+
* );
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
schedule(task, options) {
|
|
417
|
+
if (typeof task !== "function") {
|
|
418
|
+
throw new AhkoConfigurationError("Task must be a valid function.");
|
|
419
|
+
}
|
|
420
|
+
const runner = new TaskRunner(task, options?.signal);
|
|
421
|
+
return this.queue.enqueue(runner, options);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Retrieves real-time telemetry metrics from the scheduler.
|
|
425
|
+
*
|
|
426
|
+
* @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled, and timed out tasks.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const stats = ahko.stats();
|
|
431
|
+
* console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}`);
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
stats() {
|
|
435
|
+
return this.queue.getStats();
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// src/version.ts
|
|
440
|
+
var VERSION = "0.1.0";
|
|
441
|
+
|
|
442
|
+
// src/errors/queue.error.ts
|
|
443
|
+
var AhkoQueueError = class extends AhkoError {
|
|
444
|
+
/**
|
|
445
|
+
* Creates a new AhkoQueueError.
|
|
446
|
+
*
|
|
447
|
+
* @param message - Explanation of the queue failure.
|
|
448
|
+
* @param options - Standard Error options including cause.
|
|
449
|
+
*/
|
|
450
|
+
constructor(message, options) {
|
|
451
|
+
super(message, options);
|
|
452
|
+
this.name = "AhkoQueueError";
|
|
453
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/errors/timeout.error.ts
|
|
458
|
+
var AhkoTimeoutError = class extends AhkoError {
|
|
459
|
+
/**
|
|
460
|
+
* Creates a new AhkoTimeoutError.
|
|
461
|
+
*
|
|
462
|
+
* @param message - Explanation of timeout expiry.
|
|
463
|
+
* @param options - Standard Error options including cause.
|
|
464
|
+
*/
|
|
465
|
+
constructor(message = "Task execution timed out", options) {
|
|
466
|
+
super(message, options);
|
|
467
|
+
this.name = "AhkoTimeoutError";
|
|
468
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
export {
|
|
472
|
+
Ahko,
|
|
473
|
+
AhkoCancellationError,
|
|
474
|
+
AhkoConfigurationError,
|
|
475
|
+
AhkoError,
|
|
476
|
+
AhkoQueueError,
|
|
477
|
+
AhkoTimeoutError,
|
|
478
|
+
EScheduleStrategy,
|
|
479
|
+
ETaskState,
|
|
480
|
+
VERSION
|
|
481
|
+
};
|
|
482
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/ahko.error.ts","../src/errors/configuration.error.ts","../src/models/state.model.ts","../src/models/strategy.model.ts","../src/scheduler/task-queue.ts","../src/errors/cancellation.error.ts","../src/scheduler/task-runner.ts","../src/ahko.ts","../src/version.ts","../src/errors/queue.error.ts","../src/errors/timeout.error.ts"],"sourcesContent":["/**\n * Base error class for all errors originating from the Ahko scheduler.\n */\nexport class AhkoError extends Error {\n /**\n * Creates a new AhkoError instance.\n *\n * @param message - Descriptive error message.\n * @param options - Standard Error options including cause.\n */\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AhkoError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { AhkoError } from \"./ahko.error.js\";\n\n/**\n * Thrown when invalid configuration or scheduling options are provided.\n */\nexport class AhkoConfigurationError extends AhkoError {\n /**\n * Creates a new AhkoConfigurationError.\n *\n * @param message - Explanation of the invalid configuration parameter.\n * @param options - Standard Error options including cause.\n */\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AhkoConfigurationError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * Represents the discrete lifecycle states of an Ahko task.\n */\nexport enum ETaskState {\n /** Task has been scheduled and is awaiting execution in queue or timer */\n PENDING = \"pending\",\n /** Task is currently executing within an allocated concurrency slot */\n RUNNING = \"running\",\n /** Task successfully finished execution */\n COMPLETED = \"completed\",\n /** Task execution threw an error or rejected */\n FAILED = \"failed\",\n /** Task was cancelled via AbortSignal before or during execution */\n CANCELLED = \"cancelled\",\n /** Task was terminated because its execution exceeded the timeout */\n TIMED_OUT = \"timed_out\",\n}\n","/**\n * Fundamental scheduling strategies supported by the Ahko scheduler.\n */\nexport enum EScheduleStrategy {\n /** Execute as soon as a concurrency slot is available */\n IMMEDIATE = \"immediate\",\n /** Delay execution for a designated duration before queuing */\n DELAY = \"delay\",\n}\n\n/**\n * Union type representing valid scheduling strategy identifiers.\n */\nexport type TScheduleStrategy = EScheduleStrategy | \"immediate\" | \"delay\";\n","import { AhkoConfigurationError } from \"../errors/configuration.error.js\";\nimport type { IScheduleOptions } from \"../models/options.model.js\";\nimport type { IAhkoStats } from \"../models/stats.model.js\";\nimport { ETaskState } from \"../models/state.model.js\";\nimport { EScheduleStrategy } from \"../models/strategy.model.js\";\nimport { TaskRunner } from \"./task-runner.js\";\n\n/**\n * Entry tracking delayed task timers for deterministic cancellation and memory cleanup.\n */\ninterface IDelayedEntry {\n runner: TaskRunner<unknown>;\n timerId: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Memory-safe FIFO task queue managing concurrency allocation,\n * delayed scheduling, and task lifecycle counters.\n */\nexport class TaskQueue {\n /** Maximum concurrent active tasks */\n public readonly concurrency: number;\n\n /** Queue of pending task runners waiting for a concurrency slot */\n private readonly queue: TaskRunner<unknown>[] = [];\n\n /** Set of task runners currently executing */\n private readonly activeRunners = new Set<TaskRunner<unknown>>();\n\n /** Set of tasks currently in delay phase */\n private readonly delayedEntries = new Set<IDelayedEntry>();\n\n /** Cumulative completed tasks counter */\n private completedTasks = 0;\n\n /** Cumulative failed tasks counter */\n private failedTasks = 0;\n\n /** Cumulative cancelled tasks counter */\n private cancelledTasks = 0;\n\n /** Cumulative timed out tasks counter */\n private timedOutTasks = 0;\n\n /**\n * Creates a new TaskQueue.\n *\n * @param concurrency - Maximum concurrent tasks (defaults to Infinity).\n * @throws {AhkoConfigurationError} If concurrency is less than 1 or not a valid number.\n */\n constructor(concurrency = Infinity) {\n if (Number.isNaN(concurrency) || concurrency < 1) {\n throw new AhkoConfigurationError(\n `Invalid concurrency \"${concurrency}\". Must be a number greater than or equal to 1.`\n );\n }\n this.concurrency = concurrency;\n }\n\n /**\n * Enqueues a task runner according to the specified schedule options.\n *\n * @template T - The return type produced by the task.\n * @param runner - The task runner instance.\n * @param options - Scheduling options.\n * @returns The deferred promise associated with the task runner.\n * @throws {AhkoConfigurationError} If scheduling options are invalid.\n */\n public enqueue<T>(runner: TaskRunner<T>, options?: IScheduleOptions): Promise<T> {\n const strategy = options?.strategy ?? EScheduleStrategy.IMMEDIATE;\n\n if (strategy !== EScheduleStrategy.IMMEDIATE && strategy !== EScheduleStrategy.DELAY) {\n throw new AhkoConfigurationError(\n `Unsupported schedule strategy \"${String(strategy)}\". Supported strategies in 0.1.0: \"immediate\", \"delay\".`\n );\n }\n\n if (runner.state === ETaskState.CANCELLED) {\n this.cancelledTasks++;\n return runner.promise;\n }\n\n if (strategy === EScheduleStrategy.DELAY) {\n const delayMs = options?.delay ?? 0;\n if (typeof delayMs !== \"number\" || Number.isNaN(delayMs) || delayMs < 0) {\n throw new AhkoConfigurationError(\n `Invalid delay \"${delayMs}\". Delay must be a non-negative number in milliseconds.`\n );\n }\n\n this.scheduleDelayed(runner as TaskRunner<unknown>, delayMs);\n return runner.promise;\n }\n\n // Attach immediate onCancel handler to dequeue without consuming concurrency\n runner.onCancel = () => {\n const index = this.queue.indexOf(runner as TaskRunner<unknown>);\n if (index !== -1) {\n this.queue.splice(index, 1);\n this.cancelledTasks++;\n }\n };\n\n // Immediate strategy: add to pending queue and pump\n this.queue.push(runner as TaskRunner<unknown>);\n this.pump();\n\n return runner.promise;\n }\n\n /**\n * Schedules a task to be placed into the queue after a delay,\n * handling early cancellation safely.\n */\n private scheduleDelayed(runner: TaskRunner<unknown>, delayMs: number): void {\n const delayedEntry: IDelayedEntry = {\n runner,\n timerId: setTimeout(() => {\n this.delayedEntries.delete(delayedEntry);\n if (runner.state === ETaskState.CANCELLED) {\n return;\n }\n\n runner.onCancel = () => {\n const index = this.queue.indexOf(runner);\n if (index !== -1) {\n this.queue.splice(index, 1);\n this.cancelledTasks++;\n }\n };\n\n this.queue.push(runner);\n this.pump();\n }, delayMs),\n };\n\n this.delayedEntries.add(delayedEntry);\n\n runner.onCancel = () => {\n if (this.delayedEntries.has(delayedEntry)) {\n clearTimeout(delayedEntry.timerId);\n this.delayedEntries.delete(delayedEntry);\n this.cancelledTasks++;\n }\n };\n }\n\n /**\n * Pumps the queue by picking pending tasks and executing them\n * as long as concurrency capacity is available.\n */\n private pump(): void {\n while (this.activeRunners.size < this.concurrency && this.queue.length > 0) {\n const runner = this.queue.shift();\n if (!runner) {\n break;\n }\n\n if (runner.state === ETaskState.CANCELLED) {\n continue;\n }\n\n this.activeRunners.add(runner);\n\n // Execute runner without unhandled rejection risk\n void this.executeRunner(runner);\n }\n }\n\n /**\n * Internal execution of an active task runner.\n * Settle caller promise strictly after stats and active status are updated.\n */\n private async executeRunner(runner: TaskRunner<unknown>): Promise<void> {\n try {\n const result = await runner.run();\n this.completedTasks++;\n this.activeRunners.delete(runner);\n runner.resolve(result);\n } catch (error) {\n if (runner.state === ETaskState.CANCELLED) {\n this.cancelledTasks++;\n } else if (runner.state === ETaskState.TIMED_OUT) {\n this.timedOutTasks++;\n } else {\n this.failedTasks++;\n }\n this.activeRunners.delete(runner);\n runner.reject(error);\n } finally {\n this.pump();\n }\n }\n\n /**\n * Returns telemetry snapshot for the scheduler.\n *\n * @returns Frozen snapshot of current task metrics.\n */\n public getStats(): IAhkoStats {\n return Object.freeze({\n activeTasks: this.activeRunners.size,\n pendingTasks: this.queue.length + this.delayedEntries.size,\n completedTasks: this.completedTasks,\n failedTasks: this.failedTasks,\n cancelledTasks: this.cancelledTasks,\n timedOutTasks: this.timedOutTasks,\n capacity: this.concurrency,\n });\n }\n}\n","import { AhkoError } from \"./ahko.error.js\";\n\n/**\n * Thrown when a task is cancelled before or during execution.\n */\nexport class AhkoCancellationError extends AhkoError {\n /**\n * Creates a new AhkoCancellationError.\n *\n * @param message - Reason for cancellation.\n * @param options - Standard Error options including cause.\n */\n constructor(message = \"Task was cancelled\", options?: ErrorOptions) {\n super(message, options);\n this.name = \"AhkoCancellationError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { AhkoCancellationError } from \"../errors/cancellation.error.js\";\nimport type { ITaskContext } from \"../models/context.model.js\";\nimport { ETaskState } from \"../models/state.model.js\";\nimport type { ITask } from \"../models/task.model.js\";\n\nlet taskIdCounter = 0;\n\n/**\n * Internal task lifecycle manager responsible for execution, state transitions,\n * AbortSignal coordination, and deterministic resource cleanup.\n *\n * @template T - The return type produced by the underlying task.\n */\nexport class TaskRunner<T> {\n /** Unique task identifier */\n public readonly taskId: string;\n\n /** Current lifecycle state */\n private _state: ETaskState = ETaskState.PENDING;\n\n /** Internal AbortController whose signal is passed to the task context */\n private readonly abortController: AbortController;\n\n /** The user task function to execute */\n private readonly task: ITask<T>;\n\n /** User-supplied AbortSignal for external cancellation */\n private readonly externalSignal?: AbortSignal;\n\n /** Abort event listener reference for clean detachment */\n private readonly abortListener?: () => void;\n\n /** Promise resolve handler */\n private resolvePromise!: (value: T | PromiseLike<T>) => void;\n\n /** Promise reject handler */\n private rejectPromise!: (reason?: unknown) => void;\n\n /** Deferred promise exposed to the caller */\n public readonly promise: Promise<T>;\n\n /** Callback invoked when runner is cancelled while pending */\n public onCancel?: (runner: TaskRunner<T>) => void;\n\n /**\n * Creates a new TaskRunner instance.\n *\n * @param task - The asynchronous work unit to run.\n * @param externalSignal - Optional external AbortSignal to propagate.\n */\n constructor(task: ITask<T>, externalSignal?: AbortSignal) {\n this.taskId = `task_${Date.now().toString(36)}_${(++taskIdCounter).toString(36)}_${Math.random().toString(36).slice(2, 6)}`;\n this.task = task;\n this.externalSignal = externalSignal;\n this.abortController = new AbortController();\n\n this.promise = new Promise<T>((resolve, reject) => {\n this.resolvePromise = resolve;\n this.rejectPromise = reject;\n });\n\n if (this.externalSignal) {\n if (this.externalSignal.aborted) {\n this._state = ETaskState.CANCELLED;\n const reason = this.externalSignal.reason;\n const cancelError = new AhkoCancellationError(\n typeof reason === \"string\" ? reason : \"Task was cancelled prior to execution\",\n { cause: reason instanceof Error ? reason : undefined }\n );\n this.rejectPromise(cancelError);\n } else {\n this.abortListener = () => {\n this.handleExternalAbort();\n };\n this.externalSignal.addEventListener(\"abort\", this.abortListener, { once: true });\n }\n }\n }\n\n /**\n * Gets the current lifecycle state of the task.\n */\n public get state(): ETaskState {\n return this._state;\n }\n\n /**\n * Resolves the deferred promise.\n *\n * @param value - Value to resolve with.\n */\n public resolve(value: T): void {\n this.resolvePromise(value);\n }\n\n /**\n * Rejects the deferred promise.\n *\n * @param reason - Reason to reject with.\n */\n public reject(reason: unknown): void {\n this.rejectPromise(reason);\n }\n\n /**\n * Executes the task within an allocated concurrency slot.\n *\n * @returns A promise resolving to the task result or rejecting on failure/cancellation.\n */\n public async run(): Promise<T> {\n if (this._state === ETaskState.CANCELLED) {\n throw new AhkoCancellationError(\"Task was cancelled prior to execution\");\n }\n\n this._state = ETaskState.RUNNING;\n\n const context: ITaskContext = {\n signal: this.abortController.signal,\n taskId: this.taskId,\n };\n\n try {\n const result = await this.task(context);\n this._state = ETaskState.COMPLETED;\n this.cleanup();\n return result;\n } catch (error) {\n this.cleanup();\n\n const isCancelled =\n (this._state as ETaskState) === ETaskState.CANCELLED ||\n this.abortController.signal.aborted;\n\n if (isCancelled) {\n this._state = ETaskState.CANCELLED;\n throw new AhkoCancellationError(\"Task was cancelled during execution\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n this._state = ETaskState.FAILED;\n throw error;\n }\n }\n\n /**\n * Cancels the task, aborting pending or running execution.\n *\n * @param reason - Optional cancellation reason.\n */\n public cancel(reason?: unknown): void {\n if (\n this._state === ETaskState.COMPLETED ||\n this._state === ETaskState.FAILED ||\n this._state === ETaskState.CANCELLED ||\n this._state === ETaskState.TIMED_OUT\n ) {\n return;\n }\n\n const wasPending = this._state === ETaskState.PENDING;\n this._state = ETaskState.CANCELLED;\n this.abortController.abort(reason);\n this.cleanup();\n\n if (wasPending) {\n const cancellationError = new AhkoCancellationError(\n typeof reason === \"string\" ? reason : \"Task was cancelled prior to execution\",\n { cause: reason instanceof Error ? reason : undefined }\n );\n this.rejectPromise(cancellationError);\n this.onCancel?.(this);\n }\n }\n\n /**\n * Handles external AbortSignal trigger.\n */\n private handleExternalAbort(): void {\n this.cancel(this.externalSignal?.reason);\n }\n\n /**\n * Detaches event listeners from external signal to guarantee memory safety.\n */\n public cleanup(): void {\n if (this.externalSignal && this.abortListener) {\n this.externalSignal.removeEventListener(\"abort\", this.abortListener);\n }\n }\n}\n","import { AhkoConfigurationError } from \"./errors/configuration.error.js\";\nimport type { IAhkoOptions, IScheduleOptions } from \"./models/options.model.js\";\nimport type { IAhkoStats } from \"./models/stats.model.js\";\nimport type { ITask } from \"./models/task.model.js\";\nimport { TaskQueue } from \"./scheduler/task-queue.js\";\nimport { TaskRunner } from \"./scheduler/task-runner.js\";\n\n/**\n * Ahko — Low-energy, production-grade asynchronous task scheduler.\n *\n * Coordinates execution timing, enforces concurrency limits, and cooperates\n * natively with AbortSignal cancellation.\n *\n * @example\n * ```typescript\n * import { Ahko } from \"@mrjacket/ahko\";\n *\n * const ahko = new Ahko({ concurrency: 2 });\n *\n * const result = await ahko.schedule(async ({ signal, taskId }) => {\n * const res = await fetch(\"https://api.example.com\", { signal });\n * return res.json();\n * });\n * ```\n */\nexport class Ahko {\n /** Internal queue and concurrency manager */\n private readonly queue: TaskQueue;\n\n /**\n * Initializes a new Ahko scheduler instance.\n *\n * @param options - Optional scheduler configuration.\n * @throws {AhkoConfigurationError} If concurrency is invalid (less than 1 or NaN).\n *\n * @example\n * ```typescript\n * const ahko = new Ahko({ concurrency: 4 });\n * ```\n */\n constructor(options?: IAhkoOptions) {\n this.queue = new TaskQueue(options?.concurrency);\n }\n\n /**\n * Schedules a task for execution with full return type inference.\n *\n * @template T - Inferred return type of the task.\n * @param task - Asynchronous or synchronous task function accepting an {@link ITaskContext}.\n * @param options - Task-specific scheduling options such as strategy, delay, and cancellation signal.\n * @returns A promise that resolves with the task's return value.\n *\n * @throws {AhkoConfigurationError} If the task is not a function or options are invalid.\n * @throws {AhkoCancellationError} If the task is cancelled prior to or during execution.\n *\n * @example\n * ```typescript\n * // Immediate execution (subject to concurrency)\n * const count = await ahko.schedule(async () => 42);\n *\n * // Delayed execution\n * await ahko.schedule(\n * async ({ signal }) => doWork({ signal }),\n * { strategy: \"delay\", delay: 1000 }\n * );\n * ```\n */\n public schedule<T>(task: ITask<T>, options?: IScheduleOptions): Promise<T> {\n if (typeof task !== \"function\") {\n throw new AhkoConfigurationError(\"Task must be a valid function.\");\n }\n\n const runner = new TaskRunner<T>(task, options?.signal);\n return this.queue.enqueue(runner, options);\n }\n\n /**\n * Retrieves real-time telemetry metrics from the scheduler.\n *\n * @returns An {@link IAhkoStats} snapshot of active, pending, completed, failed, cancelled, and timed out tasks.\n *\n * @example\n * ```typescript\n * const stats = ahko.stats();\n * console.log(`Active: ${stats.activeTasks}, Pending: ${stats.pendingTasks}`);\n * ```\n */\n public stats(): IAhkoStats {\n return this.queue.getStats();\n }\n}\n","/**\n * Current version of @mrjacket/ahko package.\n */\nexport const VERSION = \"0.1.0\";\n","import { AhkoError } from \"./ahko.error.js\";\n\n/**\n * Thrown when an internal queue invariant is violated or queue limits are breached.\n */\nexport class AhkoQueueError extends AhkoError {\n /**\n * Creates a new AhkoQueueError.\n *\n * @param message - Explanation of the queue failure.\n * @param options - Standard Error options including cause.\n */\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AhkoQueueError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { AhkoError } from \"./ahko.error.js\";\n\n/**\n * Thrown when a task exceeds its allotted timeout duration.\n */\nexport class AhkoTimeoutError extends AhkoError {\n /**\n * Creates a new AhkoTimeoutError.\n *\n * @param message - Explanation of timeout expiry.\n * @param options - Standard Error options including cause.\n */\n constructor(message = \"Task execution timed out\", options?: ErrorOptions) {\n super(message, options);\n this.name = \"AhkoTimeoutError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"],"mappings":";AAGO,IAAM,YAAN,cAAwB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACVO,IAAM,yBAAN,cAAqC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpD,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACdO,IAAK,aAAL,kBAAKA,gBAAL;AAEL,EAAAA,YAAA,aAAU;AAEV,EAAAA,YAAA,aAAU;AAEV,EAAAA,YAAA,eAAY;AAEZ,EAAAA,YAAA,YAAS;AAET,EAAAA,YAAA,eAAY;AAEZ,EAAAA,YAAA,eAAY;AAZF,SAAAA;AAAA,GAAA;;;ACAL,IAAK,oBAAL,kBAAKC,uBAAL;AAEL,EAAAA,mBAAA,eAAY;AAEZ,EAAAA,mBAAA,WAAQ;AAJE,SAAAA;AAAA,GAAA;;;ACgBL,IAAM,YAAN,MAAgB;AAAA;AAAA,EAEL;AAAA;AAAA,EAGC,QAA+B,CAAC;AAAA;AAAA,EAGhC,gBAAgB,oBAAI,IAAyB;AAAA;AAAA,EAG7C,iBAAiB,oBAAI,IAAmB;AAAA;AAAA,EAGjD,iBAAiB;AAAA;AAAA,EAGjB,cAAc;AAAA;AAAA,EAGd,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxB,YAAY,cAAc,UAAU;AAClC,QAAI,OAAO,MAAM,WAAW,KAAK,cAAc,GAAG;AAChD,YAAM,IAAI;AAAA,QACR,wBAAwB,WAAW;AAAA,MACrC;AAAA,IACF;AACA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWO,QAAW,QAAuB,SAAwC;AAC/E,UAAM,WAAW,SAAS;AAE1B,QAAI,4CAA4C,kCAAsC;AACpF,YAAM,IAAI;AAAA,QACR,kCAAkC,OAAO,QAAQ,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,QAAI,OAAO,uCAAgC;AACzC,WAAK;AACL,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,kCAAsC;AACxC,YAAM,UAAU,SAAS,SAAS;AAClC,UAAI,OAAO,YAAY,YAAY,OAAO,MAAM,OAAO,KAAK,UAAU,GAAG;AACvE,cAAM,IAAI;AAAA,UACR,kBAAkB,OAAO;AAAA,QAC3B;AAAA,MACF;AAEA,WAAK,gBAAgB,QAA+B,OAAO;AAC3D,aAAO,OAAO;AAAA,IAChB;AAGA,WAAO,WAAW,MAAM;AACtB,YAAM,QAAQ,KAAK,MAAM,QAAQ,MAA6B;AAC9D,UAAI,UAAU,IAAI;AAChB,aAAK,MAAM,OAAO,OAAO,CAAC;AAC1B,aAAK;AAAA,MACP;AAAA,IACF;AAGA,SAAK,MAAM,KAAK,MAA6B;AAC7C,SAAK,KAAK;AAEV,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgB,QAA6B,SAAuB;AAC1E,UAAM,eAA8B;AAAA,MAClC;AAAA,MACA,SAAS,WAAW,MAAM;AACxB,aAAK,eAAe,OAAO,YAAY;AACvC,YAAI,OAAO,uCAAgC;AACzC;AAAA,QACF;AAEA,eAAO,WAAW,MAAM;AACtB,gBAAM,QAAQ,KAAK,MAAM,QAAQ,MAAM;AACvC,cAAI,UAAU,IAAI;AAChB,iBAAK,MAAM,OAAO,OAAO,CAAC;AAC1B,iBAAK;AAAA,UACP;AAAA,QACF;AAEA,aAAK,MAAM,KAAK,MAAM;AACtB,aAAK,KAAK;AAAA,MACZ,GAAG,OAAO;AAAA,IACZ;AAEA,SAAK,eAAe,IAAI,YAAY;AAEpC,WAAO,WAAW,MAAM;AACtB,UAAI,KAAK,eAAe,IAAI,YAAY,GAAG;AACzC,qBAAa,aAAa,OAAO;AACjC,aAAK,eAAe,OAAO,YAAY;AACvC,aAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAa;AACnB,WAAO,KAAK,cAAc,OAAO,KAAK,eAAe,KAAK,MAAM,SAAS,GAAG;AAC1E,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AAEA,UAAI,OAAO,uCAAgC;AACzC;AAAA,MACF;AAEA,WAAK,cAAc,IAAI,MAAM;AAG7B,WAAK,KAAK,cAAc,MAAM;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cAAc,QAA4C;AACtE,QAAI;AACF,YAAM,SAAS,MAAM,OAAO,IAAI;AAChC,WAAK;AACL,WAAK,cAAc,OAAO,MAAM;AAChC,aAAO,QAAQ,MAAM;AAAA,IACvB,SAAS,OAAO;AACd,UAAI,OAAO,uCAAgC;AACzC,aAAK;AAAA,MACP,WAAW,OAAO,uCAAgC;AAChD,aAAK;AAAA,MACP,OAAO;AACL,aAAK;AAAA,MACP;AACA,WAAK,cAAc,OAAO,MAAM;AAChC,aAAO,OAAO,KAAK;AAAA,IACrB,UAAE;AACA,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,WAAuB;AAC5B,WAAO,OAAO,OAAO;AAAA,MACnB,aAAa,KAAK,cAAc;AAAA,MAChC,cAAc,KAAK,MAAM,SAAS,KAAK,eAAe;AAAA,MACtD,gBAAgB,KAAK;AAAA,MACrB,aAAa,KAAK;AAAA,MAClB,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK;AAAA,MACpB,UAAU,KAAK;AAAA,IACjB,CAAC;AAAA,EACH;AACF;;;AC7MO,IAAM,wBAAN,cAAoC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnD,YAAY,UAAU,sBAAsB,SAAwB;AAClE,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACZA,IAAI,gBAAgB;AAQb,IAAM,aAAN,MAAoB;AAAA;AAAA,EAET;AAAA;AAAA,EAGR;AAAA;AAAA,EAGS;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGT;AAAA;AAAA,EAGA;AAAA;AAAA,EAGQ;AAAA;AAAA,EAGT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQP,YAAY,MAAgB,gBAA8B;AACxD,SAAK,SAAS,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,eAAe,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACzH,SAAK,OAAO;AACZ,SAAK,iBAAiB;AACtB,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,SAAK,UAAU,IAAI,QAAW,CAAC,SAAS,WAAW;AACjD,WAAK,iBAAiB;AACtB,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAED,QAAI,KAAK,gBAAgB;AACvB,UAAI,KAAK,eAAe,SAAS;AAC/B,aAAK;AACL,cAAM,SAAS,KAAK,eAAe;AACnC,cAAM,cAAc,IAAI;AAAA,UACtB,OAAO,WAAW,WAAW,SAAS;AAAA,UACtC,EAAE,OAAO,kBAAkB,QAAQ,SAAS,OAAU;AAAA,QACxD;AACA,aAAK,cAAc,WAAW;AAAA,MAChC,OAAO;AACL,aAAK,gBAAgB,MAAM;AACzB,eAAK,oBAAoB;AAAA,QAC3B;AACA,aAAK,eAAe,iBAAiB,SAAS,KAAK,eAAe,EAAE,MAAM,KAAK,CAAC;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAW,QAAoB;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,QAAQ,OAAgB;AAC7B,SAAK,eAAe,KAAK;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAO,QAAuB;AACnC,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,MAAkB;AAC7B,QAAI,KAAK,wCAAiC;AACxC,YAAM,IAAI,sBAAsB,uCAAuC;AAAA,IACzE;AAEA,SAAK;AAEL,UAAM,UAAwB;AAAA,MAC5B,QAAQ,KAAK,gBAAgB;AAAA,MAC7B,QAAQ,KAAK;AAAA,IACf;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAAK,OAAO;AACtC,WAAK;AACL,WAAK,QAAQ;AACb,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,QAAQ;AAEb,YAAM,cACH,KAAK,0CACN,KAAK,gBAAgB,OAAO;AAE9B,UAAI,aAAa;AACf,aAAK;AACL,cAAM,IAAI,sBAAsB,uCAAuC;AAAA,UACrE,OAAO,iBAAiB,QAAQ,QAAQ;AAAA,QAC1C,CAAC;AAAA,MACH;AAEA,WAAK;AACL,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAO,QAAwB;AACpC,QACE,KAAK,0CACL,KAAK,oCACL,KAAK,0CACL,KAAK,wCACL;AACA;AAAA,IACF;AAEA,UAAM,aAAa,KAAK;AACxB,SAAK;AACL,SAAK,gBAAgB,MAAM,MAAM;AACjC,SAAK,QAAQ;AAEb,QAAI,YAAY;AACd,YAAM,oBAAoB,IAAI;AAAA,QAC5B,OAAO,WAAW,WAAW,SAAS;AAAA,QACtC,EAAE,OAAO,kBAAkB,QAAQ,SAAS,OAAU;AAAA,MACxD;AACA,WAAK,cAAc,iBAAiB;AACpC,WAAK,WAAW,IAAI;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,SAAK,OAAO,KAAK,gBAAgB,MAAM;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKO,UAAgB;AACrB,QAAI,KAAK,kBAAkB,KAAK,eAAe;AAC7C,WAAK,eAAe,oBAAoB,SAAS,KAAK,aAAa;AAAA,IACrE;AAAA,EACF;AACF;;;ACrKO,IAAM,OAAN,MAAW;AAAA;AAAA,EAEC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAajB,YAAY,SAAwB;AAClC,SAAK,QAAQ,IAAI,UAAU,SAAS,WAAW;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBO,SAAY,MAAgB,SAAwC;AACzE,QAAI,OAAO,SAAS,YAAY;AAC9B,YAAM,IAAI,uBAAuB,gCAAgC;AAAA,IACnE;AAEA,UAAM,SAAS,IAAI,WAAc,MAAM,SAAS,MAAM;AACtD,WAAO,KAAK,MAAM,QAAQ,QAAQ,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaO,QAAoB;AACzB,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AACF;;;ACvFO,IAAM,UAAU;;;ACEhB,IAAM,iBAAN,cAA6B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO5C,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACZO,IAAM,mBAAN,cAA+B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9C,YAAY,UAAU,4BAA4B,SAAwB;AACxE,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;","names":["ETaskState","EScheduleStrategy"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution context supplied to every scheduled Ahko task.
|
|
3
|
+
*/
|
|
4
|
+
export interface ITaskContext {
|
|
5
|
+
/**
|
|
6
|
+
* Cooperative cancellation signal for the task.
|
|
7
|
+
* Listeners should be attached to this signal to gracefully abort asynchronous operations.
|
|
8
|
+
*/
|
|
9
|
+
readonly signal: AbortSignal;
|
|
10
|
+
/**
|
|
11
|
+
* Unique identifier assigned to the task by the scheduler.
|
|
12
|
+
*/
|
|
13
|
+
readonly taskId: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { TScheduleStrategy } from "./strategy.model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options to configure a specific scheduled task.
|
|
4
|
+
*/
|
|
5
|
+
export interface IScheduleOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Scheduling strategy to use.
|
|
8
|
+
* @default "immediate"
|
|
9
|
+
*/
|
|
10
|
+
strategy?: TScheduleStrategy;
|
|
11
|
+
/**
|
|
12
|
+
* Delay in milliseconds before queuing or executing the task.
|
|
13
|
+
* Applicable when strategy is "delay".
|
|
14
|
+
*/
|
|
15
|
+
delay?: number;
|
|
16
|
+
/**
|
|
17
|
+
* External cancellation signal.
|
|
18
|
+
* If aborted before start, the task is removed from the queue without execution.
|
|
19
|
+
* If aborted while running, the abort event is propagated to the task context signal.
|
|
20
|
+
*/
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Global configuration options for the Ahko scheduler instance.
|
|
25
|
+
*/
|
|
26
|
+
export interface IAhkoOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Maximum number of tasks allowed to execute concurrently.
|
|
29
|
+
* Must be an integer greater than or equal to 1, or Infinity.
|
|
30
|
+
* @default Infinity
|
|
31
|
+
*/
|
|
32
|
+
concurrency?: number;
|
|
33
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the discrete lifecycle states of an Ahko task.
|
|
3
|
+
*/
|
|
4
|
+
export declare enum ETaskState {
|
|
5
|
+
/** Task has been scheduled and is awaiting execution in queue or timer */
|
|
6
|
+
PENDING = "pending",
|
|
7
|
+
/** Task is currently executing within an allocated concurrency slot */
|
|
8
|
+
RUNNING = "running",
|
|
9
|
+
/** Task successfully finished execution */
|
|
10
|
+
COMPLETED = "completed",
|
|
11
|
+
/** Task execution threw an error or rejected */
|
|
12
|
+
FAILED = "failed",
|
|
13
|
+
/** Task was cancelled via AbortSignal before or during execution */
|
|
14
|
+
CANCELLED = "cancelled",
|
|
15
|
+
/** Task was terminated because its execution exceeded the timeout */
|
|
16
|
+
TIMED_OUT = "timed_out"
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry snapshot of the Ahko scheduler.
|
|
3
|
+
*/
|
|
4
|
+
export interface IAhkoStats {
|
|
5
|
+
/** Number of tasks currently executing in a concurrency slot */
|
|
6
|
+
activeTasks: number;
|
|
7
|
+
/** Number of tasks waiting in queue or in delay phase */
|
|
8
|
+
pendingTasks: number;
|
|
9
|
+
/** Cumulative count of successfully completed tasks */
|
|
10
|
+
completedTasks: number;
|
|
11
|
+
/** Cumulative count of failed tasks */
|
|
12
|
+
failedTasks: number;
|
|
13
|
+
/** Cumulative count of cancelled tasks */
|
|
14
|
+
cancelledTasks: number;
|
|
15
|
+
/** Cumulative count of timed out tasks */
|
|
16
|
+
timedOutTasks: number;
|
|
17
|
+
/** Maximum concurrent execution capacity */
|
|
18
|
+
capacity: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fundamental scheduling strategies supported by the Ahko scheduler.
|
|
3
|
+
*/
|
|
4
|
+
export declare enum EScheduleStrategy {
|
|
5
|
+
/** Execute as soon as a concurrency slot is available */
|
|
6
|
+
IMMEDIATE = "immediate",
|
|
7
|
+
/** Delay execution for a designated duration before queuing */
|
|
8
|
+
DELAY = "delay"
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Union type representing valid scheduling strategy identifiers.
|
|
12
|
+
*/
|
|
13
|
+
export type TScheduleStrategy = EScheduleStrategy | "immediate" | "delay";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ITaskContext } from "./context.model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Represents an asynchronous or synchronous unit of work managed by Ahko.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The return type produced by the task.
|
|
6
|
+
* @param context - The execution context including cancellation signal and task ID.
|
|
7
|
+
* @returns The resolved value or a Promise resolving to the value.
|
|
8
|
+
*/
|
|
9
|
+
export type ITask<T> = (context: ITaskContext) => Promise<T> | T;
|