feat(core): Add step-based progress tracking, task metadata and enhanced TaskManager scheduling/metadata APIs
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
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.'
|
||||
}
|
||||
|
10
ts/index.ts
10
ts/index.ts
@@ -1,10 +1,18 @@
|
||||
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 { Taskparallel } from './taskbuffer.classes.taskparallel.js';
|
||||
export { TaskManager } from './taskbuffer.classes.taskmanager.js';
|
||||
export { TaskOnce } from './taskbuffer.classes.taskonce.js';
|
||||
export { TaskRunner } from './taskbuffer.classes.taskrunner.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';
|
||||
export { distributedCoordination };
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import * as plugins from './taskbuffer.plugins.js';
|
||||
import { BufferRunner } from './taskbuffer.classes.bufferrunner.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';
|
||||
|
||||
@@ -14,18 +16,21 @@ export interface ITaskSetupFunction<T = undefined> {
|
||||
|
||||
export type TPreOrAfterTaskFunction = () => Task<any>;
|
||||
|
||||
export class Task<T = undefined> {
|
||||
public static extractTask<T = undefined>(
|
||||
preOrAfterTaskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||
): Task<T> {
|
||||
// Type helper to extract step names from array
|
||||
export type StepNames<T> = T extends ReadonlyArray<{ name: infer N }> ? N : never;
|
||||
|
||||
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) {
|
||||
case !preOrAfterTaskArg:
|
||||
return null;
|
||||
case preOrAfterTaskArg instanceof Task:
|
||||
return preOrAfterTaskArg as Task<T>;
|
||||
return preOrAfterTaskArg as Task<T, TSteps>;
|
||||
case typeof preOrAfterTaskArg === 'function':
|
||||
const taskFunction = preOrAfterTaskArg as TPreOrAfterTaskFunction;
|
||||
return taskFunction();
|
||||
return taskFunction() as unknown as Task<T, TSteps>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -45,9 +50,9 @@ export class Task<T = undefined> {
|
||||
}
|
||||
};
|
||||
|
||||
public static isTaskTouched<T = undefined>(
|
||||
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||
touchedTasksArray: Task<T>[],
|
||||
public static isTaskTouched<T = undefined, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
||||
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
||||
touchedTasksArray: Task<T, TSteps>[],
|
||||
): boolean {
|
||||
const taskToCheck = Task.extractTask(taskArg);
|
||||
let result = false;
|
||||
@@ -59,9 +64,9 @@ export class Task<T = undefined> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static runTask = async <T>(
|
||||
taskArg: Task<T> | TPreOrAfterTaskFunction,
|
||||
optionsArg: { x?: any; touchedTasksArray?: Task<T>[] },
|
||||
public static runTask = async <T, TSteps extends ReadonlyArray<{ name: string; description: string; percentage: number }> = []>(
|
||||
taskArg: Task<T, TSteps> | TPreOrAfterTaskFunction,
|
||||
optionsArg: { x?: any; touchedTasksArray?: Task<T, TSteps>[] },
|
||||
) => {
|
||||
const taskToRun = Task.extractTask(taskArg);
|
||||
const done = plugins.smartpromise.defer();
|
||||
@@ -80,9 +85,17 @@ export class Task<T = undefined> {
|
||||
}
|
||||
|
||||
taskToRun.running = true;
|
||||
taskToRun.runCount++;
|
||||
taskToRun.lastRun = new Date();
|
||||
|
||||
// Reset steps at the beginning of task execution
|
||||
taskToRun.resetSteps();
|
||||
|
||||
done.promise.then(async () => {
|
||||
taskToRun.running = false;
|
||||
|
||||
// Complete all steps when task finishes
|
||||
taskToRun.completeAllSteps();
|
||||
|
||||
// When the task has finished running, resolve the finished promise
|
||||
taskToRun.resolveFinished();
|
||||
@@ -98,7 +111,7 @@ export class Task<T = undefined> {
|
||||
...optionsArg,
|
||||
};
|
||||
const x = options.x;
|
||||
const touchedTasksArray: Task<T>[] = options.touchedTasksArray;
|
||||
const touchedTasksArray: Task<T, TSteps>[] = options.touchedTasksArray;
|
||||
|
||||
touchedTasksArray.push(taskToRun);
|
||||
|
||||
@@ -158,8 +171,8 @@ export class Task<T = undefined> {
|
||||
public execDelay: number;
|
||||
public timeout: number;
|
||||
|
||||
public preTask: Task<T> | TPreOrAfterTaskFunction;
|
||||
public afterTask: Task<T> | TPreOrAfterTaskFunction;
|
||||
public preTask: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
public afterTask: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
|
||||
// Add a list to store the blocking tasks
|
||||
public blockingTasks: Task[] = [];
|
||||
@@ -171,6 +184,8 @@ export class Task<T = undefined> {
|
||||
public running: boolean = false;
|
||||
public bufferRunner = new BufferRunner(this);
|
||||
public cycleCounter = new CycleCounter(this);
|
||||
public lastRun?: Date;
|
||||
public runCount: number = 0;
|
||||
|
||||
public get idle() {
|
||||
return !this.running;
|
||||
@@ -179,15 +194,22 @@ export class Task<T = undefined> {
|
||||
public taskSetup: ITaskSetupFunction<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: {
|
||||
taskFunction: ITaskFunction<T>;
|
||||
preTask?: Task<T> | TPreOrAfterTaskFunction;
|
||||
afterTask?: Task<T> | TPreOrAfterTaskFunction;
|
||||
preTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
afterTask?: Task<T, any> | TPreOrAfterTaskFunction;
|
||||
buffered?: boolean;
|
||||
bufferMax?: number;
|
||||
execDelay?: number;
|
||||
name?: string;
|
||||
taskSetup?: ITaskSetupFunction<T>;
|
||||
steps?: TSteps;
|
||||
}) {
|
||||
this.taskFunction = optionsArg.taskFunction;
|
||||
this.preTask = optionsArg.preTask;
|
||||
@@ -198,6 +220,19 @@ export class Task<T = undefined> {
|
||||
this.name = optionsArg.name;
|
||||
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
|
||||
this.finished = new Promise((resolve) => {
|
||||
this.resolveFinished = resolve;
|
||||
@@ -213,10 +248,102 @@ export class Task<T = undefined> {
|
||||
}
|
||||
|
||||
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> {
|
||||
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,
|
||||
type IDistributedTaskRequestResult,
|
||||
} from './taskbuffer.classes.distributedcoordinator.js';
|
||||
import type { ITaskMetadata, ITaskExecutionReport, IScheduledTaskInfo } from './taskbuffer.interfaces.js';
|
||||
|
||||
export interface ICronJob {
|
||||
cronString: string;
|
||||
@@ -17,7 +18,7 @@ export interface ITaskManagerConstructorOptions {
|
||||
|
||||
export class TaskManager {
|
||||
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();
|
||||
public options: ITaskManagerConstructorOptions = {
|
||||
distributedCoordinator: null,
|
||||
@@ -27,18 +28,18 @@ export class TaskManager {
|
||||
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);
|
||||
}
|
||||
|
||||
public addTask(task: Task): void {
|
||||
public addTask(task: Task<any, any>): void {
|
||||
if (!task.name) {
|
||||
throw new Error('Task must have a name to be added to taskManager');
|
||||
}
|
||||
this.taskMap.add(task);
|
||||
}
|
||||
|
||||
public addAndScheduleTask(task: Task, cronString: string) {
|
||||
public addAndScheduleTask(task: Task<any, any>, cronString: string) {
|
||||
this.addTask(task);
|
||||
this.scheduleTaskByName(task.name, cronString);
|
||||
}
|
||||
@@ -51,7 +52,7 @@ export class TaskManager {
|
||||
return taskToTrigger.trigger();
|
||||
}
|
||||
|
||||
public async triggerTask(task: Task) {
|
||||
public async triggerTask(task: Task<any, any>) {
|
||||
return task.trigger();
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ export class TaskManager {
|
||||
this.handleTaskScheduling(taskToSchedule, cronString);
|
||||
}
|
||||
|
||||
private handleTaskScheduling(task: Task, cronString: string) {
|
||||
private handleTaskScheduling(task: Task<any, any>, cronString: string) {
|
||||
const cronJob = this.cronJobManager.addCronjob(
|
||||
cronString,
|
||||
async (triggerTime: number) => {
|
||||
@@ -86,7 +87,7 @@ export class TaskManager {
|
||||
task.cronJob = cronJob;
|
||||
}
|
||||
|
||||
private logTaskState(task: Task) {
|
||||
private logTaskState(task: Task<any, any>) {
|
||||
console.log(`Taskbuffer schedule triggered task >>${task.name}<<`);
|
||||
const bufferState = task.buffered
|
||||
? `buffered with max ${task.bufferMax} buffered calls`
|
||||
@@ -95,7 +96,7 @@ export class TaskManager {
|
||||
}
|
||||
|
||||
private async performDistributedConsultation(
|
||||
task: Task,
|
||||
task: Task<any, any>,
|
||||
triggerTime: number,
|
||||
): Promise<IDistributedTaskRequestResult> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -145,4 +146,123 @@ export class TaskManager {
|
||||
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;
|
||||
}
|
Reference in New Issue
Block a user