WorkflowDescriptor API
Complete API reference for WorkflowDescriptor and related types.
WorkflowDescriptor
A collection of type descriptors and executors for extending the workflow engine.
dart
class WorkflowDescriptor {
const WorkflowDescriptor({
this.tasks = const [],
this.userTasks = const [],
this.conditions = const [],
this.nodeConfigurations = const [],
this.nodeExecutors = const [],
this.title,
this.description,
});
/// Task executors for automated work
final List<TypeDescriptor<TaskExecutor>> tasks;
/// User task executors for human tasks
final List<TypeDescriptor<UserTaskExecutor>> userTasks;
/// Condition executors for gateway routing
final List<TypeDescriptor<ConditionExecutor>> conditions;
/// Node configuration type descriptors
final List<TypeDescriptor<NodeConfiguration>> nodeConfigurations;
/// Node executor instances
final List<NodeExecutor> nodeExecutors;
/// Optional title
final String? title;
/// Optional description
final String? description;
}Properties
| Property | Type | Description |
|---|---|---|
tasks | List<TypeDescriptor<TaskExecutor>> | Task executor type descriptors |
userTasks | List<TypeDescriptor<UserTaskExecutor>> | User task executor type descriptors |
conditions | List<TypeDescriptor<ConditionExecutor>> | Condition executor type descriptors |
nodeConfigurations | List<TypeDescriptor<NodeConfiguration>> | Node config type descriptors |
nodeExecutors | List<NodeExecutor> | Node executor instances |
title | String? | Human-readable title |
description | String? | Description of this descriptor |
Methods
merge
Combine two descriptors into one:
dart
WorkflowDescriptor merge(WorkflowDescriptor other)dart
final combined = descriptor1.merge(descriptor2);
// combined.tasks = [...descriptor1.tasks, ...descriptor2.tasks]hasTask / hasUserTask / hasCondition
Check if a specific schema type is provided:
dart
bool hasTask(String schemaType)
bool hasUserTask(String schemaType)
bool hasCondition(String schemaType)
bool hasNodeConfiguration(String schemaType)Schema Type Getters
Get all registered schema types:
dart
Set<String> get taskSchemaTypes
Set<String> get userTaskSchemaTypes
Set<String> get conditionSchemaTypes
Set<String> get nodeConfigurationSchemaTypesTypeDescriptor
Connects a schemaType string to a Dart class via a fromJson factory.
dart
class TypeDescriptor<T> {
const TypeDescriptor({
required this.schemaType,
required this.fromJson,
this.title,
this.description,
this.category,
this.iconName,
this.version,
this.author,
this.dependencies,
this.tags,
this.inputSchema,
this.outputSchema,
this.documentationUrl,
});
/// Unique schema type identifier
final String schemaType;
/// Factory to create instance from JSON
final T Function(Map<String, dynamic> json) fromJson;
/// Human-readable title
final String? title;
/// Description of what this type does
final String? description;
/// Category for UI grouping
final String? category;
/// Icon name for UI
final String? iconName;
/// Version string
final String? version;
/// Author name
final String? author;
/// Dependencies on other schema types
final List<String>? dependencies;
/// Tags for search/filtering
final List<String>? tags;
/// JSON Schema for input validation
final Map<String, dynamic>? inputSchema;
/// JSON Schema for output validation
final Map<String, dynamic>? outputSchema;
/// URL to documentation
final String? documentationUrl;
}Properties
| Property | Type | Description |
|---|---|---|
schemaType | String | Unique identifier (required) |
fromJson | Function | Factory function (required) |
title | String? | Display name |
description | String? | What this does |
category | String? | UI grouping category |
iconName | String? | Icon identifier |
version | String? | Version (e.g., '1.0.0') |
author | String? | Author name/email |
dependencies | List<String>? | Other required schema types |
tags | List<String>? | Search tags |
inputSchema | Map? | JSON Schema for inputs |
outputSchema | Map? | JSON Schema for outputs |
documentationUrl | String? | Link to docs |
Usage Pattern
dart
class MyTaskExecutor extends TaskExecutor {
static const _schemaType = 'task.my.custom';
static final typeDescriptor = TypeDescriptor<TaskExecutor>(
schemaType: _schemaType,
fromJson: (json) => MyTaskExecutor.fromJson(json),
title: 'My Custom Task',
description: 'Does something custom',
category: 'Custom',
);
factory MyTaskExecutor.fromJson(Map<String, dynamic> json) {
return MyTaskExecutor();
}
@override
String get schemaType => _schemaType;
@override
String get name => 'My Custom Task';
@override
Future<TaskResult> execute(ExecutionContext context) async {
// implementation
}
}TypeRegistry
Generic registry for type descriptors.
dart
class TypeRegistry<T> {
TypeRegistry({this.schemaTypeKey = 'schemaType'});
/// Register a type descriptor
void register(TypeDescriptor<T> descriptor);
/// Register multiple descriptors
void registerAll(Iterable<TypeDescriptor<T>> descriptors);
/// Unregister a type
void unregister(String schemaType);
/// Get descriptor by schema type
TypeDescriptor<T>? getDescriptor(String schemaType);
/// Create instance by schema type
T? create(String schemaType, {Map<String, dynamic>? config});
/// Create instance, throw if not found
T createRequired(String schemaType, {Map<String, dynamic>? config});
/// Check if type is registered
bool hasType(String schemaType);
/// Get all registered schema types
Set<String> get registeredTypes;
/// Get all descriptors
Iterable<TypeDescriptor<T>> get descriptors;
/// Deserialize from JSON using schemaType field
T? fromJson(Map<String, dynamic>? json);
/// Deserialize, throw if type not found
T fromJsonRequired(Map<String, dynamic> json);
/// Deserialize list
List<T>? listFromJson(dynamic json);
/// Clear all registrations
void clear();
}DefaultWorkflowDescriptor
Pre-configured descriptor with all built-in executors.
dart
class DefaultWorkflowDescriptor extends WorkflowDescriptor {
DefaultWorkflowDescriptor();
}Included Node Executors
| Executor | Node Type | Description |
|---|---|---|
StartEventNodeExecutor | start | Workflow entry |
EndEventNodeExecutor | end | Workflow termination |
SignalEventNodeExecutor | signalWait | External signals |
TaskNodeExecutor | task | Automated tasks |
UserTaskNodeExecutor | userTask | Human tasks |
OneOfGatewayNodeExecutor | oneOf | XOR routing |
AnyOfGatewayNodeExecutor | anyOf | Race routing |
AllOfGatewayNodeExecutor | allOf | AND routing |
Included Node Configurations
| Configuration | Schema Type | Description |
|---|---|---|
TaskNodeConfiguration | config.task | Task node config |
UserTaskNodeConfiguration | config.userTask | User task config |
SignalWaitNodeConfiguration | config.signalWait | Signal wait config |
GatewayNodeConfiguration | config.gateway | Gateway config |
NodeExecutor
Interface for node execution.
dart
abstract class NodeExecutor {
/// The node type this executor supports
NodeType get type;
/// Execute the node
Future<NodeResult> execute(WorkflowContext context);
/// Handle received signal (for waiting nodes)
Future<NodeResult> onSignalReceived(
WorkflowContext context,
String signalName,
Map<String, dynamic>? payload,
);
/// Validate node configuration
List<String> validateNode(WorkflowNode node);
}Implementing a Custom Handler
dart
class MyCustomGatewayHandler extends NodeExecutor {
@override
NodeType get type => NodeType.oneOf;
@override
Future<NodeResult> execute(WorkflowContext context) async {
// Custom routing logic
final decision = evaluateCustomLogic(context);
return ContinueResult.single(decision.targetNodeId);
}
}TypeRegistryException
Exception thrown when registry operations fail.
dart
class TypeRegistryException implements Exception {
const TypeRegistryException(
this.message, {
this.schemaType,
this.json,
});
final String message;
final String? schemaType;
final Map<String, dynamic>? json;
}Complete Example
dart
// Define executors
class SendEmailExecutor extends TaskExecutor {
static const _schemaType = 'task.notification.sendEmail';
static final typeDescriptor = TypeDescriptor<TaskExecutor>(
schemaType: _schemaType,
fromJson: (json) => SendEmailExecutor(),
title: 'Send Email',
category: 'Notifications',
);
@override
String get schemaType => _schemaType;
@override
String get name => 'Send Email';
@override
Future<TaskResult> execute(ExecutionContext context) async {
final to = context.getRequired<String>('to');
final subject = context.get<String>('subject') ?? 'Notification';
await emailService.send(to: to, subject: subject);
return TaskSuccess(output: {
'sentAt': DateTime.now().toIso8601String(),
});
}
}
// Create descriptor
final notificationExecutors = WorkflowDescriptor(
title: 'Notification Executors',
tasks: [SendEmailExecutor.typeDescriptor],
);
// Create deserialization context with all descriptors
final context = RegistryDeserializationContext(
descriptors: [
DefaultWorkflowDescriptor(),
notificationExecutors,
],
);
// Create engine with context and storage
final engine = WorkflowEngine(
context: context,
storage: InMemoryStorage(context: context),
);
await engine.initialize();See Also
- WorkflowDescriptor Concepts - Conceptual overview
- Task Executors - Task implementation
- Type Registries - Registry details