@push.rocks/taskbuffer 3.1.10 โ†’ 3.2.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/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @push.rocks/taskbuffer ๐Ÿš€
2
2
 
3
- A **powerful**, **flexible**, and **TypeScript-first** task management library for orchestrating asynchronous operations with style. From simple task execution to complex distributed workflows, taskbuffer has got you covered.
3
+ A **powerful**, **flexible**, and **TypeScript-first** task management library for orchestrating asynchronous operations with style. From simple task execution to complex distributed workflows with real-time progress tracking, taskbuffer has got you covered.
4
4
 
5
5
  ## Install ๐Ÿ“ฆ
6
6
 
@@ -23,35 +23,33 @@ In the modern JavaScript ecosystem, managing asynchronous tasks efficiently is c
23
23
  - **๐Ÿ”„ Smart buffering**: Control concurrent executions with intelligent buffer management
24
24
  - **โฐ Built-in scheduling**: Cron-based task scheduling without additional dependencies
25
25
  - **๐ŸŽญ Multiple paradigms**: Support for debounced, throttled, and one-time execution patterns
26
+ - **๐Ÿ“Š Progress tracking**: Real-time step-by-step progress monitoring for UI integration
26
27
  - **๐Ÿ”Œ Extensible**: Clean architecture that's easy to extend and customize
27
28
  - **๐Ÿƒ Zero dependencies on external schedulers**: Everything you need is included
28
29
 
29
30
  ## Core Concepts ๐ŸŽ“
30
31
 
31
32
  ### Task
32
-
33
- The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
33
+ The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control, now with step-by-step progress tracking.
34
34
 
35
35
  ### Taskchain
36
-
37
36
  Sequential task execution - tasks run one after another, with results passed along the chain.
38
37
 
39
38
  ### Taskparallel
40
-
41
39
  Parallel task execution - multiple tasks run simultaneously for maximum performance.
42
40
 
43
41
  ### TaskManager
44
-
45
- Centralized task scheduling and management using cron expressions.
42
+ Centralized task scheduling and management using cron expressions, with rich metadata collection.
46
43
 
47
44
  ### TaskDebounced
48
-
49
45
  Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
50
46
 
51
47
  ### TaskOnce
52
-
53
48
  Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
54
49
 
50
+ ### TaskStep ๐Ÿ†•
51
+ Granular progress tracking - define named steps with percentage weights for real-time progress monitoring.
52
+
55
53
  ## Quick Start ๐Ÿ
56
54
 
57
55
  ### Basic Task Execution
@@ -72,6 +70,42 @@ const myTask = new Task({
72
70
  const result = await myTask.trigger();
73
71
  ```
74
72
 
73
+ ### Task with Progress Steps ๐Ÿ†•
74
+
75
+ Track granular progress for complex operations - perfect for UI progress bars:
76
+
77
+ ```typescript
78
+ const dataProcessingTask = new Task({
79
+ name: 'DataProcessor',
80
+ steps: [
81
+ { name: 'validate', description: 'Validating input data', percentage: 15 },
82
+ { name: 'fetch', description: 'Fetching external resources', percentage: 25 },
83
+ { name: 'transform', description: 'Transforming data', percentage: 35 },
84
+ { name: 'save', description: 'Saving to database', percentage: 25 }
85
+ ] as const, // Use 'as const' for full type safety
86
+ taskFunction: async (inputData) => {
87
+ // TypeScript knows these step names!
88
+ dataProcessingTask.notifyStep('validate');
89
+ const validated = await validateData(inputData);
90
+
91
+ dataProcessingTask.notifyStep('fetch');
92
+ const external = await fetchExternalData();
93
+
94
+ dataProcessingTask.notifyStep('transform');
95
+ const transformed = await transformData(validated, external);
96
+
97
+ dataProcessingTask.notifyStep('save');
98
+ const result = await saveToDatabase(transformed);
99
+
100
+ return result;
101
+ }
102
+ });
103
+
104
+ // Monitor progress in real-time
105
+ const result = await dataProcessingTask.trigger();
106
+ console.log(`Final progress: ${dataProcessingTask.getProgress()}%`); // 100%
107
+ ```
108
+
75
109
  ## TypeScript Generics Support ๐Ÿ”ฌ
76
110
 
77
111
  TaskBuffer leverages TypeScript's powerful generics system for complete type safety across your task chains and workflows.
@@ -97,7 +131,7 @@ interface ProcessedUser {
97
131
  }
98
132
 
99
133
  // Create strongly typed tasks
100
- const processUserTask = new Task<UserData, ProcessedUser>({
134
+ const processUserTask = new Task<ProcessedUser>({
101
135
  name: 'ProcessUser',
102
136
  taskFunction: async (user: UserData): Promise<ProcessedUser> => {
103
137
  return {
@@ -129,7 +163,7 @@ interface TaskConfig {
129
163
 
130
164
  const configuredTask = new Task<TaskConfig>({
131
165
  name: 'ConfiguredTask',
132
- taskSetup: async () => ({
166
+ taskSetup: async (): Promise<TaskConfig> => ({
133
167
  apiEndpoint: 'https://api.example.com',
134
168
  retryCount: 3,
135
169
  timeout: 5000
@@ -156,21 +190,21 @@ Chain tasks with preserved type flow:
156
190
 
157
191
  ```typescript
158
192
  // Each task knows its input and output types
159
- const fetchTask = new Task<void, UserData[]>({
193
+ const fetchTask = new Task<void>({
160
194
  name: 'FetchUsers',
161
195
  taskFunction: async (): Promise<UserData[]> => {
162
196
  return await api.getUsers();
163
197
  }
164
198
  });
165
199
 
166
- const filterTask = new Task<UserData[], UserData[]>({
200
+ const filterTask = new Task<void>({
167
201
  name: 'FilterActive',
168
202
  taskFunction: async (users: UserData[]): Promise<UserData[]> => {
169
203
  return users.filter(user => user.isActive);
170
204
  }
171
205
  });
172
206
 
173
- const mapTask = new Task<UserData[], ProcessedUser[]>({
207
+ const mapTask = new Task<void>({
174
208
  name: 'MapToProcessed',
175
209
  taskFunction: async (users: UserData[]): Promise<ProcessedUser[]> => {
176
210
  return users.map(transformUser);
@@ -186,6 +220,204 @@ const chain = new Taskchain({
186
220
  const finalResult: ProcessedUser[] = await chain.trigger();
187
221
  ```
188
222
 
223
+ ## Progress Tracking & Metadata ๐Ÿ“Š ๐Ÿ†•
224
+
225
+ TaskBuffer now provides comprehensive progress tracking and metadata collection, perfect for building dashboards and monitoring systems.
226
+
227
+ ### Step-by-Step Progress
228
+
229
+ Define weighted steps for accurate progress calculation:
230
+
231
+ ```typescript
232
+ const migrationTask = new Task({
233
+ name: 'DatabaseMigration',
234
+ steps: [
235
+ { name: 'backup', description: 'Backing up database', percentage: 20 },
236
+ { name: 'schema', description: 'Updating schema', percentage: 30 },
237
+ { name: 'data', description: 'Migrating data', percentage: 40 },
238
+ { name: 'validate', description: 'Validating integrity', percentage: 10 }
239
+ ] as const,
240
+ taskFunction: async () => {
241
+ migrationTask.notifyStep('backup');
242
+ await backupDatabase();
243
+ console.log(`Progress: ${migrationTask.getProgress()}%`); // ~20%
244
+
245
+ migrationTask.notifyStep('schema');
246
+ await updateSchema();
247
+ console.log(`Progress: ${migrationTask.getProgress()}%`); // ~50%
248
+
249
+ migrationTask.notifyStep('data');
250
+ await migrateData();
251
+ console.log(`Progress: ${migrationTask.getProgress()}%`); // ~90%
252
+
253
+ migrationTask.notifyStep('validate');
254
+ await validateIntegrity();
255
+ console.log(`Progress: ${migrationTask.getProgress()}%`); // 100%
256
+ }
257
+ });
258
+
259
+ // Get detailed step information
260
+ const steps = migrationTask.getStepsMetadata();
261
+ steps.forEach(step => {
262
+ console.log(`${step.name}: ${step.status} (${step.percentage}%)`);
263
+ if (step.duration) {
264
+ console.log(` Duration: ${step.duration}ms`);
265
+ }
266
+ });
267
+ ```
268
+
269
+ ### Task Metadata Collection
270
+
271
+ Get comprehensive metadata about task execution:
272
+
273
+ ```typescript
274
+ const task = new Task({
275
+ name: 'DataProcessor',
276
+ buffered: true,
277
+ bufferMax: 5,
278
+ steps: [
279
+ { name: 'process', description: 'Processing', percentage: 100 }
280
+ ] as const,
281
+ taskFunction: async () => {
282
+ task.notifyStep('process');
283
+ await processData();
284
+ }
285
+ });
286
+
287
+ // Get complete task metadata
288
+ const metadata = task.getMetadata();
289
+ console.log({
290
+ name: metadata.name,
291
+ status: metadata.status, // 'idle' | 'running' | 'completed' | 'failed'
292
+ progress: metadata.currentProgress, // 0-100
293
+ currentStep: metadata.currentStep,
294
+ runCount: metadata.runCount,
295
+ lastRun: metadata.lastRun,
296
+ buffered: metadata.buffered,
297
+ bufferMax: metadata.bufferMax
298
+ });
299
+ ```
300
+
301
+ ### TaskManager Enhanced Metadata
302
+
303
+ The TaskManager now provides rich metadata for monitoring and dashboards:
304
+
305
+ ```typescript
306
+ const manager = new TaskManager();
307
+
308
+ // Add tasks with step tracking
309
+ manager.addAndScheduleTask(backupTask, '0 2 * * *'); // 2 AM daily
310
+ manager.addAndScheduleTask(cleanupTask, '0 */6 * * *'); // Every 6 hours
311
+
312
+ // Get metadata for all tasks
313
+ const allTasksMetadata = manager.getAllTasksMetadata();
314
+ allTasksMetadata.forEach(task => {
315
+ console.log(`Task: ${task.name}`);
316
+ console.log(` Status: ${task.status}`);
317
+ console.log(` Progress: ${task.currentProgress}%`);
318
+ console.log(` Run count: ${task.runCount}`);
319
+ console.log(` Schedule: ${task.cronSchedule}`);
320
+ });
321
+
322
+ // Get scheduled tasks with next run times
323
+ const scheduledTasks = manager.getScheduledTasks();
324
+ scheduledTasks.forEach(task => {
325
+ console.log(`${task.name}: Next run at ${task.nextRun}`);
326
+ if (task.steps) {
327
+ console.log(` Steps: ${task.steps.length}`);
328
+ }
329
+ });
330
+
331
+ // Get upcoming executions
332
+ const nextRuns = manager.getNextScheduledRuns(10);
333
+ console.log('Next 10 scheduled executions:', nextRuns);
334
+ ```
335
+
336
+ ### Execute and Track Tasks
337
+
338
+ Execute tasks with full lifecycle tracking and automatic cleanup:
339
+
340
+ ```typescript
341
+ const manager = new TaskManager();
342
+
343
+ const analyticsTask = new Task({
344
+ name: 'Analytics',
345
+ steps: [
346
+ { name: 'collect', description: 'Collecting metrics', percentage: 30 },
347
+ { name: 'analyze', description: 'Analyzing data', percentage: 50 },
348
+ { name: 'report', description: 'Generating report', percentage: 20 }
349
+ ] as const,
350
+ taskFunction: async () => {
351
+ analyticsTask.notifyStep('collect');
352
+ const metrics = await collectMetrics();
353
+
354
+ analyticsTask.notifyStep('analyze');
355
+ const analysis = await analyzeData(metrics);
356
+
357
+ analyticsTask.notifyStep('report');
358
+ return await generateReport(analysis);
359
+ }
360
+ });
361
+
362
+ // Execute with automatic cleanup and metadata collection
363
+ const report = await manager.addExecuteRemoveTask(analyticsTask, {
364
+ trackProgress: true
365
+ });
366
+
367
+ console.log('Execution Report:', {
368
+ taskName: report.taskName,
369
+ duration: report.duration,
370
+ stepsCompleted: report.stepsCompleted,
371
+ finalProgress: report.progress,
372
+ result: report.result
373
+ });
374
+ ```
375
+
376
+ ### Frontend Integration Example
377
+
378
+ Perfect for building real-time progress UIs:
379
+
380
+ ```typescript
381
+ // WebSocket server for real-time updates
382
+ io.on('connection', (socket) => {
383
+ socket.on('startTask', async (taskId) => {
384
+ const task = new Task({
385
+ name: taskId,
386
+ steps: [
387
+ { name: 'start', description: 'Starting...', percentage: 10 },
388
+ { name: 'process', description: 'Processing...', percentage: 70 },
389
+ { name: 'finish', description: 'Finishing...', percentage: 20 }
390
+ ] as const,
391
+ taskFunction: async () => {
392
+ task.notifyStep('start');
393
+ socket.emit('progress', {
394
+ step: 'start',
395
+ progress: task.getProgress(),
396
+ metadata: task.getStepsMetadata()
397
+ });
398
+
399
+ task.notifyStep('process');
400
+ socket.emit('progress', {
401
+ step: 'process',
402
+ progress: task.getProgress(),
403
+ metadata: task.getStepsMetadata()
404
+ });
405
+
406
+ task.notifyStep('finish');
407
+ socket.emit('progress', {
408
+ step: 'finish',
409
+ progress: task.getProgress(),
410
+ metadata: task.getStepsMetadata()
411
+ });
412
+ }
413
+ });
414
+
415
+ await task.trigger();
416
+ socket.emit('complete', task.getMetadata());
417
+ });
418
+ });
419
+ ```
420
+
189
421
  ## Buffer Behavior Deep Dive ๐ŸŒŠ
190
422
 
191
423
  The buffer system in TaskBuffer provides intelligent control over concurrent executions, preventing system overload while maximizing throughput.
@@ -388,26 +620,7 @@ setInterval(() => {
388
620
  });
389
621
  ```
390
622
 
391
- ### Buffered Execution (Rate Limiting)
392
-
393
- Perfect for API calls or database operations that need throttling:
394
-
395
- ```typescript
396
- const apiTask = new Task({
397
- name: 'APICall',
398
- taskFunction: async (endpoint: string) => {
399
- return await fetch(endpoint);
400
- },
401
- buffered: true,
402
- bufferMax: 3, // Maximum 3 concurrent executions
403
- execDelay: 1000, // Wait 1 second between executions
404
- });
405
-
406
- // These will be automatically throttled
407
- for (let i = 0; i < 10; i++) {
408
- apiTask.trigger(`/api/data/${i}`);
409
- }
410
- ```
623
+ ## Common Patterns ๐ŸŽจ
411
624
 
412
625
  ### Task Chains - Sequential Workflows
413
626
 
@@ -488,8 +701,17 @@ import { Task, TaskManager } from '@push.rocks/taskbuffer';
488
701
 
489
702
  const backupTask = new Task({
490
703
  name: 'DatabaseBackup',
704
+ steps: [
705
+ { name: 'dump', description: 'Creating dump', percentage: 70 },
706
+ { name: 'upload', description: 'Uploading to S3', percentage: 30 }
707
+ ] as const,
491
708
  taskFunction: async () => {
709
+ backupTask.notifyStep('dump');
492
710
  await performBackup();
711
+
712
+ backupTask.notifyStep('upload');
713
+ await uploadToS3();
714
+
493
715
  console.log(`Backup completed at ${new Date().toISOString()}`);
494
716
  },
495
717
  });
@@ -498,11 +720,14 @@ const manager = new TaskManager();
498
720
 
499
721
  // Add and schedule tasks
500
722
  manager.addAndScheduleTask(backupTask, '0 0 * * *'); // Daily at midnight
501
- manager.addAndScheduleTask(healthCheck, '*/5 * * * *'); // Every 5 minutes
502
723
 
503
724
  // Start the scheduler
504
725
  manager.start();
505
726
 
727
+ // Monitor scheduled tasks
728
+ const scheduled = manager.getScheduledTasks();
729
+ console.log('Scheduled tasks:', scheduled);
730
+
506
731
  // Later... stop if needed
507
732
  manager.stop();
508
733
  ```
@@ -598,45 +823,6 @@ runner.registerTask(imageResizeTask);
598
823
  runner.start();
599
824
  ```
600
825
 
601
- ### Buffer Management Strategies
602
-
603
- Fine-tune concurrent execution behavior:
604
-
605
- ```typescript
606
- const task = new Task({
607
- name: 'ResourceIntensive',
608
- taskFunction: async () => {
609
- /* ... */
610
- },
611
- buffered: true,
612
- bufferMax: 5, // Max 5 concurrent
613
- execDelay: 100, // 100ms between starts
614
- timeout: 30000, // 30 second timeout
615
- });
616
- ```
617
-
618
- ### Cycle Detection and Prevention
619
-
620
- TaskBuffer automatically detects and prevents circular dependencies:
621
-
622
- ```typescript
623
- const taskA = new Task({
624
- name: 'TaskA',
625
- taskFunction: async () => {
626
- /* ... */
627
- },
628
- preTask: taskB, // This would create a cycle
629
- });
630
-
631
- const taskB = new Task({
632
- name: 'TaskB',
633
- taskFunction: async () => {
634
- /* ... */
635
- },
636
- preTask: taskA, // Circular dependency detected!
637
- });
638
- ```
639
-
640
826
  ### Dynamic Task Creation
641
827
 
642
828
  Create tasks on-the-fly based on runtime conditions:
@@ -647,8 +833,17 @@ const dynamicWorkflow = async (config: Config) => {
647
833
  (step) =>
648
834
  new Task({
649
835
  name: step.name,
836
+ steps: step.substeps?.map(s => ({
837
+ name: s.id,
838
+ description: s.label,
839
+ percentage: s.weight
840
+ })) as const,
650
841
  taskFunction: async (input) => {
651
- return await processStep(step, input);
842
+ for (const substep of step.substeps || []) {
843
+ task.notifyStep(substep.id);
844
+ await processStep(substep, input);
845
+ }
846
+ return input;
652
847
  },
653
848
  }),
654
849
  );
@@ -666,26 +861,46 @@ const dynamicWorkflow = async (config: Config) => {
666
861
 
667
862
  ### Task Options
668
863
 
669
- | Option | Type | Description |
670
- | -------------- | ---------- | ------------------------------ |
671
- | `name` | `string` | Unique identifier for the task |
672
- | `taskFunction` | `Function` | Async function to execute |
673
- | `buffered` | `boolean` | Enable buffer management |
674
- | `bufferMax` | `number` | Maximum concurrent executions |
675
- | `execDelay` | `number` | Delay between executions (ms) |
676
- | `timeout` | `number` | Task timeout (ms) |
677
- | `preTask` | `Task` | Task to run before |
678
- | `afterTask` | `Task` | Task to run after |
864
+ | Option | Type | Description |
865
+ | -------------- | ---------- | -------------------------------------- |
866
+ | `name` | `string` | Unique identifier for the task |
867
+ | `taskFunction` | `Function` | Async function to execute |
868
+ | `steps` | `Array` | Step definitions with name, description, percentage |
869
+ | `buffered` | `boolean` | Enable buffer management |
870
+ | `bufferMax` | `number` | Maximum concurrent executions |
871
+ | `execDelay` | `number` | Delay between executions (ms) |
872
+ | `timeout` | `number` | Task timeout (ms) |
873
+ | `preTask` | `Task` | Task to run before |
874
+ | `afterTask` | `Task` | Task to run after |
875
+
876
+ ### Task Methods
877
+
878
+ | Method | Description |
879
+ | ------------------------- | ---------------------------------------------- |
880
+ | `trigger(x?)` | Execute the task |
881
+ | `notifyStep(stepName)` | Mark a step as active (typed step names!) |
882
+ | `getProgress()` | Get current progress percentage (0-100) |
883
+ | `getStepsMetadata()` | Get all steps with their current status |
884
+ | `getMetadata()` | Get complete task metadata |
885
+ | `resetSteps()` | Reset all steps to pending state |
679
886
 
680
887
  ### TaskManager Methods
681
888
 
682
- | Method | Description |
683
- | ------------------------------- | ------------------------ |
684
- | `addTask(task, cronExpression)` | Add and schedule a task |
685
- | `removeTask(taskName)` | Remove a scheduled task |
686
- | `start()` | Start the scheduler |
687
- | `stop()` | Stop the scheduler |
688
- | `getStats()` | Get execution statistics |
889
+ | Method | Description |
890
+ | ----------------------------------- | -------------------------------------- |
891
+ | `addTask(task)` | Add a task to the manager |
892
+ | `addAndScheduleTask(task, cron)` | Add and schedule a task |
893
+ | `getTaskByName(name)` | Get a specific task by name |
894
+ | `getTaskMetadata(name)` | Get metadata for a specific task |
895
+ | `getAllTasksMetadata()` | Get metadata for all tasks |
896
+ | `getScheduledTasks()` | Get all scheduled tasks with info |
897
+ | `getNextScheduledRuns(limit)` | Get upcoming scheduled executions |
898
+ | `addExecuteRemoveTask(task, opts)` | Execute task with lifecycle tracking |
899
+ | `triggerTaskByName(name)` | Trigger a task by its name |
900
+ | `scheduleTaskByName(name, cron)` | Schedule a task using cron expression |
901
+ | `descheduleTaskByName(name)` | Remove task from schedule |
902
+ | `start()` | Start the scheduler |
903
+ | `stop()` | Stop the scheduler |
689
904
 
690
905
  ### Taskchain Methods
691
906
 
@@ -703,14 +918,20 @@ const dynamicWorkflow = async (config: Config) => {
703
918
  3. **Implement proper error handling**: Use try-catch in task functions
704
919
  4. **Monitor task execution**: Use the built-in stats and logging
705
920
  5. **Set appropriate timeouts**: Prevent hanging tasks from blocking your system
921
+ 6. **Use step tracking wisely**: Don't create too many granular steps - aim for meaningful progress points
706
922
 
707
923
  ## Error Handling ๐Ÿ›ก๏ธ
708
924
 
709
925
  ```typescript
710
926
  const robustTask = new Task({
711
927
  name: 'RobustOperation',
928
+ steps: [
929
+ { name: 'try', description: 'Attempting operation', percentage: 80 },
930
+ { name: 'retry', description: 'Retrying on failure', percentage: 20 }
931
+ ] as const,
712
932
  taskFunction: async (input) => {
713
933
  try {
934
+ robustTask.notifyStep('try');
714
935
  return await riskyOperation(input);
715
936
  } catch (error) {
716
937
  // Log error
@@ -718,6 +939,7 @@ const robustTask = new Task({
718
939
 
719
940
  // Optionally retry
720
941
  if (error.retryable) {
942
+ robustTask.notifyStep('retry');
721
943
  return await riskyOperation(input);
722
944
  }
723
945
 
@@ -731,12 +953,20 @@ const robustTask = new Task({
731
953
 
732
954
  ## Real-World Examples ๐ŸŒ
733
955
 
734
- ### API Rate Limiting
956
+ ### API Rate Limiting with Progress
735
957
 
736
958
  ```typescript
737
959
  const apiClient = new Task({
738
960
  name: 'RateLimitedAPI',
961
+ steps: [
962
+ { name: 'wait', description: 'Rate limit delay', percentage: 10 },
963
+ { name: 'call', description: 'API call', percentage: 90 }
964
+ ] as const,
739
965
  taskFunction: async (endpoint: string) => {
966
+ apiClient.notifyStep('wait');
967
+ await delay(100); // Rate limiting
968
+
969
+ apiClient.notifyStep('call');
740
970
  return await fetch(`https://api.example.com${endpoint}`);
741
971
  },
742
972
  buffered: true,
@@ -745,22 +975,43 @@ const apiClient = new Task({
745
975
  });
746
976
  ```
747
977
 
748
- ### Database Migration Pipeline
978
+ ### Database Migration Pipeline with Progress
749
979
 
750
980
  ```typescript
751
981
  const migrationChain = new Taskchain({
752
982
  name: 'DatabaseMigration',
753
983
  taskArray: [
754
- backupTask,
755
- schemaUpdateTask,
756
- dataTransformTask,
757
- validationTask,
758
- cleanupTask,
984
+ new Task({
985
+ name: 'Backup',
986
+ steps: [{ name: 'backup', description: 'Creating backup', percentage: 100 }] as const,
987
+ taskFunction: async () => {
988
+ backupTask.notifyStep('backup');
989
+ return await createBackup();
990
+ }
991
+ }),
992
+ new Task({
993
+ name: 'SchemaUpdate',
994
+ steps: [
995
+ { name: 'analyze', description: 'Analyzing changes', percentage: 30 },
996
+ { name: 'apply', description: 'Applying migrations', percentage: 70 }
997
+ ] as const,
998
+ taskFunction: async () => {
999
+ schemaTask.notifyStep('analyze');
1000
+ const changes = await analyzeSchema();
1001
+
1002
+ schemaTask.notifyStep('apply');
1003
+ return await applyMigrations(changes);
1004
+ }
1005
+ }),
1006
+ // ... more tasks
759
1007
  ],
760
1008
  });
1009
+
1010
+ // Execute with progress monitoring
1011
+ const result = await migrationChain.trigger();
761
1012
  ```
762
1013
 
763
- ### Microservice Health Monitoring
1014
+ ### Microservice Health Monitoring Dashboard
764
1015
 
765
1016
  ```typescript
766
1017
  const healthMonitor = new TaskManager();
@@ -768,36 +1019,89 @@ const healthMonitor = new TaskManager();
768
1019
  services.forEach((service) => {
769
1020
  const healthCheck = new Task({
770
1021
  name: `HealthCheck:${service.name}`,
1022
+ steps: [
1023
+ { name: 'ping', description: 'Pinging service', percentage: 30 },
1024
+ { name: 'check', description: 'Checking health', percentage: 50 },
1025
+ { name: 'report', description: 'Reporting status', percentage: 20 }
1026
+ ] as const,
771
1027
  taskFunction: async () => {
1028
+ healthCheck.notifyStep('ping');
1029
+ const responsive = await ping(service.url);
1030
+
1031
+ healthCheck.notifyStep('check');
772
1032
  const healthy = await checkHealth(service.url);
1033
+
1034
+ healthCheck.notifyStep('report');
773
1035
  if (!healthy) {
774
1036
  await alertOps(service);
775
1037
  }
1038
+
1039
+ return { service: service.name, healthy, timestamp: Date.now() };
776
1040
  },
777
1041
  });
778
1042
 
779
1043
  healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
780
1044
  });
1045
+
1046
+ // Dashboard endpoint
1047
+ app.get('/api/health/dashboard', (req, res) => {
1048
+ const metadata = healthMonitor.getAllTasksMetadata();
1049
+ res.json({
1050
+ services: metadata.map(task => ({
1051
+ name: task.name.replace('HealthCheck:', ''),
1052
+ status: task.status,
1053
+ lastCheck: task.lastRun,
1054
+ nextCheck: healthMonitor.getScheduledTasks()
1055
+ .find(s => s.name === task.name)?.nextRun,
1056
+ progress: task.currentProgress,
1057
+ currentStep: task.currentStep
1058
+ }))
1059
+ });
1060
+ });
781
1061
  ```
782
1062
 
783
1063
  ## Testing ๐Ÿงช
784
1064
 
785
1065
  ```typescript
786
1066
  import { expect, tap } from '@git.zone/tstest';
787
- import { Task } from '@push.rocks/taskbuffer';
1067
+ import { Task, TaskStep } from '@push.rocks/taskbuffer';
1068
+
1069
+ tap.test('should track task progress through steps', async () => {
1070
+ const task = new Task({
1071
+ name: 'TestTask',
1072
+ steps: [
1073
+ { name: 'step1', description: 'First step', percentage: 50 },
1074
+ { name: 'step2', description: 'Second step', percentage: 50 }
1075
+ ] as const,
1076
+ taskFunction: async () => {
1077
+ task.notifyStep('step1');
1078
+ expect(task.getProgress()).toBeLessThanOrEqual(50);
1079
+
1080
+ task.notifyStep('step2');
1081
+ expect(task.getProgress()).toBeLessThanOrEqual(100);
1082
+ }
1083
+ });
1084
+
1085
+ await task.trigger();
1086
+ expect(task.getProgress()).toEqual(100);
1087
+ });
788
1088
 
789
- tap.test('should execute task successfully', async () => {
790
- const result = await myTask.trigger();
791
- expect(result).toEqual(expectedValue);
1089
+ tap.test('should collect execution metadata', async () => {
1090
+ const manager = new TaskManager();
1091
+ const task = new Task({
1092
+ name: 'MetadataTest',
1093
+ taskFunction: async () => 'result'
1094
+ });
1095
+
1096
+ const report = await manager.addExecuteRemoveTask(task);
1097
+ expect(report.taskName).toEqual('MetadataTest');
1098
+ expect(report.result).toEqual('result');
1099
+ expect(report.duration).toBeGreaterThan(0);
792
1100
  });
793
1101
 
794
1102
  tap.start();
795
1103
  ```
796
1104
 
797
- ## Contributing ๐Ÿค
798
-
799
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
800
-
801
1105
  ## Support ๐Ÿ’ฌ
802
1106
 
803
1107
  - ๐Ÿ“ง Email: [hello@task.vc](mailto:hello@task.vc)
@@ -806,7 +1110,7 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
806
1110
 
807
1111
  ## License and Legal Information
808
1112
 
809
- This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
1113
+ This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
810
1114
 
811
1115
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
812
1116
 
@@ -821,4 +1125,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
821
1125
 
822
1126
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
823
1127
 
824
- By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
1128
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.