kist
Version:
Lightweight Package Pipeline Processor with Plugin Architecture
122 lines • 4.56 kB
JavaScript
import { AbstractProcess } from "../abstract/AbstractProcess.js";
import { FileCache } from "../cache/FileCache.js";
import { BuildCache } from "../cache/BuildCache.js";
import { ProgressReporter } from "../progress/ProgressReporter.js";
import { Stage } from "./Stage.js";
export class Pipeline extends AbstractProcess {
config;
stages;
options;
fileCache;
buildCache;
progress;
constructor(config) {
super();
this.config = config;
this.stages = config.stages.map((stage) => new Stage(stage));
this.options = config.options;
this.logInfo("Pipeline instance created.");
}
async run() {
const startTime = performance.now();
this.logInfo("Starting pipeline execution...");
await this.initializeCaching();
this.initializeProgress();
const completedStages = new Set();
try {
this.logDebug("Pipeline execution started with debug logging.");
this.progress?.start();
const stagePromises = this.stages.map((stage, index) => stage.execute(completedStages).then(() => {
this.progress?.increment();
}));
await this.runWithConcurrencyControl(stagePromises);
this.progress?.finish();
await this.saveCaches();
const duration = performance.now() - startTime;
this.logInfo(`Pipeline execution completed successfully in ${this.formatDuration(duration)}.`);
this.reportCacheStats();
}
catch (error) {
this.progress?.cancel();
this.logError("Pipeline execution failed:", error);
await this.saveCaches();
if (this.options?.haltOnFailure !== false) {
this.logError("Halting pipeline due to failure.");
process.exit(1);
}
else {
this.logWarn("Continuing pipeline execution despite errors.");
}
}
}
async initializeCaching() {
const cacheOptions = this.options?.cache;
if (!cacheOptions?.enabled) {
return;
}
this.logInfo("Initializing build cache...");
this.fileCache = FileCache.getInstance({
cacheDir: cacheOptions.cacheDir,
ttl: cacheOptions.ttl,
});
await this.fileCache.initialize();
this.buildCache = BuildCache.getInstance({
cacheDir: cacheOptions.cacheDir,
maxCacheSize: cacheOptions.maxCacheSize,
ttl: cacheOptions.ttl,
});
await this.buildCache.initialize();
this.logDebug("Build cache initialized.");
}
initializeProgress() {
const perfOptions = this.options?.performance;
if (perfOptions?.showProgress === false) {
return;
}
this.progress = new ProgressReporter({
total: this.stages.length,
label: "Pipeline",
showPercentage: true,
showEta: true,
});
}
async saveCaches() {
await Promise.all([this.fileCache?.save(), this.buildCache?.save()]);
}
reportCacheStats() {
if (this.fileCache) {
const stats = this.fileCache.getStats();
this.logDebug(`File cache: ${stats.size} entries, ${stats.hitRate} hit rate`);
}
if (this.buildCache) {
const stats = this.buildCache.getStats();
this.logDebug(`Build cache: ${stats.size} entries, ${stats.hitRate} hit rate`);
}
}
formatDuration(ms) {
if (ms < 1000) {
return `${Math.round(ms)}ms`;
}
if (ms < 60000) {
return `${(ms / 1000).toFixed(2)}s`;
}
const minutes = Math.floor(ms / 60000);
const seconds = ((ms % 60000) / 1000).toFixed(1);
return `${minutes}m ${seconds}s`;
}
async runWithConcurrencyControl(stagePromises) {
const maxConcurrentStages = this.options?.performance?.maxConcurrentStages ||
this.options?.maxConcurrentStages ||
stagePromises.length;
const executingStages = new Set();
for (const stagePromise of stagePromises) {
executingStages.add(stagePromise);
stagePromise.finally(() => executingStages.delete(stagePromise));
if (executingStages.size >= maxConcurrentStages) {
await Promise.race(executingStages);
}
}
await Promise.all(executingStages);
}
}
//# sourceMappingURL=Pipeline.js.map