UNPKG

@temporalio/workflow

Version:
329 lines (328 loc) 15 kB
import type { RawSourceMap } from 'source-map'; import { FailureConverter, PayloadConverter, HandlerUnfinishedPolicy, Workflow, WorkflowQueryAnnotatedType, WorkflowSignalAnnotatedType, WorkflowUpdateAnnotatedType, ProtoFailure, WorkflowUpdateType, WorkflowUpdateValidatorType, WorkflowFunctionWithOptions, VersioningBehavior, WorkflowDefinitionOptions } from '@temporalio/common'; import type { coresdk } from '@temporalio/proto'; import { RNG } from './alea'; import { RootCancellationScope } from './cancellation-scope'; import { QueryInput, SignalInput, StartNexusOperationOutput, UpdateInput, WorkflowExecuteInput, WorkflowInterceptors } from './interceptors'; import { DefaultSignalHandler, StackTraceFileLocation, WorkflowInfo, WorkflowCreateOptionsInternal, ActivationCompletion, DefaultUpdateHandler, DefaultQueryHandler } from './interfaces'; import { type SinkCall } from './sinks'; import { SdkFlag } from './flags'; export interface Stack { formatted: string; structured: StackTraceFileLocation[]; } /** * Global store to track promise stacks for stack trace query */ export interface PromiseStackStore { childToParent: Map<Promise<unknown>, Set<Promise<unknown>>>; promiseToStack: Map<Promise<unknown>, Stack>; } export interface Completion<Success> { resolve(val: Success): void; reject(reason: Error): void; } export interface Condition { fn(): boolean; resolve(): void; } export type ActivationHandlerFunction<K extends keyof coresdk.workflow_activation.IWorkflowActivationJob> = (activation: NonNullable<coresdk.workflow_activation.IWorkflowActivationJob[K]>) => void; /** * Verifies all activation job handling methods are implemented */ export type ActivationHandler = { [P in keyof coresdk.workflow_activation.IWorkflowActivationJob]: ActivationHandlerFunction<P>; }; /** * Information about an update or signal handler execution. */ interface MessageHandlerExecution { name: string; unfinishedPolicy: HandlerUnfinishedPolicy; id?: string; } /** * Keeps all of the Workflow runtime state like pending completions for activities and timers. * * Implements handlers for all workflow activation jobs. * * Note that most methods in this class are meant to be called only from within the VM. * * However, a few methods may be called directly from outside the VM (essentially from `vm-shared.ts`). * These methods are specifically marked with a comment and require careful consideration, as the * execution context may not properly reflect that of the target workflow execution (e.g.: with Reusable * VMs, the `global` may not have been swapped to those of that workflow execution; the active microtask * queue may be that of the thread/process, rather than the queue of that VM context; etc). Consequently, * methods that are meant to be called from outside of the VM must not do any of the following: * * - Access any global variable; * - Create Promise objects, use async/await, or otherwise schedule microtasks; * - Call user-defined functions, including any form of interceptor. */ export declare class Activator implements ActivationHandler { /** * Cache for modules - referenced in reusable-vm.ts */ readonly moduleCache: Map<string, unknown>; /** * Map of task sequence to a Completion */ readonly completions: { timer: Map<number, Completion<void>>; activity: Map<number, Completion<unknown>>; nexusOperationStart: Map<number, Completion<StartNexusOperationOutput>>; nexusOperationComplete: Map<number, Completion<unknown>>; childWorkflowStart: Map<number, Completion<string>>; childWorkflowComplete: Map<number, Completion<unknown>>; signalWorkflow: Map<number, Completion<void>>; cancelWorkflow: Map<number, Completion<void>>; }; /** * Holds buffered Update calls until a handler is registered */ readonly bufferedUpdates: coresdk.workflow_activation.IDoUpdate[]; /** * Holds buffered signal calls until a handler is registered */ readonly bufferedSignals: coresdk.workflow_activation.ISignalWorkflow[]; /** * Mapping of update name to handler and validator */ readonly updateHandlers: Map<string, WorkflowUpdateAnnotatedType>; /** * Mapping of signal name to handler */ readonly signalHandlers: Map<string, WorkflowSignalAnnotatedType>; /** * Mapping of in-progress updates to handler execution information. */ readonly inProgressUpdates: Map<string, MessageHandlerExecution>; /** * Mapping of in-progress signals to handler execution information. */ readonly inProgressSignals: Map<number, MessageHandlerExecution>; /** * A sequence number providing unique identifiers for signal handler executions. */ protected signalHandlerExecutionSeq: number; /** * A signal handler that catches calls for non-registered signal names. */ defaultSignalHandler?: DefaultSignalHandler; /** * A update handler that catches calls for non-registered update names. */ defaultUpdateHandler?: DefaultUpdateHandler; /** * A query handler that catches calls for non-registered query names. */ defaultQueryHandler?: DefaultQueryHandler; /** * Source map file for looking up the source files in response to __enhanced_stack_trace */ protected readonly sourceMap: RawSourceMap; /** * Whether or not to send the sources in enhanced stack trace query responses */ protected readonly showStackTraceSources: boolean; readonly promiseStackStore: PromiseStackStore; /** * The error that caused the current Workflow Task to fail. Sets if a non-`TemporalFailure` * error bubbles up out of the Workflow function, or out of a Signal or Update handler. We * capture errors this way because those functions are not technically awaited when started, * but left to run asynchronously. There is therefore no real "parent" function that can * directly handle those errors, and not capturing it would result in an Unhandled Promise * Rejection. So instead, we buffer the error here, to then be processed in the context * of our own synchronous Activation handling event loop. * * Our code does a best effort to stop processing the current activation as soon as possible * after this field is set: * - If an error is thrown while executing code synchronously (e.g. anything before the * first `await` statement in a Workflow function or a signal/update handler), the error * will be _immediately_ rethrown, which will prevent execution of further jobs in the * current activation. We know we're currently running code synchronously thanks to the * `rethrowSynchronously` flag below. * - It an error is thrown while executing microtasks, then the error will be rethrown on * the next call to `tryUnblockConditions()`. * * Unfortunately, there's no way for us to prevent further execution of microtasks that have * already been scheduled, nor those that will be recursively scheduled from those microtasks. * Should more errors get thrown while settling microtasks, those will be ignored (i.e. only * the first captured error is preserved). */ workflowTaskError: unknown; /** * Set to true when running synchronous code (e.g. while processing activation jobs and when calling * `tryUnblockConditions()`). While this flag is set, it is safe to let errors bubble up. */ rethrowSynchronously: boolean; readonly rootScope: RootCancellationScope; /** * Mapping of query name to handler */ readonly queryHandlers: Map<string, WorkflowQueryAnnotatedType>; /** * Loaded in {@link initRuntime} */ readonly interceptors: Required<WorkflowInterceptors>; /** * Buffer that stores all generated commands, reset after each activation */ protected commands: coresdk.workflow_commands.IWorkflowCommand[]; /** * Stores all {@link condition}s that haven't been unblocked yet */ readonly blockedConditions: Map<number, Condition>; /** * Is this Workflow completed? * * A Workflow will be considered completed if it generates a command that the * system considers as a final Workflow command (e.g. * completeWorkflowExecution or failWorkflowExecution). */ completed: boolean; /** * Was this Workflow cancelled? */ protected cancelled: boolean; /** * The next (incremental) sequence to assign when generating completable commands */ nextSeqs: { timer: number; activity: number; childWorkflow: number; signalWorkflow: number; cancelWorkflow: number; condition: number; nexusOperation: number; stack: number; }; /** * This is set every time the workflow executes an activation * May be accessed and modified from outside the VM. */ now: number; /** * Reference to the current Workflow, initialized when a Workflow is started */ workflow?: Workflow | WorkflowFunctionWithOptions<any[], any>; /** * Information about the current Workflow * May be accessed from outside the VM. */ info: WorkflowInfo; /** * A deterministic RNG, used by the isolate's overridden Math.random */ random: RNG; payloadConverter: PayloadConverter; failureConverter: FailureConverter; /** * Patches we know the status of for this workflow, as in {@link patched} */ private readonly knownPresentPatches; /** * Patches we sent to core {@link patched} */ private readonly sentPatches; private readonly knownFlags; /** * Buffered sink calls per activation */ sinkCalls: SinkCall[]; /** * A nanosecond resolution time function, externally injected. This is used to * precisely sort logs entries emitted from the Workflow Context vs those emitted * from other sources (e.g. main thread, Core, etc). */ readonly getTimeOfDay: () => bigint; readonly registeredActivityNames: Set<string>; currentDetails: string; versioningBehavior?: VersioningBehavior; workflowDefinitionOptionsGetter?: () => WorkflowDefinitionOptions; constructor({ info, now, showStackTraceSources, sourceMap, getTimeOfDay, randomnessSeed, registeredActivityNames, }: WorkflowCreateOptionsInternal); /** * May be invoked from outside the VM. */ mutateWorkflowInfo(fn: (info: WorkflowInfo) => WorkflowInfo): void; protected getStackTraces(): Stack[]; /** * May be invoked from outside the VM. */ getAndResetSinkCalls(): SinkCall[]; /** * Buffer a Workflow command to be collected at the end of the current activation. * * Prevents commands from being added after Workflow completion. */ pushCommand(cmd: coresdk.workflow_commands.IWorkflowCommand, complete?: boolean): void; concludeActivation(): ActivationCompletion; startWorkflowNextHandler({ args }: WorkflowExecuteInput): Promise<any>; startWorkflow(activation: coresdk.workflow_activation.IInitializeWorkflow): void; initializeWorkflow(activation: coresdk.workflow_activation.IInitializeWorkflow): void; cancelWorkflow(_activation: coresdk.workflow_activation.ICancelWorkflow): void; fireTimer(activation: coresdk.workflow_activation.IFireTimer): void; resolveActivity(activation: coresdk.workflow_activation.IResolveActivity): void; resolveChildWorkflowExecutionStart(activation: coresdk.workflow_activation.IResolveChildWorkflowExecutionStart): void; resolveChildWorkflowExecution(activation: coresdk.workflow_activation.IResolveChildWorkflowExecution): void; resolveNexusOperationStart(activation: coresdk.workflow_activation.IResolveNexusOperationStart): void; resolveNexusOperation(activation: coresdk.workflow_activation.IResolveNexusOperation): void; protected queryWorkflowNextHandler({ queryName, args }: QueryInput): Promise<unknown>; queryWorkflow(activation: coresdk.workflow_activation.IQueryWorkflow): void; doUpdate(activation: coresdk.workflow_activation.IDoUpdate): void; protected updateNextHandler(handler: WorkflowUpdateType, { args }: UpdateInput): Promise<unknown>; protected validateUpdateNextHandler(validator: WorkflowUpdateValidatorType | undefined, { args }: UpdateInput): void; dispatchBufferedUpdates(): void; rejectBufferedUpdates(): void; signalWorkflowNextHandler({ signalName, args }: SignalInput): Promise<void>; signalWorkflow(activation: coresdk.workflow_activation.ISignalWorkflow): void; dispatchBufferedSignals(): void; resolveSignalExternalWorkflow(activation: coresdk.workflow_activation.IResolveSignalExternalWorkflow): void; resolveRequestCancelExternalWorkflow(activation: coresdk.workflow_activation.IResolveRequestCancelExternalWorkflow): void; warnIfUnfinishedHandlers(): void; updateRandomSeed(activation: coresdk.workflow_activation.IUpdateRandomSeed): void; notifyHasPatch(activation: coresdk.workflow_activation.INotifyHasPatch): void; patchInternal(patchId: string, deprecated: boolean): boolean; /** * Called early while handling an activation to register known flags. * May be invoked from outside the VM. */ addKnownFlags(flags: number[]): void; /** * Check if an SDK Flag may be considered as enabled for the current Workflow Task. * * SDK flags play a role similar to the `patched()` API, but are meant for internal usage by the * SDK itself. They make it possible for the SDK to evolve its behaviors over time, while still * maintaining compatibility with Workflow histories produced by older SDKs, without causing * determinism violations. * * May be invoked from outside the VM. */ hasFlag(flag: SdkFlag): boolean; removeFromCache(): void; /** * Transforms failures into a command to be sent to the server. * Used to handle any failure emitted by the Workflow. */ handleWorkflowFailure(error: unknown): void; recordWorkflowTaskError(error: unknown): void; /** * If a Workflow Task error was captured, and we are running in synchronous mode, * then bubble it up now. This is safe to call even if there is no error to rethrow. */ maybeRethrowWorkflowTaskError(): void; private completeQuery; private failQuery; private acceptUpdate; private completeUpdate; private rejectUpdate; /** Consume a completion if it exists in Workflow state */ private maybeConsumeCompletion; /** Consume a completion if it exists in Workflow state, throws if it doesn't */ private consumeCompletion; private completeWorkflow; errorToFailure(err: unknown): ProtoFailure; failureToError(failure: ProtoFailure): Error; } export {};