@push.rocks/taskbuffer 4.2.1 → 5.0.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.
@@ -0,0 +1,80 @@
1
+ import type { Task } from './taskbuffer.classes.task.js';
2
+ import type { ITaskConstraintGroupOptions } from './taskbuffer.interfaces.js';
3
+
4
+ export class TaskConstraintGroup<TData extends Record<string, unknown> = Record<string, unknown>> {
5
+ public name: string;
6
+ public maxConcurrent: number;
7
+ public cooldownMs: number;
8
+ private constraintKeyForTask: (task: Task<any, any, TData>) => string | null | undefined;
9
+
10
+ private runningCounts = new Map<string, number>();
11
+ private lastCompletionTimes = new Map<string, number>();
12
+
13
+ constructor(options: ITaskConstraintGroupOptions<TData>) {
14
+ this.name = options.name;
15
+ this.constraintKeyForTask = options.constraintKeyForTask;
16
+ this.maxConcurrent = options.maxConcurrent ?? Infinity;
17
+ this.cooldownMs = options.cooldownMs ?? 0;
18
+ }
19
+
20
+ public getConstraintKey(task: Task<any, any, TData>): string | null {
21
+ const key = this.constraintKeyForTask(task);
22
+ return key ?? null;
23
+ }
24
+
25
+ public canRun(subGroupKey: string): boolean {
26
+ const running = this.runningCounts.get(subGroupKey) ?? 0;
27
+ if (running >= this.maxConcurrent) {
28
+ return false;
29
+ }
30
+
31
+ if (this.cooldownMs > 0) {
32
+ const lastCompletion = this.lastCompletionTimes.get(subGroupKey);
33
+ if (lastCompletion !== undefined) {
34
+ const elapsed = Date.now() - lastCompletion;
35
+ if (elapsed < this.cooldownMs) {
36
+ return false;
37
+ }
38
+ }
39
+ }
40
+
41
+ return true;
42
+ }
43
+
44
+ public acquireSlot(subGroupKey: string): void {
45
+ const current = this.runningCounts.get(subGroupKey) ?? 0;
46
+ this.runningCounts.set(subGroupKey, current + 1);
47
+ }
48
+
49
+ public releaseSlot(subGroupKey: string): void {
50
+ const current = this.runningCounts.get(subGroupKey) ?? 0;
51
+ const next = Math.max(0, current - 1);
52
+ if (next === 0) {
53
+ this.runningCounts.delete(subGroupKey);
54
+ } else {
55
+ this.runningCounts.set(subGroupKey, next);
56
+ }
57
+ this.lastCompletionTimes.set(subGroupKey, Date.now());
58
+ }
59
+
60
+ public getCooldownRemaining(subGroupKey: string): number {
61
+ if (this.cooldownMs <= 0) {
62
+ return 0;
63
+ }
64
+ const lastCompletion = this.lastCompletionTimes.get(subGroupKey);
65
+ if (lastCompletion === undefined) {
66
+ return 0;
67
+ }
68
+ const elapsed = Date.now() - lastCompletion;
69
+ return Math.max(0, this.cooldownMs - elapsed);
70
+ }
71
+
72
+ public getRunningCount(subGroupKey: string): number {
73
+ return this.runningCounts.get(subGroupKey) ?? 0;
74
+ }
75
+
76
+ public reset(): void {
77
+ this.runningCounts.clear();
78
+ this.lastCompletionTimes.clear();
79
+ }
80
+ }
@@ -1,10 +1,11 @@
1
1
  import * as plugins from './taskbuffer.plugins.js';
2
2
  import { Task } from './taskbuffer.classes.task.js';
3
+ import { TaskConstraintGroup } from './taskbuffer.classes.taskconstraintgroup.js';
3
4
  import {
4
5
  AbstractDistributedCoordinator,
5
6
  type IDistributedTaskRequestResult,
6
7
  } from './taskbuffer.classes.distributedcoordinator.js';
7
- import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo, ITaskEvent } from './taskbuffer.interfaces.js';
8
+ import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo, ITaskEvent, IConstrainedTaskEntry } from './taskbuffer.interfaces.js';
8
9
  import { logger } from './taskbuffer.logging.js';
9
10
 
10
11
  export interface ICronJob {
@@ -19,23 +20,28 @@ export interface ITaskManagerConstructorOptions {
19
20
 
20
21
  export class TaskManager {
21
22
  public randomId = plugins.smartunique.shortId();
22
- public taskMap = new plugins.lik.ObjectMap<Task<any, any>>();
23
+ public taskMap = new plugins.lik.ObjectMap<Task<any, any, any>>();
23
24
  public readonly taskSubject = new plugins.smartrx.rxjs.Subject<ITaskEvent>();
24
- private taskSubscriptions = new Map<Task<any, any>, plugins.smartrx.rxjs.Subscription>();
25
+ private taskSubscriptions = new Map<Task<any, any, any>, plugins.smartrx.rxjs.Subscription>();
25
26
  private cronJobManager = new plugins.smarttime.CronManager();
26
27
  public options: ITaskManagerConstructorOptions = {
27
28
  distributedCoordinator: null,
28
29
  };
29
30
 
31
+ // Constraint system
32
+ public constraintGroups: TaskConstraintGroup<any>[] = [];
33
+ private constraintQueue: IConstrainedTaskEntry[] = [];
34
+ private drainTimer: ReturnType<typeof setTimeout> | null = null;
35
+
30
36
  constructor(options: ITaskManagerConstructorOptions = {}) {
31
37
  this.options = Object.assign(this.options, options);
32
38
  }
33
39
 
34
- public getTaskByName(taskName: string): Task<any, any> {
40
+ public getTaskByName(taskName: string): Task<any, any, any> {
35
41
  return this.taskMap.findSync((task) => task.name === taskName);
36
42
  }
37
43
 
38
- public addTask(task: Task<any, any>): void {
44
+ public addTask(task: Task<any, any, any>): void {
39
45
  if (!task.name) {
40
46
  throw new Error('Task must have a name to be added to taskManager');
41
47
  }
@@ -46,7 +52,7 @@ export class TaskManager {
46
52
  this.taskSubscriptions.set(task, subscription);
47
53
  }
48
54
 
49
- public removeTask(task: Task<any, any>): void {
55
+ public removeTask(task: Task<any, any, any>): void {
50
56
  this.taskMap.remove(task);
51
57
  const subscription = this.taskSubscriptions.get(task);
52
58
  if (subscription) {
@@ -55,21 +61,134 @@ export class TaskManager {
55
61
  }
56
62
  }
57
63
 
58
- public addAndScheduleTask(task: Task<any, any>, cronString: string) {
64
+ public addAndScheduleTask(task: Task<any, any, any>, cronString: string) {
59
65
  this.addTask(task);
60
66
  this.scheduleTaskByName(task.name, cronString);
61
67
  }
62
68
 
69
+ // Constraint group management
70
+ public addConstraintGroup(group: TaskConstraintGroup<any>): void {
71
+ this.constraintGroups.push(group);
72
+ }
73
+
74
+ public removeConstraintGroup(name: string): void {
75
+ this.constraintGroups = this.constraintGroups.filter((g) => g.name !== name);
76
+ }
77
+
78
+ // Core constraint evaluation
79
+ public async triggerTaskConstrained(task: Task<any, any, any>, input?: any): Promise<any> {
80
+ // Gather applicable constraints
81
+ const applicableGroups: Array<{ group: TaskConstraintGroup<any>; key: string }> = [];
82
+ for (const group of this.constraintGroups) {
83
+ const key = group.getConstraintKey(task);
84
+ if (key !== null) {
85
+ applicableGroups.push({ group, key });
86
+ }
87
+ }
88
+
89
+ // No constraints apply → trigger directly
90
+ if (applicableGroups.length === 0) {
91
+ return task.trigger(input);
92
+ }
93
+
94
+ // Check if all constraints allow running
95
+ const allCanRun = applicableGroups.every(({ group, key }) => group.canRun(key));
96
+ if (allCanRun) {
97
+ return this.executeWithConstraintTracking(task, input, applicableGroups);
98
+ }
99
+
100
+ // Blocked → enqueue with deferred promise
101
+ const deferred = plugins.smartpromise.defer<any>();
102
+ this.constraintQueue.push({ task, input, deferred });
103
+ return deferred.promise;
104
+ }
105
+
106
+ private async executeWithConstraintTracking(
107
+ task: Task<any, any, any>,
108
+ input: any,
109
+ groups: Array<{ group: TaskConstraintGroup<any>; key: string }>,
110
+ ): Promise<any> {
111
+ // Acquire slots
112
+ for (const { group, key } of groups) {
113
+ group.acquireSlot(key);
114
+ }
115
+
116
+ try {
117
+ return await task.trigger(input);
118
+ } finally {
119
+ // Release slots
120
+ for (const { group, key } of groups) {
121
+ group.releaseSlot(key);
122
+ }
123
+ this.drainConstraintQueue();
124
+ }
125
+ }
126
+
127
+ private drainConstraintQueue(): void {
128
+ let shortestCooldown = Infinity;
129
+ const stillQueued: IConstrainedTaskEntry[] = [];
130
+
131
+ for (const entry of this.constraintQueue) {
132
+ const applicableGroups: Array<{ group: TaskConstraintGroup<any>; key: string }> = [];
133
+ for (const group of this.constraintGroups) {
134
+ const key = group.getConstraintKey(entry.task);
135
+ if (key !== null) {
136
+ applicableGroups.push({ group, key });
137
+ }
138
+ }
139
+
140
+ // No constraints apply anymore (group removed?) → run directly
141
+ if (applicableGroups.length === 0) {
142
+ entry.task.trigger(entry.input).then(
143
+ (result) => entry.deferred.resolve(result),
144
+ (err) => entry.deferred.reject(err),
145
+ );
146
+ continue;
147
+ }
148
+
149
+ const allCanRun = applicableGroups.every(({ group, key }) => group.canRun(key));
150
+ if (allCanRun) {
151
+ this.executeWithConstraintTracking(entry.task, entry.input, applicableGroups).then(
152
+ (result) => entry.deferred.resolve(result),
153
+ (err) => entry.deferred.reject(err),
154
+ );
155
+ } else {
156
+ stillQueued.push(entry);
157
+ // Track shortest cooldown for timer scheduling
158
+ for (const { group, key } of applicableGroups) {
159
+ const remaining = group.getCooldownRemaining(key);
160
+ if (remaining > 0 && remaining < shortestCooldown) {
161
+ shortestCooldown = remaining;
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ this.constraintQueue = stillQueued;
168
+
169
+ // Schedule next drain if there are cooldown-blocked entries
170
+ if (this.drainTimer) {
171
+ clearTimeout(this.drainTimer);
172
+ this.drainTimer = null;
173
+ }
174
+ if (stillQueued.length > 0 && shortestCooldown < Infinity) {
175
+ this.drainTimer = setTimeout(() => {
176
+ this.drainTimer = null;
177
+ this.drainConstraintQueue();
178
+ }, shortestCooldown + 1);
179
+ }
180
+ }
181
+
63
182
  public async triggerTaskByName(taskName: string): Promise<any> {
64
183
  const taskToTrigger = this.getTaskByName(taskName);
65
184
  if (!taskToTrigger) {
66
185
  throw new Error(`No task with the name ${taskName} found.`);
67
186
  }
68
- return taskToTrigger.trigger();
187
+ return this.triggerTaskConstrained(taskToTrigger);
69
188
  }
70
189
 
71
- public async triggerTask(task: Task<any, any>) {
72
- return task.trigger();
190
+ public async triggerTask(task: Task<any, any, any>) {
191
+ return this.triggerTaskConstrained(task);
73
192
  }
74
193
 
75
194
  public scheduleTaskByName(taskName: string, cronString: string) {
@@ -80,7 +199,7 @@ export class TaskManager {
80
199
  this.handleTaskScheduling(taskToSchedule, cronString);
81
200
  }
82
201
 
83
- private handleTaskScheduling(task: Task<any, any>, cronString: string) {
202
+ private handleTaskScheduling(task: Task<any, any, any>, cronString: string) {
84
203
  const cronJob = this.cronJobManager.addCronjob(
85
204
  cronString,
86
205
  async (triggerTime: number) => {
@@ -98,7 +217,7 @@ export class TaskManager {
98
217
  }
99
218
  }
100
219
  try {
101
- await task.trigger();
220
+ await this.triggerTaskConstrained(task);
102
221
  } catch (err) {
103
222
  logger.log('error', `TaskManager: scheduled task "${task.name || 'unnamed'}" failed: ${err instanceof Error ? err.message : String(err)}`);
104
223
  }
@@ -107,7 +226,7 @@ export class TaskManager {
107
226
  task.cronJob = cronJob;
108
227
  }
109
228
 
110
- private logTaskState(task: Task<any, any>) {
229
+ private logTaskState(task: Task<any, any, any>) {
111
230
  logger.log('info', `Taskbuffer schedule triggered task >>${task.name}<<`);
112
231
  const bufferState = task.buffered
113
232
  ? `buffered with max ${task.bufferMax} buffered calls`
@@ -116,7 +235,7 @@ export class TaskManager {
116
235
  }
117
236
 
118
237
  private async performDistributedConsultation(
119
- task: Task<any, any>,
238
+ task: Task<any, any, any>,
120
239
  triggerTime: number,
121
240
  ): Promise<IDistributedTaskRequestResult> {
122
241
  logger.log('info', 'Found a distributed coordinator, performing consultation.');
@@ -144,7 +263,7 @@ export class TaskManager {
144
263
  }
145
264
  }
146
265
 
147
- public async descheduleTask(task: Task<any, any>) {
266
+ public async descheduleTask(task: Task<any, any, any>) {
148
267
  await this.descheduleTaskByName(task.name);
149
268
  }
150
269
 
@@ -169,6 +288,10 @@ export class TaskManager {
169
288
  subscription.unsubscribe();
170
289
  }
171
290
  this.taskSubscriptions.clear();
291
+ if (this.drainTimer) {
292
+ clearTimeout(this.drainTimer);
293
+ this.drainTimer = null;
294
+ }
172
295
  }
173
296
 
174
297
  // Get metadata for a specific task
@@ -186,7 +309,7 @@ export class TaskManager {
186
309
  // Get scheduled tasks with their schedules and next run times
187
310
  public getScheduledTasks(): IScheduledTaskInfo[] {
188
311
  const scheduledTasks: IScheduledTaskInfo[] = [];
189
-
312
+
190
313
  for (const task of this.taskMap.getArray()) {
191
314
  if (task.cronJob) {
192
315
  scheduledTasks.push({
@@ -199,7 +322,7 @@ export class TaskManager {
199
322
  });
200
323
  }
201
324
  }
202
-
325
+
203
326
  return scheduledTasks;
204
327
  }
205
328
 
@@ -213,11 +336,11 @@ export class TaskManager {
213
336
  }))
214
337
  .sort((a, b) => a.nextRun.getTime() - b.nextRun.getTime())
215
338
  .slice(0, limit);
216
-
339
+
217
340
  return scheduledRuns;
218
341
  }
219
342
 
220
- public getTasksByLabel(key: string, value: string): Task<any, any>[] {
343
+ public getTasksByLabel(key: string, value: string): Task<any, any, any>[] {
221
344
  return this.taskMap.getArray().filter(task => task.labels[key] === value);
222
345
  }
223
346
 
@@ -227,7 +350,7 @@ export class TaskManager {
227
350
 
228
351
  // Add, execute, and remove a task while collecting metadata
229
352
  public async addExecuteRemoveTask<T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }>>(
230
- task: Task<T, TSteps>,
353
+ task: Task<T, TSteps, any>,
231
354
  options?: {
232
355
  schedule?: string;
233
356
  trackProgress?: boolean;
@@ -235,19 +358,18 @@ export class TaskManager {
235
358
  ): Promise<ITaskExecutionReport> {
236
359
  // Add task to manager
237
360
  this.addTask(task);
238
-
361
+
239
362
  // Optionally schedule it
240
363
  if (options?.schedule) {
241
364
  this.scheduleTaskByName(task.name!, options.schedule);
242
365
  }
243
-
366
+
244
367
  const startTime = Date.now();
245
- const progressUpdates: Array<{ stepName: string; timestamp: number }> = [];
246
-
368
+
247
369
  try {
248
- // Execute the task
249
- const result = await task.trigger();
250
-
370
+ // Execute the task through constraints
371
+ const result = await this.triggerTaskConstrained(task);
372
+
251
373
  // Collect execution report
252
374
  const report: ITaskExecutionReport = {
253
375
  taskName: task.name || 'unnamed',
@@ -261,15 +383,15 @@ export class TaskManager {
261
383
  progress: task.getProgress(),
262
384
  result,
263
385
  };
264
-
386
+
265
387
  // Remove task from manager
266
388
  this.removeTask(task);
267
-
389
+
268
390
  // Deschedule if it was scheduled
269
391
  if (options?.schedule && task.name) {
270
392
  this.descheduleTaskByName(task.name);
271
393
  }
272
-
394
+
273
395
  return report;
274
396
  } catch (error) {
275
397
  // Create error report
@@ -285,15 +407,15 @@ export class TaskManager {
285
407
  progress: task.getProgress(),
286
408
  error: error as Error,
287
409
  };
288
-
410
+
289
411
  // Remove task from manager even on error
290
412
  this.removeTask(task);
291
-
413
+
292
414
  // Deschedule if it was scheduled
293
415
  if (options?.schedule && task.name) {
294
416
  this.descheduleTaskByName(task.name);
295
417
  }
296
-
418
+
297
419
  throw errorReport;
298
420
  }
299
421
  }
@@ -1,4 +1,18 @@
1
1
  import type { ITaskStep } from './taskbuffer.classes.taskstep.js';
2
+ import type { Task } from './taskbuffer.classes.task.js';
3
+
4
+ export interface ITaskConstraintGroupOptions<TData extends Record<string, unknown> = Record<string, unknown>> {
5
+ name: string;
6
+ constraintKeyForTask: (task: Task<any, any, TData>) => string | null | undefined;
7
+ maxConcurrent?: number; // default: Infinity
8
+ cooldownMs?: number; // default: 0
9
+ }
10
+
11
+ export interface IConstrainedTaskEntry {
12
+ task: Task<any, any, any>;
13
+ input: any;
14
+ deferred: import('@push.rocks/smartpromise').Deferred<any>;
15
+ }
2
16
 
3
17
  export interface ITaskMetadata {
4
18
  name: string;
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/taskbuffer',
6
- version: '4.2.1',
6
+ version: '5.0.1',
7
7
  description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
8
8
  }
@@ -1,30 +0,0 @@
1
- import * as plugins from './taskbuffer.plugins.js';
2
- import { Task } from './taskbuffer.classes.task.js';
3
- export declare class TaskRunner {
4
- maxParallelJobs: number;
5
- status: 'stopped' | 'running';
6
- runningTasks: plugins.lik.ObjectMap<Task>;
7
- queuedTasks: Task[];
8
- constructor();
9
- /**
10
- * adds a task to the queue
11
- */
12
- addTask(taskArg: Task): void;
13
- /**
14
- * set amount of parallel tasks
15
- * be careful, you might lose dependability of tasks
16
- */
17
- setMaxParallelJobs(maxParallelJobsArg: number): void;
18
- /**
19
- * starts the task queue
20
- */
21
- start(): Promise<void>;
22
- /**
23
- * checks whether execution is on point
24
- */
25
- checkExecution(): Promise<void>;
26
- /**
27
- * stops the task queue
28
- */
29
- stop(): Promise<void>;
30
- }
@@ -1,60 +0,0 @@
1
- import * as plugins from './taskbuffer.plugins.js';
2
- import { Task } from './taskbuffer.classes.task.js';
3
- import { logger } from './taskbuffer.logging.js';
4
- export class TaskRunner {
5
- constructor() {
6
- this.maxParallelJobs = 1;
7
- this.status = 'stopped';
8
- this.runningTasks = new plugins.lik.ObjectMap();
9
- this.queuedTasks = [];
10
- this.runningTasks.eventSubject.subscribe(async (eventArg) => {
11
- this.checkExecution();
12
- });
13
- }
14
- /**
15
- * adds a task to the queue
16
- */
17
- addTask(taskArg) {
18
- this.queuedTasks.push(taskArg);
19
- this.checkExecution();
20
- }
21
- /**
22
- * set amount of parallel tasks
23
- * be careful, you might lose dependability of tasks
24
- */
25
- setMaxParallelJobs(maxParallelJobsArg) {
26
- this.maxParallelJobs = maxParallelJobsArg;
27
- }
28
- /**
29
- * starts the task queue
30
- */
31
- async start() {
32
- this.status = 'running';
33
- }
34
- /**
35
- * checks whether execution is on point
36
- */
37
- async checkExecution() {
38
- if (this.runningTasks.getArray().length < this.maxParallelJobs &&
39
- this.status === 'running' &&
40
- this.queuedTasks.length > 0) {
41
- const nextJob = this.queuedTasks.shift();
42
- this.runningTasks.add(nextJob);
43
- try {
44
- await nextJob.trigger();
45
- }
46
- catch (err) {
47
- logger.log('error', `TaskRunner: task "${nextJob.name || 'unnamed'}" failed: ${err instanceof Error ? err.message : String(err)}`);
48
- }
49
- this.runningTasks.remove(nextJob);
50
- this.checkExecution();
51
- }
52
- }
53
- /**
54
- * stops the task queue
55
- */
56
- async stop() {
57
- this.status = 'stopped';
58
- }
59
- }
60
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFza2J1ZmZlci5jbGFzc2VzLnRhc2tydW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90YXNrYnVmZmVyLmNsYXNzZXMudGFza3J1bm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBRW5ELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFakQsTUFBTSxPQUFPLFVBQVU7SUFPckI7UUFOTyxvQkFBZSxHQUFXLENBQUMsQ0FBQztRQUM1QixXQUFNLEdBQTBCLFNBQVMsQ0FBQztRQUMxQyxpQkFBWSxHQUNqQixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFRLENBQUM7UUFDN0IsZ0JBQVcsR0FBVyxFQUFFLENBQUM7UUFHOUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsRUFBRTtZQUMxRCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDeEIsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPLENBQUMsT0FBYTtRQUMxQixJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGtCQUFrQixDQUFDLGtCQUEwQjtRQUNsRCxJQUFJLENBQUMsZUFBZSxHQUFHLGtCQUFrQixDQUFDO0lBQzVDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLElBQUksQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBQzFCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxjQUFjO1FBQ3pCLElBQ0UsSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGVBQWU7WUFDMUQsSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTO1lBQ3pCLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUMsRUFDM0IsQ0FBQztZQUNELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDekMsSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDL0IsSUFBSSxDQUFDO2dCQUNILE1BQU0sT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQzFCLENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHFCQUFxQixPQUFPLENBQUMsSUFBSSxJQUFJLFNBQVMsYUFBYSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3JJLENBQUM7WUFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNsQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUM7SUFDMUIsQ0FBQztDQUNGIn0=
@@ -1,69 +0,0 @@
1
- import * as plugins from './taskbuffer.plugins.js';
2
-
3
- import { Task } from './taskbuffer.classes.task.js';
4
- import { logger } from './taskbuffer.logging.js';
5
-
6
- export class TaskRunner {
7
- public maxParallelJobs: number = 1;
8
- public status: 'stopped' | 'running' = 'stopped';
9
- public runningTasks: plugins.lik.ObjectMap<Task> =
10
- new plugins.lik.ObjectMap<Task>();
11
- public queuedTasks: Task[] = [];
12
-
13
- constructor() {
14
- this.runningTasks.eventSubject.subscribe(async (eventArg) => {
15
- this.checkExecution();
16
- });
17
- }
18
-
19
- /**
20
- * adds a task to the queue
21
- */
22
- public addTask(taskArg: Task) {
23
- this.queuedTasks.push(taskArg);
24
- this.checkExecution();
25
- }
26
-
27
- /**
28
- * set amount of parallel tasks
29
- * be careful, you might lose dependability of tasks
30
- */
31
- public setMaxParallelJobs(maxParallelJobsArg: number) {
32
- this.maxParallelJobs = maxParallelJobsArg;
33
- }
34
-
35
- /**
36
- * starts the task queue
37
- */
38
- public async start() {
39
- this.status = 'running';
40
- }
41
-
42
- /**
43
- * checks whether execution is on point
44
- */
45
- public async checkExecution() {
46
- if (
47
- this.runningTasks.getArray().length < this.maxParallelJobs &&
48
- this.status === 'running' &&
49
- this.queuedTasks.length > 0
50
- ) {
51
- const nextJob = this.queuedTasks.shift();
52
- this.runningTasks.add(nextJob);
53
- try {
54
- await nextJob.trigger();
55
- } catch (err) {
56
- logger.log('error', `TaskRunner: task "${nextJob.name || 'unnamed'}" failed: ${err instanceof Error ? err.message : String(err)}`);
57
- }
58
- this.runningTasks.remove(nextJob);
59
- this.checkExecution();
60
- }
61
- }
62
-
63
- /**
64
- * stops the task queue
65
- */
66
- public async stop() {
67
- this.status = 'stopped';
68
- }
69
- }