src / progressIndicator.ts
import { logger } from "./logger";
export interface ProgressOptions {
label?: string;
totalSteps?: number;
intervalMs?: number;
}
/**
* Lightweight progress tracking for long-running operations.
*/
export class ProgressIndicator {
private label: string;
private totalSteps: number;
private currentStep: number;
private startTime: number;
private intervalMs: number;
private intervalId?: ReturnType<typeof setInterval>;
constructor(options: ProgressOptions = {}) {
this.label = options.label ?? "Progress";
this.totalSteps = options.totalSteps ?? 100;
this.currentStep = 0;
this.startTime = Date.now();
this.intervalMs = options.intervalMs ?? 1000;
}
start(): void {
this.startTime = Date.now();
this.currentStep = 0;
logger.info(`${this.label}: started`);
}
step(increment: number = 1): number {
this.currentStep = Math.min(this.currentStep + increment, this.totalSteps);
const pct = ((this.currentStep / this.totalSteps) * 100).toFixed(1);
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
logger.info(`${this.label}: ${pct}% (${this.currentStep}/${this.totalSteps}) — ${elapsed}s`);
return this.currentStep;
}
finish(): void {
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
logger.info(`${this.label}: completed in ${elapsed}s`);
if (this.intervalId) clearInterval(this.intervalId);
}
/**
* Run a callback with progress steps automatically tracked.
*/
static async track<T>(
options: ProgressOptions,
fn: (progress: ProgressIndicator) => Promise<T>
): Promise<T> {
const progress = new ProgressIndicator(options);
progress.start();
try {
const result = await fn(progress);
progress.finish();
return result;
} catch (err) {
progress.finish();
throw err;
}
}
}
src / progressIndicator.ts
import { logger } from "./logger";
export interface ProgressOptions {
label?: string;
totalSteps?: number;
intervalMs?: number;
}
/**
* Lightweight progress tracking for long-running operations.
*/
export class ProgressIndicator {
private label: string;
private totalSteps: number;
private currentStep: number;
private startTime: number;
private intervalMs: number;
private intervalId?: ReturnType<typeof setInterval>;
constructor(options: ProgressOptions = {}) {
this.label = options.label ?? "Progress";
this.totalSteps = options.totalSteps ?? 100;
this.currentStep = 0;
this.startTime = Date.now();
this.intervalMs = options.intervalMs ?? 1000;
}
start(): void {
this.startTime = Date.now();
this.currentStep = 0;
logger.info(`${this.label}: started`);
}
step(increment: number = 1): number {
this.currentStep = Math.min(this.currentStep + increment, this.totalSteps);
const pct = ((this.currentStep / this.totalSteps) * 100).toFixed(1);
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
logger.info(`${this.label}: ${pct}% (${this.currentStep}/${this.totalSteps}) — ${elapsed}s`);
return this.currentStep;
}
finish(): void {
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
logger.info(`${this.label}: completed in ${elapsed}s`);
if (this.intervalId) clearInterval(this.intervalId);
}
/**
* Run a callback with progress steps automatically tracked.
*/
static async track<T>(
options: ProgressOptions,
fn: (progress: ProgressIndicator) => Promise<T>
): Promise<T> {
const progress = new ProgressIndicator(options);
progress.start();
try {
const result = await fn(progress);
progress.finish();
return result;
} catch (err) {
progress.finish();
throw err;
}
}
}