Saga Playground
Design and visualize your sagas in real-time using actual TypeScript DSL code. Edit the saga definition on the left and watch the flow diagram update instantly.
Interactive Saga Designer
Loading editor...
Pattern:
.when(state => state.status === 'from') → status: 'to' in handlerHow It Works​
The playground parses your TypeScript saga definition and extracts:
- States from your
statustype in the interface - Transitions from
.on()handlers with.when()guards
Requirements for Visualization​
For the parser to detect transitions, your handlers should follow this pattern:
.on('EventName')
.when(state => state.status === 'fromState') // Guard defines "from"
.handle(async (msg, state) => ({
...state,
status: 'toState', // Assignment defines "to"
}))
Examples​
Payment Saga​
interface PaymentState {
paymentId: string;
status: 'pending' | 'validating' | 'charging' | 'captured' | 'declined' | 'refunded';
}
const paymentSaga = createSagaMachine<PaymentState, PaymentMessages>()
.name('PaymentSaga')
.correlate('PaymentRequested', msg => msg.paymentId, { canStart: true })
.initial<PaymentRequested>(msg => ({
paymentId: msg.paymentId,
status: 'pending',
}))
.on('CardValidated')
.when(state => state.status === 'pending')
.handle(async (msg, state) => ({
...state,
status: 'validating',
}))
.on('ChargeInitiated')
.when(state => state.status === 'validating')
.handle(async (msg, state) => ({
...state,
status: 'charging',
}))
.on('ChargeSucceeded')
.when(state => state.status === 'charging')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'captured' };
})
.on('ChargeFailed')
.when(state => state.status === 'charging')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'declined' };
})
.build();
Loan Application​
interface LoanState {
applicationId: string;
status: 'submitted' | 'identity_check' | 'credit_check' | 'verified' | 'underwriting' | 'approved' | 'rejected' | 'funded';
}
const loanSaga = createSagaMachine<LoanState, LoanMessages>()
.name('LoanSaga')
.correlate('ApplicationSubmitted', msg => msg.applicationId, { canStart: true })
.initial<ApplicationSubmitted>(msg => ({
applicationId: msg.applicationId,
status: 'submitted',
}))
.on('IdentityVerified')
.when(state => state.status === 'submitted')
.handle(async (msg, state) => ({
...state,
status: 'identity_check',
}))
.on('CreditChecked')
.when(state => state.status === 'identity_check')
.handle(async (msg, state) => ({
...state,
status: 'credit_check',
}))
.on('AllChecksComplete')
.when(state => state.status === 'credit_check')
.handle(async (msg, state) => ({
...state,
status: 'verified',
}))
.on('UnderwritingStarted')
.when(state => state.status === 'verified')
.handle(async (msg, state) => ({
...state,
status: 'underwriting',
}))
.on('LoanApproved')
.when(state => state.status === 'underwriting')
.handle(async (msg, state) => ({
...state,
status: 'approved',
}))
.on('LoanRejected')
.when(state => state.status === 'underwriting')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'rejected' };
})
.on('FundsTransferred')
.when(state => state.status === 'approved')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'funded' };
})
.build();
Tips​
- Define all status values in your interface's union type
- Use
.when()guards to specify the source state - Set
statusin your handler return to specify the target state - Drag nodes to rearrange after the diagram generates
- Event names containing "fail", "error", "cancel" get red edges
- Event names containing "success", "complete", "confirm" get green edges
Limitations​
The parser uses regex and works best with standard patterns. Complex expressions in .when() guards or computed status values may not be detected.
See Also​
- DSL Reference - Full API documentation
- Order Saga - Complete implementation example
- Common Patterns - Reusable saga patterns