Last active
August 2, 2019 00:57
-
-
Save michaelsbradleyjr/ab221d5242c9da02fc97cb24affc16e3 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* global Machine XState actions assign interpret raise send sendParent | |
| setTimeout spawn window */ | |
| // in starting, want services to initially go to checking and if not auto start then go to stopped | |
| // in starting, want failed to have a null event that checks if can fail; if it's okay to fail then go to stopped but don't send STOP event, otherwise send STOP event | |
| // in running, want services to initially go to checking and if stopped then go to stopped otherwise go to running | |
| // in runnin, when services goes to running should invoke the someserviceRunner which setups up duplex comm | |
| // maybe can use null event with guard in starting/running instead of an explicit checking state but uncertain | |
| // about interaction w/ invoke and entry, i.e. if null event condition is true then will invoke and/or entry not happen? need to experiment | |
| // need a SHUTDOWN even that puts shutdown flag in the context and '' on running should not only check allStopped but also if shutdown flag is in the context | |
| class Module { | |
| constructor ({name}) { | |
| this.name = name; | |
| } | |
| static randomInt(min, max) { | |
| max = max + 1; // so the range is inclusive of max | |
| return Math.floor(Math.random() * (max - min)) + min; | |
| } | |
| static doFail() { | |
| const fail = Module.randomInt(1, 20); | |
| if (fail === 1) return true; | |
| return false; | |
| } | |
| static timeout() { | |
| return Module.randomInt(1000, 3000); | |
| } | |
| async failMaybe(message) { | |
| return await (new Promise((resolve, reject) => { | |
| if (Module.doFail()) { | |
| setTimeout(() => { | |
| reject(new Error(`${this.name} ${message}`)); | |
| }, Module.timeout()); | |
| } else { | |
| setTimeout(resolve, Module.timeout()); | |
| } | |
| })); | |
| } | |
| restart() { | |
| return this.failMaybe(Module.messages.FAILED_TO_RESTART); | |
| } | |
| start() { | |
| return this.failMaybe(Module.messages.FAILED_TO_START); | |
| } | |
| stop() { | |
| return this.failMaybe(Module.messages.FAILED_TO_STOP); | |
| } | |
| } | |
| Module.messages = { | |
| FAILED_TO_RESTART: 'failed to restart for some reason', | |
| FAILED_TO_START: 'failed to start for some reason', | |
| FAILED_TO_STOP: 'failed to stop cleanly for some reason, but it has been forcibly stopped' | |
| }; | |
| class Blockchain extends Module { | |
| constructor () { | |
| super({name: 'blockchain'}); | |
| } | |
| } | |
| class Compiler extends Module { | |
| constructor () { | |
| super({name: 'compiler'}); | |
| } | |
| } | |
| const modules = { | |
| blockchain: new Blockchain(), | |
| compiler: new Compiler() | |
| }; | |
| const context = {}; | |
| const acts = { | |
| clearError: assign((context, event) => { | |
| const serviceName = [event.type.split('.').pop()]; | |
| context = {...context}; | |
| delete context[serviceName]; | |
| return context; | |
| }), | |
| flagShutdown: assign((context, event) => ({ | |
| ...context, | |
| shutdown: true | |
| })), | |
| restartAll: send({type: 'RESTART', services: '*'}), | |
| stop: send('STOP'), | |
| stopAll: send({type: 'STOP', services: '*'}), | |
| stopBlockchain: send({type: 'STOP', services: 'blockchain'}), | |
| stopCompiler: send({type: 'STOP', services: 'compiler'}), | |
| storeError: assign((context, event) => ({ | |
| ...context, | |
| [event.type.split('.').pop()]: { | |
| error: event.data, | |
| message: event.data.message | |
| } | |
| })) | |
| }; | |
| function allTest(matches, states) { | |
| return function (context, event, meta) { | |
| let cond, state; | |
| if (meta) ({cond, state} = meta); | |
| if (state) { | |
| matches = [].concat(matches); | |
| for (const match of matches) { | |
| if (state.matches(match)) { | |
| return Object.values( | |
| state.value[match] | |
| ).every(state => [].concat(states).includes(state)); | |
| } | |
| } | |
| return undefined; | |
| } else { | |
| return undefined; | |
| } | |
| }; | |
| } | |
| function serviceMatchTest(eventName, serviceName) { | |
| return function (context, event, meta) { | |
| let cond, state; | |
| if (meta) ({cond, state} = meta); | |
| if (state && event.services) { | |
| if (event.type !== eventName) return false; | |
| if (event.services === '*') return true; | |
| if ([].concat(event.services).includes(serviceName)) return true; | |
| return false; | |
| } else { | |
| return undefined; | |
| } | |
| }; | |
| } | |
| const guards = { | |
| allStarted: allTest('starting', 'started'), | |
| allStopped: allTest(['starting', 'running'], ['crashed', 'failed', 'stopped']), | |
| canShutdown: (context, event, meta) => context.shutdown && guards.allStopped(context, event, meta), | |
| isBlockchainRestart: serviceMatchTest('RESTART', 'blockchain'), | |
| isBlockchainStop: serviceMatchTest('STOP', 'blockchain'), | |
| isCompilerRestart: serviceMatchTest('RESTART', 'compiler'), | |
| isCompilerStop: serviceMatchTest('STOP', 'compiler') | |
| }; | |
| const services = { | |
| blockchainRestarter: () => modules.blockchain.restart(), | |
| blockchainStarter: () => modules.blockchain.start(), | |
| blockchainStopper: () => modules.blockchain.stop(), | |
| compilerRestarter: () => modules.compiler.restart(), | |
| compilerStarter: () => modules.compiler.start(), | |
| compilerStopper: () => modules.compiler.stop(), | |
| }; | |
| const starting = { | |
| blockchain: { | |
| initial: 'starting', | |
| states: { | |
| starting: { | |
| invoke: { | |
| src: 'blockchainStarter', | |
| onDone: { | |
| target: 'started' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| STOP: 'stopping' | |
| } | |
| }, | |
| started: { | |
| on: { | |
| STOP: 'stopping' | |
| } | |
| }, | |
| failed: { | |
| entry: 'stop' | |
| }, | |
| stopping: { | |
| invoke: { | |
| src: 'blockchainStopper', | |
| onDone: { | |
| target: 'stopped' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| } | |
| }, | |
| stopped: {} | |
| } | |
| }, | |
| compiler: { | |
| initial: 'starting', | |
| states: { | |
| starting: { | |
| invoke: { | |
| src: 'compilerStarter', | |
| onDone: { | |
| target: 'started' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| STOP: 'stopping' | |
| } | |
| }, | |
| started: { | |
| on: { | |
| STOP: 'stopping' | |
| } | |
| }, | |
| failed: { | |
| entry: 'stop' | |
| }, | |
| stopping: { | |
| invoke: { | |
| src: 'compilerStopper', | |
| onDone: { | |
| target: 'stopped' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| } | |
| }, | |
| stopped: {} | |
| } | |
| } | |
| }; | |
| const running = { | |
| blockchain: { | |
| initial: 'running', | |
| states: { | |
| running: { | |
| // invoke: 'blockchainRunner' --> should be able to transition to crashed, should clear error | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| }, | |
| STOP: { | |
| target: 'stopping', | |
| cond: 'isBlockchainStop' | |
| } | |
| } | |
| }, | |
| crashed: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| } | |
| } | |
| }, | |
| failed: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| } | |
| } | |
| }, | |
| stopping: { | |
| invoke: { | |
| src: 'blockchainStopper', | |
| onDone: { | |
| target: 'stopped', | |
| actions: 'clearError' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| } | |
| } | |
| }, | |
| stopped: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| } | |
| } | |
| }, | |
| restarting: { | |
| invoke: { | |
| src: 'blockchainRestarter', | |
| onDone: { | |
| target: 'running', | |
| actions: 'clearError' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isBlockchainRestart' | |
| }, | |
| STOP: { | |
| target: 'stopping', | |
| cond: 'isBlockchainStop' | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| compiler: { | |
| initial: 'running', | |
| states: { | |
| running: { | |
| // invoke: 'compilerRunner' --> should be able to transition to crashed, should clear error | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| }, | |
| STOP: { | |
| target: 'stopping', | |
| cond: 'isCompilerStop' | |
| } | |
| } | |
| }, | |
| crashed: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| } | |
| } | |
| }, | |
| failed: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| } | |
| } | |
| }, | |
| stopping: { | |
| invoke: { | |
| src: 'compilerStopper', | |
| onDone: { | |
| target: 'stopped', | |
| actions: 'clearError' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| } | |
| } | |
| }, | |
| stopped: { | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| } | |
| } | |
| }, | |
| restarting: { | |
| invoke: { | |
| src: 'compilerRestarter', | |
| onDone: { | |
| target: 'running', | |
| actions: 'clearError' | |
| }, | |
| onError: { | |
| target: 'failed', | |
| actions: 'storeError' | |
| } | |
| }, | |
| on: { | |
| RESTART: { | |
| target: 'restarting', | |
| cond: 'isCompilerRestart' | |
| }, | |
| STOP: { | |
| target: 'stopping', | |
| cond: 'isCompilerStop' | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| const newAppInstance = 'new EmbarkRun(options, configs)'; | |
| const embarkRun = Machine( | |
| { | |
| id: 'embark run [--options]', | |
| context: context, | |
| initial: 'setting up', | |
| states: { | |
| 'setting up': { | |
| type: 'parallel', | |
| states: { | |
| 'CLI options ➡︎ Application options': {}, | |
| 'processing DApp configs': {} | |
| }, | |
| on: { | |
| READY: newAppInstance | |
| } | |
| }, | |
| [newAppInstance]: { | |
| on: { | |
| EXECUTE: 'starting' | |
| } | |
| }, | |
| starting: { | |
| type: 'parallel', | |
| states: { | |
| ...starting | |
| }, | |
| on: { | |
| '': [ | |
| { | |
| target: 'running', | |
| cond: 'allStarted' | |
| }, | |
| { | |
| target: 'stopped', | |
| cond: 'allStopped' | |
| } | |
| ] | |
| } | |
| }, | |
| running: { | |
| type: 'parallel', | |
| states: { | |
| ...running | |
| }, | |
| on: { | |
| '': { | |
| target: 'stopped', | |
| cond: 'canShutdown' | |
| }, | |
| RESTART_ALL: { | |
| internal: true, | |
| actions: 'restartAll' | |
| }, | |
| SHUTDOWN: { | |
| internal: true, | |
| actions: ['flagShutdown', 'stopAll'] | |
| }, | |
| STOP_ALL: { | |
| internal: true, | |
| actions: 'stopAll' | |
| } | |
| } | |
| }, | |
| stopped: { | |
| type: 'final' | |
| } | |
| } | |
| }, | |
| { | |
| actions: { ...acts }, | |
| guards: { ...guards }, | |
| services: { ...services } | |
| } | |
| ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment