Process Context

type ProcessContext = { 
  parent?: ProcessContext;
  [key: string]: unknown;
}

Process context is a container for shared resources in StateWalker FSM applications. It passes through all state handlers and enables modules to access common entities without creating tight coupling between them.

The context acts as a communication bridge between orchestrator components, allowing handlers to share data models, APIs, services, and configuration while maintaining separation of concerns.

Process contexts support hierarchical structures where child contexts inherit resources from parent contexts. This enables resource scoping and configuration overrides without creating direct dependencies between modules.

The adapter pattern provides type-safe access to context resources through getter and setter functions. This eliminates manual type casting and provides compile-time validation of resource access.

Using Adapters for Resource Access

The adapter pattern provides a clean interface for storing and retrieving typed resources from the process context. By creating dedicated accessor functions, you establish clear contracts for resource management while maintaining type safety throughout your application.

import { newContextAdapter } from "@/utils/adapters.js";

// Create type-safe adapters for a FileSystem API
const [getFileSystem, setFileSystem] = newContextAdapter<FileSystem>("fileSystem");

// Create adapters for other common resources
const [getConfiguration, setConfiguration] = newContextAdapter<AppConfig>("config");
const [getLogger, setLogger] = newContextAdapter<Logger>("logger");
const [getUserModel, setUserModel] = newContextAdapter<UserModel>("user:model");

Resource initialization typically occurs in top-level application handlers during the bootstrap phase, ensuring that shared resources are available before other modules attempt to access them.

async function ApplicationBootstrap(context: ProcessContext) {
  // Initialize core APIs and services
  const filesApi = new InBrowserFilesApi({ 
    baseDirectory: '/app-data',
    maxFileSize: 10 * 1024 * 1024 
  });
  const config = await loadConfiguration();
  const logger = new ConsoleLogger(config.logLevel);
  
  // Bind resources to context for application-wide access
  setFileSystem(context, filesApi);
  setConfiguration(context, config);
  setLogger(context, logger);
  
  // Initialize reactive data models
  const userModel = new UserModel();
  setUserModel(context, userModel);
  
  return () => {
    // Cleanup resources when application shuts down
    filesApi.close();
    logger.flush();
  };
}

Once resources are bound to the context, any handler in the application can access them through the corresponding getter functions, maintaining type safety and clear dependency relationships.

async function SaveUserData(context: ProcessContext) {
  const filesApi = getFileSystem(context);
  const userModel = getUserModel(context);
  const logger = getLogger(context);
  
  try {
    const userData = userModel.serialize();
    await filesApi.writeFile('user-profile.json', userData);
    logger.info('User data saved successfully');
  } catch (error) {
    logger.error('Failed to save user data', error);
    throw error;
  }
}

async function LoadConfiguration(context: ProcessContext) {
  const config = getConfiguration(context);
  const logger = getLogger(context);
  
  logger.debug(`Loading configuration: ${config.environment}`);
  
  // Use configuration to customize behavior
  if (config.features.enableAnalytics) {
    // Initialize analytics tracking
  }
}

Hierarchical Context Management

The hierarchical nature of process contexts enables sophisticated resource scoping and inheritance patterns. Child contexts automatically inherit resources from their parents while allowing local overrides for specific use cases.

async function CreateUserSession(context: ProcessContext) {
  // Retrieve a process runner from the parent context
  const startProcess = getProcessRunner(context);

  // Create child context for user session
  const sessionContext: ProcessContext = { parent: context };
  
  // Session-specific resources that override global ones
  const sessionLogger = new SessionLogger(getUserId(context));
  const sessionConfig = { ...getConfiguration(context), sessionTimeout: 3600 };
  
  setLogger(sessionContext, sessionLogger);
  setConfiguration(sessionContext, sessionConfig);
  
  // Session context inherits FileSystem from parent but uses custom logger
  const cleanupChildProcess = await startProces("child-process-name", sessionContext)

  // Return the function terminating the child process
  return cleanupChildProcess;
}

async function SessionHandler(context: ProcessContext) {
  // Accesses session-specific logger but inherits FileSystem from parent
  const logger = getLogger(context);      // Session-specific logger
  const filesApi = getFileSystem(context); // Inherited from parent context
  
  logger.info('Session started');
  await filesApi.createDirectory('session-data');
}

This hierarchical approach enables powerful patterns like feature flags, environment-specific configuration, and user-specific customization without polluting the global context or creating complex dependency chains. Each level of the hierarchy can provide its own resources while falling back to parent contexts for missing resources, creating a flexible and maintainable resource management system.