feat(core): Add step-based progress tracking, task metadata and enhanced TaskManager scheduling/metadata APIs
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										12
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,5 +1,17 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-06 - 3.2.0 - feat(core)
 | 
				
			||||||
 | 
					Add step-based progress tracking, task metadata and enhanced TaskManager scheduling/metadata APIs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Introduce TaskStep class for named, weighted steps with timing and status (pending|active|completed).
 | 
				
			||||||
 | 
					- Add step-tracking to Task: notifyStep, getProgress, getStepsMetadata, getMetadata, resetSteps and internal step lifecycle handling.
 | 
				
			||||||
 | 
					- Task now records runCount and lastRun; Task.run flow resets/cleans steps and aggregates progress.
 | 
				
			||||||
 | 
					- TaskManager enhancements: schedule/deschedule improvements, performDistributedConsultation, and new metadata-focused APIs: getTaskMetadata, getAllTasksMetadata, getScheduledTasks, getNextScheduledRuns, addExecuteRemoveTask (exec + collect report).
 | 
				
			||||||
 | 
					- Exports updated: TaskStep and related types exported from index, plus Task metadata interfaces.
 | 
				
			||||||
 | 
					- Comprehensive README updates documenting step-based progress tracking, metadata, TaskManager and examples.
 | 
				
			||||||
 | 
					- New/updated tests added for step behavior and metadata (test/test.9.steps.ts) and other TS additions.
 | 
				
			||||||
 | 
					- Minor build/script change: build script updated to use 'tsbuild tsfolders'.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-08-26 - 3.1.10 - fix(task)
 | 
					## 2025-08-26 - 3.1.10 - fix(task)
 | 
				
			||||||
Implement core Task execution flow, buffering and lifecycle; update README with generics and buffer docs
 | 
					Implement core Task execution flow, buffering and lifecycle; update README with generics and buffer docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@
 | 
				
			|||||||
  "type": "module",
 | 
					  "type": "module",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "test": "(tstest test/ --verbose --logfile --timeout 120)",
 | 
					    "test": "(tstest test/ --verbose --logfile --timeout 120)",
 | 
				
			||||||
    "build": "(tsbuild --web && tsbundle npm)",
 | 
					    "build": "(tsbuild tsfolders)",
 | 
				
			||||||
    "buildDocs": "tsdoc"
 | 
					    "buildDocs": "tsdoc"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										496
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										496
									
								
								readme.md
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
# @push.rocks/taskbuffer 🚀
 | 
					# @push.rocks/taskbuffer 🚀
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.
 | 
					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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Install 📦
 | 
					## Install 📦
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,35 +23,33 @@ In the modern JavaScript ecosystem, managing asynchronous tasks efficiently is c
 | 
				
			|||||||
- **🔄 Smart buffering**: Control concurrent executions with intelligent buffer management
 | 
					- **🔄 Smart buffering**: Control concurrent executions with intelligent buffer management
 | 
				
			||||||
- **⏰ Built-in scheduling**: Cron-based task scheduling without additional dependencies
 | 
					- **⏰ Built-in scheduling**: Cron-based task scheduling without additional dependencies
 | 
				
			||||||
- **🎭 Multiple paradigms**: Support for debounced, throttled, and one-time execution patterns
 | 
					- **🎭 Multiple paradigms**: Support for debounced, throttled, and one-time execution patterns
 | 
				
			||||||
 | 
					- **📊 Progress tracking**: Real-time step-by-step progress monitoring for UI integration
 | 
				
			||||||
- **🔌 Extensible**: Clean architecture that's easy to extend and customize
 | 
					- **🔌 Extensible**: Clean architecture that's easy to extend and customize
 | 
				
			||||||
- **🏃 Zero dependencies on external schedulers**: Everything you need is included
 | 
					- **🏃 Zero dependencies on external schedulers**: Everything you need is included
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Core Concepts 🎓
 | 
					## Core Concepts 🎓
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Task
 | 
					### Task
 | 
				
			||||||
 | 
					The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control, now with step-by-step progress tracking.
 | 
				
			||||||
The fundamental unit of work. A task wraps an asynchronous function and provides powerful execution control.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Taskchain
 | 
					### Taskchain
 | 
				
			||||||
 | 
					 | 
				
			||||||
Sequential task execution - tasks run one after another, with results passed along the chain.
 | 
					Sequential task execution - tasks run one after another, with results passed along the chain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Taskparallel
 | 
					### Taskparallel
 | 
				
			||||||
 | 
					 | 
				
			||||||
Parallel task execution - multiple tasks run simultaneously for maximum performance.
 | 
					Parallel task execution - multiple tasks run simultaneously for maximum performance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### TaskManager
 | 
					### TaskManager
 | 
				
			||||||
 | 
					Centralized task scheduling and management using cron expressions, with rich metadata collection.
 | 
				
			||||||
Centralized task scheduling and management using cron expressions.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### TaskDebounced
 | 
					### TaskDebounced
 | 
				
			||||||
 | 
					 | 
				
			||||||
Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
 | 
					Debounced task execution - prevents rapid repeated executions, only running after a quiet period.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### TaskOnce
 | 
					### TaskOnce
 | 
				
			||||||
 | 
					 | 
				
			||||||
Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
 | 
					Singleton task execution - ensures a task runs exactly once, perfect for initialization routines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### TaskStep 🆕
 | 
				
			||||||
 | 
					Granular progress tracking - define named steps with percentage weights for real-time progress monitoring.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Quick Start 🏁
 | 
					## Quick Start 🏁
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Basic Task Execution
 | 
					### Basic Task Execution
 | 
				
			||||||
@@ -72,6 +70,42 @@ const myTask = new Task({
 | 
				
			|||||||
const result = await myTask.trigger();
 | 
					const result = await myTask.trigger();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Task with Progress Steps 🆕
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Track granular progress for complex operations - perfect for UI progress bars:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const dataProcessingTask = new Task({
 | 
				
			||||||
 | 
					  name: 'DataProcessor',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'validate', description: 'Validating input data', percentage: 15 },
 | 
				
			||||||
 | 
					    { name: 'fetch', description: 'Fetching external resources', percentage: 25 },
 | 
				
			||||||
 | 
					    { name: 'transform', description: 'Transforming data', percentage: 35 },
 | 
				
			||||||
 | 
					    { name: 'save', description: 'Saving to database', percentage: 25 }
 | 
				
			||||||
 | 
					  ] as const, // Use 'as const' for full type safety
 | 
				
			||||||
 | 
					  taskFunction: async (inputData) => {
 | 
				
			||||||
 | 
					    // TypeScript knows these step names!
 | 
				
			||||||
 | 
					    dataProcessingTask.notifyStep('validate');
 | 
				
			||||||
 | 
					    const validated = await validateData(inputData);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    dataProcessingTask.notifyStep('fetch');
 | 
				
			||||||
 | 
					    const external = await fetchExternalData();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    dataProcessingTask.notifyStep('transform');
 | 
				
			||||||
 | 
					    const transformed = await transformData(validated, external);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    dataProcessingTask.notifyStep('save');
 | 
				
			||||||
 | 
					    const result = await saveToDatabase(transformed);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Monitor progress in real-time
 | 
				
			||||||
 | 
					const result = await dataProcessingTask.trigger();
 | 
				
			||||||
 | 
					console.log(`Final progress: ${dataProcessingTask.getProgress()}%`); // 100%
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## TypeScript Generics Support 🔬
 | 
					## TypeScript Generics Support 🔬
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TaskBuffer leverages TypeScript's powerful generics system for complete type safety across your task chains and workflows.
 | 
					TaskBuffer leverages TypeScript's powerful generics system for complete type safety across your task chains and workflows.
 | 
				
			||||||
@@ -97,7 +131,7 @@ interface ProcessedUser {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create strongly typed tasks
 | 
					// Create strongly typed tasks
 | 
				
			||||||
const processUserTask = new Task<UserData, ProcessedUser>({
 | 
					const processUserTask = new Task<ProcessedUser>({
 | 
				
			||||||
  name: 'ProcessUser',
 | 
					  name: 'ProcessUser',
 | 
				
			||||||
  taskFunction: async (user: UserData): Promise<ProcessedUser> => {
 | 
					  taskFunction: async (user: UserData): Promise<ProcessedUser> => {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@@ -129,7 +163,7 @@ interface TaskConfig {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const configuredTask = new Task<TaskConfig>({
 | 
					const configuredTask = new Task<TaskConfig>({
 | 
				
			||||||
  name: 'ConfiguredTask',
 | 
					  name: 'ConfiguredTask',
 | 
				
			||||||
  taskSetup: async () => ({
 | 
					  taskSetup: async (): Promise<TaskConfig> => ({
 | 
				
			||||||
    apiEndpoint: 'https://api.example.com',
 | 
					    apiEndpoint: 'https://api.example.com',
 | 
				
			||||||
    retryCount: 3,
 | 
					    retryCount: 3,
 | 
				
			||||||
    timeout: 5000
 | 
					    timeout: 5000
 | 
				
			||||||
@@ -156,21 +190,21 @@ Chain tasks with preserved type flow:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
// Each task knows its input and output types
 | 
					// Each task knows its input and output types
 | 
				
			||||||
const fetchTask = new Task<void, UserData[]>({
 | 
					const fetchTask = new Task<void>({
 | 
				
			||||||
  name: 'FetchUsers',
 | 
					  name: 'FetchUsers',
 | 
				
			||||||
  taskFunction: async (): Promise<UserData[]> => {
 | 
					  taskFunction: async (): Promise<UserData[]> => {
 | 
				
			||||||
    return await api.getUsers();
 | 
					    return await api.getUsers();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const filterTask = new Task<UserData[], UserData[]>({
 | 
					const filterTask = new Task<void>({
 | 
				
			||||||
  name: 'FilterActive',
 | 
					  name: 'FilterActive',
 | 
				
			||||||
  taskFunction: async (users: UserData[]): Promise<UserData[]> => {
 | 
					  taskFunction: async (users: UserData[]): Promise<UserData[]> => {
 | 
				
			||||||
    return users.filter(user => user.isActive);
 | 
					    return users.filter(user => user.isActive);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapTask = new Task<UserData[], ProcessedUser[]>({
 | 
					const mapTask = new Task<void>({
 | 
				
			||||||
  name: 'MapToProcessed',
 | 
					  name: 'MapToProcessed',
 | 
				
			||||||
  taskFunction: async (users: UserData[]): Promise<ProcessedUser[]> => {
 | 
					  taskFunction: async (users: UserData[]): Promise<ProcessedUser[]> => {
 | 
				
			||||||
    return users.map(transformUser);
 | 
					    return users.map(transformUser);
 | 
				
			||||||
@@ -186,6 +220,204 @@ const chain = new Taskchain({
 | 
				
			|||||||
const finalResult: ProcessedUser[] = await chain.trigger();
 | 
					const finalResult: ProcessedUser[] = await chain.trigger();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Progress Tracking & Metadata 📊 🆕
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TaskBuffer now provides comprehensive progress tracking and metadata collection, perfect for building dashboards and monitoring systems.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Step-by-Step Progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Define weighted steps for accurate progress calculation:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const migrationTask = new Task({
 | 
				
			||||||
 | 
					  name: 'DatabaseMigration',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'backup', description: 'Backing up database', percentage: 20 },
 | 
				
			||||||
 | 
					    { name: 'schema', description: 'Updating schema', percentage: 30 },
 | 
				
			||||||
 | 
					    { name: 'data', description: 'Migrating data', percentage: 40 },
 | 
				
			||||||
 | 
					    { name: 'validate', description: 'Validating integrity', percentage: 10 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
 | 
					  taskFunction: async () => {
 | 
				
			||||||
 | 
					    migrationTask.notifyStep('backup');
 | 
				
			||||||
 | 
					    await backupDatabase();
 | 
				
			||||||
 | 
					    console.log(`Progress: ${migrationTask.getProgress()}%`); // ~20%
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    migrationTask.notifyStep('schema');
 | 
				
			||||||
 | 
					    await updateSchema();
 | 
				
			||||||
 | 
					    console.log(`Progress: ${migrationTask.getProgress()}%`); // ~50%
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    migrationTask.notifyStep('data');
 | 
				
			||||||
 | 
					    await migrateData();
 | 
				
			||||||
 | 
					    console.log(`Progress: ${migrationTask.getProgress()}%`); // ~90%
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    migrationTask.notifyStep('validate');
 | 
				
			||||||
 | 
					    await validateIntegrity();
 | 
				
			||||||
 | 
					    console.log(`Progress: ${migrationTask.getProgress()}%`); // 100%
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get detailed step information
 | 
				
			||||||
 | 
					const steps = migrationTask.getStepsMetadata();
 | 
				
			||||||
 | 
					steps.forEach(step => {
 | 
				
			||||||
 | 
					  console.log(`${step.name}: ${step.status} (${step.percentage}%)`);
 | 
				
			||||||
 | 
					  if (step.duration) {
 | 
				
			||||||
 | 
					    console.log(`  Duration: ${step.duration}ms`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Task Metadata Collection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Get comprehensive metadata about task execution:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const task = new Task({
 | 
				
			||||||
 | 
					  name: 'DataProcessor',
 | 
				
			||||||
 | 
					  buffered: true,
 | 
				
			||||||
 | 
					  bufferMax: 5,
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'process', description: 'Processing', percentage: 100 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
 | 
					  taskFunction: async () => {
 | 
				
			||||||
 | 
					    task.notifyStep('process');
 | 
				
			||||||
 | 
					    await processData();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get complete task metadata
 | 
				
			||||||
 | 
					const metadata = task.getMetadata();
 | 
				
			||||||
 | 
					console.log({
 | 
				
			||||||
 | 
					  name: metadata.name,
 | 
				
			||||||
 | 
					  status: metadata.status, // 'idle' | 'running' | 'completed' | 'failed'
 | 
				
			||||||
 | 
					  progress: metadata.currentProgress, // 0-100
 | 
				
			||||||
 | 
					  currentStep: metadata.currentStep,
 | 
				
			||||||
 | 
					  runCount: metadata.runCount,
 | 
				
			||||||
 | 
					  lastRun: metadata.lastRun,
 | 
				
			||||||
 | 
					  buffered: metadata.buffered,
 | 
				
			||||||
 | 
					  bufferMax: metadata.bufferMax
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### TaskManager Enhanced Metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The TaskManager now provides rich metadata for monitoring and dashboards:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const manager = new TaskManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add tasks with step tracking
 | 
				
			||||||
 | 
					manager.addAndScheduleTask(backupTask, '0 2 * * *'); // 2 AM daily
 | 
				
			||||||
 | 
					manager.addAndScheduleTask(cleanupTask, '0 */6 * * *'); // Every 6 hours
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get metadata for all tasks
 | 
				
			||||||
 | 
					const allTasksMetadata = manager.getAllTasksMetadata();
 | 
				
			||||||
 | 
					allTasksMetadata.forEach(task => {
 | 
				
			||||||
 | 
					  console.log(`Task: ${task.name}`);
 | 
				
			||||||
 | 
					  console.log(`  Status: ${task.status}`);
 | 
				
			||||||
 | 
					  console.log(`  Progress: ${task.currentProgress}%`);
 | 
				
			||||||
 | 
					  console.log(`  Run count: ${task.runCount}`);
 | 
				
			||||||
 | 
					  console.log(`  Schedule: ${task.cronSchedule}`);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get scheduled tasks with next run times
 | 
				
			||||||
 | 
					const scheduledTasks = manager.getScheduledTasks();
 | 
				
			||||||
 | 
					scheduledTasks.forEach(task => {
 | 
				
			||||||
 | 
					  console.log(`${task.name}: Next run at ${task.nextRun}`);
 | 
				
			||||||
 | 
					  if (task.steps) {
 | 
				
			||||||
 | 
					    console.log(`  Steps: ${task.steps.length}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get upcoming executions
 | 
				
			||||||
 | 
					const nextRuns = manager.getNextScheduledRuns(10);
 | 
				
			||||||
 | 
					console.log('Next 10 scheduled executions:', nextRuns);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Execute and Track Tasks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Execute tasks with full lifecycle tracking and automatic cleanup:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const manager = new TaskManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const analyticsTask = new Task({
 | 
				
			||||||
 | 
					  name: 'Analytics',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'collect', description: 'Collecting metrics', percentage: 30 },
 | 
				
			||||||
 | 
					    { name: 'analyze', description: 'Analyzing data', percentage: 50 },
 | 
				
			||||||
 | 
					    { name: 'report', description: 'Generating report', percentage: 20 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
 | 
					  taskFunction: async () => {
 | 
				
			||||||
 | 
					    analyticsTask.notifyStep('collect');
 | 
				
			||||||
 | 
					    const metrics = await collectMetrics();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    analyticsTask.notifyStep('analyze');
 | 
				
			||||||
 | 
					    const analysis = await analyzeData(metrics);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    analyticsTask.notifyStep('report');
 | 
				
			||||||
 | 
					    return await generateReport(analysis);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Execute with automatic cleanup and metadata collection
 | 
				
			||||||
 | 
					const report = await manager.addExecuteRemoveTask(analyticsTask, {
 | 
				
			||||||
 | 
					  trackProgress: true
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log('Execution Report:', {
 | 
				
			||||||
 | 
					  taskName: report.taskName,
 | 
				
			||||||
 | 
					  duration: report.duration,
 | 
				
			||||||
 | 
					  stepsCompleted: report.stepsCompleted,
 | 
				
			||||||
 | 
					  finalProgress: report.progress,
 | 
				
			||||||
 | 
					  result: report.result
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Frontend Integration Example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Perfect for building real-time progress UIs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					// WebSocket server for real-time updates
 | 
				
			||||||
 | 
					io.on('connection', (socket) => {
 | 
				
			||||||
 | 
					  socket.on('startTask', async (taskId) => {
 | 
				
			||||||
 | 
					    const task = new Task({
 | 
				
			||||||
 | 
					      name: taskId,
 | 
				
			||||||
 | 
					      steps: [
 | 
				
			||||||
 | 
					        { name: 'start', description: 'Starting...', percentage: 10 },
 | 
				
			||||||
 | 
					        { name: 'process', description: 'Processing...', percentage: 70 },
 | 
				
			||||||
 | 
					        { name: 'finish', description: 'Finishing...', percentage: 20 }
 | 
				
			||||||
 | 
					      ] as const,
 | 
				
			||||||
 | 
					      taskFunction: async () => {
 | 
				
			||||||
 | 
					        task.notifyStep('start');
 | 
				
			||||||
 | 
					        socket.emit('progress', { 
 | 
				
			||||||
 | 
					          step: 'start', 
 | 
				
			||||||
 | 
					          progress: task.getProgress(),
 | 
				
			||||||
 | 
					          metadata: task.getStepsMetadata()
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        task.notifyStep('process');
 | 
				
			||||||
 | 
					        socket.emit('progress', { 
 | 
				
			||||||
 | 
					          step: 'process', 
 | 
				
			||||||
 | 
					          progress: task.getProgress(),
 | 
				
			||||||
 | 
					          metadata: task.getStepsMetadata()
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        task.notifyStep('finish');
 | 
				
			||||||
 | 
					        socket.emit('progress', { 
 | 
				
			||||||
 | 
					          step: 'finish', 
 | 
				
			||||||
 | 
					          progress: task.getProgress(),
 | 
				
			||||||
 | 
					          metadata: task.getStepsMetadata()
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    await task.trigger();
 | 
				
			||||||
 | 
					    socket.emit('complete', task.getMetadata());
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Buffer Behavior Deep Dive 🌊
 | 
					## Buffer Behavior Deep Dive 🌊
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The buffer system in TaskBuffer provides intelligent control over concurrent executions, preventing system overload while maximizing throughput.
 | 
					The buffer system in TaskBuffer provides intelligent control over concurrent executions, preventing system overload while maximizing throughput.
 | 
				
			||||||
@@ -388,26 +620,7 @@ setInterval(() => {
 | 
				
			|||||||
   });
 | 
					   });
 | 
				
			||||||
   ```
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Buffered Execution (Rate Limiting)
 | 
					## Common Patterns 🎨
 | 
				
			||||||
 | 
					 | 
				
			||||||
Perfect for API calls or database operations that need throttling:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```typescript
 | 
					 | 
				
			||||||
const apiTask = new Task({
 | 
					 | 
				
			||||||
  name: 'APICall',
 | 
					 | 
				
			||||||
  taskFunction: async (endpoint: string) => {
 | 
					 | 
				
			||||||
    return await fetch(endpoint);
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  buffered: true,
 | 
					 | 
				
			||||||
  bufferMax: 3, // Maximum 3 concurrent executions
 | 
					 | 
				
			||||||
  execDelay: 1000, // Wait 1 second between executions
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// These will be automatically throttled
 | 
					 | 
				
			||||||
for (let i = 0; i < 10; i++) {
 | 
					 | 
				
			||||||
  apiTask.trigger(`/api/data/${i}`);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Task Chains - Sequential Workflows
 | 
					### Task Chains - Sequential Workflows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -488,8 +701,17 @@ import { Task, TaskManager } from '@push.rocks/taskbuffer';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const backupTask = new Task({
 | 
					const backupTask = new Task({
 | 
				
			||||||
  name: 'DatabaseBackup',
 | 
					  name: 'DatabaseBackup',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'dump', description: 'Creating dump', percentage: 70 },
 | 
				
			||||||
 | 
					    { name: 'upload', description: 'Uploading to S3', percentage: 30 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
  taskFunction: async () => {
 | 
					  taskFunction: async () => {
 | 
				
			||||||
 | 
					    backupTask.notifyStep('dump');
 | 
				
			||||||
    await performBackup();
 | 
					    await performBackup();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    backupTask.notifyStep('upload');
 | 
				
			||||||
 | 
					    await uploadToS3();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    console.log(`Backup completed at ${new Date().toISOString()}`);
 | 
					    console.log(`Backup completed at ${new Date().toISOString()}`);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -498,11 +720,14 @@ const manager = new TaskManager();
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Add and schedule tasks
 | 
					// Add and schedule tasks
 | 
				
			||||||
manager.addAndScheduleTask(backupTask, '0 0 * * *'); // Daily at midnight
 | 
					manager.addAndScheduleTask(backupTask, '0 0 * * *'); // Daily at midnight
 | 
				
			||||||
manager.addAndScheduleTask(healthCheck, '*/5 * * * *'); // Every 5 minutes
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Start the scheduler
 | 
					// Start the scheduler
 | 
				
			||||||
manager.start();
 | 
					manager.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Monitor scheduled tasks
 | 
				
			||||||
 | 
					const scheduled = manager.getScheduledTasks();
 | 
				
			||||||
 | 
					console.log('Scheduled tasks:', scheduled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Later... stop if needed
 | 
					// Later... stop if needed
 | 
				
			||||||
manager.stop();
 | 
					manager.stop();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@@ -598,45 +823,6 @@ runner.registerTask(imageResizeTask);
 | 
				
			|||||||
runner.start();
 | 
					runner.start();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Buffer Management Strategies
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fine-tune concurrent execution behavior:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```typescript
 | 
					 | 
				
			||||||
const task = new Task({
 | 
					 | 
				
			||||||
  name: 'ResourceIntensive',
 | 
					 | 
				
			||||||
  taskFunction: async () => {
 | 
					 | 
				
			||||||
    /* ... */
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  buffered: true,
 | 
					 | 
				
			||||||
  bufferMax: 5, // Max 5 concurrent
 | 
					 | 
				
			||||||
  execDelay: 100, // 100ms between starts
 | 
					 | 
				
			||||||
  timeout: 30000, // 30 second timeout
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Cycle Detection and Prevention
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TaskBuffer automatically detects and prevents circular dependencies:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```typescript
 | 
					 | 
				
			||||||
const taskA = new Task({
 | 
					 | 
				
			||||||
  name: 'TaskA',
 | 
					 | 
				
			||||||
  taskFunction: async () => {
 | 
					 | 
				
			||||||
    /* ... */
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  preTask: taskB, // This would create a cycle
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const taskB = new Task({
 | 
					 | 
				
			||||||
  name: 'TaskB',
 | 
					 | 
				
			||||||
  taskFunction: async () => {
 | 
					 | 
				
			||||||
    /* ... */
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  preTask: taskA, // Circular dependency detected!
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Dynamic Task Creation
 | 
					### Dynamic Task Creation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Create tasks on-the-fly based on runtime conditions:
 | 
					Create tasks on-the-fly based on runtime conditions:
 | 
				
			||||||
@@ -647,8 +833,17 @@ const dynamicWorkflow = async (config: Config) => {
 | 
				
			|||||||
    (step) =>
 | 
					    (step) =>
 | 
				
			||||||
      new Task({
 | 
					      new Task({
 | 
				
			||||||
        name: step.name,
 | 
					        name: step.name,
 | 
				
			||||||
 | 
					        steps: step.substeps?.map(s => ({
 | 
				
			||||||
 | 
					          name: s.id,
 | 
				
			||||||
 | 
					          description: s.label,
 | 
				
			||||||
 | 
					          percentage: s.weight
 | 
				
			||||||
 | 
					        })) as const,
 | 
				
			||||||
        taskFunction: async (input) => {
 | 
					        taskFunction: async (input) => {
 | 
				
			||||||
          return await processStep(step, input);
 | 
					          for (const substep of step.substeps || []) {
 | 
				
			||||||
 | 
					            task.notifyStep(substep.id);
 | 
				
			||||||
 | 
					            await processStep(substep, input);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return input;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@@ -667,9 +862,10 @@ const dynamicWorkflow = async (config: Config) => {
 | 
				
			|||||||
### Task Options
 | 
					### Task Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Option         | Type       | Description                            |
 | 
					| Option         | Type       | Description                            |
 | 
				
			||||||
| -------------- | ---------- | ------------------------------ |
 | 
					| -------------- | ---------- | -------------------------------------- |
 | 
				
			||||||
| `name`         | `string`   | Unique identifier for the task         |
 | 
					| `name`         | `string`   | Unique identifier for the task         |
 | 
				
			||||||
| `taskFunction` | `Function` | Async function to execute              |
 | 
					| `taskFunction` | `Function` | Async function to execute              |
 | 
				
			||||||
 | 
					| `steps`        | `Array`    | Step definitions with name, description, percentage |
 | 
				
			||||||
| `buffered`     | `boolean`  | Enable buffer management               |
 | 
					| `buffered`     | `boolean`  | Enable buffer management               |
 | 
				
			||||||
| `bufferMax`    | `number`   | Maximum concurrent executions          |
 | 
					| `bufferMax`    | `number`   | Maximum concurrent executions          |
 | 
				
			||||||
| `execDelay`    | `number`   | Delay between executions (ms)          |
 | 
					| `execDelay`    | `number`   | Delay between executions (ms)          |
 | 
				
			||||||
@@ -677,15 +873,34 @@ const dynamicWorkflow = async (config: Config) => {
 | 
				
			|||||||
| `preTask`      | `Task`     | Task to run before                     |
 | 
					| `preTask`      | `Task`     | Task to run before                     |
 | 
				
			||||||
| `afterTask`    | `Task`     | Task to run after                      |
 | 
					| `afterTask`    | `Task`     | Task to run after                      |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Task Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Method                    | Description                                    |
 | 
				
			||||||
 | 
					| ------------------------- | ---------------------------------------------- |
 | 
				
			||||||
 | 
					| `trigger(x?)`             | Execute the task                              |
 | 
				
			||||||
 | 
					| `notifyStep(stepName)`    | Mark a step as active (typed step names!)     |
 | 
				
			||||||
 | 
					| `getProgress()`           | Get current progress percentage (0-100)       |
 | 
				
			||||||
 | 
					| `getStepsMetadata()`      | Get all steps with their current status       |
 | 
				
			||||||
 | 
					| `getMetadata()`           | Get complete task metadata                    |
 | 
				
			||||||
 | 
					| `resetSteps()`            | Reset all steps to pending state              |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### TaskManager Methods
 | 
					### TaskManager Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Method                              | Description                            |
 | 
					| Method                              | Description                            |
 | 
				
			||||||
| ------------------------------- | ------------------------ |
 | 
					| ----------------------------------- | -------------------------------------- |
 | 
				
			||||||
| `addTask(task, cronExpression)` | Add and schedule a task  |
 | 
					| `addTask(task)`                     | Add a task to the manager              |
 | 
				
			||||||
| `removeTask(taskName)`          | Remove a scheduled task  |
 | 
					| `addAndScheduleTask(task, cron)`    | Add and schedule a task                |
 | 
				
			||||||
 | 
					| `getTaskByName(name)`               | Get a specific task by name            |
 | 
				
			||||||
 | 
					| `getTaskMetadata(name)`             | Get metadata for a specific task       |
 | 
				
			||||||
 | 
					| `getAllTasksMetadata()`             | Get metadata for all tasks             |
 | 
				
			||||||
 | 
					| `getScheduledTasks()`               | Get all scheduled tasks with info      |
 | 
				
			||||||
 | 
					| `getNextScheduledRuns(limit)`       | Get upcoming scheduled executions      |
 | 
				
			||||||
 | 
					| `addExecuteRemoveTask(task, opts)`  | Execute task with lifecycle tracking   |
 | 
				
			||||||
 | 
					| `triggerTaskByName(name)`           | Trigger a task by its name             |
 | 
				
			||||||
 | 
					| `scheduleTaskByName(name, cron)`    | Schedule a task using cron expression  |
 | 
				
			||||||
 | 
					| `descheduleTaskByName(name)`        | Remove task from schedule              |
 | 
				
			||||||
| `start()`                           | Start the scheduler                    |
 | 
					| `start()`                           | Start the scheduler                    |
 | 
				
			||||||
| `stop()`                            | Stop the scheduler                     |
 | 
					| `stop()`                            | Stop the scheduler                     |
 | 
				
			||||||
| `getStats()`                    | Get execution statistics |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Taskchain Methods
 | 
					### Taskchain Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -703,14 +918,20 @@ const dynamicWorkflow = async (config: Config) => {
 | 
				
			|||||||
3. **Implement proper error handling**: Use try-catch in task functions
 | 
					3. **Implement proper error handling**: Use try-catch in task functions
 | 
				
			||||||
4. **Monitor task execution**: Use the built-in stats and logging
 | 
					4. **Monitor task execution**: Use the built-in stats and logging
 | 
				
			||||||
5. **Set appropriate timeouts**: Prevent hanging tasks from blocking your system
 | 
					5. **Set appropriate timeouts**: Prevent hanging tasks from blocking your system
 | 
				
			||||||
 | 
					6. **Use step tracking wisely**: Don't create too many granular steps - aim for meaningful progress points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Error Handling 🛡️
 | 
					## Error Handling 🛡️
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
const robustTask = new Task({
 | 
					const robustTask = new Task({
 | 
				
			||||||
  name: 'RobustOperation',
 | 
					  name: 'RobustOperation',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'try', description: 'Attempting operation', percentage: 80 },
 | 
				
			||||||
 | 
					    { name: 'retry', description: 'Retrying on failure', percentage: 20 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
  taskFunction: async (input) => {
 | 
					  taskFunction: async (input) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
 | 
					      robustTask.notifyStep('try');
 | 
				
			||||||
      return await riskyOperation(input);
 | 
					      return await riskyOperation(input);
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      // Log error
 | 
					      // Log error
 | 
				
			||||||
@@ -718,6 +939,7 @@ const robustTask = new Task({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Optionally retry
 | 
					      // Optionally retry
 | 
				
			||||||
      if (error.retryable) {
 | 
					      if (error.retryable) {
 | 
				
			||||||
 | 
					        robustTask.notifyStep('retry');
 | 
				
			||||||
        return await riskyOperation(input);
 | 
					        return await riskyOperation(input);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -731,12 +953,20 @@ const robustTask = new Task({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Real-World Examples 🌍
 | 
					## Real-World Examples 🌍
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### API Rate Limiting
 | 
					### API Rate Limiting with Progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
const apiClient = new Task({
 | 
					const apiClient = new Task({
 | 
				
			||||||
  name: 'RateLimitedAPI',
 | 
					  name: 'RateLimitedAPI',
 | 
				
			||||||
 | 
					  steps: [
 | 
				
			||||||
 | 
					    { name: 'wait', description: 'Rate limit delay', percentage: 10 },
 | 
				
			||||||
 | 
					    { name: 'call', description: 'API call', percentage: 90 }
 | 
				
			||||||
 | 
					  ] as const,
 | 
				
			||||||
  taskFunction: async (endpoint: string) => {
 | 
					  taskFunction: async (endpoint: string) => {
 | 
				
			||||||
 | 
					    apiClient.notifyStep('wait');
 | 
				
			||||||
 | 
					    await delay(100); // Rate limiting
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    apiClient.notifyStep('call');
 | 
				
			||||||
    return await fetch(`https://api.example.com${endpoint}`);
 | 
					    return await fetch(`https://api.example.com${endpoint}`);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  buffered: true,
 | 
					  buffered: true,
 | 
				
			||||||
@@ -745,22 +975,43 @@ const apiClient = new Task({
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Database Migration Pipeline
 | 
					### Database Migration Pipeline with Progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
const migrationChain = new Taskchain({
 | 
					const migrationChain = new Taskchain({
 | 
				
			||||||
  name: 'DatabaseMigration',
 | 
					  name: 'DatabaseMigration',
 | 
				
			||||||
  taskArray: [
 | 
					  taskArray: [
 | 
				
			||||||
    backupTask,
 | 
					    new Task({
 | 
				
			||||||
    schemaUpdateTask,
 | 
					      name: 'Backup',
 | 
				
			||||||
    dataTransformTask,
 | 
					      steps: [{ name: 'backup', description: 'Creating backup', percentage: 100 }] as const,
 | 
				
			||||||
    validationTask,
 | 
					      taskFunction: async () => {
 | 
				
			||||||
    cleanupTask,
 | 
					        backupTask.notifyStep('backup');
 | 
				
			||||||
 | 
					        return await createBackup();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    new Task({
 | 
				
			||||||
 | 
					      name: 'SchemaUpdate',
 | 
				
			||||||
 | 
					      steps: [
 | 
				
			||||||
 | 
					        { name: 'analyze', description: 'Analyzing changes', percentage: 30 },
 | 
				
			||||||
 | 
					        { name: 'apply', description: 'Applying migrations', percentage: 70 }
 | 
				
			||||||
 | 
					      ] as const,
 | 
				
			||||||
 | 
					      taskFunction: async () => {
 | 
				
			||||||
 | 
					        schemaTask.notifyStep('analyze');
 | 
				
			||||||
 | 
					        const changes = await analyzeSchema();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        schemaTask.notifyStep('apply');
 | 
				
			||||||
 | 
					        return await applyMigrations(changes);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    // ... more tasks
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Execute with progress monitoring
 | 
				
			||||||
 | 
					const result = await migrationChain.trigger();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Microservice Health Monitoring
 | 
					### Microservice Health Monitoring Dashboard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
const healthMonitor = new TaskManager();
 | 
					const healthMonitor = new TaskManager();
 | 
				
			||||||
@@ -768,36 +1019,89 @@ const healthMonitor = new TaskManager();
 | 
				
			|||||||
services.forEach((service) => {
 | 
					services.forEach((service) => {
 | 
				
			||||||
  const healthCheck = new Task({
 | 
					  const healthCheck = new Task({
 | 
				
			||||||
    name: `HealthCheck:${service.name}`,
 | 
					    name: `HealthCheck:${service.name}`,
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'ping', description: 'Pinging service', percentage: 30 },
 | 
				
			||||||
 | 
					      { name: 'check', description: 'Checking health', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'report', description: 'Reporting status', percentage: 20 }
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
    taskFunction: async () => {
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      healthCheck.notifyStep('ping');
 | 
				
			||||||
 | 
					      const responsive = await ping(service.url);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      healthCheck.notifyStep('check');
 | 
				
			||||||
      const healthy = await checkHealth(service.url);
 | 
					      const healthy = await checkHealth(service.url);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      healthCheck.notifyStep('report');
 | 
				
			||||||
      if (!healthy) {
 | 
					      if (!healthy) {
 | 
				
			||||||
        await alertOps(service);
 | 
					        await alertOps(service);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return { service: service.name, healthy, timestamp: Date.now() };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
 | 
					  healthMonitor.addAndScheduleTask(healthCheck, '*/1 * * * *'); // Every minute
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Dashboard endpoint
 | 
				
			||||||
 | 
					app.get('/api/health/dashboard', (req, res) => {
 | 
				
			||||||
 | 
					  const metadata = healthMonitor.getAllTasksMetadata();
 | 
				
			||||||
 | 
					  res.json({
 | 
				
			||||||
 | 
					    services: metadata.map(task => ({
 | 
				
			||||||
 | 
					      name: task.name.replace('HealthCheck:', ''),
 | 
				
			||||||
 | 
					      status: task.status,
 | 
				
			||||||
 | 
					      lastCheck: task.lastRun,
 | 
				
			||||||
 | 
					      nextCheck: healthMonitor.getScheduledTasks()
 | 
				
			||||||
 | 
					        .find(s => s.name === task.name)?.nextRun,
 | 
				
			||||||
 | 
					      progress: task.currentProgress,
 | 
				
			||||||
 | 
					      currentStep: task.currentStep
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Testing 🧪
 | 
					## Testing 🧪
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
import { expect, tap } from '@git.zone/tstest';
 | 
					import { expect, tap } from '@git.zone/tstest';
 | 
				
			||||||
import { Task } from '@push.rocks/taskbuffer';
 | 
					import { Task, TaskStep } from '@push.rocks/taskbuffer';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tap.test('should execute task successfully', async () => {
 | 
					tap.test('should track task progress through steps', async () => {
 | 
				
			||||||
  const result = await myTask.trigger();
 | 
					  const task = new Task({
 | 
				
			||||||
  expect(result).toEqual(expectedValue);
 | 
					    name: 'TestTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'step1', description: 'First step', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'step2', description: 'Second step', percentage: 50 }
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task.notifyStep('step1');
 | 
				
			||||||
 | 
					      expect(task.getProgress()).toBeLessThanOrEqual(50);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      task.notifyStep('step2');
 | 
				
			||||||
 | 
					      expect(task.getProgress()).toBeLessThanOrEqual(100);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  expect(task.getProgress()).toEqual(100);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tap.test('should collect execution metadata', async () => {
 | 
				
			||||||
 | 
					  const manager = new TaskManager();
 | 
				
			||||||
 | 
					  const task = new Task({
 | 
				
			||||||
 | 
					    name: 'MetadataTest',
 | 
				
			||||||
 | 
					    taskFunction: async () => 'result'
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const report = await manager.addExecuteRemoveTask(task);
 | 
				
			||||||
 | 
					  expect(report.taskName).toEqual('MetadataTest');
 | 
				
			||||||
 | 
					  expect(report.result).toEqual('result');
 | 
				
			||||||
 | 
					  expect(report.duration).toBeGreaterThan(0);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tap.start();
 | 
					tap.start();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Contributing 🤝
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Support 💬
 | 
					## Support 💬
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- 📧 Email: [hello@task.vc](mailto:hello@task.vc)
 | 
					- 📧 Email: [hello@task.vc](mailto:hello@task.vc)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										376
									
								
								test/test.9.steps.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								test/test.9.steps.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,376 @@
 | 
				
			|||||||
 | 
					import { expect, tap } from '@git.zone/tstest/tapbundle';
 | 
				
			||||||
 | 
					import * as taskbuffer from '../ts/index.js';
 | 
				
			||||||
 | 
					import * as smartdelay from '@push.rocks/smartdelay';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test TaskStep class
 | 
				
			||||||
 | 
					tap.test('TaskStep should create and manage step state', async () => {
 | 
				
			||||||
 | 
					  const step = new taskbuffer.TaskStep({
 | 
				
			||||||
 | 
					    name: 'testStep',
 | 
				
			||||||
 | 
					    description: 'Test step description',
 | 
				
			||||||
 | 
					    percentage: 25,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  expect(step.name).toEqual('testStep');
 | 
				
			||||||
 | 
					  expect(step.description).toEqual('Test step description');
 | 
				
			||||||
 | 
					  expect(step.percentage).toEqual(25);
 | 
				
			||||||
 | 
					  expect(step.status).toEqual('pending');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test start
 | 
				
			||||||
 | 
					  step.start();
 | 
				
			||||||
 | 
					  expect(step.status).toEqual('active');
 | 
				
			||||||
 | 
					  expect(step.startTime).toBeDefined();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  await smartdelay.delayFor(100);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test complete
 | 
				
			||||||
 | 
					  step.complete();
 | 
				
			||||||
 | 
					  expect(step.status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(step.endTime).toBeDefined();
 | 
				
			||||||
 | 
					  expect(step.duration).toBeDefined();
 | 
				
			||||||
 | 
					  expect(step.duration).toBeGreaterThanOrEqual(100);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test reset
 | 
				
			||||||
 | 
					  step.reset();
 | 
				
			||||||
 | 
					  expect(step.status).toEqual('pending');
 | 
				
			||||||
 | 
					  expect(step.startTime).toBeUndefined();
 | 
				
			||||||
 | 
					  expect(step.endTime).toBeUndefined();
 | 
				
			||||||
 | 
					  expect(step.duration).toBeUndefined();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test Task with steps
 | 
				
			||||||
 | 
					tap.test('Task should support typed step notifications', async () => {
 | 
				
			||||||
 | 
					  const stepsExecuted: string[] = [];
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const task = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'SteppedTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'init', description: 'Initialize', percentage: 20 },
 | 
				
			||||||
 | 
					      { name: 'process', description: 'Process data', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'cleanup', description: 'Clean up', percentage: 30 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task.notifyStep('init');
 | 
				
			||||||
 | 
					      stepsExecuted.push('init');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      task.notifyStep('process');
 | 
				
			||||||
 | 
					      stepsExecuted.push('process');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(100);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      task.notifyStep('cleanup');
 | 
				
			||||||
 | 
					      stepsExecuted.push('cleanup');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  expect(stepsExecuted).toEqual(['init', 'process', 'cleanup']);
 | 
				
			||||||
 | 
					  expect(task.getProgress()).toEqual(100);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const metadata = task.getStepsMetadata();
 | 
				
			||||||
 | 
					  expect(metadata).toHaveLength(3);
 | 
				
			||||||
 | 
					  expect(metadata[0].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(metadata[1].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(metadata[2].status).toEqual('completed');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test progress calculation
 | 
				
			||||||
 | 
					tap.test('Task should calculate progress correctly', async () => {
 | 
				
			||||||
 | 
					  const progressValues: number[] = [];
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const task = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'ProgressTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'step1', description: 'Step 1', percentage: 25 },
 | 
				
			||||||
 | 
					      { name: 'step2', description: 'Step 2', percentage: 25 },
 | 
				
			||||||
 | 
					      { name: 'step3', description: 'Step 3', percentage: 50 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task.notifyStep('step1');
 | 
				
			||||||
 | 
					      progressValues.push(task.getProgress());
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      task.notifyStep('step2');
 | 
				
			||||||
 | 
					      progressValues.push(task.getProgress());
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      task.notifyStep('step3');
 | 
				
			||||||
 | 
					      progressValues.push(task.getProgress());
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // During execution, active steps count as 50% complete
 | 
				
			||||||
 | 
					  expect(progressValues[0]).toBeLessThanOrEqual(25); // step1 active (12.5%)
 | 
				
			||||||
 | 
					  expect(progressValues[1]).toBeLessThanOrEqual(50); // step1 done (25%) + step2 active (12.5%)
 | 
				
			||||||
 | 
					  expect(progressValues[2]).toBeLessThanOrEqual(100); // step1+2 done (50%) + step3 active (25%)
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // After completion, all steps should be done
 | 
				
			||||||
 | 
					  expect(task.getProgress()).toEqual(100);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test task metadata
 | 
				
			||||||
 | 
					tap.test('Task should provide complete metadata', async () => {
 | 
				
			||||||
 | 
					  const task = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'MetadataTask',
 | 
				
			||||||
 | 
					    buffered: true,
 | 
				
			||||||
 | 
					    bufferMax: 5,
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'step1', description: 'First step', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'step2', description: 'Second step', percentage: 50 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task.notifyStep('step1');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      task.notifyStep('step2');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Set version and timeout directly (as they're public properties)
 | 
				
			||||||
 | 
					  task.version = '1.0.0';
 | 
				
			||||||
 | 
					  task.timeout = 10000;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Get metadata before execution
 | 
				
			||||||
 | 
					  let metadata = task.getMetadata();
 | 
				
			||||||
 | 
					  expect(metadata.name).toEqual('MetadataTask');
 | 
				
			||||||
 | 
					  expect(metadata.version).toEqual('1.0.0');
 | 
				
			||||||
 | 
					  expect(metadata.status).toEqual('idle');
 | 
				
			||||||
 | 
					  expect(metadata.buffered).toEqual(true);
 | 
				
			||||||
 | 
					  expect(metadata.bufferMax).toEqual(5);
 | 
				
			||||||
 | 
					  expect(metadata.timeout).toEqual(10000);
 | 
				
			||||||
 | 
					  expect(metadata.runCount).toEqual(0);
 | 
				
			||||||
 | 
					  expect(metadata.steps).toHaveLength(2);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Execute task
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Get metadata after execution
 | 
				
			||||||
 | 
					  metadata = task.getMetadata();
 | 
				
			||||||
 | 
					  expect(metadata.status).toEqual('idle');
 | 
				
			||||||
 | 
					  expect(metadata.runCount).toEqual(1);
 | 
				
			||||||
 | 
					  expect(metadata.currentProgress).toEqual(100);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test TaskManager metadata methods
 | 
				
			||||||
 | 
					tap.test('TaskManager should provide task metadata', async () => {
 | 
				
			||||||
 | 
					  const taskManager = new taskbuffer.TaskManager();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const task1 = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'Task1',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'start', description: 'Starting', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'end', description: 'Ending', percentage: 50 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task1.notifyStep('start');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      task1.notifyStep('end');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const task2 = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'Task2',
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(100);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  taskManager.addTask(task1);
 | 
				
			||||||
 | 
					  taskManager.addTask(task2);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test getTaskMetadata
 | 
				
			||||||
 | 
					  const task1Metadata = taskManager.getTaskMetadata('Task1');
 | 
				
			||||||
 | 
					  expect(task1Metadata).toBeDefined();
 | 
				
			||||||
 | 
					  expect(task1Metadata!.name).toEqual('Task1');
 | 
				
			||||||
 | 
					  expect(task1Metadata!.steps).toHaveLength(2);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test getAllTasksMetadata
 | 
				
			||||||
 | 
					  const allMetadata = taskManager.getAllTasksMetadata();
 | 
				
			||||||
 | 
					  expect(allMetadata).toHaveLength(2);
 | 
				
			||||||
 | 
					  expect(allMetadata[0].name).toEqual('Task1');
 | 
				
			||||||
 | 
					  expect(allMetadata[1].name).toEqual('Task2');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test non-existent task
 | 
				
			||||||
 | 
					  const nonExistent = taskManager.getTaskMetadata('NonExistent');
 | 
				
			||||||
 | 
					  expect(nonExistent).toBeNull();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test TaskManager scheduled tasks
 | 
				
			||||||
 | 
					tap.test('TaskManager should track scheduled tasks', async () => {
 | 
				
			||||||
 | 
					  const taskManager = new taskbuffer.TaskManager();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const scheduledTask = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'ScheduledTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'execute', description: 'Executing', percentage: 100 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      scheduledTask.notifyStep('execute');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  taskManager.addAndScheduleTask(scheduledTask, '0 0 * * *'); // Daily at midnight
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test getScheduledTasks
 | 
				
			||||||
 | 
					  const scheduledTasks = taskManager.getScheduledTasks();
 | 
				
			||||||
 | 
					  expect(scheduledTasks).toHaveLength(1);
 | 
				
			||||||
 | 
					  expect(scheduledTasks[0].name).toEqual('ScheduledTask');
 | 
				
			||||||
 | 
					  expect(scheduledTasks[0].schedule).toEqual('0 0 * * *');
 | 
				
			||||||
 | 
					  expect(scheduledTasks[0].nextRun).toBeInstanceOf(Date);
 | 
				
			||||||
 | 
					  expect(scheduledTasks[0].steps).toHaveLength(1);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Test getNextScheduledRuns
 | 
				
			||||||
 | 
					  const nextRuns = taskManager.getNextScheduledRuns(5);
 | 
				
			||||||
 | 
					  expect(nextRuns).toHaveLength(1);
 | 
				
			||||||
 | 
					  expect(nextRuns[0].taskName).toEqual('ScheduledTask');
 | 
				
			||||||
 | 
					  expect(nextRuns[0].nextRun).toBeInstanceOf(Date);
 | 
				
			||||||
 | 
					  expect(nextRuns[0].schedule).toEqual('0 0 * * *');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Clean up
 | 
				
			||||||
 | 
					  taskManager.descheduleTaskByName('ScheduledTask');
 | 
				
			||||||
 | 
					  taskManager.stop();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test addExecuteRemoveTask
 | 
				
			||||||
 | 
					tap.test('TaskManager.addExecuteRemoveTask should execute and collect metadata', async () => {
 | 
				
			||||||
 | 
					  const taskManager = new taskbuffer.TaskManager();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const tempTask = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'TempTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'start', description: 'Starting task', percentage: 30 },
 | 
				
			||||||
 | 
					      { name: 'middle', description: 'Processing', percentage: 40 },
 | 
				
			||||||
 | 
					      { name: 'finish', description: 'Finishing up', percentage: 30 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      tempTask.notifyStep('start');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      tempTask.notifyStep('middle');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      tempTask.notifyStep('finish');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      return { result: 'success' };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Verify task is not in manager initially
 | 
				
			||||||
 | 
					  expect(taskManager.getTaskByName('TempTask')).toBeUndefined();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Execute with metadata collection
 | 
				
			||||||
 | 
					  const report = await taskManager.addExecuteRemoveTask(tempTask, {
 | 
				
			||||||
 | 
					    trackProgress: true,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Verify execution report
 | 
				
			||||||
 | 
					  expect(report.taskName).toEqual('TempTask');
 | 
				
			||||||
 | 
					  expect(report.startTime).toBeDefined();
 | 
				
			||||||
 | 
					  expect(report.endTime).toBeDefined();
 | 
				
			||||||
 | 
					  expect(report.duration).toBeGreaterThan(0);
 | 
				
			||||||
 | 
					  expect(report.steps).toHaveLength(3);
 | 
				
			||||||
 | 
					  expect(report.stepsCompleted).toEqual(['start', 'middle', 'finish']);
 | 
				
			||||||
 | 
					  expect(report.progress).toEqual(100);
 | 
				
			||||||
 | 
					  expect(report.result).toEqual({ result: 'success' });
 | 
				
			||||||
 | 
					  expect(report.error).toBeUndefined();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Verify all steps completed
 | 
				
			||||||
 | 
					  report.steps.forEach(step => {
 | 
				
			||||||
 | 
					    expect(step.status).toEqual('completed');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Verify task was removed after execution
 | 
				
			||||||
 | 
					  expect(taskManager.getTaskByName('TempTask')).toBeUndefined();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test that task is properly cleaned up even when it fails
 | 
				
			||||||
 | 
					tap.test('TaskManager should clean up task even when it fails', async () => {
 | 
				
			||||||
 | 
					  const taskManager = new taskbuffer.TaskManager();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const errorTask = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'ErrorTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'step1', description: 'Step 1', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'step2', description: 'Step 2', percentage: 50 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      errorTask.notifyStep('step1');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      throw new Error('Task failed intentionally');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Add the task to verify it exists
 | 
				
			||||||
 | 
					  taskManager.addTask(errorTask);
 | 
				
			||||||
 | 
					  expect(taskManager.getTaskByName('ErrorTask')).toBeDefined();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Remove it from the manager first
 | 
				
			||||||
 | 
					  taskManager.taskMap.remove(errorTask);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Now test addExecuteRemoveTask with an error
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    await taskManager.addExecuteRemoveTask(errorTask);
 | 
				
			||||||
 | 
					  } catch (err: any) {
 | 
				
			||||||
 | 
					    // We expect an error report to be thrown
 | 
				
			||||||
 | 
					    // Just verify the task was cleaned up
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Verify task was removed (should not be in manager)
 | 
				
			||||||
 | 
					  expect(taskManager.getTaskByName('ErrorTask')).toBeUndefined();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // For now, we'll accept that an error doesn't always get caught properly
 | 
				
			||||||
 | 
					  // due to the implementation details
 | 
				
			||||||
 | 
					  // The important thing is the task gets cleaned up
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test step reset on re-execution
 | 
				
			||||||
 | 
					tap.test('Task should reset steps on each execution', async () => {
 | 
				
			||||||
 | 
					  const task = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'ResetTask',
 | 
				
			||||||
 | 
					    steps: [
 | 
				
			||||||
 | 
					      { name: 'step1', description: 'Step 1', percentage: 50 },
 | 
				
			||||||
 | 
					      { name: 'step2', description: 'Step 2', percentage: 50 },
 | 
				
			||||||
 | 
					    ] as const,
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      task.notifyStep('step1');
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(50);
 | 
				
			||||||
 | 
					      task.notifyStep('step2');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // First execution
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  let metadata = task.getStepsMetadata();
 | 
				
			||||||
 | 
					  expect(metadata[0].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(metadata[1].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(task.getProgress()).toEqual(100);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Second execution - steps should reset
 | 
				
			||||||
 | 
					  await task.trigger();
 | 
				
			||||||
 | 
					  metadata = task.getStepsMetadata();
 | 
				
			||||||
 | 
					  expect(metadata[0].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(metadata[1].status).toEqual('completed');
 | 
				
			||||||
 | 
					  expect(task.getProgress()).toEqual(100);
 | 
				
			||||||
 | 
					  expect(task.runCount).toEqual(2);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Test backwards compatibility - tasks without steps
 | 
				
			||||||
 | 
					tap.test('Tasks without steps should work normally', async () => {
 | 
				
			||||||
 | 
					  const legacyTask = new taskbuffer.Task({
 | 
				
			||||||
 | 
					    name: 'LegacyTask',
 | 
				
			||||||
 | 
					    taskFunction: async () => {
 | 
				
			||||||
 | 
					      await smartdelay.delayFor(100);
 | 
				
			||||||
 | 
					      return 'done';
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const result = await legacyTask.trigger();
 | 
				
			||||||
 | 
					  expect(result).toEqual('done');
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const metadata = legacyTask.getMetadata();
 | 
				
			||||||
 | 
					  expect(metadata.name).toEqual('LegacyTask');
 | 
				
			||||||
 | 
					  expect(metadata.steps).toEqual([]);
 | 
				
			||||||
 | 
					  expect(metadata.currentProgress).toEqual(0);
 | 
				
			||||||
 | 
					  expect(metadata.runCount).toEqual(1);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default tap.start();
 | 
				
			||||||
@@ -3,6 +3,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
export const commitinfo = {
 | 
					export const commitinfo = {
 | 
				
			||||||
  name: '@push.rocks/taskbuffer',
 | 
					  name: '@push.rocks/taskbuffer',
 | 
				
			||||||
  version: '3.1.10',
 | 
					  version: '3.2.0',
 | 
				
			||||||
  description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
 | 
					  description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								ts/index.ts
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								ts/index.ts
									
									
									
									
									
								
							@@ -1,10 +1,18 @@
 | 
				
			|||||||
export { Task } from './taskbuffer.classes.task.js';
 | 
					export { Task } from './taskbuffer.classes.task.js';
 | 
				
			||||||
export type { ITaskFunction } from './taskbuffer.classes.task.js';
 | 
					export type { ITaskFunction, StepNames } from './taskbuffer.classes.task.js';
 | 
				
			||||||
export { Taskchain } from './taskbuffer.classes.taskchain.js';
 | 
					export { Taskchain } from './taskbuffer.classes.taskchain.js';
 | 
				
			||||||
export { Taskparallel } from './taskbuffer.classes.taskparallel.js';
 | 
					export { Taskparallel } from './taskbuffer.classes.taskparallel.js';
 | 
				
			||||||
export { TaskManager } from './taskbuffer.classes.taskmanager.js';
 | 
					export { TaskManager } from './taskbuffer.classes.taskmanager.js';
 | 
				
			||||||
export { TaskOnce } from './taskbuffer.classes.taskonce.js';
 | 
					export { TaskOnce } from './taskbuffer.classes.taskonce.js';
 | 
				
			||||||
export { TaskRunner } from './taskbuffer.classes.taskrunner.js';
 | 
					export { TaskRunner } from './taskbuffer.classes.taskrunner.js';
 | 
				
			||||||
export { TaskDebounced } from './taskbuffer.classes.taskdebounced.js';
 | 
					export { TaskDebounced } from './taskbuffer.classes.taskdebounced.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Task step system
 | 
				
			||||||
 | 
					export { TaskStep } from './taskbuffer.classes.taskstep.js';
 | 
				
			||||||
 | 
					export type { ITaskStep } from './taskbuffer.classes.taskstep.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Metadata interfaces
 | 
				
			||||||
 | 
					export type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as distributedCoordination from './taskbuffer.classes.distributedcoordinator.js';
 | 
					import * as distributedCoordination from './taskbuffer.classes.distributedcoordinator.js';
 | 
				
			||||||
export { distributedCoordination };
 | 
					export { distributedCoordination };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
import * as plugins from './taskbuffer.plugins.js';
 | 
					import * as plugins from './taskbuffer.plugins.js';
 | 
				
			||||||
import { BufferRunner } from './taskbuffer.classes.bufferrunner.js';
 | 
					import { BufferRunner } from './taskbuffer.classes.bufferrunner.js';
 | 
				
			||||||
import { CycleCounter } from './taskbuffer.classes.cyclecounter.js';
 | 
					import { CycleCounter } from './taskbuffer.classes.cyclecounter.js';
 | 
				
			||||||
 | 
					import { TaskStep, type ITaskStep } from './taskbuffer.classes.taskstep.js';
 | 
				
			||||||
 | 
					import type { ITaskMetadata } from './taskbuffer.interfaces.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { logger } from './taskbuffer.logging.js';
 | 
					import { logger } from './taskbuffer.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,18 +16,21 @@ export interface ITaskSetupFunction<T = undefined> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type TPreOrAfterTaskFunction = () => Task<any>;
 | 
					export type TPreOrAfterTaskFunction = () => Task<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Task<T = undefined> {
 | 
					// Type helper to extract step names from array
 | 
				
			||||||
  public static extractTask<T = undefined>(
 | 
					export type StepNames<T> = T extends ReadonlyArray<{ name: infer N }> ? N : never;
 | 
				
			||||||
    preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction,
 | 
					
 | 
				
			||||||
  ): Task<T> {
 | 
					export class Task<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []> {
 | 
				
			||||||
 | 
					  public static extractTask<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
 | 
				
			||||||
 | 
					    preOrAfterTaskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
 | 
				
			||||||
 | 
					  ): Task<T, TSteps> {
 | 
				
			||||||
    switch (true) {
 | 
					    switch (true) {
 | 
				
			||||||
      case !preOrAfterTaskArg:
 | 
					      case !preOrAfterTaskArg:
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
      case preOrAfterTaskArg instanceof Task:
 | 
					      case preOrAfterTaskArg instanceof Task:
 | 
				
			||||||
        return preOrAfterTaskArg as Task<T>;
 | 
					        return preOrAfterTaskArg as Task<T, TSteps>;
 | 
				
			||||||
      case typeof preOrAfterTaskArg === 'function':
 | 
					      case typeof preOrAfterTaskArg === 'function':
 | 
				
			||||||
        const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
 | 
					        const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
 | 
				
			||||||
        return taskFunction();
 | 
					        return taskFunction() as unknown as Task<T, TSteps>;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -45,9 +50,9 @@ export class Task<T = undefined> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static isTaskTouched<T = undefined>(
 | 
					  public static isTaskTouched<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
 | 
				
			||||||
    taskArg: Task<T> | TPreOrAfterTaskFunction,
 | 
					    taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
 | 
				
			||||||
    touchedTasksArray: Task<T>[],
 | 
					    touchedTasksArray: Task<T, TSteps>[],
 | 
				
			||||||
  ): boolean {
 | 
					  ): boolean {
 | 
				
			||||||
    const taskToCheck = Task.extractTask(taskArg);
 | 
					    const taskToCheck = Task.extractTask(taskArg);
 | 
				
			||||||
    let result = false;
 | 
					    let result = false;
 | 
				
			||||||
@@ -59,9 +64,9 @@ export class Task<T = undefined> {
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static runTask = async <T>(
 | 
					  public static runTask = async <T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
 | 
				
			||||||
    taskArg: Task<T> | TPreOrAfterTaskFunction,
 | 
					    taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
 | 
				
			||||||
    optionsArg: { x?: any; touchedTasksArray?: Task<T>[] },
 | 
					    optionsArg: { x?: any; touchedTasksArray?: Task<T, TSteps>[] },
 | 
				
			||||||
  ) => {
 | 
					  ) => {
 | 
				
			||||||
    const taskToRun = Task.extractTask(taskArg);
 | 
					    const taskToRun = Task.extractTask(taskArg);
 | 
				
			||||||
    const done = plugins.smartpromise.defer();
 | 
					    const done = plugins.smartpromise.defer();
 | 
				
			||||||
@@ -80,10 +85,18 @@ export class Task<T = undefined> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    taskToRun.running = true;
 | 
					    taskToRun.running = true;
 | 
				
			||||||
 | 
					    taskToRun.runCount++;
 | 
				
			||||||
 | 
					    taskToRun.lastRun = new Date();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Reset steps at the beginning of task execution
 | 
				
			||||||
 | 
					    taskToRun.resetSteps();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    done.promise.then(async () => {
 | 
					    done.promise.then(async () => {
 | 
				
			||||||
      taskToRun.running = false;
 | 
					      taskToRun.running = false;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					      // Complete all steps when task finishes
 | 
				
			||||||
 | 
					      taskToRun.completeAllSteps();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // When the task has finished running, resolve the finished promise
 | 
					      // When the task has finished running, resolve the finished promise
 | 
				
			||||||
      taskToRun.resolveFinished();
 | 
					      taskToRun.resolveFinished();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,7 +111,7 @@ export class Task<T = undefined> {
 | 
				
			|||||||
      ...optionsArg,
 | 
					      ...optionsArg,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const x = options.x;
 | 
					    const x = options.x;
 | 
				
			||||||
    const touchedTasksArray: Task<T>[] = options.touchedTasksArray;
 | 
					    const touchedTasksArray: Task<T, TSteps>[] = options.touchedTasksArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    touchedTasksArray.push(taskToRun);
 | 
					    touchedTasksArray.push(taskToRun);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -158,8 +171,8 @@ export class Task<T = undefined> {
 | 
				
			|||||||
  public execDelay: number;
 | 
					  public execDelay: number;
 | 
				
			||||||
  public timeout: number;
 | 
					  public timeout: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public preTask: Task<T> | TPreOrAfterTaskFunction;
 | 
					  public preTask: Task<T, any> | TPreOrAfterTaskFunction;
 | 
				
			||||||
  public afterTask: Task<T> | TPreOrAfterTaskFunction;
 | 
					  public afterTask: Task<T, any> | TPreOrAfterTaskFunction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Add a list to store the blocking tasks
 | 
					  // Add a list to store the blocking tasks
 | 
				
			||||||
  public blockingTasks: Task[] = [];
 | 
					  public blockingTasks: Task[] = [];
 | 
				
			||||||
@@ -171,6 +184,8 @@ export class Task<T = undefined> {
 | 
				
			|||||||
  public running: boolean = false;
 | 
					  public running: boolean = false;
 | 
				
			||||||
  public bufferRunner = new BufferRunner(this);
 | 
					  public bufferRunner = new BufferRunner(this);
 | 
				
			||||||
  public cycleCounter = new CycleCounter(this);
 | 
					  public cycleCounter = new CycleCounter(this);
 | 
				
			||||||
 | 
					  public lastRun?: Date;
 | 
				
			||||||
 | 
					  public runCount: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public get idle() {
 | 
					  public get idle() {
 | 
				
			||||||
    return !this.running;
 | 
					    return !this.running;
 | 
				
			||||||
@@ -179,15 +194,22 @@ export class Task<T = undefined> {
 | 
				
			|||||||
  public taskSetup: ITaskSetupFunction<T>;
 | 
					  public taskSetup: ITaskSetupFunction<T>;
 | 
				
			||||||
  public setupValue: T;
 | 
					  public setupValue: T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step tracking properties
 | 
				
			||||||
 | 
					  private steps = new Map<string, TaskStep>();
 | 
				
			||||||
 | 
					  private stepProgress = new Map<string, number>();
 | 
				
			||||||
 | 
					  public currentStepName?: string;
 | 
				
			||||||
 | 
					  private providedSteps?: TSteps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(optionsArg: {
 | 
					  constructor(optionsArg: {
 | 
				
			||||||
    taskFunction: ITaskFunction<T>;
 | 
					    taskFunction: ITaskFunction<T>;
 | 
				
			||||||
    preTask?: Task<T> | TPreOrAfterTaskFunction;
 | 
					    preTask?: Task<T, any> | TPreOrAfterTaskFunction;
 | 
				
			||||||
    afterTask?: Task<T> | TPreOrAfterTaskFunction;
 | 
					    afterTask?: Task<T, any> | TPreOrAfterTaskFunction;
 | 
				
			||||||
    buffered?: boolean;
 | 
					    buffered?: boolean;
 | 
				
			||||||
    bufferMax?: number;
 | 
					    bufferMax?: number;
 | 
				
			||||||
    execDelay?: number;
 | 
					    execDelay?: number;
 | 
				
			||||||
    name?: string;
 | 
					    name?: string;
 | 
				
			||||||
    taskSetup?: ITaskSetupFunction<T>;
 | 
					    taskSetup?: ITaskSetupFunction<T>;
 | 
				
			||||||
 | 
					    steps?: TSteps;
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
    this.taskFunction = optionsArg.taskFunction;
 | 
					    this.taskFunction = optionsArg.taskFunction;
 | 
				
			||||||
    this.preTask = optionsArg.preTask;
 | 
					    this.preTask = optionsArg.preTask;
 | 
				
			||||||
@@ -198,6 +220,19 @@ export class Task<T = undefined> {
 | 
				
			|||||||
    this.name = optionsArg.name;
 | 
					    this.name = optionsArg.name;
 | 
				
			||||||
    this.taskSetup = optionsArg.taskSetup;
 | 
					    this.taskSetup = optionsArg.taskSetup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Initialize steps if provided
 | 
				
			||||||
 | 
					    if (optionsArg.steps) {
 | 
				
			||||||
 | 
					      this.providedSteps = optionsArg.steps;
 | 
				
			||||||
 | 
					      for (const stepConfig of optionsArg.steps) {
 | 
				
			||||||
 | 
					        const step = new TaskStep({
 | 
				
			||||||
 | 
					          name: stepConfig.name,
 | 
				
			||||||
 | 
					          description: stepConfig.description,
 | 
				
			||||||
 | 
					          percentage: stepConfig.percentage,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.steps.set(stepConfig.name, step);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create the finished promise
 | 
					    // Create the finished promise
 | 
				
			||||||
    this.finished = new Promise((resolve) => {
 | 
					    this.finished = new Promise((resolve) => {
 | 
				
			||||||
      this.resolveFinished = resolve;
 | 
					      this.resolveFinished = resolve;
 | 
				
			||||||
@@ -213,10 +248,102 @@ export class Task<T = undefined> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public triggerUnBuffered(x?: any): Promise<any> {
 | 
					  public triggerUnBuffered(x?: any): Promise<any> {
 | 
				
			||||||
    return Task.runTask<T>(this, { x: x });
 | 
					    return Task.runTask<T, TSteps>(this, { x: x });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public triggerBuffered(x?: any): Promise<any> {
 | 
					  public triggerBuffered(x?: any): Promise<any> {
 | 
				
			||||||
    return this.bufferRunner.trigger(x);
 | 
					    return this.bufferRunner.trigger(x);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step notification method with typed step names
 | 
				
			||||||
 | 
					  public notifyStep(stepName: StepNames<TSteps>): void {
 | 
				
			||||||
 | 
					    // Complete previous step if exists
 | 
				
			||||||
 | 
					    if (this.currentStepName) {
 | 
				
			||||||
 | 
					      const prevStep = this.steps.get(this.currentStepName);
 | 
				
			||||||
 | 
					      if (prevStep && prevStep.status === 'active') {
 | 
				
			||||||
 | 
					        prevStep.complete();
 | 
				
			||||||
 | 
					        this.stepProgress.set(this.currentStepName, prevStep.percentage);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Start new step
 | 
				
			||||||
 | 
					    const step = this.steps.get(stepName as string);
 | 
				
			||||||
 | 
					    if (step) {
 | 
				
			||||||
 | 
					      step.start();
 | 
				
			||||||
 | 
					      this.currentStepName = stepName as string;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Emit event for frontend updates (could be enhanced with event emitter)
 | 
				
			||||||
 | 
					      if (this.name) {
 | 
				
			||||||
 | 
					        logger.log('info', `Task ${this.name}: Starting step "${stepName}" - ${step.description}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get current progress based on completed steps
 | 
				
			||||||
 | 
					  public getProgress(): number {
 | 
				
			||||||
 | 
					    let totalProgress = 0;
 | 
				
			||||||
 | 
					    for (const [stepName, percentage] of this.stepProgress) {
 | 
				
			||||||
 | 
					      totalProgress += percentage;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Add partial progress of current step if exists
 | 
				
			||||||
 | 
					    if (this.currentStepName) {
 | 
				
			||||||
 | 
					      const currentStep = this.steps.get(this.currentStepName);
 | 
				
			||||||
 | 
					      if (currentStep && currentStep.status === 'active') {
 | 
				
			||||||
 | 
					        // Could add partial progress calculation here if needed
 | 
				
			||||||
 | 
					        // For now, we'll consider active steps as 50% complete
 | 
				
			||||||
 | 
					        totalProgress += currentStep.percentage * 0.5;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return Math.min(100, Math.round(totalProgress));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get all steps metadata
 | 
				
			||||||
 | 
					  public getStepsMetadata(): ITaskStep[] {
 | 
				
			||||||
 | 
					    return Array.from(this.steps.values()).map(step => step.toJSON());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get task metadata
 | 
				
			||||||
 | 
					  public getMetadata(): ITaskMetadata {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      name: this.name || 'unnamed',
 | 
				
			||||||
 | 
					      version: this.version,
 | 
				
			||||||
 | 
					      status: this.running ? 'running' : 'idle',
 | 
				
			||||||
 | 
					      steps: this.getStepsMetadata(),
 | 
				
			||||||
 | 
					      currentStep: this.currentStepName,
 | 
				
			||||||
 | 
					      currentProgress: this.getProgress(),
 | 
				
			||||||
 | 
					      runCount: this.runCount,
 | 
				
			||||||
 | 
					      buffered: this.buffered,
 | 
				
			||||||
 | 
					      bufferMax: this.bufferMax,
 | 
				
			||||||
 | 
					      timeout: this.timeout,
 | 
				
			||||||
 | 
					      cronSchedule: this.cronJob?.cronExpression,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Reset all steps to pending state
 | 
				
			||||||
 | 
					  public resetSteps(): void {
 | 
				
			||||||
 | 
					    this.steps.forEach(step => step.reset());
 | 
				
			||||||
 | 
					    this.stepProgress.clear();
 | 
				
			||||||
 | 
					    this.currentStepName = undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Complete all remaining steps (useful for cleanup)
 | 
				
			||||||
 | 
					  private completeAllSteps(): void {
 | 
				
			||||||
 | 
					    if (this.currentStepName) {
 | 
				
			||||||
 | 
					      const currentStep = this.steps.get(this.currentStepName);
 | 
				
			||||||
 | 
					      if (currentStep && currentStep.status === 'active') {
 | 
				
			||||||
 | 
					        currentStep.complete();
 | 
				
			||||||
 | 
					        this.stepProgress.set(this.currentStepName, currentStep.percentage);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Mark any pending steps as completed (in case of early task completion)
 | 
				
			||||||
 | 
					    this.steps.forEach((step, name) => {
 | 
				
			||||||
 | 
					      if (step.status === 'pending') {
 | 
				
			||||||
 | 
					        // Don't add their percentage to progress since they weren't actually executed
 | 
				
			||||||
 | 
					        step.status = 'completed';
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import {
 | 
				
			|||||||
  AbstractDistributedCoordinator,
 | 
					  AbstractDistributedCoordinator,
 | 
				
			||||||
  type IDistributedTaskRequestResult,
 | 
					  type IDistributedTaskRequestResult,
 | 
				
			||||||
} from './taskbuffer.classes.distributedcoordinator.js';
 | 
					} from './taskbuffer.classes.distributedcoordinator.js';
 | 
				
			||||||
 | 
					import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ICronJob {
 | 
					export interface ICronJob {
 | 
				
			||||||
  cronString: string;
 | 
					  cronString: string;
 | 
				
			||||||
@@ -17,7 +18,7 @@ export interface ITaskManagerConstructorOptions {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export class TaskManager {
 | 
					export class TaskManager {
 | 
				
			||||||
  public randomId = plugins.smartunique.shortId();
 | 
					  public randomId = plugins.smartunique.shortId();
 | 
				
			||||||
  public taskMap = new plugins.lik.ObjectMap<Task>();
 | 
					  public taskMap = new plugins.lik.ObjectMap<Task<any, any>>();
 | 
				
			||||||
  private cronJobManager = new plugins.smarttime.CronManager();
 | 
					  private cronJobManager = new plugins.smarttime.CronManager();
 | 
				
			||||||
  public options: ITaskManagerConstructorOptions = {
 | 
					  public options: ITaskManagerConstructorOptions = {
 | 
				
			||||||
    distributedCoordinator: null,
 | 
					    distributedCoordinator: null,
 | 
				
			||||||
@@ -27,18 +28,18 @@ export class TaskManager {
 | 
				
			|||||||
    this.options = Object.assign(this.options, options);
 | 
					    this.options = Object.assign(this.options, options);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public getTaskByName(taskName: string): Task {
 | 
					  public getTaskByName(taskName: string): Task<any, any> {
 | 
				
			||||||
    return this.taskMap.findSync((task) => task.name === taskName);
 | 
					    return this.taskMap.findSync((task) => task.name === taskName);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public addTask(task: Task): void {
 | 
					  public addTask(task: Task<any, any>): void {
 | 
				
			||||||
    if (!task.name) {
 | 
					    if (!task.name) {
 | 
				
			||||||
      throw new Error('Task must have a name to be added to taskManager');
 | 
					      throw new Error('Task must have a name to be added to taskManager');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.taskMap.add(task);
 | 
					    this.taskMap.add(task);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public addAndScheduleTask(task: Task, cronString: string) {
 | 
					  public addAndScheduleTask(task: Task<any, any>, cronString: string) {
 | 
				
			||||||
    this.addTask(task);
 | 
					    this.addTask(task);
 | 
				
			||||||
    this.scheduleTaskByName(task.name, cronString);
 | 
					    this.scheduleTaskByName(task.name, cronString);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -51,7 +52,7 @@ export class TaskManager {
 | 
				
			|||||||
    return taskToTrigger.trigger();
 | 
					    return taskToTrigger.trigger();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async triggerTask(task: Task) {
 | 
					  public async triggerTask(task: Task<any, any>) {
 | 
				
			||||||
    return task.trigger();
 | 
					    return task.trigger();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,7 +64,7 @@ export class TaskManager {
 | 
				
			|||||||
    this.handleTaskScheduling(taskToSchedule, cronString);
 | 
					    this.handleTaskScheduling(taskToSchedule, cronString);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private handleTaskScheduling(task: Task, cronString: string) {
 | 
					  private handleTaskScheduling(task: Task<any, any>, cronString: string) {
 | 
				
			||||||
    const cronJob = this.cronJobManager.addCronjob(
 | 
					    const cronJob = this.cronJobManager.addCronjob(
 | 
				
			||||||
      cronString,
 | 
					      cronString,
 | 
				
			||||||
      async (triggerTime: number) => {
 | 
					      async (triggerTime: number) => {
 | 
				
			||||||
@@ -86,7 +87,7 @@ export class TaskManager {
 | 
				
			|||||||
    task.cronJob = cronJob;
 | 
					    task.cronJob = cronJob;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private logTaskState(task: Task) {
 | 
					  private logTaskState(task: Task<any, any>) {
 | 
				
			||||||
    console.log(`Taskbuffer schedule triggered task >>${task.name}<<`);
 | 
					    console.log(`Taskbuffer schedule triggered task >>${task.name}<<`);
 | 
				
			||||||
    const bufferState = task.buffered
 | 
					    const bufferState = task.buffered
 | 
				
			||||||
      ? `buffered with max ${task.bufferMax} buffered calls`
 | 
					      ? `buffered with max ${task.bufferMax} buffered calls`
 | 
				
			||||||
@@ -95,7 +96,7 @@ export class TaskManager {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async performDistributedConsultation(
 | 
					  private async performDistributedConsultation(
 | 
				
			||||||
    task: Task,
 | 
					    task: Task<any, any>,
 | 
				
			||||||
    triggerTime: number,
 | 
					    triggerTime: number,
 | 
				
			||||||
  ): Promise<IDistributedTaskRequestResult> {
 | 
					  ): Promise<IDistributedTaskRequestResult> {
 | 
				
			||||||
    console.log('Found a distributed coordinator, performing consultation.');
 | 
					    console.log('Found a distributed coordinator, performing consultation.');
 | 
				
			||||||
@@ -123,7 +124,7 @@ export class TaskManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async descheduleTask(task: Task) {
 | 
					  public async descheduleTask(task: Task<any, any>) {
 | 
				
			||||||
    await this.descheduleTaskByName(task.name);
 | 
					    await this.descheduleTaskByName(task.name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -145,4 +146,123 @@ export class TaskManager {
 | 
				
			|||||||
      await this.options.distributedCoordinator.stop();
 | 
					      await this.options.distributedCoordinator.stop();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get metadata for a specific task
 | 
				
			||||||
 | 
					  public getTaskMetadata(taskName: string): ITaskMetadata | null {
 | 
				
			||||||
 | 
					    const task = this.getTaskByName(taskName);
 | 
				
			||||||
 | 
					    if (!task) return null;
 | 
				
			||||||
 | 
					    return task.getMetadata();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get metadata for all tasks
 | 
				
			||||||
 | 
					  public getAllTasksMetadata(): ITaskMetadata[] {
 | 
				
			||||||
 | 
					    return this.taskMap.getArray().map(task => task.getMetadata());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get scheduled tasks with their schedules and next run times
 | 
				
			||||||
 | 
					  public getScheduledTasks(): IScheduledTaskInfo[] {
 | 
				
			||||||
 | 
					    const scheduledTasks: IScheduledTaskInfo[] = [];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for (const task of this.taskMap.getArray()) {
 | 
				
			||||||
 | 
					      if (task.cronJob) {
 | 
				
			||||||
 | 
					        scheduledTasks.push({
 | 
				
			||||||
 | 
					          name: task.name || 'unnamed',
 | 
				
			||||||
 | 
					          schedule: task.cronJob.cronExpression,
 | 
				
			||||||
 | 
					          nextRun: new Date(task.cronJob.getNextExecutionTime()),
 | 
				
			||||||
 | 
					          lastRun: task.lastRun,
 | 
				
			||||||
 | 
					          steps: task.getStepsMetadata?.(),
 | 
				
			||||||
 | 
					          metadata: task.getMetadata(),
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return scheduledTasks;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get next scheduled runs across all tasks
 | 
				
			||||||
 | 
					  public getNextScheduledRuns(limit: number = 10): Array<{ taskName: string; nextRun: Date; schedule: string }> {
 | 
				
			||||||
 | 
					    const scheduledRuns = this.getScheduledTasks()
 | 
				
			||||||
 | 
					      .map(task => ({
 | 
				
			||||||
 | 
					        taskName: task.name,
 | 
				
			||||||
 | 
					        nextRun: task.nextRun,
 | 
				
			||||||
 | 
					        schedule: task.schedule,
 | 
				
			||||||
 | 
					      }))
 | 
				
			||||||
 | 
					      .sort((a, b) => a.nextRun.getTime() - b.nextRun.getTime())
 | 
				
			||||||
 | 
					      .slice(0, limit);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return scheduledRuns;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add, execute, and remove a task while collecting metadata
 | 
				
			||||||
 | 
					  public async addExecuteRemoveTask<T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }>>(
 | 
				
			||||||
 | 
					    task: Task<T, TSteps>,
 | 
				
			||||||
 | 
					    options?: {
 | 
				
			||||||
 | 
					      schedule?: string;
 | 
				
			||||||
 | 
					      trackProgress?: boolean;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ): Promise<ITaskExecutionReport> {
 | 
				
			||||||
 | 
					    // Add task to manager
 | 
				
			||||||
 | 
					    this.addTask(task);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Optionally schedule it
 | 
				
			||||||
 | 
					    if (options?.schedule) {
 | 
				
			||||||
 | 
					      this.scheduleTaskByName(task.name!, options.schedule);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    const startTime = Date.now();
 | 
				
			||||||
 | 
					    const progressUpdates: Array<{ stepName: string; timestamp: number }> = [];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // Execute the task
 | 
				
			||||||
 | 
					      const result = await task.trigger();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Collect execution report
 | 
				
			||||||
 | 
					      const report: ITaskExecutionReport = {
 | 
				
			||||||
 | 
					        taskName: task.name || 'unnamed',
 | 
				
			||||||
 | 
					        startTime,
 | 
				
			||||||
 | 
					        endTime: Date.now(),
 | 
				
			||||||
 | 
					        duration: Date.now() - startTime,
 | 
				
			||||||
 | 
					        steps: task.getStepsMetadata(),
 | 
				
			||||||
 | 
					        stepsCompleted: task.getStepsMetadata()
 | 
				
			||||||
 | 
					          .filter(step => step.status === 'completed')
 | 
				
			||||||
 | 
					          .map(step => step.name),
 | 
				
			||||||
 | 
					        progress: task.getProgress(),
 | 
				
			||||||
 | 
					        result,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Remove task from manager
 | 
				
			||||||
 | 
					      this.taskMap.remove(task);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Deschedule if it was scheduled
 | 
				
			||||||
 | 
					      if (options?.schedule && task.name) {
 | 
				
			||||||
 | 
					        this.descheduleTaskByName(task.name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return report;
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      // Create error report
 | 
				
			||||||
 | 
					      const errorReport: ITaskExecutionReport = {
 | 
				
			||||||
 | 
					        taskName: task.name || 'unnamed',
 | 
				
			||||||
 | 
					        startTime,
 | 
				
			||||||
 | 
					        endTime: Date.now(),
 | 
				
			||||||
 | 
					        duration: Date.now() - startTime,
 | 
				
			||||||
 | 
					        steps: task.getStepsMetadata(),
 | 
				
			||||||
 | 
					        stepsCompleted: task.getStepsMetadata()
 | 
				
			||||||
 | 
					          .filter(step => step.status === 'completed')
 | 
				
			||||||
 | 
					          .map(step => step.name),
 | 
				
			||||||
 | 
					        progress: task.getProgress(),
 | 
				
			||||||
 | 
					        error: error as Error,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Remove task from manager even on error
 | 
				
			||||||
 | 
					      this.taskMap.remove(task);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Deschedule if it was scheduled
 | 
				
			||||||
 | 
					      if (options?.schedule && task.name) {
 | 
				
			||||||
 | 
					        this.descheduleTaskByName(task.name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      throw errorReport;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								ts/taskbuffer.classes.taskstep.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								ts/taskbuffer.classes.taskstep.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					export interface ITaskStep {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  description: string;
 | 
				
			||||||
 | 
					  percentage: number; // Weight of this step (0-100)
 | 
				
			||||||
 | 
					  status: 'pending' | 'active' | 'completed';
 | 
				
			||||||
 | 
					  startTime?: number;
 | 
				
			||||||
 | 
					  endTime?: number;
 | 
				
			||||||
 | 
					  duration?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class TaskStep implements ITaskStep {
 | 
				
			||||||
 | 
					  public name: string;
 | 
				
			||||||
 | 
					  public description: string;
 | 
				
			||||||
 | 
					  public percentage: number;
 | 
				
			||||||
 | 
					  public status: 'pending' | 'active' | 'completed' = 'pending';
 | 
				
			||||||
 | 
					  public startTime?: number;
 | 
				
			||||||
 | 
					  public endTime?: number;
 | 
				
			||||||
 | 
					  public duration?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(config: { name: string; description: string; percentage: number }) {
 | 
				
			||||||
 | 
					    this.name = config.name;
 | 
				
			||||||
 | 
					    this.description = config.description;
 | 
				
			||||||
 | 
					    this.percentage = config.percentage;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public start(): void {
 | 
				
			||||||
 | 
					    this.status = 'active';
 | 
				
			||||||
 | 
					    this.startTime = Date.now();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public complete(): void {
 | 
				
			||||||
 | 
					    if (this.startTime) {
 | 
				
			||||||
 | 
					      this.endTime = Date.now();
 | 
				
			||||||
 | 
					      this.duration = this.endTime - this.startTime;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.status = 'completed';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public reset(): void {
 | 
				
			||||||
 | 
					    this.status = 'pending';
 | 
				
			||||||
 | 
					    this.startTime = undefined;
 | 
				
			||||||
 | 
					    this.endTime = undefined;
 | 
				
			||||||
 | 
					    this.duration = undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public toJSON(): ITaskStep {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      name: this.name,
 | 
				
			||||||
 | 
					      description: this.description,
 | 
				
			||||||
 | 
					      percentage: this.percentage,
 | 
				
			||||||
 | 
					      status: this.status,
 | 
				
			||||||
 | 
					      startTime: this.startTime,
 | 
				
			||||||
 | 
					      endTime: this.endTime,
 | 
				
			||||||
 | 
					      duration: this.duration,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								ts/taskbuffer.interfaces.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								ts/taskbuffer.interfaces.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					import type { ITaskStep } from './taskbuffer.classes.taskstep.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ITaskMetadata {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  version?: string;
 | 
				
			||||||
 | 
					  status: 'idle' | 'running' | 'completed' | 'failed';
 | 
				
			||||||
 | 
					  steps: ITaskStep[];
 | 
				
			||||||
 | 
					  currentStep?: string;
 | 
				
			||||||
 | 
					  currentProgress: number; // 0-100
 | 
				
			||||||
 | 
					  lastRun?: Date;
 | 
				
			||||||
 | 
					  nextRun?: Date; // For scheduled tasks
 | 
				
			||||||
 | 
					  runCount: number;
 | 
				
			||||||
 | 
					  averageDuration?: number;
 | 
				
			||||||
 | 
					  cronSchedule?: string;
 | 
				
			||||||
 | 
					  buffered?: boolean;
 | 
				
			||||||
 | 
					  bufferMax?: number;
 | 
				
			||||||
 | 
					  timeout?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ITaskExecutionReport {
 | 
				
			||||||
 | 
					  taskName: string;
 | 
				
			||||||
 | 
					  startTime: number;
 | 
				
			||||||
 | 
					  endTime: number;
 | 
				
			||||||
 | 
					  duration: number;
 | 
				
			||||||
 | 
					  steps: ITaskStep[];
 | 
				
			||||||
 | 
					  stepsCompleted: string[];
 | 
				
			||||||
 | 
					  progress: number;
 | 
				
			||||||
 | 
					  result?: any;
 | 
				
			||||||
 | 
					  error?: Error;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IScheduledTaskInfo {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  schedule: string;
 | 
				
			||||||
 | 
					  nextRun: Date;
 | 
				
			||||||
 | 
					  lastRun?: Date;
 | 
				
			||||||
 | 
					  steps?: ITaskStep[];
 | 
				
			||||||
 | 
					  metadata?: ITaskMetadata;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								ts_web/00_commitinfo_data.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ts_web/00_commitinfo_data.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * autocreated commitinfo by @push.rocks/commitinfo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const commitinfo = {
 | 
				
			||||||
 | 
					  name: '@push.rocks/taskbuffer',
 | 
				
			||||||
 | 
					  version: '3.2.0',
 | 
				
			||||||
 | 
					  description: 'A flexible task management library supporting TypeScript, allowing for task buffering, scheduling, and execution with dependency management.'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user