or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

architect.mdbuild-context.mdbuilder-creation.mdindex.mdjob-system.mdnode-integration.mdprogress-reporting.mdtesting.md
tile.json

progress-reporting.mddocs/

Progress Reporting

Real-time progress reporting system with state management for build operations. The progress reporting system enables builders to communicate their current status, completion progress, and operational state to users and tooling interfaces.

Capabilities

Builder Progress States

Enumeration of possible progress states during builder execution.

/**
 * Possible states for builder progress reporting
 */
enum BuilderProgressState {
  /**
   * Builder has stopped execution
   */
  Stopped = "stopped",
  
  /**
   * Builder encountered an error
   */
  Error = "error",
  
  /**
   * Builder is waiting to start or for dependencies
   */
  Waiting = "waiting",
  
  /**
   * Builder is actively running
   */
  Running = "running"
}

Typed Progress Interface

Type-safe progress reporting with state-specific properties.

/**
 * Type-safe progress reporting interface
 */
type TypedBuilderProgress =
  | { 
      state: BuilderProgressState.Stopped 
    }
  | { 
      state: BuilderProgressState.Error; 
      error: json.JsonValue 
    }
  | { 
      state: BuilderProgressState.Waiting; 
      status?: string 
    }
  | { 
      state: BuilderProgressState.Running; 
      status?: string; 
      current: number; 
      total?: number 
    };

/**
 * Complete progress interface combining typed and raw progress
 */
type BuilderProgress = json.JsonObject & RealBuilderProgress & TypedBuilderProgress;

Usage Examples:

import { BuilderProgressState } from "@angular-devkit/architect";

// Progress state examples
const stoppedProgress: TypedBuilderProgress = {
  state: BuilderProgressState.Stopped
};

const errorProgress: TypedBuilderProgress = {
  state: BuilderProgressState.Error,
  error: "Compilation failed"
};

const waitingProgress: TypedBuilderProgress = {
  state: BuilderProgressState.Waiting,
  status: "Waiting for dependencies"
};

const runningProgress: TypedBuilderProgress = {
  state: BuilderProgressState.Running,
  status: "Compiling TypeScript",
  current: 75,
  total: 100
};

Progress Report Interface

Complete progress report including target and builder information.

/**
 * Complete progress report with metadata
 */
interface BuilderProgressReport extends BuilderProgress {
  /**
   * Target information if available
   */
  target?: Target;
  
  /**
   * Builder information and metadata
   */
  builder: BuilderInfo;
  
  /**
   * Unique identifier for the build context
   */
  id: number;
}

Usage Examples:

// Monitor progress reports
const run = await architect.scheduleTarget({ project: "app", target: "build" });

run.progress.subscribe((progress: BuilderProgressReport) => {
  console.log(`Builder: ${progress.builder.builderName}`);
  console.log(`State: ${progress.state}`);
  
  if (progress.target) {
    console.log(`Target: ${progress.target.project}:${progress.target.target}`);
  }
  
  if (progress.state === BuilderProgressState.Running) {
    console.log(`Progress: ${progress.current}/${progress.total || '?'}`);
    if (progress.status) {
      console.log(`Status: ${progress.status}`);
    }
  }
  
  if (progress.state === BuilderProgressState.Error) {
    console.error(`Error: ${progress.error}`);
  }
});

Context Progress Methods

Methods available on BuilderContext for reporting progress.

interface BuilderContext {
  /**
   * Report that the builder is now running
   */
  reportRunning(): void;
  
  /**
   * Update the status message displayed to users
   * @param status - Status message to display
   */
  reportStatus(status: string): void;
  
  /**
   * Report progress with current and total values
   * @param current - Current progress value
   * @param total - Total progress value (optional, uses previous if omitted)
   * @param status - Status message (optional)
   */
  reportProgress(current: number, total?: number, status?: string): void;
}

Usage Examples:

import { createBuilder } from "@angular-devkit/architect";

export default createBuilder((options, context) => {
  // Report initial state
  context.reportRunning();
  context.reportStatus("Initializing build process");
  
  return new Promise((resolve) => {
    const tasks = [
      "Reading configuration",
      "Analyzing dependencies", 
      "Compiling TypeScript",
      "Bundling modules",
      "Optimizing output",
      "Writing files"
    ];
    
    let completed = 0;
    
    const processTask = (taskIndex: number) => {
      const task = tasks[taskIndex];
      
      // Update status and progress
      context.reportStatus(task);
      context.reportProgress(taskIndex, tasks.length, task);
      
      // Simulate work
      setTimeout(() => {
        completed++;
        context.logger.info(`Completed: ${task}`);
        
        if (completed < tasks.length) {
          processTask(completed);
        } else {
          // Final progress update
          context.reportProgress(tasks.length, tasks.length, "Build complete");
          resolve({ success: true });
        }
      }, 1000);
    };
    
    processTask(0);
  });
});

Advanced Progress Patterns

Complex progress reporting patterns for sophisticated builders.

// Multi-phase builder with progress tracking
export const multiPhaseBuilder = createBuilder(async (options, context) => {
  const phases = [
    { name: "Analysis", weight: 10 },
    { name: "Compilation", weight: 60 },
    { name: "Optimization", weight: 20 },
    { name: "Output", weight: 10 }
  ];
  
  const totalWeight = phases.reduce((sum, phase) => sum + phase.weight, 0);
  let completedWeight = 0;
  
  for (const phase of phases) {
    context.reportStatus(`Starting ${phase.name}`);
    
    // Simulate phase work with sub-progress
    for (let step = 0; step <= 10; step++) {
      const phaseProgress = step / 10;
      const overallProgress = completedWeight + (phase.weight * phaseProgress);
      const percentage = Math.round((overallProgress / totalWeight) * 100);
      
      context.reportProgress(
        percentage,
        100,
        `${phase.name}: ${step}/10 steps`
      );
      
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    
    completedWeight += phase.weight;
    context.logger.info(`Completed ${phase.name}`);
  }
  
  return { success: true };
});

// Builder with error handling and progress
export const robustProgressBuilder = createBuilder(async (options, context) => {
  try {
    context.reportRunning();
    context.reportProgress(0, 100, "Starting");
    
    // Risky operation
    await performRiskyOperation();
    context.reportProgress(50, 100, "Halfway complete");
    
    // Another risky operation
    await performAnotherRiskyOperation();
    context.reportProgress(100, 100, "Complete");
    
    return { success: true };
    
  } catch (error) {
    // Report error state through progress
    context.reportStatus(`Error: ${error.message}`);
    
    // Note: Error state is typically handled by the framework
    // but you can provide additional context through status
    
    return { success: false, error: error.message };
  }
});

Progress Monitoring

Examples of monitoring and reacting to progress updates.

// Progress monitoring with detailed logging
async function monitorBuildProgress(architect: Architect, target: Target) {
  const run = await architect.scheduleTarget(target);
  
  // Track progress with timestamps
  const startTime = Date.now();
  let lastProgress = 0;
  
  run.progress.subscribe(progress => {
    const elapsed = Date.now() - startTime;
    
    switch (progress.state) {
      case BuilderProgressState.Waiting:
        console.log(`[${elapsed}ms] Waiting: ${progress.status || 'Ready to start'}`);
        break;
        
      case BuilderProgressState.Running:
        const current = progress.current || 0;
        const total = progress.total || 100;
        const percent = Math.round((current / total) * 100);
        
        if (percent > lastProgress) {
          console.log(`[${elapsed}ms] Progress: ${percent}% - ${progress.status || 'Running'}`);
          lastProgress = percent;
        }
        break;
        
      case BuilderProgressState.Error:
        console.error(`[${elapsed}ms] Error: ${progress.error}`);
        break;
        
      case BuilderProgressState.Stopped:
        console.log(`[${elapsed}ms] Stopped`);
        break;
    }
  });
  
  const result = await run.result;
  const totalTime = Date.now() - startTime;
  
  console.log(`Build completed in ${totalTime}ms: ${result.success ? 'SUCCESS' : 'FAILED'}`);
  
  return result;
}

// Progress aggregation across multiple builders
async function runParallelBuilds(architect: Architect, targets: Target[]) {
  const runs = await Promise.all(
    targets.map(target => architect.scheduleTarget(target))
  );
  
  // Aggregate progress from all runs
  const progressStreams = runs.map((run, index) => 
    run.progress.pipe(
      map(progress => ({ ...progress, runIndex: index }))
    )
  );
  
  merge(...progressStreams).subscribe(progress => {
    console.log(`Run ${progress.runIndex}: ${progress.state} - ${progress.status}`);
  });
  
  // Wait for all to complete
  const results = await Promise.all(runs.map(run => run.result));
  
  return {
    success: results.every(r => r.success),
    results
  };
}

Progress Schema

JSON schema definitions for progress validation.

// Progress schema is defined in progress-schema.json
// Key properties:
// - state: enum ["stopped", "error", "waiting", "running"]  
// - current: number (when state is "running")
// - total: number (optional, when state is "running")
// - status: string (optional status message)
// - error: any (when state is "error")