autotel
Version:
Write Once, Observe Anywhere
346 lines (343 loc) • 12.1 kB
TypeScript
import { SafeBaggageSchema } from './business-baggage.js';
import { T as TraceContext } from './trace-context-t5X1AP-e.js';
import '@opentelemetry/api';
/**
* Pre-built baggage schema for distributed workflows
*
* Use this to read/write workflow context that propagates across services.
*
* @example Setting workflow baggage
* ```typescript
* WorkflowBaggage.set(ctx, {
* workflowId: 'order-12345',
* workflowName: 'OrderFulfillment',
* stepName: 'ReserveInventory',
* stepIndex: 1,
* });
* ```
*
* @example Reading workflow baggage in downstream service
* ```typescript
* const { workflowId, workflowName, stepIndex } = WorkflowBaggage.get(ctx);
* console.log(`Processing ${workflowName} step ${stepIndex}`);
* ```
*/
declare const WorkflowBaggage: SafeBaggageSchema<{
/** Unique identifier for the workflow instance */
readonly workflowId: {
readonly type: "string";
readonly maxLength: 128;
readonly required: true;
};
/** Name/type of the workflow (e.g., "OrderFulfillment") */
readonly workflowName: {
readonly type: "string";
readonly maxLength: 64;
readonly required: true;
};
/** Version of the workflow definition */
readonly workflowVersion: {
readonly type: "string";
readonly maxLength: 32;
};
/** Current step name */
readonly stepName: {
readonly type: "string";
readonly maxLength: 64;
};
/** Current step index (0-based) */
readonly stepIndex: {
readonly type: "number";
};
/** Total number of steps (if known) */
readonly totalSteps: {
readonly type: "number";
};
/** Parent workflow ID (for sub-workflows) */
readonly parentWorkflowId: {
readonly type: "string";
readonly maxLength: 128;
};
/** Correlation ID for external systems */
readonly correlationId: {
readonly type: "string";
readonly maxLength: 128;
};
/** Workflow priority */
readonly priority: {
readonly type: "enum";
readonly values: readonly ["low", "normal", "high", "critical"];
};
/** Initiating user/system */
readonly initiatedBy: {
readonly type: "string";
readonly maxLength: 64;
};
/** Workflow start timestamp (ISO) */
readonly startedAt: {
readonly type: "string";
readonly maxLength: 30;
};
}>;
/**
* Type for workflow baggage values
*/
type WorkflowBaggageValues = {
workflowId: string;
workflowName: string;
workflowVersion?: string;
stepName?: string;
stepIndex?: number;
totalSteps?: number;
parentWorkflowId?: string;
correlationId?: string;
priority?: 'low' | 'normal' | 'high' | 'critical';
initiatedBy?: string;
startedAt?: string;
};
/**
* Configuration for distributed workflow tracing
*/
interface DistributedWorkflowConfig {
/** Workflow name/type (e.g., "OrderFulfillment", "UserOnboarding") */
name: string;
/**
* Extract workflow ID from function arguments
*
* Receives all arguments passed to the workflow function, allowing
* multi-parameter handlers to derive workflow IDs from any argument.
*
* @example Single argument
* ```typescript
* workflowIdFrom: (order) => order.id
* ```
*
* @example Multiple arguments (payload + metadata)
* ```typescript
* workflowIdFrom: (payload, metadata) => metadata.correlationId ?? payload.id
* ```
*/
workflowIdFrom: (...args: unknown[]) => string;
/** Workflow version (e.g., "1.0.0", "2023-01-15") */
version?: string;
/** Total number of steps if known */
totalSteps?: number;
/** Parent workflow ID (for sub-workflows) */
parentWorkflowId?: string;
/** Correlation ID for external systems */
correlationId?: string;
/** Workflow priority */
priority?: 'low' | 'normal' | 'high' | 'critical';
/** User/system that initiated the workflow */
initiatedBy?: string;
/** Additional span attributes */
attributes?: Record<string, string | number | boolean>;
/** Callback on workflow start */
onStart?: (ctx: DistributedWorkflowContext) => void;
/** Callback on workflow completion */
onComplete?: (ctx: DistributedWorkflowContext, result: unknown) => void;
/** Callback on workflow error */
onError?: (ctx: DistributedWorkflowContext, error: Error) => void;
}
/**
* Configuration for distributed workflow step
*/
interface DistributedStepConfig {
/** Step name (e.g., "ReserveInventory", "ChargePayment") */
name: string;
/**
* Extract baggage from incoming message/request
*
* If true, reads workflow baggage from current context (assumes already extracted).
* If function, extracts from arguments.
*
* @default true
*/
extractBaggage?: boolean | ((args: unknown[]) => WorkflowBaggageValues | null);
/** Override step index (otherwise uses baggage or auto-increments) */
stepIndex?: number;
/** Additional span attributes */
attributes?: Record<string, string | number | boolean>;
/** Whether this step is idempotent (safe to retry) */
idempotent?: boolean;
/** Whether this step is a compensation/rollback step */
isCompensation?: boolean;
/** Callback on step start */
onStart?: (ctx: DistributedStepContext) => void;
/** Callback on step completion */
onComplete?: (ctx: DistributedStepContext, result: unknown) => void;
/** Callback on step error */
onError?: (ctx: DistributedStepContext, error: Error) => void;
}
/**
* Extended context for distributed workflow root
*/
interface DistributedWorkflowContext extends TraceContext {
/** The workflow ID */
workflowId: string;
/** The workflow name */
workflowName: string;
/** The workflow version */
workflowVersion?: string;
/** Get workflow baggage for propagation to other services */
getWorkflowBaggage(): WorkflowBaggageValues;
/** Set additional workflow baggage fields */
setWorkflowBaggage(values: Partial<WorkflowBaggageValues>): void;
/** Get headers with workflow baggage for outgoing requests */
getWorkflowHeaders(): Record<string, string>;
/** Record workflow step completion (for progress tracking) */
recordStepProgress(stepName: string, stepIndex: number): void;
}
/**
* Extended context for distributed workflow step
*/
interface DistributedStepContext extends TraceContext {
/** The workflow ID (from baggage) */
workflowId: string | null;
/** The workflow name (from baggage) */
workflowName: string | null;
/** The current step name */
stepName: string;
/** The current step index */
stepIndex: number | null;
/** Whether this step is a compensation */
isCompensation: boolean;
/** Get the full workflow baggage */
getWorkflowBaggage(): WorkflowBaggageValues | null;
/** Update workflow baggage (e.g., increment step index) */
updateWorkflowBaggage(values: Partial<WorkflowBaggageValues>): void;
/** Get headers with updated workflow baggage for downstream calls */
getWorkflowHeaders(): Record<string, string>;
/** Mark step as requiring compensation on failure */
requiresCompensation(compensationData?: Record<string, unknown>): void;
}
/**
* Create a traced distributed workflow function
*
* Wraps a function as the entry point for a distributed workflow. Automatically:
* - Generates or extracts workflow ID
* - Sets workflow baggage for downstream propagation
* - Creates root span with workflow attributes
*
* @param config - Workflow configuration
* @returns Factory function for the workflow handler
*
* @example Basic usage
* ```typescript
* export const createOrder = traceDistributedWorkflow({
* name: 'OrderFulfillment',
* workflowIdFrom: (order) => order.id,
* version: '1.0.0',
* })(ctx => async (order: Order) => {
* ctx.recordStepProgress('ValidateOrder', 0);
* await validateOrder(order);
*
* ctx.recordStepProgress('ReserveInventory', 1);
* await publishToInventoryService(order);
*
* return { workflowId: ctx.workflowId, status: 'started' };
* });
* ```
*/
declare function traceDistributedWorkflow<TArgs extends unknown[], TReturn>(config: DistributedWorkflowConfig): (fnFactory: (ctx: DistributedWorkflowContext) => (...args: TArgs) => Promise<TReturn>) => ((...args: TArgs) => Promise<TReturn>);
/**
* Create a traced distributed workflow step
*
* Use in downstream services to trace steps that are part of a distributed workflow.
* Automatically extracts workflow baggage from the current context.
*
* @param config - Step configuration
* @returns Factory function for the step handler
*
* @example Consumer in downstream service
* ```typescript
* export const processInventory = traceConsumer({
* system: 'kafka',
* destination: 'inventory-requests',
* extractBaggage: true, // Extracts workflow.* from headers
* })(ctx => {
* // Wrap inner logic with traceDistributedStep
* return traceDistributedStep({
* name: 'ReserveInventory',
* })(stepCtx => async (message) => {
* console.log(`Processing workflow ${stepCtx.workflowId}`);
* await reserveItems(message.items);
* })(message);
* });
* ```
*
* @example Standalone step handler
* ```typescript
* export const reserveInventory = traceDistributedStep({
* name: 'ReserveInventory',
* idempotent: true,
* })(ctx => async (request: InventoryRequest) => {
* if (ctx.workflowId) {
* console.log(`Part of workflow ${ctx.workflowId}, step ${ctx.stepIndex}`);
* }
* return await inventoryService.reserve(request.items);
* });
* ```
*/
declare function traceDistributedStep<TArgs extends unknown[], TReturn>(config: DistributedStepConfig): (fnFactory: (ctx: DistributedStepContext) => (...args: TArgs) => Promise<TReturn>) => ((...args: TArgs) => Promise<TReturn>);
/**
* Generate a unique workflow ID
*
* @param prefix - Optional prefix for the ID
* @returns A unique workflow ID
*
* @example
* ```typescript
* const workflowId = generateWorkflowId('order'); // "order-abc123def456"
* ```
*/
declare function generateWorkflowId(prefix?: string): string;
/**
* Check if the current context is part of a distributed workflow
*
* @param ctx - The trace context
* @returns True if workflow baggage is present
*/
declare function isInDistributedWorkflow(ctx: TraceContext): boolean;
/**
* Get workflow progress information
*
* @param ctx - The trace context
* @returns Progress info or null if not in a workflow
*/
declare function getWorkflowProgress(ctx: TraceContext): {
workflowId: string;
workflowName: string;
currentStep: string | null;
currentStepIndex: number | null;
totalSteps: number | null;
percentComplete: number | null;
} | null;
/**
* Create workflow correlation headers for manual propagation
*
* Use when you need to manually add workflow context to outgoing requests.
*
* @param values - Workflow baggage values
* @returns Headers object with workflow baggage
*
* @example
* ```typescript
* const headers = createWorkflowHeaders({
* workflowId: 'order-123',
* workflowName: 'OrderFulfillment',
* stepIndex: 2,
* });
*
* await fetch('/api/inventory', { headers });
* ```
*/
declare function createWorkflowHeaders(values: Partial<WorkflowBaggageValues>): Record<string, string>;
/**
* Parse workflow context from baggage header
*
* @param baggageHeader - The baggage header value
* @returns Parsed workflow values or null
*/
declare function parseWorkflowFromBaggage(baggageHeader: string): Partial<WorkflowBaggageValues> | null;
export { type DistributedStepConfig, type DistributedStepContext, type DistributedWorkflowConfig, type DistributedWorkflowContext, WorkflowBaggage, type WorkflowBaggageValues, createWorkflowHeaders, generateWorkflowId, getWorkflowProgress, isInDistributedWorkflow, parseWorkflowFromBaggage, traceDistributedStep, traceDistributedWorkflow };