Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

first commit

  • Loading branch information...
commit 0f1ac91eeb71d46cf79b19dff6e2de11c353f3ea 0 parents
@adrai authored
Showing with 44,909 additions and 0 deletions.
  1. +69 −0 README.markdown
  2. +13 −0 index.js
  3. +161 −0 lib/bases/eventDenormalizerBase.js
  4. +80 −0 lib/bases/eventExtenderBase.js
  5. +88 −0 lib/contextEventDenormalizer.js
  6. +86 −0 lib/eventDispatcher.js
  7. +7 −0 lib/eventEmitter.js
  8. +72 −0 lib/loaders/eventDenormalizerLoader.js
  9. +72 −0 lib/loaders/eventExtenderLoader.js
  10. +37 −0 lib/orderQueue.js
  11. +4 −0 lib/utils.js
  12. +51 −0 lib/utils/object.js
  13. +88 −0 lib/utils/path.js
  14. +19 −0 licence
  15. +9 −0 node_modules/async/.gitmodules
  16. +4 −0 node_modules/async/.npmignore
  17. +19 −0 node_modules/async/LICENSE
  18. +25 −0 node_modules/async/Makefile
  19. +1,022 −0 node_modules/async/README.md
  20. +3 −0  node_modules/async/index.js
  21. +692 −0 node_modules/async/lib/async.js
  22. +42 −0 node_modules/async/package.json
  23. +13 −0 node_modules/eventemitter2/.npmignore
  24. +203 −0 node_modules/eventemitter2/README.md
  25. +1 −0  node_modules/eventemitter2/index.js
  26. +547 −0 node_modules/eventemitter2/lib/eventemitter2.js
  27. +79 −0 node_modules/eventemitter2/package.json
  28. +123 −0 node_modules/eventemitter2/test/common.js
  29. +53 −0 node_modules/eventemitter2/test/perf/benchmark.js
  30. +192 −0 node_modules/eventemitter2/test/simple/addListener.js
  31. +153 −0 node_modules/eventemitter2/test/simple/emit.js
  32. +199 −0 node_modules/eventemitter2/test/simple/removeListener.js
  33. +133 −0 node_modules/eventemitter2/test/simple/setMax.js
  34. +126 −0 node_modules/eventemitter2/test/simple/ttl.js
  35. +307 −0 node_modules/eventemitter2/test/wildcardEvents/addListener.js
  36. +829 −0 node_modules/eventemitter2/test/wildcardEvents/all.js
  37. +216 −0 node_modules/eventemitter2/test/wildcardEvents/customDelimiter.js
  38. +56 −0 node_modules/eventemitter2/test/wildcardEvents/k1.js
  39. +79 −0 node_modules/eventemitter2/test/wildcardEvents/options.js
  40. +284 −0 node_modules/eventemitter2/test/wildcardEvents/removeListener.js
  41. +196 −0 node_modules/eventemitter2/test/wildcardEvents/ttl.js
  42. +3 −0  node_modules/expect.js/.npmignore
  43. +21 −0 node_modules/expect.js/History.md
  44. +242 −0 node_modules/expect.js/README.md
  45. +1,202 −0 node_modules/expect.js/expect.js
  46. +25 −0 node_modules/expect.js/package.json
  47. +13 −0 node_modules/node-queue/.npmignore
  48. +13 −0 node_modules/node-queue/.travis.yml
  49. +161 −0 node_modules/node-queue/README.markdown
  50. +13 −0 node_modules/node-queue/index.js
  51. +104 −0 node_modules/node-queue/lib/databases/inMemory.js
  52. +120 −0 node_modules/node-queue/lib/databases/mongoDb.js
  53. +45 −0 node_modules/node-queue/lib/queue.js
  54. +19 −0 node_modules/node-queue/licence
  55. +56 −0 node_modules/node-queue/package.json
  56. +1 −0  node_modules/node-queue/test/mocha.opts
  57. +322 −0 node_modules/node-queue/test/queueImplementationTest.js
  58. +53 −0 node_modules/node-queue/test/queueTest.js
  59. +2 −0  node_modules/sinon/.npmignore
  60. +24 −0 node_modules/sinon/AUTHORS
  61. +236 −0 node_modules/sinon/Changelog.txt
  62. BIN  node_modules/sinon/GPATH
  63. BIN  node_modules/sinon/GRTAGS
  64. BIN  node_modules/sinon/GSYMS
  65. BIN  node_modules/sinon/GTAGS
  66. +27 −0 node_modules/sinon/LICENSE
  67. +10 −0 node_modules/sinon/Makefile
  68. +42 −0 node_modules/sinon/README.md
  69. +92 −0 node_modules/sinon/build
  70. +25 −0 node_modules/sinon/jsTestDriver.conf
  71. +10 −0 node_modules/sinon/jsTestDriverBuild.conf
  72. +54 −0 node_modules/sinon/jsl.conf
  73. +313 −0 node_modules/sinon/lib/sinon.js
  74. +171 −0 node_modules/sinon/lib/sinon/assert.js
  75. +141 −0 node_modules/sinon/lib/sinon/collection.js
  76. +407 −0 node_modules/sinon/lib/sinon/mock.js
  77. +125 −0 node_modules/sinon/lib/sinon/sandbox.js
  78. +488 −0 node_modules/sinon/lib/sinon/spy.js
  79. +277 −0 node_modules/sinon/lib/sinon/stub.js
  80. +67 −0 node_modules/sinon/lib/sinon/test.js
  81. +97 −0 node_modules/sinon/lib/sinon/test_case.js
  82. +74 −0 node_modules/sinon/lib/sinon/util/event.js
  83. +201 −0 node_modules/sinon/lib/sinon/util/fake_server.js
  84. +83 −0 node_modules/sinon/lib/sinon/util/fake_server_with_clock.js
  85. +339 −0 node_modules/sinon/lib/sinon/util/fake_timers.js
  86. +471 −0 node_modules/sinon/lib/sinon/util/fake_xml_http_request.js
  87. +27 −0 node_modules/sinon/lib/sinon/util/timers_ie.js
  88. +19 −0 node_modules/sinon/lib/sinon/util/xhr_ie.js
  89. +115 −0 node_modules/sinon/package.json
  90. +49 −0 node_modules/sinon/step-tests
  91. +595 −0 node_modules/sinon/test/Asserts.js
  92. +27 −0 node_modules/sinon/test/helper.js
  93. +13 −0 node_modules/sinon/test/node/run.js
  94. +1 −0  node_modules/sinon/test/resources/xhr_target.txt
  95. +13,989 −0 node_modules/sinon/test/rhino/env.rhino.1.2.js
  96. +34 −0 node_modules/sinon/test/rhino/run.js
  97. +1,086 −0 node_modules/sinon/test/sinon/assert_test.js
  98. +321 −0 node_modules/sinon/test/sinon/collection_test.js
  99. +955 −0 node_modules/sinon/test/sinon/mock_test.js
  100. +470 −0 node_modules/sinon/test/sinon/sandbox_test.js
  101. +2,401 −0 node_modules/sinon/test/sinon/spy_test.js
  102. +1,011 −0 node_modules/sinon/test/sinon/stub_test.js
  103. +230 −0 node_modules/sinon/test/sinon/test_case_test.js
  104. +513 −0 node_modules/sinon/test/sinon/test_test.js
  105. +125 −0 node_modules/sinon/test/sinon/util/event_test.js
  106. +691 −0 node_modules/sinon/test/sinon/util/fake_server_test.js
  107. +232 −0 node_modules/sinon/test/sinon/util/fake_server_with_clock_test.js
  108. +878 −0 node_modules/sinon/test/sinon/util/fake_timers_test.js
  109. +1,198 −0 node_modules/sinon/test/sinon/util/fake_xml_http_request_test.js
  110. +408 −0 node_modules/sinon/test/sinon_test.js
  111. +79 −0 node_modules/sinon/test/test_case_shim.js
  112. +75 −0 node_modules/sinon/todo.org
  113. +3 −0  node_modules/underscore/.npmignore
  114. +1 −0  node_modules/underscore/CNAME
  115. +22 −0 node_modules/underscore/LICENSE
  116. +19 −0 node_modules/underscore/README.md
  117. BIN  node_modules/underscore/favicon.ico
  118. +2,109 −0 node_modules/underscore/index.html
  119. +1 −0  node_modules/underscore/index.js
  120. +38 −0 node_modules/underscore/package.json
  121. BIN  node_modules/underscore/raw/underscore.psd
  122. +32 −0 node_modules/underscore/underscore-min.js
  123. +1,059 −0 node_modules/underscore/underscore.js
  124. +13 −0 node_modules/viewmodel/.npmignore
  125. +13 −0 node_modules/viewmodel/.travis.yml
  126. +153 −0 node_modules/viewmodel/README.markdown
  127. +13 −0 node_modules/viewmodel/index.js
  128. +171 −0 node_modules/viewmodel/lib/databases/inMemory.js
  129. +160 −0 node_modules/viewmodel/lib/databases/mongoDb.js
  130. +124 −0 node_modules/viewmodel/lib/repository.js
  131. +19 −0 node_modules/viewmodel/licence
  132. +65 −0 node_modules/viewmodel/package.json
  133. +1 −0  node_modules/viewmodel/test/mocha.opts
  134. +388 −0 node_modules/viewmodel/test/repositoryReadImplementationTest.js
  135. +68 −0 node_modules/viewmodel/test/repositoryTest.js
  136. +792 −0 node_modules/viewmodel/test/repositoryWriteImplementationTest.js
  137. +44 −0 package.json
  138. +436 −0 test/eventDenormalizerBaseTest.js
  139. +298 −0 test/eventDispatcherTest.js
  140. +147 −0 test/eventExtenderBaseTest.js
  141. +496 −0 test/integration/contextEventDenormalizerTest.js
  142. +14 −0 test/integration/eventDenormalizers/dummyDenormalizer.js
  143. +26 −0 test/integration/eventExtenders/dummyExtender.js
  144. +1 −0  test/mocha.opts
69 README.markdown
@@ -0,0 +1,69 @@
+# Introduction
+
+[![Build Status](https://secure.travis-ci.org/adrai/node-cqrs-eventdenormalizer.png)](http://travis-ci.org/adrai/node-cqrs-eventdenormalizer)
+
+Node-cqrs-eventdenormalizer is a node.js module that implements the cqrs pattern.
+It can be very useful as eventdenormalizer component if you work with (d)ddd, cqrs, domain, host, etc.
+
+# Installation
+
+ $ npm install node-cqrs-eventdenormalizer
+
+# Usage
+
+## Initialization
+
+ var contextEventDenormalizer = require('node-cqrs-eventdenormalizer').contextEventDenormalizer;
+
+ contextEventDenormalizer.on('event', function(evt) {
+ // send to clients
+ });
+ contextEventDenormalizer.initialize({
+ denormalizersPath: __dirname + '/eventDenormalizers',
+ extendersPath: __dirname + '/eventExtenders'
+ }, function(err) {
+
+ });
+
+ contextEventDenormalizer.denormalize({ id: 'msgId', event: 'dummyChanged', payload: { id: '23445' } }, function(err) {
+
+ });
+
+## Define eventdenormalizers...
+
+ var base = require('node-cqrs-eventdenormalizer').eventDenormalizerBase;
+
+ module.exports = base.extend({
+
+ events: ['dummied', {'dummyCreated': 'create'}, {'dummyChanged': 'update'}, {'dummyDeleted': 'delete'}],
+ collectionName: 'dummies',
+
+ dummied: function(evt, aux, callback) {
+ callback(null);
+ }
+
+ });
+
+See [tests](https://github.com/adrai/node-cqrs-eventdenormalizer/tree/master/test) for detailed information...
+
+# License
+
+Copyright (c) 2012 Adriano Raiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
13 index.js
@@ -0,0 +1,13 @@
+var index;
+
+if (typeof module.exports !== 'undefined') {
+ index = module.exports;
+} else {
+ index = root.index = {};
+}
+
+index.VERSION = '0.0.1';
+
+index.contextEventDenormalizer = require('./lib/contextEventDenormalizer');
+index.eventDenormalizerBase = require('./lib/bases/eventDenormalizerBase');
+index.eventExtenderBase = require('./lib/bases/eventExtenderBase');
161 lib/bases/eventDenormalizerBase.js
@@ -0,0 +1,161 @@
+var _ = require('underscore')
+ , eventEmitter = require('../eventEmitter')
+ , Queue = require('../orderQueue');
+
+var EventDenormalizer = {};
+EventDenormalizer.prototype = {
+
+ configure: function(fn) {
+ fn.call(this);
+ return this;
+ },
+
+ use: function(module) {
+ if (!module) return;
+
+ if (module.commit) {
+ this.repository = module;
+ }
+ },
+
+ create: function(evt, aux, callback) {
+ return this.defaultAction(evt, aux, 'create', callback);
+ },
+
+ update: function(evt, aux, callback) {
+ return this.defaultAction(evt, aux, 'update', callback);
+ },
+
+ 'delete': function(evt, aux, callback) {
+ return this.defaultAction(evt, aux, 'delete', callback);
+ },
+
+ defaultAction: function(evt, aux, action, callback) {
+
+ var self = this;
+ aux.repository.get(evt.payload.id, function(err, vm) {
+ // If the view model has just been created (i.e. it has not been
+ // saved yet), and it shall be deleted, simply discard it and
+ // return.
+ if((vm.actionOnCommit === 'create') && (action === 'delete')) {
+ return callback(null);
+ }
+
+ if(!aux.defaultQueuingStrategy(evt, vm, callback)) {
+ return;
+ }
+
+ // set the next expected revision
+ aux.defaultRevisionUpdateStrategy(vm, evt);
+
+ if(action !== 'delete') {
+ _.extend(vm, evt.payload);
+ } else {
+ vm.destroy();
+ }
+
+ aux.repository.commit(vm, function(err) {
+ callback(err); // done this event
+
+ // dequeue
+ aux.defaultDequeuingStrategy(vm);
+ });
+ });
+
+ },
+
+ _getAux: function() {
+ var self = this;
+
+ this._aux = this._aux || {
+ repository: self.repository,
+
+ queueEvent: function(id, evt) {
+ self.queue.push(id, evt);
+ },
+ getQueuedEvents: function(id) {
+ return self.queue.get(id);
+ },
+ removeQueuedEvent: function(id, evt) {
+ self.queue.remove(id, evt);
+ },
+
+ defaultQueuingStrategy: function(evt, vm, callback) {
+ if(evt.head.revision < vm._revision) {
+ callback(null);
+ return false;
+ }
+ if(evt.head.revision > vm._revision) {
+ this.queueEvent(vm.id, evt);
+ return false;
+ }
+ return true;
+ },
+ defaultDequeuingStrategy: function(vm) {
+ var pendingEvents = this.getQueuedEvents(vm.id);
+ if(!pendingEvents) return;
+
+ var nextEvent = _.find(pendingEvents, function(item) {
+ return item.head.revision === vm._revision;
+ });
+ if(!nextEvent) return;
+
+ this.removeQueuedEvent(vm.id, nextEvent); // dequeue event
+ self.handle(nextEvent); // handle event
+ },
+ defaultRevisionUpdateStrategy: function(vm, evt) {
+ vm._revision = evt.head.revision + 1;
+ }
+ };
+
+ return this._aux;
+ },
+
+ handle: function(evt) {
+
+ // Map events to function names:
+ // - For the event handler matching the current event, its name is returned
+ // - For all other event handlers, undefined is returned
+ var fnNames = _.map(this.events, function(item) {
+ if (_.isString(item) && item === evt.event) {
+ return item;
+ } else if (item[evt.event]) {
+ return item[evt.event];
+ }
+ }
+ );
+
+ // Reduce function names to function name:
+ // - Replace all undefineds by an empty string
+ // - Keep all non-undefined values
+ //
+ // NOTE: This will fail if multiple event handlers match the current event,
+ // but this is not allowed anyway, so it can only happen on error.
+ var fnName = _.reduce(fnNames, function(memo, item) {
+ return memo + (item || '');
+ }, '');
+
+ if(this[fnName]) {
+ // Call the event handler found by map-reduce.
+ this[fnName](evt, this._getAux(), function(err) {
+ eventEmitter.emit('denormalized:' + evt.event, evt);
+ });
+ } else {
+ throw(new Error('missing handle function'));
+ }
+
+ }
+
+};
+
+module.exports = {
+
+ extend: function(obj) {
+ var newObj = _.extend(_.clone(EventDenormalizer.prototype), obj);
+
+ newObj.queue = new Queue();
+
+ return newObj;
+ }
+
+};
80 lib/bases/eventExtenderBase.js
@@ -0,0 +1,80 @@
+var _ = require('underscore')
+ , eventEmitter = require('../eventEmitter');
+
+var EventExtender = {};
+EventExtender.prototype = {
+
+ configure: function(fn) {
+ fn.call(this);
+ return this;
+ },
+
+ use: function(module) {
+ if (!module) return;
+
+ if (module.commit) {
+ this.repository = module;
+ }
+ },
+
+ _getAux: function() {
+ var self = this;
+
+ this._aux = this._aux || {
+ repository: self.repository
+ };
+
+ return this._aux;
+ },
+
+ handle: function(evt) {
+
+ // Map events to function names:
+ // - For the event handler matching the current event, its name is returned
+ // - For all other event handlers, undefined is returned
+ var fnNames = _.map(this.events, function(item) {
+ if (_.isString(item) && item === evt.event) {
+ return item;
+ } else if (item[evt.event]) {
+ return item[evt.event];
+ }
+ }
+ );
+
+ // Reduce function names to function name:
+ // - Replace all undefineds by an empty string
+ // - Keep all non-undefined values
+ //
+ // NOTE: This will fail if multiple event handlers match the current event,
+ // but this is not allowed anyway, so it can only happen on error.
+ var fnName = _.reduce(fnNames, function(memo, item) {
+ return memo + (item || '');
+ }, '');
+
+ if(this[fnName]) {
+ // Call the event handler found by map-reduce.
+ this[fnName](evt, this._getAux(), function(err, extEvt) {
+ extEvt = extEvt || evt;
+ eventEmitter.emit('extended:' + extEvt.event, extEvt);
+ });
+ } else if(this.defaultAction) {
+ // Call the event handler found by map-reduce.
+ this.defaultAction(evt, this._getAux(), function(err, extEvt) {
+ extEvt = extEvt || evt;
+ eventEmitter.emit('extended:' + extEvt.event, extEvt);
+ });
+ } else {
+ eventEmitter.emit('extended:' + evt.event, evt);
+ }
+
+ }
+
+};
+
+module.exports = {
+
+ extend: function(obj) {
+ return _.extend(_.clone(EventExtender.prototype), obj);
+ }
+
+};
88 lib/contextEventDenormalizer.js
@@ -0,0 +1,88 @@
+var eventDenormalizerLoader = require('./loaders/eventDenormalizerLoader')
+ , eventExtenderLoader = require('./loaders/eventExtenderLoader')
+ , eventDispatcher = require('./eventDispatcher')
+ , eventEmitter = require('./eventEmitter')
+ , EventEmitter2 = require('eventemitter2').EventEmitter2
+ , _ = require('underscore')
+ , async = require('async')
+ , queue = require('node-queue')
+ , repository = require('viewmodel').write
+ , ctxEvtDen;
+
+module.exports = ctxEvtDen = _.extend(new EventEmitter2({
+ wildcard: true,
+ delimiter: ':',
+ maxListeners: 1000 // default would be 10!
+ }), {
+
+ initialize: function(options, callback) {
+
+ if(_.isFunction(options)) {
+ callback = options;
+ }
+
+ var defaults = {
+ eventQueue: { type: 'inMemory', collectionName: 'events' },
+ repository: { type: 'inMemory' }
+ };
+
+ _.defaults(options, defaults);
+
+ eventEmitter.on('extended:*', function(evt) {
+ ctxEvtDen.emit('event', evt);
+ });
+
+ eventEmitter.on('handlingMissed:*', function(evt, id) {
+ ctxEvtDen.emit('handlingMissed:' + evt.event, evt, id);
+ });
+
+ async.series([
+
+ function(callback) {
+ if (!repository.isConnected) {
+ repository.init(options.repository, callback);
+ } else {
+ callback(null);
+ }
+ },
+
+ function(callback) {
+ eventDenormalizerLoader.configure(function() {
+ this.use(repository);
+ });
+ eventExtenderLoader.configure(function() {
+ this.use(repository);
+ });
+ callback(null);
+ },
+
+ function(callback) {
+ if (options.extendersPath) {
+ eventExtenderLoader.load(options.extendersPath, callback);
+ } else {
+ callback(null);
+ }
+ },
+
+ function(callback) {
+ eventDenormalizerLoader.load(options.denormalizersPath, callback);
+ }
+
+ ], function(err) {
+ queue.connect(options.eventQueue, function(err, eventQueue) {
+ eventDispatcher.configure(function() {
+ this.use(eventQueue);
+ });
+ eventDispatcher.initialize({}, callback);
+ });
+ });
+
+ },
+
+ denormalize: function(evt, callback) {
+ eventDispatcher.queueEvent(evt, function(err) {
+ if (callback) callback(null);
+ });
+ }
+
+});
86 lib/eventDispatcher.js
@@ -0,0 +1,86 @@
+var async = require('async')
+ , _ = require('underscore')
+ , eventEmitter = require('./eventEmitter');
+
+module.exports = {
+
+ configure: function(fn) {
+ fn.call(this);
+ return this;
+ },
+
+ use: function(module) {
+ if (!module) return;
+
+ if (module.push) {
+ this.eventQueue = module;
+ }
+ },
+
+ initialize: function(options, callback) {
+ var self = this;
+
+ if (!callback) callback = options;
+
+ eventEmitter.on('extend:*', function func(evt) {
+ var listeners = _.filter(eventEmitter.listeners('extend:' + evt.event), function(listener) {
+ return listener !== func;
+ });
+
+ if (listeners.length !== 1) {
+ eventEmitter.emit('extended:' + evt.event, evt);
+ }
+ });
+
+ eventEmitter.on('denormalized:*', function(evt) {
+ self.eventQueue.decrement(evt.id, function(err, removed) {
+ if (removed) {
+ eventEmitter.emit('extend:' + evt.event, evt);
+ }
+ });
+ });
+
+ this.resetWorkers(function(err) {
+ self.reEmitEvents(callback);
+ });
+ },
+
+ resetWorkers: function(callback) {
+ var self = this;
+
+ this.eventQueue.getAll(function(err, items) {
+ async.forEach(items, function(item, cb) {
+ item.data.workers = eventEmitter.listeners('denormalize:' + item.data.event.event).length;
+ self.eventQueue.push(item.id, item.data, cb);
+ }, callback);
+ });
+ },
+
+ reEmitEvents: function(callback) {
+ this.eventQueue.getAll(function(err, items) {
+ async.forEach(items, function(item, cb) {
+ eventEmitter.emit('denormalize:' + item.data.event.event, item.data.event);
+ cb();
+ }, callback);
+ });
+ },
+
+ queueEvent: function(evt, callback) {
+
+ var entry = {
+ workers: eventEmitter.listeners('denormalize:' + evt.event).length,
+ event: evt
+ };
+
+ if (entry.workers === 0) {
+ eventEmitter.emit('extended:' + evt.event, evt);
+ return callback(null);
+ }
+
+ this.eventQueue.push(evt.id, entry, function(err) {
+ eventEmitter.emit('denormalize:' + entry.event.event, entry.event);
+ callback(err);
+ });
+ }
+
+};
7 lib/eventEmitter.js
@@ -0,0 +1,7 @@
+var EventEmitter2 = require('eventemitter2').EventEmitter2;
+
+module.exports = new EventEmitter2({
+ wildcard: true,
+ delimiter: ':',
+ maxListeners: 1000 // default would be 10!
+});
72 lib/loaders/eventDenormalizerLoader.js
@@ -0,0 +1,72 @@
+var path = require('path')
+ , eventEmitter = require('../eventEmitter')
+ , utils = require('../utils')
+ , _ = require('underscore');
+
+var eventDenormalizerLoader = {
+
+ configure: function(fn) {
+ fn.call(this);
+ return this;
+ },
+
+ use: function(module) {
+ if (!module) return;
+
+ if (module.commit) {
+ this.repository = module;
+ }
+ },
+
+ load: function(p, callback) {
+
+ var eventDenormalizers = [];
+
+ if (!path.existsSync(p)){
+ return callback(null, eventDenormalizers);
+ }
+
+ utils.path.dive(p, function(err, file) {
+ var eventDenormalizer = require(file);
+ eventDenormalizers.push(eventDenormalizer);
+
+ // add repository
+ var repo = eventDenormalizerLoader.repository.extend({
+ collectionName: eventDenormalizer.collectionName
+ });
+ eventDenormalizer.configure(function() {
+ eventDenormalizer.use(repo);
+ });
+
+ // event binding
+ var evtNames = _.map(eventDenormalizer.events, function(item) {
+ if (_.isString(item)) {
+ return item;
+ } else {
+ // as there is only one property in the object we could take the first
+ // (if it would have more properties this could fail - cause order is not
+ // guaranteed!!!)
+ for (var i in item) {
+ if (item.hasOwnProperty(i)) {
+ return i;
+ }
+ }
+ }
+ });
+
+ function action(evt) {
+ eventDenormalizer.handle(evt);
+ }
+
+ // bind each denormalizer event
+ for(var i = 0, len = evtNames.length; i < len; i++) {
+ var evtName = evtNames[i];
+ eventEmitter.on('denormalize:' + evtName, action);
+ }
+ }, function() {
+ callback(null, eventDenormalizers);
+ });
+ }
+};
+
+module.exports = eventDenormalizerLoader;
72 lib/loaders/eventExtenderLoader.js
@@ -0,0 +1,72 @@
+var path = require('path')
+ , eventEmitter = require('../eventEmitter')
+ , utils = require('../utils')
+ , _ = require('underscore');
+
+var eventExtenderLoader = {
+
+ configure: function(fn) {
+ fn.call(this);
+ return this;
+ },
+
+ use: function(module) {
+ if (!module) return;
+
+ if (module.commit) {
+ this.repository = module;
+ }
+ },
+
+ load: function(p, callback) {
+
+ var eventExtenders = [];
+
+ if (!path.existsSync(p)){
+ return callback(null, eventExtenders);
+ }
+
+ utils.path.dive(p, function(err, file) {
+ var eventExtender = require(file);
+ eventExtenders.push(eventExtender);
+
+ // add repository
+ var repo = eventExtenderLoader.repository.extend({
+ collectionName: eventExtender.collectionName
+ });
+ eventExtender.configure(function() {
+ eventExtender.use(repo);
+ });
+
+ // event binding
+ var evtNames = _.map(eventExtender.events, function(item) {
+ if (_.isString(item)) {
+ return item;
+ } else {
+ // as there is only one property in the object we could take the first
+ // (if it would have more properties this could fail - cause order is not
+ // guaranteed!!!)
+ for (var i in item) {
+ if (item.hasOwnProperty(i)) {
+ return i;
+ }
+ }
+ }
+ });
+
+ function action(evt) {
+ eventExtender.handle(evt);
+ }
+
+ // bind each denormalizer event
+ for(var i = 0, len = evtNames.length; i < len; i++) {
+ var evtName = evtNames[i];
+ eventEmitter.on('extend:' + evtName, action);
+ }
+ }, function() {
+ callback(null, eventExtenders);
+ });
+ }
+};
+
+module.exports = eventExtenderLoader;
37 lib/orderQueue.js
@@ -0,0 +1,37 @@
+var _ = require('underscore')
+ , eventEmitter = require('./eventEmitter');
+
+var Queue = function(options) {
+ this.queue = {};
+ this.options = options || { queueTimeout: 3000 };
+};
+
+Queue.prototype = {
+
+ push: function(id, object) {
+ if(!this.queue[id]) this.queue[id] = [];
+ this.queue[id].push(object);
+ var self = this;
+ setTimeout(function() {
+ if (_.indexOf(self.queue[id], object) >= 0) {
+ eventEmitter.emit('handlingMissed:' + object.event, object, id);
+ }
+ }, this.options.queueTimeout);
+ },
+
+ get: function(id) {
+ return this.queue[id];
+ },
+
+ remove: function(id, object) {
+ if (this.queue[id]) {
+ this.queue[id].splice(_.indexOf(this.queue[id], object), 1);
+ }
+ },
+
+ clear: function() {
+ this.queue = {};
+ }
+
+};
+module.exports = Queue;
4 lib/utils.js
@@ -0,0 +1,4 @@
+module.exports = {
+ extend: require('./utils/object'),
+ path: require('./utils/path')
+};
51 lib/utils/object.js
@@ -0,0 +1,51 @@
+var _ = require('underscore');
+
+// https://gist.github.com/1714086
+// Shared empty constructor function to aid in prototype-chain creation.
+var ctor = function(){};
+
+// Helper function to correctly set up the prototype chain, for subclasses.
+// Similar to `goog.inherits`, but uses a hash of prototype properties and
+// class properties to be extended.
+var inherits = function(parent, protoProps, staticProps) {
+ var child;
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent's constructor.
+ if (protoProps && protoProps.hasOwnProperty('constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ parent.apply(this, arguments); };
+ }
+
+ // Inherit class (static) properties from parent.
+ _.extend(child, parent);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Add static properties to the constructor function, if supplied.
+ if (staticProps) _.extend(child, staticProps);
+
+ // Correctly set child's `prototype.constructor`.
+ child.prototype.constructor = child;
+
+ // Set a convenience property in case the parent's prototype is needed later.
+ child.__super__ = parent.prototype;
+
+ return child;
+};
+
+// The self-propagating extend function that Backbone classes use.
+module.exports = function (protoProps, classProps) {
+ var child = inherits(this, protoProps, classProps);
+ child.extend = this.extend;
+ return child;
+};
88 lib/utils/path.js
@@ -0,0 +1,88 @@
+var fs = require('fs');
+
+module.exports = {
+
+ dive: function(dir, opt, action, complete) {
+
+ // default options
+ var defaultOpt = {
+ all: false,
+ recursive: true,
+ directories: false
+ };
+
+ // check args
+ if (typeof opt == 'function') {
+ if (typeof action == 'undefined')
+ complete = function () {};
+ else
+ complete = action;
+
+ action = opt;
+ opt = { };
+ } else if (typeof complete == 'undefined')
+ complete = function () {};
+
+ // Assert that dir is a string
+ if (typeof dir != 'string')
+ dir = process.cwd();
+
+ opt.all = opt.all || defaultOpt.all;
+ opt.recursive = opt.recursive || defaultOpt.recursive;
+ opt.directories = opt.directories || defaultOpt.directories;
+
+ function dive(dir) {
+ // Read the directory
+ fs.readdir(dir, function(err, list) {
+ todo--;
+ // Return the error if something went wrong
+ if (err) return action(err);
+
+ // For every file in the list
+ list.forEach(function(file) {
+
+ if (opt.all || file[0] != '.') {
+ todo++;
+
+ // Full path of that file
+ var path = dir + '/' + file;
+ // Get the file's stats
+ fs.stat(path, function(err, stat) {
+ if (err) {
+ todo--;
+ return action(err);
+ }
+
+ // If the file is a directory
+ if (stat) {
+ if (stat.isDirectory()) {
+ // Call action if enabled for directories
+ if (opt.directories)
+ action(null, path, stat);
+
+ // Dive into the directory
+ if (opt.recursive) {
+ dive(path);
+ }
+ } else {
+ // Call the action
+ action(null, path, stat);
+
+ if (!--todo)
+ complete();
+ }
+ }
+ });
+ }
+ });
+ //empty directories
+ if(!list.length && !todo) {
+ complete();
+ }
+ });
+ }
+
+ var todo = 1;
+ dive(dir);
+ }
+};
19 licence
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Adriano Raiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
9 node_modules/async/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "deps/nodeunit"]
+ path = deps/nodeunit
+ url = git://github.com/caolan/nodeunit.git
+[submodule "deps/UglifyJS"]
+ path = deps/UglifyJS
+ url = https://github.com/mishoo/UglifyJS.git
+[submodule "deps/nodelint"]
+ path = deps/nodelint
+ url = https://github.com/tav/nodelint.git
4 node_modules/async/.npmignore
@@ -0,0 +1,4 @@
+deps
+dist
+test
+nodelint.cfg
19 node_modules/async/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Caolan McMahon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
25 node_modules/async/Makefile
@@ -0,0 +1,25 @@
+PACKAGE = asyncjs
+NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
+CWD := $(shell pwd)
+NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit
+UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs
+NODELINT = $(CWD)/node_modules/nodelint/nodelint
+
+BUILDDIR = dist
+
+all: clean test build
+
+build: $(wildcard lib/*.js)
+ mkdir -p $(BUILDDIR)
+ $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js
+
+test:
+ $(NODEUNIT) test
+
+clean:
+ rm -rf $(BUILDDIR)
+
+lint:
+ $(NODELINT) --config nodelint.cfg lib/async.js
+
+.PHONY: test build all
1,022 node_modules/async/README.md
@@ -0,0 +1,1022 @@
+# Async.js
+
+Async is a utility module which provides straight-forward, powerful functions
+for working with asynchronous JavaScript. Although originally designed for
+use with [node.js](http://nodejs.org), it can also be used directly in the
+browser.
+
+Async provides around 20 functions that include the usual 'functional'
+suspects (map, reduce, filter, forEach…) as well as some common patterns
+for asynchronous control flow (parallel, series, waterfall…). All these
+functions assume you follow the node.js convention of providing a single
+callback as the last argument of your async function.
+
+
+## Quick Examples
+
+ async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+ });
+
+ async.filter(['file1','file2','file3'], path.exists, function(results){
+ // results now equals an array of the existing files
+ });
+
+ async.parallel([
+ function(){ ... },
+ function(){ ... }
+ ], callback);
+
+ async.series([
+ function(){ ... },
+ function(){ ... }
+ ]);
+
+There are many more functions available so take a look at the docs below for a
+full list. This module aims to be comprehensive, so if you feel anything is
+missing please create a GitHub issue for it.
+
+
+## Download
+
+Releases are available for download from
+[GitHub](http://github.com/caolan/async/downloads).
+Alternatively, you can install using Node Package Manager (npm):
+
+ npm install async
+
+
+__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed
+
+__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped
+
+
+## In the Browser
+
+So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
+
+ <script type="text/javascript" src="async.js"></script>
+ <script type="text/javascript">
+
+ async.map(data, asyncProcess, function(err, results){
+ alert(results);
+ });
+
+ </script>
+
+
+## Documentation
+
+### Collections
+
+* [forEach](#forEach)
+* [map](#map)
+* [filter](#filter)
+* [reject](#reject)
+* [reduce](#reduce)
+* [detect](#detect)
+* [sortBy](#sortBy)
+* [some](#some)
+* [every](#every)
+* [concat](#concat)
+
+### Control Flow
+
+* [series](#series)
+* [parallel](#parallel)
+* [whilst](#whilst)
+* [until](#until)
+* [waterfall](#waterfall)
+* [queue](#queue)
+* [auto](#auto)
+* [iterator](#iterator)
+* [apply](#apply)
+* [nextTick](#nextTick)
+
+### Utils
+
+* [memoize](#memoize)
+* [unmemoize](#unmemoize)
+* [log](#log)
+* [dir](#dir)
+* [noConflict](#noConflict)
+
+
+## Collections
+
+<a name="forEach" />
+### forEach(arr, iterator, callback)
+
+Applies an iterator function to each item in an array, in parallel.
+The iterator is called with an item from the list and a callback for when it
+has finished. If the iterator passes an error to this callback, the main
+callback for the forEach function is immediately called with the error.
+
+Note, that since this function applies the iterator to each item in parallel
+there is no guarantee that the iterator functions will complete in order.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(err) - A callback which is called after all the iterator functions
+ have finished, or an error has occurred.
+
+__Example__
+
+ // assuming openFiles is an array of file names and saveFile is a function
+ // to save the modified contents of that file:
+
+ async.forEach(openFiles, saveFile, function(err){
+ // if any of the saves produced an error, err would equal that error
+ });
+
+---------------------------------------
+
+<a name="forEachSeries" />
+### forEachSeries(arr, iterator, callback)
+
+The same as forEach only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. This means the iterator functions will complete in order.
+
+
+---------------------------------------
+
+<a name="forEachLimit" />
+### forEachLimit(arr, limit, iterator, callback)
+
+The same as forEach only the iterator is applied to batches of items in the
+array, in series. The next batch of iterators is only called once the current
+one has completed processing.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* limit - How many items should be in each batch.
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(err) - A callback which is called after all the iterator functions
+ have finished, or an error has occurred.
+
+__Example__
+
+ // Assume documents is an array of JSON objects and requestApi is a
+ // function that interacts with a rate-limited REST api.
+
+ async.forEachLimit(documents, 20, requestApi, function(err){
+ // if any of the saves produced an error, err would equal that error
+ });
+---------------------------------------
+
+<a name="map" />
+### map(arr, iterator, callback)
+
+Produces a new array of values by mapping each value in the given array through
+the iterator function. The iterator is called with an item from the array and a
+callback for when it has finished processing. The callback takes 2 arguments,
+an error and the transformed item from the array. If the iterator passes an
+error to this callback, the main callback for the map function is immediately
+called with the error.
+
+Note, that since this function applies the iterator to each item in parallel
+there is no guarantee that the iterator functions will complete in order, however
+the results array will be in the same order as the original array.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed
+ with an error (which can be null) and a transformed item.
+* callback(err, results) - A callback which is called after all the iterator
+ functions have finished, or an error has occurred. Results is an array of the
+ transformed items from the original array.
+
+__Example__
+
+ async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+ });
+
+---------------------------------------
+
+<a name="mapSeries" />
+### mapSeries(arr, iterator, callback)
+
+The same as map only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
+
+
+---------------------------------------
+
+<a name="filter" />
+### filter(arr, iterator, callback)
+
+__Alias:__ select
+
+Returns a new array of all the values which pass an async truth test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like path.exists. This operation is
+performed in parallel, but the results array will be in the same order as the
+original.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(results) - A callback which is called after all the iterator
+ functions have finished.
+
+__Example__
+
+ async.filter(['file1','file2','file3'], path.exists, function(results){
+ // results now equals an array of the existing files
+ });
+
+---------------------------------------
+
+<a name="filterSeries" />
+### filterSeries(arr, iterator, callback)
+
+__alias:__ selectSeries
+
+The same as filter only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
+
+---------------------------------------
+
+<a name="reject" />
+### reject(arr, iterator, callback)
+
+The opposite of filter. Removes values that pass an async truth test.
+
+---------------------------------------
+
+<a name="rejectSeries" />
+### rejectSeries(arr, iterator, callback)
+
+The same as filter, only the iterator is applied to each item in the array
+in series.
+
+
+---------------------------------------
+
+<a name="reduce" />
+### reduce(arr, memo, iterator, callback)
+
+__aliases:__ inject, foldl
+
+Reduces a list of values into a single value using an async iterator to return
+each successive step. Memo is the initial state of the reduction. This
+function only operates in series. For performance reasons, it may make sense to
+split a call to this function into a parallel map, then use the normal
+Array.prototype.reduce on the results. This function is for situations where
+each step in the reduction needs to be async, if you can get the data before
+reducing it then its probably a good idea to do so.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* memo - The initial state of the reduction.
+* iterator(memo, item, callback) - A function applied to each item in the
+ array to produce the next step in the reduction. The iterator is passed a
+ callback which accepts an optional error as its first argument, and the state
+ of the reduction as the second. If an error is passed to the callback, the
+ reduction is stopped and the main callback is immediately called with the
+ error.
+* callback(err, result) - A callback which is called after all the iterator
+ functions have finished. Result is the reduced value.
+
+__Example__
+
+ async.reduce([1,2,3], 0, function(memo, item, callback){
+ // pointless async:
+ process.nextTick(function(){
+ callback(null, memo + item)
+ });
+ }, function(err, result){
+ // result is now equal to the last value of memo, which is 6
+ });
+
+---------------------------------------
+
+<a name="reduceRight" />
+### reduceRight(arr, memo, iterator, callback)
+
+__Alias:__ foldr
+
+Same as reduce, only operates on the items in the array in reverse order.
+
+
+---------------------------------------
+
+<a name="detect" />
+### detect(arr, iterator, callback)
+
+Returns the first value in a list that passes an async truth test. The
+iterator is applied in parallel, meaning the first iterator to return true will
+fire the detect callback with that result. That means the result might not be
+the first item in the original array (in terms of order) that passes the test.
+
+If order within the original array is important then look at detectSeries.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(result) - A callback which is called as soon as any iterator returns
+ true, or after all the iterator functions have finished. Result will be
+ the first item in the array that passes the truth test (iterator) or the
+ value undefined if none passed.
+
+__Example__
+
+ async.detect(['file1','file2','file3'], path.exists, function(result){
+ // result now equals the first file in the list that exists
+ });
+
+---------------------------------------
+
+<a name="detectSeries" />
+### detectSeries(arr, iterator, callback)
+
+The same as detect, only the iterator is applied to each item in the array
+in series. This means the result is always the first in the original array (in
+terms of array order) that passes the truth test.
+
+
+---------------------------------------
+
+<a name="sortBy" />
+### sortBy(arr, iterator, callback)
+
+Sorts a list by the results of running each value through an async iterator.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed
+ with an error (which can be null) and a value to use as the sort criteria.
+* callback(err, results) - A callback which is called after all the iterator
+ functions have finished, or an error has occurred. Results is the items from
+ the original array sorted by the values returned by the iterator calls.
+
+__Example__
+
+ async.sortBy(['file1','file2','file3'], function(file, callback){
+ fs.stat(file, function(err, stats){
+ callback(err, stats.mtime);
+ });
+ }, function(err, results){
+ // results is now the original array of files sorted by
+ // modified date
+ });
+
+
+---------------------------------------
+
+<a name="some" />
+### some(arr, iterator, callback)
+
+__Alias:__ any
+
+Returns true if at least one element in the array satisfies an async test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like path.exists. Once any iterator
+call returns true, the main callback is immediately called.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(result) - A callback which is called as soon as any iterator returns
+ true, or after all the iterator functions have finished. Result will be
+ either true or false depending on the values of the async tests.
+
+__Example__
+
+ async.some(['file1','file2','file3'], path.exists, function(result){
+ // if result is true then at least one of the files exists
+ });
+
+---------------------------------------
+
+<a name="every" />
+### every(arr, iterator, callback)
+
+__Alias:__ all
+
+Returns true if every element in the array satisfies an async test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like path.exists.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+* callback(result) - A callback which is called after all the iterator
+ functions have finished. Result will be either true or false depending on
+ the values of the async tests.
+
+__Example__
+
+ async.every(['file1','file2','file3'], path.exists, function(result){
+ // if result is true then every file exists
+ });
+
+---------------------------------------
+
+<a name="concat" />
+### concat(arr, iterator, callback)
+
+Applies an iterator to each item in a list, concatenating the results. Returns the
+concatenated list. The iterators are called in parallel, and the results are
+concatenated as they return. There is no guarantee that the results array will
+be returned in the original order of the arguments passed to the iterator function.
+
+__Arguments__
+
+* arr - An array to iterate over
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed
+ with an error (which can be null) and an array of results.
+* callback(err, results) - A callback which is called after all the iterator
+ functions have finished, or an error has occurred. Results is an array containing
+ the concatenated results of the iterator function.
+
+__Example__
+
+ async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
+ // files is now a list of filenames that exist in the 3 directories
+ });
+
+---------------------------------------
+
+<a name="concatSeries" />
+### concatSeries(arr, iterator, callback)
+
+Same as async.concat, but executes in series instead of parallel.
+
+
+## Control Flow
+
+<a name="series" />
+### series(tasks, [callback])
+
+Run an array of functions in series, each one running once the previous
+function has completed. If any functions in the series pass an error to its
+callback, no more functions are run and the callback for the series is
+immediately called with the value of the error. Once the tasks have completed,
+the results are passed to the final callback as an array.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final callback as an object
+instead of an array. This can be a more readable way of handling results from
+async.series.
+
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed
+ a callback it must call on completion.
+* callback(err, results) - An optional callback to run once all the functions
+ have completed. This function gets an array of all the arguments passed to
+ the callbacks used in the array.
+
+__Example__
+
+ async.series([
+ function(callback){
+ // do some stuff ...
+ callback(null, 'one');
+ },
+ function(callback){
+ // do some more stuff ...
+ callback(null, 'two');
+ },
+ ],
+ // optional callback
+ function(err, results){
+ // results is now equal to ['one', 'two']
+ });
+
+
+ // an example using an object instead of an array
+ async.series({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ },
+ },
+ function(err, results) {
+ // results is now equal to: {one: 1, two: 2}
+ });
+
+
+---------------------------------------
+
+<a name="parallel" />
+### parallel(tasks, [callback])
+
+Run an array of functions in parallel, without waiting until the previous
+function has completed. If any of the functions pass an error to its
+callback, the main callback is immediately called with the value of the error.
+Once the tasks have completed, the results are passed to the final callback as an
+array.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final callback as an object
+instead of an array. This can be a more readable way of handling results from
+async.parallel.
+
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed a
+ callback it must call on completion.
+* callback(err, results) - An optional callback to run once all the functions
+ have completed. This function gets an array of all the arguments passed to
+ the callbacks used in the array.
+
+__Example__
+
+ async.parallel([
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'one');
+ }, 200);
+ },
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'two');
+ }, 100);
+ },
+ ],
+ // optional callback
+ function(err, results){
+ // in this case, the results array will equal ['two','one']
+ // because the functions were run in parallel and the second
+ // function had a shorter timeout before calling the callback.
+ });
+
+
+ // an example using an object instead of an array
+ async.parallel({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ },
+ },
+ function(err, results) {
+ // results is now equals to: {one: 1, two: 2}
+ });
+
+
+---------------------------------------
+
+<a name="whilst" />
+### whilst(test, fn, callback)
+
+Repeatedly call fn, while test returns true. Calls the callback when stopped,
+or an error occurs.
+
+__Arguments__
+
+* test() - synchronous truth test to perform before each execution of fn.
+* fn(callback) - A function to call each time the test passes. The function is
+ passed a callback which must be called once it has completed with an optional
+ error as the first argument.
+* callback(err) - A callback which is called after the test fails and repeated
+ execution of fn has stopped.
+
+__Example__
+
+ var count = 0;
+
+ async.whilst(
+ function () { return count < 5; },
+ function (callback) {
+ count++;
+ setTimeout(callback, 1000);
+ },
+ function (err) {
+ // 5 seconds have passed
+ }
+ );
+
+
+---------------------------------------
+
+<a name="until" />
+### until(test, fn, callback)
+
+Repeatedly call fn, until test returns true. Calls the callback when stopped,
+or an error occurs.
+
+The inverse of async.whilst.
+
+
+---------------------------------------
+
+<a name="waterfall" />
+### waterfall(tasks, [callback])
+
+Runs an array of functions in series, each passing their results to the next in
+the array. However, if any of the functions pass an error to the callback, the
+next function is not executed and the main callback is immediately called with
+the error.
+
+__Arguments__
+
+* tasks - An array of functions to run, each function is passed a callback it
+ must call on completion.
+* callback(err, [results]) - An optional callback to run once all the functions
+ have completed. This will be passed the results of the last task's callback.
+
+
+
+__Example__
+
+ async.waterfall([
+ function(callback){
+ callback(null, 'one', 'two');
+ },
+ function(arg1, arg2, callback){
+ callback(null, 'three');
+ },
+ function(arg1, callback){
+ // arg1 now equals 'three'
+ callback(null, 'done');
+ }
+ ], function (err, result) {
+ // result now equals 'done'
+ });
+
+
+---------------------------------------
+
+<a name="queue" />
+### queue(worker, concurrency)
+
+Creates a queue object with the specified concurrency. Tasks added to the
+queue will be processed in parallel (up to the concurrency limit). If all
+workers are in progress, the task is queued until one is available. Once
+a worker has completed a task, the task's callback is called.
+
+__Arguments__
+
+* worker(task, callback) - An asynchronous function for processing a queued
+ task.
+* concurrency - An integer for determining how many worker functions should be
+ run in parallel.
+
+__Queue objects__
+
+The queue object returned by this function has the following properties and
+methods:
+
+* length() - a function returning the number of items waiting to be processed.
+* concurrency - an integer for determining how many worker functions should be
+ run in parallel. This property can be changed after a queue is created to
+ alter the concurrency on-the-fly.
+* push(task, [callback]) - add a new task to the queue, the callback is called
+ once the worker has finished processing the task.
+ instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
+* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
+* empty - a callback that is called when the last item from the queue is given to a worker
+* drain - a callback that is called when the last item from the queue has returned from the worker
+
+__Example__
+
+ // create a queue object with concurrency 2
+
+ var q = async.queue(function (task, callback) {
+ console.log('hello ' + task.name);
+ callback();
+ }, 2);
+
+
+ // assign a callback
+ q.drain = function() {
+ console.log('all items have been processed');
+ }
+
+ // add some items to the queue
+
+ q.push({name: 'foo'}, function (err) {
+ console.log('finished processing foo');
+ });
+ q.push({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+ });
+
+ // add some items to the queue (batch-wise)
+
+ q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
+ console.log('finished processing bar');
+ });
+
+
+---------------------------------------
+
+<a name="auto" />
+### auto(tasks, [callback])
+
+Determines the best order for running functions based on their requirements.
+Each function can optionally depend on other functions being completed first,
+and each function is run as soon as its requirements are satisfied. If any of
+the functions pass an error to their callback, that function will not complete
+(so any other functions depending on it will not run) and the main callback
+will be called immediately with the error. Functions also receive an object
+containing the results of functions which have completed so far.
+
+__Arguments__
+
+* tasks - An object literal containing named functions or an array of
+ requirements, with the function itself the last item in the array. The key
+ used for each function or array is used when specifying requirements. The
+ syntax is easier to understand by looking at the example.
+* callback(err, results) - An optional callback which is called when all the
+ tasks have been completed. The callback will receive an error as an argument
+ if any tasks pass an error to their callback. If all tasks complete
+ successfully, it will receive an object containing their results.
+
+__Example__
+
+ async.auto({
+ get_data: function(callback){
+ // async code to get some data
+ },
+ make_folder: function(callback){
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ },
+ write_file: ['get_data', 'make_folder', function(callback){
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ callback(null, filename);
+ }],
+ email_link: ['write_file', function(callback, results){
+ // once the file is written let's email a link to it...
+ // results.write_file contains the filename returned by write_file.
+ }]
+ });
+
+This is a fairly trivial example, but to do this using the basic parallel and
+series functions would look like this:
+
+ async.parallel([
+ function(callback){
+ // async code to get some data
+ },
+ function(callback){
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ }
+ ],
+ function(results){
+ async.series([
+ function(callback){
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ },
+ email_link: function(callback){
+ // once the file is written let's email a link to it...
+ }
+ ]);
+ });
+
+For a complicated series of async tasks using the auto function makes adding
+new tasks much easier and makes the code more readable.
+
+
+---------------------------------------
+
+<a name="iterator" />
+### iterator(tasks)
+
+Creates an iterator function which calls the next function in the array,
+returning a continuation to call the next one after that. Its also possible to
+'peek' the next iterator by doing iterator.next().
+
+This function is used internally by the async module but can be useful when
+you want to manually control the flow of functions in series.
+
+__Arguments__
+
+* tasks - An array of functions to run, each function is passed a callback it
+ must call on completion.
+
+__Example__
+
+ var iterator = async.iterator([
+ function(){ sys.p('one'); },
+ function(){ sys.p('two'); },
+ function(){ sys.p('three'); }
+ ]);
+
+ node> var iterator2 = iterator();
+ 'one'
+ node> var iterator3 = iterator2();
+ 'two'
+ node> iterator3();
+ 'three'
+ node> var nextfn = iterator2.next();
+ node> nextfn();
+ 'three'
+
+
+---------------------------------------
+
+<a name="apply" />
+### apply(function, arguments..)
+
+Creates a continuation function with some arguments already applied, a useful
+shorthand when combined with other control flow functions. Any arguments
+passed to the returned function are added to the arguments originally passed
+to apply.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to automatically apply when the
+ continuation is called.
+
+__Example__
+
+ // using apply
+
+ async.parallel([
+ async.apply(fs.writeFile, 'testfile1', 'test1'),
+ async.apply(fs.writeFile, 'testfile2', 'test2'),
+ ]);
+
+
+ // the same process without using apply
+
+ async.parallel([
+ function(callback){
+ fs.writeFile('testfile1', 'test1', callback);
+ },
+ function(callback){
+ fs.writeFile('testfile2', 'test2', callback);
+ },
+ ]);
+
+It's possible to pass any number of additional arguments when calling the
+continuation:
+
+ node> var fn = async.apply(sys.puts, 'one');
+ node> fn('two', 'three');
+ one
+ two
+ three
+
+---------------------------------------
+
+<a name="nextTick" />
+### nextTick(callback)
+
+Calls the callback on a later loop around the event loop. In node.js this just
+calls process.nextTick, in the browser it falls back to setTimeout(callback, 0),
+which means other higher priority events may precede the execution of the callback.
+
+This is used internally for browser-compatibility purposes.
+
+__Arguments__
+
+* callback - The function to call on a later loop around the event loop.
+
+__Example__
+
+ var call_order = [];
+ async.nextTick(function(){
+ call_order.push('two');
+ // call_order now equals ['one','two]
+ });
+ call_order.push('one')
+
+
+## Utils
+
+<a name="memoize" />
+### memoize(fn, [hasher])
+
+Caches the results of an async function. When creating a hash to store function
+results against, the callback is omitted from the hash and an optional hash
+function can be used.
+
+__Arguments__
+
+* fn - the function you to proxy and cache results from.
+* hasher - an optional function for generating a custom hash for storing
+ results, it has all the arguments applied to it apart from the callback, and
+ must be synchronous.
+
+__Example__
+
+ var slow_fn = function (name, callback) {
+ // do something
+ callback(null, result);
+ };
+ var fn = async.memoize(slow_fn);
+
+ // fn can now be used as if it were slow_fn
+ fn('some name', function () {
+ // callback
+ });
+
+<a name="unmemoize" />
+### unmemoize(fn)
+
+Undoes a memoized function, reverting it to the original, unmemoized
+form. Comes handy in tests.
+
+__Arguments__
+
+* fn - the memoized function
+
+<a name="log" />
+### log(function, arguments)
+
+Logs the result of an async function to the console. Only works in node.js or
+in browsers that support console.log and console.error (such as FF and Chrome).
+If multiple arguments are returned from the async function, console.log is
+called on each argument in order.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to apply to the function.
+
+__Example__
+
+ var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, 'hello ' + name);
+ }, 1000);
+ };
+
+ node> async.log(hello, 'world');
+ 'hello world'
+
+
+---------------------------------------
+
+<a name="dir" />
+### dir(function, arguments)
+
+Logs the result of an async function to the console using console.dir to
+display the properties of the resulting object. Only works in node.js or
+in browsers that support console.dir and console.error (such as FF and Chrome).
+If multiple arguments are returned from the async function, console.dir is
+called on each argument in order.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to apply to the function.
+
+__Example__
+
+ var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, {hello: name});
+ }, 1000);
+ };
+
+ node> async.dir(hello, 'world');
+ {hello: 'world'}
+
+
+---------------------------------------
+
+<a name="noConflict" />
+### noConflict()
+
+Changes the value of async back to its original value, returning a reference to the
+async object.
3  node_modules/async/index.js
@@ -0,0 +1,3 @@
+// This file is just added for convenience so this repository can be
+// directly checked out into a project's deps folder
+module.exports = require('./lib/async');
692 node_modules/async/lib/async.js
@@ -0,0 +1,692 @@
+/*global setTimeout: false, console: false */
+(function () {
+
+ var async = {};
+
+ // global on the server, window in the browser
+ var root = this,
+ previous_async = root.async;
+
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
+ else {
+ root.async = async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ //// cross-browser compatiblity functions ////
+
+ var _forEach = function (arr, iterator) {
+ if (arr.forEach) {
+ return arr.forEach(iterator);
+ }
+ for (var i = 0; i < arr.length; i += 1) {
+ iterator(arr[i], i, arr);
+ }
+ };
+
+ var _map = function (arr, iterator) {
+ if (arr.map) {
+ return arr.map(iterator);
+ }
+ var results = [];
+ _forEach(arr, function (x, i, a) {
+ results.push(iterator(x, i, a));
+ });
+ return results;
+ };
+
+ var _reduce = function (arr, iterator, memo) {
+ if (arr.reduce) {
+ return arr.reduce(iterator, memo);
+ }
+ _forEach(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ };
+
+ var _keys = function (obj) {
+ if (Object.keys) {
+ return Object.keys(obj);
+ }
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+ if (typeof process === 'undefined' || !(process.nextTick)) {
+ async.nextTick = function (fn) {
+ setTimeout(fn, 0);
+ };
+ }
+ else {
+ async.nextTick = process.nextTick;
+ }
+
+ async.forEach = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ _forEach(arr, function (x) {
+ iterator(x, function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed === arr.length) {
+ callback();
+ }
+ }
+ });
+ });
+ };
+
+ async.forEachSeries = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ var iterate = function () {
+ iterator(arr[completed], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed === arr.length) {
+ callback();
+ }
+ else {
+ iterate();
+ }
+ }
+ });
+ };
+ iterate();
+ };
+
+ async.forEachLimit = function (arr, limit, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length || limit <= 0) {
+ return callback();
+ }
+ var completed = 0;
+ var started = 0;
+ var running = 0;
+
+ (function replenish () {
+ if (completed === arr.length) {
+ return callback();
+ }
+
+ while (running < limit && started < arr.length) {
+ iterator(arr[started], function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ running -= 1;
+ if (completed === arr.length) {
+ callback();
+ }
+ else {
+ replenish();
+ }
+ }
+ });
+ started += 1;
+ running += 1;
+ }
+ })();
+ };
+
+
+ var doParallel = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.forEach].concat(args));
+ };
+ };
+ var doSeries = function (fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [async.forEachSeries].concat(args));
+ };
+ };
+
+
+ var _asyncMap = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (err, v) {
+ results[x.index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ };
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.forEachSeries(arr, function (x, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, memo);
+ });
+ };
+ // inject alias
+ async.inject = async.reduce;
+ // foldl alias
+ async.foldl = async.reduce;
+
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, function (x) {
+ return x;
+ }).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+ // foldr alias
+ async.foldr = async.reduceRight;
+
+ var _filter = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.filter = doParallel(_filter);
+ async.filterSeries = doSeries(_filter);
+ // select alias
+ async.select = async.filter;
+ async.selectSeries = async.filterSeries;
+
+ var _reject = function (eachfn, arr, iterator, callback) {
+ var results = [];
+ arr = _map(arr, function (x, i) {
+ return {index: i, value: x};
+ });
+ eachfn(arr, function (x, callback) {
+ iterator(x.value, function (v) {
+ if (!v) {
+ results.push(x);
+ }
+ callback();
+ });
+ }, function (err) {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ };
+ async.reject = doParallel(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ var _detect = function (eachfn, arr, iterator, main_callback) {
+ eachfn(arr, function (x, callback) {
+ iterator(x, function (result) {
+ if (result) {
+ main_callback(x);
+ main_callback = function () {};
+ }
+ else {
+ callback();
+ }
+ });
+ }, function (err) {
+ main_callback();
+ });
+ };
+ async.detect = doParallel(_detect);
+ async.detectSeries = doSeries(_detect);
+
+ async.some = function (arr, iterator, main_callback) {
+ async.forEach(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ main_callback(true);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(false);
+ });
+ };
+ // any alias
+ async.any = async.some;
+
+ async.every = function (arr, iterator, main_callback) {
+ async.forEach(arr, function (x, callback) {
+ iterator(x, function (v) {
+ if (!v) {
+ main_callback(false);
+ main_callback = function () {};
+ }
+ callback();
+ });
+ }, function (err) {
+ main_callback(true);
+ });
+ };
+ // all alias
+ async.all = async.every;
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });