> ## Documentation Index
> Fetch the complete documentation index at: https://crystalflow.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Execution Engine

> Understanding how CrystalFlow executes workflows

The **Execution Engine** is responsible for running workflows, managing node execution, propagating data, and handling errors.

## Overview

CrystalFlow's execution engine provides:

<CardGroup cols={2}>
  <Card title="Plan-Based Execution" icon="diagram-project">
    Build execution plan, then execute with full control
  </Card>

  <Card title="Conditional Flow" icon="code-branch">
    Support for if/else and switch/case branching
  </Card>

  <Card title="Event System" icon="bolt">
    Comprehensive events for monitoring execution
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation">
    Graceful error handling with detailed context
  </Card>

  <Card title="Cancellation" icon="stop">
    Support for cancelling long-running executions
  </Card>
</CardGroup>

## The Executor

The `Executor` class orchestrates workflow execution:

```typescript theme={null}
import { Executor } from '@crystalflow/core';

const executor = new Executor();
const result = await executor.execute(workflow);
```

### Execution Options

```typescript theme={null}
const result = await executor.execute(workflow, {
  timeout: 30000,                    // Timeout in milliseconds
  variables: { apiKey: 'xxx' },      // Global variables
  abortSignal: controller.signal     // Cancellation signal
});
```

<ParamField path="timeout" type="number">
  Maximum execution time in milliseconds (default: no timeout)
</ParamField>

<ParamField path="variables" type="Record<string, any>">
  Global variables accessible to all nodes during execution
</ParamField>

<ParamField path="abortSignal" type="AbortSignal">
  Optional AbortSignal for cancelling execution
</ParamField>

## Execution Plan Architecture

CrystalFlow uses a **plan-based execution architecture** that enables advanced control flow patterns:

### How It Works

1. **Build Phase**: The workflow graph is analyzed and converted into an execution plan
2. **Execution Phase**: The plan is executed step-by-step with full control

```typescript theme={null}
// Simplified internal flow
const plan = planBuilder.build(workflow);
const result = await planExecutor.execute(plan, workflow, context);
```

### Benefits

<CardGroup cols={2}>
  <Card title="Conditional Execution" icon="code-branch">
    Native support for if/else, switch/case branching
  </Card>

  <Card title="Loop Support" icon="rotate">
    Future support for for/while loops (planned)
  </Card>

  <Card title="Debugging" icon="bug">
    Step-by-step debugging with breakpoints (planned)
  </Card>

  <Card title="Inspectability" icon="eye">
    Full visibility into execution plan structure
  </Card>
</CardGroup>

### Step Types

The execution plan consists of different step types:

* **NodeExecutionStep**: Execute a single node
* **ConditionalStep**: Evaluate condition and execute matching branch
* **LoopStep**: Execute steps repeatedly (planned)
* **ParallelStep**: Execute multiple steps in parallel (planned)

<Note>
  The plan-based architecture is transparent to users - workflows execute smoothly
  while supporting advanced control flow patterns like conditionals and future loop support.
</Note>

For a deep dive into the architecture, see the [execution-plan-architecture.md](https://github.com/Fladeed/crystalflow/blob/main/docs/execution-plan-architecture.md) document.

## Execution Flow

The execution follows these steps:

<Steps>
  <Step title="Validation">
    Workflow is validated (connections, required inputs)
  </Step>

  <Step title="Context Creation">
    ExecutionContext is created with unique execution ID
  </Step>

  <Step title="Build Execution Plan">
    Workflow graph is analyzed and converted to execution plan with steps and branches
  </Step>

  <Step title="Step-by-Step Execution">
    Each step executes in order:

    * **Node Step**: Execute node, transfer data, validate, store results
    * **Conditional Step**: Evaluate condition, select and execute matching branch
    * Check for cancellation between steps
  </Step>

  <Step title="Branch Evaluation" subtitle="For Conditional Nodes">
    When a conditional step is encountered:

    * Execute the conditional node to update its state
    * Call `evaluateCondition()` to get active branch
    * Execute steps in the matching branch
    * Skip other branches
  </Step>

  <Step title="Result Collection">
    All node results are collected and returned
  </Step>
</Steps>

## Event System

The Executor emits events throughout execution:

### Before Execution

```typescript theme={null}
executor.on('beforeExecute', (context) => {
  console.log('Starting execution:', context.executionId);
});
```

### Node Events

```typescript theme={null}
executor.on('onNodeStart', (nodeId, node) => {
  console.log(`Starting node ${nodeId} (${node.type})`);
});

executor.on('onNodeComplete', (nodeId, result) => {
  console.log(`Node ${nodeId} completed:`, result.outputs);
});

executor.on('onNodeError', (nodeId, error) => {
  console.error(`Node ${nodeId} failed:`, error.message);
});
```

### After Execution

```typescript theme={null}
executor.on('afterExecute', (result) => {
  console.log('Execution complete:', result.status);
  console.log('Duration:', result.duration, 'ms');
});
```

### Error Events

```typescript theme={null}
executor.on('onError', (error) => {
  console.error('Workflow execution failed:', error);
});
```

### Step Events

Monitor execution plan step execution:

```typescript theme={null}
executor.on('onStepStart', (stepId, stepType) => {
  console.log(`Starting step ${stepId} of type ${stepType}`);
});

executor.on('onStepComplete', (stepId, stepType) => {
  console.log(`Step ${stepId} completed`);
});

executor.on('onStepError', (stepId, stepType, error) => {
  console.error(`Step ${stepId} failed:`, error);
});
```

Step types include:

* `ExecutionStepType.Node` - Regular node execution
* `ExecutionStepType.Conditional` - Conditional branch selection
* `ExecutionStepType.Loop` - Loop execution (planned)
* `ExecutionStepType.Parallel` - Parallel execution (planned)

### Branch Events

Track conditional branch execution:

```typescript theme={null}
executor.on('onBranchEnter', (nodeId, branchId, condition) => {
  console.log(`Entering ${condition} branch of ${nodeId}`);
});

executor.on('onBranchExit', (nodeId, branchId) => {
  console.log(`Exiting branch ${branchId} of ${nodeId}`);
});
```

**Example with IfNode:**

```typescript theme={null}
const executor = new Executor();

executor.on('onBranchEnter', (nodeId, branchId, condition) => {
  // condition will be 'thenOutput' or 'elseOutput'
  console.log(`IfNode ${nodeId} taking ${condition} branch`);
});

executor.on('onBranchExit', (nodeId, branchId) => {
  console.log(`Completed ${branchId} branch of ${nodeId}`);
});

const result = await executor.execute(workflowWithIfNode);
// Output:
// "IfNode if-node-1 taking thenOutput branch"
// "Completed branch-id branch of if-node-1"
```

<Tip>
  Branch events are useful for:

  * Debugging conditional logic
  * Visualizing execution paths
  * Tracking which branches execute in complex workflows
  * Performance profiling of different branches
</Tip>

### Async Event Listeners

All event listeners support async functions - the executor waits for them to complete:

```typescript theme={null}
executor.on('onNodeComplete', async (nodeId, result) => {
  // Executor waits for async operations
  await saveToDatabase(result);
  await sendWebhook(nodeId);
  console.log('Node result processed');
});
```

## Execution Result

The executor returns an `ExecutionResult` object:

```typescript theme={null}
interface ExecutionResult {
  id: string;                                    // Unique execution ID
  workflowId: string;                            // Workflow identifier
  status: 'success' | 'failed' | 'cancelled';    // Execution status
  startTime: Date;                               // When execution started
  endTime?: Date;                                // When execution ended
  duration?: number;                             // Duration in ms
  nodeResults: Map<string, NodeExecutionResult>; // Results for each node
  error?: Error;                                 // Error if failed
  cancellationReason?: CancellationReason;       // Reason if cancelled
  cancelledAt?: Date;                            // When cancelled
}
```

### Using Results

```typescript theme={null}
const result = await executor.execute(workflow);

if (result.status === 'success') {
  // Access node outputs
  result.nodeResults.forEach((nodeResult, nodeId) => {
    console.log(`${nodeId}:`, nodeResult.outputs);
  });
  
  console.log('Total duration:', result.duration, 'ms');
} else {
  console.error('Execution failed:', result.error);
}
```

## Data Propagation

Data flows automatically between connected nodes:

```typescript theme={null}
// Node 1 outputs: { result: 42 }
// Connected to Node 2 input 'value'
// Node 2 automatically receives: this.value = 42
```

### How It Works

1. Source node executes and sets output values
2. Executor identifies connections from source outputs
3. Target node inputs are populated with output values
4. Target node executes with received data

## Error Handling

The execution engine provides robust error handling:

### Node Execution Errors

```typescript theme={null}
import { NodeExecutionError } from '@crystalflow/core';

try {
  const result = await executor.execute(workflow);
} catch (error) {
  if (error instanceof NodeExecutionError) {
    console.error(`Node ${error.nodeId} (${error.nodeType}) failed`);
    console.error('Execution ID:', error.executionId);
    console.error('Workflow ID:', error.workflowId);
    console.error('Cause:', error.cause);
  }
}
```

### Validation Errors

```typescript theme={null}
import { ValidationError } from '@crystalflow/core';

try {
  const result = await executor.execute(workflow);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Workflow validation failed:', error.message);
  }
}
```

### Timeout Errors

```typescript theme={null}
import { TimeoutError } from '@crystalflow/core';

try {
  const result = await executor.execute(workflow, { timeout: 5000 });
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error('Execution timed out after', error.timeout, 'ms');
  }
}
```

## Cancellation

Workflows can be cancelled during execution:

### User-Initiated Cancellation

```typescript theme={null}
// Start execution
const promise = executor.execute(workflow);

// Cancel after 2 seconds
setTimeout(() => {
  executor.cancel(executionId, CancellationReason.UserCancelled);
}, 2000);

try {
  await promise;
} catch (error) {
  if (error instanceof UserCancelledError) {
    console.log('User cancelled execution');
  }
}
```

### External Signal Cancellation

```typescript theme={null}
const controller = new AbortController();

// Start execution
const promise = executor.execute(workflow, {
  abortSignal: controller.signal
});

// Cancel from external signal
controller.abort();
```

### Timeout-Based Cancellation

```typescript theme={null}
try {
  const result = await executor.execute(workflow, {
    timeout: 30000 // 30 seconds
  });
} catch (error) {
  if (error instanceof TimeoutError) {
    console.log('Execution timed out');
  }
}
```

### Mid-Node Cancellation

Nodes can check for cancellation during long operations:

```typescript theme={null}
class LongRunningNode extends Node {
  async execute() {
    for (let i = 0; i < 1000; i++) {
      // Check if execution was cancelled
      this.checkCancellation();
      
      await processItem(i);
    }
  }
}
```

## Execution Context

The `ExecutionContext` provides runtime information to nodes:

```typescript theme={null}
class CustomNode extends Node {
  execute() {
    // Access execution context
    const executionId = this.context.executionId;
    const workflowId = this.context.workflowId;
    const variables = this.context.variables;
    
    // Use context data
    console.log(`Executing in ${executionId}`);
  }
}
```

### Context Properties

<ParamField path="executionId" type="string">
  Unique identifier for this execution
</ParamField>

<ParamField path="workflowId" type="string">
  Identifier of the workflow being executed
</ParamField>

<ParamField path="variables" type="Record<string, any>">
  Global variables passed to execution
</ParamField>

<ParamField path="startTime" type="Date">
  When execution started
</ParamField>

## Performance Monitoring

Track execution performance with timing data:

```typescript theme={null}
const result = await executor.execute(workflow);

console.log('Total execution time:', result.duration, 'ms');

// Individual node timings
result.nodeResults.forEach((nodeResult, nodeId) => {
  console.log(`${nodeId}: ${nodeResult.duration}ms`);
});
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always Handle Errors" icon="triangle-exclamation">
    Wrap execution in try-catch and handle different error types appropriately.
  </Accordion>

  <Accordion title="Set Reasonable Timeouts" icon="clock">
    Use timeouts to prevent workflows from hanging indefinitely.
  </Accordion>

  <Accordion title="Monitor Execution Events" icon="eye">
    Use events for logging, monitoring, and debugging execution flow.
  </Accordion>

  <Accordion title="Check Cancellation in Long Nodes" icon="stop">
    For long-running nodes, periodically check for cancellation.
  </Accordion>

  <Accordion title="Use Variables for Configuration" icon="gear">
    Pass runtime configuration through variables rather than hardcoding.
  </Accordion>
</AccordionGroup>

## Example: Complete Execution

```typescript theme={null}
import { Executor, CancellationReason } from '@crystalflow/core';

async function runWorkflow(workflow) {
  const executor = new Executor();
  
  // Set up event listeners
  executor.on('beforeExecute', (context) => {
    console.log(`Starting execution ${context.executionId}`);
  });
  
  executor.on('onNodeStart', (nodeId, node) => {
    console.log(`Executing ${node.type}`);
  });
  
  executor.on('onNodeComplete', async (nodeId, result) => {
    // Async event handler - executor waits
    await logToDatabase(nodeId, result);
  });
  
  executor.on('afterExecute', (result) => {
    console.log(`Completed in ${result.duration}ms`);
  });
  
  // Execute with options
  try {
    const result = await executor.execute(workflow, {
      timeout: 60000,
      variables: {
        apiKey: process.env.API_KEY,
        environment: 'production'
      }
    });
    
    if (result.status === 'success') {
      console.log('Workflow succeeded!');
      return result;
    }
  } catch (error) {
    console.error('Execution failed:', error);
    throw error;
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Decorators" icon="at" href="/core-concepts/decorators">
    Learn about decorator syntax
  </Card>

  <Card title="Execution & Events Guide" icon="bolt" href="/guides/execution-and-events">
    Master the event system
  </Card>

  <Card title="Cancellation" icon="stop" href="/advanced/cancellation">
    Advanced cancellation patterns
  </Card>

  <Card title="Executor API" icon="code" href="/api-reference/core/executor">
    Complete Executor API docs
  </Card>
</CardGroup>
