chrono-forge
Version:
A comprehensive framework for building resilient Temporal workflows, advanced state management, and real-time streaming activities in TypeScript. Designed for a seamless developer experience with powerful abstractions, dynamic orchestration, and full cont
460 lines (459 loc) • 19.7 kB
TypeScript
import 'reflect-metadata';
import * as workflow from '@temporalio/workflow';
import EventEmitter from 'eventemitter3';
import { Duration } from '@temporalio/common';
import { LRUCacheWithDelete } from 'mnemonist';
import { DSLDefinition, DSLGeneration } from './DSLInterpreter';
/**
* Options for configuring a Temporal Workflow.
*
* This interface defines the configuration options that can be passed to a Temporal workflow
* when it is created or registered with the Temporal service.
*/
export interface TemporalOptions {
/**
* The name of the workflow.
* If not provided, the class name will be used as the workflow name.
*/
name?: string;
/**
* Activity functions to use with the DSL interpreter.
* If not provided, will use default activity proxy.
*/
activities?: Record<string, (...args: string[]) => Promise<string | undefined>>;
/**
* Additional arbitrary options.
* Any other configuration options that might be needed for specific workflow implementations.
*/
[key: string]: any;
}
/**
* Temporal Decorator
*
* A class decorator that transforms a TypeScript class into a Temporal workflow function.
*
* This decorator handles the registration, initialization, and lifecycle management of workflows
* in the TemporalForge framework.
*
* Key Features:
* - Automatically registers the class as a Temporal workflow
* - Manages workflow initialization and lifecycle events
* - Handles workflow naming and task queue assignment
* - Provides automatic error handling and cancellation support
* - Supports dynamic class extension for non-Workflow classes
*
* The decorator performs the following operations:
* 1. Ensures the class extends the Workflow base class
* 2. Sets up workflow metadata and configuration
* 3. Binds event handlers, hooks, and signals
* 4. Manages workflow execution flow and error handling
*
* @param options - Configuration options for the workflow
* @returns A class decorator function that transforms the target class into a Temporal workflow
*/
export declare function Temporal(options?: TemporalOptions): (constructor: any) => any;
/**
* Base Workflow class for Temporal workflows.
*
* Represents a base class that provides essential infrastructure for creating workflows.
* Includes mechanisms for handling signals, queries, event handling, and more.
* Extends the EventEmitter to aid in workflow event management.
*
* This class implements core Temporal workflow functionality including:
* - Signal and query handling
* - Event emission and subscription
* - Workflow state management
* - Child workflow management
* - Execution flow control (pause, resume, cancel)
* - Continuation (continueAsNew) support
* - Structured logging
*
* @template P - Type of input parameters for the workflow constructor.
* @template O - Type of configuration options for the workflow.
*/
export declare abstract class Workflow<P = unknown, O = unknown> extends EventEmitter {
protected args: P;
protected options: O;
protected workflowType: string;
/**
* Map to keep track of child workflow handles.
* Uses LRU cache to efficiently manage a large number of child workflow references
* without consuming excessive memory.
*/
protected handles: LRUCacheWithDelete<string, workflow.ChildWorkflowHandle<any>>;
/**
* Internal flags used to determine if certain decorators have been bound to this workflow instance.
* These flags prevent duplicate binding of handlers during workflow execution.
*/
protected _eventsBound: boolean;
private _hooksBound;
private _signalsBound;
private _queriesBound;
private _propertiesBound;
private _stepsBound;
protected dsl: DSLDefinition;
/**
* Workflow logging instance provided by Temporal.
*
* Provides structured logging with workflow context information automatically included.
* All log messages are prefixed with workflow class name and workflow ID for easier debugging.
*
* Available log levels: debug, info, warn, error, trace
*/
private createLogger;
protected log: {
debug: (message: string, ...args: any[]) => void;
info: (message: string, ...args: any[]) => void;
warn: (message: string, ...args: any[]) => void;
error: (message: string, ...args: any[]) => void;
trace: (message: string, ...args: any[]) => void;
};
/**
* Workflow execution result.
* Stores the final result of the workflow execution that will be returned to the caller.
*/
protected result: any;
/**
* Handlers for workflow queries.
* Maps query names to their handler functions.
* Query handlers allow external systems to retrieve workflow state without modifying it.
*/
protected queryHandlers: {
[key: string]: (...args: any[]) => any;
};
/**
* Handlers for workflow signals.
* Maps signal names to their handler functions.
* Signal handlers allow external systems to trigger actions within the workflow.
*/
protected signalHandlers: {
[key: string]: (args: any[]) => Promise<void>;
};
/**
* Handlers for workflow steps.
* Maps step names to their handler functions.
* Step handlers allow external systems to trigger actions within the workflow.
*/
protected stepHandlers: Record<string, (...args: string[]) => Promise<string | undefined>>;
protected startDelay: Duration | undefined;
/**
* Determines whether the workflow is a long running continueAsNew type workflow or a short lived one.
* When true, the workflow will use the continueAsNew mechanism to restart itself with the same parameters
* when it reaches certain limits (history size, iterations, etc.).
*/
protected isContinueable: boolean;
/**
* Boolean flag indicating if workflow should continue as new immediately.
* When set to true, the workflow will trigger the continueAsNew mechanism at the end of the current iteration.
*/
protected continueAsNew: boolean;
/**
* Maximum number of iterations for the workflow.
* Prevents infinite loops and ensures the workflow history doesn't grow too large.
*
* When this limit is reached, the workflow will either continue as new or terminate.
*/
protected maxIterations: number;
/**
* Maximum number of history size for the workflow represented in bytes.
* This is used to prevent the workflow history from growing too large.
*
* Default is 25MB.
*/
protected maxHistorySize: number;
/**
* Current workflow iteration count.
* Tracks how many times the workflow has executed its main loop.
*/
protected iteration: number;
/**
* Boolean flag indicating pending iteration.
* Setting this to true will result in a full execute() loop being run.
* This is useful if you have made changes and want the state or memo to update, or similar.
*/
protected pendingIteration: boolean;
/**
* Current status of the workflow.
* Possible values include: 'running', 'paused', 'cancelling', 'cancelled', 'complete', 'completed', 'error', etc.
* This status is used to control workflow execution flow and can be queried externally.
*/
protected status: string;
/**
* Boolean flag indicating a pending update.
* Setting this to true will result in loadData() being called if it is defined.
* Useful for triggering data refresh operations within the workflow.
*/
protected pendingUpdate: boolean;
/**
* Signal to pause workflow execution.
*
* When this signal is received, the workflow will:
* 1. Change its status to 'paused'
* 2. Update the workflow memo with the new status
* 3. Forward the pause signal to all child workflows
* 4. Emit the 'paused' event
* 5. Wait for a resume signal before continuing execution
*
* The workflow will not process any further iterations until resumed.
*/
pause(): void;
/**
* Signal to resume workflow execution.
*
* When this signal is received, the workflow will:
* 1. Change its status from 'paused' to 'running'
* 2. Update the workflow memo with the new status
* 3. Forward the resume signal to all child workflows
* 4. Continue processing iterations
*
* If the workflow is already running, this signal has no effect.
*/
resume(): void;
/**
* Signal to cancel workflow execution.
*
* When this signal is received, the workflow will:
* 1. Change its status to 'cancelling'
* 2. Update the workflow memo with the new status
* 3. Forward the cancel signal to all child workflows
* 4. Eventually transition to 'cancelled' state
*
* The workflow will terminate after completing any necessary cleanup operations.
* If the workflow is already cancelling, this signal has no effect.
*/
cancel(): void;
/**
* Constructor for the Workflow base class.
*
* Initializes the workflow with the provided arguments and options.
* Sets up the basic workflow state and prepares it for execution.
*
* @param args - Initial parameters for workflow execution. These parameters define the input data for the workflow.
* @param options - Configuration options for the workflow. These options control workflow behavior and settings.
*/
constructor(args: P, options: O);
/**
* Optional duration for condition timeout.
*
* When set, this duration limits how long the workflow will wait for conditions to be met
* before continuing execution. If not set, the workflow will wait indefinitely.
*/
protected conditionTimeout: Duration | undefined;
/**
* Check if workflow is in a terminal state.
*
* Terminal states indicate that the workflow has finished execution and will not
* process any more iterations. This includes successful completion, cancellation,
* and error states.
*
* @returns {boolean} True if the workflow is in a terminal state, false otherwise.
*/
protected isInTerminalState(): boolean;
/**
* Optional method that can be implemented by subclasses to define custom continue-as-new behavior.
* If implemented, this method will be called when the workflow reaches its maximum iterations.
* @returns A promise that resolves when the continue-as-new process is complete.
*/
protected onContinue?(): Promise<Record<string, unknown>>;
/**
* Base method to execute the workflow logic using DSL.
*
* This method executes the workflow's DSL definition if one exists.
* Subclasses can override this method to provide custom logic,
* and can optionally call super.execute() to run the DSL steps.
*
* @param args - Arguments required for execution.
* @returns {Promise<unknown>} Result of the workflow execution.
*/
protected interpreter: AsyncGenerator<DSLGeneration, void, unknown> | undefined;
protected currentGeneration: DSLGeneration | undefined;
protected execute(...args: unknown[]): Promise<unknown>;
/**
* Checks if there are more nodes to execute in the DSL.
* This is used in the workflow condition to determine if execution should continue.
*
* @returns {boolean} True if there is a current generation to execute, false otherwise.
*/
protected hasDSLNodesToExecute(): boolean;
/**
* Core method to manage workflow execution and its lifecycle.
*
* This method implements the main execution loop of the workflow, handling:
* - Condition waiting
* - Pause/resume/cancel signals
* - Iteration counting
* - Error handling
* - ContinueAsNew logic
* - Terminal state detection
*
* @param args - Arguments passed for workflow execution.
* @returns {Promise<any>} Result of the workflow processing.
*/
protected executeWorkflow(...args: unknown[]): Promise<any>;
/**
* Handle the scenario when maximum iterations are reached.
*
* This method is called when the workflow reaches its maximum iteration count
* or when the history size becomes too large. It manages the continueAsNew process
* by waiting for all handlers to finish and then calling the appropriate continueAsNew method.
*
* @returns {Promise<void>} Resolves when the continueAsNew process is complete.
* @throws {Error} If no onContinue method is found.
*/
protected handleMaxIterations(): Promise<void>;
/**
* Handle errors encountered during workflow execution.
*
* This method provides centralized error handling for the workflow, with special
* handling for cancellation errors. For cancellation errors, it ensures all child
* workflows are also cancelled. For other errors, it logs the error and rejects
* the workflow promise.
*
* @param err - Error encountered during workflow execution.
* @param reject - Function to call with rejection error to propagate it up the call stack.
* @returns {Promise<void>} Resolves when error handling is complete.
*/
protected handleExecutionError(err: any, reject: (err: Error) => void): Promise<void>;
/**
* Override of EventEmitter's emit method to prevent synchronous event emission.
*
* Temporal workflows must be deterministic, so synchronous event emission is not allowed.
* This method throws an error to remind developers to use emitAsync instead.
*
* @param event - The event to emit.
* @param args - Arguments to pass to the event listeners.
* @throws {Error} Always throws an error directing the developer to use emitAsync.
*/
emit<T extends string | symbol>(event: T, ...args: any[]): boolean;
/**
* Emit events asynchronously to the necessary listeners.
*
* This method provides a deterministic way to emit events in Temporal workflows.
* It ensures that all event listeners are called in sequence and any errors are
* properly handled without affecting other listeners.
*
* @param event - The event to be emitted.
* @param args - Arguments to pass to the listeners.
* @returns {Promise<boolean>} True if there were listeners for the event, false otherwise.
*/
protected emitAsync(event: string, ...args: any[]): Promise<boolean>;
/**
* Cleans up event listeners and resets binding flags to prevent memory leaks in the shared V8 VM.
*
* This method should be called before a workflow completes or during continueAsNew
* to ensure that event listeners don't persist between workflow executions in the
* shared VM environment.
*
* ## Cleanup actions:
* - Removes all event listeners from the workflow instance
* - Resets all binding flags (_eventsBound, _hooksBound, _signalsBound, etc.)
*
* @returns {void}
*/
protected cleanup(): void;
/**
* Forward a signal to all child workflows.
*
* This method sends the specified signal to all child workflows managed by this workflow.
* It's commonly used to propagate pause, resume, and cancel signals to ensure consistent
* state across the workflow hierarchy.
*
* @param signalName - The name of the signal to be forwarded.
* @param args - Additional arguments to pass with the signal.
* @returns {Promise<void>} Resolves when all signals have been sent.
*/
protected forwardSignalToChildren(signalName: string, ...args: unknown[]): Promise<void>;
/**
* Bind event handlers based on metadata.
*
* This method uses reflection to find all methods decorated with @On and binds them
* as event handlers. It ensures that event handlers are only bound once per workflow instance.
*
* @returns {Promise<void>} Resolves when all event handlers are bound.
*/
protected bindEventHandlers(): Promise<void>;
/**
* Bind lifecycle hooks to workflow methods.
*
* This method finds all methods with @Before and @After decorators and wraps the
* target methods to execute the hooks at the appropriate times. It ensures hooks
* are only bound once per workflow instance.
*
* @returns {Promise<void>} Resolves when all hooks are bound.
*/
private bindHooks;
/**
* Create a method wrapped with before and after hooks.
*
* This helper method creates a new function that wraps the original method with
* the specified before and after hooks. The hooks are executed in the order they
* were defined.
*
* @param methodName - The name of the method being wrapped.
* @param originalMethod - The original method implementation.
* @param hookConfig - Configuration specifying before and after hooks.
* @returns {Function} A new function that executes the hooks and the original method.
*/
private createHookedMethod;
/**
* Bind property handlers based on metadata.
*
* This method finds all properties decorated with @Property and sets up the appropriate
* query and signal handlers for them. It allows properties to be accessed via queries
* and modified via signals.
*
* @returns {Promise<void>} Resolves when all property handlers are bound.
*/
protected bindProperties(): Promise<void>;
/**
* Bind query handlers for the workflow.
*
* This method finds all methods decorated with @Query and registers them as
* Temporal query handlers. It allows external systems to query the workflow state
* without modifying it.
*
* @returns {Promise<void>} Resolves when all query handlers are bound.
*/
protected bindQueries(): void;
/**
* Bind signal handlers for the workflow.
*
* This method finds all methods decorated with @Signal and registers them as
* Temporal signal handlers. It allows external systems to trigger actions within
* the workflow.
*
* @returns {Promise<void>} Resolves when all signal handlers are bound.
*/
protected bindSignals(): void;
/**
* Bind step handlers for the workflow.
*
* This method finds all methods decorated with @Step and registers them as
* workflow step handlers. These steps can then be used by the DSL interpreter.
*/
protected bindSteps(): void;
protected initDSL(): void;
/**
* Collect custom metadata for decorators.
*
* This method traverses the prototype chain to collect all metadata for a specific
* metadata key. It ensures that metadata from parent classes is properly inherited
* and that duplicate metadata is filtered out.
*
* @param metadataKey - The metadata key symbol to collect.
* @param target - The target object to collect metadata from.
* @returns {any[]} The collected metadata as an array.
*/
protected collectMetadata(metadataKey: Symbol, target: any): any[];
/**
* Collect hook metadata for processing lifecycle hooks.
*
* This method traverses the prototype chain to collect all hook metadata.
* It ensures that hooks from parent classes are properly inherited and merged
* with hooks from child classes.
*
* @param target - The target prototype to collect hook metadata from.
* @returns {Object} An object mapping method names to their before and after hooks.
*/
private collectHookMetadata;
}