[
  {
    "path": ".gitignore",
    "content": "node_modules/\n"
  },
  {
    "path": ".jscsrc",
    "content": "{\n    \"fileExtensions\": [\".js\"],\n\n    \"maximumLineLength\": 120,\n    \"validateIndentation\": 4,\n    \"validateLineBreaks\": \"LF\",\n    \"validateQuoteMarks\": \"'\",\n\n    \"disallowAnonymousFunctions\": true,\n    \"disallowMultipleLineBreaks\": true,\n    \"disallowMultipleVarDecl\": true,\n    \"disallowQuotedKeysInObjects\": true,\n    \"disallowSpacesInsideObjectBrackets\": \"all\",\n\n    \"excludeFiles\": [\n        \"node_modules/**\"\n    ],\n\n    // crockford preset with exceptions\n    \"requireCurlyBraces\": [\n        \"if\",\n        \"else\",\n        \"for\",\n        \"while\",\n        \"do\",\n        \"try\",\n        \"catch\"\n    ],\n    \"requireSpaceAfterKeywords\": [\n        \"if\",\n        \"else\",\n        \"for\",\n        \"while\",\n        \"do\",\n        \"switch\",\n        \"case\",\n        \"return\",\n        \"try\",\n        \"catch\",\n        \"function\",\n        \"typeof\"\n    ],\n    \"requireSpaceBeforeBlockStatements\": true,\n    \"requireParenthesesAroundIIFE\": true,\n    \"requireSpacesInConditionalExpression\": true,\n    \"disallowSpacesInNamedFunctionExpression\": {\n        \"beforeOpeningRoundBrace\": true\n    },\n    \"disallowSpacesInFunctionDeclaration\": {\n        \"beforeOpeningRoundBrace\": true\n    },\n    // \"requireMultipleVarDecl\": \"onevar\",\n    \"requireBlocksOnNewline\": true,\n    \"disallowEmptyBlocks\": true,\n    \"disallowSpacesInsideArrayBrackets\": true,\n    \"disallowSpacesInsideParentheses\": true,\n    \"disallowDanglingUnderscores\": true,\n    \"requireCommaBeforeLineBreak\": true,\n    \"disallowSpaceAfterPrefixUnaryOperators\": true,\n    \"disallowSpaceBeforePostfixUnaryOperators\": true,\n    \"disallowSpaceBeforeBinaryOperators\": [\n        \",\"\n    ],\n    \"requireSpacesInForStatement\": true,\n    \"requireSpaceBeforeBinaryOperators\": true,\n    \"requireSpaceAfterBinaryOperators\": true,\n    \"disallowKeywords\": [\n        \"with\",\n        \"continue\"\n    ],\n    \"validateIndentation\": 4,\n    \"disallowMixedSpacesAndTabs\": true,\n    \"disallowTrailingWhitespace\": true,\n    \"disallowTrailingComma\": true,\n    \"disallowKeywordsOnNewLine\": [\n        \"else\"\n    ],\n    \"requireLineFeedAtFileEnd\": true,\n    \"requireCapitalizedConstructors\": true,\n    \"requireDotNotation\": true,\n    \"disallowNewlineBeforeBlockStatements\": true,\n    \"disallowMultipleLineStrings\": true\n}\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n  \"bitwise\": true,\n  \"camelcase\": true,\n  \"curly\": true,\n  \"eqeqeq\": true,\n  \"esnext\": true,\n  \"freeze\": true,\n  \"immed\": true,\n  \"indent\": 4,\n  \"latedef\": \"nofunc\",\n  \"maxlen\": 120,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"noempty\": true,\n  \"nonew\": true,\n  \"quotmark\": \"single\",\n  \"strict\": true,\n  \"undef\": true,\n  \"unused\": true,\n  \"node\": true\n}\n"
  },
  {
    "path": ".npmignore",
    "content": "# Do not package non-functional code when importing this package via\n# npm.\ndocs/\nbench/\ntest/\n.travis.yml\n.jscsrc\n.jshint.rc\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"node\"\n  - \"lts/*\"\nenv:\n  - CXX=g++-4.8\naddons:\n  apt:\n    sources:\n    - ubuntu-toolchain-r-test\n    packages:\n    - g++-4.8\n"
  },
  {
    "path": "README.md",
    "content": "# swim-js [![Build Status](https://travis-ci.org/mrhooray/swim-js.svg?branch=master)](https://travis-ci.org/mrhooray/swim-js)\n> JavaScript implementation of [SWIM](http://www.cs.cornell.edu/~asdas/research/dsn02-SWIM.pdf) membership protocol\n\n* [npm](https://www.npmjs.com/package/swim)\n* [Motivation](#motivation)\n* [Usage](#usage)\n* [Benchmark](#benchmark)\n* [License](#license)\n\n## Motivation\n\nMembership management is important to distributed systems and large clusters need a decentralized protocol such as SWIM,\n which handles failure detection and membership dissemination in a scalable and weakly-consistent way.\nIt can be used to implement functionalities based on membership like distributed consensus, application layer sharding, log replication, etc.\n\n## Usage\n\nInstallation\n```sh\nnpm install swim --save\n```\n```js\nvar Swim = require('swim');\nvar opts = {\n    local: {\n        host: '10.31.1.191:11000',\n        meta: {'application': 'info'} // optional\n    },\n    codec: 'msgpack', // optional\n    disseminationFactor: 15, // optional\n    interval: 100, // optional\n    joinTimeout: 200, // optional\n    pingTimeout: 20, // optional\n    pingReqTimeout: 60, // optional\n    pingReqGroupSize: 3, // optional\n    suspectTimeout: 60, // optional\n    udp: {maxDgramSize: 512}, // optional\n    preferCurrentMeta: true // optional\n};\nvar swim = new Swim(opts);\nvar hostsToJoin = ['10.31.1.192:11000', '10.31.1.193:11000'];\n\nswim.bootstrap(hostsToJoin, function onBootstrap(err) {\n    if (err) {\n        // error handling\n        return;\n    }\n\n    // ready\n    console.log(swim.whoami());\n    console.log(swim.members());\n    console.log(swim.checksum());\n\n    // change on membership, e.g. new node or node died/left\n    swim.on(Swim.EventType.Change, function onChange(update) {});\n    // update on membership, e.g. node recovered or update on meta data\n    swim.on(Swim.EventType.Update, function onUpdate(update) {});\n\n    // shutdown\n    swim.leave();\n});\n\n// or\nswim.bootstrap(hostsToJoin);\n// bootstrap error handling\nswim.on(Swim.EventType.Error, function onError(err) {});\n// bootstrap ready\nswim.on(Swim.EventType.Ready, function onReady() {});\n```\n\n[Additional API Documentation](docs/api.md)\n\n## Benchmark\n\nBenchmark convergence time under different configuration\n```sh\nnode bench/script/convergence-time.js -h\n\n  Usage: convergence-time [options]\n\n  Options:\n\n    -h, --help                      output usage information\n    --cycles [value]                number of cycles\n    --workers [value]               number of workers\n    --codec [value]                 msgpack or json\n    --dissemination-factor [value]  dissemination factor\n    --interval [value]              interval\n    --join-timeout [value]          join timeout\n    --ping-timeout [value]          ping timeout\n    --ping-req-timeout [value]      ping req timeout\n    --ping-req-group-size [value]   ping req group size\n    --max-dgram-size [value]        max dgram size\n```\n```sh\nnode bench/script/convergence-time.js\n\nconfiguration:\n- cycles 10\n- workers 10\n- codec msgpack\n- dissemination factor 15\n- interval 20 ms\n- join timeout 100 ms\n- ping timeout 4 ms\n- ping req timeout 12 ms\n- ping req group size 3\n- max dgram size 512 bytes\nconvergence time under single node failure\nhistogram data:\n- count 10\n- min 76\n- max 123\n- mean 100\n- median 101\n- variance 308.44444444444446\n- std dev 17.56258649642599\n- p75 116.25\n- p95 123\n- p99 123\n```\n\n## License\n\nMIT\n"
  },
  {
    "path": "bench/index.js",
    "content": "'use strict';\n// var cycles = [10, 20];\n// var workers = [4, 16, 32, 64];\n// var codec = ['json', 'msgpack'];\n// var disseminationFactor = [4, 16, 32];\n// var interval = [10, 100];\n// var pingTimeout = [4, 16];\n// var pingReqTimeout = [12, 48];\n// var pingReqGroupSize = [1, 4];\n// var maxDgramSize = [512, 1024, 2048];\n"
  },
  {
    "path": "bench/lib/runner.js",
    "content": "'use strict';\nvar async = require('async');\nvar events = require('events');\nvar util = require('util');\n\nfunction Runner(opts) {\n    this.cycles = opts.cycles || 0;\n    this.setup = opts.setup || noop;\n    this.teardown = opts.teardown || noop;\n    this.before = opts.suite.before || noop;\n    this.fn = opts.suite.fn || noop;\n    this.after = opts.suite.after || noop;\n}\n\nutil.inherits(Runner, events.EventEmitter);\n\nRunner.prototype.run = function run(callback) {\n    var self = this;\n\n    async.series([\n        function setup(callback) {\n            self.emit(Runner.EventType.Setup);\n            self.setup(callback);\n        },\n        function run(callback) {\n            async.timesSeries(self.cycles, function wrappedRun(i, callback) {\n                async.series([\n                    function before(callback) {\n                        self.emit(Runner.EventType.Before);\n                        self.before(callback);\n                    },\n                    function fn(callback) {\n                        self.emit(Runner.EventType.Fn);\n                        self.fn(callback);\n                    },\n                    function after(callback) {\n                        self.emit(Runner.EventType.After);\n                        self.after(callback);\n                    }\n                ], callback);\n            }, callback);\n        },\n        function teardown(callback) {\n            self.emit(Runner.EventType.Teardown);\n            self.teardown(callback);\n        }\n    ], callback);\n};\n\nRunner.EventType = {\n    After: 'after',\n    Before: 'before',\n    Fn: 'fn',\n    Setup: 'setup',\n    Teardown: 'teardown'\n};\n\nmodule.exports = Runner;\n\nfunction noop(callback) {\n    if (typeof callback === 'function') {\n        process.nextTick(callback);\n    }\n}\n"
  },
  {
    "path": "bench/scenario/single-node-failure.js",
    "content": "'use strict';\n\nmodule.exports = function singleNodeFailure(context, callback) {\n    var hosts = Object.keys(context.hostToAliveWorker);\n    var host = hosts[Math.floor(Math.random() * hosts.length)];\n\n    if (host) {\n        context.hostToFaultyWorker[host] = context.hostToAliveWorker[host];\n        delete context.hostToAliveWorker[host];\n        context.hostToFaultyWorker[host].send({\n            cmd: 'leave'\n        });\n    }\n\n    process.nextTick(callback);\n};\n"
  },
  {
    "path": "bench/script/convergence-time.js",
    "content": "#!/usr/bin/env node\n'use strict';\nvar cp = require('child_process');\nvar metrics = require('metrics');\nvar program = require('commander');\n\nvar Runner = require('../lib/runner');\nvar singleNodeFailure = require('../scenario/single-node-failure');\n\nvar PORT_BASE = 20000;\n\nif (require.main === module) {\n    parseArgs();\n    main();\n}\n\nfunction parseArgs() {\n    program\n    .option('--cycles [value]', 'number of cycles', parseInt, 10)\n    .option('--workers [value]', 'number of workers', parseInt, 10)\n    .option('--codec [value]', 'msgpack or json', 'msgpack')\n    .option('--dissemination-factor [value]', 'dissemination factor', parseInt, 15)\n    .option('--interval [value]', 'interval', parseInt, 20)\n    .option('--join-timeout [value]', 'join timeout', parseInt, 100)\n    .option('--ping-timeout [value]', 'ping timeout', parseInt, 4)\n    .option('--ping-req-timeout [value]', 'ping req timeout', parseInt, 12)\n    .option('--ping-req-group-size [value]', 'ping req group size', parseInt, 3)\n    .option('--max-dgram-size [value]', 'max dgram size', parseInt, 512)\n    .parse(process.argv);\n\n    console.log('configuration:');\n    console.log('- cycles', program.cycles);\n    console.log('- workers', program.workers);\n    console.log('- codec', program.codec);\n    console.log('- dissemination factor', program.disseminationFactor);\n    console.log('- interval', program.interval, 'ms');\n    console.log('- join timeout', program.joinTimeout, 'ms');\n    console.log('- ping timeout', program.pingTimeout, 'ms');\n    console.log('- ping req timeout', program.pingReqTimeout, 'ms');\n    console.log('- ping req group size', program.pingReqGroupSize);\n    console.log('- max dgram size', program.maxDgramSize, 'bytes');\n}\n\nfunction main() {\n    var histogram = new metrics.Histogram();\n    var context = {\n        hostToAliveWorker: Object.create(null),\n        hostToFaultyWorker: Object.create(null),\n        hostToChecksum: Object.create(null),\n        numberOfWorkers: program.workers\n    };\n    var runner = new Runner({\n        cycles: program.cycles,\n        setup: setup.bind(undefined, context),\n        teardown: teardown.bind(undefined, context),\n        suite: {\n            before: before.bind(undefined, context),\n            fn: fn.bind(undefined, context),\n            after: after.bind(undefined, context)\n        }\n    });\n    var time;\n\n    runner.on(Runner.EventType.Fn, function onCycleStart() {\n        time = Date.now();\n    });\n    runner.on(Runner.EventType.After, function onCycleComplete() {\n        histogram.update(Date.now() - time);\n    });\n\n    console.log('convergence time under single node failure');\n\n    runner.run(function report() {\n        var result = histogram.printObj();\n\n        console.log('histogram data:');\n        console.log('- count', result.count);\n        console.log('- min', result.min);\n        console.log('- max', result.max);\n        console.log('- mean', result.mean);\n        console.log('- median', result.median);\n        console.log('- variance', result.variance);\n        /* jshint camelcase: false */\n        console.log('- std dev', result.std_dev);\n        /* jshint camelcase: true */\n        console.log('- p75', result.p75);\n        console.log('- p95', result.p95);\n        console.log('- p99', result.p99);\n    });\n}\n\nfunction setup(context, callback) {\n    var readyCount = 0;\n\n    fork(context, function onMessage(message) {\n        switch (message.type) {\n            case 'ready':\n                readyCount += 1;\n                if (readyCount === context.numberOfWorkers) {\n                    Object.keys(context.hostToAliveWorker).forEach(function join(host) {\n                        context.hostToAliveWorker[host].send({\n                            cmd: 'join',\n                            hosts: getHostsToJoin(Math.ceil(context.numberOfWorkers / 3))\n                        });\n                    });\n                    waitForConvergence(context, callback);\n                }\n                break;\n            case 'checksum':\n                context.hostToChecksum[message.host] = message.value;\n                break;\n        }\n    });\n}\n\nfunction fork(context, onMessage) {\n    var args;\n    var host;\n    var worker;\n    var i;\n\n    for (i = 0; i < context.numberOfWorkers; i++) {\n        host = '127.0.0.1:' + (PORT_BASE + i);\n        args = [];\n        args.push('--host', host);\n        if (program.codec) {\n            args.push('--codec', program.codec);\n        }\n        if (program.disseminationFactor) {\n            args.push('--dissemination-factor', program.disseminationFactor);\n        }\n        if (program.interval) {\n            args.push('--interval', program.interval);\n        }\n        if (program.joinTimeout) {\n            args.push('--join-timeout', program.joinTimeout);\n        }\n        if (program.pingTimeout) {\n            args.push('--ping-timeout', program.pingTimeout);\n        }\n        if (program.pingReqTimeout) {\n            args.push('--ping-req-timeout', program.pingReqTimeout);\n        }\n        if (program.pingReqGroupSize) {\n            args.push('--ping-req-group-size', program.pingReqGroupSize);\n        }\n        if (program.maxDgramSize) {\n            args.push('--max-dgram-size', program.maxDgramSize);\n        }\n\n        worker = cp.fork(__dirname + '/worker.js', args);\n        worker.on('message', onMessage);\n        context.hostToAliveWorker[host] = worker;\n    }\n}\n\nfunction getHostsToJoin(n) {\n    var hostToJoin = [];\n    var i;\n\n    for (i = 0; i < n; i++) {\n        hostToJoin.push('127.0.0.1:' + (PORT_BASE + i));\n    }\n\n    return hostToJoin;\n}\n\nfunction waitForConvergence(context, callback) {\n    var handle = setInterval(function check() {\n        var hosts = Object.keys(context.hostToAliveWorker);\n        var i;\n\n        for (i = 1; i < hosts.length; i++) {\n            if (!context.hostToChecksum[hosts[i]] ||\n                context.hostToChecksum[hosts[i - 1]] !== context.hostToChecksum[hosts[i]]) {\n                return;\n            }\n        }\n\n        if (Object.keys(context.hostToChecksum).length >= Object.keys(context.hostToAliveWorker).length) {\n            context.hostToChecksum = Object.create(null);\n            clearInterval(handle);\n            callback();\n        }\n    }, 5);\n}\n\nfunction teardown(context, callback) {\n    Object.keys(context.hostToAliveWorker).forEach(function shutdown(host) {\n        context.hostToAliveWorker[host].send({\n            cmd: 'shutdown'\n        });\n    });\n    process.nextTick(callback);\n}\n\nfunction before(context, callback) {\n    singleNodeFailure(context, callback);\n}\n\nfunction fn(context, callback) {\n    waitForConvergence(context, callback);\n}\n\nfunction after(context, callback) {\n    Object.keys(context.hostToFaultyWorker).forEach(function join(host) {\n        context.hostToAliveWorker[host] = context.hostToFaultyWorker[host];\n        delete context.hostToFaultyWorker[host];\n        context.hostToAliveWorker[host].send({\n            cmd: 'bootstrap',\n            hosts: getHostsToJoin(Math.ceil(context.numberOfWorkers / 3))\n        });\n    });\n    waitForConvergence(context, callback);\n}\n"
  },
  {
    "path": "bench/script/dumb.js",
    "content": "#!/usr/bin/env node\n'use strict';\nvar Swim = require('../../lib/swim');\n\nvar hosts = [\n    '127.0.0.1:11111',\n    '127.0.0.1:22222',\n    '127.0.0.1:33333'\n];\nvar swim0 = new Swim({\n    local: {\n        host: hosts[0],\n        meta: {\n            node: 1\n        }\n    }\n});\n\nvar swim1 = new Swim({\n    local: {\n        host: hosts[1],\n        meta: {\n            node: 2\n        }\n    }\n});\n\nvar swim2 = new Swim({\n    local: {\n        host: hosts[2],\n        meta: {\n            node: 3\n        }\n    }\n});\n\nswim0.bootstrap([], function onBootstrap(err) {\n    console.log('swim0 bootstrap error', err);\n    swim1.bootstrap(hosts, function onBootstrap(err) {\n        console.log('swim1 bootstrap error', err);\n    });\n    swim2.bootstrap(hosts, function onBootstrap(err) {\n        console.log('swim2 bootstrap error', err);\n    });\n});\n\nsetInterval(function print() {\n    console.log('-------------------------------');\n    console.log(swim0.localhost(), swim0.members());\n    console.log(swim1.localhost(), swim1.members());\n    console.log(swim2.localhost(), swim2.members());\n}, 1000);\n\nsetTimeout(function leave() {\n    console.log('swim0 leaves');\n    swim0.leave();\n}, 1000 * 5);\n\nsetTimeout(function rejoin() {\n    console.log('swim0 rejoins');\n    swim0.bootstrap(hosts, function onBootstrap(err) {\n        console.log('swim0 bootstrap error', err);\n    });\n}, 1000 * 15);\n\nsetTimeout(function leave() {\n    console.log('swim0 leaves');\n    swim0.leave();\n}, 1000 * 20);\n\nsetTimeout(function rejoin() {\n    console.log('swim0 rejoins');\n    swim0.bootstrap(hosts, function onBootstrap(err) {\n        console.log('swim0 bootstrap error', err);\n    });\n}, 1000 * 23);\n"
  },
  {
    "path": "bench/script/worker.js",
    "content": "#!/usr/bin/env node\n'use strict';\nvar assert = require('assert');\nvar program = require('commander');\n\nvar Swim = require('../../');\n\nif (require.main === module) {\n    parseArgs();\n    bootstrap(function onBootstrap(err, swim) {\n        if (err) {\n            console.log(err);\n            process.exit(1);\n        }\n\n        handleMessage(swim);\n    });\n}\n\nfunction parseArgs() {\n    program\n    .option('--host <value>', 'host')\n    .option('--hosts-to-join [hosts]', 'hosts to join', function split(list) {\n        return list.split(',');\n    }, [])\n    .option('--codec [value]', 'msgpack or json')\n    .option('--dissemination-factor [value]', 'dissemination factor', parseInt)\n    .option('--interval [value]', 'interval', parseInt)\n    .option('--join-timeout [value]', 'join timeout', parseInt)\n    .option('--ping-timeout [value]', 'ping timeout', parseInt)\n    .option('--ping-req-timeout [value]', 'ping req timeout', parseInt)\n    .option('--ping-req-group-size [value]', 'ping req group size', parseInt)\n    .option('--max-dgram-size [value]', 'max dgram size', parseInt)\n    .parse(process.argv);\n\n    assert(/^(\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+)$/.test(program.host));\n    assert(Array.isArray(program.hostsToJoin));\n}\n\nfunction handleMessage(swim) {\n    var onMessage = function onMessage(message) {\n        switch (message.cmd) {\n            case 'bootstrap':\n                swim.bootstrap(message.hosts, function onBootstrap(err) {\n                    if (err) {\n                        console.log(err);\n                        process.exit(1);\n                    }\n\n                    process.send({\n                        type: 'checksum',\n                        host: swim.localhost(),\n                        value: swim.checksum()\n                    });\n                });\n                break;\n            case 'join':\n                swim.join(message.hosts, function onBootstrap(err) {\n                    if (err) {\n                        console.log(err);\n                        process.exit(1);\n                    }\n\n                    process.send({\n                        type: 'checksum',\n                        host: swim.localhost(),\n                        value: swim.checksum()\n                    });\n                });\n                break;\n            case 'leave':\n                swim.leave();\n                break;\n            case 'shutdown':\n                swim.leave();\n                process.removeListener('message', onMessage);\n                break;\n        }\n    };\n\n    process.on('message', onMessage);\n}\n\nfunction bootstrap(callback) {\n    var opts = {\n        local: {\n            host: program.host,\n            meta: {\n                app: 'benchmark'\n            }\n        },\n        codec: program.codec,\n        disseminationFactor: program.disseminationFactor,\n        interval: program.interval,\n        joinTimeout: program.joinTimeout,\n        pingTimeout: program.pingTimeout,\n        pingReqTimeout: program.pingReqTimeout,\n        pingReqGroupSize: program.pingReqGroupSize,\n        udp: {\n            maxDgramSize: program.maxDgramSize\n        }\n    };\n    var swim = new Swim(opts);\n\n    swim.on(Swim.EventType.Update, function onUpdate() {\n        process.send({\n            type: 'checksum',\n            host: swim.localhost(),\n            value: swim.checksum()\n        });\n    });\n\n    swim.bootstrap(program.hostsToJoin, function onBootstrap(err) {\n        if (err) {\n            return callback(err);\n        }\n\n        process.send({\n            type: 'checksum',\n            host: swim.localhost(),\n            value: swim.checksum()\n        });\n\n        process.send({\n            type: 'ready'\n        });\n\n        callback(null, swim);\n    });\n}\n"
  },
  {
    "path": "docs/api.md",
    "content": "# API Documentation\n\n## Swim object\n\n### Constructor(options)\nSee Options Object.\n\n### boostrap(hosts, callback)\nBootstrap swim instance and join cluster.\n* hosts - address:port list to connect to when joining the cluster\n* callback(err) - callback function\n\n### join(hosts, callback)\nJoin cluster.\n* hosts - address:port list to connect to when joining the cluster\n* callback(err) - callback function\n\n### leave()\nLeave cluster.\n\n### localhost()\nGet host for local node.\n\n### whoami()\nAlias to localhost().\n\n### members(hasLocal, hasFaulty)\nGet members in the cluster.\n* hasLocal - include local node\n* hasFaulty - include nodes marked as faulty\n\n### checksum()\nGet membership checksum.\n\n### updateMeta(meta)\nUpdate metadata of local node and disseminate within cluster.\n\n## Options Object\nThis object contains configuration settings.\n\n### local\nLocal Option.\n* local.host (required) - address:port for this instance, e.g. 127.0.0.1:11000\n* local.meta (optional) - metadata about this node, which will be disseminated within cluster\n\n### codec\nCodec of message payload. Default: msgpack.\n* json - https://www.json.org/\n* msgpack - https://msgpack.org/\n\n### disseminationFactor\nDissemination factor can be used to fine tune the responsiveness of the cluster.\nGreater dissemination factor results to:\n* more hosts being notified in every round of dissemination\n* lower convergence time of cluster membership\n* more/bigger network packets being sent\n\nand vice versa.\n\n### interval\nNumber of milliseconds between failure detections, also known as the protocol\ninterval. Every X milliseconds, nodes will ping a member of the SWIM network to\ncheck its liveness with Time-Bounded Strong Completeness as described in the\n[paper](http://www.cs.cornell.edu/~asdas/research/dsn02-SWIM.pdf).\n\n### joinTimeout\nNumber of milliseconds before emitting a JoinTimeout error. The node will still\nrun as a base node separate from the network.\n\n### pingTimout\nNumber of milliseconds before sending ping-req messages to the unresponsive node.\n\n### pingReqTimout\nNumber of milliseconds elapsed from sending ping-req message before marking the\nunresponsive node suspicious.\n\n### pingReqGroupSize\nNumber of hosts to send ping-req messages to for pinging unresponsive nodes\nindirectly to reduce false positives.\n\n### suspectTimeout\nNumber of milliseconds before considering a suspect node faulty.\n\n### udp\nUDP Option.\n* udp.maxDgramSize - Max size of UDP datagram. If bigger than what the network supports,\nmessages might be chunked into multiple packets and discarded at receiver end.\n\n### preferCurrentMeta\nIf set to true, current metadata of local node, instead of the copy of metadata of local node in cluster membership, will be used during conflict resolution. Defaut: false.\n"
  },
  {
    "path": "index.js",
    "content": "'use strict';\nmodule.exports = require('./lib/swim');\n"
  },
  {
    "path": "lib/codec.js",
    "content": "'use strict';\nvar assert = require('assert');\nvar msgpack = require('msgpack');\n\nfunction Codec(opts) {\n    opts = opts || {};\n\n    this.codec = opts.codec || Codec.Default.codec;\n\n    assert.strictEqual(['json', 'msgpack'].indexOf(this.codec) !== -1, true, 'unsupported codec');\n\n    if (this.codec === 'json') {\n        this.encode = function encode(obj) {\n            return new Buffer(JSON.stringify(obj));\n        };\n        this.decode = function decode(buffer) {\n            return JSON.parse(buffer.toString());\n        };\n    } else {\n        this.encode = msgpack.pack.bind(msgpack);\n        this.decode = msgpack.unpack.bind(msgpack);\n    }\n}\n\nCodec.Default = {\n    codec: 'msgpack'\n};\n\nmodule.exports = Codec;\n"
  },
  {
    "path": "lib/disseminator.js",
    "content": "'use strict';\nvar debug = require('debug');\nvar clone = require('clone');\nvar events = require('events');\nvar util = require('util');\n\nvar Membership = require('./membership');\nvar MessageType = require('./message-type');\nvar Net = require('./net');\n\nfunction Disseminator(opts) {\n    this.swim = opts.swim;\n\n    this.disseminationFactor = opts.disseminationFactor || Disseminator.Default.disseminationFactor;\n    this.disseminationFormula =  opts.disseminationFormula || Disseminator.Default.disseminationFormula;\n    this.disseminationLimit = Disseminator.Default.disseminationLimit;\n\n    this.updateListener = this.onUpdate.bind(this);\n    this.changeListener = this.onChange.bind(this);\n\n    this.attemptsToUpdates = Object.create(null);\n    this.hostToAttempts = Object.create(null);\n    this.debug = debug('swim:disseminator').bind(undefined, opts.debugIdentifier);\n}\n\nutil.inherits(Disseminator, events.EventEmitter);\n\nDisseminator.prototype.start = function start() {\n    this.swim.membership.on(Membership.EventType.Change, this.changeListener);\n    this.swim.membership.on(Membership.EventType.Update, this.updateListener);\n    this.updateDisseminationLimit();\n};\n\nDisseminator.prototype.stop = function stop() {\n    this.swim.membership.removeListener(Membership.EventType.Change, this.changeListener);\n    this.swim.membership.removeListener(Membership.EventType.Update, this.updateListener);\n};\n\nDisseminator.prototype.onChange = function onChange() {\n    this.updateDisseminationLimit();\n};\n\nDisseminator.prototype.updateDisseminationLimit = function updateDisseminationLimit() {\n    var self = this;\n\n    self.disseminationLimit = self.disseminationFormula(self.disseminationFactor, self.swim.membership.size(true));\n\n    Object.keys(self.attemptsToUpdates).forEach(function removeAttempts(attempts) {\n        if (attempts >= self.disseminationLimit) {\n            Object.keys(self.attemptsToUpdates[attempts]).forEach(function removeUpdate(host) {\n                delete self.hostToAttempts[host];\n            });\n\n            delete self.attemptsToUpdates[attempts];\n        }\n    });\n};\n\nDisseminator.prototype.onUpdate = function onUpdate(data) {\n    var update = clone(data);\n\n    update.attempts = 0;\n\n    this.removeUpdate(update);\n    this.addUpdate(update);\n};\n\nDisseminator.prototype.addUpdate = function addUpdate(update) {\n    if (update.attempts >= this.disseminationLimit) {\n        return;\n    }\n\n    if (!this.attemptsToUpdates[update.attempts]) {\n        this.attemptsToUpdates[update.attempts] = Object.create(null);\n    }\n\n    this.attemptsToUpdates[update.attempts][update.host] = update;\n    this.hostToAttempts[update.host] = update.attempts;\n};\n\nDisseminator.prototype.removeUpdate = function removeUpdate(update) {\n    if (this.hostToAttempts[update.host] >= 0) {\n        delete this.attemptsToUpdates[this.hostToAttempts[update.host]][update.host];\n    }\n\n    delete this.hostToAttempts[update.host];\n};\n\nDisseminator.prototype.getUpdatesUpTo = function getUpdatesUpTo(bytesAvailable) {\n    var self = this;\n    var buffers = [];\n    var updates = [];\n    var hostToUpdates;\n    var attempts;\n    var buffer;\n    var update;\n\n    for (attempts = 0; attempts < self.disseminationLimit; attempts++) {\n        if (bytesAvailable <= Net.MessageTypeSize) {\n            break;\n        }\n\n        hostToUpdates = self.attemptsToUpdates[attempts];\n\n        if (hostToUpdates) {\n            /* jshint loopfunc: true */\n            Object.keys(hostToUpdates).forEach(function disseminateUpdateOf(host) {\n                if (bytesAvailable <= Net.MessageTypeSize) {\n                    return;\n                }\n\n                update = hostToUpdates[host];\n                buffer = self.serializeUpdate(update);\n\n                if (buffer.length + Net.LengthSize <= bytesAvailable) {\n                    buffers.push(buffer);\n                    updates.push(update);\n                    self.removeUpdate(update);\n                    bytesAvailable -= buffer.length + Net.LengthSize;\n                }\n            });\n            /* jshint loopfunc: false */\n        }\n    }\n\n    updates.forEach(function addBack(update) {\n        update.attempts += 1;\n        self.addUpdate(update);\n    });\n\n    return buffers;\n};\n\nDisseminator.prototype.serializeUpdate = function serializeUpdate(update) {\n    var header = new Buffer(Net.MessageTypeSize);\n\n    Net.WriteMessageType.call(header, MessageType.Update, 0);\n\n    return Buffer.concat([header, this.swim.codec.encode({\n        meta: update.meta,\n        host: update.host,\n        state: update.state,\n        incarnation: update.incarnation\n    })]);\n};\n\nDisseminator.Default = {\n    disseminationFactor: 15,\n    disseminationLimit: 3,\n    disseminationFormula: function disseminationFormula(factor, size) {\n        return Math.ceil(factor * Math.log(size + 1) / Math.log(10));\n    }\n};\n\nmodule.exports = Disseminator;\n"
  },
  {
    "path": "lib/error.js",
    "content": "'use strict';\nvar util = require('util');\n\nfunction JoinFailedError(meta) {\n    Error.captureStackTrace(this, this.constructor);\n\n    Object.defineProperty(this, 'name', {\n        value: this.constructor.name\n    });\n\n    if (meta) {\n        this.meta = meta;\n    }\n}\n\nfunction InvalidStateError(meta) {\n    Error.captureStackTrace(this, this.constructor);\n\n    Object.defineProperty(this, 'name', {\n        value: this.constructor.name\n    });\n\n    if (meta) {\n        this.meta = meta;\n    }\n}\n\nfunction ListenFailedError(meta) {\n    Error.captureStackTrace(this, this.constructor);\n\n    Object.defineProperty(this, 'name', {\n        value: this.constructor.name\n    });\n\n    if (meta) {\n        this.meta = meta;\n    }\n}\n\nutil.inherits(JoinFailedError, Error);\nutil.inherits(InvalidStateError, Error);\nutil.inherits(ListenFailedError, Error);\n\nmodule.exports = {\n    JoinFailedError: JoinFailedError,\n    InvalidStateError: InvalidStateError,\n    ListenFailedError: ListenFailedError\n};\n"
  },
  {
    "path": "lib/failure-detector.js",
    "content": "'use strict';\nvar debug = require('debug');\nvar events = require('events');\nvar util = require('util');\n\nvar MessageType = require('./message-type');\nvar Net = require('./net');\n\nfunction FailureDetector(opts) {\n    this.swim = opts.swim;\n    this.interval = opts.interval || FailureDetector.Default.interval;\n    this.pingTimeout = opts.pingTimeout || FailureDetector.Default.pingTimeout;\n    this.pingReqTimeout = opts.pingReqTimeout || FailureDetector.Default.pingReqTimeout;\n    this.pingReqGroupSize = opts.pingReqGroupSize || FailureDetector.Default.pingReqGroupSize;\n\n    this.seq = 0;\n    this.pingListener = this.onPing.bind(this);\n    this.pingReqListener = this.onPingReq.bind(this);\n    this.ackListener = this.onAck.bind(this);\n\n    this.tickHandle = undefined;\n    this.seqToTimeout = Object.create(null);\n    this.seqToCallback = Object.create(null);\n    this.debug = debug('swim:failure-detector').bind(undefined, opts.debugIdentifier);\n}\n\nutil.inherits(FailureDetector, events.EventEmitter);\n\nFailureDetector.prototype.start = function start() {\n    this.swim.net.on(Net.EventType.Ping, this.pingListener);\n    this.swim.net.on(Net.EventType.PingReq, this.pingReqListener);\n    this.swim.net.on(Net.EventType.Ack, this.ackListener);\n    this.tick();\n};\n\nFailureDetector.prototype.stop = function stop() {\n    var self = this;\n\n    clearInterval(self.tickHandle);\n    self.tickHandle = undefined;\n\n    self.swim.net.removeListener(Net.EventType.Ping, self.pingListener);\n    self.swim.net.removeListener(Net.EventType.PingReq, self.pingReqListener);\n    self.swim.net.removeListener(Net.EventType.Ack, self.ackListener);\n\n    Object.keys(self.seqToTimeout).forEach(function clearTimeoutWithDeletion(seq) {\n        clearTimeout(self.seqToTimeout[seq]);\n        delete self.seqToTimeout[seq];\n    });\n\n    Object.keys(self.seqToCallback).forEach(function clearCallback(seq) {\n        delete self.seqToCallback[seq];\n    });\n};\n\nFailureDetector.prototype.tick = function tick() {\n    setImmediate(this.ping.bind(this));\n    this.tickHandle = setInterval(this.ping.bind(this), this.interval);\n};\n\nFailureDetector.prototype.ping = function ping() {\n    this.pingMember(this.swim.membership.next());\n};\n\nFailureDetector.prototype.pingMember = function pingMember(member) {\n    var self = this;\n    var seq = self.seq;\n\n    if (!member) {\n        return;\n    }\n\n    self.seq += 1;\n\n    self.seqToTimeout[seq] = setTimeout(function receiveTimeout() {\n        self.clearSeq(seq);\n        self.pingReq(member);\n    }, self.pingTimeout);\n\n    self.swim.net.sendMessage({\n        type: MessageType.Ping,\n        data: {\n            seq: seq\n        }\n    }, member.host);\n};\n\nFailureDetector.prototype.pingReq = function pingReq(member) {\n    var self = this;\n    var relayMembers = self.swim.membership.random(self.pingReqGroupSize);\n    var timeout;\n\n    if (relayMembers.length === 0) {\n        return;\n    }\n\n    timeout = setTimeout(function pingReqTimeout() {\n        self.emit(FailureDetector.EventType.Suspect, member);\n    }, self.pingReqTimeout);\n\n    relayMembers.forEach(function pingThrough(relayMember) {\n        self.pingReqThroughMember(member, relayMember, function pingReqThroughMemberCallback() {\n            clearTimeout(timeout);\n        });\n    });\n};\n\nFailureDetector.prototype.pingReqThroughMember = function pingReqThroughMember(member, relayMember, callback) {\n    var self = this;\n    var seq = self.seq;\n\n    self.seq += 1;\n\n    self.seqToTimeout[seq] = setTimeout(function receiveTimeout() {\n        self.clearSeq(seq);\n    }, self.pingReqTimeout);\n\n    self.seqToCallback[seq] = function pingReqAckReceiveCallback() {\n        self.clearSeq(seq);\n        callback.apply(undefined, arguments);\n    };\n\n    self.swim.net.sendMessage({\n        type: MessageType.PingReq,\n        data: {\n            seq: seq,\n            destination: member.host\n        }\n    }, relayMember.host);\n};\n\nFailureDetector.prototype.onPing = function onPing(data, host) {\n    this.swim.net.sendMessage({\n        type: MessageType.Ack,\n        data: {\n            seq: data.seq\n        }\n    }, host);\n};\n\nFailureDetector.prototype.onPingReq = function onPingReq(data, host) {\n    var self = this;\n    var seq = self.seq;\n\n    self.seq += 1;\n\n    self.seqToTimeout[seq] = setTimeout(function receiveTimeout() {\n        self.clearSeq(seq);\n    }, self.pingTimeout);\n\n    self.seqToCallback[seq] = function pingAckReceiveCallback() {\n        self.clearSeq(seq);\n        self.swim.net.sendMessage({\n            type: MessageType.Ack,\n            data: {\n                seq: data.seq\n            }\n        }, host);\n    };\n\n    self.swim.net.sendMessage({\n        type: MessageType.Ping,\n        data: {\n            seq: seq\n        }\n    }, data.destination);\n};\n\nFailureDetector.prototype.onAck = function onAck(data) {\n    var callback = this.seqToCallback[data.seq];\n\n    if (callback) {\n        process.nextTick(callback);\n    }\n\n    this.clearSeq(data.seq);\n};\n\nFailureDetector.prototype.clearSeq = function clearSeq(seq) {\n    clearTimeout(this.seqToTimeout[seq]);\n    delete this.seqToCallback[seq];\n    delete this.seqToTimeout[seq];\n};\n\nFailureDetector.Default = {\n    interval: 20,\n    pingTimeout: 4,\n    pingReqTimeout: 12,\n    pingReqGroupSize: 3\n};\n\nFailureDetector.EventType = {\n    Suspect: 'suspect'\n};\n\nmodule.exports = FailureDetector;\n"
  },
  {
    "path": "lib/member.js",
    "content": "'use strict';\nvar clone = require('clone');\n\nfunction Member(opts) {\n    this.meta = opts.meta || undefined;\n    this.host = opts.host;\n    this.state = opts.state || Member.State.Alive;\n    this.incarnation = opts.incarnation || 0;\n}\n\nMember.prototype.data = function data() {\n    return clone({\n        meta: this.meta,\n        host: this.host,\n        state: this.state,\n        incarnation: this.incarnation\n    });\n};\n\nMember.prototype.incarnate = function incarnate(data, force, preferCurrentMeta) {\n    if (!data) {\n        this.incarnation += 1;\n        return true;\n    }\n\n    if (data.incarnation > this.incarnation) {\n        if (!preferCurrentMeta) {\n            this.meta = data.meta;\n        }\n        this.incarnation = data.incarnation + 1;\n        return true;\n    }\n\n    if (data.incarnation === this.incarnation && force) {\n        this.incarnation = data.incarnation + 1;\n        return true;\n    }\n\n    return false;\n};\n\nMember.State = {\n    Alive: 0,\n    Suspect: 1,\n    Faulty: 2\n};\n\nmodule.exports = Member;\n"
  },
  {
    "path": "lib/membership.js",
    "content": "'use strict';\nvar debug = require('debug');\nvar events = require('events');\nvar farmhash = require('farmhash');\nvar util = require('util');\n\nvar FailureDetector = require('./failure-detector');\nvar Member = require('./member');\nvar MessageType = require('./message-type');\nvar Net = require('./net');\n\nfunction Membership(opts) {\n    this.swim = opts.swim;\n    this.local = new Member(opts.local);\n    this.suspectTimeout = opts.suspectTimeout || Membership.Default.suspectTimeout;\n    this.preferCurrentMeta = opts.preferCurrentMeta || Membership.Default.preferCurrentMeta;\n\n    this.ackListener = this.onAck.bind(this);\n    this.updateListener = this.onUpdate.bind(this);\n    this.suspectListener = this.onSuspect.bind(this);\n    this.syncListener = this.onSync.bind(this);\n\n    this.hostToMember = Object.create(null);\n    this.hostToIterable = Object.create(null);\n    this.hostToFaulty = Object.create(null);\n    this.hostToSuspectTimeout = Object.create(null);\n    this.debug = debug('swim:membership').bind(undefined, opts.debugIdentifier);\n}\n\nutil.inherits(Membership, events.EventEmitter);\n\nMembership.prototype.start = function start() {\n    var self = this;\n\n    self.swim.failureDetector.on(FailureDetector.EventType.Suspect, self.suspectListener);\n    self.swim.net.on(Net.EventType.Ack, self.ackListener);\n    self.swim.net.on(Net.EventType.Sync, self.syncListener);\n    self.swim.net.on(Net.EventType.Update, self.updateListener);\n\n    Object.keys(self.hostToSuspectTimeout).forEach(function resumeSuspect(host) {\n        self.onSuspect(self.get(host));\n    });\n};\n\nMembership.prototype.stop = function stop() {\n    var self = this;\n\n    self.swim.failureDetector.removeListener(FailureDetector.EventType.Suspect, self.suspectListener);\n    self.swim.net.removeListener(Net.EventType.Ack, self.ackListener);\n    self.swim.net.removeListener(Net.EventType.Sync, self.syncListener);\n    self.swim.net.removeListener(Net.EventType.Update, self.updateListener);\n\n    Object.keys(self.hostToSuspectTimeout).forEach(function clearTimeoutWithoutDeletion(host) {\n        clearTimeout(self.hostToSuspectTimeout[host]);\n    });\n};\n\nMembership.prototype.onAck = function onAck(data, host) {\n    if (this.hostToMember[host] && this.hostToMember[host].state === Member.State.Suspect) {\n        this.swim.net.sendMessage({\n            type: MessageType.Update,\n            data: this.hostToMember[host].data()\n        }, host);\n    }\n};\n\nMembership.prototype.onSuspect = function onSuspect(member) {\n    var self = this;\n    var data;\n\n    member = new Member(member.data());\n    member.state = Member.State.Suspect;\n    data = member.data();\n\n    clearTimeout(self.hostToSuspectTimeout[data.host]);\n    delete self.hostToSuspectTimeout[data.host];\n\n    self.hostToSuspectTimeout[data.host] = setTimeout(function setFaulty() {\n        delete self.hostToSuspectTimeout[data.host];\n\n        data.state = Member.State.Faulty;\n\n        self.onUpdate(data);\n    }, self.suspectTimeout);\n\n    self.onUpdate(member.data());\n};\n\nMembership.prototype.onSync = function onSync(data) {\n    var host = data.host;\n\n    this.updateAlive(data);\n\n    this.swim.net.sendMessages(this.all(true, true).map(function toMessage(data) {\n        return {\n            type: MessageType.Update,\n            data: data\n        };\n    }), host);\n};\n\nMembership.prototype.sync = function sync(hosts) {\n    var self = this;\n    var messages = [{\n        type: MessageType.Sync,\n        data: self.local.data()\n    }];\n\n    this.all(false, true).forEach(function addMessage(data) {\n        messages.push({\n            type: MessageType.Update,\n            data: data\n        });\n    });\n\n    hosts.forEach(function sendToHost(host) {\n        self.swim.net.sendMessages(messages, host);\n    });\n};\n\nMembership.prototype.onUpdate = function onUpdate(data) {\n    this.debug('received update', data);\n\n    switch (data.state) {\n        case Member.State.Alive:\n            this.updateAlive(data);\n            break;\n        case Member.State.Suspect:\n            this.updateSuspect(data);\n            break;\n        case Member.State.Faulty:\n            this.updateFaulty(data);\n            break;\n    }\n};\n\nMembership.prototype.updateAlive = function updateAlive(data) {\n    if (this.isLocal(data.host)) {\n        if (this.local.incarnate(data, false, this.preferCurrentMeta)) {\n            this.emit(Membership.EventType.Update, this.local.data());\n        } else {\n            this.emit(Membership.EventType.Drop, data);\n        }\n        return;\n    }\n\n    if (this.hostToFaulty[data.host] && this.hostToFaulty[data.host].incarnation >= data.incarnation) {\n        this.emit(Membership.EventType.Drop, data);\n        return;\n    }\n\n    if (!this.hostToMember[data.host] ||\n        data.incarnation > this.hostToMember[data.host].incarnation) {\n\n        clearTimeout(this.hostToSuspectTimeout[data.host]);\n        delete this.hostToSuspectTimeout[data.host];\n        delete this.hostToFaulty[data.host];\n\n        if (!this.hostToMember[data.host]) {\n            this.hostToMember[data.host] = new Member(data);\n            this.hostToIterable[data.host] = this.hostToMember[data.host];\n            this.emit(Membership.EventType.Change, this.hostToMember[data.host].data());\n        } else {\n            this.hostToMember[data.host] = new Member(data);\n        }\n\n        this.emit(Membership.EventType.Update, this.hostToMember[data.host].data());\n    } else {\n        this.emit(Membership.EventType.Drop, data);\n    }\n};\n\nMembership.prototype.updateSuspect = function updateSuspect(data) {\n    if (this.isLocal(data.host)) {\n        this.emit(Membership.EventType.Drop, data);\n        this.local.incarnate(data, true, this.preferCurrentMeta);\n        this.emit(Membership.EventType.Update, this.local.data());\n        return;\n    }\n\n    if (this.hostToFaulty[data.host] && this.hostToFaulty[data.host].incarnation >= data.incarnation) {\n        this.emit(Membership.EventType.Drop, data);\n        return;\n    }\n\n    if (!this.hostToMember[data.host] ||\n        data.incarnation > this.hostToMember[data.host].incarnation ||\n        data.incarnation === this.hostToMember[data.host].incarnation &&\n        this.hostToMember[data.host].state === Member.State.Alive) {\n\n        delete this.hostToFaulty[data.host];\n\n        if (!this.hostToMember[data.host]) {\n            this.hostToMember[data.host] = new Member(data);\n            this.hostToIterable[data.host] = this.hostToMember[data.host];\n            this.emit(Membership.EventType.Change, this.hostToMember[data.host].data());\n        } else {\n            this.hostToMember[data.host] = new Member(data);\n        }\n\n        this.emit(Membership.EventType.Update, this.hostToMember[data.host].data());\n    } else {\n        this.emit(Membership.EventType.Drop, data);\n    }\n};\n\nMembership.prototype.updateFaulty = function updateFaulty(data) {\n    if (this.isLocal(data.host)) {\n        this.emit(Membership.EventType.Drop, data);\n        this.local.incarnate(data, true, this.preferCurrentMeta);\n        this.emit(Membership.EventType.Update, this.local.data());\n        return;\n    }\n\n    if (this.hostToMember[data.host] &&\n        data.incarnation >= this.hostToMember[data.host].incarnation) {\n\n        this.hostToFaulty[data.host] = new Member(data);\n        delete this.hostToMember[data.host];\n        delete this.hostToIterable[data.host];\n        this.emit(Membership.EventType.Change, data);\n\n        this.emit(Membership.EventType.Update, data);\n    } else {\n        this.emit(Membership.EventType.Drop, data);\n    }\n};\n\nMembership.prototype.next = function next() {\n    var hosts = Object.keys(this.hostToIterable);\n    var host;\n    var member;\n\n    if (hosts.length === 0) {\n        this.shuffle();\n        hosts = Object.keys(this.hostToIterable);\n    }\n\n    host = hosts[Math.floor(Math.random() * hosts.length)];\n    member = this.hostToIterable[host];\n    delete this.hostToIterable[host];\n\n    return member;\n};\n\nMembership.prototype.random = function random(n) {\n    var hosts = Object.keys(this.hostToMember);\n    var selected = [];\n    var index;\n    var i;\n\n    for (i = 0; i < n && i < hosts.length; i++) {\n        index = i + Math.floor(Math.random() * (hosts.length - i));\n        selected.push(this.hostToMember[hosts[index]]);\n        hosts[index] = hosts[i];\n    }\n\n    return selected;\n};\n\nMembership.prototype.shuffle = function shuffle() {\n    var self = this;\n\n    self.hostToIterable = Object.create(null);\n\n    Object.keys(self.hostToMember).forEach(function addToIterable(host) {\n        self.hostToIterable[host] = self.hostToMember[host];\n    });\n};\n\nMembership.prototype.get = function get(host) {\n    return this.hostToMember[host];\n};\n\nMembership.prototype.size = function size(hasLocal) {\n    return Object.keys(this.hostToMember).length + (hasLocal ? 1 : 0);\n};\n\nMembership.prototype.all = function all(hasLocal, hasFaulty) {\n    var self = this;\n    var results = Object.keys(self.hostToMember).map(function toData(host) {\n        return self.hostToMember[host].data();\n    });\n\n    if (hasLocal) {\n        results.push(self.local.data());\n    }\n\n    if (hasFaulty) {\n        Object.keys(self.hostToFaulty).forEach(function toData(host) {\n            results.push(self.hostToFaulty[host].data());\n        });\n    }\n\n    return results;\n};\n\nMembership.prototype.checksum = function checksum() {\n    var self = this;\n    var strs = self.all(true).sort(function compare(a, b) {\n        return parseInt(a.host.split(':')[1]) - parseInt(b.host.split(':')[1]);\n    }).map(function toString(member) {\n        return member.host + member.state + member.incarnation;\n    });\n\n    return farmhash.hash64(strs.join('-'));\n};\n\nMembership.prototype.isLocal = function isLocal(host) {\n    return host === this.local.host;\n};\n\nMembership.prototype.localhost = function localhost() {\n    return this.local.host;\n};\n\nMembership.prototype.updateMeta = function updateMeta(meta) {\n    this.local.meta = meta;\n    this.local.incarnate();\n    this.emit(Membership.EventType.Update, this.local.data());\n};\n\nMembership.Default = {\n    suspectTimeout: 10,\n    preferCurrentMeta: false\n};\n\nMembership.EventType = {\n    Change: 'change',\n    Drop: 'drop',\n    Update: 'update'\n};\n\nmodule.exports = Membership;\n"
  },
  {
    "path": "lib/message-type.js",
    "content": "'use strict';\n\nmodule.exports = {\n    Compound: 0,\n    Compressed: 1,\n    Encrypted: 2,\n    Ping: 10,\n    PingReq: 11,\n    Sync: 12,\n    Ack: 13,\n    Update: 14\n};\n"
  },
  {
    "path": "lib/net.js",
    "content": "'use strict';\nvar debug = require('debug');\nvar dgram = require('dgram');\nvar events = require('events');\nvar util = require('util');\n\nvar MessageType = require('./message-type');\n\nfunction Net(opts) {\n    this.swim = opts.swim;\n    this.udp = {\n        port: opts.udp.port,\n        type: opts.udp.type || Net.Default.udp.type,\n        maxDgramSize: opts.udp.maxDgramSize || Net.Default.udp.maxDgramSize\n    };\n\n    this.errorListener = this.emit.bind(this, Net.EventType.Error);\n    this.listeningListener = this.emit.bind(this, Net.EventType.Listening);\n    this.messageListener = this.onNetMessage.bind(this);\n\n    this.udpSocket = dgram.createSocket(this.udp.type);\n    this.debug = debug('swim:net').bind(undefined, opts.debugIdentifier);\n}\n\nutil.inherits(Net, events.EventEmitter);\n\nNet.prototype.listen = function listen(callback) {\n    this.udpSocket.on('error', this.errorListener);\n    this.udpSocket.on('listening', this.listeningListener);\n    this.udpSocket.on('message', this.messageListener);\n    this.udpSocket.bind(this.udp.port, callback);\n};\n\nNet.prototype.close = function close() {\n    this.udpSocket.removeListener('error', this.errorListener);\n    this.udpSocket.removeListener('listening', this.listeningListener);\n    this.udpSocket.removeListener('message', this.messageListener);\n    this.udpSocket.close();\n    this.udpSocket = dgram.createSocket(this.udp.type);\n};\n\nNet.prototype.onNetMessage = function onNetMessage(buffer, rinfo) {\n    this.debug('received buffer', {\n        from: formatRinfo(rinfo),\n        length: buffer.length\n    });\n\n    this.onMessage(buffer, rinfo);\n};\n\nNet.prototype.onMessage = function onMessage(buffer, rinfo) {\n    if (buffer.length < Net.MessageTypeSize) {\n        return this.onUnknown(buffer, rinfo);\n    }\n\n    var messageType = Net.ReadMessageType.call(buffer, 0);\n\n    switch (messageType) {\n        case MessageType.Compound:\n            this.onCompound(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        case MessageType.Ping:\n            this.onPing(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        case MessageType.PingReq:\n            this.onPingReq(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        case MessageType.Sync:\n            this.onSync(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        case MessageType.Ack:\n            this.onAck(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        case MessageType.Update:\n            this.onUpdate(buffer.slice(Net.MessageTypeSize), rinfo);\n            break;\n        default:\n            this.onUnknown(buffer, rinfo);\n    }\n};\n\nNet.prototype.onCompound = function onCompound(buffer, rinfo) {\n    this.debug('received compound message');\n\n    if (buffer.length < Net.LengthSize) {\n        this.debug('cannot parse number of messages in compound message', {\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    var numberOfMessages = Net.ReadLength.call(buffer, 0);\n    var readIndex = Net.LengthSize;\n    var length;\n    var i;\n\n    for (i = 0; i < numberOfMessages; i++) {\n        if (buffer.length - readIndex < Net.LengthSize) {\n            this.debug('cannot parse length of message in compound message', {\n                readIndex: readIndex,\n                from: formatRinfo(rinfo),\n                length: buffer.length,\n                buffer: buffer.toString()\n            });\n            break;\n        }\n\n        length = Net.ReadLength.call(buffer, readIndex);\n        readIndex += Net.LengthSize;\n        this.onMessage(buffer.slice(readIndex, readIndex + length), rinfo);\n        readIndex += length;\n    }\n};\n\nNet.prototype.onPing = function onPing(buffer, rinfo) {\n    var data;\n\n    try {\n        data = this.swim.codec.decode(buffer);\n    } catch (e) {\n        this.debug('failed to decode data', {\n            stack: e.stack,\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    this.debug('received ping message', {\n        from: formatRinfo(rinfo),\n        data: data\n    });\n\n    this.emit(Net.EventType.Ping, data, formatRinfo(rinfo));\n};\n\nNet.prototype.onPingReq = function onPingReq(buffer, rinfo) {\n    var data;\n\n    try {\n        data = this.swim.codec.decode(buffer);\n    } catch (e) {\n        this.debug('failed to decode data', {\n            stack: e.stack,\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    this.debug('received pingreq message', {\n        from: formatRinfo(rinfo),\n        data: data\n    });\n\n    this.emit(Net.EventType.PingReq, data, formatRinfo(rinfo));\n};\n\nNet.prototype.onSync = function onSync(buffer, rinfo) {\n    var data;\n\n    try {\n        data = this.swim.codec.decode(buffer);\n    } catch (e) {\n        this.debug('failed to decode data', {\n            stack: e.stack,\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    this.debug('received sync message', {\n        from: formatRinfo(rinfo),\n        data: data\n    });\n\n    this.emit(Net.EventType.Sync, data, formatRinfo(rinfo));\n};\n\nNet.prototype.onAck = function onAck(buffer, rinfo) {\n    var data;\n\n    try {\n        data = this.swim.codec.decode(buffer);\n    } catch (e) {\n        this.debug('failed to decode data', {\n            stack: e.stack,\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    this.debug('received ack message', {\n        from: formatRinfo(rinfo),\n        data: data\n    });\n\n    this.emit(Net.EventType.Ack, data, formatRinfo(rinfo));\n};\n\nNet.prototype.onUpdate = function onUpdate(buffer, rinfo) {\n    var data;\n\n    try {\n        data = this.swim.codec.decode(buffer);\n    } catch (e) {\n        this.debug('failed to decode data', {\n            stack: e.stack,\n            from: formatRinfo(rinfo),\n            length: buffer.length,\n            buffer: buffer.toString()\n        });\n        return;\n    }\n\n    this.debug('received update message', {\n        from: formatRinfo(rinfo),\n        data: data\n    });\n\n    this.emit(Net.EventType.Update, data, formatRinfo(rinfo));\n};\n\nNet.prototype.onUnknown = function onUnknown(buffer, rinfo) {\n    this.debug('received unknown buffer', {\n        from: formatRinfo(rinfo),\n        buffer: buffer.toString()\n    });\n\n    this.emit(Net.EventType.Unknown, buffer, formatRinfo(rinfo));\n};\n\nNet.prototype.sendMessages = function sendMessages(messages, host) {\n    var self = this;\n    var bytesAvailable = self.udp.maxDgramSize - Net.MessageTypeSize - Net.LengthSize;\n    var buffers = [];\n    var buffer;\n    var message;\n    var i;\n\n    for (i = 0; i < messages.length; i++) {\n        message = messages[i];\n        buffer = new Buffer(Net.MessageTypeSize);\n        Net.WriteMessageType.call(buffer, message.type, 0);\n        buffer = Buffer.concat([buffer, self.swim.codec.encode(message.data)]);\n\n        if (buffer.length + Net.LengthSize < bytesAvailable) {\n            buffers.push(buffer);\n            bytesAvailable -= buffer.length + Net.LengthSize;\n        } else if (buffers.length === 0) {\n            this.debug('oversized message', {\n                length: buffer.length,\n                message: message\n            });\n        } else {\n            self.sendBuffer(self.makeCompoundMessage(buffers), host);\n            bytesAvailable = self.udp.maxDgramSize - Net.LengthSize;\n            buffers = [];\n            i--;\n        }\n    }\n\n    if (buffers.length > 0) {\n        self.sendBuffer(self.makeCompoundMessage(buffers), host);\n    }\n};\n\nNet.prototype.sendMessage = function sendMessage(message, host) {\n    var header = new Buffer(Net.MessageTypeSize);\n\n    Net.WriteMessageType.call(header, message.type, 0);\n\n    if (message.data) {\n        this.piggybackAndSend(Buffer.concat([header, this.swim.codec.encode(message.data)]), host);\n    } else {\n        this.piggybackAndSend(header, host);\n    }\n};\n\nNet.prototype.piggybackAndSend = function piggybackAndSend(buffer, host) {\n    var bytesAvailable = this.udp.maxDgramSize - Net.MessageTypeSize - Net.LengthSize * 2 - buffer.length;\n    var buffers = this.swim.disseminator.getUpdatesUpTo(bytesAvailable);\n\n    if (buffers.length === 0) {\n        return this.sendBuffer(buffer, host);\n    }\n\n    buffers.unshift(buffer);\n\n    this.sendBuffer(this.makeCompoundMessage(buffers), host);\n};\n\nNet.prototype.makeCompoundMessage = function makeCompoundMessage(buffers) {\n    var header = new Buffer(Net.MessageTypeSize + Net.LengthSize);\n\n    Net.WriteMessageType.call(header, MessageType.Compound, 0);\n    Net.WriteLength.call(header, buffers.length, Net.MessageTypeSize);\n\n    buffers = buffers.map(function toBuffer(buffer) {\n        var header = new Buffer(Net.LengthSize);\n\n        Net.WriteLength.call(header, buffer.length, 0);\n\n        return Buffer.concat([header, buffer]);\n    });\n\n    buffers.unshift(header);\n\n    return Buffer.concat(buffers);\n};\n\nNet.prototype.sendBuffer = function sendBuffer(buffer, host) {\n    var address = host.split(':')[0];\n    var port = host.split(':')[1];\n\n    this.udpSocket.send(buffer, 0, buffer.length, port, address);\n    this.debug('sent buffer', {\n        to: host,\n        length: buffer.length\n    });\n};\n\nNet.MessageTypeSize = 1;\nNet.LengthSize = 2;\nNet.ReadMessageType = Buffer.prototype.readUInt8;\nNet.ReadLength = Buffer.prototype.readUInt16LE;\nNet.WriteMessageType = Buffer.prototype.writeUInt8;\nNet.WriteLength = Buffer.prototype.writeUInt16LE;\n\nNet.Default = {\n    udp: {\n        type: 'udp4',\n        maxDgramSize: 512\n    }\n};\n\nNet.EventType = {\n    Error: 'error',\n    Listening: 'listening',\n    Ping: 'ping',\n    PingReq: 'pingreq',\n    Sync: 'sync',\n    Ack: 'ack',\n    Update: 'update',\n    Unknown: 'unknown'\n};\n\nfunction formatRinfo(rinfo) {\n    return rinfo.address + ':' + rinfo.port;\n}\n\nmodule.exports = Net;\n"
  },
  {
    "path": "lib/swim.js",
    "content": "'use strict';\nvar debug = require('debug');\nvar events = require('events');\nvar util = require('util');\n\nvar Codec = require('./codec');\nvar Disseminator = require('./disseminator');\nvar FailureDetector = require('./failure-detector');\nvar Membership = require('./membership');\nvar Net = require('./net');\n\nvar JoinFailedError = require('./error').JoinFailedError;\nvar InvalidStateError = require('./error').InvalidStateError;\nvar ListenFailedError = require('./error').ListenFailedError;\n\nfunction Swim(opts) {\n    this.opts = opts;\n\n    this.codec = new Codec({\n        codec: opts.codec,\n        swim: this,\n        debugIdentifier: opts.local.host\n    });\n    this.disseminator = new Disseminator({\n        disseminationFactor: opts.disseminationFactor,\n        swim: this,\n        debugIdentifier: opts.local.host\n    });\n    this.failureDetector = new FailureDetector({\n        interval: opts.interval,\n        pingTimeout: opts.pingTimeout,\n        pingReqTimeout: opts.pingReqTimeout,\n        pingReqGroupSize: opts.pingReqGroupSize,\n        swim: this,\n        debugIdentifier: opts.local.host\n    });\n    this.membership = new Membership({\n        local: opts.local,\n        suspectTimeout: opts.suspectTimeout,\n        preferCurrentMeta: opts.preferCurrentMeta,\n        swim: this,\n        debugIdentifier: opts.local.host\n    });\n    this.net = new Net({\n        udp: {\n            port: parseInt(opts.local.host.split(':')[1]),\n            type: opts.udp && opts.udp.type,\n            maxDgramSize: opts.udp && opts.udp.maxDgramSize\n        },\n        swim: this,\n        debugIdentifier: opts.local.host\n    });\n    this.state = Swim.State.Stopped;\n\n    this.joinTimeout = opts.joinTimeout || Swim.Default.joinTimeout;\n    this.joinCheckInterval = opts.joinCheckInterval || Swim.Default.joinCheckInterval;\n\n    this.changeListener = this.emit.bind(this, Swim.EventType.Change);\n    this.updateListener = this.emit.bind(this, Swim.EventType.Update);\n    this.debug = debug('swim').bind(undefined, opts.local.host);\n}\n\nutil.inherits(Swim, events.EventEmitter);\n\nSwim.prototype.bootstrap = function bootstrap(hosts, callback) {\n    var self = this;\n    var err;\n\n    if (self.state !== Swim.State.Stopped) {\n        err = new InvalidStateError({\n            current: self.state,\n            expected: Swim.State.Stopped\n        });\n\n        if (typeof callback === 'function') {\n            callback(err);\n        } else {\n            self.emit(Swim.EventType.Error, err);\n        }\n\n        return;\n    }\n\n    self.net.listen(function onListen(err) {\n        if (err) {\n            err = new ListenFailedError({\n                host: self.opts.local.host,\n                type: self.opts.udp.type\n            });\n\n            if (typeof callback === 'function') {\n                callback(err);\n            } else {\n                self.emit(Swim.EventType.Error, err);\n            }\n\n            return;\n        }\n\n        self.failureDetector.start();\n        self.membership.start();\n        self.disseminator.start();\n        self.membership.on(Membership.EventType.Change, self.changeListener);\n        self.membership.on(Membership.EventType.Update, self.updateListener);\n\n        self.state = Swim.State.Started;\n        self.join(hosts, callback);\n    });\n};\n\nSwim.prototype.join = function join(hosts, callback) {\n    var self = this;\n    var err;\n\n    if (self.state !== Swim.State.Started) {\n        err = new InvalidStateError({\n            currect: self.state,\n            expected: Swim.State.Started\n        });\n\n        if (typeof callback === 'function') {\n            callback(err);\n        } else {\n            self.emit(Swim.EventType.Error, err);\n        }\n    }\n\n    if (!hosts || hosts.length === 0) {\n        if (typeof callback === 'function') {\n            callback();\n        } else {\n            self.emit(Swim.EventType.Ready);\n        }\n\n        return;\n    }\n\n    hosts = hosts.filter(function notLocal(host) {\n        return host !== self.opts.local.host;\n    });\n\n    self.membership.sync(hosts);\n\n    var checker = null;\n\n    var timeout = setTimeout(function joinTimeout() {\n        clearInterval(checker);\n        var numberOfHostsResponded = checkJoin();\n        if (numberOfHostsResponded === 0) {\n            var err = new JoinFailedError({\n                local: self.localhost(),\n                hosts: hosts,\n                numberOfHostsResponded: numberOfHostsResponded,\n                timeout: self.joinTimeout\n            });\n            if (typeof callback === 'function') {\n                callback(err);\n            } else {\n                self.emit(Swim.EventType.Error, err);\n            }\n        }\n    }, self.joinTimeout);\n\n    if (self.joinCheckInterval < self.joinTimeout) {\n        checker = setInterval(checkJoin, self.joinCheckInterval);\n    }\n\n    function checkJoin() {\n        var numberOfHostsResponded = hosts.reduce(function countRespondedHosts(num, host) {\n            num += self.membership.get(host) ? 1 : 0;\n            return num;\n        }, 0);\n        if (numberOfHostsResponded >= 1) {\n            clearInterval(checker);\n            clearTimeout(timeout);\n            if (typeof callback === 'function') {\n                callback();\n            } else {\n                self.emit(Swim.EventType.Ready);\n            }\n        }\n        return numberOfHostsResponded;\n    }\n};\n\nSwim.prototype.leave = function leave() {\n    this.membership.removeListener(Membership.EventType.Update, this.updateListener);\n    this.membership.removeListener(Membership.EventType.Change, this.changeListener);\n    this.disseminator.stop();\n    this.membership.stop();\n    this.failureDetector.stop();\n    this.net.close();\n    this.state = Swim.State.Stopped;\n};\n\nSwim.prototype.members = function members(hasLocal, hasFaulty) {\n    return this.membership.all(hasLocal, hasFaulty);\n};\n\nSwim.prototype.checksum = function checksum() {\n    return this.membership.checksum();\n};\n\nSwim.prototype.localhost = function localhost() {\n    return this.membership && this.membership.localhost() || this.opts.local.host;\n};\n\nSwim.prototype.whoami = function whoami() {\n    return this.localhost();\n};\n\nSwim.prototype.updateMeta = function updateMeta(meta) {\n    return this.membership.updateMeta(meta);\n};\n\nSwim.Default = {\n    joinTimeout: 300,\n    joinCheckInterval: 50\n};\n\nSwim.EventType = {\n    Change: 'change',\n    Update: 'update',\n    Error: 'error',\n    Ready: 'ready'\n};\n\nSwim.State = {\n    Started: 'started',\n    Stopped: 'stopped'\n};\n\nmodule.exports = Swim;\nmodule.exports.Error = require('./error');\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"swim\",\n  \"version\": \"0.6.0\",\n  \"description\": \"Gossip protocol based on SWIM\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"cover\": \"covert test/index -q\",\n    \"lint\": \"jshint . --exclude-path=./.gitignore && jscs .\",\n    \"test\": \"npm run lint && node test/index | tap-spec\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/mrhooray/swim-js.git\"\n  },\n  \"keywords\": [\n    \"gossip\",\n    \"swim\",\n    \"failure\",\n    \"detection\",\n    \"membership\"\n  ],\n  \"author\": \"Rui Hu <code@mrhooray.com>\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/mrhooray/swim-js/issues\"\n  },\n  \"homepage\": \"https://github.com/mrhooray/swim-js\",\n  \"devDependencies\": {\n    \"async\": \"^0.9.0\",\n    \"covert\": \"^1.0.1\",\n    \"metrics\": \"^0.1.9\",\n    \"jscs\": \"^1.10.0\",\n    \"jshint\": \"^2.6.0\",\n    \"tap-spec\": \"^2.2.0\",\n    \"tape\": \"^3.4.0\"\n  },\n  \"dependencies\": {\n    \"clone\": \"^1.0.2\",\n    \"commander\": \"^2.6.0\",\n    \"debug\": \"^2.1.1\",\n    \"farmhash\": \"^2.0.0\",\n    \"msgpack\": \"^1.0.2\"\n  }\n}\n"
  },
  {
    "path": "test/codec.js",
    "content": "'use strict';\nvar test = require('tape');\n\nvar Codec = require('../lib/codec');\n\ntest('Codec constructs a codec instance', function t(assert) {\n    assert.strictEqual(new Codec() instanceof Codec, true);\n    assert.end();\n});\n\ntest('Codec supports json or msgpack', function t(assert) {\n    assert.doesNotThrow(Codec.bind({}, {codec: 'json'}));\n    assert.doesNotThrow(Codec.bind({}, {codec: 'msgpack'}));\n    assert.throws(Codec.bind({}, {codec: 'protobuf'}));\n    assert.end();\n});\n\ntest('Codec can decode data encoded by same codec', function t(assert) {\n    var data = {foo: 'bar'};\n    var jsonCodec = new Codec({codec: 'json'});\n    var msgpackCodec = new Codec({codec: 'msgpack'});\n\n    assert.deepEqual(jsonCodec.decode(jsonCodec.encode(data)), data);\n    assert.deepEqual(msgpackCodec.decode(msgpackCodec.encode(data)), data);\n    assert.end();\n});\n"
  },
  {
    "path": "test/disseminator.js",
    "content": "'use strict';\nvar events = require('events');\nvar test = require('tape');\n\nvar Codec = require('../lib/codec');\nvar Disseminator = require('../lib/disseminator');\nvar Membership = require('../lib/membership');\nvar MessageType = require('../lib/message-type');\nvar Net = require('../lib/net');\n\nvar codec = new Codec();\n\ntest('Disseminator honors bytesAvailable, dissemination limit and priority', function t(assert) {\n    var membership = new events.EventEmitter();\n    var disseminator = new Disseminator({\n        swim: {\n            codec: codec,\n            membership: membership\n        }\n    });\n    var minDgramSize = Math.pow(2, 6);\n    var maxDgramSize = Math.pow(2, 9);\n    var numberOfUpdates = Math.ceil(Math.random() * 5);\n    var numberOfMembers = Math.ceil(Math.random() * 3);\n    var numberOfBuffers = 0;\n    var hostToCount = Object.create(null);\n    var buffers;\n    var bytesAvailable;\n    var update;\n    var length;\n    var i;\n\n    membership.size = function size() {\n        return numberOfMembers;\n    };\n\n    disseminator.start();\n\n    buffers = disseminator.getUpdatesUpTo(Infinity);\n    assert.strictEqual(buffers.length, 0);\n\n    for (i = 0; i < numberOfUpdates; i++) {\n        membership.emit(Membership.EventType.Update, {\n            host: 'localhost:' + i\n        });\n    }\n\n    bytesAvailable = Math.ceil(Math.random() * (maxDgramSize - minDgramSize)) + minDgramSize;\n    buffers = disseminator.getUpdatesUpTo(bytesAvailable);\n\n    while (buffers.length > 0) {\n        length = 0;\n\n        /* jshint loopfunc: true */\n        buffers.forEach(function verify(buffer) {\n            assert.strictEqual(Net.ReadMessageType.call(buffer, 0), MessageType.Update);\n\n            length += buffer.length;\n            update = codec.decode(buffer.slice(Net.MessageTypeSize));\n\n            Object.keys(hostToCount).forEach(function verifyPriority(host) {\n                assert.strictEqual(hostToCount[update.host] > hostToCount[host], false);\n            });\n\n            hostToCount[update.host] = (hostToCount[update.host] || 0) + 1;\n        });\n        /* jshint loopfunc: false */\n\n        assert.strictEqual(length < bytesAvailable, true);\n        numberOfBuffers += buffers.length;\n\n        bytesAvailable = Math.ceil(Math.random() * (maxDgramSize - minDgramSize)) + minDgramSize;\n        buffers = disseminator.getUpdatesUpTo(bytesAvailable);\n    }\n\n    assert.strictEqual(numberOfBuffers, numberOfUpdates *\n        Disseminator.Default.disseminationFormula(Disseminator.Default.disseminationFactor, numberOfMembers));\n    disseminator.stop();\n    assert.end();\n});\n\ntest('Disseminator removes over disseminated updates on decrease of dissemination limit', function t(assert) {\n    var membership = new events.EventEmitter();\n    var disseminator = new Disseminator({\n        swim: {\n            codec: codec,\n            membership: membership\n        },\n        disseminationFactor: 1,\n        disseminationFormula: function disseminationFormula(factor, size) {\n            return factor * size;\n        }\n    });\n    var numberOfMembers = 10;\n    var buffers;\n\n    membership.size = function size() {\n        return numberOfMembers;\n    };\n\n    disseminator.start();\n\n    membership.emit(Membership.EventType.Update, {\n        host: 'localhost:22'\n    });\n\n    buffers = disseminator.getUpdatesUpTo(Infinity);\n    assert.strictEqual(buffers.length, 1);\n\n    numberOfMembers = 1;\n    membership.emit(Membership.EventType.Change);\n\n    buffers = disseminator.getUpdatesUpTo(Infinity);\n    assert.strictEqual(buffers.length, 0);\n\n    disseminator.stop();\n    assert.end();\n});\n"
  },
  {
    "path": "test/failure-detector.js",
    "content": "'use strict';\nvar events = require('events');\nvar test = require('tape');\n\nvar FailureDetector = require('../lib/failure-detector');\nvar Member = require('../lib/member');\nvar MessageType = require('../lib/message-type');\nvar Net = require('../lib/net');\n\nvar INTERVAL = 100;\nvar MAX_TICKS = 3;\nvar TICKS = Math.ceil(Math.random() * MAX_TICKS);\nvar TICK_OFFSET = -0.5;\nvar MEMBER_A = new Member({\n    host: 'localhost:1111'\n});\nvar MEMBER_B = new Member({\n    host: 'localhost:2222'\n});\n\ntest('FailureDetector ticks/pings at defined interval', function t(assert) {\n    var pingCalled = 0;\n    var membership = {\n        next: function next() {\n            pingCalled++;\n        }\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL\n    });\n\n    failureDetector.start();\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(pingCalled, TICKS);\n        assert.end();\n    }, INTERVAL * (TICKS + TICK_OFFSET));\n});\n\ntest('FailureDetector does not send PingReq when receives Ack of Ping in time', function t(assert) {\n    var pingCalled = 0;\n    var membership = {\n        next: function next() {\n            pingCalled++;\n            return MEMBER_A;\n        },\n        random: function random() {\n            assert.fail();\n        }\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL\n    });\n\n    net.sendMessage = function sendMessage(message, host) {\n        net.emit(Net.EventType.Ack, {\n            seq: message.data.seq\n        }, host);\n    };\n\n    failureDetector.start();\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(pingCalled, TICKS);\n        assert.end();\n    }, INTERVAL * (TICKS + TICK_OFFSET));\n});\n\ntest('FailureDetector sends PingReq when does not receive Ack of Ping in time', function t(assert) {\n    var pingTimeout = 1;\n    var pingReqCalled = 0;\n    var membership = {\n        next: function next() {\n            return MEMBER_A;\n        },\n        random: function random() {\n            pingReqCalled++;\n            return [];\n        }\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL,\n        pingTimeout: pingTimeout\n    });\n\n    net.sendMessage = function sendMessage() {};\n\n    failureDetector.start();\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(pingReqCalled, TICKS);\n        assert.end();\n    }, INTERVAL * (TICKS + TICK_OFFSET));\n});\n\ntest('FailureDetector does not emit suspect event when receives Ack of PingReq in time', function t(assert) {\n    var pingTimeout = 1;\n    var pingReqTimeout = 10;\n    var pingReqCalled = 0;\n    var membership = {\n        next: function next() {\n            return MEMBER_A;\n        },\n        random: function random() {\n            pingReqCalled++;\n            return [MEMBER_B];\n        }\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL,\n        pingTimeout: pingTimeout,\n        pingReqTimeout: pingReqTimeout\n    });\n\n    net.sendMessage = function sendMessage(message, host) {\n        if (message.type === MessageType.PingReq) {\n            net.emit(Net.EventType.Ack, {\n                seq: message.data.seq\n            }, host);\n        }\n    };\n\n    failureDetector.on(FailureDetector.EventType.Suspect, function onSuspect() {\n        assert.fail();\n    });\n\n    failureDetector.start();\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(pingReqCalled, TICKS);\n        assert.end();\n    }, INTERVAL * (TICKS + TICK_OFFSET));\n});\n\ntest('FailureDetector emits suspect event when does not receive Ack of PingReq in time', function t(assert) {\n    var pingTimeout = 1;\n    var pingReqTimeout = 1;\n    var suspectEmitted = 0;\n    var membership = {\n        next: function next() {\n            return MEMBER_A;\n        },\n        random: function random() {\n            return [MEMBER_B];\n        }\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL,\n        pingTimeout: pingTimeout,\n        pingReqTimeout: pingReqTimeout\n    });\n\n    net.sendMessage = function sendMessage() {};\n\n    failureDetector.on(FailureDetector.EventType.Suspect, function onSuspect(suspect) {\n        suspectEmitted++;\n        assert.strictEqual(suspect, MEMBER_A);\n    });\n\n    failureDetector.start();\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(suspectEmitted, TICKS);\n        assert.end();\n    }, INTERVAL * (TICKS + TICK_OFFSET));\n});\n\ntest('FailureDetector sends Ack when receives Ping', function t(assert) {\n    var membership = {\n        next: function next() {}\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        }\n    });\n    var seq = Math.random();\n\n    net.sendMessage = function sendMessage(message, host) {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.strictEqual(message.type, MessageType.Ack);\n        assert.strictEqual(message.data.seq, seq);\n        assert.strictEqual(host, MEMBER_A.host);\n        assert.end();\n    };\n\n    failureDetector.start();\n\n    net.emit(Net.EventType.Ping, {\n        seq: seq\n    }, MEMBER_A.host);\n});\n\ntest('FailureDetector sends Ping to destination when receives PingReq, ' +\n     'then sends Ack to PingReq requester when receives Ack from destination', function t(assert) {\n    var membership = {\n        next: function next() {}\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        }\n    });\n    var seq = Math.random();\n    var sendMessageCalled = 0;\n\n    net.sendMessage = function sendMessage(message, host) {\n        switch (sendMessageCalled) {\n            case 0:\n                sendMessageCalled++;\n\n                assert.strictEqual(message.type, MessageType.Ping);\n                assert.strictEqual(host, MEMBER_B.host);\n\n                net.emit(Net.EventType.Ack, {\n                    seq: message.data.seq\n                }, host);\n                break;\n            case 1:\n                verifyInternal(failureDetector, assert);\n                failureDetector.stop();\n\n                assert.strictEqual(message.type, MessageType.Ack);\n                assert.strictEqual(host, MEMBER_A.host);\n\n                assert.end();\n                break;\n        }\n    };\n\n    failureDetector.start();\n\n    net.emit(Net.EventType.PingReq, {\n        seq: seq,\n        destination: MEMBER_B.host\n    }, MEMBER_A.host);\n});\n\ntest('FailureDetector does not send Ack to PingReq requester ' +\n     'when does not receive Ack from destination', function t(assert) {\n    var pingTimeout = 1;\n    var membership = {\n        next: function next() {}\n    };\n    var net = new events.EventEmitter();\n    var failureDetector = new FailureDetector({\n        swim: {\n            membership: membership,\n            net: net\n        },\n        interval: INTERVAL,\n        pingTimeout: pingTimeout\n    });\n    var seq = Math.random();\n    var sendMessageCalled = 0;\n\n    net.sendMessage = function sendMessage(message, host) {\n        switch (sendMessageCalled) {\n            case 0:\n                sendMessageCalled++;\n\n                assert.strictEqual(message.type, MessageType.Ping);\n                assert.strictEqual(host, MEMBER_B.host);\n                break;\n            case 1:\n                assert.fail();\n                break;\n        }\n    };\n\n    failureDetector.start();\n\n    net.emit(Net.EventType.PingReq, {\n        seq: seq,\n        destination: MEMBER_B.host\n    }, MEMBER_A.host);\n\n    setTimeout(function verify() {\n        verifyInternal(failureDetector, assert);\n        failureDetector.stop();\n\n        assert.end();\n    }, INTERVAL);\n});\n\nfunction verifyInternal(failureDetector, assert) {\n    assert.strictEqual(Object.keys(failureDetector.seqToTimeout).length, 0);\n    assert.strictEqual(Object.keys(failureDetector.seqToCallback).length, 0);\n}\n"
  },
  {
    "path": "test/index.js",
    "content": "'use strict';\nrequire('./codec');\nrequire('./disseminator');\nrequire('./failure-detector');\nrequire('./membership');\nrequire('./net');\n"
  },
  {
    "path": "test/membership.js",
    "content": "'use strict';\nvar events = require('events');\nvar test = require('tape');\n\nvar FailureDetector = require('../lib/failure-detector');\nvar Member = require('../lib/member');\nvar Membership = require('../lib/membership');\nvar MessageType = require('../lib/message-type');\nvar Net = require('../lib/net');\n\nvar SUSPECT_TIMEOUT = 10;\nvar LOCAL = new Member({\n    host: 'localhost:0000'\n});\nvar MEMBER_A = new Member({\n    host: 'localhost:1111'\n});\n\ntest('Membership sends suspect update back when receives Ack from member in suspect state', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n\n    net.sendMessage = function sendMessage(message, host) {\n        membership.stop();\n\n        assert.strictEqual(host, MEMBER_A.host);\n        assert.strictEqual(message.type, MessageType.Update);\n        assert.strictEqual(message.data.state, Member.State.Suspect);\n        assert.deepEqual(message.data, membership.get(MEMBER_A.host).data());\n        assert.end();\n    };\n\n    membership.start();\n    failureDetector.emit(FailureDetector.EventType.Suspect, MEMBER_A);\n    net.emit(Net.EventType.Ack, MEMBER_A, MEMBER_A.host);\n});\n\ntest('Membership emits update event when member becomes suspect', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(data.state, Member.State.Suspect);\n        assert.deepEqual(data, membership.get(data.host).data());\n        assert.end();\n    });\n\n    membership.start();\n    failureDetector.emit(FailureDetector.EventType.Suspect, MEMBER_A);\n});\n\ntest('Membership emits update event when member becomes faulty', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        },\n        suspectTimeout: SUSPECT_TIMEOUT\n    });\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.strictEqual(membership.get(data.host), undefined);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n    failureDetector.emit(FailureDetector.EventType.Suspect, MEMBER_A);\n});\n\ntest('Membership resumes suspect mechanism after restart', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        },\n        suspectTimeout: SUSPECT_TIMEOUT\n    });\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.strictEqual(membership.get(data.host), undefined);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n    failureDetector.emit(FailureDetector.EventType.Suspect, MEMBER_A);\n    membership.stop();\n    membership.start();\n});\n\ntest('Membership accepts alive update for remote member not in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.deepEqual(data, update);\n        assert.deepEqual(data, membership.get(data.host).data());\n        assert.end();\n    });\n\n    membership.start();\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts alive update with greater incarnation ' +\n     'for alive/suspect remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation += 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops alive update with smaller or equal incarnation ' +\n     'for alive/suspect remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 2:\n                assert.fail();\n                break;\n        }\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        switch (dropEmitted) {\n            case 0:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 1);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                break;\n            case 1:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 1);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                break;\n            case 2:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                break;\n            case 3:\n                membership.stop();\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    // accept\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.incarnation -= 1;\n    net.emit(Net.EventType.Update, update);\n\n    // accept\n    update.incarnation += 1;\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.state = Member.State.Alive;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.incarnation -= 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops alive update with smaller or equal incarnation ' +\n     'for faulty member', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.deepEqual(data, update);\n                assert.deepEqual(membership.get(data.host), undefined);\n                break;\n            case 2:\n                assert.fail();\n                break;\n        }\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        switch (dropEmitted) {\n            case 0:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    // accept\n    update.state = Member.State.Alive;\n    net.emit(Net.EventType.Update, update);\n\n    // accept\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.state = Member.State.Alive;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.incarnation -= 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops alive update with smaller or equal incarnation for local member', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = LOCAL.data();\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate() {\n        assert.fail();\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        switch (dropEmitted) {\n            case 0:\n                dropEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts alive update with greater incarnation for local member ' +\n     'and local member affirms itself with greater incarnation', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = LOCAL.data();\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.strictEqual(data.incarnation > update.incarnation, true);\n        assert.end();\n    });\n\n    membership.start();\n\n    update.incarnation += 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts suspect update for remote member not in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(data.state, Member.State.Suspect);\n        assert.deepEqual(data, update);\n        assert.deepEqual(data, membership.get(data.host).data());\n        assert.end();\n    });\n\n    membership.start();\n\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts suspect update with greater incarnation ' +\n     'for alive/suspect remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 2:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation += 1;\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation += 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops suspect update with smaller or equal incarnation ' +\n     'for suspect remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                assert.fail();\n                break;\n        }\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        switch (dropEmitted) {\n            case 0:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 1);\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(updateEmitted, 1);\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    // accept\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.incarnation -= 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops suspect update with smaller or equal incarnation ' +\n     'for faulty member', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.deepEqual(data, update);\n                assert.deepEqual(membership.get(data.host), undefined);\n                break;\n            case 2:\n                assert.fail();\n                break;\n        }\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        switch (dropEmitted) {\n            case 0:\n                dropEmitted++;\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(updateEmitted, 2);\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    // accept\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n\n    // accept\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n\n    // drop\n    update.incarnation -= 1;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts suspect update with equal incarnation ' +\n     'for alive remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Suspect);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops suspect update with smaller incarnation ' +\n     'for alive remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        updateEmitted++;\n\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.deepEqual(data, update);\n        assert.deepEqual(data, membership.get(data.host).data());\n    });\n\n    membership.on(Membership.EventType.Drop, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(updateEmitted, 1);\n        assert.strictEqual(data.state, Member.State.Suspect);\n        assert.deepEqual(data, update);\n        assert.end();\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation -= 1;\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops suspect update with for local member ' +\n     'and local member affirms itself with greater incarnation', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = LOCAL.data();\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(dropEmitted, 1);\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.strictEqual(data.incarnation > update.incarnation, true);\n        assert.end();\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        dropEmitted++;\n\n        assert.strictEqual(data.state, Member.State.Suspect);\n        assert.deepEqual(data, update);\n    });\n\n    membership.start();\n\n    update.state = Member.State.Suspect;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership accepts faulty update with greater or equal incarnation ' +\n     'for remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        switch (updateEmitted) {\n            case 0:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 1:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.deepEqual(data, update);\n                assert.strictEqual(membership.get(data.host), undefined);\n                break;\n            case 2:\n                updateEmitted++;\n\n                assert.strictEqual(data.state, Member.State.Alive);\n                assert.deepEqual(data, update);\n                assert.deepEqual(data, membership.get(data.host).data());\n                break;\n            case 3:\n                membership.stop();\n\n                assert.strictEqual(data.state, Member.State.Faulty);\n                assert.deepEqual(data, update);\n                assert.strictEqual(membership.get(data.host), undefined);\n                assert.end();\n                break;\n        }\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation += 1;\n    update.state = Member.State.Alive;\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation += 1;\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops faulty update with for remote member not in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        membership.stop();\n\n        assert.strictEqual(data.state, Member.State.Faulty);\n        assert.deepEqual(data, update);\n        assert.end();\n    });\n\n    membership.start();\n\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops faulty update with smaller incarnation for remote member in membership', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = MEMBER_A.data();\n    var updateEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        updateEmitted++;\n\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.deepEqual(data, update);\n        assert.deepEqual(data, membership.get(data.host).data());\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        membership.stop();\n\n        assert.strictEqual(updateEmitted, 1);\n        assert.strictEqual(data.state, Member.State.Faulty);\n        assert.deepEqual(data, update);\n        assert.end();\n    });\n\n    membership.start();\n\n    net.emit(Net.EventType.Update, update);\n\n    update.incarnation -= 1;\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership drops faulty update with for local member ' +\n     'and local member affirms itself with greater incarnation', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var update = LOCAL.data();\n    var dropEmitted = 0;\n\n    membership.on(Membership.EventType.Update, function onUpdate(data) {\n        membership.stop();\n\n        assert.strictEqual(dropEmitted, 1);\n        assert.strictEqual(data.state, Member.State.Alive);\n        assert.strictEqual(data.incarnation > update.incarnation, true);\n        assert.end();\n    });\n\n    membership.on(Membership.EventType.Drop, function onDrop(data) {\n        assert.strictEqual(dropEmitted, 0);\n        assert.strictEqual(data.state, Member.State.Faulty);\n        assert.deepEqual(data, update);\n\n        dropEmitted++;\n    });\n\n    membership.start();\n\n    update.state = Member.State.Faulty;\n    net.emit(Net.EventType.Update, update);\n});\n\ntest('Membership is a infinite round robin iterator', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var numberOfMembers = Math.ceil(Math.random() * 10);\n    var numberOfIterations = Math.ceil(Math.random() * 5);\n    var hostToCount = Object.create(null);\n    var member;\n    var i;\n    var j;\n\n    membership.start();\n\n    for (i = 0; i < numberOfMembers; i++) {\n        net.emit(Net.EventType.Update, new Member({\n            host: 'localhost:' + i\n        }).data());\n    }\n\n    membership.stop();\n\n    for (i = 0; i < numberOfMembers; i++) {\n        member = membership.next();\n        hostToCount[member.host] = (hostToCount[member.host] || 0) + 1;\n    }\n\n    Object.keys(hostToCount).forEach(function verify(host) {\n        assert.strictEqual(hostToCount[host], 1);\n    });\n\n    for (j = 0; j < numberOfIterations; j++) {\n        for (i = 0; i < numberOfMembers; i++) {\n            member = membership.next();\n            hostToCount[member.host] = (hostToCount[member.host] || 0) + 1;\n        }\n    }\n\n    Object.keys(hostToCount).forEach(function verify(host) {\n        assert.strictEqual(hostToCount[host], numberOfIterations + 1);\n    });\n\n    assert.strictEqual(hostToCount[LOCAL.host], undefined);\n    assert.end();\n});\n\ntest('Membership next to undefined when there is only local member', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n\n    assert.strictEqual(membership.next(), undefined);\n    assert.end();\n});\n\ntest('Membership can randomly select up to n unique remote members', function t(assert) {\n    var failureDetector = new events.EventEmitter();\n    var net = new events.EventEmitter();\n    var membership = new Membership({\n        local: LOCAL.data(),\n        swim: {\n            failureDetector: failureDetector,\n            net: net\n        }\n    });\n    var numberOfMembers = Math.ceil(Math.random() * 10);\n    var numberOfIterations = Math.ceil(Math.random() * 5);\n    var selected;\n    var n;\n    var i;\n    var j;\n\n    assert.strictEqual(membership.random(1).length, 0);\n\n    membership.start();\n\n    for (i = 0; i < numberOfMembers; i++) {\n        net.emit(Net.EventType.Update, new Member({\n            host: 'localhost:' + i\n        }).data());\n    }\n\n    membership.stop();\n\n    for (j = 0; j < numberOfIterations; j++) {\n        n = Math.floor(Math.random() * numberOfMembers * 2);\n        selected = membership.random(n);\n\n        assert.strictEqual(Array.isArray(selected), true);\n        assert.strictEqual(selected.length <= n, true);\n        /* jshint loopfunc: true */\n        assert.strictEqual(Object.keys(selected.reduce(function mark(result, curr) {\n            result[curr.host] = true;\n            return result;\n        }, Object.create(null))).length, selected.length);\n        /* jshint loopfunc: false */\n    }\n\n    assert.end();\n});\n"
  },
  {
    "path": "test/net.js",
    "content": "'use strict';\nvar async = require('async');\nvar test = require('tape');\n\nvar Codec = require('../lib/codec');\nvar MessageType = require('../lib/message-type');\nvar Net = require('../lib/net');\n\nvar codec = new Codec();\n\ntest('Net can send and receive primitive messages', function t(assert) {\n    var senderOpts = {\n        swim: {\n            codec: codec,\n            disseminator: {\n                getUpdatesUpTo: function getUpdatesUpTo() {\n                    return [];\n                }\n            }\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var receiverOpts = {\n        swim: {\n            codec: codec\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var sender = new Net(senderOpts);\n    var receiver = new Net(receiverOpts);\n    var tests = [{\n        type: MessageType.Ping,\n        data: {\n            seq: Math.random()\n        },\n        event: Net.EventType.Ping\n    }, {\n        type: MessageType.PingReq,\n        data: {\n            seq: Math.random()\n        },\n        event: Net.EventType.PingReq\n    }, {\n        type: MessageType.Sync,\n        data: {\n            seq: Math.random()\n        },\n        event: Net.EventType.Sync\n    }, {\n        type: MessageType.Ack,\n        data: {\n            seq: Math.random()\n        },\n        event: Net.EventType.Ack\n    }, {\n        type: MessageType.Update,\n        data: {\n            seq: Math.random()\n        },\n        event: Net.EventType.Update\n    }];\n    var senderPort;\n    var receiverPort;\n\n    async.parallel([\n        sender.listen.bind(sender),\n        receiver.listen.bind(receiver)\n    ], function parallelCallback(err) {\n        assert.notOk(err);\n\n        senderPort = sender.udpSocket.address().port;\n        receiverPort = receiver.udpSocket.address().port;\n\n        async.each(tests, function runTest(test, callback) {\n            receiver.on(test.event, function onEvent(data, host) {\n                assert.deepEqual(data, test.data);\n                assert.strictEqual(parseInt(host.split(':')[1]), senderPort);\n\n                receiver.removeAllListeners(test.event);\n                callback();\n            });\n\n            sender.sendMessage({\n                type: test.type,\n                data: test.data\n            }, 'localhost:' + receiverPort);\n        }, function eachCallback() {\n            sender.close();\n            receiver.close();\n\n            assert.end();\n        });\n    });\n});\n\ntest('Net can send and receive primitive messages with piggybacked updates', function t(assert) {\n    var randomPingData = {\n        seq: Math.random()\n    };\n    var randomUpdateData = {\n        seq: Math.random()\n    };\n    var senderOpts = {\n        swim: {\n            codec: codec,\n            disseminator: {\n                getUpdatesUpTo: function getUpdatesUpTo(bytesAvailable) {\n                    var expectedBytesAvailable = Net.Default.udp.maxDgramSize - Net.MessageTypeSize -\n                        Net.LengthSize * 2 - Net.MessageTypeSize - codec.encode(randomPingData).length;\n                    var header;\n\n                    assert.strictEqual(bytesAvailable, expectedBytesAvailable);\n\n                    header = new Buffer(Net.MessageTypeSize);\n                    Net.WriteMessageType.call(header, MessageType.Update, 0);\n\n                    return [Buffer.concat([header, codec.encode(randomUpdateData)])];\n                }\n            }\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var receiverOpts = {\n        swim: {\n            codec: codec\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var sender = new Net(senderOpts);\n    var receiver = new Net(receiverOpts);\n    var senderPort;\n    var receiverPort;\n\n    async.parallel([\n        sender.listen.bind(sender),\n        receiver.listen.bind(receiver)\n    ], function parallelCallback(err) {\n        assert.notOk(err);\n\n        senderPort = sender.udpSocket.address().port;\n        receiverPort = receiver.udpSocket.address().port;\n\n        async.parallel([\n            function receivePing(callback) {\n                receiver.on(Net.EventType.Ping, function onPing(data, host) {\n                    assert.deepEqual(data, randomPingData);\n                    assert.strictEqual(parseInt(host.split(':')[1]), senderPort);\n                    callback();\n                });\n            },\n            function receiveUpdate(callback) {\n                receiver.on(Net.EventType.Update, function onUpdate(data, host) {\n                    assert.deepEqual(data, randomUpdateData);\n                    assert.strictEqual(parseInt(host.split(':')[1]), senderPort);\n                    callback();\n                });\n            },\n            function send(callback) {\n                sender.sendMessage({\n                    type: MessageType.Ping,\n                    data: randomPingData\n                }, 'localhost:' + receiverPort);\n                callback();\n            }\n        ], function parallelCallback() {\n            receiver.removeAllListeners();\n\n            sender.close();\n            receiver.close();\n\n            assert.end();\n        });\n    });\n});\n\ntest('Net can send and receive multiple messages batched over packets', function t(assert) {\n    var senderOpts = {\n        swim: {\n            codec: codec\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var receiverOpts = {\n        swim: {\n            codec: codec\n        },\n        udp: {\n            port: 0\n        }\n    };\n    var sender = new Net(senderOpts);\n    var receiver = new Net(receiverOpts);\n    var messages = [];\n    var received = [];\n    var messageCount = 100;\n    var packetCount = 0;\n    var senderPort;\n    var receiverPort;\n    var i;\n\n    for (i = 0; i < messageCount; i++) {\n        messages.push({\n            type: MessageType.Update,\n            data: {\n                seq: Math.random()\n            }\n        });\n    }\n\n    sender.sendBuffer = function sendBuffer(buffer, host) {\n        packetCount++;\n        Net.prototype.sendBuffer.call(sender, buffer, host);\n    };\n\n    async.parallel([\n        sender.listen.bind(sender),\n        receiver.listen.bind(receiver)\n    ], function parallelCallback(err) {\n        assert.notOk(err);\n\n        senderPort = sender.udpSocket.address().port;\n        receiverPort = receiver.udpSocket.address().port;\n\n        async.parallel([\n            function receiveUpdate(callback) {\n                receiver.on(Net.EventType.Update, function onUpdate(data) {\n                    received.push({\n                        type: MessageType.Update,\n                        data: data\n                    });\n\n                    if (received.length === messages.length) {\n                        callback();\n                    }\n                });\n            },\n            function send(callback) {\n                sender.sendMessages(messages, 'localhost:' + receiverPort);\n                callback();\n            }\n        ], function parallelCallback() {\n            assert.strictEqual(packetCount < messageCount, true);\n            assert.deepEqual(received.length, messages.length);\n            assert.deepEqual(sum(received), sum(messages));\n\n            receiver.removeAllListeners();\n\n            sender.close();\n            receiver.close();\n\n            assert.end();\n        });\n    });\n});\n\nfunction sum(messages) {\n    messages.reduce(function sum(acc, m) {\n        return acc += m.seq;\n    }, 0);\n}\n"
  }
]