@push.rocks/taskbuffer 4.0.0 → 4.1.1

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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/taskbuffer',
6
- version: '4.0.0',
6
+ version: '4.1.1',
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
@@ -12,7 +12,7 @@ export { TaskStep } from './taskbuffer.classes.taskstep.js';
12
12
  export type { ITaskStep } from './taskbuffer.classes.taskstep.js';
13
13
 
14
14
  // Metadata interfaces
15
- export type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
15
+ export type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo, ITaskEvent, TTaskEventType } from './taskbuffer.interfaces.js';
16
16
 
17
17
  import * as distributedCoordination from './taskbuffer.classes.distributedcoordinator.js';
18
18
  export { distributedCoordination };
@@ -2,7 +2,7 @@ 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
4
  import { TaskStep, type ITaskStep } from './taskbuffer.classes.taskstep.js';
5
- import type { ITaskMetadata } from './taskbuffer.interfaces.js';
5
+ import type { ITaskMetadata, ITaskEvent, TTaskEventType } from './taskbuffer.interfaces.js';
6
6
 
7
7
  import { logger } from './taskbuffer.logging.js';
8
8
 
@@ -91,6 +91,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
91
91
  // Reset steps and error state at the beginning of task execution
92
92
  taskToRun.resetSteps();
93
93
  taskToRun.lastError = undefined;
94
+ taskToRun.emitEvent('started');
94
95
 
95
96
  done.promise
96
97
  .then(async () => {
@@ -98,6 +99,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
98
99
 
99
100
  // Complete all steps when task finishes
100
101
  taskToRun.completeAllSteps();
102
+ taskToRun.emitEvent(taskToRun.lastError ? 'failed' : 'completed');
101
103
 
102
104
  // When the task has finished running, resolve the finished promise
103
105
  taskToRun.resolveFinished();
@@ -109,6 +111,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
109
111
  })
110
112
  .catch((err) => {
111
113
  taskToRun.running = false;
114
+ taskToRun.emitEvent('failed', { error: err instanceof Error ? err.message : String(err) });
112
115
 
113
116
  // Resolve finished so blocking dependants don't hang
114
117
  taskToRun.resolveFinished();
@@ -218,6 +221,8 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
218
221
  public catchErrors: boolean = false;
219
222
  public lastError?: Error;
220
223
  public errorCount: number = 0;
224
+ public labels: Record<string, string> = {};
225
+ public readonly eventSubject = new plugins.smartrx.rxjs.Subject<ITaskEvent>();
221
226
 
222
227
  public get idle() {
223
228
  return !this.running;
@@ -227,6 +232,38 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
227
232
  this.lastError = undefined;
228
233
  }
229
234
 
235
+ public setLabel(key: string, value: string): void {
236
+ this.labels[key] = value;
237
+ }
238
+
239
+ public getLabel(key: string): string | undefined {
240
+ return this.labels[key];
241
+ }
242
+
243
+ public removeLabel(key: string): boolean {
244
+ if (key in this.labels) {
245
+ delete this.labels[key];
246
+ return true;
247
+ }
248
+ return false;
249
+ }
250
+
251
+ public hasLabel(key: string, value?: string): boolean {
252
+ if (value !== undefined) {
253
+ return this.labels[key] === value;
254
+ }
255
+ return key in this.labels;
256
+ }
257
+
258
+ private emitEvent(type: TTaskEventType, extra?: Partial<ITaskEvent>): void {
259
+ this.eventSubject.next({
260
+ type,
261
+ task: this.getMetadata(),
262
+ timestamp: Date.now(),
263
+ ...extra,
264
+ });
265
+ }
266
+
230
267
  public taskSetup: ITaskSetupFunction<T>;
231
268
  public setupValue: T;
232
269
 
@@ -247,6 +284,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
247
284
  taskSetup?: ITaskSetupFunction<T>;
248
285
  steps?: TSteps;
249
286
  catchErrors?: boolean;
287
+ labels?: Record<string, string>;
250
288
  }) {
251
289
  this.taskFunction = optionsArg.taskFunction;
252
290
  this.preTask = optionsArg.preTask;
@@ -257,6 +295,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
257
295
  this.name = optionsArg.name;
258
296
  this.taskSetup = optionsArg.taskSetup;
259
297
  this.catchErrors = optionsArg.catchErrors ?? false;
298
+ this.labels = optionsArg.labels ? { ...optionsArg.labels } : {};
260
299
 
261
300
  // Initialize steps if provided
262
301
  if (optionsArg.steps) {
@@ -309,8 +348,8 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
309
348
  if (step) {
310
349
  step.start();
311
350
  this.currentStepName = stepName as string;
312
-
313
- // Emit event for frontend updates (could be enhanced with event emitter)
351
+ this.emitEvent('step', { stepName: stepName as string });
352
+
314
353
  if (this.name) {
315
354
  logger.log('info', `Task ${this.name}: Starting step "${stepName}" - ${step.description}`);
316
355
  }
@@ -369,6 +408,7 @@ export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; de
369
408
  cronSchedule: this.cronJob?.cronExpression,
370
409
  lastError: this.lastError?.message,
371
410
  errorCount: this.errorCount,
411
+ labels: { ...this.labels },
372
412
  };
373
413
  }
374
414
 
@@ -4,7 +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
+ import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo, ITaskEvent } from './taskbuffer.interfaces.js';
8
8
  import { logger } from './taskbuffer.logging.js';
9
9
 
10
10
  export interface ICronJob {
@@ -20,6 +20,8 @@ export interface ITaskManagerConstructorOptions {
20
20
  export class TaskManager {
21
21
  public randomId = plugins.smartunique.shortId();
22
22
  public taskMap = new plugins.lik.ObjectMap<Task<any, any>>();
23
+ public readonly taskSubject = new plugins.smartrx.rxjs.Subject<ITaskEvent>();
24
+ private taskSubscriptions = new Map<Task<any, any>, plugins.smartrx.rxjs.Subscription>();
23
25
  private cronJobManager = new plugins.smarttime.CronManager();
24
26
  public options: ITaskManagerConstructorOptions = {
25
27
  distributedCoordinator: null,
@@ -38,6 +40,19 @@ export class TaskManager {
38
40
  throw new Error('Task must have a name to be added to taskManager');
39
41
  }
40
42
  this.taskMap.add(task);
43
+ const subscription = task.eventSubject.subscribe((event) => {
44
+ this.taskSubject.next(event);
45
+ });
46
+ this.taskSubscriptions.set(task, subscription);
47
+ }
48
+
49
+ public removeTask(task: Task<any, any>): void {
50
+ this.taskMap.remove(task);
51
+ const subscription = this.taskSubscriptions.get(task);
52
+ if (subscription) {
53
+ subscription.unsubscribe();
54
+ this.taskSubscriptions.delete(task);
55
+ }
41
56
  }
42
57
 
43
58
  public addAndScheduleTask(task: Task<any, any>, cronString: string) {
@@ -150,6 +165,10 @@ export class TaskManager {
150
165
  if (this.options.distributedCoordinator) {
151
166
  await this.options.distributedCoordinator.stop();
152
167
  }
168
+ for (const [, subscription] of this.taskSubscriptions) {
169
+ subscription.unsubscribe();
170
+ }
171
+ this.taskSubscriptions.clear();
153
172
  }
154
173
 
155
174
  // Get metadata for a specific task
@@ -198,6 +217,14 @@ export class TaskManager {
198
217
  return scheduledRuns;
199
218
  }
200
219
 
220
+ public getTasksByLabel(key: string, value: string): Task<any, any>[] {
221
+ return this.taskMap.getArray().filter(task => task.labels[key] === value);
222
+ }
223
+
224
+ public getTasksMetadataByLabel(key: string, value: string): ITaskMetadata[] {
225
+ return this.getTasksByLabel(key, value).map(task => task.getMetadata());
226
+ }
227
+
201
228
  // Add, execute, and remove a task while collecting metadata
202
229
  public async addExecuteRemoveTask<T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }>>(
203
230
  task: Task<T, TSteps>,
@@ -236,7 +263,7 @@ export class TaskManager {
236
263
  };
237
264
 
238
265
  // Remove task from manager
239
- this.taskMap.remove(task);
266
+ this.removeTask(task);
240
267
 
241
268
  // Deschedule if it was scheduled
242
269
  if (options?.schedule && task.name) {
@@ -260,7 +287,7 @@ export class TaskManager {
260
287
  };
261
288
 
262
289
  // Remove task from manager even on error
263
- this.taskMap.remove(task);
290
+ this.removeTask(task);
264
291
 
265
292
  // Deschedule if it was scheduled
266
293
  if (options?.schedule && task.name) {
@@ -17,6 +17,7 @@ export interface ITaskMetadata {
17
17
  timeout?: number;
18
18
  lastError?: string;
19
19
  errorCount?: number;
20
+ labels?: Record<string, string>;
20
21
  }
21
22
 
22
23
  export interface ITaskExecutionReport {
@@ -38,4 +39,14 @@ export interface IScheduledTaskInfo {
38
39
  lastRun?: Date;
39
40
  steps?: ITaskStep[];
40
41
  metadata?: ITaskMetadata;
42
+ }
43
+
44
+ export type TTaskEventType = 'started' | 'step' | 'completed' | 'failed';
45
+
46
+ export interface ITaskEvent {
47
+ type: TTaskEventType;
48
+ task: ITaskMetadata;
49
+ timestamp: number;
50
+ stepName?: string; // present when type === 'step'
51
+ error?: string; // present when type === 'failed'
41
52
  }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/taskbuffer',
6
- version: '4.0.0',
6
+ version: '4.1.1',
7
7
  description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
8
8
  }