[
  {
    "path": ".gitignore",
    "content": "# files\n*.log\n*.pid\n*.seed\n\n# folders\nlogs/\npids/\nbuild/\ncoverage/\nnode_modules/"
  },
  {
    "path": ".travis.yml",
    "content": "node_js:\n- \"4\"\n- \"6\"\nsudo: false\nlanguage: node_js\nscript: \"npm run test:cov\"\nafter_script: \"npm install coveralls@2 && cat ./coverage/lcov.info | coveralls\"\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Yoshua Wuyts\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."
  },
  {
    "path": "README.md",
    "content": "# barracks\n[![NPM version][npm-image]][npm-url]\n[![build status][travis-image]][travis-url]\n[![Test coverage][coveralls-image]][coveralls-url]\n[![Downloads][downloads-image]][downloads-url]\n\nAction dispatcher for unidirectional data flows. Creates tiny models of data\nthat can be accessed with actions through a small API.\n\n## Usage\n````js\nconst barracks = require('barracks')\n\nconst store = barracks()\n\nstore.use({\n  onError: (err, state, createSend) => {\n    console.error(`error: ${err}`)\n  },\n  onAction: (state, data, name, caller, createSend) => {\n    console.log(`data: ${data}`)\n  },\n  onStateChange: (state, data, prev, caller, createSend) => {\n    console.log(`state: ${prev} -> ${state}`)\n  }\n})\n\nstore.model({\n  namespace: 'cakes',\n  state: {},\n  effects: {},\n  reducers: {},\n  subscriptions: {}\n})\n\nconst createSend = store.start({ subscriptions: true })\nconst send = createSend('myDispatcher', true)\ndocument.addEventListener('DOMContentLoaded', () => {\n  store.start() // fire up subscriptions\n  const state = store.state()\n  send('foo:start', { name: 'Loki' })\n})\n````\n\n## API\n### store = barracks(hooks?)\nInitialize a new `barracks` instance. Takes an optional object of hooks which\nis passed to `.use()`.\n\n### store.use(hooks)\nRegister new hooks on the store. Hooks are little plugins that can extend\nbehavior or perform actions at specific points in the life cycle. The following\nhooks are possible:\n- __models:__ an array of models that will be merged with the store.\n- __onError(err, state, createSend):__ called when an `effect` or\n  `subscription` emit an error; if no hook is passed, the default hook will\n  `throw` on each error\n- __onAction(state, data, name, caller, createSend):__ called when an `action`\n  is fired\n- __onStateChange(state, data, prev, caller, createSend):__ called after a\n  reducer changes the `state`.\n- __wrapSubscriptions(fn):__ wraps a `subscription` to add custom behavior\n- __wrapReducers(fn):__ wraps a `reducer` to add custom behavior\n- __wrapEffects(fn):__ wraps an `effect` to add custom behavior\n- __wrapInitialState(obj):__ mutate the initial `state` to add custom\n  behavior - useful to mutate the state before starting up\n\n`createSend()` is a special function that allows the creation of a new named\n`send()` function. The first argument should be a string which is the name, the\nsecond argument is a boolean `callOnError` which can be set to `true` to call\nthe `onError` hook instead of a provided callback. It then returns a\n`send(actionName, data?)` function.\n\nThe `wrap*` hooks are synchronously resolved when the `store.start()` method is\ncalled, and the corresponding values from the models are loaded. All wrap hooks\n(or wrappers) are passed the argument that would usually be called, so it can\nbe wrapped or modified. Say we want to make all our `reducers` print `'golden\npony'` every time they're run, we'd do:\n```js\nconst barracks = require('barracks')\nconst store = barracks()\n\nstore.use({\n  wrapReducers: function wrapConstructor (reducer) {\n    return function wrapper (state, data) {\n      console.log('golden pony')\n      return reducer(state, data)\n    }\n  }\n})\n```\n\nHooks should be used with care, as they're the most powerful interface into\nthe state. For application level code, it's generally recommended to delegate to\nactions inside models using the `send()` call, and only shape the actions\ninside the hooks.\n\n### store.model()\nRegister a new model on the store. Models are optionally namespaced objects\nwith an initial `state` and handlers for dealing with data:\n- __namespace:__ namespace the model so that it cannot access any properties\n  and handlers in other models\n- __state:__ initial values of `state` inside the model\n- __reducers:__ synchronous operations that modify state; triggered by `actions`\n- __effects:__ asynchronous operations that don't modify state directly;\n  triggered by `actions`, can call `actions`\n- __subscriptions:__ asynchronous read-only operations that don't modify state\n  directly; can call `actions`\n\n`state` within handlers is immutable through `Object.freeze()` and thus cannot\nbe modified. Return data from `reducers` to modify `state`. See [handler\nsignatures](#handler-signatures) for more info on the handlers.\n\nFor debugging purposes, internal references to values can be inspected through a\nseries of private accessors:\n- `store._subscriptions`\n- `store._reducers`\n- `store._effects`\n- `store._models`\n\n### state = store.state(opts)\nGet the current state from the store. Opts can take the following values:\n- __freeze:__ default: true; set to false to not freeze state in handlers\n  using `Object.freeze()`; useful for optimizing performance in production\n  builds\n- __state:__ pass in a state object that will be merged with the state returned\n  from the store; useful for rendering in Node\n\n### send = createSend(name) = store.start(opts)\nStart the store and get a `createSend(name)` function. Pass a unique `name` to\n`createSend()` to get a `send()` function. Opts can take the following values:\n- __subscriptions:__ default: true; set to false to not register\n  `subscriptions` when starting the application; useful to delay `init`\n  functions until the DOM has loaded\n- __effects:__ default: true; set to `false` to not register `effects` when\n  starting the application; useful when only wanting the initial `state`\n- __reducers:__ default: true; set to false to not register `reducers` when\n  starting the application; useful when only wanting the initial `state`\n\nIf the store has disabled any of the handlers (e.g. `{ reducers: false }`),\ncalling `store.start()` a second time will register the remaining values. This\nis useful if not everything can be started at the same time (e.g. have\n`subscriptions` wait for the `DOMContentLoaded` event).\n\n### send(name, data?)\nSend a new action to the models with optional data attached. Namespaced models\ncan be accessed by prefixing the name with the namespace separated with a `:`,\ne.g. `namespace:name`.\n\n### store.stop()\n\nAfter an app is \"stopped\" all subsequent `send()` calls become no-ops.\n\n```js\nstore.stop()\nsend('trimBeard') // -> does not call a reducer/effect\n```\n\n## Handler signatures\nThese are the signatures for the properties that can be passed into a model.\n\n### namespace\nAn optional string that causes `state`, `effects` and `reducers` to be\nprefixed.\n\n```js\napp.model({\n  namespace: 'users'\n})\n```\n\n### state\nState can either be a value or an object of values that is used as the initial\nstate for the application. If namespaced the values will live under\n`state[namespace]`.\n```js\napp.model({\n  namespace: 'hey',\n  state: { foo: 'bar' }\n})\napp.model({\n  namespace: 'there',\n  state: { bin: [ 'beep', 'boop' ] }\n})\napp.model({\n  namespace: 'people',\n  state: 'oi'\n}})\n```\n\n### reducers\nReducers are synchronous functions that return a value synchronously. No\neventual values, just values that are relevant for the state. It takes two\narguments of `data` and `state`. `data` is the data that was emitted, and\n`state` is the current state. Each action has a name that can be accessed\nthrough `send(name)`, and when under a namespace can be accessed as\n`send(namespace:name)`. When operating under a namespace, reducers only have\naccess to the state within the namespace.\n```js\n// some model\napp.model({\n  namespace: 'plantcake',\n  state: {\n    enums: [ 'veggie', 'potato', 'lettuce' ]\n    paddie: 'veggie'\n  }\n})\n\n// so this model can't access anything in the 'plantcake' namespace\napp.model({\n  namespace: 'burlybeardos',\n  state: { count: 1 },\n  reducers: {\n    feedPlantcake: (state, data) => {\n      return { count: state.count + 1 }\n    },\n    trimBeard: (state, data) => ({ count: state.count - 1 })\n  }\n})\n```\n\n### effects\n`effects` are asynchronous methods that can be triggered by `actions` in\n`send()`. They never update the state directly, but can instead do thing\nasynchronously, and then call `send()` again to trigger a `reducer` that can\nupdate the state. `effects` can also trigger other `effects`, making them fully\ncomposable. Generally, it's recommended to only have `effects` without a\n`namespace` call other `effects`, as to keep namespaced models as isolated as\npossible.\n\nWhen an `effect` is done executing, or encounters an error, it should call the\nfinal `done(err)` callback. If the `effect` was called by another `effect` it\nwill call the callback of the caller. When an error propagates all the way to\nthe top, the `onError` handler will be called, registered in\n`barracks(handlers)`. If no callback is registered, errors will `throw`.\n\nHaving callbacks in `effects` means that error handling can be formalized\nwithout knowledge of the rest of the application leaking into the model. This\nalso causes `effects` to become fully composable, which smooths parallel\ndevelopment in large teams, and keeps the mental overhead low when developing a\nsingle model.\n\n```js\nconst http = require('xhr')\nconst app = barracks({\n  onError: (state, data, prev, send) => send('app:error', data)\n})\n\napp.model({\n  namespace: 'app',\n  effects: {\n    error: (state, data, send, done) => {\n      // if doing http calls here be super sure not to get lost\n      // in a recursive error handling loop: remember this IS\n      // the error handler\n      console.error(data.message)\n      done()\n    }\n  }\n})\n\napp.model({\n  namespace: 'foo',\n  state: { foo: 1 },\n  reducers: {\n    moreFoo: (state, data) => ({ foo: state.foo + data.count })\n  }\n  effects: {\n    fetch: (state, data, send, done) => {\n      http('foobar.com', function (err, res, body) {\n        if (err || res.statusCode !== 200) {\n          return done(new Error({\n            message: 'error accessing server',\n            error: err\n          }))\n        } else {\n          send('moreFoo', { count: foo.count }, done)\n        }\n      })\n    }\n  }\n})\n```\n\n### subscriptions\n`subscriptions` are read-only sources of data. This means they cannot be\ntriggered by actions, but can emit actions themselves whenever they want. This\nis useful for stuff like listening to keyboard events or incoming websocket\ndata. They should generally be started when the application is loaded, using\nthe `DOMContentLoaded` listener.\n\n```js\napp.model({\n  subscriptions: {\n    emitWoofs: (send, done) => {\n      // emit a woof every second\n      setInterval(() =>  send('printWoofs', { woof: 'meow?' }, done), 1000)\n    }\n  },\n  effects: {\n    printWoofs: (state, data) => console.log(data.woof)\n  }\n})\n```\n`done()` is passed as the final argument so if an error occurs in a subscriber,\nit can be communicated to the `onError` hook.\n\n## FAQ\n### What is an \"action dispatcher\"?\nAn action dispatcher gets data from one place to another without tightly\ncoupling code. The best known use case for this is in the `flux` pattern. Say\nyou want to update a piece of data (for example a user's name), instead of\ndirectly calling the update logic inside the view, the action calls a function\nthat updates the user's name for you. Now all the views that need to update a\nuser's name can call the same action and pass in the relevant data. This\npattern tends to make views more robust and easier to maintain.\n\n### Why did you build this?\nPassing messages around should not be complicated. Many `flux` implementations\ncasually throw restrictions at users without having a clear architecture. I\ndon't like that. `barracks` is a package that creates a clear flow of data within an\napplication, concerning itself with state, code separation, and data flow. I\nbelieve that having strong opinions and being transparent in them makes for\nbetter architectures than sprinkles of opinions left and right, without a cohesive\nstory as to _why_.\n\n### How is this different from choo?\n`choo` is a framework that handles views, data and all problems related to\nthat. This is a package that only concerns itself with data flow, without being\nexplicitly tied to the DOM.\n\n### This looks like more than five functions!\nWelllll, no. It's technically five functions with a high arity, hah. Nah,\nyou're right - but five functions _sounds_ good. Besides: you don't need to\nknow all options and toggles to get this working; that only relevant once you\nstart hitting edge cases like we did in `choo` :sparkles:\n\n## See Also\n- [choo](https://github.com/yoshuawuyts/choo) - sturdy frontend framework\n- [sheet-router](https://github.com/yoshuawuyts/sheet-router) - fast, modular\n  client-side router\n- [yo-yo](https://github.com/maxogden/yo-yo) - template string based view\n  framework\n- [send-action](https://github.com/sethvincent/send-action) - unidirectional\n  action emitter\n\n## Installation\n```sh\n$ npm install barracks\n```\n\n## License\n[MIT](https://tldrlegal.com/license/mit-license)\n\n[npm-image]: https://img.shields.io/npm/v/barracks.svg?style=flat-square\n[npm-url]: https://npmjs.org/package/barracks\n[travis-image]: https://img.shields.io/travis/yoshuawuyts/barracks/master.svg?style=flat-square\n[travis-url]: https://travis-ci.org/yoshuawuyts/barracks\n[coveralls-image]: https://img.shields.io/coveralls/yoshuawuyts/barracks.svg?style=flat-square\n[coveralls-url]: https://coveralls.io/r/yoshuawuyts/barracks?branch=master\n[downloads-image]: http://img.shields.io/npm/dm/barracks.svg?style=flat-square\n[downloads-url]: https://npmjs.org/package/barracks\n\n[flux]: http://facebook.github.io/react/blog/2014/05/06/flux.html\n[browserify]: https://github.com/substack/node-browserify\n"
  },
  {
    "path": "apply-hook.js",
    "content": "module.exports = applyHook\n\n// apply arguments onto an array of functions, useful for hooks\n// (arr, any?, any?, any?, any?, any?) -> null\nfunction applyHook (arr, arg1, arg2, arg3, arg4, arg5) {\n  arr.forEach(function (fn) {\n    fn(arg1, arg2, arg3, arg4, arg5)\n  })\n}\n"
  },
  {
    "path": "index.js",
    "content": "var mutate = require('xtend/mutable')\nvar nanotick = require('nanotick')\nvar assert = require('assert')\nvar xtend = require('xtend')\n\nvar applyHook = require('./apply-hook')\n\nmodule.exports = dispatcher\n\n// initialize a new barracks instance\n// obj -> obj\nfunction dispatcher (hooks) {\n  hooks = hooks || {}\n  assert.equal(typeof hooks, 'object', 'barracks: hooks should be undefined or an object')\n\n  var onStateChangeHooks = []\n  var onActionHooks = []\n  var onErrorHooks = []\n\n  var subscriptionWraps = []\n  var initialStateWraps = []\n  var reducerWraps = []\n  var effectWraps = []\n\n  use(hooks)\n\n  var reducersCalled = false\n  var effectsCalled = false\n  var stateCalled = false\n  var subsCalled = false\n  var stopped = false\n\n  var subscriptions = start._subscriptions = {}\n  var reducers = start._reducers = {}\n  var effects = start._effects = {}\n  var models = start._models = []\n  var _state = {}\n\n  var tick = nanotick()\n\n  start.model = setModel\n  start.state = getState\n  start.start = start\n  start.stop = stop\n  start.use = use\n\n  return start\n\n  // push an object of hooks onto an array\n  // obj -> null\n  function use (hooks) {\n    assert.equal(typeof hooks, 'object', 'barracks.use: hooks should be an object')\n    assert.ok(!hooks.onError || typeof hooks.onError === 'function', 'barracks.use: onError should be undefined or a function')\n    assert.ok(!hooks.onAction || typeof hooks.onAction === 'function', 'barracks.use: onAction should be undefined or a function')\n    assert.ok(!hooks.onStateChange || typeof hooks.onStateChange === 'function', 'barracks.use: onStateChange should be undefined or a function')\n\n    if (hooks.onStateChange) onStateChangeHooks.push(hooks.onStateChange)\n    if (hooks.onError) onErrorHooks.push(wrapOnError(hooks.onError))\n    if (hooks.onAction) onActionHooks.push(hooks.onAction)\n    if (hooks.wrapSubscriptions) subscriptionWraps.push(hooks.wrapSubscriptions)\n    if (hooks.wrapInitialState) initialStateWraps.push(hooks.wrapInitialState)\n    if (hooks.wrapReducers) reducerWraps.push(hooks.wrapReducers)\n    if (hooks.wrapEffects) effectWraps.push(hooks.wrapEffects)\n    if (hooks.models) hooks.models.forEach(setModel)\n  }\n\n  // push a model to be initiated\n  // obj -> null\n  function setModel (model) {\n    assert.equal(typeof model, 'object', 'barracks.store.model: model should be an object')\n    models.push(model)\n  }\n\n  // get the current state from the store\n  // obj? -> obj\n  function getState (opts) {\n    opts = opts || {}\n    assert.equal(typeof opts, 'object', 'barracks.store.state: opts should be an object')\n\n    var state = opts.state\n    if (!opts.state && opts.freeze === false) return xtend(_state)\n    else if (!opts.state) return Object.freeze(xtend(_state))\n    assert.equal(typeof state, 'object', 'barracks.store.state: state should be an object')\n\n    var namespaces = []\n    var newState = {}\n\n    // apply all fields from the model, and namespaced fields from the passed\n    // in state\n    models.forEach(function (model) {\n      var ns = model.namespace\n      namespaces.push(ns)\n      var modelState = model.state || {}\n      if (ns) {\n        newState[ns] = newState[ns] || {}\n        apply(ns, modelState, newState)\n        newState[ns] = xtend(newState[ns], state[ns])\n      } else {\n        mutate(newState, modelState)\n      }\n    })\n\n    // now apply all fields that weren't namespaced from the passed in state\n    Object.keys(state).forEach(function (key) {\n      if (namespaces.indexOf(key) !== -1) return\n      newState[key] = state[key]\n    })\n\n    var tmpState = xtend(_state, xtend(state, newState))\n    var wrappedState = wrapHook(tmpState, initialStateWraps)\n\n    return (opts.freeze === false)\n      ? wrappedState\n      : Object.freeze(wrappedState)\n  }\n\n  // initialize the store hooks, get the send() function\n  // obj? -> fn\n  function start (opts) {\n    opts = opts || {}\n    assert.equal(typeof opts, 'object', 'barracks.store.start: opts should be undefined or an object')\n\n    // register values from the models\n    models.forEach(function (model) {\n      var ns = model.namespace\n      if (!stateCalled && model.state && opts.state !== false) {\n        var modelState = model.state || {}\n        if (ns) {\n          _state[ns] = _state[ns] || {}\n          apply(ns, modelState, _state)\n        } else {\n          mutate(_state, modelState)\n        }\n      }\n      if (!reducersCalled && model.reducers && opts.reducers !== false) {\n        apply(ns, model.reducers, reducers, function (cb) {\n          return wrapHook(cb, reducerWraps)\n        })\n      }\n      if (!effectsCalled && model.effects && opts.effects !== false) {\n        apply(ns, model.effects, effects, function (cb) {\n          return wrapHook(cb, effectWraps)\n        })\n      }\n      if (!subsCalled && model.subscriptions && opts.subscriptions !== false) {\n        apply(ns, model.subscriptions, subscriptions, function (cb, key) {\n          var send = createSend('subscription: ' + (ns ? ns + ':' + key : key))\n          cb = wrapHook(cb, subscriptionWraps)\n          cb(send, function (err) {\n            applyHook(onErrorHooks, err, _state, createSend)\n          })\n          return cb\n        })\n      }\n    })\n\n    // the state wrap is special because we want to operate on the full\n    // state rather than indvidual chunks, so we apply it outside the loop\n    if (!stateCalled && opts.state !== false) {\n      _state = wrapHook(_state, initialStateWraps)\n    }\n\n    if (!opts.noSubscriptions) subsCalled = true\n    if (!opts.noReducers) reducersCalled = true\n    if (!opts.noEffects) effectsCalled = true\n    if (!opts.noState) stateCalled = true\n\n    if (!onErrorHooks.length) onErrorHooks.push(wrapOnError(defaultOnError))\n\n    return createSend\n\n    // call an action from a view\n    // (str, bool?) -> (str, any?, fn?) -> null\n    function createSend (selfName, callOnError) {\n      assert.equal(typeof selfName, 'string', 'barracks.store.start.createSend: selfName should be a string')\n      assert.ok(!callOnError || typeof callOnError === 'boolean', 'barracks.store.start.send: callOnError should be undefined or a boolean')\n\n      return function send (name, data, cb) {\n        if (!cb && !callOnError) {\n          cb = data\n          data = null\n        }\n        data = (typeof data === 'undefined' ? null : data)\n\n        assert.equal(typeof name, 'string', 'barracks.store.start.send: name should be a string')\n        assert.ok(!cb || typeof cb === 'function', 'barracks.store.start.send: cb should be a function')\n\n        var done = callOnError ? onErrorCallback : cb\n        _send(name, data, selfName, done)\n\n        function onErrorCallback (err) {\n          err = err || null\n          if (err) {\n            applyHook(onErrorHooks, err, _state, function createSend (selfName) {\n              return function send (name, data) {\n                assert.equal(typeof name, 'string', 'barracks.store.start.send: name should be a string')\n                data = (typeof data === 'undefined' ? null : data)\n                _send(name, data, selfName, done)\n              }\n            })\n          }\n        }\n      }\n    }\n\n    // call an action\n    // (str, str, any, fn) -> null\n    function _send (name, data, caller, cb) {\n      if (stopped) return\n\n      assert.equal(typeof name, 'string', 'barracks._send: name should be a string')\n      assert.equal(typeof caller, 'string', 'barracks._send: caller should be a string')\n      assert.equal(typeof cb, 'function', 'barracks._send: cb should be a function')\n\n      ;(tick(function () {\n        var reducersCalled = false\n        var effectsCalled = false\n        var newState = xtend(_state)\n\n        if (onActionHooks.length) {\n          applyHook(onActionHooks, _state, data, name, caller, createSend)\n        }\n\n        // validate if a namespace exists. Namespaces are delimited by ':'.\n        var actionName = name\n        if (/:/.test(name)) {\n          var arr = name.split(':')\n          var ns = arr.shift()\n          actionName = arr.join(':')\n        }\n\n        var _reducers = ns ? reducers[ns] : reducers\n        if (_reducers && _reducers[actionName]) {\n          if (ns) {\n            var reducedState = _reducers[actionName](_state[ns], data)\n            newState[ns] = xtend(_state[ns], reducedState)\n          } else {\n            mutate(newState, reducers[actionName](_state, data))\n          }\n          reducersCalled = true\n          if (onStateChangeHooks.length) {\n            applyHook(onStateChangeHooks, newState, data, _state, actionName, createSend)\n          }\n          _state = newState\n          cb(null, newState)\n        }\n\n        var _effects = ns ? effects[ns] : effects\n        if (!reducersCalled && _effects && _effects[actionName]) {\n          var send = createSend('effect: ' + name)\n          if (ns) _effects[actionName](_state[ns], data, send, cb)\n          else _effects[actionName](_state, data, send, cb)\n          effectsCalled = true\n        }\n\n        if (!reducersCalled && !effectsCalled) {\n          throw new Error('Could not find action ' + actionName)\n        }\n      }))()\n    }\n  }\n\n  // stop an app, essentially turns\n  // all send() calls into no-ops.\n  // () -> null\n  function stop () {\n    stopped = true\n  }\n}\n\n// compose an object conditionally\n// optionally contains a namespace\n// which is used to nest properties.\n// (str, obj, obj, fn?) -> null\nfunction apply (ns, source, target, wrap) {\n  if (ns && !target[ns]) target[ns] = {}\n  Object.keys(source).forEach(function (key) {\n    var cb = wrap ? wrap(source[key], key) : source[key]\n    if (ns) target[ns][key] = cb\n    else target[key] = cb\n  })\n}\n\n// handle errors all the way at the top of the trace\n// err? -> null\nfunction defaultOnError (err) {\n  throw err\n}\n\nfunction wrapOnError (onError) {\n  return function onErrorWrap (err, state, createSend) {\n    if (err) onError(err, state, createSend)\n  }\n}\n\n// take a apply an array of transforms onto a value. The new value\n// must be returned synchronously from the transform\n// (any, [fn]) -> any\nfunction wrapHook (value, transforms) {\n  transforms.forEach(function (transform) {\n    value = transform(value)\n  })\n  return value\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"barracks\",\n  \"version\": \"9.3.2\",\n  \"description\": \"Action dispatcher for unidirectional data flows\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"standard && NODE_ENV=test node test\",\n    \"test:cov\": \"standard && NODE_ENV=test istanbul cover test.js\",\n    \"watch\": \"watch 'npm t'\"\n  },\n  \"repository\": \"yoshuawuyts/barracks\",\n  \"keywords\": [\n    \"action\",\n    \"dispatcher\"\n  ],\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"nanotick\": \"^1.1.2\",\n    \"xtend\": \"^4.0.1\"\n  },\n  \"devDependencies\": {\n    \"bundle-collapser\": \"^1.2.1\",\n    \"coveralls\": \"~2.11.12\",\n    \"es2020\": \"^1.1.6\",\n    \"istanbul\": \"~0.4.5\",\n    \"noop2\": \"^2.0.0\",\n    \"standard\": \"^8.0.0\",\n    \"tape\": \"^4.6.0\",\n    \"uglifyify\": \"^3.0.2\",\n    \"unassertify\": \"^2.0.3\",\n    \"watch\": \"^1.0.0\"\n  },\n  \"files\": [\n    \"LICENSE\",\n    \"README.md\",\n    \"index.js\",\n    \"apply-hook.js\"\n  ]\n}\n"
  },
  {
    "path": "script/test-size",
    "content": "#!/bin/sh\n\nusage () {\ncat << USAGE\nscript/test-size\n  Commands:\n    g, gzip (default)    Output gzip size\n    m, minified          Output size minified\n    d, discify           Run discify\nUSAGE\n}\n\ngzip_size () {\n  browserify index.js \\\n    -g unassertify \\\n    -g es2020 \\\n    -g uglifyify \\\n    -p bundle-collapser/plugin \\\n    | uglifyjs \\\n    | gzip-size \\\n    | pretty-bytes\n}\n\nmin_size () {\n  browserify index.js \\\n    -g unassertify \\\n    -g es2020 \\\n    -g uglifyify \\\n    -p bundle-collapser/plugin \\\n    | uglifyjs \\\n    | wc -c \\\n    | pretty-bytes\n}\n\nrun_discify () {\n  browserify index.js --full-paths \\\n    -g unassertify \\\n    -g es2020 \\\n    -g uglifyify \\\n    | uglifyjs \\\n    | discify --open\n}\n\n# set CLI flags\ngetopt -T > /dev/null\nif [ \"$?\" -eq 4 ]; then\n  args=\"$(getopt --long help discify minified --options hmdg -- \"$*\")\"\nelse args=\"$(getopt h \"$*\")\"; fi\n[ ! $? -eq 0 ] && { usage && exit 2; }\neval set -- \"$args\"\n\n# parse CLI flags\nwhile true; do\n  case \"$1\" in\n    -h|--help) usage && exit 1 ;;\n    -- ) shift; break ;;\n    * ) break ;;\n  esac\ndone\n\ncase \"$1\" in\n  d|discify) shift; run_discify \"$@\" ;;\n  m|minified) shift; printf \"min:  %s\\n\" \"$(min_size)\" \"$@\" ;;\n  g|gzip|*) shift; printf \"gzip: %s\\n\" \"$(gzip_size)\" \"$@\" ;;\nesac\n"
  },
  {
    "path": "test.js",
    "content": "const barracks = require('./')\nconst xtend = require('xtend')\nconst noop = require('noop2')\nconst tape = require('tape')\n\ntape('api: store = barracks(handlers)', (t) => {\n  t.test('should validate input types', (t) => {\n    t.plan(3)\n    t.doesNotThrow(barracks, 'no args does not throw')\n    t.doesNotThrow(barracks.bind(null, {}), 'object does not throw')\n    t.throws(barracks.bind(null, 123), 'non-object throws')\n  })\n\n  t.test('should validate hook types', (t) => {\n    t.plan(3)\n    t.throws(barracks.bind(null, { onError: 123 }), /function/, 'onError throws')\n    t.throws(barracks.bind(null, { onAction: 123 }), /function/, 'onAction throws')\n    t.throws(barracks.bind(null, { onStateChange: 123 }), /function/, 'onStateChange throws')\n  })\n})\n\ntape('api: store.model()', (t) => {\n  t.test('should validate input types', (t) => {\n    t.plan(2)\n    const store = barracks()\n    t.throws(store.model.bind(null, 123), /object/, 'non-obj should throw')\n    t.doesNotThrow(store.model.bind(null, {}), 'obj should not throw')\n  })\n})\n\ntape('api: store.use()', (t) => {\n  t.test('should allow model injection', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({\n      state: { accessed: false }\n    })\n    store.use({\n      models: [{\n        namespace: 'namespaced',\n        state: { 'accessed': false },\n        reducers: { update: (state, data) => data }\n      }, {\n        state: {},\n        reducers: { update: (state, data) => data }\n      }]\n    })\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('namespaced:update', { accessed: true })\n    send('update', { accessed: true })\n\n    setTimeout(function () {\n      const expected = { accessed: true, namespaced: { accessed: true } }\n      t.deepEqual(store.state(), expected, 'models can be injected')\n    }, 100)\n  })\n\n  t.test('should call multiples', (t) => {\n    t.plan(1)\n    const store = barracks()\n    const called = { first: false, second: false }\n\n    store.use({\n      onAction: (state, data, name, caller, createSend) => {\n        called.first = true\n      }\n    })\n\n    store.use({\n      onAction: (state, data, name, caller, createSend) => {\n        called.second = true\n      }\n    })\n\n    store.model({\n      state: {\n        count: 0\n      },\n      reducers: {\n        foo: (state, data) => ({ count: state.count + 1 })\n      }\n    })\n\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('foo', { count: 3 })\n\n    setTimeout(function () {\n      const expected = { first: true, second: true }\n      t.deepEqual(called, expected, 'all hooks were called')\n    }, 100)\n  })\n})\n\ntape('api: createSend = store.start(opts)', (t) => {\n  t.test('should validate input types', (t) => {\n    t.plan(3)\n    const store = barracks()\n    t.throws(store.start.bind(null, 123), /object/, 'non-obj should throw')\n    t.doesNotThrow(store.start.bind(null, {}), /object/, 'obj should not throw')\n    t.doesNotThrow(store.start.bind(null), 'undefined should not throw')\n  })\n\n  t.test('opts.state = false should not register state', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ state: { foo: 'bar' } })\n    store.start({ state: false })\n    const state = store.state()\n    t.deepEqual(state, {}, 'no state returned')\n  })\n\n  t.test('opts.effects = false should not register effects', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ effects: { foo: noop } })\n    store.start({ effects: false })\n    const effects = Object.keys(store._effects)\n    t.deepEqual(effects.length, 0, 'no effects registered')\n  })\n\n  t.test('opts.reducers = false should not register reducers', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ reducers: { foo: noop } })\n    store.start({ reducers: false })\n    const reducers = Object.keys(store._reducers)\n    t.deepEqual(reducers.length, 0, 'no reducers registered')\n  })\n\n  t.test('opts.subscriptions = false should not register subs', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ subscriptions: { foo: noop } })\n    store.start({ subscriptions: false })\n    const subscriptions = Object.keys(store._subscriptions)\n    t.deepEqual(subscriptions.length, 0, 'no subscriptions registered')\n  })\n})\n\ntape('api: state = store.state()', (t) => {\n  t.test('should return initial state', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ state: { foo: 'bar' } })\n    store.start()\n    const state = store.state()\n    t.deepEqual(state, { foo: 'bar' })\n  })\n\n  t.test('should initialize state with empty namespace object', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({\n      namespace: 'beep',\n      state: {}\n    })\n    store.start()\n    const state = store.state()\n    const expected = {\n      beep: {}\n    }\n    t.deepEqual(expected, state, 'has initial empty namespace object')\n  })\n\n  t.test('should return the combined state', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({\n      namespace: 'beep',\n      state: { foo: 'bar', bin: 'baz' }\n    })\n    store.model({\n      namespace: 'boop',\n      state: { foo: 'bar', bin: 'baz' }\n    })\n    store.model({\n      state: { hello: 'dog', welcome: 'world' }\n    })\n    store.start()\n    const state = store.state()\n    const expected = {\n      beep: { foo: 'bar', bin: 'baz' },\n      boop: { foo: 'bar', bin: 'baz' },\n      hello: 'dog',\n      welcome: 'world'\n    }\n    t.deepEqual(expected, state, 'initial state of models is combined')\n  })\n\n  t.test('object should be frozen by default', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ state: { foo: 'bar' } })\n    store.start()\n    const state = store.state()\n    state.baz = 'bin'\n    const expected = { foo: 'bar' }\n    t.deepEqual(state, expected, 'state was frozen')\n  })\n\n  t.test('freeze = false should not freeze objects', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ state: { foo: 'bar' } })\n    store.start()\n    const state = store.state({ freeze: false })\n    state.baz = 'bin'\n    const expected = { foo: 'bar', baz: 'bin' }\n    t.deepEqual(state, expected, 'state was not frozen')\n  })\n\n  t.test('passing a state opts should merge state', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.model({ state: { foo: 'bar' } })\n    store.model({\n      namespace: 'beep',\n      state: { foo: 'bar', bin: 'baz' }\n    })\n    store.start()\n\n    const extendedState = {\n      woof: 'dog',\n      beep: { foo: 'baz' },\n      barp: { bli: 'bla' }\n    }\n    const state = store.state({ state: extendedState })\n    const expected = {\n      foo: 'bar',\n      woof: 'dog',\n      beep: { foo: 'baz', bin: 'baz' },\n      barp: { bli: 'bla' }\n    }\n    t.deepEqual(state, expected, 'state was merged')\n  })\n})\n\ntape('api: send(name, data?)', (t) => {\n  t.test('should validate input types', (t) => {\n    t.plan(1)\n    const store = barracks()\n    const createSend = store.start()\n    const send = createSend('test')\n    t.throws(send.bind(null, 123), /string/, 'non-string should throw')\n  })\n})\n\ntape('api: stop()', (t) => {\n  t.test('should stop executing send() calls', (t) => {\n    t.plan(1)\n    const store = barracks()\n    var count = 0\n    store.model({ reducers: { foo: (state, action) => { count += 1 } } })\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('foo')\n    store.stop()\n    send('foo')\n    setTimeout(() => t.equal(count, 1, 'no actions after stop()'), 10)\n  })\n})\n\ntape('handlers: reducers', (t) => {\n  t.test('should be able to be called', (t) => {\n    t.plan(6)\n    const store = barracks()\n    store.model({\n      namespace: 'meow',\n      state: { beep: 'boop' },\n      reducers: {\n        woof: (state, data) => t.pass('meow.woof called')\n      }\n    })\n\n    store.model({\n      state: {\n        foo: 'bar',\n        beep: 'boop'\n      },\n      reducers: {\n        foo: (state, data) => {\n          t.deepEqual(data, { foo: 'baz' }, 'action is equal')\n          t.equal(state.foo, 'bar', 'state.foo = bar')\n          return { foo: 'baz' }\n        },\n        sup: (state, data) => {\n          t.equal(data, 'nope', 'action is equal')\n          t.equal(state.beep, 'boop', 'state.beep = boop')\n          return { beep: 'nope' }\n        }\n      }\n    })\n    const createSend = store.start()\n    const send = createSend('tester', true)\n    send('foo', { foo: 'baz' })\n    send('sup', 'nope')\n    send('meow:woof')\n    setTimeout(function () {\n      const state = store.state()\n      const expected = {\n        foo: 'baz',\n        beep: 'nope',\n        meow: { beep: 'boop' }\n      }\n      t.deepEqual(state, expected, 'state was updated')\n    }, 10)\n  })\n})\n\ntape('handlers: effects', (t) => {\n  t.test('should be able to be called', (t) => {\n    t.plan(5)\n    const store = barracks()\n\n    store.model({\n      namespace: 'meow',\n      effects: {\n        woof: (state, data, send, done) => {\n          t.pass('woof called')\n        }\n      }\n    })\n\n    store.model({\n      state: { bin: 'baz', beep: 'boop' },\n      reducers: {\n        bar: (state, data) => {\n          t.pass('reducer was called')\n          return { beep: data.beep }\n        }\n      },\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('effect was called')\n          send('bar', { beep: data.beep }, () => {\n            t.pass('effect callback was called')\n            done()\n          })\n        }\n      }\n    })\n    const createSend = store.start()\n    const send = createSend('tester', true)\n    send('foo', { beep: 'woof' })\n    send('meow:woof')\n\n    setTimeout(function () {\n      const state = store.state()\n      const expected = { bin: 'baz', beep: 'woof' }\n      t.deepEqual(state, expected, 'state was updated')\n    }, 10)\n  })\n\n  t.test('should be able to nest effects and return data', (t) => {\n    t.plan(12)\n    const store = barracks()\n    store.model({\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('foo was called')\n          send('bar', { beep: 'boop' }, () => {\n            t.pass('foo:bar effect callback was called')\n            send('baz', (err, res) => {\n              t.ifError(err, 'no error')\n              t.equal(res, 'yay', 'res is equal')\n              t.pass('foo:baz effect callback was called')\n              done()\n            })\n          })\n        },\n        bar: (state, data, send, done) => {\n          t.pass('bar was called')\n          t.deepEqual(data, { beep: 'boop' }, 'action is equal')\n          send('baz', (err, res) => {\n            t.ifError(err, 'no error')\n            t.equal(res, 'yay', 'res is equal')\n            t.pass('bar:baz effect callback was called')\n            done()\n          })\n        },\n        baz: (state, data, send, done) => {\n          t.pass('baz effect was called')\n          done(null, 'yay')\n        }\n      }\n    })\n    const createSend = store.start()\n    const send = createSend('tester', true)\n    send('foo')\n  })\n\n  t.test('should be able to propagate nested errors', (t) => {\n    t.plan(7)\n    const store = barracks()\n    store.model({\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('foo was called')\n          send('bar', (err, res) => {\n            t.ok(err, 'error detected')\n            t.pass('foo:bar effect callback was called')\n            done()\n          })\n        },\n        bar: (state, data, send, done) => {\n          t.pass('bar was called')\n          send('baz', (err, res) => {\n            t.ok(err, 'error detected')\n            t.pass('bar:baz effect callback was called')\n            done(err)\n          })\n        },\n        baz: (state, data, send, done) => {\n          t.pass('baz effect was called')\n          done(new Error('oh noooo'))\n        }\n      }\n    })\n    const createSend = store.start()\n    const send = createSend('tester', true)\n    send('foo')\n  })\n})\n\ntape('handlers: subscriptions', (t) => {\n  t.test('should be able to call', (t) => {\n    t.plan(9)\n    const store = barracks()\n    store.model({\n      namespace: 'foo',\n      subscriptions: {\n        mySub: (send, done) => {\n          t.pass('namespaced sub initiated')\n        }\n      }\n    })\n\n    store.model({\n      reducers: {\n        bar: () => t.pass('reducer called')\n      },\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('foo was called')\n          done(new Error('oh no!'), 'hello')\n        }\n      },\n      subscriptions: {\n        mySub: (send, done) => {\n          t.pass('mySub was initiated')\n          send('foo', (err, res) => {\n            t.ok(err, 'error detected')\n            t.equal(res, 'hello', 'res was passed')\n            t.pass('mySub:foo effect callback was called')\n            send('bar', (err, res) => {\n              t.error(err, 'no error detected')\n              t.pass('mySub:bar effect callback was called')\n            })\n          })\n        }\n      }\n    })\n    store.start()\n  })\n\n  t.test('should be able to emit an error', (t) => {\n    t.plan(4)\n    const store = barracks({\n      onError: (err, state, createSend) => {\n        t.equal(err.message, 'oh no!', 'err was received')\n        t.equal((state || {}).a, 1, 'state was passed')\n        t.equal(typeof createSend, 'function', 'createSend is a function')\n      }\n    })\n\n    store.model({\n      state: { a: 1 },\n      subscriptions: {\n        mySub: (send, done) => {\n          t.pass('sub initiated')\n          done(new Error('oh no!'))\n        }\n      }\n    })\n    store.start()\n  })\n})\n\ntape('hooks: onStateChange', (t) => {\n  t.test('should be called whenever state changes', (t) => {\n    t.plan(4)\n    const store = barracks({\n      onStateChange: (state, data, prev, caller, createSend) => {\n        t.deepEqual(data, { count: 3 }, 'action is equal')\n        t.deepEqual(state, { count: 4 }, 'state is equal')\n        t.deepEqual(prev, { count: 1 }, 'prev is equal')\n        t.equal(caller, 'increment', 'caller is equal')\n      }\n    })\n\n    store.model({\n      state: { count: 1 },\n      reducers: {\n        increment: (state, data) => ({ count: state.count + data.count })\n      }\n    })\n\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('increment', { count: 3 })\n  })\n\n  t.test('should allow triggering other actions', (t) => {\n    t.plan(2)\n    const store = barracks({\n      onStateChange: function (state, data, prev, caller, createSend) {\n        t.pass('onStateChange called')\n        const send = createSend('test:onStateChange', true)\n        send('foo')\n      }\n    })\n\n    store.model({\n      state: { count: 1 },\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('called')\n          done()\n        }\n      },\n      reducers: {\n        increment: (state, data) => ({ count: state.count + data.count })\n      }\n    })\n\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('increment', { count: 3 })\n  })\n\n  t.test('previous state should not be mutated', (t) => {\n    t.plan(2)\n    const storeNS = barracks({\n      onStateChange: (state, data, prev, caller, createSend) => {\n        t.equal(state.ns.items.length, 3, 'state was updated')\n        t.equal(prev.ns.items.length, 0, 'prev was left as-is')\n      }\n    })\n\n    storeNS.model({\n      namespace: 'ns',\n      state: { items: [] },\n      reducers: {\n        add: (_, state) => ({ items: [1, 2, 3] })\n      }\n    })\n\n    const createSendNS = storeNS.start()\n    const sendNS = createSendNS('testNS', true)\n    sendNS('ns:add')\n  })\n})\n\ntape('hooks: onAction', (t) => {\n  t.test('should be called whenever an action is emitted', (t) => {\n    t.plan(5)\n    const store = barracks({\n      onAction: (state, data, actionName, caller, createSend) => {\n        t.deepEqual(data, { count: 3 }, 'action is equal')\n        t.deepEqual(state, { count: 1 }, 'state is equal')\n        t.deepEqual(actionName, 'foo', 'actionName is equal')\n        t.equal(caller, 'test', 'caller is equal')\n      }\n    })\n\n    store.model({\n      state: { count: 1 },\n      effects: {\n        foo: (state, data, send, done) => {\n          t.pass('effect called')\n          done()\n        }\n      }\n    })\n\n    const createSend = store.start()\n    const send = createSend('test', true)\n    send('foo', { count: 3 })\n  })\n})\n\ntape('hooks: onError', (t) => {\n  t.test('should have a default err handler')\n  t.test('should not call itself')\n})\n\ntape('wrappers: wrapSubscriptions')\ntape('wrappers: wrapReducers')\ntape('wrappers: wrapEffects')\n\ntape('wrappers: wrapInitialState', (t) => {\n  t.test('should wrap initial state in start', (t) => {\n    t.plan(2)\n    const store = barracks()\n    store.use({\n      wrapInitialState: (state) => {\n        t.deepEqual(state, { foo: 'bar' }, 'initial state is correct')\n        return xtend(state, { beep: 'boop' })\n      }\n    })\n\n    store.model({\n      state: { foo: 'bar' }\n    })\n\n    store.start()\n    process.nextTick(() => {\n      const state = store.state()\n      t.deepEqual(state, { foo: 'bar', beep: 'boop' }, 'wrapped state correct')\n    })\n  })\n\n  t.test('should wrap initial state in getState', (t) => {\n    t.plan(1)\n    const store = barracks()\n    store.use({\n      wrapInitialState: (state) => {\n        return xtend(state, { beep: 'boop' })\n      }\n    })\n\n    store.model({\n      state: { foo: 'bar' }\n    })\n\n    process.nextTick(() => {\n      const opts = {\n        state: { bin: 'baz' }\n      }\n      const expected = {\n        foo: 'bar',\n        beep: 'boop',\n        bin: 'baz'\n      }\n      const state = store.state(opts)\n      t.deepEqual(state, expected, 'wrapped state correct')\n    })\n  })\n})\n"
  }
]