@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.
Files changed (66) hide show
  1. package/LICENSE +1 -1
  2. package/dist_ts/00_commitinfo_data.js +2 -2
  3. package/dist_ts/index.d.ts +4 -1
  4. package/dist_ts/index.js +3 -1
  5. package/dist_ts/taskbuffer.classes.task.d.ts +43 -9
  6. package/dist_ts/taskbuffer.classes.task.js +105 -1
  7. package/dist_ts/taskbuffer.classes.taskmanager.d.ts +23 -6
  8. package/dist_ts/taskbuffer.classes.taskmanager.js +98 -1
  9. package/dist_ts/taskbuffer.classes.taskstep.d.ts +27 -0
  10. package/dist_ts/taskbuffer.classes.taskstep.js +37 -0
  11. package/dist_ts/taskbuffer.interfaces.d.ts +36 -0
  12. package/dist_ts/taskbuffer.interfaces.js +2 -0
  13. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  14. package/dist_ts_web/00_commitinfo_data.js +9 -0
  15. package/dist_ts_web/ts/index.d.ts +13 -0
  16. package/dist_ts_web/ts/index.js +12 -0
  17. package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.d.ts +8 -0
  18. package/dist_ts_web/ts/taskbuffer.classes.bufferrunner.js +28 -0
  19. package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.d.ts +13 -0
  20. package/dist_ts_web/ts/taskbuffer.classes.cyclecounter.js +31 -0
  21. package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.d.ts +27 -0
  22. package/dist_ts_web/ts/taskbuffer.classes.distributedcoordinator.js +5 -0
  23. package/dist_ts_web/ts/taskbuffer.classes.task.d.ts +86 -0
  24. package/dist_ts_web/ts/taskbuffer.classes.task.js +257 -0
  25. package/dist_ts_web/ts/taskbuffer.classes.taskchain.d.ts +14 -0
  26. package/dist_ts_web/ts/taskbuffer.classes.taskchain.js +51 -0
  27. package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.d.ts +10 -0
  28. package/dist_ts_web/ts/taskbuffer.classes.taskdebounced.js +20 -0
  29. package/dist_ts_web/ts/taskbuffer.classes.taskmanager.d.ts +49 -0
  30. package/dist_ts_web/ts/taskbuffer.classes.taskmanager.js +208 -0
  31. package/dist_ts_web/ts/taskbuffer.classes.taskonce.d.ts +11 -0
  32. package/dist_ts_web/ts/taskbuffer.classes.taskonce.js +20 -0
  33. package/dist_ts_web/ts/taskbuffer.classes.taskparallel.d.ts +7 -0
  34. package/dist_ts_web/ts/taskbuffer.classes.taskparallel.js +23 -0
  35. package/dist_ts_web/ts/taskbuffer.classes.taskrunner.d.ts +30 -0
  36. package/dist_ts_web/ts/taskbuffer.classes.taskrunner.js +54 -0
  37. package/dist_ts_web/ts/taskbuffer.classes.taskstep.d.ts +27 -0
  38. package/dist_ts_web/ts/taskbuffer.classes.taskstep.js +37 -0
  39. package/dist_ts_web/ts/taskbuffer.interfaces.d.ts +36 -0
  40. package/dist_ts_web/ts/taskbuffer.interfaces.js +2 -0
  41. package/dist_ts_web/ts/taskbuffer.logging.d.ts +2 -0
  42. package/dist_ts_web/ts/taskbuffer.logging.js +3 -0
  43. package/dist_ts_web/ts/taskbuffer.plugins.d.ts +8 -0
  44. package/dist_ts_web/ts/taskbuffer.plugins.js +9 -0
  45. package/dist_ts_web/ts_web/00_commitinfo_data.d.ts +8 -0
  46. package/dist_ts_web/ts_web/00_commitinfo_data.js +9 -0
  47. package/dist_ts_web/ts_web/demorunner.d.ts +1 -0
  48. package/dist_ts_web/ts_web/demorunner.js +33 -0
  49. package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.d.ts +2 -0
  50. package/dist_ts_web/ts_web/elements/taskbuffer-dashboard.demo.js +285 -0
  51. package/dist_ts_web/ts_web/index.d.ts +2 -0
  52. package/dist_ts_web/ts_web/index.js +3 -0
  53. package/dist_ts_web/ts_web/taskbuffer-dashboard.d.ts +24 -0
  54. package/dist_ts_web/ts_web/taskbuffer-dashboard.js +557 -0
  55. package/package.json +6 -5
  56. package/readme.md +421 -643
  57. package/ts/00_commitinfo_data.ts +1 -1
  58. package/ts/index.ts +9 -1
  59. package/ts/taskbuffer.classes.task.ts +145 -18
  60. package/ts/taskbuffer.classes.taskmanager.ts +129 -9
  61. package/ts/taskbuffer.classes.taskstep.ts +57 -0
  62. package/ts/taskbuffer.interfaces.ts +39 -0
  63. package/ts_web/00_commitinfo_data.ts +8 -0
  64. package/ts_web/elements/taskbuffer-dashboard.demo.ts +311 -0
  65. package/ts_web/index.ts +12 -0
  66. package/ts_web/taskbuffer-dashboard.ts +541 -0
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/taskbuffer',
6
- version: '3.1.10',
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
- export class Task<T = undefined> {
18
- public static extractTask<T = undefined>(
19
- preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction,
20
- ): Task<T> {
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
+ }