Skip to content

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

PropertyTypeDescription
tasksList<TypeDescriptor<TaskExecutor>>Task executor type descriptors
userTasksList<TypeDescriptor<UserTaskExecutor>>User task executor type descriptors
conditionsList<TypeDescriptor<ConditionExecutor>>Condition executor type descriptors
nodeConfigurationsList<TypeDescriptor<NodeConfiguration>>Node config type descriptors
nodeExecutorsList<NodeExecutor>Node executor instances
titleString?Human-readable title
descriptionString?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 nodeConfigurationSchemaTypes

TypeDescriptor

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

PropertyTypeDescription
schemaTypeStringUnique identifier (required)
fromJsonFunctionFactory function (required)
titleString?Display name
descriptionString?What this does
categoryString?UI grouping category
iconNameString?Icon identifier
versionString?Version (e.g., '1.0.0')
authorString?Author name/email
dependenciesList<String>?Other required schema types
tagsList<String>?Search tags
inputSchemaMap?JSON Schema for inputs
outputSchemaMap?JSON Schema for outputs
documentationUrlString?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

ExecutorNode TypeDescription
StartEventNodeExecutorstartWorkflow entry
EndEventNodeExecutorendWorkflow termination
SignalEventNodeExecutorsignalWaitExternal signals
TaskNodeExecutortaskAutomated tasks
UserTaskNodeExecutoruserTaskHuman tasks
OneOfGatewayNodeExecutoroneOfXOR routing
AnyOfGatewayNodeExecutoranyOfRace routing
AllOfGatewayNodeExecutorallOfAND routing

Included Node Configurations

ConfigurationSchema TypeDescription
TaskNodeConfigurationconfig.taskTask node config
UserTaskNodeConfigurationconfig.userTaskUser task config
SignalWaitNodeConfigurationconfig.signalWaitSignal wait config
GatewayNodeConfigurationconfig.gatewayGateway 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