Triggers

Transform Data Changes to FSM Events

Triggers serve as reactive event generators that bridge application data and state machine transitions within StateWalker’s unidirectional data flow architecture. They observe data models and convert data changes into events that drive state transitions, representing the sole connection point between the generic core library and the orchestrator framework.

Triggers access models through the process context using the adapter pattern. This approach maintains loose coupling while enabling reactive connections between the data layer and state machine events. Unlike controllers that modify models, triggers maintain strict read-only access to application data.

Triggers establish subscriptions to models through onChange listeners, enabling them to respond automatically to data changes by evaluating business rules and generating appropriate events. When models change, triggers analyze these modifications and yield string events that the state machine orchestrator uses to determine and execute state transitions.

Triggers return cleanup functions through async generator patterns to ensure proper resource management during state transitions. These cleanup functions unsubscribe from model change notifications and release any allocated resources, preventing memory leaks and maintaining system stability.

Implementation Examples

Basic Trigger: Model Access and Event Generation

import { newAsyncGenerator } from "@/utils/generators";

export default async function* UserLoginTrigger(context: ProcessContext): AsyncGenerator<string> {
  // Context access: Get model through process context
  const userModel = getUserModel(context);
  
  // Event generation: Transform model changes into FSM events
  yield* newAsyncGenerator((dispatch) => {
    // Model subscription: Listen to changes in read-only mode
    return userModel.onChange((user) => {
      if (user.isAuthenticated) {
        dispatch("loginSuccess"); 
      } else {
        dispatch("logout");
      }
    });
  });
}

Advanced Trigger: Multiple Model Monitoring and Complex Cleanup

async function* AuthenticationTrigger(context: ProcessContext) {
  // Registry pattern: Manage multiple cleanup functions
  const [register, cleanup] = newRegistry();
  
  // Context access: Get multiple models
  const userModel = getUserModel(context);
  const authModel = getAuthModel(context);

  yield* newAsyncGenerator((dispatch) => {
    // Business logic: Evaluate authentication state from multiple models
    const checkAuthenticationStatus = () => {
      if (authModel.isAuthenticated && userModel.profile) {
        dispatch("authenticated");
      } else if (authModel.authError) {
        dispatch("authenticationFailed");
      } else if (authModel.sessionExpired) {
        dispatch("sessionExpired");
      }
    };
    
    // Model subscriptions: Monitor changes across multiple models
    register(userModel.onChange(checkAuthenticationStatus));
    register(authModel.onChange(checkAuthenticationStatus));
    
    // Initial evaluation on trigger activation
    checkAuthenticationStatus();
    
    // Cleanup function: Registry releases all subscriptions
    return cleanup;
  });
}

Complex Event Stream: Resource Management and Multiple Subscriptions

async function* UserActivityTrigger(context: ProcessContext): AsyncGenerator<string> {
  // Context access: Get models for activity monitoring
  const userModel = getUserModel(context);
  const sessionModel = getSessionModel(context);
  
  yield* newAsyncGenerator<string>((next, done) => {
    // Model subscription: Monitor user state changes
    const userCleanup = userModel.onChange(() => {
      const user = userModel.currentUser;
      
      // Event generation: Transform user state into activity events
      if (!user) {
        next("userLoggedOut");
      } else if (user.isActive) {
        next("userBecameActive");
      } else {
        next("userBecameInactive");
      }
    });
    
    // Model subscription: Monitor session state changes
    const sessionCleanup = sessionModel.onChange(() => {
      if (sessionModel.isExpired) {
        next("sessionExpired");
      }
    });
    
    // Cleanup function: Release all model subscriptions
    return () => {
      userCleanup();
      sessionCleanup();
    };
  });
}

Key Concepts in Practice

Event Generation: Triggers observe model changes and transform them into string events within the unidirectional data flow architecture. This enables declarative state management that responds to data changes rather than requiring manual event coordination.

Context Access: The process context provides access to models using the adapter pattern. This maintains loose coupling while enabling triggers to monitor application data in read-only mode.

Model Monitoring: Triggers establish reactive subscriptions to models through onChange listeners, evaluating business rules to determine when state transitions should occur.

Resource Management: Triggers use async generator patterns with cleanup functions to unsubscribe from model notifications and release resources. The registry pattern manages multiple cleanup operations automatically.

These patterns combine to create triggers that respond to model changes, generate state machine events, access shared resources through context, and maintain clean resource management throughout the application lifecycle.