Tokens
Tokens track execution position in a workflow, enabling parallel execution, visualization, and crash recovery.
Structure
dart
class WorkflowToken {
final String id;
final String currentNodeId; // Current position
final String? previousNodeId; // Where it came from
final bool isActive; // Is this token still executing?
final DateTime createdAt; // When this token was created
final String? branchId; // Branch identifier
final String? parentTokenId; // For parallel branches
}Token Lifecycle
1. Creation at Start
When a workflow starts, a single token is created at the start node:
dart
tokens: [
WorkflowToken(
id: 'token-1',
currentNodeId: 'start',
isActive: true,
)
]2. Movement Through Nodes
As the workflow executes, the token moves:
dart
// After validate task
tokens: [
WorkflowToken(
id: 'token-1',
currentNodeId: 'validateTask',
previousNodeId: 'start',
isActive: true,
)
]3. Splitting at Parallel Gateway
At a parallel (AND) gateway fork, the token splits:
dart
// Before fork
tokens: [
{ id: 'token-1', currentNodeId: 'parallelGateway', isActive: true }
]
// After fork (3 parallel branches)
tokens: [
{ id: 'token-1', currentNodeId: 'parallelGateway', isActive: false }, // Deactivated
{ id: 'token-2', currentNodeId: 'branchA', parentTokenId: 'token-1', branchId: 'a', isActive: true },
{ id: 'token-3', currentNodeId: 'branchB', parentTokenId: 'token-1', branchId: 'b', isActive: true },
{ id: 'token-4', currentNodeId: 'branchC', parentTokenId: 'token-1', branchId: 'c', isActive: true },
]4. Merging at Join
When all branches reach a join gateway:
dart
// Branches arriving at join
tokens: [
{ id: 'token-2', currentNodeId: 'joinGateway', isActive: false }, // Arrived, waiting
{ id: 'token-3', currentNodeId: 'joinGateway', isActive: false }, // Arrived, waiting
{ id: 'token-4', currentNodeId: 'joinGateway', isActive: false }, // Arrived, now all here
{ id: 'token-5', currentNodeId: 'nextTask', parentTokenId: 'token-1', isActive: true }, // Merged
]5. Completion
At an end node, the token is deactivated:
dart
tokens: [
{ id: 'token-5', currentNodeId: 'end', isActive: false }
]Active vs Inactive Tokens
| State | Meaning |
|---|---|
isActive: true | Token is at a node and ready to execute |
isActive: false | Token has completed or is waiting (join) |
Only active tokens represent current execution positions.
Token Queries
dart
// Get active tokens
final activeTokens = instance.tokens.where((t) => t.isActive);
// Get tokens at specific node
final tokensAtNode = instance.tokens.where(
(t) => t.currentNodeId == 'approvalTask'
);
// Get child tokens of a parent
final childTokens = instance.tokens.where(
(t) => t.parentTokenId == 'token-1'
);Visualization
Tokens enable real-time workflow visualization:
dart
// In your UI
Widget buildWorkflowVisualization(WorkflowInstance instance) {
return WorkflowDiagram(
definition: definition,
activeNodeIds: instance.tokens
.where((t) => t.isActive)
.map((t) => t.currentNodeId)
.toSet(),
);
}Recovery
Tokens enable crash recovery:
dart
// On server restart
final waitingInstances = await storage.instances.findByStatus(
WorkflowStatus.waitingForSignal,
);
for (final instance in waitingInstances) {
// Resume from exact token positions
await engine.resumeWorkflow(instance.id);
}