Skip to content

Instantly share code, notes, and snippets.

@pyoner
Last active October 16, 2021 12:30
Show Gist options
  • Save pyoner/da38cab7da0a52f6c3b3386564217828 to your computer and use it in GitHub Desktop.
Save pyoner/da38cab7da0a52f6c3b3386564217828 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
/*
interface Event {
type: string
executor: 'client' | 'provider' | 'admin' | 'validator'
sender: 'admin' | 'client' | 'provider'
}
**/
const guards = {
// executors
isAdmin: (_, {executor}) => executor === 'admin',
isValidator: (_, {executor}) => executor === 'validator',
isClient: (_, {executor}) => executor === 'client',
isProvider: (_, {executor}) => executor === 'provider',
// sender / validator roles
asAdmin: (_, {sender}) => sender === 'admin',
asClient: (_, {sender}) => sender === 'client',
asProvider: (_, {sender}) => sender === 'provider',
canCloseJob: (context, event, meta) => !meta.state.matches('fundsInEscrow'),
canStartDispute: (context, event, meta) => !meta.state.matches('dispute'),
canAcceptOffer: ({offer}, {sender}) => offer.from !== sender,
canMakeCounterOffer: ({offer}, {sender}) => offer.from !== sender,
}
const proxyGuards = new Proxy(guards, {
get (target, prop) {
if (prop in target) {
return target[prop]
}
return (ctx, event, meta) => meta.cond.guards.every(key => target[key](ctx, event, meta))
}
})
const every = (...fn_names) => {
const name = fn_names.join(', ')
return {
type: name,
guards: fn_names
}
}
const asyncState = (src, onDone, onError = 'err') => {
const err = onError === 'err' ? {
on: {
RETRY: 'await'
}
} : null;
const state = {
initial: 'await',
states: {
await: {
invoke: {
src,
onDone,
onError
}
}
}
}
if (err) {
state.states.err = err
}
return state;
}
const jobMachine = Machine({
id: 'job',
initial: 'init',
states: {
init: {
meta: {
DRAFT: 'Create a job draft',
PUBLISH: 'Publish a job'
},
on: {
DRAFT: [{
target: 'asyncDraft',
cond: 'isClient'
}, {
target: 'draft',
cond: every('isValidator', 'asClient')
}],
PUBLISH: [{
target: 'asyncPublish',
cond: 'isClient'
}, {
target: 'public',
cond: every('isValidator', 'asClient')
}],
}
},
asyncDraft: asyncState('jobDraft', '#draft'),
draft: {
id: 'draft',
meta: {
CLOSE: 'Close the job',
PUBLISH: 'Publish the job'
},
on: {
PUBLISH: [{
target: 'asyncPublish',
cond: 'isClient'
}, {
target: '#public',
cond: every('isValidator', 'asClient')
}],
CLOSE: [{
target: '#job.asyncClose',
cond: 'isClient'
}, {
target: '#closed',
cond: every('isValidator', 'asClient')
}],
}
},
asyncPublish: asyncState('jobPublish', '#public'),
public: {
id: 'public',
initial: 'init',
meta: {
CLOSE: 'Close the job',
},
on: {
CLOSE: [{
target: '#job.asyncClose',
cond: every('isClient', 'canCloseJob')
}, {
target: '#closed',
cond: every('isValidator', 'asClient', 'canCloseJob')
}],
},
states: {
init: {
meta: {
BID: 'Make a bid',
},
on: {
BID: [{
target: 'asyncBid',
cond: 'isProvider'
}, {
target: 'bid',
cond: every('isValidator', 'asProvider')
}],
}
},
asyncBid: asyncState('makeBid', '#public.bid'),
bid: {
meta: {
ACCEPT: 'Accept the bid',
},
on: {
ACCEPT: [{
target: 'asyncOffer',
cond: 'isClient'
}, {
target: '#offer',
cond: every('isValidator', 'asClient')
}]
}
},
asyncOffer: asyncState('jobOffer', '#offer'),
offer: {
id: 'offer',
initial: 'init',
meta: {
ACCEPT: 'Accept the offer',
COUNTER_OFFER: 'Make a counter offer'
},
states: {
init: {
on: {
ACCEPT: [{
target: 'asyncAcceptOffer',
cond: every('isProvider', 'canAcceptOffer')
}, {
target: 'asyncAcceptOffer',
cond: every('isClient', 'canAcceptOffer')
}, {
target: '#public.offer.accepted',
cond: every('isValidator', 'asProvider', 'canAcceptOffer')
}, {
target: '#public.offer.accepted',
cond: every('isValidator', 'asClient', 'canAcceptOffer')
}],
COUNTER_OFFER: [{
target: '#public.asyncOffer',
cond: every('isClient', 'canMakeCounterOffer')
}, {
target: '#public.asyncOffer',
cond: every('isProvider', 'canMakeCounterOffer')
}, {
cond: every('isValidator', 'asProvider', 'canMakeCounterOffer')
}, {
cond: every('isValidator', 'asClient', 'canMakeCounterOffer')
}]
},
},
asyncAcceptOffer: asyncState('acceptOffer', '#offer.accepted'),
accepted: {
initial: 'init',
states: {
init: {
meta: {
MAKE_ESCROW: 'Please make escrow',
},
on: {
MAKE_ESCROW: [{
cond: 'isClient',
target: 'asyncDeclareFundsInEscrow'
}, {
cond: every('isValidator', 'asClient'),
target: 'declareFundsInEscrow'
}]
}
},
asyncDeclareFundsInEscrow: asyncState('makeEscrow', '#offer.accepted.declareFundsInEscrow'),
declareFundsInEscrow: {
initial: 'init',
states: {
init: {
meta: {
CHECK: 'Click to check the funds in the escrow',
},
on: {
CHECK: [{
target: 'checking',
cond: 'isValidator'
}, {
target: 'asyncChecking',
cond: 'isClient'
}, {
target: 'asyncChecking',
cond: 'isProvider'
}]
}
},
asyncChecking: asyncState('checkEscrow', '#offer.accepted.fundsInEscrow.init'),
checking: {
invoke: {
src: 'checkEscrow',
onDone: '#offer.accepted.fundsInEscrow',
onError: '#offer.accepted.declareFundsInEscrow.init'
}
},
}
},
fundsInEscrow: {
initial: 'init',
meta: {
DISPUTE: 'Open a dispute',
},
on: {
DISPUTE: [{
target: '.asyncDispute',
cond: every('isClient', 'canStartDispute')
}, {
target: '.asyncDispute',
cond: every('isProvider', 'canStartDispute')
}, {
target: '.dispute',
cond: every('isValidator', 'asClient', 'canStartDispute')
}, {
target: '.dispute',
cond: every('isValidator', 'asProvider', 'canStartDispute')
}]
},
states: {
init: {
meta: {
JOB_COMPLETE: 'Click to complete the job',
},
on: {
JOB_COMPLETE: [{
target: 'pendingCompletion',
cond: every('isValidator', 'asProvider')
}, {
target: 'pendingCompletion',
cond: 'isProvider'
}]
}
},
asyncDispute: asyncState('startDispute', '#offer.accepted.fundsInEscrow.dispute'),
dispute: {
meta: {
RELEASE_ESCROW: 'Would you like to release the escrow',
REFUND_ESCROW: 'Return back the funds to a client'
},
on: {
RELEASE_ESCROW: [{
target: 'escrow.released',
cond: every('isValidator', 'asAdmin')
}, {
target: 'asyncReleaseEscrow',
cond: 'isAdmin'
}],
REFUND_ESCROW: [{
target: 'escrow.refunded',
cond: every('isValidator', 'asAdmin')
}, {
target: 'asyncRefundEscrow',
cond: 'isAdmin'
}]
}
},
asyncReleaseEscrow: asyncState('releaseEscrow', '#offer.accepted.fundsInEscrow.escrow.released'),
asyncRefundEscrow: asyncState('refundEscrow', '#offer.accepted.fundsInEscrow.escrow.refunded'),
escrow: {
meta: {
REVIEW: 'Please make a review',
},
on: {
REVIEW: [{
target: 'review',
cond: every('isValidator', 'asClient')
}, {
target: 'review',
cond: every('isValidator', 'asProvider')
}, {
target: 'asyncReview',
cond: 'isClient'
}, {
target: 'asyncReview',
cond: 'isProvider'
}]
},
states: {
released: {},
refunded: {}
}
},
asyncPendingCompletion: asyncState('jobComplete', '#offer.accepted.fundsInEscrow.pendingCompletion'),
pendingCompletion: {
meta: {
RELEASE_ESCROW: 'Would you like to release the escrow',
},
on: {
RELEASE_ESCROW: [{
target: 'escrow.released',
cond: every('isValidator', 'asClient')
}, {
target: 'asyncReleaseEscrow',
cond: 'isClient'
}]
}
},
asyncReview: asyncState('jobReview', '#offer.accepted.fundsInEscrow.review'),
review: {
type: 'parallel',
entry: raise('REVIEW'),
meta: {
REVIEW: 'Review',
},
on: {
REVIEW: [{
target: '.client',
cond: 'isClient'
}, {
target: '.provider',
cond: 'isProvider'
}, {
target: '.client',
cond: every('isValidator', 'asClient')
}, {
target: '.provider',
cond: every('isValidator', 'asProvider')
}]
},
states: {
client: {},
provider: {}
}
}
}
}
}
}
}
}
}
},
asyncClose: asyncState('closeJob', '#closed'),
closed: {
id: 'closed'
}
}
}, {
guards: proxyGuards
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment