Skip to content

Workflow

@granit/workflow provides framework-agnostic types and API functions for a finite state machine — mirroring Granit.Workflow on the .NET backend. It models entity lifecycle through named states, guarded transitions, and optional approval routing.

@granit/react-workflow wraps these into a provider and hooks that manage status fetching, transition execution, and paginated audit history.

Peer dependencies: axios, @granit/logger, react ^19

  • Directory@granit/workflow/ FSM types, transition/history API functions (framework-agnostic)
    • @granit/react-workflow WorkflowProvider, status/transition/history hooks
PackageRoleDepends on
@granit/workflowDTOs, transition outcome enum, API functionsaxios
@granit/react-workflowWorkflowProvider, useWorkflowStatus, useWorkflowTransition, useWorkflowHistory@granit/workflow, @granit/logger, react
import { WorkflowProvider } from '@granit/react-workflow';
import { api } from './api-client';
function App({ children }) {
return (
<WorkflowProvider apiClient={api} basePath="/api/v1/workflow">
{children}
</WorkflowProvider>
);
}
const TransitionOutcome = {
Completed: 'Completed',
ApprovalRequested: 'ApprovalRequested',
Denied: 'Denied',
InvalidTransition: 'InvalidTransition',
} as const;
type TransitionOutcomeValue = (typeof TransitionOutcome)[keyof typeof TransitionOutcome];
const WorkflowLifecycleStatus = {
Draft: 0,
PendingReview: 1,
Published: 2,
Archived: 3,
} as const;
type WorkflowLifecycleStatusValue =
(typeof WorkflowLifecycleStatus)[keyof typeof WorkflowLifecycleStatus];
interface TransitionDto {
readonly targetState: string;
readonly name: string;
readonly allowed: boolean;
readonly requiresApproval: boolean;
}
interface TransitionRequestDto {
readonly targetState: string;
readonly comment?: string;
}
interface TransitionResultDto {
readonly succeeded: boolean;
readonly resultingState: string;
readonly outcome: TransitionOutcomeValue;
}
interface TransitionHistoryDto {
readonly previousState: string;
readonly newState: string;
readonly transitionedAt: string;
readonly transitionedBy: string;
readonly comment: string | null;
}
interface WorkflowStatusDto {
readonly currentState: string;
readonly availableTransitions: TransitionDto[];
}
interface WorkflowHistoryPage {
items: TransitionHistoryDto[];
totalCount: number;
nextCursor: string | null;
}
FunctionEndpointDescription
fetchStatus(client, basePath, entityType, entityId)GET /{entityType}/{entityId}/transitionsCurrent state and available transitions
executeTransition(client, basePath, entityType, entityId, request)POST /{entityType}/{entityId}/transitionTrigger a transition
fetchHistory(client, basePath, entityType, entityId, params?)GET /{entityType}/{entityId}/historyPaginated transition history
interface WorkflowProviderProps {
apiClient: AxiosInstance;
basePath?: string; // default: '/api/v1/workflow'
children: React.ReactNode;
}
<WorkflowProvider apiClient={api} basePath="/api/v1/workflow">
{children}
</WorkflowProvider>

Fetches current state and available transitions for an entity.

interface UseWorkflowStatusReturn {
readonly currentState: string | null;
readonly transitions: TransitionDto[];
readonly loading: boolean;
readonly error: Error | null;
readonly refetch: () => Promise<void>;
}

Executes a state transition with optional audit comment.

interface UseWorkflowTransitionOptions {
entityType: string;
entityId: string;
onSuccess?: (result: TransitionResultDto) => void;
onError?: (error: Error) => void;
}
interface UseWorkflowTransitionReturn {
readonly transition: (targetState: string, comment?: string) => Promise<TransitionResultDto | null>;
readonly loading: boolean;
readonly result: TransitionResultDto | null;
readonly error: Error | null;
}

Fetches paginated transition audit trail for an entity.

interface UseWorkflowHistoryOptions {
entityType: string;
entityId: string;
enabled?: boolean; // default: true
}
interface UseWorkflowHistoryReturn {
readonly history: TransitionHistoryDto[];
readonly loading: boolean;
readonly error: Error | null;
readonly refetch: () => Promise<void>;
}
graph LR
    Fetch["Fetch status"] --> Check["Check transitions"]
    Check --> Execute["Execute transition"]
    Execute --> Result{"Outcome?"}
    Result -->|Completed| Refetch["Refetch status"]
    Result -->|ApprovalRequested| Pending["Pending approval"]
    Result -->|Denied| Error["Show error"]

When a transition has requiresApproval: true, calling executeTransition returns outcome: 'ApprovalRequested' and the entity enters a pending state. An approver must then complete or deny the transition via the same API.

CategoryKey exportsPackage
EnumsTransitionOutcome, WorkflowLifecycleStatus@granit/workflow
TypesWorkflowStatusDto, TransitionDto, TransitionResultDto, TransitionHistoryDto@granit/workflow
API functionsfetchStatus(), executeTransition(), fetchHistory()@granit/workflow
ProviderWorkflowProvider, useWorkflowConfig()@granit/react-workflow
HooksuseWorkflowStatus(), useWorkflowTransition(), useWorkflowHistory()@granit/react-workflow
  • Granit.Workflow module — .NET FSM engine with publication lifecycle
  • Timeline — Audit entries can reference workflow transitions
  • Templating — Templates use WorkflowLifecycleStatus for publication lifecycle