/** * Defines the complete state machine configuration for a domain entity. * Maps each status to its available actions and their metadata. * * Used as the single source of truth for what actions are valid * at each status, and what permissions are required to execute them. * * @template STATUS_ENUM_TYPE - String enum of all possible statuses * @template ACTION_ENUM_TYPE - String enum of all possible actions * @template CONTEXT_DATA_TYPE - Optional context data shape passed to the `next()` resolver. * Defaults to `never` for flows where `next()` needs no runtime data. * * @example * // Simple flat flow (billing, leave, vendor) * const billingFlowConfig: FlowConfig = { * [BillingStatusEnum.PENDING]: { * actions: { * [BillingActionsEnum.APPROVE]: { * permissions: ["BILLING_APPROVE_ORG"], * next: () => BillingStatusEnum.APPROVED, * }, * }, * }, * }; * * @example * // Flow with context data (reimbursement parent) * const config: FlowConfig = { ... } */ export type FlowConfig = { [key in STATUS_ENUM_TYPE]?: { actions: { [key in ACTION_ENUM_TYPE]?: IFlowConfigActionConfig; }; description?: string; }; }; /** * Configuration for a single action within a {@link FlowConfig}. * Defines who can execute the action and what status it transitions to. * * @template STATUS_ENUM_TYPE - String enum of statuses, used as the return type of `next()` * @template CONTEXT_DATA_TYPE - Optional context data passed to `next()` for dynamic status resolution. * Pass `never` for actions where the next status is always static. * * @example * // Static next status * const approveAction: IFlowConfigActionConfig = { * permissions: ["BILLING_APPROVE_ORG"], * next: () => BillingStatusEnum.APPROVED, * description: "Approve this billing entry", * }; * * @example * // Dynamic next status based on context * const editAction: IFlowConfigActionConfig = { * permissions: ["REIMBURSEMENT_UPDATE_SELF"], * next: (data) => areAllRowsApproved(data) ? ReimbursementStatusEnum.APPROVED : ReimbursementStatusEnum.APPROVAL_PENDING, * }; */ export interface IFlowConfigActionConfig { permissions: string[]; next: (data: CONTEXT_DATA_TYPE) => STATUS_ENUM_TYPE; description?: string; /** * Optional runtime guard. When present, the flow consumer evaluates it against the same * context data as `next()` and refuses the action when it returns false. Distinct from * `permissions`, which gate *who* may act; this gates *whether the action applies at all* * to the row in its current state. */ isApplicable?: (data: CONTEXT_DATA_TYPE) => boolean; }