[
  {
    "path": ".eslintignore",
    "content": "distribution\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 8,\n    \"ecmaFeatures\": {\n      \"blockBindings\": true,\n      \"jsx\": true,\n      \"modules\": true\n    }\n  },\n  \"env\": {\n    \"browser\": true,\n    \"es6\": true,\n    \"jasmine\": true,\n    \"node\": true\n  },\n  \"rules\": {\n    \"arrow-spacing\": 2,\n    \"brace-style\": 2,\n    \"camelcase\": 2,\n    \"comma-dangle\": [\n      2,\n      \"never\"\n    ],\n    \"comma-spacing\": [\n      2,\n      {\n        \"before\": false,\n        \"after\": true\n      }\n    ],\n    \"comma-style\": [\n      2,\n      \"last\"\n    ],\n    \"complexity\": [\n      1,\n      8\n    ],\n    \"consistent-this\": [\n      2,\n      \"_this\"\n    ],\n    \"default-case\": 2,\n    \"dot-notation\": 2,\n    \"eol-last\": 2,\n    \"eqeqeq\": 2,\n    \"guard-for-in\": 1,\n    \"indent\": [\n      2,\n      2,\n      {\n        \"SwitchCase\": 1\n      }\n    ],\n    \"key-spacing\": [\n      2,\n      {\n        \"beforeColon\": false,\n        \"afterColon\": true\n      }\n    ],\n    \"keyword-spacing\": [\n      1,\n      {\n        \"before\": true,\n        \"after\": true\n      }\n    ],\n    \"new-cap\": 2,\n    \"new-parens\": 2,\n    \"no-caller\": 2,\n    \"no-debugger\": 1,\n    \"no-dupe-args\": 2,\n    \"no-dupe-keys\": 2,\n    \"no-dupe-class-members\": 2,\n    \"no-duplicate-case\": 2,\n    \"no-eq-null\": 0,\n    \"no-eval\": 2,\n    \"no-implied-eval\": 2,\n    \"no-invalid-regexp\": 2,\n    \"no-mixed-spaces-and-tabs\": 2,\n    \"no-redeclare\": 2,\n    \"no-self-compare\": 1,\n    \"no-shadow-restricted-names\": 2,\n    \"no-trailing-spaces\": 2,\n    \"no-undef\": 2,\n    \"no-undef-init\": 2,\n    \"no-underscore-dangle\": 0,\n    \"no-unreachable\": 2,\n    \"no-unused-vars\": 1,\n    \"no-use-before-define\": 2,\n    \"no-with\": 2,\n    # \"no-magic-numbers\": 1,\n    \"one-var\": [\n      2,\n      \"never\"\n    ],\n    \"operator-assignment\": [\n      2,\n      \"always\"\n    ],\n    \"quote-props\": 0,\n    \"quotes\": [\n      2,\n      \"single\"\n    ],\n    \"radix\": 2,\n    \"semi\": [\n      2,\n      \"always\"\n    ],\n    \"semi-spacing\": [\n      2,\n      {\n        \"before\": false,\n        \"after\": true\n      }\n    ],\n    \"sort-vars\": [\n      1,\n      {\n        \"ignoreCase\": true\n      }\n    ],\n    \"space-before-function-paren\": [\n      2,\n      {\n        \"anonymous\": \"always\",\n        \"named\": \"never\"\n      }\n    ],\n    \"space-in-parens\": [\n      2,\n      \"never\"\n    ],\n    \"space-infix-ops\": 2,\n    \"space-unary-ops\": [\n      2,\n      {\n        \"words\": true,\n        \"nonwords\": false\n      }\n    ],\n    \"strict\": [\n      2,\n      \"global\"\n    ],\n    \"use-isnan\": 2,\n    \"valid-jsdoc\": 1,\n    \"yoda\": [\n      2,\n      \"never\",\n      {\n        \"exceptRange\": false\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\nnpm-debug.log\ndistribution\n"
  },
  {
    "path": ".npmignore",
    "content": "source\n"
  },
  {
    "path": ".snyk",
    "content": "# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.\nversion: v1.10.1\nignore: {}\npatch: {}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"8\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Eric Elliott\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Redux DSM\n\nDeclarative State Machines for Redux: Reduce your async-state boilerplate.\n\nRedux-dsm takes a nested array of transitions and states and automatically generates:\n\n* Reducers to manage all state transitions, complete with logic that will not let your app get into an invalid state (according to the graph you supply)\n* Action creators and action types to trigger all transitions\n\n## Status\n\nIn production use on [DevAnywhere.io](https://devanywhere.io).\n\n\n## Install\n\n```sh\nnpm install --save redux-dsm\n```\n\nAnd then in your file :\n\n```js\nimport dsm from 'redux-dsm';\n```\n\nOr using CommonJS modules :\n\n```js\nvar dsm = require('redux-dsm');\n```\n\n\n## Why?\n\nYour state isn't always available synchronously all the time. Some state has to be loaded asynchronously, which requires you to cycle through UI states representing concepts like fetching, processing, error, success, and idle states. In fact, a simple ajax fetch might have up to 7 transitions leading into 4 different states:\n\n```\nTransition        Next Status\n['initial',           'idle']\n['fetch',         'fetching']\n['cancel',            'idle']\n['report error',     'error']\n['handle error',      'idle']\n['report success', 'success']\n['handle success',    'idle']\n```\n\nYour view code will look at the status and payload to determine whether or not to render spinners, success messages, error messages, empty states, or data. I don't know about you, but I sometimes forget some of those transitions or states.\n\nEvery app I've ever written needs to do this a bunch of times. Since I switched to Redux, I handle all of my view state transitions by dispatching action objects, and that requires writing a bunch of boilerplate, such as action types (e.g., `myComponent::FETCH_FOO::FETCH`), and action creators (which your view or service layers can call to create actions without forcing you to import all those action type constants everywhere).\n\nThis little library takes a few declarative inputs and spits out all of the boilerplate for you, including a mini reducer that you can combine with your feature-level reducers.\n\n## Can I Use it With *x*?\n\nThis library is not just for ajax, though that will be a very common use-case, and it doesn't care how you handle your async I/O. You can use it with [Sagas](https://github.com/yelouafi/redux-saga), redux-thunks, action creators with side-effects, etc..., or just use it by itself and manually wire up your async I/O.\n\nYou don't even have to use it with Redux -- anything that uses reducer-based state is fine, including [ngrx/store](https://github.com/ngrx/store) or even `Array.prototype.reduce()`.\n\n\n## Usage Example\n\n```js\nimport dsm from 'redux-dsm';\n\n// ['action', 'next state',\n//   ['scoped action', 'another state']\n// ]\n// e.g., in the following example, from 'fetching' state, we can:\n// * cancel\n// * report an error\n// * report success\nconst fetchingStates = ['initial', 'idle',\n  ['fetch', 'fetching',\n    ['cancel', 'idle'],\n    ['report error', 'error',\n      ['handle error', 'idle']\n    ],\n    ['report success', 'success',\n      ['handle success', 'idle']\n    ]\n  ]\n];\n\n// ({\n//   component?: String,\n//   description?: String,\n//   actionStates: Array,\n//   delimiter?: String\n// }) => { actions: Object, actionCreators: Object, reducer: Function }\nconst foo = dsm({\n  component: 'myComponent',\n  description: 'fetch foo',\n  actionStates: fetchingStates\n});\n```\n\n## .actions: Object\n\n`actions` is an object with camelCased keys and strings corresponding to your state transitions. If you use the returned `.actionCreators`, you probably don't need to use these, but it's handy for debugging. For the above example, it returns:\n\n```js\n  \"actions\": {\n    \"fetch\": \"myComponent::FETCH_FOO::FETCH\",\n    \"cancel\": \"myComponent::FETCH_FOO::CANCEL\",\n    \"reportError\": \"myComponent::FETCH_FOO::REPORT_ERROR\",\n    \"handleError\": \"myComponent::FETCH_FOO::HANDLE_ERROR\",\n    \"reportSuccess\": \"myComponent::FETCH_FOO::REPORT_SUCCESS\",\n    \"handleSuccess\": \"myComponent::FETCH_FOO::HANDLE_SUCCESS\"\n  }\n```\n\n## .actionCreators: Object\n\n`actionCreators` will be an object with camelCased keys and function values corresponding to your state transitions. For each transition, an action creator is created which will automatically fill in the correct action type, and pass through `payload` to the state.\n\nThe example fetch state machine will produce the following `actionCreators`:\n\n```js\nfetch()\ncancel()\nreportError()\nhandleError()\nreportSuccess()\nhandleSuccess()\n```\n\n## .reducer: (state, action) => state\n\n`.reducer()` is a normal Redux reducer function that takes the current state and an action object, and returns the new state. Matching action objects can be created using the `.actionCreators`. The reducer can be combined with a parent reducer using `combineReducers()`. Any payload passed into an action creator will be passed through to `state.payload`.\n\n## State: { status: String, payload: Any }\n\nThe state object will have two keys, `status` and `payload`. In the example above, `status` will be one of `idle`, `fetching`, `error`, or `success`.\n\nBy default, the `payload` key is an object with `type: 'empty'`.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"redux-dsm\",\n  \"version\": \"3.0.3\",\n  \"description\": \"Declarative State Machines for Redux\",\n  \"main\": \"./distribution/dsm.js\",\n  \"directories\": {\n    \"doc\": \"docs\"\n  },\n  \"scripts\": {\n    \"lint\": \"eslint . && echo 'Lint finished...\\n'\",\n    \"posttest\": \"npm run -s lint && node tools/cli.js\",\n    \"test\": \"node -r @std/esm source/test/index.js && node -r @std/esm source/test/authenticate-flow-test.js\",\n    \"debug\": \"node -r @std/esm --inspect-brk source/test/test.js\",\n    \"watch\": \"watch 'clear && npm run -s test' source\",\n    \"update\": \"updtr\",\n    \"build\": \"babel source --presets babel-preset-es2015 --out-dir distribution\",\n    \"prepublish\": \"npm run build\"\n  },\n  \"@std/esm\": \"cjs\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/ericelliott/redux-dsm.git\"\n  },\n  \"keywords\": [\n    \"state\",\n    \"machine\",\n    \"redux\"\n  ],\n  \"author\": \"Eric Elliott\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/ericelliott/redux-dsm/issues\"\n  },\n  \"homepage\": \"https://github.com/ericelliott/redux-dsm#readme\",\n  \"engines\": {\n    \"node\": \">=6.0.0\"\n  },\n  \"devDependencies\": {\n    \"@std/esm\": \"0.26.0\",\n    \"babel-cli\": \"6.26.0\",\n    \"babel-preset-es2015\": \"6.24.1\",\n    \"colors\": \"1.3.2\",\n    \"eslint\": \"5.6.1\",\n    \"lodash\": \"4.17.11\",\n    \"riteway\": \"3.0.0\",\n    \"snyk\": \"1.101.1\",\n    \"tape\": \"4.9.1\",\n    \"updtr\": \"3.1.0\",\n    \"watch\": \"1.0.2\"\n  },\n  \"dependencies\": {\n    \"lodash.camelcase\": \"4.3.0\",\n    \"lodash.snakecase\": \"4.1.1\"\n  }\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\n    \"config:base\"\n  ],\n  \"automerge\": true,\n  \"major\": {\n    \"automerge\": false\n  }\n}\n"
  },
  {
    "path": "source/dsm.js",
    "content": "const camelCase = require('lodash.camelcase');\nconst snakeCase = require('lodash.snakecase');\n\nconst defaultStatus = 'idle';\n\nconst parseNode = node => {\n  const data = node.slice(0, 2);\n  const child = node.slice(2);\n  return { data, child };\n};\n\nconst getDefaultStatus = (actionStates) => {\n  const status = actionStates[1];\n  return status ? status : defaultStatus;\n};\n\nconst getStates = (graph, initialMemo = [], parentStatus) => (\n  graph.reduce((memo, node) => {\n    const { data, child } = parseNode(node);\n\n    if (Array.isArray(data)) memo = memo.concat([data.concat([parentStatus])]);\n    if (Array.isArray(child)) memo = getStates(child, memo, data[1]);\n\n    return memo;\n  }, initialMemo)\n);\n\nconst formatConstant = text => snakeCase(text).toUpperCase();\n\nconst empty = {\n  type: 'empty'\n};\n\nconst composeReducers = (...reducers) =>\n  reducers.reduce((step, reducer) => (a, c) => step(reducer(a, c), c));\n\nconst createReducer = (defaultState, reducerMap) => {\n  return (state = defaultState, action = {}) => {\n    const { type } = action;\n\n    const reducer = Array.isArray(reducerMap[type]) ?\n      composeReducers(...reducerMap[type]) :\n      state => state\n    ;\n\n    return reducer(state, action);\n  };\n};\n\nconst getActionCreatorNames = states => {\n  return states.map(state => {\n    const description = state[0];\n    return camelCase(description);\n  });\n};\n\nconst dsm = ({\n  component = '',\n  description = '',\n  delimiter = '::',\n  actionStates = []\n}) => {\n  const defaultState = {\n    status: getDefaultStatus(actionStates),\n    payload: empty\n  };\n  const states = [...getStates(actionStates, [], defaultState.status)];\n  const actionNames = getActionCreatorNames(states);\n\n  const actionMap = states.map(a => {\n    const action = component + [\n      component ? delimiter : '',\n      formatConstant(description),\n      description ? delimiter : '',\n      formatConstant(a[0])\n    ].join('');\n    const status = a[1];\n    const parentStatus = a[2];\n\n    return {\n      action,\n      status,\n      parentStatus\n    };\n  });\n\n  const actions = actionMap.map(a => a.action).reduce((acs, action, i) => {\n    acs[actionNames[i]] = action;\n    return acs;\n  }, {});\n\n  const reducerMap = actionMap.reduce((map, a) => {\n    const reducer = (state = defaultState, action = {}) => {\n\n      if (state.status === a.parentStatus && action.type === a.action) {\n        const { status } = a;\n        const { payload } = action;\n\n        return Object.assign({}, state, {\n          status,\n          payload\n        });\n      }\n\n      return state;\n    };\n\n    if (!map[a.action]) map[a.action] = [reducer];\n    else map[a.action].push(reducer);\n\n    return map;\n  }, {});\n\n  const reducer = createReducer(defaultState, reducerMap);\n\n  const actionCreators = actionNames.reduce((acs, description) => {\n    acs[description] = payload => ({\n      type: actions[description],\n      payload\n    });\n\n    return acs;\n  }, {});\n\n  return {\n    actions,\n    actionCreators,\n    reducer\n  };\n};\n\nmodule.exports = dsm;\nmodule.exports.dsm = dsm;\nmodule.exports.default = dsm;\n"
  },
  {
    "path": "source/test/authenticate-flow-test.js",
    "content": "import { describe } from 'riteway';\n\nimport dsm from '../dsm';\n\nconst SIGNED_OUT = 'signed out';\nconst AUTHENTICATING = 'authenticating';\nconst ERROR = 'error';\nconst SIGNED_IN = 'signed in';\n\nconst actionStates = ['initial', SIGNED_OUT,\n  ['sign in', AUTHENTICATING,\n    ['report sign in failure', ERROR,\n      ['handle sign in failure', SIGNED_OUT]\n    ],\n    ['report sign in success', SIGNED_IN,\n      ['sign out', SIGNED_OUT]\n    ]\n  ],\n  ['report sign in success', SIGNED_IN,\n    ['sign out', SIGNED_OUT]\n  ]\n];\n\nconst {\n  reducer,\n  actionCreators: {\n    signIn,\n    reportSignInSuccess\n  }\n} = dsm({\n  component: 'user-authentication',\n  description: 'authenticate user',\n  actionStates\n});\n\n\ndescribe('userAuthenticationReducer', async assert => {\n  {\n    const should = 'use \"signed out\" as initialized state';\n\n    assert({\n      given: '[\"initial\", \"signed out\", /*...*/',\n      should,\n      actual: reducer().status,\n      expected: SIGNED_OUT\n    });\n  }\n\n  {\n    const should = 'transition into authenticating state';\n\n    assert({\n      given: 'signed out initial state & signIn action',\n      should,\n      actual: reducer(undefined, signIn()).status,\n      expected: AUTHENTICATING\n    });\n  }\n\n  {\n    const should = 'transition into \"signed in\" state';\n    const initialState = reducer(undefined, signIn());\n\n    assert({\n      given: '\"authenticating\" initial state & reportSignInSuccess action',\n      should,\n      actual: reducer(initialState, reportSignInSuccess()).status,\n      expected: SIGNED_IN\n    });\n  }\n\n  {\n    const should = 'transition into \"signed in\" state';\n\n    assert({\n      given: '\"signed out\" initial state & reportSignInSuccess action',\n      should,\n      actual: reducer(undefined, reportSignInSuccess()).status,\n      expected: SIGNED_IN\n    });\n  }\n});\n"
  },
  {
    "path": "source/test/index.js",
    "content": "import './test';\n"
  },
  {
    "path": "source/test/test.js",
    "content": "const test = require('tape');\nconst lodash = require('lodash');\nconst pipe = lodash.flow;\n\nconst dsm = require('../dsm');\n\nconst createFlatStates = () => ['initial', 'idle',\n  ['fetch', 'fetching'],\n  ['cancel', 'idle'],\n  ['report error', 'error'],\n  ['handle error', 'idle'],\n  ['report success', 'success'],\n  ['handle success', 'idle']\n];\n\nconst mockStates = ['initial', 'idle',\n  ['fetch', 'fetching',\n    ['cancel', 'idle'],\n    ['report error', 'error',\n      ['handle error', 'idle']\n    ],\n    ['report success', 'success',\n      ['handle success', 'idle']\n    ]\n  ]\n];\n\nconst mockOptions = ({\n  component = 'myComponent',\n  description = 'fetch foo',\n  actionStates = mockStates\n} = {}) => ({\n  component, description, actionStates\n});\n\ntest('modules & package specs', nest => {\n  nest.test('dsm function exposed', assert => {\n    const msg = 'should export commonjs module';\n    const expected = 'function';\n    const actual = typeof require('../dsm');\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('dsm object exposed', assert => {\n    const msg = 'should export dsm property allowing `import { dsm }`';\n    const expected = 'function';\n    const actual = typeof require('../dsm').dsm;\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n});\n\ntest('dsm() action types', nest => {\n  nest.test('flat state', assert => {\n    const msg = 'should return action types corresponding with given transitions';\n\n    const expected = {\n      fetch: 'myComponent::FETCH_FOO::FETCH',\n      cancel: 'myComponent::FETCH_FOO::CANCEL',\n      reportError: 'myComponent::FETCH_FOO::REPORT_ERROR',\n      handleError: 'myComponent::FETCH_FOO::HANDLE_ERROR',\n      reportSuccess: 'myComponent::FETCH_FOO::REPORT_SUCCESS',\n      handleSuccess: 'myComponent::FETCH_FOO::HANDLE_SUCCESS'\n    };\n    const actual = dsm(mockOptions({\n      actionStates: createFlatStates()\n    })).actions;\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('nested state', assert => {\n    const msg = 'should return action types corresponding with given transitions';\n\n    const expected = {\n      fetch: 'myComponent::FETCH_FOO::FETCH',\n      cancel: 'myComponent::FETCH_FOO::CANCEL',\n      reportError: 'myComponent::FETCH_FOO::REPORT_ERROR',\n      handleError: 'myComponent::FETCH_FOO::HANDLE_ERROR',\n      reportSuccess: 'myComponent::FETCH_FOO::REPORT_SUCCESS',\n      handleSuccess: 'myComponent::FETCH_FOO::HANDLE_SUCCESS'\n    };\n    const actual = dsm(mockOptions()).actions;\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n});\n\ntest('dsm() reducer', nest => {\n  nest.test('with unrestricted state', assert => {\n    const msg = 'should transition to correct state';\n    const action = {\n      type: 'myComponent::FETCH_FOO::FETCH'\n    };\n    const reducer = dsm(mockOptions({\n      actionStates: createFlatStates()\n    })).reducer;\n\n    const actual = reducer(undefined, action).status;\n    const expected = 'fetching';\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('with unrestricted state', assert => {\n    const msg = 'should deliver correct payload';\n\n    const payload = {\n      type: 'response',\n      data: 'some data'\n    };\n\n    const action = {\n      type: 'myComponent::FETCH_FOO::REPORT_SUCCESS',\n      payload\n    };\n    const reducer = dsm(mockOptions({\n      actionStates: createFlatStates()\n    })).reducer;\n\n    const actual = reducer(undefined, action);\n    const expected = {\n      status: 'success',\n      payload\n    };\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('with nested state', assert => {\n    const msg = 'should transition to correct state';\n    const action = {\n      type: 'myComponent::FETCH_FOO::FETCH'\n    };\n    const reducer = dsm(mockOptions()).reducer;\n\n    const actual = reducer(undefined, action).status;\n    const expected = 'fetching';\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('with nested state', assert => {\n    const msg = 'should ignore invalid action for current state';\n    const action = {\n      type: 'myComponent::FETCH_FOO::REPORT_ERROR'\n    };\n    const reducer = dsm(mockOptions()).reducer;\n\n    const actual = reducer(undefined, action).status;\n    const expected = 'idle';\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n});\n\ntest('action creators', nest => {\n  nest.test('names', assert => {\n    const msg = 'should return action creators with correct names';\n\n    const expected = [\n      'fetch',\n      'cancel',\n      'reportError',\n      'handleError',\n      'reportSuccess',\n      'handleSuccess'\n    ];\n\n    const pipeline = pipe(\n      dsm,\n      obj => obj.actionCreators,\n      Object.keys\n    );\n    const actual = pipeline(mockOptions({\n      actionStates: createFlatStates()\n    }));\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('functions', assert => {\n    const msg = 'should return correct actions';\n\n    const payload = 'some data';\n\n    const expected = {\n      type: 'myComponent::FETCH_FOO::REPORT_SUCCESS',\n      payload\n    };\n\n    const pipeline = pipe(\n      dsm,\n      obj => obj.actionCreators.reportSuccess(payload)\n    );\n    const actual = pipeline(mockOptions({\n      actionStates: createFlatStates()\n    }));\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n\n  nest.test('payloads', assert => {\n    const msg = 'new state should reflect action payloads';\n\n    const payload = 'some data';\n\n    const request = dsm(mockOptions({\n      actionStates: createFlatStates()\n    }));\n    const action = request.actionCreators.reportSuccess(payload);\n    const actual = request.reducer(undefined, action);\n    const expected = {\n      status: 'success',\n      payload\n    };\n\n    assert.same(actual, expected, msg);\n    assert.end();\n  });\n});\n"
  },
  {
    "path": "tools/cli.js",
    "content": "require('colors');\n\n/**\n * Checks node version and prints a message before tests start\n * Latest Nodejs or at least 6.x.x is required to run our ES6 tests\n * @param {String} nodeVersion node semantic version\n * @returns {undefined}\n */\nfunction checkNode(nodeVersion) {\n  if (Number(nodeVersion.slice('.')[0]) < 8) {\n    console.log('Tests require Node version > v8.0.0 to run'.red);\n  }\n}\n\nmodule.exports = checkNode(process.versions.node);\n"
  }
]