UNPKG

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

446 lines (445 loc) 19.1 kB
import 'reflect-metadata'; import * as workflow from '@temporalio/workflow'; import EventEmitter from 'eventemitter3'; import { Duration } from '@temporalio/common'; import { LRUCacheWithDelete } from 'mnemonist'; /** * 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; /** * The task queue for the workflow. * Specifies which task queue the workflow should be registered with. * Workers listening on this task queue will be able to execute this workflow. */ taskQueue?: string; /** * 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; /** * 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 */ 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: { [key: string]: (args: any[]) => Promise<void>; }; /** * 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 continueAsNew: 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 shouldContinueAsNew: 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; /** * 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; /** * Abstract method to execute the workflow logic. * * This method must be implemented by concrete workflow classes to define the * actual business logic of the workflow. It will be called repeatedly during * workflow execution until a terminal state is reached or the workflow continues as new. * * @param args - Arguments required for execution. * @returns {Promise<unknown>} Result of the workflow execution. */ protected abstract execute(...args: unknown[]): Promise<unknown>; /** * 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>>; /** * 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; /** * Get all registered step handlers. * This method is used by the DSL interpreter to access available steps. */ protected getStepHandlers(): Record<string, (...args: any[]) => Promise<any>>; /** * 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; }