@push.rocks/taskbuffer 3.1.10 → 3.4.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/LICENSE +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +4 -1
- package/dist_ts/index.js +3 -1
- package/dist_ts/taskbuffer.classes.task.d.ts +43 -9
- package/dist_ts/taskbuffer.classes.task.js +105 -1
- package/dist_ts/taskbuffer.classes.taskmanager.d.ts +23 -6
- package/dist_ts/taskbuffer.classes.taskmanager.js +98 -1
- package/dist_ts/taskbuffer.classes.taskstep.d.ts +27 -0
- package/dist_ts/taskbuffer.classes.taskstep.js +37 -0
- package/dist_ts/taskbuffer.interfaces.d.ts +36 -0
- package/dist_ts/taskbuffer.interfaces.js +2 -0
- package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/ts/index.d.ts +13 -0
- package/dist_ts_web/ts/index.js +12 -0
- package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.d.ts +8 -0
- package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.js +28 -0
- package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.d.ts +13 -0
- package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.js +31 -0
- package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.d.ts +27 -0
- package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.js +5 -0
- package/dist_ts_web/ts/taskbuffer.classes.task.d.ts +86 -0
- package/dist_ts_web/ts/taskbuffer.classes.task.js +257 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskchain.d.ts +14 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskchain.js +51 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.d.ts +10 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.js +20 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskmanager.d.ts +49 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskmanager.js +208 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskonce.d.ts +11 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskonce.js +20 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskparallel.d.ts +7 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskparallel.js +23 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskrunner.d.ts +30 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskrunner.js +54 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskstep.d.ts +27 -0
- package/dist_ts_web/ts/taskbuffer.classes.taskstep.js +37 -0
- package/dist_ts_web/ts/taskbuffer.interfaces.d.ts +36 -0
- package/dist_ts_web/ts/taskbuffer.interfaces.js +2 -0
- package/dist_ts_web/ts/taskbuffer.logging.d.ts +2 -0
- package/dist_ts_web/ts/taskbuffer.logging.js +3 -0
- package/dist_ts_web/ts/taskbuffer.plugins.d.ts +8 -0
- package/dist_ts_web/ts/taskbuffer.plugins.js +9 -0
- package/dist_ts_web/ts_web/00_commitinfo_data.d.ts +8 -0
- package/dist_ts_web/ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/ts_web/demorunner.d.ts +1 -0
- package/dist_ts_web/ts_web/demorunner.js +33 -0
- package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.d.ts +2 -0
- package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.js +285 -0
- package/dist_ts_web/ts_web/index.d.ts +2 -0
- package/dist_ts_web/ts_web/index.js +3 -0
- package/dist_ts_web/ts_web/taskbuffer-dashboard.d.ts +24 -0
- package/dist_ts_web/ts_web/taskbuffer-dashboard.js +557 -0
- package/package.json +6 -5
- package/readme.md +421 -643
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +9 -1
- package/ts/taskbuffer.classes.task.ts +145 -18
- package/ts/taskbuffer.classes.taskmanager.ts +129 -9
- package/ts/taskbuffer.classes.taskstep.ts +57 -0
- package/ts/taskbuffer.interfaces.ts +39 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/elements/taskbuffer-dashboard.demo.ts +311 -0
- package/ts_web/index.ts +12 -0
- package/ts_web/taskbuffer-dashboard.ts +541 -0
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/taskbuffer',
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.4.0',
|
|
7
7
|
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
|
8
8
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
export { Task } from './taskbuffer.classes.task.js';
|
|
2
|
-
export type { ITaskFunction } from './taskbuffer.classes.task.js';
|
|
2
|
+
export type { ITaskFunction, StepNames } from './taskbuffer.classes.task.js';
|
|
3
3
|
export { Taskchain } from './taskbuffer.classes.taskchain.js';
|
|
4
4
|
export { Taskparallel } from './taskbuffer.classes.taskparallel.js';
|
|
5
5
|
export { TaskManager } from './taskbuffer.classes.taskmanager.js';
|
|
6
6
|
export { TaskOnce } from './taskbuffer.classes.taskonce.js';
|
|
7
7
|
export { TaskRunner } from './taskbuffer.classes.taskrunner.js';
|
|
8
8
|
export { TaskDebounced } from './taskbuffer.classes.taskdebounced.js';
|
|
9
|
+
|
|
10
|
+
// Task step system
|
|
11
|
+
export { TaskStep } from './taskbuffer.classes.taskstep.js';
|
|
12
|
+
export type { ITaskStep } from './taskbuffer.classes.taskstep.js';
|
|
13
|
+
|
|
14
|
+
// Metadata interfaces
|
|
15
|
+
export type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
|
|
16
|
+
|
|
9
17
|
import * as distributedCoordination from './taskbuffer.classes.distributedcoordinator.js';
|
|
10
18
|
export { distributedCoordination };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as plugins from './taskbuffer.plugins.js';
|
|
2
2
|
import { BufferRunner } from './taskbuffer.classes.bufferrunner.js';
|
|
3
3
|
import { CycleCounter } from './taskbuffer.classes.cyclecounter.js';
|
|
4
|
+
import { TaskStep, type ITaskStep } from './taskbuffer.classes.taskstep.js';
|
|
5
|
+
import type { ITaskMetadata } from './taskbuffer.interfaces.js';
|
|
4
6
|
|
|
5
7
|
import { logger } from './taskbuffer.logging.js';
|
|
6
8
|
|
|
@@ -14,18 +16,21 @@ export interface ITaskSetupFunction<T = undefined> {
|
|
|
14
16
|
|
|
15
17
|
export type TPreOrAfterTaskFunction = () => Task<any>;
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// Type helper to extract step names from array
|
|
20
|
+
export type StepNames<T> = T extends ReadonlyArray<{ name: infer N }> ? N : never;
|
|
21
|
+
|
|
22
|
+
export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []> {
|
|
23
|
+
public static extractTask<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
|
24
|
+
preOrAfterTaskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
|
25
|
+
): Task<T, TSteps> {
|
|
21
26
|
switch (true) {
|
|
22
27
|
case !preOrAfterTaskArg:
|
|
23
28
|
return null;
|
|
24
29
|
case preOrAfterTaskArg instanceof Task:
|
|
25
|
-
return preOrAfterTaskArg as Task<T>;
|
|
30
|
+
return preOrAfterTaskArg as Task<T, TSteps>;
|
|
26
31
|
case typeof preOrAfterTaskArg === 'function':
|
|
27
32
|
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
|
28
|
-
return taskFunction()
|
|
33
|
+
return taskFunction() as unknown as Task<T, TSteps>;
|
|
29
34
|
default:
|
|
30
35
|
return null;
|
|
31
36
|
}
|
|
@@ -45,9 +50,9 @@ export class Task<T = undefined> {
|
|
|
45
50
|
}
|
|
46
51
|
};
|
|
47
52
|
|
|
48
|
-
public static isTaskTouched<T = undefined>(
|
|
49
|
-
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
|
50
|
-
touchedTasksArray: Task<T>[],
|
|
53
|
+
public static isTaskTouched<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
|
54
|
+
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
|
55
|
+
touchedTasksArray: Task<T, TSteps>[],
|
|
51
56
|
): boolean {
|
|
52
57
|
const taskToCheck = Task.extractTask(taskArg);
|
|
53
58
|
let result = false;
|
|
@@ -59,9 +64,9 @@ export class Task<T = undefined> {
|
|
|
59
64
|
return result;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
public static runTask = async <T>(
|
|
63
|
-
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
|
64
|
-
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] },
|
|
67
|
+
public static runTask = async <T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
|
68
|
+
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
|
69
|
+
optionsArg: { x?: any; touchedTasksArray?: Task<T, TSteps>[] },
|
|
65
70
|
) => {
|
|
66
71
|
const taskToRun = Task.extractTask(taskArg);
|
|
67
72
|
const done = plugins.smartpromise.defer();
|
|
@@ -80,9 +85,17 @@ export class Task<T = undefined> {
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
taskToRun.running = true;
|
|
88
|
+
taskToRun.runCount++;
|
|
89
|
+
taskToRun.lastRun = new Date();
|
|
90
|
+
|
|
91
|
+
// Reset steps at the beginning of task execution
|
|
92
|
+
taskToRun.resetSteps();
|
|
83
93
|
|
|
84
94
|
done.promise.then(async () => {
|
|
85
95
|
taskToRun.running = false;
|
|
96
|
+
|
|
97
|
+
// Complete all steps when task finishes
|
|
98
|
+
taskToRun.completeAllSteps();
|
|
86
99
|
|
|
87
100
|
// When the task has finished running, resolve the finished promise
|
|
88
101
|
taskToRun.resolveFinished();
|
|
@@ -98,7 +111,7 @@ export class Task<T = undefined> {
|
|
|
98
111
|
...optionsArg,
|
|
99
112
|
};
|
|
100
113
|
const x = options.x;
|
|
101
|
-
const touchedTasksArray: Task<T>[] = options.touchedTasksArray;
|
|
114
|
+
const touchedTasksArray: Task<T, TSteps>[] = options.touchedTasksArray;
|
|
102
115
|
|
|
103
116
|
touchedTasksArray.push(taskToRun);
|
|
104
117
|
|
|
@@ -158,8 +171,8 @@ export class Task<T = undefined> {
|
|
|
158
171
|
public execDelay: number;
|
|
159
172
|
public timeout: number;
|
|
160
173
|
|
|
161
|
-
public preTask: Task<T> | TPreOrAfterTaskFunction;
|
|
162
|
-
public afterTask: Task<T> | TPreOrAfterTaskFunction;
|
|
174
|
+
public preTask: Task<T, any> | TPreOrAfterTaskFunction;
|
|
175
|
+
public afterTask: Task<T, any> | TPreOrAfterTaskFunction;
|
|
163
176
|
|
|
164
177
|
// Add a list to store the blocking tasks
|
|
165
178
|
public blockingTasks: Task[] = [];
|
|
@@ -171,6 +184,8 @@ export class Task<T = undefined> {
|
|
|
171
184
|
public running: boolean = false;
|
|
172
185
|
public bufferRunner = new BufferRunner(this);
|
|
173
186
|
public cycleCounter = new CycleCounter(this);
|
|
187
|
+
public lastRun?: Date;
|
|
188
|
+
public runCount: number = 0;
|
|
174
189
|
|
|
175
190
|
public get idle() {
|
|
176
191
|
return !this.running;
|
|
@@ -179,15 +194,22 @@ export class Task<T = undefined> {
|
|
|
179
194
|
public taskSetup: ITaskSetupFunction<T>;
|
|
180
195
|
public setupValue: T;
|
|
181
196
|
|
|
197
|
+
// Step tracking properties
|
|
198
|
+
private steps = new Map<string, TaskStep>();
|
|
199
|
+
private stepProgress = new Map<string, number>();
|
|
200
|
+
public currentStepName?: string;
|
|
201
|
+
private providedSteps?: TSteps;
|
|
202
|
+
|
|
182
203
|
constructor(optionsArg: {
|
|
183
204
|
taskFunction: ITaskFunction<T>;
|
|
184
|
-
preTask?: Task<T> | TPreOrAfterTaskFunction;
|
|
185
|
-
afterTask?: Task<T> | TPreOrAfterTaskFunction;
|
|
205
|
+
preTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
|
206
|
+
afterTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
|
186
207
|
buffered?: boolean;
|
|
187
208
|
bufferMax?: number;
|
|
188
209
|
execDelay?: number;
|
|
189
210
|
name?: string;
|
|
190
211
|
taskSetup?: ITaskSetupFunction<T>;
|
|
212
|
+
steps?: TSteps;
|
|
191
213
|
}) {
|
|
192
214
|
this.taskFunction = optionsArg.taskFunction;
|
|
193
215
|
this.preTask = optionsArg.preTask;
|
|
@@ -198,6 +220,19 @@ export class Task<T = undefined> {
|
|
|
198
220
|
this.name = optionsArg.name;
|
|
199
221
|
this.taskSetup = optionsArg.taskSetup;
|
|
200
222
|
|
|
223
|
+
// Initialize steps if provided
|
|
224
|
+
if (optionsArg.steps) {
|
|
225
|
+
this.providedSteps = optionsArg.steps;
|
|
226
|
+
for (const stepConfig of optionsArg.steps) {
|
|
227
|
+
const step = new TaskStep({
|
|
228
|
+
name: stepConfig.name,
|
|
229
|
+
description: stepConfig.description,
|
|
230
|
+
percentage: stepConfig.percentage,
|
|
231
|
+
});
|
|
232
|
+
this.steps.set(stepConfig.name, step);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
201
236
|
// Create the finished promise
|
|
202
237
|
this.finished = new Promise((resolve) => {
|
|
203
238
|
this.resolveFinished = resolve;
|
|
@@ -213,10 +248,102 @@ export class Task<T = undefined> {
|
|
|
213
248
|
}
|
|
214
249
|
|
|
215
250
|
public triggerUnBuffered(x?: any): Promise<any> {
|
|
216
|
-
return Task.runTask<T>(this, { x: x });
|
|
251
|
+
return Task.runTask<T, TSteps>(this, { x: x });
|
|
217
252
|
}
|
|
218
253
|
|
|
219
254
|
public triggerBuffered(x?: any): Promise<any> {
|
|
220
255
|
return this.bufferRunner.trigger(x);
|
|
221
256
|
}
|
|
257
|
+
|
|
258
|
+
// Step notification method with typed step names
|
|
259
|
+
public notifyStep(stepName: StepNames<TSteps>): void {
|
|
260
|
+
// Complete previous step if exists
|
|
261
|
+
if (this.currentStepName) {
|
|
262
|
+
const prevStep = this.steps.get(this.currentStepName);
|
|
263
|
+
if (prevStep && prevStep.status === 'active') {
|
|
264
|
+
prevStep.complete();
|
|
265
|
+
this.stepProgress.set(this.currentStepName, prevStep.percentage);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Start new step
|
|
270
|
+
const step = this.steps.get(stepName as string);
|
|
271
|
+
if (step) {
|
|
272
|
+
step.start();
|
|
273
|
+
this.currentStepName = stepName as string;
|
|
274
|
+
|
|
275
|
+
// Emit event for frontend updates (could be enhanced with event emitter)
|
|
276
|
+
if (this.name) {
|
|
277
|
+
logger.log('info', `Task ${this.name}: Starting step "${stepName}" - ${step.description}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Get current progress based on completed steps
|
|
283
|
+
public getProgress(): number {
|
|
284
|
+
let totalProgress = 0;
|
|
285
|
+
for (const [stepName, percentage] of this.stepProgress) {
|
|
286
|
+
totalProgress += percentage;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Add partial progress of current step if exists
|
|
290
|
+
if (this.currentStepName) {
|
|
291
|
+
const currentStep = this.steps.get(this.currentStepName);
|
|
292
|
+
if (currentStep && currentStep.status === 'active') {
|
|
293
|
+
// Could add partial progress calculation here if needed
|
|
294
|
+
// For now, we'll consider active steps as 50% complete
|
|
295
|
+
totalProgress += currentStep.percentage * 0.5;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return Math.min(100, Math.round(totalProgress));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Get all steps metadata
|
|
303
|
+
public getStepsMetadata(): ITaskStep[] {
|
|
304
|
+
return Array.from(this.steps.values()).map(step => step.toJSON());
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Get task metadata
|
|
308
|
+
public getMetadata(): ITaskMetadata {
|
|
309
|
+
return {
|
|
310
|
+
name: this.name || 'unnamed',
|
|
311
|
+
version: this.version,
|
|
312
|
+
status: this.running ? 'running' : 'idle',
|
|
313
|
+
steps: this.getStepsMetadata(),
|
|
314
|
+
currentStep: this.currentStepName,
|
|
315
|
+
currentProgress: this.getProgress(),
|
|
316
|
+
runCount: this.runCount,
|
|
317
|
+
buffered: this.buffered,
|
|
318
|
+
bufferMax: this.bufferMax,
|
|
319
|
+
timeout: this.timeout,
|
|
320
|
+
cronSchedule: this.cronJob?.cronExpression,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Reset all steps to pending state
|
|
325
|
+
public resetSteps(): void {
|
|
326
|
+
this.steps.forEach(step => step.reset());
|
|
327
|
+
this.stepProgress.clear();
|
|
328
|
+
this.currentStepName = undefined;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Complete all remaining steps (useful for cleanup)
|
|
332
|
+
private completeAllSteps(): void {
|
|
333
|
+
if (this.currentStepName) {
|
|
334
|
+
const currentStep = this.steps.get(this.currentStepName);
|
|
335
|
+
if (currentStep && currentStep.status === 'active') {
|
|
336
|
+
currentStep.complete();
|
|
337
|
+
this.stepProgress.set(this.currentStepName, currentStep.percentage);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Mark any pending steps as completed (in case of early task completion)
|
|
342
|
+
this.steps.forEach((step, name) => {
|
|
343
|
+
if (step.status === 'pending') {
|
|
344
|
+
// Don't add their percentage to progress since they weren't actually executed
|
|
345
|
+
step.status = 'completed';
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
222
349
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
AbstractDistributedCoordinator,
|
|
5
5
|
type IDistributedTaskRequestResult,
|
|
6
6
|
} from './taskbuffer.classes.distributedcoordinator.js';
|
|
7
|
+
import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
|
|
7
8
|
|
|
8
9
|
export interface ICronJob {
|
|
9
10
|
cronString: string;
|
|
@@ -17,7 +18,7 @@ export interface ITaskManagerConstructorOptions {
|
|
|
17
18
|
|
|
18
19
|
export class TaskManager {
|
|
19
20
|
public randomId = plugins.smartunique.shortId();
|
|
20
|
-
public taskMap = new plugins.lik.ObjectMap<Task
|
|
21
|
+
public taskMap = new plugins.lik.ObjectMap<Task<any, any>>();
|
|
21
22
|
private cronJobManager = new plugins.smarttime.CronManager();
|
|
22
23
|
public options: ITaskManagerConstructorOptions = {
|
|
23
24
|
distributedCoordinator: null,
|
|
@@ -27,18 +28,18 @@ export class TaskManager {
|
|
|
27
28
|
this.options = Object.assign(this.options, options);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
public getTaskByName(taskName: string): Task {
|
|
31
|
+
public getTaskByName(taskName: string): Task<any, any> {
|
|
31
32
|
return this.taskMap.findSync((task) => task.name === taskName);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
public addTask(task: Task): void {
|
|
35
|
+
public addTask(task: Task<any, any>): void {
|
|
35
36
|
if (!task.name) {
|
|
36
37
|
throw new Error('Task must have a name to be added to taskManager');
|
|
37
38
|
}
|
|
38
39
|
this.taskMap.add(task);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
public addAndScheduleTask(task: Task, cronString: string) {
|
|
42
|
+
public addAndScheduleTask(task: Task<any, any>, cronString: string) {
|
|
42
43
|
this.addTask(task);
|
|
43
44
|
this.scheduleTaskByName(task.name, cronString);
|
|
44
45
|
}
|
|
@@ -51,7 +52,7 @@ export class TaskManager {
|
|
|
51
52
|
return taskToTrigger.trigger();
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
public async triggerTask(task: Task) {
|
|
55
|
+
public async triggerTask(task: Task<any, any>) {
|
|
55
56
|
return task.trigger();
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -63,7 +64,7 @@ export class TaskManager {
|
|
|
63
64
|
this.handleTaskScheduling(taskToSchedule, cronString);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
private handleTaskScheduling(task: Task, cronString: string) {
|
|
67
|
+
private handleTaskScheduling(task: Task<any, any>, cronString: string) {
|
|
67
68
|
const cronJob = this.cronJobManager.addCronjob(
|
|
68
69
|
cronString,
|
|
69
70
|
async (triggerTime: number) => {
|
|
@@ -86,7 +87,7 @@ export class TaskManager {
|
|
|
86
87
|
task.cronJob = cronJob;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
private logTaskState(task: Task) {
|
|
90
|
+
private logTaskState(task: Task<any, any>) {
|
|
90
91
|
console.log(`Taskbuffer schedule triggered task >>${task.name}<<`);
|
|
91
92
|
const bufferState = task.buffered
|
|
92
93
|
? `buffered with max ${task.bufferMax} buffered calls`
|
|
@@ -95,7 +96,7 @@ export class TaskManager {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
private async performDistributedConsultation(
|
|
98
|
-
task: Task,
|
|
99
|
+
task: Task<any, any>,
|
|
99
100
|
triggerTime: number,
|
|
100
101
|
): Promise<IDistributedTaskRequestResult> {
|
|
101
102
|
console.log('Found a distributed coordinator, performing consultation.');
|
|
@@ -123,7 +124,7 @@ export class TaskManager {
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
public async descheduleTask(task: Task) {
|
|
127
|
+
public async descheduleTask(task: Task<any, any>) {
|
|
127
128
|
await this.descheduleTaskByName(task.name);
|
|
128
129
|
}
|
|
129
130
|
|
|
@@ -145,4 +146,123 @@ export class TaskManager {
|
|
|
145
146
|
await this.options.distributedCoordinator.stop();
|
|
146
147
|
}
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
// Get metadata for a specific task
|
|
151
|
+
public getTaskMetadata(taskName: string): ITaskMetadata | null {
|
|
152
|
+
const task = this.getTaskByName(taskName);
|
|
153
|
+
if (!task) return null;
|
|
154
|
+
return task.getMetadata();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Get metadata for all tasks
|
|
158
|
+
public getAllTasksMetadata(): ITaskMetadata[] {
|
|
159
|
+
return this.taskMap.getArray().map(task => task.getMetadata());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Get scheduled tasks with their schedules and next run times
|
|
163
|
+
public getScheduledTasks(): IScheduledTaskInfo[] {
|
|
164
|
+
const scheduledTasks: IScheduledTaskInfo[] = [];
|
|
165
|
+
|
|
166
|
+
for (const task of this.taskMap.getArray()) {
|
|
167
|
+
if (task.cronJob) {
|
|
168
|
+
scheduledTasks.push({
|
|
169
|
+
name: task.name || 'unnamed',
|
|
170
|
+
schedule: task.cronJob.cronExpression,
|
|
171
|
+
nextRun: new Date(task.cronJob.getNextExecutionTime()),
|
|
172
|
+
lastRun: task.lastRun,
|
|
173
|
+
steps: task.getStepsMetadata?.(),
|
|
174
|
+
metadata: task.getMetadata(),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return scheduledTasks;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Get next scheduled runs across all tasks
|
|
183
|
+
public getNextScheduledRuns(limit: number = 10): Array<{ taskName: string; nextRun: Date; schedule: string }> {
|
|
184
|
+
const scheduledRuns = this.getScheduledTasks()
|
|
185
|
+
.map(task => ({
|
|
186
|
+
taskName: task.name,
|
|
187
|
+
nextRun: task.nextRun,
|
|
188
|
+
schedule: task.schedule,
|
|
189
|
+
}))
|
|
190
|
+
.sort((a, b) => a.nextRun.getTime() - b.nextRun.getTime())
|
|
191
|
+
.slice(0, limit);
|
|
192
|
+
|
|
193
|
+
return scheduledRuns;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add, execute, and remove a task while collecting metadata
|
|
197
|
+
public async addExecuteRemoveTask<T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }>>(
|
|
198
|
+
task: Task<T, TSteps>,
|
|
199
|
+
options?: {
|
|
200
|
+
schedule?: string;
|
|
201
|
+
trackProgress?: boolean;
|
|
202
|
+
}
|
|
203
|
+
): Promise<ITaskExecutionReport> {
|
|
204
|
+
// Add task to manager
|
|
205
|
+
this.addTask(task);
|
|
206
|
+
|
|
207
|
+
// Optionally schedule it
|
|
208
|
+
if (options?.schedule) {
|
|
209
|
+
this.scheduleTaskByName(task.name!, options.schedule);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const startTime = Date.now();
|
|
213
|
+
const progressUpdates: Array<{ stepName: string; timestamp: number }> = [];
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
// Execute the task
|
|
217
|
+
const result = await task.trigger();
|
|
218
|
+
|
|
219
|
+
// Collect execution report
|
|
220
|
+
const report: ITaskExecutionReport = {
|
|
221
|
+
taskName: task.name || 'unnamed',
|
|
222
|
+
startTime,
|
|
223
|
+
endTime: Date.now(),
|
|
224
|
+
duration: Date.now() - startTime,
|
|
225
|
+
steps: task.getStepsMetadata(),
|
|
226
|
+
stepsCompleted: task.getStepsMetadata()
|
|
227
|
+
.filter(step => step.status === 'completed')
|
|
228
|
+
.map(step => step.name),
|
|
229
|
+
progress: task.getProgress(),
|
|
230
|
+
result,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Remove task from manager
|
|
234
|
+
this.taskMap.remove(task);
|
|
235
|
+
|
|
236
|
+
// Deschedule if it was scheduled
|
|
237
|
+
if (options?.schedule && task.name) {
|
|
238
|
+
this.descheduleTaskByName(task.name);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return report;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
// Create error report
|
|
244
|
+
const errorReport: ITaskExecutionReport = {
|
|
245
|
+
taskName: task.name || 'unnamed',
|
|
246
|
+
startTime,
|
|
247
|
+
endTime: Date.now(),
|
|
248
|
+
duration: Date.now() - startTime,
|
|
249
|
+
steps: task.getStepsMetadata(),
|
|
250
|
+
stepsCompleted: task.getStepsMetadata()
|
|
251
|
+
.filter(step => step.status === 'completed')
|
|
252
|
+
.map(step => step.name),
|
|
253
|
+
progress: task.getProgress(),
|
|
254
|
+
error: error as Error,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Remove task from manager even on error
|
|
258
|
+
this.taskMap.remove(task);
|
|
259
|
+
|
|
260
|
+
// Deschedule if it was scheduled
|
|
261
|
+
if (options?.schedule && task.name) {
|
|
262
|
+
this.descheduleTaskByName(task.name);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
throw errorReport;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
148
268
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface ITaskStep {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
percentage: number; // Weight of this step (0-100)
|
|
5
|
+
status: 'pending' | 'active' | 'completed';
|
|
6
|
+
startTime?: number;
|
|
7
|
+
endTime?: number;
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class TaskStep implements ITaskStep {
|
|
12
|
+
public name: string;
|
|
13
|
+
public description: string;
|
|
14
|
+
public percentage: number;
|
|
15
|
+
public status: 'pending' | 'active' | 'completed' = 'pending';
|
|
16
|
+
public startTime?: number;
|
|
17
|
+
public endTime?: number;
|
|
18
|
+
public duration?: number;
|
|
19
|
+
|
|
20
|
+
constructor(config: { name: string; description: string; percentage: number }) {
|
|
21
|
+
this.name = config.name;
|
|
22
|
+
this.description = config.description;
|
|
23
|
+
this.percentage = config.percentage;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public start(): void {
|
|
27
|
+
this.status = 'active';
|
|
28
|
+
this.startTime = Date.now();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public complete(): void {
|
|
32
|
+
if (this.startTime) {
|
|
33
|
+
this.endTime = Date.now();
|
|
34
|
+
this.duration = this.endTime - this.startTime;
|
|
35
|
+
}
|
|
36
|
+
this.status = 'completed';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public reset(): void {
|
|
40
|
+
this.status = 'pending';
|
|
41
|
+
this.startTime = undefined;
|
|
42
|
+
this.endTime = undefined;
|
|
43
|
+
this.duration = undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public toJSON(): ITaskStep {
|
|
47
|
+
return {
|
|
48
|
+
name: this.name,
|
|
49
|
+
description: this.description,
|
|
50
|
+
percentage: this.percentage,
|
|
51
|
+
status: this.status,
|
|
52
|
+
startTime: this.startTime,
|
|
53
|
+
endTime: this.endTime,
|
|
54
|
+
duration: this.duration,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ITaskStep } from './taskbuffer.classes.taskstep.js';
|
|
2
|
+
|
|
3
|
+
export interface ITaskMetadata {
|
|
4
|
+
name: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
status: 'idle' | 'running' | 'completed' | 'failed';
|
|
7
|
+
steps: ITaskStep[];
|
|
8
|
+
currentStep?: string;
|
|
9
|
+
currentProgress: number; // 0-100
|
|
10
|
+
lastRun?: Date;
|
|
11
|
+
nextRun?: Date; // For scheduled tasks
|
|
12
|
+
runCount: number;
|
|
13
|
+
averageDuration?: number;
|
|
14
|
+
cronSchedule?: string;
|
|
15
|
+
buffered?: boolean;
|
|
16
|
+
bufferMax?: number;
|
|
17
|
+
timeout?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ITaskExecutionReport {
|
|
21
|
+
taskName: string;
|
|
22
|
+
startTime: number;
|
|
23
|
+
endTime: number;
|
|
24
|
+
duration: number;
|
|
25
|
+
steps: ITaskStep[];
|
|
26
|
+
stepsCompleted: string[];
|
|
27
|
+
progress: number;
|
|
28
|
+
result?: any;
|
|
29
|
+
error?: Error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IScheduledTaskInfo {
|
|
33
|
+
name: string;
|
|
34
|
+
schedule: string;
|
|
35
|
+
nextRun: Date;
|
|
36
|
+
lastRun?: Date;
|
|
37
|
+
steps?: ITaskStep[];
|
|
38
|
+
metadata?: ITaskMetadata;
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autocreated commitinfo by @push.rocks/commitinfo
|
|
3
|
+
*/
|
|
4
|
+
export const commitinfo = {
|
|
5
|
+
name: '@push.rocks/taskbuffer',
|
|
6
|
+
version: '3.4.0',
|
|
7
|
+
description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
|
|
8
|
+
}
|