From 905b3258fb7079a397f178d5517987116898457e Mon Sep 17 00:00:00 2001 From: Stuiart Stent Date: Sat, 26 Jan 2013 17:00:58 -0500 Subject: [PATCH] started moving content to corretn divs --- .idea/copyright/profiles_settings.xml | 5 + .idea/libraries/sass_stdlib.xml | 9 + .idea/scopes/scope_settings.xml | 5 + node_modules/connect-mongo/.travis.yml | 5 + node_modules/connect-mongo/Readme.md | 103 + node_modules/connect-mongo/index.js | 4 + .../connect-mongo/lib/connect-mongo.js | 299 ++ .../node_modules/mongodb/.travis.yml | 5 + .../node_modules/mongodb/CONTRIBUTING.md | 23 + .../node_modules/mongodb/Makefile | 64 + .../node_modules/mongodb/Readme.md | 442 +++ .../node_modules/mongodb/index.js | 1 + .../node_modules/mongodb/install.js | 40 + .../node_modules/mongodb/lib/mongodb/admin.js | 338 +++ .../mongodb/lib/mongodb/collection.js | 1726 ++++++++++++ .../lib/mongodb/commands/base_command.js | 29 + .../lib/mongodb/commands/db_command.js | 240 ++ .../lib/mongodb/commands/delete_command.js | 114 + .../lib/mongodb/commands/get_more_command.js | 83 + .../lib/mongodb/commands/insert_command.js | 147 + .../mongodb/commands/kill_cursor_command.js | 98 + .../lib/mongodb/commands/query_command.js | 261 ++ .../lib/mongodb/commands/update_command.js | 174 ++ .../mongodb/lib/mongodb/connection/base.js | 50 + .../lib/mongodb/connection/connection.js | 440 +++ .../lib/mongodb/connection/connection_pool.js | 249 ++ .../mongodb/connection/connection_utils.js | 23 + .../mongodb/lib/mongodb/connection/mongos.js | 333 +++ .../lib/mongodb/connection/read_preference.js | 66 + .../lib/mongodb/connection/repl_set.js | 1311 +++++++++ .../mongodb/lib/mongodb/connection/server.js | 860 ++++++ .../connection/strategies/ping_strategy.js | 188 ++ .../strategies/statistics_strategy.js | 78 + .../lib/mongodb/connection/url_parser.js | 223 ++ .../mongodb/lib/mongodb/cursor.js | 902 ++++++ .../mongodb/lib/mongodb/cursorstream.js | 151 + .../node_modules/mongodb/lib/mongodb/db.js | 2212 +++++++++++++++ .../mongodb/lib/mongodb/gridfs/chunk.js | 213 ++ .../mongodb/lib/mongodb/gridfs/grid.js | 103 + .../mongodb/lib/mongodb/gridfs/gridstore.js | 1486 ++++++++++ .../mongodb/lib/mongodb/gridfs/readstream.js | 188 ++ .../node_modules/mongodb/lib/mongodb/index.js | 69 + .../mongodb/lib/mongodb/mongo_client.js | 116 + .../lib/mongodb/responses/mongo_reply.js | 140 + .../node_modules/mongodb/lib/mongodb/utils.js | 97 + .../mongodb/node_modules/bson/.travis.yml | 5 + .../mongodb/node_modules/bson/Makefile | 16 + .../mongodb/node_modules/bson/README.md | 1 + .../bson/benchmarks/benchmarks.js | 130 + .../mongodb/node_modules/bson/binding.gyp | 17 + .../mongodb/node_modules/bson/build/Makefile | 359 +++ .../build/Release/.deps/Release/bson.node.d | 1 + .../Release/obj.target/bson/ext/bson.o.d | 34 + .../node_modules/bson/build/Release/bson.node | Bin 0 -> 45292 bytes .../bson/build/Release/linker.lock | 0 .../build/Release/obj.target/bson/ext/bson.o | Bin 0 -> 256544 bytes .../node_modules/bson/build/binding.Makefile | 6 + .../node_modules/bson/build/bson.target.mk | 145 + .../node_modules/bson/build/config.gypi | 28 + .../node_modules/bson/build/gyp-mac-tool | 210 ++ .../mongodb/node_modules/bson/ext/Makefile | 28 + .../mongodb/node_modules/bson/ext/bson.cc | 1020 +++++++ .../mongodb/node_modules/bson/ext/bson.h | 273 ++ .../mongodb/node_modules/bson/ext/index.js | 30 + .../bson/ext/win32/ia32/bson.node | Bin 0 -> 118272 bytes .../node_modules/bson/ext/win32/x64/bson.node | Bin 0 -> 134656 bytes .../mongodb/node_modules/bson/ext/wscript | 39 + .../mongodb/node_modules/bson/install.js | 56 + .../node_modules/bson/lib/bson/binary.js | 332 +++ .../bson/lib/bson/binary_parser.js | 385 +++ .../node_modules/bson/lib/bson/bson.js | 1495 ++++++++++ .../node_modules/bson/lib/bson/code.js | 25 + .../node_modules/bson/lib/bson/db_ref.js | 31 + .../node_modules/bson/lib/bson/double.js | 33 + .../bson/lib/bson/float_parser.js | 121 + .../node_modules/bson/lib/bson/index.js | 74 + .../node_modules/bson/lib/bson/long.js | 854 ++++++ .../node_modules/bson/lib/bson/max_key.js | 13 + .../node_modules/bson/lib/bson/min_key.js | 13 + .../node_modules/bson/lib/bson/objectid.js | 253 ++ .../node_modules/bson/lib/bson/symbol.js | 48 + .../node_modules/bson/lib/bson/timestamp.js | 853 ++++++ .../mongodb/node_modules/bson/package.json | 58 + .../bson/test/browser/bson_test.js | 260 ++ .../bson/test/browser/nodeunit.js | 2034 ++++++++++++++ .../node_modules/bson/test/browser/suite2.js | 13 + .../node_modules/bson/test/browser/suite3.js | 7 + .../node_modules/bson/test/browser/test.html | 30 + .../bson/test/node/bson_array_test.js | 240 ++ .../test/node/bson_parser_comparision_test.js | 482 ++++ .../node_modules/bson/test/node/bson_test.js | 1671 +++++++++++ .../bson/test/node/bson_typed_array_test.js | 392 +++ .../bson/test/node/data/test_gs_weird_bug.png | Bin 0 -> 52184 bytes .../bson/test/node/test_full_bson.js | 315 +++ .../bson/test/node/to_bson_test.js | 109 + .../bson/test/node/tools/utils.js | 80 + .../mongodb/node_modules/bson/tools/gleak.js | 21 + .../bson/tools/jasmine-1.1.0/MIT.LICENSE | 20 + .../bson/tools/jasmine-1.1.0/jasmine-html.js | 190 ++ .../bson/tools/jasmine-1.1.0/jasmine.css | 166 ++ .../bson/tools/jasmine-1.1.0/jasmine.js | 2476 +++++++++++++++++ .../tools/jasmine-1.1.0/jasmine_favicon.png | Bin 0 -> 905 bytes .../node_modules/mongodb/package.json | 225 ++ .../node_modules/mongodb/upload.py | 2347 ++++++++++++++++ node_modules/connect-mongo/package.json | 51 + routes/middleware/musclearray.js | 25 + views/workouts/recent.jade | 7 + 107 files changed, 31860 insertions(+) create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/libraries/sass_stdlib.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 node_modules/connect-mongo/.travis.yml create mode 100644 node_modules/connect-mongo/Readme.md create mode 100644 node_modules/connect-mongo/index.js create mode 100644 node_modules/connect-mongo/lib/connect-mongo.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/.travis.yml create mode 100644 node_modules/connect-mongo/node_modules/mongodb/CONTRIBUTING.md create mode 100644 node_modules/connect-mongo/node_modules/mongodb/Makefile create mode 100644 node_modules/connect-mongo/node_modules/mongodb/Readme.md create mode 100644 node_modules/connect-mongo/node_modules/mongodb/index.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/install.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/admin.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/collection.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/base_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/db_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/delete_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/get_more_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/insert_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/query_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/update_command.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/base.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_pool.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_utils.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/mongos.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/read_preference.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/repl_set.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/server.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/url_parser.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursor.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursorstream.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/db.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/chunk.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/grid.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/readstream.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/index.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/mongo_client.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/utils.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/.travis.yml create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/Makefile create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/README.md create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/binding.gyp create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Makefile create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/bson.node create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/linker.lock create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/binding.Makefile create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/bson.target.mk create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/config.gypi create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/gyp-mac-tool create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/Makefile create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/bson.cc create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/bson.h create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/index.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/wscript create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/install.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/bson.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/code.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/double.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/index.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/long.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/package.json create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite2.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite3.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/test.html create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/gleak.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js create mode 100644 node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png create mode 100644 node_modules/connect-mongo/node_modules/mongodb/package.json create mode 100644 node_modules/connect-mongo/node_modules/mongodb/upload.py create mode 100644 node_modules/connect-mongo/package.json create mode 100644 routes/middleware/musclearray.js create mode 100644 views/workouts/recent.jade diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..b385f01 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/libraries/sass_stdlib.xml b/.idea/libraries/sass_stdlib.xml new file mode 100644 index 0000000..0c35d1e --- /dev/null +++ b/.idea/libraries/sass_stdlib.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..0d5175c --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/node_modules/connect-mongo/.travis.yml b/node_modules/connect-mongo/.travis.yml new file mode 100644 index 0000000..cff864b --- /dev/null +++ b/node_modules/connect-mongo/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.8 +services: mongodb + diff --git a/node_modules/connect-mongo/Readme.md b/node_modules/connect-mongo/Readme.md new file mode 100644 index 0000000..a592702 --- /dev/null +++ b/node_modules/connect-mongo/Readme.md @@ -0,0 +1,103 @@ +# connect-mongo + + MongoDB session store for Connect + + [![Build Status](https://secure.travis-ci.org/kcbanner/connect-mongo.png?branch=master)](http://travis-ci.org/kcbanner/connect-mongo) + +## Installation + +connect-mongo supports only connect `>= 1.0.3`. + +via npm: + + $ npm install connect-mongo + +## Options + + - `db` Database name OR fully instantiated node-mongo-native object + - `collection` Collection (optional, default: `sessions`) + - `host` MongoDB server hostname (optional, default: `127.0.0.1`) + - `port` MongoDB server port (optional, default: `27017`) + - `username` Username (optional) + - `password` Password (optional) + - `auto_reconnect` This is passed directly to the MongoDB `Server` constructor as the auto_reconnect + option (optional, default: false). + - `url` Connection url of the form: `mongodb://user:pass@host:port/database/collection`. + If provided, information in the URL takes priority over the other options. + - `mongoose_connection` in the form: `someMongooseDb.connections[0]` to use an existing mongoose connection. (optional) + - `stringify` If true, connect-mongo will serialize sessions using `JSON.stringify` before + setting them, and deserialize them with `JSON.parse` when getting them. + (optional, default: true). This is useful if you are using types that + MongoDB doesn't support. + +The second parameter to the `MongoStore` constructor is a callback which will be called once the database connection is established. +This is mainly used for the tests, however you can use this callback if you want to wait until the store has connected before +starting your app. + +## Example + +With express: + + var express = require('express'); + var MongoStore = require('connect-mongo')(express); + + app.use(express.session({ + secret: settings.cookie_secret, + store: new MongoStore({ + db: settings.db + }) + })); + +With connect: + + var connect = require('connect'); + var MongoStore = require('connect-mongo')(connect); + +## Removing expired sessions + + connect-mongo uses MongoDB's TTL collection feature (2.2+) to + have mongod automatically remove expired sessions. (mongod runs this + check every minute.) + + **Note:** By connect/express's default, session cookies are set to + expire when the user closes their browser (maxAge: null). In accordance + with standard industry practices, connect-mongo will set these sessions + to expire two weeks from their last 'set'. You can override this + behavior by manually setting the maxAge for your cookies -- just keep in + mind that any value less than 60 seconds is pointless, as mongod will + only delete expired documents in a TTL collection every minute. + + For more information, consult connect's [session documentation](http://www.senchalabs.org/connect/session.html) + +## Tests + +You need `mocha`. + + make test + +The tests use a database called `connect-mongo-test`. + +## License + +(The MIT License) + +Copyright (c) 2011 Casey Banner <kcbanner@gmail.com> + +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. diff --git a/node_modules/connect-mongo/index.js b/node_modules/connect-mongo/index.js new file mode 100644 index 0000000..76aa71b --- /dev/null +++ b/node_modules/connect-mongo/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.CONNECT_MONGO_COV + ? require('./lib-cov/connect-mongo') + : require('./lib/connect-mongo'); \ No newline at end of file diff --git a/node_modules/connect-mongo/lib/connect-mongo.js b/node_modules/connect-mongo/lib/connect-mongo.js new file mode 100644 index 0000000..59c489b --- /dev/null +++ b/node_modules/connect-mongo/lib/connect-mongo.js @@ -0,0 +1,299 @@ +/*! + * connect-mongo + * Copyright(c) 2011 Casey Banner + * MIT Licensed + */ + +/** + * Module dependencies + */ + +var mongo = require('mongodb'); +var url = require('url'); + + +/** + * Default options + */ + +var defaultOptions = {host: '127.0.0.1', + port: 27017, + stringify: true, + collection: 'sessions', + auto_reconnect: false, + w: 1}; + +module.exports = function(connect) { + var Store = connect.session.Store; + + /** + * Initialize MongoStore with the given `options`. + * Calls `callback` when db connection is ready (mainly for testing purposes). + * + * @param {Object} options + * @param {Function} callback + * @api public + */ + + function MongoStore(options, callback) { + options = options || {}; + Store.call(this, options); + + if(options.url) { + var db_url = url.parse(options.url); + + if (db_url.port) { + options.port = parseInt(db_url.port); + } + + if (db_url.pathname != undefined) { + var pathname = db_url.pathname.split('/'); + + if (pathname.length >= 2 && pathname[1]) { + options.db = pathname[1]; + } + + if (pathname.length >= 3 && pathname[2]) { + options.collection = pathname[2]; + } + } + + if (db_url.hostname != undefined) { + options.host = db_url.hostname; + } + + if (db_url.auth != undefined) { + var auth = db_url.auth.split(':'); + + if (auth.length >= 1) { + options.username = auth[0]; + } + + if (auth.length >= 2) { + options.password = auth[1]; + } + } + } + + if (options.mongoose_connection){ + if (options.mongoose_connection.user && options.mongoose_connection.pass) { + options.username = options.mongoose_connection.user; + options.password = options.mongoose_connection.pass; + } + + this.db = new mongo.Db(options.mongoose_connection.db.databaseName, + new mongo.Server(options.mongoose_connection.db.serverConfig.host, + options.mongoose_connection.db.serverConfig.port, + options.mongoose_connection.db.serverConfig.options + ), + { w: options.w || defaultOptions.w }); + + } else { + if(!options.db) { + throw new Error('Required MongoStore option `db` missing'); + } + + if (typeof options.db == "object") { + this.db = options.db; // Assume it's an instantiated DB Object + } + else { + this.db = new mongo.Db(options.db, + new mongo.Server(options.host || defaultOptions.host, + options.port || defaultOptions.port, + { + auto_reconnect: options.auto_reconnect || + defaultOptions.auto_reconnect + }), + { w: options.w || defaultOptions.w }); + } + } + this.db_collection_name = options.collection || defaultOptions.collection; + + if (options.hasOwnProperty('stringify') ? + options.stringify : defaultOptions.stringify) { + this._serialize_session = JSON.stringify; + this._unserialize_session = JSON.parse; + } else { + this._serialize_session = function(x) { return x; }; + this._unserialize_session = function(x) { return x; }; + } + + var self = this; + this._get_collection = function(callback) { + if (self.collection) { + callback && callback(self.collection); + } else { + self.db.collection(self.db_collection_name, function(err, collection) { + if (err) { + throw new Error('Error getting collection: ' + self.db_collection_name); + } else { + self.collection = collection; + + // Make sure we have a TTL index on "expires", so mongod will automatically + // remove expired sessions. expireAfterSeconds is set to 0 because we want + // mongo to remove anything expired without any additional delay. + self.collection.ensureIndex({expires: 1}, {expireAfterSeconds: 0}, function(err, result) { + if (err) { + throw new Error('Error setting TTL index on collection : ' + self.db_collection_name); + } + + callback && callback(self.collection); + }); + + } + }); + } + }; + if (this.db.openCalled) { + this._get_collection(callback) + } + else { + this.db.open(function(err, db) { + if (err) { + throw new Error('Error connecting to database'); + } + + if (options.username && options.password) { + db.authenticate(options.username, options.password, function () { + self._get_collection(callback); + }); + } else { + self._get_collection(callback); + } + }); + } + }; + + /** + * Inherit from `Store`. + */ + + MongoStore.prototype.__proto__ = Store.prototype; + + /** + * Attempt to fetch session by the given `sid`. + * + * @param {String} sid + * @param {Function} callback + * @api public + */ + + MongoStore.prototype.get = function(sid, callback) { + var self = this; + this._get_collection(function(collection) { + collection.findOne({_id: sid}, function(err, session) { + try { + if (err) { + callback && callback(err, null); + } else { + + if (session) { + if (!session.expires || new Date < session.expires) { + callback(null, self._unserialize_session(session.session)); + } else { + self.destroy(sid, callback); + } + } else { + callback && callback(); + } + } + } catch (err) { + callback && callback(err); + } + }); + }); + }; + + /** + * Commit the given `sess` object associated with the given `sid`. + * + * @param {String} sid + * @param {Session} sess + * @param {Function} callback + * @api public + */ + + MongoStore.prototype.set = function(sid, session, callback) { + try { + var s = {_id: sid, session: this._serialize_session(session)}; + + if (session && session.cookie) { + if (session.cookie._expires) { + s.expires = new Date(session.cookie._expires); + } else { + // If there's no expiration date specified, it is + // browser-session cookie, as per the connect docs. + // So we set the expiration to two-weeks from now, + // as is common practice in the industry (e.g Django). + var today = new Date(), + twoWeeks = 1000 * 60 * 60 * 24 * 14; + s.expires = new Date(today.getTime() + twoWeeks); + } + } + + this._get_collection(function(collection) { + collection.update({_id: sid}, s, {upsert: true, safe: true}, function(err, data) { + if (err) { + callback && callback(err); + } else { + callback && callback(null); + } + }); + }); + } catch (err) { + callback && callback(err); + } + }; + + /** + * Destroy the session associated with the given `sid`. + * + * @param {String} sid + * @param {Function} callback + * @api public + */ + + MongoStore.prototype.destroy = function(sid, callback) { + this._get_collection(function(collection) { + collection.remove({_id: sid}, function() { + callback && callback(); + }); + }); + }; + + /** + * Fetch number of sessions. + * + * @param {Function} callback + * @api public + */ + + MongoStore.prototype.length = function(callback) { + this._get_collection(function(collection) { + collection.count({}, function(err, count) { + if (err) { + callback && callback(err); + } else { + callback && callback(null, count); + } + }); + }); + }; + + /** + * Clear all sessions. + * + * @param {Function} callback + * @api public + */ + + MongoStore.prototype.clear = function(callback) { + this._get_collection(function(collection) { + collection.drop(function() { + callback && callback(); + }); + }); + }; + + return MongoStore; +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/.travis.yml b/node_modules/connect-mongo/node_modules/mongodb/.travis.yml new file mode 100644 index 0000000..94740d0 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 # development version of 0.8, may be unstable \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/CONTRIBUTING.md b/node_modules/connect-mongo/node_modules/mongodb/CONTRIBUTING.md new file mode 100644 index 0000000..2a1c52e --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/CONTRIBUTING.md @@ -0,0 +1,23 @@ +## Contributing to the driver + +### Bugfixes + +- Before starting to write code, look for existing [tickets](https://github.com/mongodb/node-mongodb-native/issues) or [create one](https://github.com/mongodb/node-mongodb-native/issues/new) for your specific issue. That way you avoid working on something that might not be of interest or that has been addressed already in a different branch. +- Fork the [repo](https://github.com/mongodb/node-mongodb-native) _or_ for small documentation changes, navigate to the source on github and click the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. +- Follow the general coding style of the rest of the project: + - 2 space tabs + - no trailing whitespace + - comma last + - inline documentation for new methods, class members, etc + - 0 space between conditionals/functions, and their parenthesis and curly braces + - `if(..) {` + - `for(..) {` + - `while(..) {` + - `function(err) {` +- Write tests and make sure they pass (execute `make test` from the cmd line to run the test suite). + +### Documentation + +To contribute to the [API documentation](http://mongodb.github.com/node-mongodb-native/) just make your changes to the inline documentation of the appropriate [source code](https://github.com/mongodb/node-mongodb-native/tree/master/docs) in the master branch and submit a [pull request](https://help.github.com/articles/using-pull-requests/). You might also use the github [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. + +If you'd like to preview your documentation changes, first commit your changes to your local master branch, then execute `make generate_docs`. Make sure you have the python documentation framework sphinx installed `easy_install sphinx`. The docs are generated under `docs/build'. If all looks good, submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the master branch with your changes. \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/Makefile b/node_modules/connect-mongo/node_modules/mongodb/Makefile new file mode 100644 index 0000000..ac55626 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/Makefile @@ -0,0 +1,64 @@ +NODE = node +NPM = npm +NODEUNIT = node_modules/nodeunit/bin/nodeunit +DOX = node_modules/dox/bin/dox +name = all + +total: build_native + +test-coverage: + rm -rf lib-cov/ + jscoverage lib/ lib-cov/ + @TEST_COVERAGE=true nodeunit test/ test/gridstore test/connection + +build_native: + +test: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --noreplicaset --boot + +test_pure: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --noreplicaset --boot --nonative + +test_junit: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --junit --noreplicaset --nokill + +jenkins: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --junit --noreplicaset --nokill + +test_nodeunit_pure: + @echo "\n == Execute Test Suite using Pure JS BSON Parser == " + @$(NODEUNIT) test/ test/gridstore test/bson + +test_nodeunit_replicaset_pure: + @echo "\n == Execute Test Suite using Pure JS BSON Parser == " + @$(NODEUNIT) test/replicaset + +test_nodeunit_native: + @echo "\n == Execute Test Suite using Native BSON Parser == " + @TEST_NATIVE=TRUE $(NODEUNIT) test/ test/gridstore test/bson + +test_nodeunit_replicaset_native: + @echo "\n == Execute Test Suite using Native BSON Parser == " + @TEST_NATIVE=TRUE $(NODEUNIT) test/replicaset + +test_all: build_native + @echo "\n == Run All tests ==" + $(NODE) dev/tools/test_all.js --boot + +test_all_junit: build_native + @echo "\n == Run All tests ==" + $(NODE) dev/tools/test_all.js --junit --boot + +clean: + rm ./external-libs/bson/bson.node + rm -r ./external-libs/bson/build + +generate_docs: + $(NODE) dev/tools/build-docs.js + make --directory=./docs/sphinx-docs --file=Makefile html + +.PHONY: total diff --git a/node_modules/connect-mongo/node_modules/mongodb/Readme.md b/node_modules/connect-mongo/node_modules/mongodb/Readme.md new file mode 100644 index 0000000..b81e719 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/Readme.md @@ -0,0 +1,442 @@ +Up to date documentation +======================== + +[Documentation](http://mongodb.github.com/node-mongodb-native/) + +Install +======= + +To install the most recent release from npm, run: + + npm install mongodb + +That may give you a warning telling you that bugs['web'] should be bugs['url'], it would be safe to ignore it (this has been fixed in the development version) + +To install the latest from the repository, run:: + + npm install path/to/node-mongodb-native + +Community +========= +Check out the google group [node-mongodb-native](http://groups.google.com/group/node-mongodb-native) for questions/answers from users of the driver. + +Try it live +============ + + +Introduction +============ + +This is a node.js driver for MongoDB. It's a port (or close to a port) of the library for ruby at http://github.com/mongodb/mongo-ruby-driver/. + +A simple example of inserting a document. + +```javascript + var client = new Db('test', new Server("127.0.0.1", 27017, {}), {w: 1}), + test = function (err, collection) { + collection.insert({a:2}, function(err, docs) { + + collection.count(function(err, count) { + test.assertEquals(1, count); + }); + + // Locate all the entries using find + collection.find().toArray(function(err, results) { + test.assertEquals(1, results.length); + test.assertTrue(results[0].a === 2); + + // Let's close the db + client.close(); + }); + }); + }; + + client.open(function(err, p_client) { + client.collection('test_insert', test); + }); +``` + +Data types +========== + +To store and retrieve the non-JSON MongoDb primitives ([ObjectID](http://www.mongodb.org/display/DOCS/Object+IDs), Long, Binary, [Timestamp](http://www.mongodb.org/display/DOCS/Timestamp+data+type), [DBRef](http://www.mongodb.org/display/DOCS/Database+References#DatabaseReferences-DBRef), Code). + +In particular, every document has a unique `_id` which can be almost any type, and by default a 12-byte ObjectID is created. ObjectIDs can be represented as 24-digit hexadecimal strings, but you must convert the string back into an ObjectID before you can use it in the database. For example: + +```javascript + // Get the objectID type + var ObjectID = require('mongodb').ObjectID; + + var idString = '4e4e1638c85e808431000003'; + collection.findOne({_id: new ObjectID(idString)}, console.log) // ok + collection.findOne({_id: idString}, console.log) // wrong! callback gets undefined +``` + +Here are the constructors the non-Javascript BSON primitive types: + +```javascript + // Fetch the library + var mongo = require('mongodb'); + // Create new instances of BSON types + new mongo.Long(numberString) + new mongo.ObjectID(hexString) + new mongo.Timestamp() // the actual unique number is generated on insert. + new mongo.DBRef(collectionName, id, dbName) + new mongo.Binary(buffer) // takes a string or Buffer + new mongo.Code(code, [context]) + new mongo.Symbol(string) + new mongo.MinKey() + new mongo.MaxKey() + new mongo.Double(number) // Force double storage +``` + +The C/C++ bson parser/serializer +-------------------------------- + +If you are running a version of this library has the C/C++ parser compiled, to enable the driver to use the C/C++ bson parser pass it the option native_parser:true like below + +```javascript + // using native_parser: + var client = new Db('integration_tests_20', + new Server("127.0.0.1", 27017), + {native_parser:true}); +``` + +The C++ parser uses the js objects both for serialization and deserialization. + +GitHub information +================== + +The source code is available at http://github.com/mongodb/node-mongodb-native. +You can either clone the repository or download a tarball of the latest release. + +Once you have the source you can test the driver by running + + $ make test + +in the main directory. You will need to have a mongo instance running on localhost for the integration tests to pass. + +Examples +======== + +For examples look in the examples/ directory. You can execute the examples using node. + + $ cd examples + $ node queries.js + +GridStore +========= + +The GridStore class allows for storage of binary files in mongoDB using the mongoDB defined files and chunks collection definition. + +For more information have a look at [Gridstore](https://github.com/mongodb/node-mongodb-native/blob/master/docs/gridfs.md) + +Replicasets +=========== +For more information about how to connect to a replicaset have a look at [Replicasets](https://github.com/mongodb/node-mongodb-native/blob/master/docs/replicaset.md) + +Primary Key Factories +--------------------- + +Defining your own primary key factory allows you to generate your own series of id's +(this could f.ex be to use something like ISBN numbers). The generated the id needs to be a 12 byte long "string". + +Simple example below + +```javascript + // Custom factory (need to provide a 12 byte array); + CustomPKFactory = function() {} + CustomPKFactory.prototype = new Object(); + CustomPKFactory.createPk = function() { + return new ObjectID("aaaaaaaaaaaa"); + } + + var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); + p_client.open(function(err, p_client) { + p_client.dropDatabase(function(err, done) { + p_client.createCollection('test_custom_key', function(err, collection) { + collection.insert({'a':1}, function(err, docs) { + collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { + cursor.toArray(function(err, items) { + test.assertEquals(1, items.length); + + // Let's close the db + p_client.close(); + }); + }); + }); + }); + }); + }); +``` + +Strict mode +----------- + +Each database has an optional strict mode. If it is set then asking for a collection +that does not exist will return an Error object in the callback. Similarly if you +attempt to create a collection that already exists. Strict is provided for convenience. + +```javascript + var error_client = new Db('integration_tests_', new Server("127.0.0.1", 27017, {auto_reconnect: false}), {strict:true}); + test.assertEquals(true, error_client.strict); + + error_client.open(function(err, error_client) { + error_client.collection('does-not-exist', function(err, collection) { + test.assertTrue(err instanceof Error); + test.assertEquals("Collection does-not-exist does not exist. Currently in strict mode.", err.message); + }); + + error_client.createCollection('test_strict_access_collection', function(err, collection) { + error_client.collection('test_strict_access_collection', function(err, collection) { + test.assertTrue(collection instanceof Collection); + // Let's close the db + error_client.close(); + }); + }); + }); +``` + +Documentation +============= + +If this document doesn't answer your questions, see the source of +[Collection](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/collection.js) +or [Cursor](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/cursor.js), +or the documentation at MongoDB for query and update formats. + +Find +---- + +The find method is actually a factory method to create +Cursor objects. A Cursor lazily uses the connection the first time +you call `nextObject`, `each`, or `toArray`. + +The basic operation on a cursor is the `nextObject` method +that fetches the next matching document from the database. The convenience +methods `each` and `toArray` call `nextObject` until the cursor is exhausted. + +Signatures: + +```javascript + var cursor = collection.find(query, [fields], options); + cursor.sort(fields).limit(n).skip(m). + + cursor.nextObject(function(err, doc) {}); + cursor.each(function(err, doc) {}); + cursor.toArray(function(err, docs) {}); + + cursor.rewind() // reset the cursor to its initial state. +``` + +Useful chainable methods of cursor. These can optionally be options of `find` instead of method calls: + +* `.limit(n).skip(m)` to control paging. +* `.sort(fields)` Order by the given fields. There are several equivalent syntaxes: + * `.sort({field1: -1, field2: 1})` descending by field1, then ascending by field2. + * `.sort([['field1', 'desc'], ['field2', 'asc']])` same as above + * `.sort([['field1', 'desc'], 'field2'])` same as above + * `.sort('field1')` ascending by field1 + +Other options of `find`: + +* `fields` the fields to fetch (to avoid transferring the entire document) +* `tailable` if true, makes the cursor [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors). +* `batchSize` The number of the subset of results to request the database +to return for every request. This should initially be greater than 1 otherwise +the database will automatically close the cursor. The batch size can be set to 1 +with `batchSize(n, function(err){})` after performing the initial query to the database. +* `hint` See [Optimization: hint](http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint). +* `explain` turns this into an explain query. You can also call +`explain()` on any cursor to fetch the explanation. +* `snapshot` prevents documents that are updated while the query is active +from being returned multiple times. See more +[details about query snapshots](http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database). +* `timeout` if false, asks MongoDb not to time out this cursor after an +inactivity period. + + +For information on how to create queries, see the +[MongoDB section on querying](http://www.mongodb.org/display/DOCS/Querying). + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.find({}, {limit:10}).toArray(function(err, docs) { + console.dir(docs); + }); + }); +``` + +Insert +------ + +Signature: + +```javascript + collection.insert(docs, options, [callback]); +``` + +where `docs` can be a single document or an array of documents. + +Useful options: + +* `safe:true` Should always set if you have a callback. + +See also: [MongoDB docs for insert](http://www.mongodb.org/display/DOCS/Inserting). + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.insert({hello: 'world'}, {safe:true}, + function(err, objects) { + if (err) console.warn(err.message); + if (err && err.message.indexOf('E11000 ') !== -1) { + // this _id was already inserted in the database + } + }); + }); +``` + +Note that there's no reason to pass a callback to the insert or update commands +unless you use the `safe:true` option. If you don't specify `safe:true`, then +your callback will be called immediately. + +Update; update and insert (upsert) +---------------------------------- + +The update operation will update the first document that matches your query +(or all documents that match if you use `multi:true`). +If `safe:true`, `upsert` is not set, and no documents match, your callback will return 0 documents updated. + +See the [MongoDB docs](http://www.mongodb.org/display/DOCS/Updating) for +the modifier (`$inc`, `$set`, `$push`, etc.) formats. + +Signature: + +```javascript + collection.update(criteria, objNew, options, [callback]); +``` + +Useful options: + +* `safe:true` Should always set if you have a callback. +* `multi:true` If set, all matching documents are updated, not just the first. +* `upsert:true` Atomically inserts the document if no documents matched. + +Example for `update`: + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.update({hi: 'here'}, {$set: {hi: 'there'}}, {safe:true}, + function(err) { + if (err) console.warn(err.message); + else console.log('successfully updated'); + }); + }); +``` + +Find and modify +--------------- + +`findAndModify` is like `update`, but it also gives the updated document to +your callback. But there are a few key differences between findAndModify and +update: + + 1. The signatures differ. + 2. You can only findAndModify a single item, not multiple items. + +Signature: + +```javascript + collection.findAndModify(query, sort, update, options, callback) +``` + +The sort parameter is used to specify which object to operate on, if more than +one document matches. It takes the same format as the cursor sort (see +Connection.find above). + +See the +[MongoDB docs for findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) +for more details. + +Useful options: + +* `remove:true` set to a true to remove the object before returning +* `new:true` set to true if you want to return the modified object rather than the original. Ignored for remove. +* `upsert:true` Atomically inserts the document if no documents matched. + +Example for `findAndModify`: + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.findAndModify({hello: 'world'}, [['_id','asc']], {$set: {hi: 'there'}}, {}, + function(err, object) { + if (err) console.warn(err.message); + else console.dir(object); // undefined if no matching object exists. + }); + }); +``` + +Save +---- + +The `save` method is a shorthand for upsert if the document contains an +`_id`, or an insert if there is no `_id`. + +Sponsors +======== +Just as Felix Geisendörfer I'm also working on the driver for my own startup and this driver is a big project that also benefits other companies who are using MongoDB. + +If your company could benefit from a even better-engineered node.js mongodb driver I would appreciate any type of sponsorship you may be able to provide. All the sponsors will get a lifetime display in this readme, priority support and help on problems and votes on the roadmap decisions for the driver. If you are interested contact me on [christkv AT g m a i l.com](mailto:christkv@gmail.com) for details. + +And I'm very thankful for code contributions. If you are interested in working on features please contact me so we can discuss API design and testing. + +Release Notes +============= + +See HISTORY + +Credits +======= + +1. [10gen](http://github.com/mongodb/mongo-ruby-driver/) +2. [Google Closure Library](http://code.google.com/closure/library/) +3. [Jonas Raoni Soares Silva](http://jsfromhell.com/classes/binary-parser) + +Contributors +============ + +Aaron Heckmann, Christoph Pojer, Pau Ramon Revilla, Nathan White, Emmerman, Seth LaForge, Boris Filipov, Stefan Schärmeli, Tedde Lundgren, renctan, Sergey Ukustov, Ciaran Jessup, kuno, srimonti, Erik Abele, Pratik Daga, Slobodan Utvic, Kristina Chodorow, Yonathan Randolph, Brian Noguchi, Sam Epstein, James Harrison Fisher, Vladimir Dronnikov, Ben Hockey, Henrik Johansson, Simon Weare, Alex Gorbatchev, Shimon Doodkin, Kyle Mueller, Eran Hammer-Lahav, Marcin Ciszak, François de Metz, Vinay Pulim, nstielau, Adam Wiggins, entrinzikyl, Jeremy Selier, Ian Millington, Public Keating, andrewjstone, Christopher Stott, Corey Jewett, brettkiefer, Rob Holland, Senmiao Liu, heroic, gitfy + +License +======= + + Copyright 2009 - 2012 Christian Amor Kvalheim. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/node_modules/connect-mongo/node_modules/mongodb/index.js b/node_modules/connect-mongo/node_modules/mongodb/index.js new file mode 100644 index 0000000..4f59e9d --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/mongodb'); diff --git a/node_modules/connect-mongo/node_modules/mongodb/install.js b/node_modules/connect-mongo/node_modules/mongodb/install.js new file mode 100644 index 0000000..f9f2a57 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/install.js @@ -0,0 +1,40 @@ +var spawn = require('child_process').spawn, + exec = require('child_process').exec; + +process.stdout.write("================================================================================\n"); +process.stdout.write("= =\n"); +process.stdout.write("= To install with C++ bson parser do =\n"); +process.stdout.write("= =\n"); +process.stdout.write("================================================================================\n"); + +// Check if we want to build the native code +var build_native = process.env['npm_package_config_native'] != null ? process.env['npm_package_config_native'] : 'false'; +build_native = build_native == 'true' ? true : false; +// If we are building the native bson extension ensure we use gmake if available +if(build_native) { + // Check if we need to use gmake + exec('which gmake', function(err, stdout, stderr) { + // Set up spawn command + var make = null; + // No gmake build using make + if(err != null) { + make = spawn('make', ['total']); + } else { + make = spawn('gmake', ['total']); + } + + // Execute spawn + make.stdout.on('data', function(data) { + process.stdout.write(data); + }) + + make.stderr.on('data', function(data) { + process.stdout.write(data); + }) + + make.on('exit', function(code) { + process.stdout.write('child process exited with code ' + code + "\n"); + }) + }); +} + diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/admin.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/admin.js new file mode 100644 index 0000000..2183cf9 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/admin.js @@ -0,0 +1,338 @@ +/*! + * Module dependencies. + */ +var Collection = require('./collection').Collection, + Cursor = require('./cursor').Cursor, + DbCommand = require('./commands/db_command').DbCommand; + +/** + * Allows the user to access the admin functionality of MongoDB + * + * @class Represents the Admin methods of MongoDB. + * @param {Object} db Current db instance we wish to perform Admin operations on. + * @return {Function} Constructor for Admin type. + */ +function Admin(db) { + if(!(this instanceof Admin)) return new Admin(db); + this.db = db; +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from buildInfo or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.buildInfo = function(callback) { + this.serverInfo(callback); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverInfo or null if an error occured. + * @return {null} Returns no result + * @api private + */ +Admin.prototype.serverInfo = function(callback) { + this.db.executeDbAdminCommand({buildinfo:1}, function(err, doc) { + if(err != null) return callback(err, null); + return callback(null, doc.documents[0]); + }); +} + +/** + * Retrieve this db's server status. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverStatus or null if an error occured. + * @return {null} + * @api public + */ +Admin.prototype.serverStatus = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({serverStatus: 1}, function(err, doc) { + if(err == null && doc.documents[0].ok === 1) { + callback(null, doc.documents[0]); + } else { + if(err) return callback(err, false); + return callback(self.db.wrap(doc.documents[0]), false); + } + }); +}; + +/** + * Retrieve the current profiling Level for MongoDB + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingLevel or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.profilingLevel = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({profile:-1}, function(err, doc) { + doc = doc.documents[0]; + + if(err == null && doc.ok === 1) { + var was = doc.was; + if(was == 0) return callback(null, "off"); + if(was == 1) return callback(null, "slow_only"); + if(was == 2) return callback(null, "all"); + return callback(new Error("Error: illegal profiling level value " + was), null); + } else { + err != null ? callback(err, null) : callback(new Error("Error with profile command"), null); + } + }); +}; + +/** + * Ping the MongoDB server and retrieve results + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from ping or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.ping = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + + this.db.executeDbAdminCommand({ping: 1}, callback); +} + +/** + * Authenticate against MongoDB + * + * @param {String} username The user name for the authentication. + * @param {String} password The password for the authentication. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from authenticate or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.authenticate = function(username, password, callback) { + this.db.authenticate(username, password, {authdb: 'admin'}, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Logout current authenticated user + * + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from logout or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.logout = function(callback) { + this.db.logout({authdb: 'admin'}, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Add a user to the MongoDB server, if the user exists it will + * overwrite the current password + * + * Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username The user name for the authentication. + * @param {String} password The password for the authentication. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from addUser or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.addUser = function(username, password, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + options.dbName = 'admin'; + // Add user + this.db.addUser(username, password, options, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Remove a user from the MongoDB server + * + * Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username The user name for the authentication. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from removeUser or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.removeUser = function(username, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options.dbName = 'admin'; + + this.db.removeUser(username, options, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Set the current profiling level of MongoDB + * + * @param {String} level The new profiling level (off, slow_only, all) + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from setProfilingLevel or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.setProfilingLevel = function(level, callback) { + var self = this; + var command = {}; + var profile = 0; + + if(level == "off") { + profile = 0; + } else if(level == "slow_only") { + profile = 1; + } else if(level == "all") { + profile = 2; + } else { + return callback(new Error("Error: illegal profiling level value " + level)); + } + + // Set up the profile number + command['profile'] = profile; + + this.db.executeDbAdminCommand(command, function(err, doc) { + doc = doc.documents[0]; + + if(err == null && doc.ok === 1) + return callback(null, level); + return err != null ? callback(err, null) : callback(new Error("Error with profile command"), null); + }); +}; + +/** + * Retrive the current profiling information for MongoDB + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingInfo or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.profilingInfo = function(callback) { + try { + new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}, {}, {dbName: 'admin'}).toArray(function(err, items) { + return callback(err, items); + }); + } catch (err) { + return callback(err, null); + } +}; + +/** + * Execute a db command against the Admin database + * + * @param {Object} command A command object `{ping:1}`. + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The command always return the whole result of the command as the second parameter. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.command = function(command, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Execute a command + this.db.executeDbAdminCommand(command, options, function(err, doc) { + // Ensure change before event loop executes + return callback != null ? callback(err, doc) : null; + }); +} + +/** + * Validate an existing collection + * + * @param {String} collectionName The name of the collection to validate. + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from validateCollection or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.validateCollection = function(collectionName, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + var self = this; + var command = {validate: collectionName}; + var keys = Object.keys(options); + + // Decorate command with extra options + for(var i = 0; i < keys.length; i++) { + if(options.hasOwnProperty(keys[i])) { + command[keys[i]] = options[keys[i]]; + } + } + + this.db.executeDbCommand(command, function(err, doc) { + if(err != null) return callback(err, null); + doc = doc.documents[0]; + + if(doc.ok === 0) + return callback(new Error("Error with validate command"), null); + if(doc.result != null && doc.result.constructor != String) + return callback(new Error("Error with validation data"), null); + if(doc.result != null && doc.result.match(/exception|corrupt/) != null) + return callback(new Error("Error: invalid collection " + collectionName), null); + if(doc.valid != null && !doc.valid) + return callback(new Error("Error: invalid collection " + collectionName), null); + + return callback(null, doc); + }); +}; + +/** + * List the available databases + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from listDatabases or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.listDatabases = function(callback) { + // Execute the listAllDatabases command + this.db.executeDbAdminCommand({listDatabases:1}, {}, function(err, doc) { + if(err != null) return callback(err, null); + return callback(null, doc.documents[0]); + }); +} + +/** + * Get ReplicaSet status + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from replSetGetStatus or null if an error occured. + * @return {null} + * @api public + */ +Admin.prototype.replSetGetStatus = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({replSetGetStatus:1}, function(err, doc) { + if(err == null && doc.documents[0].ok === 1) + return callback(null, doc.documents[0]); + if(err) return callback(err, false); + return callback(self.db.wrap(doc.documents[0]), false); + }); +}; + +/** + * @ignore + */ +exports.Admin = Admin; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/collection.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/collection.js new file mode 100644 index 0000000..60c6030 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/collection.js @@ -0,0 +1,1726 @@ +/** + * Module dependencies. + * @ignore + */ +var InsertCommand = require('./commands/insert_command').InsertCommand + , QueryCommand = require('./commands/query_command').QueryCommand + , DeleteCommand = require('./commands/delete_command').DeleteCommand + , UpdateCommand = require('./commands/update_command').UpdateCommand + , DbCommand = require('./commands/db_command').DbCommand + , ObjectID = require('bson').ObjectID + , Code = require('bson').Code + , Cursor = require('./cursor').Cursor + , utils = require('./utils'); + +/** + * Precompiled regexes + * @ignore +**/ +const eErrorMessages = /No matching object found/; + +/** + * toString helper. + * @ignore + */ +var toString = Object.prototype.toString; + +/** + * Create a new Collection instance + * + * Options + * - **slaveOk** {Boolean, default:false}, Allow reads from secondaries. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * + * @class Represents a Collection + * @param {Object} db db instance. + * @param {String} collectionName collection name. + * @param {Object} [pkFactory] alternative primary key factory. + * @param {Object} [options] additional options for the collection. + * @return {Object} a collection instance. + */ +function Collection (db, collectionName, pkFactory, options) { + if(!(this instanceof Collection)) return new Collection(db, collectionName, pkFactory, options); + + checkCollectionName(collectionName); + + this.db = db; + this.collectionName = collectionName; + this.internalHint = null; + this.opts = options != null && ('object' === typeof options) ? options : {}; + this.slaveOk = options == null || options.slaveOk == null ? db.slaveOk : options.slaveOk; + this.serializeFunctions = options == null || options.serializeFunctions == null ? db.serializeFunctions : options.serializeFunctions; + this.raw = options == null || options.raw == null ? db.raw : options.raw; + + this.readPreference = options == null || options.readPreference == null ? db.serverConfig.readPreference : options.readPreference; + this.readPreference = this.readPreference == null ? 'primary' : this.readPreference; + + this.pkFactory = pkFactory == null + ? ObjectID + : pkFactory; + + var self = this; +} + +/** + * Inserts a single document or a an array of documents into MongoDB. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **continueOnError/keepGoing** {Boolean, default:false}, keep inserting documents even if one document has an error, *mongodb 1.9.1 >*. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Array|Object} docs + * @param {Object} [options] optional options for insert command + * @param {Function} [callback] optional callback for the function, must be provided when using a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.insert = function insert (docs, options, callback) { + if ('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + var self = this; + insertAll(self, Array.isArray(docs) ? docs : [docs], options, callback); + return this; +}; + +/** + * @ignore + */ +var checkCollectionName = function checkCollectionName (collectionName) { + if ('string' !== typeof collectionName) { + throw Error("collection name must be a String"); + } + + if (!collectionName || collectionName.indexOf('..') != -1) { + throw Error("collection names cannot be empty"); + } + + if (collectionName.indexOf('$') != -1 && + collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null) { + throw Error("collection names must not contain '$'"); + } + + if (collectionName.match(/^\.|\.$/) != null) { + throw Error("collection names must not start or end with '.'"); + } +}; + +/** + * Removes documents specified by `selector` from the db. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **single** {Boolean, default:false}, removes the first document found. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} [selector] optional select, no selector is equivalent to removing all documents. + * @param {Object} [options] additional options during remove. + * @param {Function} [callback] must be provided if you performing a remove with a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.remove = function remove(selector, options, callback) { + if ('function' === typeof selector) { + callback = selector; + selector = options = {}; + } else if ('function' === typeof options) { + callback = options; + options = {}; + } + + // Ensure options + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + // Ensure we have at least an empty selector + selector = selector == null ? {} : selector; + // Set up flags for the command, if we have a single document remove + var flags = 0 | (options.single ? 1 : 0); + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = this.db.databaseName; + } + + // Create a delete command + var deleteCommand = new DeleteCommand( + this.db + , dbName + "." + this.collectionName + , selector + , flags); + + var self = this; + var errorOptions = _getWriteConcern(self, options, callback); + // Execute the command, do not add a callback as it's async + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + var commandOptions = {read:false}; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + // Set safe option + commandOptions['safe'] = true; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + this.db._executeRemoveCommand(deleteCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if(err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + callback(null, error[0].n); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + var result = this.db._executeRemoveCommand(deleteCommand); + // If no callback just return + if (!callback) return; + // If error return error + if (result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(); + } +}; + +/** + * Renames the collection. + * + * @param {String} newName the new name of the collection. + * @param {Function} callback the callback accepting the result + * @return {null} + * @api public + */ +Collection.prototype.rename = function rename (newName, callback) { + var self = this; + // Ensure the new name is valid + checkCollectionName(newName); + // Execute the command, return the new renamed collection if successful + self.db._executeQueryCommand(DbCommand.createRenameCollectionCommand(self.db, self.collectionName, newName), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) { + // Set current object to point to the new name + self.collectionName = newName; + // Return the current collection + callback(null, self); + } + } else if(result.documents[0].errmsg != null) { + if(callback != null) { + err != null ? callback(err, null) : callback(self.db.wrap(result.documents[0]), null); + } + } + }); +}; + +/** + * @ignore + */ +var insertAll = function insertAll (self, docs, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Insert options (flags for insert) + var insertFlags = {}; + // If we have a mongodb version >= 1.9.1 support keepGoing attribute + if(options['keepGoing'] != null) { + insertFlags['keepGoing'] = options['keepGoing']; + } + + // If we have a mongodb version >= 1.9.1 support keepGoing attribute + if(options['continueOnError'] != null) { + insertFlags['continueOnError'] = options['continueOnError']; + } + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = self.db.databaseName; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + insertFlags['serializeFunctions'] = options['serializeFunctions']; + } else { + insertFlags['serializeFunctions'] = self.serializeFunctions; + } + + // Pass in options + var insertCommand = new InsertCommand( + self.db + , dbName + "." + self.collectionName, true, insertFlags); + + // Add the documents and decorate them with id's if they have none + for(var index = 0, len = docs.length; index < len; ++index) { + var doc = docs[index]; + + // Add id to each document if it's not already defined + if (!(Buffer.isBuffer(doc)) && doc['_id'] == null && self.db.forceServerObjectId != true) { + doc['_id'] = self.pkFactory.createPk(); + } + + insertCommand.add(doc); + } + + // Collect errorOptions + var errorOptions = _getWriteConcern(self, options, callback); + // Default command options + var commandOptions = {}; + // If safe is defined check for error message + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + self.db._executeInsertCommand(insertCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if (err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + callback(null, docs); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute the call without a write concern + var result = self.db._executeInsertCommand(insertCommand, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, docs); + } +}; + +/** + * Save a document. Simple full document replacement function. Not recommended for efficiency, use atomic + * operators and update instead for more efficient operations. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} [doc] the document to save + * @param {Object} [options] additional options during remove. + * @param {Function} [callback] must be provided if you performing a safe save + * @return {null} + * @api public + */ +Collection.prototype.save = function save(doc, options, callback) { + if('function' === typeof options) callback = options, options = null; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + // Extract the id, if we have one we need to do a update command + var id = doc['_id']; + var commandOptions = _getWriteConcern(this, options, callback); + + if(id) { + commandOptions.upsert = true; + this.update({ _id: id }, doc, commandOptions, callback); + } else { + this.insert(doc, commandOptions, callback && function (err, docs) { + if (err) return callback(err, null); + + if (Array.isArray(docs)) { + callback(err, docs[0]); + } else { + callback(err, docs); + } + }); + } +}; + +/** + * Updates documents. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **upsert** {Boolean, default:false}, perform an upsert operation. + * - **multi** {Boolean, default:false}, update all documents matching the selector. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} selector the query to select the document/documents to be updated + * @param {Object} document the fields/vals to be updated, or in the case of an upsert operation, inserted. + * @param {Object} [options] additional options during update. + * @param {Function} [callback] must be provided if you performing an update with a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.update = function update(selector, document, options, callback) { + if('function' === typeof options) callback = options, options = null; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = this.db.databaseName; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + options['serializeFunctions'] = options['serializeFunctions']; + } else { + options['serializeFunctions'] = this.serializeFunctions; + } + + var updateCommand = new UpdateCommand( + this.db + , dbName + "." + this.collectionName + , selector + , document + , options); + + var self = this; + // Unpack the error options if any + var errorOptions = _getWriteConcern(this, options, callback); + // If safe is defined check for error message + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + var commandOptions = {read:false}; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + this.db._executeUpdateCommand(updateCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if(err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + // Perform the callback + callback(null, error[0].n, error[0]); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute update + var result = this.db._executeUpdateCommand(updateCommand); + // If no callback just return + if (!callback) return; + // If error return error + if (result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(); + } +}; + +/** + * The distinct command returns returns a list of distinct values for the given key across a collection. + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {String} key key to run distinct against. + * @param {Object} [query] option query to narrow the returned objects. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from distinct or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.distinct = function distinct(key, query, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + query = args.length ? args.shift() : {}; + options = args.length ? args.shift() : {}; + + var mapCommandHash = { + 'distinct': this.collectionName + , 'query': query + , 'key': key + }; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + // Create the command + var cmd = DbCommand.createDbSlaveOkCommand(this.db, mapCommandHash); + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if(err) + return callback(err); + if(result.documents[0].ok != 1) + return callback(new Error(result.documents[0].errmsg)); + callback(null, result.documents[0].values); + }); +}; + +/** + * Count number of matching documents in the db to a query. + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Object} [query] query to filter by before performing count. + * @param {Object} [options] additional options during count. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the count method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.count = function count (query, options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + query = args.length ? args.shift() : {}; + options = args.length ? args.shift() : {}; + + // Final query + var final_query = { + 'count': this.collectionName + , 'query': query + , 'fields': null + }; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + // Set up query options + var queryOptions = QueryCommand.OPTS_NO_CURSOR_TIMEOUT; + if (this.slaveOk || this.db.slaveOk) { + queryOptions |= QueryCommand.OPTS_SLAVE; + } + + var queryCommand = new QueryCommand( + this.db + , this.db.databaseName + ".$cmd" + , queryOptions + , 0 + , -1 + , final_query + , null + ); + + var self = this; + this.db._executeQueryCommand(queryCommand, {read:readPreference}, function (err, result) { + result = result && result.documents; + if(!callback) return; + + if(err) return callback(err); + if (result[0].ok != 1 || result[0].errmsg) return callback(self.db.wrap(result[0])); + callback(null, result[0].n); + }); +}; + + +/** + * Drop the collection + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the drop method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.drop = function drop(callback) { + this.db.dropCollection(this.collectionName, callback); +}; + +/** + * Find and update a document. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **remove** {Boolean, default:false}, set to true to remove the object before returning. + * - **upsert** {Boolean, default:false}, perform an upsert operation. + * - **new** {Boolean, default:false}, set to true if you want to return the modified object rather than the original. Ignored for remove. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} query query object to locate the object to modify + * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate + * @param {Object} doc - the fields/vals to be updated + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndModify method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.findAndModify = function findAndModify (query, sort, doc, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + sort = args.length ? args.shift() : []; + doc = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + var self = this; + + var queryObject = { + 'findandmodify': this.collectionName + , 'query': query + , 'sort': utils.formattedOrderClause(sort) + }; + + queryObject.new = options.new ? 1 : 0; + queryObject.remove = options.remove ? 1 : 0; + queryObject.upsert = options.upsert ? 1 : 0; + + if (options.fields) { + queryObject.fields = options.fields; + } + + if (doc && !options.remove) { + queryObject.update = doc; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + options['serializeFunctions'] = options['serializeFunctions']; + } else { + options['serializeFunctions'] = this.serializeFunctions; + } + + // Unpack the error options if any + var errorOptions = _getWriteConcern(this, options, callback); + + // If we have j, w or something else do the getLast Error path + if(errorOptions != null && typeof errorOptions == 'object') { + // Commands to send + var commands = []; + // Add the find and modify command + commands.push(DbCommand.createDbCommand(this.db, queryObject, options)); + // If we have safe defined we need to return both call results + var chainedCommands = errorOptions != null ? true : false; + // Add error command if we have one + if(chainedCommands) { + commands.push(DbCommand.createGetLastErrorCommand(errorOptions, this.db)); + } + + // Fire commands and + this.db._executeQueryCommand(commands, {read:false}, function(err, result) { + if(err != null) return callback(err); + result = result && result.documents; + + if(result[0].err != null) return callback(self.db.wrap(result[0]), null); + // Workaround due to 1.8.X returning an error on no matching object + // while 2.0.X does not not, making 2.0.X behaviour standard + if(result[0].errmsg != null && !result[0].errmsg.match(eErrorMessages)) + return callback(self.db.wrap(result[0]), null, result[0]); + return callback(null, result[0].value, result[0]); + }); + } else { + // Only run command and rely on getLastError command + var command = DbCommand.createDbCommand(this.db, queryObject, options) + // Execute command + this.db._executeQueryCommand(command, {read:false}, function(err, result) { + if(err != null) return callback(err); + result = result && result.documents; + if(result[0].errmsg != null && !result[0].errmsg.match(eErrorMessages)) + return callback(self.db.wrap(result[0]), null, result[0]); + // If we have an error return it + if(result[0].lastErrorObject && result[0].lastErrorObject.err != null) return callback(self.db.wrap(result[0].lastErrorObject), null); + return callback(null, result[0].value, result[0]); + }); + } +} + +/** + * Find and remove a document + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} query query object to locate the object to modify + * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndRemove method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.findAndRemove = function(query, sort, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + sort = args.length ? args.shift() : []; + options = args.length ? args.shift() : {}; + // Add the remove option + options['remove'] = true; + // Execute the callback + this.findAndModify(query, sort, null, options, callback); +} + +var testForFields = { + limit: 1, sort: 1, fields:1, skip: 1, hint: 1, explain: 1, snapshot: 1, timeout: 1, tailable: 1, tailableRetryInterval: 1 + , numberOfRetries: 1, awaitdata: 1, exhaust: 1, batchSize: 1, returnKey: 1, maxScan: 1, min: 1, max: 1, showDiskLoc: 1 + , comment: 1, raw: 1, readPreference: 1, numberOfRetries: 1, partial: 1, read: 1, dbName: 1 +}; + +/** + * Creates a cursor for a query that can be used to iterate over results from MongoDB + * + * Various argument possibilities + * - callback? + * - selector, callback?, + * - selector, fields, callback? + * - selector, options, callback? + * - selector, fields, options, callback? + * - selector, fields, skip, limit, callback? + * - selector, fields, skip, limit, timeout, callback? + * + * Options + * - **limit** {Number, default:0}, sets the limit of documents returned in the query. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1} + * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination). + * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1} + * - **explain** {Boolean, default:false}, explain the query instead of returning the data. + * - **snapshot** {Boolean, default:false}, snapshot query. + * - **timeout** {Boolean, default:false}, specify if the cursor can timeout. + * - **tailable** {Boolean, default:false}, specify if the cursor is tailable. + * - **tailableRetryInterval** {Number, default:100}, specify the miliseconds between getMores on tailable cursor. + * - **numberOfRetries** {Number, default:5}, specify the number of times to retry the tailable cursor. + * - **awaitdata** {Boolean, default:false} allow the cursor to wait for data, only applicable for tailable cursor. + * - **exhaust** {Boolean, default:false} have the server send all the documents at once as getMore packets, not recommended. + * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results. + * - **returnKey** {Boolean, default:false}, only return the index key. + * - **maxScan** {Number}, Limit the number of items to scan. + * - **min** {Number}, Set index bounds. + * - **max** {Number}, Set index bounds. + * - **showDiskLoc** {Boolean, default:false}, Show disk location of results. + * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler. + * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * - **numberOfRetries** {Number, default:5}, if using awaidata specifies the number of times to retry on timeout. + * - **partial** {Boolean, default:false}, specify if the cursor should return partial results when querying against a sharded system + * + * @param {Object} query query object to locate the object to modify + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the find method or null if an error occured. + * @return {Cursor} returns a cursor to the query + * @api public + */ +Collection.prototype.find = function find () { + var options + , args = Array.prototype.slice.call(arguments, 0) + , has_callback = typeof args[args.length - 1] === 'function' + , has_weird_callback = typeof args[0] === 'function' + , callback = has_callback ? args.pop() : (has_weird_callback ? args.shift() : null) + , len = args.length + , selector = len >= 1 ? args[0] : {} + , fields = len >= 2 ? args[1] : undefined; + + if(len === 1 && has_weird_callback) { + // backwards compat for callback?, options case + selector = {}; + options = args[0]; + } + + if(len === 2 && !Array.isArray(fields)) { + var fieldKeys = Object.getOwnPropertyNames(fields); + var is_option = false; + + for(var i = 0; i < fieldKeys.length; i++) { + if(testForFields[fieldKeys[i]] != null) { + is_option = true; + break; + } + } + + if(is_option) { + options = fields; + fields = undefined; + } else { + options = {}; + } + } else if(len === 2 && Array.isArray(fields) && !Array.isArray(fields[0])) { + var newFields = {}; + // Rewrite the array + for(var i = 0; i < fields.length; i++) { + newFields[fields[i]] = 1; + } + // Set the fields + fields = newFields; + } + + if(3 === len) { + options = args[2]; + } + + // Ensure selector is not null + selector = selector == null ? {} : selector; + // Validate correctness off the selector + var object = selector; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Validate correctness of the field selector + var object = fields; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Check special case where we are using an objectId + if(selector instanceof ObjectID) { + selector = {_id:selector}; + } + + // If it's a serialized fields field we need to just let it through + // user be warned it better be good + if(options && options.fields && !(Buffer.isBuffer(options.fields))) { + fields = {}; + + if(Array.isArray(options.fields)) { + if(!options.fields.length) { + fields['_id'] = 1; + } else { + for (var i = 0, l = options.fields.length; i < l; i++) { + fields[options.fields[i]] = 1; + } + } + } else { + fields = options.fields; + } + } + + if (!options) options = {}; + options.skip = len > 3 ? args[2] : options.skip ? options.skip : 0; + options.limit = len > 3 ? args[3] : options.limit ? options.limit : 0; + options.raw = options.raw != null && typeof options.raw === 'boolean' ? options.raw : this.raw; + options.hint = options.hint != null ? normalizeHintField(options.hint) : this.internalHint; + options.timeout = len == 5 ? args[4] : typeof options.timeout === 'undefined' ? undefined : options.timeout; + // If we have overridden slaveOk otherwise use the default db setting + options.slaveOk = options.slaveOk != null ? options.slaveOk : this.db.slaveOk; + + // Set option + var o = options; + // Support read/readPreference + if(o["read"] != null) o["readPreference"] = o["read"]; + // Set the read preference + o.read = o["readPreference"] ? o.readPreference : this.readPreference; + // Adjust slave ok if read preference is secondary or secondary only + if(o.read == "secondary" || o.read == "secondaryOnly") options.slaveOk = true; + + // callback for backward compatibility + if(callback) { + // TODO refactor Cursor args + callback(null, new Cursor(this.db, this, selector, fields, o)); + } else { + return new Cursor(this.db, this, selector, fields, o); + } +}; + +/** + * Normalizes a `hint` argument. + * + * @param {String|Object|Array} hint + * @return {Object} + * @api private + */ +var normalizeHintField = function normalizeHintField(hint) { + var finalHint = null; + + if (null != hint) { + switch (hint.constructor) { + case String: + finalHint = {}; + finalHint[hint] = 1; + break; + case Object: + finalHint = {}; + for (var name in hint) { + finalHint[name] = hint[name]; + } + break; + case Array: + finalHint = {}; + hint.forEach(function(param) { + finalHint[param] = 1; + }); + break; + } + } + + return finalHint; +}; + +/** + * Finds a single document based on the query + * + * Various argument possibilities + * - callback? + * - selector, callback?, + * - selector, fields, callback? + * - selector, options, callback? + * - selector, fields, options, callback? + * - selector, fields, skip, limit, callback? + * - selector, fields, skip, limit, timeout, callback? + * + * Options + * - **limit** {Number, default:0}, sets the limit of documents returned in the query. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1} + * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination). + * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1} + * - **explain** {Boolean, default:false}, explain the query instead of returning the data. + * - **snapshot** {Boolean, default:false}, snapshot query. + * - **timeout** {Boolean, default:false}, specify if the cursor can timeout. + * - **tailable** {Boolean, default:false}, specify if the cursor is tailable. + * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results. + * - **returnKey** {Boolean, default:false}, only return the index key. + * - **maxScan** {Number}, Limit the number of items to scan. + * - **min** {Number}, Set index bounds. + * - **max** {Number}, Set index bounds. + * - **showDiskLoc** {Boolean, default:false}, Show disk location of results. + * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler. + * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents. + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * - **partial** {Boolean, default:false}, specify if the cursor should return partial results when querying against a sharded system + * + * @param {Object} query query object to locate the object to modify + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findOne method or null if an error occured. + * @return {Cursor} returns a cursor to the query + * @api public + */ +Collection.prototype.findOne = function findOne () { + var self = this; + var args = Array.prototype.slice.call(arguments, 0); + var callback = args.pop(); + var cursor = this.find.apply(this, args).limit(-1).batchSize(1); + // Return the item + cursor.nextObject(function(err, item) { + if(err != null) return callback(err instanceof Error ? err : self.db.wrap(err), null); + callback(null, item); + }); +}; + +/** + * Creates an index on the collection. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the createIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, callback) { + // Clean up call + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options = typeof callback === 'function' ? options : callback; + options = options == null ? {} : options; + + // Collect errorOptions + var errorOptions = _getWriteConcern(this, options, callback); + // Execute create index + this.db.createIndex(this.collectionName, fieldOrSpec, options, callback); +}; + +/** + * Ensures that an index exists, if it does not it creates it + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the ensureIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, callback) { + // Clean up call + if (typeof callback === 'undefined' && typeof options === 'function') { + callback = options; + options = {}; + } + + if (options == null) { + options = {}; + } + + // Execute create index + this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback); +}; + +/** + * Retrieves this collections index info. + * + * Options + * - **full** {Boolean, default:false}, returns the full raw index information. + * + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexInformation method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexInformation = function indexInformation (options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + // Call the index information + this.db.indexInformation(this.collectionName, options, callback); +}; + +/** + * Drops an index from this collection. + * + * @param {String} name + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.dropIndex = function dropIndex (name, callback) { + this.db.dropIndex(this.collectionName, name, callback); +}; + +/** + * Drops all indexes from this collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropAllIndexes method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.dropAllIndexes = function dropIndexes (callback) { + this.db.dropIndex(this.collectionName, '*', function (err, result) { + if(err != null) { + callback(err, false); + } else if(result.documents[0].errmsg == null) { + callback(null, true); + } else { + callback(new Error(result.documents[0].errmsg), false); + } + }); +}; + +/** + * Drops all indexes from this collection. + * + * @deprecated + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndexes method or null if an error occured. + * @return {null} + * @api private + */ +Collection.prototype.dropIndexes = Collection.prototype.dropAllIndexes; + +/** + * Reindex all indexes on the collection + * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the reIndex method or null if an error occured. + * @return {null} + * @api public +**/ +Collection.prototype.reIndex = function(callback) { + this.db.reIndex(this.collectionName, callback); +} + +/** + * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection. + * + * Options + * - **out** {Object, default:*{inline:1}*}, sets the output target for the map reduce job. *{inline:1} | {replace:'collectionName'} | {merge:'collectionName'} | {reduce:'collectionName'}* + * - **query** {Object}, query filter object. + * - **sort** {Object}, sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces. + * - **limit** {Number}, number of objects to return from collection. + * - **keeptemp** {Boolean, default:false}, keep temporary data. + * - **finalize** {Function | String}, finalize function. + * - **scope** {Object}, can pass in variables that can be access from map/reduce/finalize. + * - **jsMode** {Boolean, default:false}, it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X. + * - **verbose** {Boolean, default:false}, provide statistics on job execution time. + * - **readPreference** {String, only for inline results}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Function|String} map the mapping function. + * @param {Function|String} reduce the reduce function. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the mapReduce method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callback) { + if ('function' === typeof options) callback = options, options = {}; + // Out must allways be defined (make sure we don't break weirdly on pre 1.8+ servers) + if(null == options.out) { + throw new Error("the out option parameter must be defined, see mongodb docs for possible values"); + } + + if ('function' === typeof map) { + map = map.toString(); + } + + if ('function' === typeof reduce) { + reduce = reduce.toString(); + } + + if ('function' === typeof options.finalize) { + options.finalize = options.finalize.toString(); + } + + var mapCommandHash = { + mapreduce: this.collectionName + , map: map + , reduce: reduce + }; + + // Add any other options passed in + for (var name in options) { + if ('scope' == name) { + mapCommandHash[name] = processScope(options[name]); + } else { + mapCommandHash[name] = options[name]; + } + } + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + // If we have a read preference and inline is not set as output fail hard + if(readPreference != false && options['out'] != 'inline') { + throw new Error("a readPreference can only be provided when performing an inline mapReduce"); + } + + // self + var self = this; + var cmd = DbCommand.createDbCommand(this.db, mapCommandHash); + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if (err) { + return callback(err); + } + + // + if (1 != result.documents[0].ok || result.documents[0].err || result.documents[0].errmsg) { + return callback(self.db.wrap(result.documents[0])); + } + + // Create statistics value + var stats = {}; + if(result.documents[0].timeMillis) stats['processtime'] = result.documents[0].timeMillis; + if(result.documents[0].counts) stats['counts'] = result.documents[0].counts; + if(result.documents[0].timing) stats['timing'] = result.documents[0].timing; + + // invoked with inline? + if(result.documents[0].results) { + return callback(null, result.documents[0].results, stats); + } + + // The returned collection + var collection = null; + + // If we have an object it's a different db + if(result.documents[0].result != null && typeof result.documents[0].result == 'object') { + var doc = result.documents[0].result; + collection = self.db.db(doc.db).collection(doc.collection); + } else { + // Create a collection object that wraps the result collection + collection = self.db.collection(result.documents[0].result) + } + + // If we wish for no verbosity + if(options['verbose'] == null || !options['verbose']) { + return callback(err, collection); + } + + // Return stats as third set of values + callback(err, collection, stats); + }); +}; + +/** + * Functions that are passed as scope args must + * be converted to Code instances. + * @ignore + */ +function processScope (scope) { + if (!utils.isObject(scope)) { + return scope; + } + + var keys = Object.keys(scope); + var i = keys.length; + var key; + + while (i--) { + key = keys[i]; + if ('function' == typeof scope[key]) { + scope[key] = new Code(String(scope[key])); + } + } + + return scope; +} + +/** + * Group function helper + * @ignore + */ +var groupFunction = function () { + var c = db[ns].find(condition); + var map = new Map(); + var reduce_function = reduce; + + while (c.hasNext()) { + var obj = c.next(); + var key = {}; + + for (var i = 0, len = keys.length; i < len; ++i) { + var k = keys[i]; + key[k] = obj[k]; + } + + var aggObj = map.get(key); + + if (aggObj == null) { + var newObj = Object.extend({}, key); + aggObj = Object.extend(newObj, initial); + map.put(key, aggObj); + } + + reduce_function(obj, aggObj); + } + + return { "result": map.values() }; +}.toString(); + +/** + * Run a group command across a collection + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Object|Array|Function|Code} keys an object, array or function expressing the keys to group by. + * @param {Object} condition an optional condition that must be true for a row to be considered. + * @param {Object} initial initial value of the aggregation counter object. + * @param {Function|Code} reduce the reduce function aggregates (reduces) the objects iterated + * @param {Function|Code} finalize an optional function to be run on each item in the result set just before the item is returned. + * @param {Boolean} command specify if you wish to run using the internal group command or using eval, default is true. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the group method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.group = function group(keys, condition, initial, reduce, finalize, command, options, callback) { + var args = Array.prototype.slice.call(arguments, 3); + callback = args.pop(); + // Fetch all commands + reduce = args.length ? args.shift() : null; + finalize = args.length ? args.shift() : null; + command = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Make sure we are backward compatible + if(!(typeof finalize == 'function')) { + command = finalize; + finalize = null; + } + + if (!Array.isArray(keys) && keys instanceof Object && typeof(keys) !== 'function' && !(keys instanceof Code)) { + keys = Object.keys(keys); + } + + if(typeof reduce === 'function') { + reduce = reduce.toString(); + } + + if(typeof finalize === 'function') { + finalize = finalize.toString(); + } + + // Set up the command as default + command = command == null ? true : command; + + // Execute using the command + if(command) { + var reduceFunction = reduce instanceof Code + ? reduce + : new Code(reduce); + + var selector = { + group: { + 'ns': this.collectionName + , '$reduce': reduceFunction + , 'cond': condition + , 'initial': initial + , 'out': "inline" + } + }; + + // if finalize is defined + if(finalize != null) selector.group['finalize'] = finalize; + // Set up group selector + if ('function' === typeof keys || keys instanceof Code) { + selector.group.$keyf = keys instanceof Code + ? keys + : new Code(keys); + } else { + var hash = {}; + keys.forEach(function (key) { + hash[key] = 1; + }); + selector.group.key = hash; + } + + var cmd = DbCommand.createDbSlaveOkCommand(this.db, selector); + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if(err != null) return callback(err); + + var document = result.documents[0]; + if (null == document.retval) { + return callback(new Error("group command failed: " + document.errmsg)); + } + + callback(null, document.retval); + }); + + } else { + // Create execution scope + var scope = reduce != null && reduce instanceof Code + ? reduce.scope + : {}; + + scope.ns = this.collectionName; + scope.keys = keys; + scope.condition = condition; + scope.initial = initial; + + // Pass in the function text to execute within mongodb. + var groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';'); + + this.db.eval(new Code(groupfn, scope), function (err, results) { + if (err) return callback(err, null); + callback(null, results.result || results); + }); + } +}; + +/** + * Returns the options of the collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the options method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.options = function options(callback) { + this.db.collectionsInfo(this.collectionName, function (err, cursor) { + if (err) return callback(err); + cursor.nextObject(function (err, document) { + callback(err, document && document.options || null); + }); + }); +}; + +/** + * Returns if the collection is a capped collection + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the isCapped method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.isCapped = function isCapped(callback) { + this.options(function(err, document) { + if(err != null) { + callback(err); + } else { + callback(null, document && document.capped); + } + }); +}; + +/** + * Checks if one or more indexes exist on the collection + * + * @param {String|Array} indexNames check if one or more indexes exist on the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexExists method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexExists = function indexExists(indexes, callback) { + this.indexInformation(function(err, indexInformation) { + // If we have an error return + if(err != null) return callback(err, null); + // Let's check for the index names + if(Array.isArray(indexes)) { + for(var i = 0; i < indexes.length; i++) { + if(indexInformation[indexes[i]] == null) { + return callback(null, false); + } + } + + // All keys found return true + return callback(null, true); + } else { + return callback(null, indexInformation[indexes] != null); + } + }); +} + +/** + * Execute the geoNear command to search for items in the collection + * + * Options + * - **num** {Number}, max number of results to return. + * - **maxDistance** {Number}, include results up to maxDistance from the point. + * - **distanceMultiplier** {Number}, include a value to multiply the distances with allowing for range conversions. + * - **query** {Object}, filter the results by a query. + * - **spherical** {Boolean, default:false}, perform query using a spherical model. + * - **uniqueDocs** {Boolean, default:false}, the closest location in a document to the center of the search region will always be returned MongoDB > 2.X. + * - **includeLocs** {Boolean, default:false}, include the location data fields in the top level of the results MongoDB > 2.X. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. + * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoNear method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.geoNear = function geoNear(x, y, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + geoNear:this.collectionName, + near: [x, y] + } + + // Decorate object if any with known properties + if(options['num'] != null) commandObject['num'] = options['num']; + if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance']; + if(options['distanceMultiplier'] != null) commandObject['distanceMultiplier'] = options['distanceMultiplier']; + if(options['query'] != null) commandObject['query'] = options['query']; + if(options['spherical'] != null) commandObject['spherical'] = options['spherical']; + if(options['uniqueDocs'] != null) commandObject['uniqueDocs'] = options['uniqueDocs']; + if(options['includeLocs'] != null) commandObject['includeLocs'] = options['includeLocs']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * Execute a geo search using a geo haystack index on a collection. + * + * Options + * - **maxDistance** {Number}, include results up to maxDistance from the point. + * - **search** {Object}, filter the results by a query. + * - **limit** {Number}, max number of results to return. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. + * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoHaystackSearch method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.geoHaystackSearch = function geoHaystackSearch(x, y, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + geoSearch:this.collectionName, + near: [x, y] + } + + // Decorate object if any with known properties + if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance']; + if(options['query'] != null) commandObject['search'] = options['query']; + if(options['search'] != null) commandObject['search'] = options['search']; + if(options['limit'] != null) commandObject['limit'] = options['limit']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * Retrieve all the indexes on the collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexes method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexes = function indexes(callback) { + // Return all the index information + this.db.indexInformation(this.collectionName, {full:true}, callback); +} + +/** + * Execute an aggregation framework pipeline against the collection, needs MongoDB >= 2.1 + * + * Options + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Array} array containing all the aggregation framework commands for the execution. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the aggregate method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.aggregate = function(pipeline, options, callback) { + // * - **explain** {Boolean}, return the query plan for the aggregation pipeline instead of the results. 2.3, 2.4 + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + var self = this; + + // If we have any of the supported options in the options object + var opts = args[args.length - 1]; + options = opts.readPreference || opts.explain ? args.pop() : {} + + // Convert operations to an array + if(!Array.isArray(args[0])) { + pipeline = []; + // Push all the operations to the pipeline + for(var i = 0; i < args.length; i++) pipeline.push(args[i]); + } + + // Build the command + var command = { aggregate : this.collectionName, pipeline : pipeline}; + // Add all options + var keys = Object.keys(options); + // Add all options + for(var i = 0; i < keys.length; i++) { + command[keys[i]] = options[keys[i]]; + } + + // Execute the command + this.db.command(command, options, function(err, result) { + if(err) { + callback(err); + } else if(result['err'] || result['errmsg']) { + callback(self.db.wrap(result)); + } else if(typeof result == 'object' && result['serverPipeline']) { + callback(null, result); + } else { + callback(null, result.result); + } + }); +} + +/** + * Get all the collection statistics. + * + * Options + * - **scale** {Number}, divide the returned sizes by scale value. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Objects} [options] options for the stats command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the stats method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.stats = function stats(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + collStats:this.collectionName, + } + + // Check if we have the scale value + if(options['scale'] != null) commandObject['scale'] = options['scale']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * @ignore + */ +Object.defineProperty(Collection.prototype, "hint", { + enumerable: true + , get: function () { + return this.internalHint; + } + , set: function (v) { + this.internalHint = normalizeHintField(v); + } +}); + +/** + * @ignore + */ +var _hasWriteConcern = function(errorOptions) { + return errorOptions == true + || errorOptions.w > 0 + || errorOptions.w == 'majority' + || errorOptions.j == true + || errorOptions.journal == true + || errorOptions.fsync == true +} + +/** + * @ignore + */ +var _setWriteConcernHash = function(options) { + var finalOptions = {}; + if(options.w != null) finalOptions.w = options.w; + if(options.journal == true) finalOptions.j = options.journal; + if(options.j == true) finalOptions.j = options.j; + if(options.fsync == true) finalOptions.fsync = options.fsync; + if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout; + return finalOptions; +} + +/** + * @ignore + */ +var _getWriteConcern = function(self, options, callback) { + // Final options + var finalOptions = {w:1}; + // Local options verification + if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(options); + } else if(typeof options.safe == "boolean") { + finalOptions = {w: (options.safe ? 1 : 0)}; + } else if(options.safe != null && typeof options.safe == 'object') { + finalOptions = _setWriteConcernHash(options.safe); + } else if(self.opts.w != null || typeof self.opts.j == 'boolean' || typeof self.opts.journal == 'boolean' || typeof self.opts.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.opts); + } else if(typeof self.opts.safe == "boolean") { + finalOptions = {w: (self.opts.safe ? 1 : 0)}; + } else if(self.db.safe.w != null || typeof self.db.safe.j == 'boolean' || typeof self.db.safe.journal == 'boolean' || typeof self.db.safe.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.db.safe); + } else if(self.db.options.w != null || typeof self.db.options.j == 'boolean' || typeof self.db.options.journal == 'boolean' || typeof self.db.options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.db.options); + } else if(typeof self.db.safe == "boolean") { + finalOptions = {w: (self.db.safe ? 1 : 0)}; + } + + // Ensure we don't have an invalid combination of write concerns + if(finalOptions.w < 1 + && (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw new Error("No acknowlegement using w < 1 cannot be combined with journal:true or fsync:true"); + + // Return the options + return finalOptions; +} + +/** + * Expose. + */ +exports.Collection = Collection; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/base_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/base_command.js new file mode 100644 index 0000000..9558582 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/base_command.js @@ -0,0 +1,29 @@ +/** + Base object used for common functionality +**/ +var BaseCommand = exports.BaseCommand = function BaseCommand() { +}; + +var id = 1; +BaseCommand.prototype.getRequestId = function getRequestId() { + if (!this.requestId) this.requestId = id++; + return this.requestId; +}; + +BaseCommand.prototype.setMongosReadPreference = function setMongosReadPreference(readPreference, tags) {} + +BaseCommand.prototype.updateRequestId = function() { + this.requestId = id++; + return this.requestId; +}; + +// OpCodes +BaseCommand.OP_REPLY = 1; +BaseCommand.OP_MSG = 1000; +BaseCommand.OP_UPDATE = 2001; +BaseCommand.OP_INSERT = 2002; +BaseCommand.OP_GET_BY_OID = 2003; +BaseCommand.OP_QUERY = 2004; +BaseCommand.OP_GET_MORE = 2005; +BaseCommand.OP_DELETE = 2006; +BaseCommand.OP_KILL_CURSORS = 2007; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/db_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/db_command.js new file mode 100644 index 0000000..b1a798d --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/db_command.js @@ -0,0 +1,240 @@ +var QueryCommand = require('./query_command').QueryCommand, + InsertCommand = require('./insert_command').InsertCommand, + inherits = require('util').inherits, + utils = require('../utils'), + crypto = require('crypto'); + +/** + Db Command +**/ +var DbCommand = exports.DbCommand = function(dbInstance, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) { + QueryCommand.call(this); + this.collectionName = collectionName; + this.queryOptions = queryOptions; + this.numberToSkip = numberToSkip; + this.numberToReturn = numberToReturn; + this.query = query; + this.returnFieldSelector = returnFieldSelector; + this.db = dbInstance; + + // Make sure we don't get a null exception + options = options == null ? {} : options; + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(DbCommand, QueryCommand); + +// Constants +DbCommand.SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"; +DbCommand.SYSTEM_INDEX_COLLECTION = "system.indexes"; +DbCommand.SYSTEM_PROFILE_COLLECTION = "system.profile"; +DbCommand.SYSTEM_USER_COLLECTION = "system.users"; +DbCommand.SYSTEM_COMMAND_COLLECTION = "$cmd"; +DbCommand.SYSTEM_JS_COLLECTION = "system.js"; + +// New commands +DbCommand.NcreateIsMasterCommand = function(db, databaseName) { + return new DbCommand(db, databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null); +}; + +// Provide constructors for different db commands +DbCommand.createIsMasterCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null); +}; + +DbCommand.createCollectionInfoCommand = function(db, selector) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_NAMESPACE_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, 0, selector, null); +}; + +DbCommand.createGetNonceCommand = function(db, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getnonce':1}, null); +}; + +DbCommand.createAuthenticationCommand = function(db, username, password, nonce, authdb) { + // Use node md5 generator + var md5 = crypto.createHash('md5'); + // Generate keys used for authentication + md5.update(username + ":mongo:" + password); + var hash_password = md5.digest('hex'); + // Final key + md5 = crypto.createHash('md5'); + md5.update(nonce + username + hash_password); + var key = md5.digest('hex'); + // Creat selector + var selector = {'authenticate':1, 'user':username, 'nonce':nonce, 'key':key}; + // Create db command + return new DbCommand(db, authdb + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NONE, 0, -1, selector, null); +}; + +DbCommand.createLogoutCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'logout':1}, null); +}; + +DbCommand.createCreateCollectionCommand = function(db, collectionName, options) { + var selector = {'create':collectionName}; + // Modify the options to ensure correct behaviour + for(var name in options) { + if(options[name] != null && options[name].constructor != Function) selector[name] = options[name]; + } + // Execute the command + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, selector, null); +}; + +DbCommand.createDropCollectionCommand = function(db, collectionName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'drop':collectionName}, null); +}; + +DbCommand.createRenameCollectionCommand = function(db, fromCollectionName, toCollectionName) { + var renameCollection = db.databaseName + "." + fromCollectionName; + var toCollection = db.databaseName + "." + toCollectionName; + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'renameCollection':renameCollection, 'to':toCollection}, null); +}; + +DbCommand.createGetLastErrorCommand = function(options, db) { + + if (typeof db === 'undefined') { + db = options; + options = {}; + } + // Final command + var command = {'getlasterror':1}; + // If we have an options Object let's merge in the fields (fsync/wtimeout/w) + if('object' === typeof options) { + for(var name in options) { + command[name] = options[name] + } + } + + // Special case for w == 1, remove the w + if(1 == command.w) { + delete command.w; + } + + // Execute command + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null); +}; + +DbCommand.createGetLastStatusCommand = DbCommand.createGetLastErrorCommand; + +DbCommand.createGetPreviousErrorsCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getpreverror':1}, null); +}; + +DbCommand.createResetErrorHistoryCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reseterror':1}, null); +}; + +DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, options) { + var fieldHash = {}; + var indexes = []; + var keys; + + // Get all the fields accordingly + if('string' == typeof fieldOrSpec) { + // 'type' + indexes.push(fieldOrSpec + '_' + 1); + fieldHash[fieldOrSpec] = 1; + + } else if(utils.isArray(fieldOrSpec)) { + + fieldOrSpec.forEach(function(f) { + if('string' == typeof f) { + // [{location:'2d'}, 'type'] + indexes.push(f + '_' + 1); + fieldHash[f] = 1; + } else if(utils.isArray(f)) { + // [['location', '2d'],['type', 1]] + indexes.push(f[0] + '_' + (f[1] || 1)); + fieldHash[f[0]] = f[1] || 1; + } else if(utils.isObject(f)) { + // [{location:'2d'}, {type:1}] + keys = Object.keys(f); + keys.forEach(function(k) { + indexes.push(k + '_' + f[k]); + fieldHash[k] = f[k]; + }); + } else { + // undefined (ignore) + } + }); + + } else if(utils.isObject(fieldOrSpec)) { + // {location:'2d', type:1} + keys = Object.keys(fieldOrSpec); + keys.forEach(function(key) { + indexes.push(key + '_' + fieldOrSpec[key]); + fieldHash[key] = fieldOrSpec[key]; + }); + } + + // Generate the index name + var indexName = typeof options.name == 'string' + ? options.name + : indexes.join("_"); + + var selector = { + 'ns': db.databaseName + "." + collectionName, + 'key': fieldHash, + 'name': indexName + } + + // Ensure we have a correct finalUnique + var finalUnique = options == null || 'object' === typeof options + ? false + : options; + + // Set up options + options = options == null || typeof options == 'boolean' + ? {} + : options; + + // Add all the options + var keys = Object.keys(options); + for(var i = 0; i < keys.length; i++) { + selector[keys[i]] = options[keys[i]]; + } + + if(selector['unique'] == null) + selector['unique'] = finalUnique; + + var name = db.databaseName + "." + DbCommand.SYSTEM_INDEX_COLLECTION; + var cmd = new InsertCommand(db, name, false); + return cmd.add(selector); +}; + +DbCommand.logoutCommand = function(db, command_hash, options) { + var dbName = options != null && options['authdb'] != null ? options['authdb'] : db.databaseName; + // Create logout command + return new DbCommand(db, dbName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null); +} + +DbCommand.createDropIndexCommand = function(db, collectionName, indexName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'deleteIndexes':collectionName, 'index':indexName}, null); +}; + +DbCommand.createReIndexCommand = function(db, collectionName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reIndex':collectionName}, null); +}; + +DbCommand.createDropDatabaseCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'dropDatabase':1}, null); +}; + +DbCommand.createDbCommand = function(db, command_hash, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null, options); +}; + +DbCommand.createAdminDbCommand = function(db, command_hash) { + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null); +}; + +DbCommand.createAdminDbCommandSlaveOk = function(db, command_hash) { + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null); +}; + +DbCommand.createDbSlaveOkCommand = function(db, command_hash, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null, options); +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/delete_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/delete_command.js new file mode 100644 index 0000000..e6ae20a --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/delete_command.js @@ -0,0 +1,114 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var DeleteCommand = exports.DeleteCommand = function(db, collectionName, selector, flags) { + BaseCommand.call(this); + + // Validate correctness off the selector + var object = selector; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("delete raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.flags = flags; + this.collectionName = collectionName; + this.selector = selector; + this.db = db; +}; + +inherits(DeleteCommand, BaseCommand); + +DeleteCommand.OP_DELETE = 2006; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + int32 ZERO; // 0 - reserved for future use + mongo.BSON selector; // query object. See below for details. +} +*/ +DeleteCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.selector, false, true) + (4 * 4); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (DeleteCommand.OP_DELETE >> 24) & 0xff; + _command[_index + 2] = (DeleteCommand.OP_DELETE >> 16) & 0xff; + _command[_index + 1] = (DeleteCommand.OP_DELETE >> 8) & 0xff; + _command[_index] = DeleteCommand.OP_DELETE & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the flags + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(this.selector)) { + documentLength = this.selector.length; + // Copy the data into the current buffer + this.selector.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(this.selector, this.checkKeys, _command, _index) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + return _command; +}; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/get_more_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/get_more_command.js new file mode 100644 index 0000000..d3aac02 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/get_more_command.js @@ -0,0 +1,83 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits, + binaryutils = require('../utils'); + +/** + Get More Document Command +**/ +var GetMoreCommand = exports.GetMoreCommand = function(db, collectionName, numberToReturn, cursorId) { + BaseCommand.call(this); + + this.collectionName = collectionName; + this.numberToReturn = numberToReturn; + this.cursorId = cursorId; + this.db = db; +}; + +inherits(GetMoreCommand, BaseCommand); + +GetMoreCommand.OP_GET_MORE = 2005; + +GetMoreCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 8 + (4 * 4); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index++] = totalLengthOfCommand & 0xff; + _command[_index++] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index++] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index++] = (totalLengthOfCommand >> 24) & 0xff; + + // Write the request ID + _command[_index++] = this.requestId & 0xff; + _command[_index++] = (this.requestId >> 8) & 0xff; + _command[_index++] = (this.requestId >> 16) & 0xff; + _command[_index++] = (this.requestId >> 24) & 0xff; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the op_code for the command + _command[_index++] = GetMoreCommand.OP_GET_MORE & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 8) & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 16) & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 24) & 0xff; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Number of documents to return + _command[_index++] = this.numberToReturn & 0xff; + _command[_index++] = (this.numberToReturn >> 8) & 0xff; + _command[_index++] = (this.numberToReturn >> 16) & 0xff; + _command[_index++] = (this.numberToReturn >> 24) & 0xff; + + // Encode the cursor id + var low_bits = this.cursorId.getLowBits(); + // Encode low bits + _command[_index++] = low_bits & 0xff; + _command[_index++] = (low_bits >> 8) & 0xff; + _command[_index++] = (low_bits >> 16) & 0xff; + _command[_index++] = (low_bits >> 24) & 0xff; + + var high_bits = this.cursorId.getHighBits(); + // Encode high bits + _command[_index++] = high_bits & 0xff; + _command[_index++] = (high_bits >> 8) & 0xff; + _command[_index++] = (high_bits >> 16) & 0xff; + _command[_index++] = (high_bits >> 24) & 0xff; + // Return command + return _command; +}; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/insert_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/insert_command.js new file mode 100644 index 0000000..d6a2100 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/insert_command.js @@ -0,0 +1,147 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var InsertCommand = exports.InsertCommand = function(db, collectionName, checkKeys, options) { + BaseCommand.call(this); + + this.collectionName = collectionName; + this.documents = []; + this.checkKeys = checkKeys == null ? true : checkKeys; + this.db = db; + this.flags = 0; + this.serializeFunctions = false; + + // Ensure valid options hash + options = options == null ? {} : options; + + // Check if we have keepGoing set -> set flag if it's the case + if(options['keepGoing'] != null && options['keepGoing']) { + // This will finish inserting all non-index violating documents even if it returns an error + this.flags = 1; + } + + // Check if we have keepGoing set -> set flag if it's the case + if(options['continueOnError'] != null && options['continueOnError']) { + // This will finish inserting all non-index violating documents even if it returns an error + this.flags = 1; + } + + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(InsertCommand, BaseCommand); + +// OpCodes +InsertCommand.OP_INSERT = 2002; + +InsertCommand.prototype.add = function(document) { + if(Buffer.isBuffer(document)) { + var object_size = document[0] | document[1] << 8 | document[2] << 16 | document[3] << 24; + if(object_size != document.length) { + var error = new Error("insert raw message size does not match message header size [" + document.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.documents.push(document); + return this; +}; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + BSON[] documents; // one or more documents to insert into the collection +} +*/ +InsertCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + (4 * 4); + // var docLength = 0 + for(var i = 0; i < this.documents.length; i++) { + if(Buffer.isBuffer(this.documents[i])) { + totalLengthOfCommand += this.documents[i].length; + } else { + // Calculate size of document + totalLengthOfCommand += this.db.bson.calculateObjectSize(this.documents[i], this.serializeFunctions, true); + } + } + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (InsertCommand.OP_INSERT >> 24) & 0xff; + _command[_index + 2] = (InsertCommand.OP_INSERT >> 16) & 0xff; + _command[_index + 1] = (InsertCommand.OP_INSERT >> 8) & 0xff; + _command[_index] = InsertCommand.OP_INSERT & 0xff; + // Adjust index + _index = _index + 4; + // Write flags if any + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write all the bson documents to the buffer at the index offset + for(var i = 0; i < this.documents.length; i++) { + // Document binary length + var documentLength = 0 + var object = this.documents[i]; + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + // Serialize the document straight to the buffer + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + + return _command; +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js new file mode 100644 index 0000000..d8ccb0c --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js @@ -0,0 +1,98 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits, + binaryutils = require('../utils'); + +/** + Insert Document Command +**/ +var KillCursorCommand = exports.KillCursorCommand = function(db, cursorIds) { + BaseCommand.call(this); + + this.cursorIds = cursorIds; + this.db = db; +}; + +inherits(KillCursorCommand, BaseCommand); + +KillCursorCommand.OP_KILL_CURSORS = 2007; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + int32 numberOfCursorIDs; // number of cursorIDs in message + int64[] cursorIDs; // array of cursorIDs to close +} +*/ +KillCursorCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + 4 + (4 * 4) + (this.cursorIds.length * 8); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (KillCursorCommand.OP_KILL_CURSORS >> 24) & 0xff; + _command[_index + 2] = (KillCursorCommand.OP_KILL_CURSORS >> 16) & 0xff; + _command[_index + 1] = (KillCursorCommand.OP_KILL_CURSORS >> 8) & 0xff; + _command[_index] = KillCursorCommand.OP_KILL_CURSORS & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Number of cursors to kill + var numberOfCursors = this.cursorIds.length; + _command[_index + 3] = (numberOfCursors >> 24) & 0xff; + _command[_index + 2] = (numberOfCursors >> 16) & 0xff; + _command[_index + 1] = (numberOfCursors >> 8) & 0xff; + _command[_index] = numberOfCursors & 0xff; + // Adjust index + _index = _index + 4; + + // Encode all the cursors + for(var i = 0; i < this.cursorIds.length; i++) { + // Encode the cursor id + var low_bits = this.cursorIds[i].getLowBits(); + // Encode low bits + _command[_index + 3] = (low_bits >> 24) & 0xff; + _command[_index + 2] = (low_bits >> 16) & 0xff; + _command[_index + 1] = (low_bits >> 8) & 0xff; + _command[_index] = low_bits & 0xff; + // Adjust index + _index = _index + 4; + + var high_bits = this.cursorIds[i].getHighBits(); + // Encode high bits + _command[_index + 3] = (high_bits >> 24) & 0xff; + _command[_index + 2] = (high_bits >> 16) & 0xff; + _command[_index + 1] = (high_bits >> 8) & 0xff; + _command[_index] = high_bits & 0xff; + // Adjust index + _index = _index + 4; + } + + return _command; +}; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/query_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/query_command.js new file mode 100644 index 0000000..76829d9 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/query_command.js @@ -0,0 +1,261 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var QueryCommand = exports.QueryCommand = function(db, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) { + BaseCommand.call(this); + + // Validate correctness off the selector + var object = query, + object_size; + if(Buffer.isBuffer(object)) { + object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + object = returnFieldSelector; + if(Buffer.isBuffer(object)) { + object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Make sure we don't get a null exception + options = options == null ? {} : options; + // Set up options + this.collectionName = collectionName; + this.queryOptions = queryOptions; + this.numberToSkip = numberToSkip; + this.numberToReturn = numberToReturn; + + // Ensure we have no null query + query = query == null ? {} : query; + // Wrap query in the $query parameter so we can add read preferences for mongos + this.query = query; + this.returnFieldSelector = returnFieldSelector; + this.db = db; + + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(QueryCommand, BaseCommand); + +QueryCommand.OP_QUERY = 2004; + +/* + * Adds the read prefrence to the current command + */ +QueryCommand.prototype.setMongosReadPreference = function(readPreference, tags) { + // If we have readPreference set to true set to secondary prefered + if(readPreference == true) { + readPreference = 'secondaryPreferred'; + } else if(readPreference == 'false') { + readPreference = 'primary'; + } + + // Force the slave ok flag to be set if we are not using primary read preference + if(readPreference != false && readPreference != 'primary') { + this.queryOptions |= QueryCommand.OPTS_SLAVE; + } + + // Backward compatibility, ensure $query only set on read preference so 1.8.X works + if((readPreference != null || tags != null) && this.query['$query'] == null) { + this.query = {'$query': this.query}; + } + + // If we have no readPreference set and no tags, check if the slaveOk bit is set + if(readPreference == null && tags == null) { + // If we have a slaveOk bit set the read preference for MongoS + if(this.queryOptions & QueryCommand.OPTS_SLAVE) { + this.query['$readPreference'] = {mode: 'secondary'} + } else { + this.query['$readPreference'] = {mode: 'primary'} + } + } + + // Build read preference object + if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') { + this.query['$readPreference'] = readPreference.toObject(); + } else if(readPreference != null) { + // Add the read preference + this.query['$readPreference'] = {mode: readPreference}; + + // If we have tags let's add them + if(tags != null) { + this.query['$readPreference']['tags'] = tags; + } + } +} + +/* +struct { + MsgHeader header; // standard message header + int32 opts; // query options. See below for details. + cstring fullCollectionName; // "dbname.collectionname" + int32 numberToSkip; // number of documents to skip when returning results + int32 numberToReturn; // number of documents to return in the first OP_REPLY + BSON query ; // query object. See below for details. + [ BSON returnFieldSelector; ] // OPTIONAL : selector indicating the fields to return. See below for details. +} +*/ +QueryCommand.prototype.toBinary = function() { + // Total length of the command + var totalLengthOfCommand = 0; + // Calculate total length of the document + if(Buffer.isBuffer(this.query)) { + totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.query.length + (4 * 4); + } else { + totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.db.bson.calculateObjectSize(this.query, this.serializeFunctions, true) + (4 * 4); + } + + // Calculate extra fields size + if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) { + if(Object.keys(this.returnFieldSelector).length > 0) { + totalLengthOfCommand += this.db.bson.calculateObjectSize(this.returnFieldSelector, this.serializeFunctions, true); + } + } else if(Buffer.isBuffer(this.returnFieldSelector)) { + totalLengthOfCommand += this.returnFieldSelector.length; + } + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (QueryCommand.OP_QUERY >> 24) & 0xff; + _command[_index + 2] = (QueryCommand.OP_QUERY >> 16) & 0xff; + _command[_index + 1] = (QueryCommand.OP_QUERY >> 8) & 0xff; + _command[_index] = QueryCommand.OP_QUERY & 0xff; + // Adjust index + _index = _index + 4; + + // Write the query options + _command[_index + 3] = (this.queryOptions >> 24) & 0xff; + _command[_index + 2] = (this.queryOptions >> 16) & 0xff; + _command[_index + 1] = (this.queryOptions >> 8) & 0xff; + _command[_index] = this.queryOptions & 0xff; + // Adjust index + _index = _index + 4; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the number of documents to skip + _command[_index + 3] = (this.numberToSkip >> 24) & 0xff; + _command[_index + 2] = (this.numberToSkip >> 16) & 0xff; + _command[_index + 1] = (this.numberToSkip >> 8) & 0xff; + _command[_index] = this.numberToSkip & 0xff; + // Adjust index + _index = _index + 4; + + // Write the number of documents to return + _command[_index + 3] = (this.numberToReturn >> 24) & 0xff; + _command[_index + 2] = (this.numberToReturn >> 16) & 0xff; + _command[_index + 1] = (this.numberToReturn >> 8) & 0xff; + _command[_index] = this.numberToReturn & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + var object = this.query; + + // Serialize the selector + if(Buffer.isBuffer(object)) { + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + // Serialize the document straight to the buffer + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + // Push field selector if available + if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) { + if(Object.keys(this.returnFieldSelector).length > 0) { + var documentLength = this.db.bson.serializeWithBufferAndIndex(this.returnFieldSelector, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + } if(this.returnFieldSelector != null && Buffer.isBuffer(this.returnFieldSelector)) { + // Document binary length + var documentLength = 0 + var object = this.returnFieldSelector; + + // Serialize the selector + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + + // Return finished command + return _command; +}; + +// Constants +QueryCommand.OPTS_NONE = 0; +QueryCommand.OPTS_TAILABLE_CURSOR = 2; +QueryCommand.OPTS_SLAVE = 4; +QueryCommand.OPTS_OPLOG_REPLY = 8; +QueryCommand.OPTS_NO_CURSOR_TIMEOUT = 16; +QueryCommand.OPTS_AWAIT_DATA = 32; +QueryCommand.OPTS_EXHAUST = 64; +QueryCommand.OPTS_PARTIAL = 128; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/update_command.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/update_command.js new file mode 100644 index 0000000..9829dea --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/commands/update_command.js @@ -0,0 +1,174 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Update Document Command +**/ +var UpdateCommand = exports.UpdateCommand = function(db, collectionName, spec, document, options) { + BaseCommand.call(this); + + var object = spec; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("update spec raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + var object = document; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("update document raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.collectionName = collectionName; + this.spec = spec; + this.document = document; + this.db = db; + this.serializeFunctions = false; + + // Generate correct flags + var db_upsert = 0; + var db_multi_update = 0; + db_upsert = options != null && options['upsert'] != null ? (options['upsert'] == true ? 1 : 0) : db_upsert; + db_multi_update = options != null && options['multi'] != null ? (options['multi'] == true ? 1 : 0) : db_multi_update; + + // Flags + this.flags = parseInt(db_multi_update.toString() + db_upsert.toString(), 2); + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(UpdateCommand, BaseCommand); + +UpdateCommand.OP_UPDATE = 2001; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + int32 flags; // bit vector. see below + BSON spec; // the query to select the document + BSON document; // the document data to update with or insert +} +*/ +UpdateCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.spec, false, true) + + this.db.bson.calculateObjectSize(this.document, this.serializeFunctions, true) + (4 * 4); + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (UpdateCommand.OP_UPDATE >> 24) & 0xff; + _command[_index + 2] = (UpdateCommand.OP_UPDATE >> 16) & 0xff; + _command[_index + 1] = (UpdateCommand.OP_UPDATE >> 8) & 0xff; + _command[_index] = UpdateCommand.OP_UPDATE & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the update flags + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + var object = this.spec; + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, false) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + // Document binary length + var documentLength = 0 + var object = this.document; + + // Serialize the document + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + return _command; +}; + +// Constants +UpdateCommand.DB_UPSERT = 0; +UpdateCommand.DB_MULTI_UPDATE = 1; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/base.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/base.js new file mode 100644 index 0000000..f0ff06f --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/base.js @@ -0,0 +1,50 @@ +var EventEmitter = require('events').EventEmitter + , inherits = require('util').inherits; + +var Base = function Base() { + EventEmitter.call(this); +} + +/** + * @ignore + */ +inherits(Base, EventEmitter); + +/** + * Fire all the errors + * @ignore + */ +Base.prototype.__executeAllCallbacksWithError = function(err) { + // Locate all the possible callbacks that need to return + for(var i = 0; i < this.dbInstances.length; i++) { + // Fetch the db Instance + var dbInstance = this.dbInstances[i]; + // Check all callbacks + var keys = Object.keys(dbInstance._callBackStore._notReplied); + // For each key check if it's a callback that needs to be returned + for(var j = 0; j < keys.length; j++) { + var info = dbInstance._callBackStore._notReplied[keys[j]]; + // Check if we have a chained command (findAndModify) + if(info && info['chained'] && Array.isArray(info['chained']) && info['chained'].length > 0) { + var chained = info['chained']; + // Only callback once and the last one is the right one + var finalCallback = chained.pop(); + // Emit only the last event + dbInstance._callBackStore.emit(finalCallback, err, null); + + // Put back the final callback to ensure we don't call all commands in the chain + chained.push(finalCallback); + + // Remove all chained callbacks + for(var i = 0; i < chained.length; i++) { + delete dbInstance._callBackStore._notReplied[chained[i]]; + } + } else { + dbInstance._callBackStore.emit(keys[j], err, null); + } + } + } +} + + +exports.Base = Base; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection.js new file mode 100644 index 0000000..f1c333a --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection.js @@ -0,0 +1,440 @@ +var utils = require('./connection_utils'), + inherits = require('util').inherits, + net = require('net'), + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + binaryutils = require('../utils'), + tls = require('tls'); + +var Connection = exports.Connection = function(id, socketOptions) { + // Set up event emitter + EventEmitter.call(this); + // Store all socket options + this.socketOptions = socketOptions ? socketOptions : {host:'localhost', port:27017, domainSocket:false}; + // Set keep alive default if not overriden + if(this.socketOptions.keepAlive == null && (process.platform !== "sunos" || process.platform !== "win32")) this.socketOptions.keepAlive = 100; + // Id for the connection + this.id = id; + // State of the connection + this.connected = false; + // Set if this is a domain socket + this.domainSocket = this.socketOptions.domainSocket; + + // + // Connection parsing state + // + this.maxBsonSize = socketOptions.maxBsonSize ? socketOptions.maxBsonSize : Connection.DEFAULT_MAX_BSON_SIZE; + // Contains the current message bytes + this.buffer = null; + // Contains the current message size + this.sizeOfMessage = 0; + // Contains the readIndex for the messaage + this.bytesRead = 0; + // Contains spill over bytes from additional messages + this.stubBuffer = 0; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[], end:[]}; + + // Just keeps list of events we allow + resetHandlers(this, false); +} + +// Set max bson size +Connection.DEFAULT_MAX_BSON_SIZE = 1024 * 1024 * 4; + +// Inherit event emitter so we can emit stuff wohoo +inherits(Connection, EventEmitter); + +Connection.prototype.start = function() { + // If we have a normal connection + if(this.socketOptions.ssl) { + // Create a new stream + this.connection = new net.Socket(); + // Set timeout allowing backward compatibility to timeout if no connectTimeoutMS is set + this.connection.setTimeout(this.socketOptions.connectTimeoutMS != null ? this.socketOptions.connectTimeoutMS : this.socketOptions.timeout); + // Work around for 0.4.X + if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay); + // Set keep alive if defined + if(process.version.indexOf("v0.4") == -1) { + if(this.socketOptions.keepAlive > 0) { + this.connection.setKeepAlive(true, this.socketOptions.keepAlive); + } else { + this.connection.setKeepAlive(false); + } + } + + // Set up pair for tls with server, accept self-signed certificates as well + var pair = this.pair = tls.createSecurePair(false); + // Set up encrypted streams + this.pair.encrypted.pipe(this.connection); + this.connection.pipe(this.pair.encrypted); + + // Setup clearText stream + this.writeSteam = this.pair.cleartext; + // Add all handlers to the socket to manage it + this.pair.on("secure", connectHandler(this)); + this.pair.cleartext.on("data", createDataHandler(this)); + // Add handlers + this.connection.on("error", errorHandler(this)); + // this.connection.on("connect", connectHandler(this)); + this.connection.on("end", endHandler(this)); + this.connection.on("timeout", timeoutHandler(this)); + this.connection.on("drain", drainHandler(this)); + this.writeSteam.on("close", closeHandler(this)); + // Start socket + this.connection.connect(this.socketOptions.port, this.socketOptions.host); + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("opened connection", this.socketOptions); + } + } else { + // Create new connection instance + if(this.domainSocket) { + this.connection = net.createConnection(this.socketOptions.host); + } else { + this.connection = net.createConnection(this.socketOptions.port, this.socketOptions.host); + } + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("opened connection", this.socketOptions); + } + // Set options on the socket + this.connection.setTimeout(this.socketOptions.connectTimeoutMS != null ? this.socketOptions.connectTimeoutMS : this.socketOptions.timeout); + // Work around for 0.4.X + if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay); + // Set keep alive if defined + if(process.version.indexOf("v0.4") == -1) { + if(this.socketOptions.keepAlive > 0) { + this.connection.setKeepAlive(true, this.socketOptions.keepAlive); + } else { + this.connection.setKeepAlive(false); + } + } + + // Set up write stream + this.writeSteam = this.connection; + // Add handlers + this.connection.on("error", errorHandler(this)); + // Add all handlers to the socket to manage it + this.connection.on("connect", connectHandler(this)); + // this.connection.on("end", endHandler(this)); + this.connection.on("data", createDataHandler(this)); + this.connection.on("timeout", timeoutHandler(this)); + this.connection.on("drain", drainHandler(this)); + this.connection.on("close", closeHandler(this)); + } +} + +// Check if the sockets are live +Connection.prototype.isConnected = function() { + return this.connected && !this.connection.destroyed && this.connection.writable && this.connection.readable; +} + +// Write the data out to the socket +Connection.prototype.write = function(command, callback) { + try { + // If we have a list off commands to be executed on the same socket + if(Array.isArray(command)) { + for(var i = 0; i < command.length; i++) { + var binaryCommand = command[i].toBinary() + if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize) + return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); + if(this.logger != null && this.logger.doDebug) + this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command[i]}); + + var r = this.writeSteam.write(binaryCommand); + } + } else { + var binaryCommand = command.toBinary() + if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize) + return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); + + if(this.logger != null && this.logger.doDebug) + this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command}); + + var r = this.writeSteam.write(binaryCommand); + } + } catch (err) { + if(typeof callback === 'function') callback(err); + } +} + +// Force the closure of the connection +Connection.prototype.close = function() { + // clear out all the listeners + resetHandlers(this, true); + // Add a dummy error listener to catch any weird last moment errors (and ignore them) + this.connection.on("error", function() {}) + // destroy connection + this.connection.destroy(); + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("closed connection", this.connection); + } +} + +// Reset all handlers +var resetHandlers = function(self, clearListeners) { + self.eventHandlers = {error:[], connect:[], close:[], end:[], timeout:[], parseError:[], message:[]}; + + // If we want to clear all the listeners + if(clearListeners && self.connection != null) { + var keys = Object.keys(self.eventHandlers); + // Remove all listeners + for(var i = 0; i < keys.length; i++) { + self.connection.removeAllListeners(keys[i]); + } + } +} + +// +// Handlers +// + +// Connect handler +var connectHandler = function(self) { + return function() { + // Set connected + self.connected = true; + // Now that we are connected set the socket timeout + self.connection.setTimeout(self.socketOptions.socketTimeoutMS != null ? self.socketOptions.socketTimeoutMS : self.socketOptions.timeout); + // Emit the connect event with no error + self.emit("connect", null, self); + } +} + +var createDataHandler = exports.Connection.createDataHandler = function(self) { + // We need to handle the parsing of the data + // and emit the messages when there is a complete one + return function(data) { + // Parse until we are done with the data + while(data.length > 0) { + // If we still have bytes to read on the current message + if(self.bytesRead > 0 && self.sizeOfMessage > 0) { + // Calculate the amount of remaining bytes + var remainingBytesToRead = self.sizeOfMessage - self.bytesRead; + // Check if the current chunk contains the rest of the message + if(remainingBytesToRead > data.length) { + // Copy the new data into the exiting buffer (should have been allocated when we know the message size) + data.copy(self.buffer, self.bytesRead); + // Adjust the number of bytes read so it point to the correct index in the buffer + self.bytesRead = self.bytesRead + data.length; + + // Reset state of buffer + data = new Buffer(0); + } else { + // Copy the missing part of the data into our current buffer + data.copy(self.buffer, self.bytesRead, 0, remainingBytesToRead); + // Slice the overflow into a new buffer that we will then re-parse + data = data.slice(remainingBytesToRead); + + // Emit current complete message + try { + var emitBuffer = self.buffer; + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Emit the buffer + self.emit("message", emitBuffer, self); + } catch(err) { + var errorObject = {err:"socketHandler", trace:err, bin:buffer, parseState:{ + sizeOfMessage:self.sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + } + } else { + // Stub buffer is kept in case we don't get enough bytes to determine the + // size of the message (< 4 bytes) + if(self.stubBuffer != null && self.stubBuffer.length > 0) { + + // If we have enough bytes to determine the message size let's do it + if(self.stubBuffer.length + data.length > 4) { + // Prepad the data + var newData = new Buffer(self.stubBuffer.length + data.length); + self.stubBuffer.copy(newData, 0); + data.copy(newData, self.stubBuffer.length); + // Reassign for parsing + data = newData; + + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + + } else { + + // Add the the bytes to the stub buffer + var newStubBuffer = new Buffer(self.stubBuffer.length + data.length); + // Copy existing stub buffer + self.stubBuffer.copy(newStubBuffer, 0); + // Copy missing part of the data + data.copy(newStubBuffer, self.stubBuffer.length); + // Exit parsing loop + data = new Buffer(0); + } + } else { + if(data.length > 4) { + // Retrieve the message size + var sizeOfMessage = binaryutils.decodeUInt32(data, 0); + // If we have a negative sizeOfMessage emit error and return + if(sizeOfMessage < 0 || sizeOfMessage > self.maxBsonSize) { + var errorObject = {err:"socketHandler", trace:'', bin:self.buffer, parseState:{ + sizeOfMessage: sizeOfMessage, + bytesRead: self.bytesRead, + stubBuffer: self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + return; + } + + // Ensure that the size of message is larger than 0 and less than the max allowed + if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage > data.length) { + self.buffer = new Buffer(sizeOfMessage); + // Copy all the data into the buffer + data.copy(self.buffer, 0); + // Update bytes read + self.bytesRead = data.length; + // Update sizeOfMessage + self.sizeOfMessage = sizeOfMessage; + // Ensure stub buffer is null + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + + } else if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage == data.length) { + try { + var emitBuffer = data; + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + // Emit the message + self.emit("message", emitBuffer, self); + } catch (err) { + var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{ + sizeOfMessage:self.sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + } else if(sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonSize) { + var errorObject = {err:"socketHandler", trace:null, bin:data, parseState:{ + sizeOfMessage:sizeOfMessage, + bytesRead:0, + buffer:null, + stubBuffer:null}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + + // Clear out the state of the parser + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + + } else { + try { + var emitBuffer = data.slice(0, sizeOfMessage); + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Copy rest of message + data = data.slice(sizeOfMessage); + // Emit the message + self.emit("message", emitBuffer, self); + } catch (err) { + var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{ + sizeOfMessage:sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + + } + } else { + // Create a buffer that contains the space for the non-complete message + self.stubBuffer = new Buffer(data.length) + // Copy the data to the stub buffer + data.copy(self.stubBuffer, 0); + // Exit parsing loop + data = new Buffer(0); + } + } + } + } + } +} + +var endHandler = function(self) { + return function() { + // Set connected to false + self.connected = false; + // Emit end event + self.emit("end", {err: 'connection received Fin packet from [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } +} + +var timeoutHandler = function(self) { + return function() { + self.emit("timeout", {err: 'connection to [' + self.socketOptions.host + ':' + self.socketOptions.port + '] timed out'}, self); + } +} + +var drainHandler = function(self) { + return function() { + } +} + +var errorHandler = function(self) { + return function(err) { + // Set connected to false + self.connected = false; + // Emit error + self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } +} + +var closeHandler = function(self) { + return function(hadError) { + // If we have an error during the connection phase + if(hadError && !self.connected) { + // Set disconnected + self.connected = false; + // Emit error + self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } else { + // Set disconnected + self.connected = false; + // Emit close + self.emit("close", {err: 'connection closed to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } + } +} + +// Some basic defaults +Connection.DEFAULT_PORT = 27017; + + + + + + + diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_pool.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_pool.js new file mode 100644 index 0000000..9bbb88e --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_pool.js @@ -0,0 +1,249 @@ +var utils = require('./connection_utils'), + inherits = require('util').inherits, + net = require('net'), + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + MongoReply = require("../responses/mongo_reply").MongoReply, + Connection = require("./connection").Connection; + +var ConnectionPool = exports.ConnectionPool = function(host, port, poolSize, bson, socketOptions) { + if(typeof host !== 'string') { + throw new Error("host must be specified [" + host + "]"); + } + + // Set up event emitter + EventEmitter.call(this); + + // Keep all options for the socket in a specific collection allowing the user to specify the + // Wished upon socket connection parameters + this.socketOptions = typeof socketOptions === 'object' ? socketOptions : {}; + this.socketOptions.host = host; + this.socketOptions.port = port; + this.socketOptions.domainSocket = false; + this.bson = bson; + // PoolSize is always + 1 for special reserved "measurment" socket (like ping, stats etc) + this.poolSize = poolSize; + this.minPoolSize = Math.floor(this.poolSize / 2) + 1; + + // Check if the host is a socket + if(host.match(/^\//)) { + this.socketOptions.domainSocket = true; + } else if(typeof port !== 'number') { + throw new Error("port must be specified [" + port + "]"); + } + + // Set default settings for the socket options + utils.setIntegerParameter(this.socketOptions, 'timeout', 0); + // Delay before writing out the data to the server + utils.setBooleanParameter(this.socketOptions, 'noDelay', true); + // Delay before writing out the data to the server + utils.setIntegerParameter(this.socketOptions, 'keepAlive', 0); + // Set the encoding of the data read, default is binary == null + utils.setStringParameter(this.socketOptions, 'encoding', null); + // Allows you to set a throttling bufferSize if you need to stop overflows + utils.setIntegerParameter(this.socketOptions, 'bufferSize', 0); + + // Internal structures + this.openConnections = []; + // Assign connection id's + this.connectionId = 0; + + // Current index for selection of pool connection + this.currentConnectionIndex = 0; + // The pool state + this._poolState = 'disconnected'; + // timeout control + this._timeout = false; +} + +inherits(ConnectionPool, EventEmitter); + +ConnectionPool.prototype.setMaxBsonSize = function(maxBsonSize) { + if(maxBsonSize == null){ + maxBsonSize = Connection.DEFAULT_MAX_BSON_SIZE; + } + + for(var i = 0; i < this.openConnections.length; i++) { + this.openConnections[i].maxBsonSize = maxBsonSize; + } +} + +// Start a function +var _connect = function(_self) { + return new function() { + // Create a new connection instance + var connection = new Connection(_self.connectionId++, _self.socketOptions); + // Set logger on pool + connection.logger = _self.logger; + // Connect handler + connection.on("connect", function(err, connection) { + // Add connection to list of open connections + _self.openConnections.push(connection); + // If the number of open connections is equal to the poolSize signal ready pool + if(_self.openConnections.length === _self.poolSize && _self._poolState !== 'disconnected') { + // Set connected + _self._poolState = 'connected'; + // Emit pool ready + _self.emit("poolReady"); + } else if(_self.openConnections.length < _self.poolSize) { + // We need to open another connection, make sure it's in the next + // tick so we don't get a cascade of errors + process.nextTick(function() { + _connect(_self); + }); + } + }); + + var numberOfErrors = 0 + + // Error handler + connection.on("error", function(err, connection) { + numberOfErrors++; + // If we are already disconnected ignore the event + if(_self._poolState != 'disconnected' && _self.listeners("error").length > 0) { + _self.emit("error", err); + } + + connection.close(); + _self._poolState = 'disconnected'; + _self.stop(); + }); + + // Close handler + connection.on("close", function() { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("close").length > 0) { + _self.emit("close"); + } + // Set disconnected + _self._poolState = 'disconnected'; + // Stop + _self.stop(); + }); + + // Timeout handler + connection.on("timeout", function(err, connection) { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("timeout").length > 0) { + _self.emit("timeout", err); + } + + connection.close(); + _self.stop(); + }); + + // Parse error, needs a complete shutdown of the pool + connection.on("parseError", function() { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("parseError").length > 0) { + _self.emit("parseError", new Error("parseError occured")); + } + + _self.stop(); + }); + + connection.on("message", function(message) { + _self.emit("message", message); + }); + + // Start connection in the next tick + connection.start(); + }(); +} + + +// Start method, will throw error if no listeners are available +// Pass in an instance of the listener that contains the api for +// finding callbacks for a given message etc. +ConnectionPool.prototype.start = function() { + var markerDate = new Date().getTime(); + var self = this; + + if(this.listeners("poolReady").length == 0) { + throw "pool must have at least one listener ready that responds to the [poolReady] event"; + } + + // Set pool state to connecting + this._poolState = 'connecting'; + this._timeout = false; + + _connect(self); +} + +// Restart a connection pool (on a close the pool might be in a wrong state) +ConnectionPool.prototype.restart = function() { + // Close all connections + this.stop(false); + // Now restart the pool + this.start(); +} + +// Stop the connections in the pool +ConnectionPool.prototype.stop = function(removeListeners) { + removeListeners = removeListeners == null ? true : removeListeners; + // Set disconnected + this._poolState = 'disconnected'; + + // Clear all listeners if specified + if(removeListeners) { + this.removeAllEventListeners(); + } + + // Close all connections + for(var i = 0; i < this.openConnections.length; i++) { + this.openConnections[i].close(); + } + + // Clean up + this.openConnections = []; +} + +// Check the status of the connection +ConnectionPool.prototype.isConnected = function() { + return this._poolState === 'connected'; +} + +// Checkout a connection from the pool for usage, or grab a specific pool instance +ConnectionPool.prototype.checkoutConnection = function(id) { + var index = (this.currentConnectionIndex++ % (this.openConnections.length)); + var connection = this.openConnections[index]; + return connection; +} + +ConnectionPool.prototype.getAllConnections = function() { + return this.openConnections; +} + +// Remove all non-needed event listeners +ConnectionPool.prototype.removeAllEventListeners = function() { + this.removeAllListeners("close"); + this.removeAllListeners("error"); + this.removeAllListeners("timeout"); + this.removeAllListeners("connect"); + this.removeAllListeners("end"); + this.removeAllListeners("parseError"); + this.removeAllListeners("message"); + this.removeAllListeners("poolReady"); +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_utils.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_utils.js new file mode 100644 index 0000000..5910924 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/connection_utils.js @@ -0,0 +1,23 @@ +exports.setIntegerParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "number" && object[field] !== parseInt(object[field], 10)) { + throw "object field [" + field + "] must be a numeric integer value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} + +exports.setBooleanParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "boolean") { + throw "object field [" + field + "] must be a boolean value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} + +exports.setStringParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "string") { + throw "object field [" + field + "] must be a string value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/mongos.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/mongos.js new file mode 100644 index 0000000..3ba7c87 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/mongos.js @@ -0,0 +1,333 @@ +var ReadPreference = require('./read_preference').ReadPreference + , Base = require('./base').Base + , inherits = require('util').inherits; + +/** + * Mongos constructor provides a connection to a mongos proxy including failover to additional servers + * + * Options + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **ha** {Boolean, default:true}, turn on high availability, attempts to reconnect to down proxies + * - **haInterval** {Number, default:2000}, time between each replicaset status check. + * + * @class Represents a Mongos connection with failover to backup proxies + * @param {Array} list of mongos server objects + * @param {Object} [options] additional options for the mongos connection + */ +var Mongos = function Mongos(servers, options) { + // Set up basic + if(!(this instanceof Mongos)) + return new Mongos(servers, options); + + // Set up event emitter + Base.call(this); + + // Throw error on wrong setup + if(servers == null || !Array.isArray(servers) || servers.length == 0) + throw new Error("At least one mongos proxy must be in the array"); + + // Ensure we have at least an empty options object + this.options = options == null ? {} : options; + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + // Enabled ha + this.haEnabled = this.options['ha'] == null ? true : this.options['ha']; + // How often are we checking for new servers in the replicaset + this.mongosStatusCheckInterval = this.options['haInterval'] == null ? 2000 : this.options['haInterval']; + // Save all the server connections + this.servers = servers; + // Servers we need to attempt reconnect with + this.downServers = []; + // Just contains the current lowest ping time and server + this.lowestPingTimeServer = null; + this.lowestPingTime = 0; + + // Add options to servers + for(var i = 0; i < this.servers.length; i++) { + var server = this.servers[i]; + // Default empty socket options object + var socketOptions = {host: server.host, port: server.port}; + // If a socket option object exists clone it + if(this.socketOptions != null) { + var keys = Object.keys(this.socketOptions); + for(var k = 0; k < keys.length;k++) socketOptions[keys[i]] = this.socketOptions[keys[i]]; + } + // Set socket options + server.socketOptions = socketOptions; + } +} + +/** + * @ignore + */ +inherits(Mongos, Base); + +/** + * @ignore + */ +Mongos.prototype.isMongos = function() { + return true; +} + +/** + * @ignore + */ +Mongos.prototype.connect = function(db, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + var self = this; + + // Keep reference to parent + this.db = db; + // Set server state to connecting + this._serverState = 'connecting'; + // Number of total servers that need to initialized (known servers) + this._numberOfServersLeftToInitialize = this.servers.length; + // Default to the first proxy server as the first one to use + this._currentMongos = this.servers[0]; + + // Connect handler + var connectHandler = function(_server) { + return function(err, result) { + self._numberOfServersLeftToInitialize = self._numberOfServersLeftToInitialize - 1; + + if(self._numberOfServersLeftToInitialize == 0) { + // Start ha function if it exists + if(self.haEnabled) { + // Setup the ha process + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + + // Set the mongos to connected + self._serverState = "connected"; + // Emit the open event + self.db.emit("open", null, self.db); + // Callback + callback(null, self.db); + } + } + }; + + // Error handler + var errorOrCloseHandler = function(_server) { + return function(err, result) { + // Create current mongos comparision + var currentUrl = self._currentMongos.host + ":" + self._currentMongos.port; + var serverUrl = this.host + ":" + this.port; + // We need to check if the server that closed is the actual current proxy we are using, otherwise + // just ignore + if(currentUrl == serverUrl) { + // Remove the server from the list + if(self.servers.indexOf(_server) != -1) { + self.servers.splice(self.servers.indexOf(_server), 1); + } + + // Pick the next one on the list if there is one + for(var i = 0; i < self.servers.length; i++) { + // Grab the server out of the array (making sure there is no servers in the list if none available) + var server = self.servers[i]; + // Generate url for comparision + var serverUrl = server.host + ":" + server.port; + // It's not the current one and connected set it as the current db + if(currentUrl != serverUrl && server.isConnected()) { + self._currentMongos = server; + break; + } + } + } + + // Ensure we don't store the _server twice + if(self.downServers.indexOf(_server) == -1) { + // Add the server instances + self.downServers.push(_server); + } + } + } + + // Mongo function + this.mongosCheckFunction = function() { + // If we have down servers let's attempt a reconnect + if(self.downServers.length > 0) { + var numberOfServersLeft = self.downServers.length; + // Attempt to reconnect + for(var i = 0; i < self.downServers.length; i++) { + var downServer = self.downServers.pop(); + // Attemp to reconnect + downServer.connect(self.db, {returnIsMasterResults: true}, function(_server) { + // Return a function to check for the values + return function(err, result) { + // Adjust the number of servers left + numberOfServersLeft = numberOfServersLeft - 1; + + if(err != null) { + self.downServers.push(_server); + } else { + // Add server event handlers + _server.on("close", errorOrCloseHandler(_server)); + _server.on("error", errorOrCloseHandler(_server)); + // Add to list of servers + self.servers.push(_server); + } + + if(numberOfServersLeft <= 0) { + // Perfom another ha + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + } + }(downServer)); + } + } else if(self.servers.length > 0) { + var numberOfServersLeft = self.servers.length; + var _s = new Date().getTime() + + // Else let's perform a ping command + for(var i = 0; i < self.servers.length; i++) { + var executePing = function(_server) { + // Get a read connection + var _connection = _server.checkoutReader(); + // Execute ping command + self.db.command({ping:1}, {connection:_connection}, function(err, result) { + var pingTime = new Date().getTime() - _s; + // If no server set set the first one, otherwise check + // the lowest ping time and assign the server if it's got a lower ping time + if(self.lowestPingTimeServer == null) { + self.lowestPingTimeServer = _server; + self.lowestPingTime = pingTime; + self._currentMongos = _server; + } else if(self.lowestPingTime > pingTime + && (_server.host != self.lowestPingTimeServer.host || _server.port != self.lowestPingTimeServer.port)) { + self.lowestPingTimeServer = _server; + self.lowestPingTime = pingTime; + self._currentMongos = _server; + } + + // Number of servers left + numberOfServersLeft = numberOfServersLeft - 1; + // All active mongos's pinged + if(numberOfServersLeft == 0) { + // Perfom another ha + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + }) + } + // Execute the function + executePing(self.servers[i]); + } + } else { + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + } + + // Connect all the server instances + for(var i = 0; i < this.servers.length; i++) { + // Get the connection + var server = this.servers[i]; + server.mongosInstance = this; + // Add server event handlers + server.on("close", errorOrCloseHandler(server)); + server.on("timeout", errorOrCloseHandler(server)); + server.on("error", errorOrCloseHandler(server)); + // Connect the instance + server.connect(self.db, {returnIsMasterResults: true}, connectHandler(server)); + } +} + +/** + * @ignore + * Just return the currently picked active connection + */ +Mongos.prototype.allServerInstances = function() { + return this.servers; +} + +/** + * Always ourselves + * @ignore + */ +Mongos.prototype.setReadPreference = function() {} + +/** + * @ignore + */ +Mongos.prototype.allRawConnections = function() { + // Neeed to build a complete list of all raw connections, start with master server + var allConnections = []; + // Add all connections + for(var i = 0; i < this.servers.length; i++) { + allConnections = allConnections.concat(this.servers[i].allRawConnections()); + } + + // Return all the conections + return allConnections; +} + +/** + * @ignore + */ +Mongos.prototype.isConnected = function() { + return this._serverState == "connected"; +} + +/** + * @ignore + */ +Mongos.prototype.checkoutWriter = function() { + // No current mongo, just pick first server + if(this._currentMongos == null && this.servers.length > 0) { + return this.servers[0].checkoutWriter(); + } + return this._currentMongos.checkoutWriter(); +} + +/** + * @ignore + */ +Mongos.prototype.checkoutReader = function(read) { + // If we have a read preference object unpack it + if(typeof read == 'object' && read['_type'] == 'ReadPreference') { + // Validate if the object is using a valid mode + if(!read.isValid()) throw new Error("Illegal readPreference mode specified, " + read.mode); + } else if(!ReadPreference.isValid(read)) { + throw new Error("Illegal readPreference mode specified, " + read); + } + + // No current mongo, just pick first server + if(this._currentMongos == null && this.servers.length > 0) { + return this.servers[0].checkoutReader(); + } + return this._currentMongos.checkoutReader(); +} + +/** + * @ignore + */ +Mongos.prototype.close = function(callback) { + var self = this; + // Set server status as disconnected + this._serverState = 'disconnected'; + // Number of connections to close + var numberOfConnectionsToClose = self.servers.length; + // If we have a ha process running kill it + if(self._replicasetTimeoutId != null) clearTimeout(self._replicasetTimeoutId); + // Close all proxy connections + for(var i = 0; i < self.servers.length; i++) { + self.servers[i].close(function(err, result) { + numberOfConnectionsToClose = numberOfConnectionsToClose - 1; + // Callback if we have one defined + if(numberOfConnectionsToClose == 0 && typeof callback == 'function') { + callback(null); + } + }); + } +} + +/** + * @ignore + * Return the used state + */ +Mongos.prototype._isUsed = function() { + return this._used; +} + +exports.Mongos = Mongos; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/read_preference.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/read_preference.js new file mode 100644 index 0000000..4cba587 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/read_preference.js @@ -0,0 +1,66 @@ +/** + * A class representation of the Read Preference. + * + * Read Preferences + * - **ReadPreference.PRIMARY**, Read from primary only. All operations produce an error (throw an exception where applicable) if primary is unavailable. Cannot be combined with tags (This is the default.). + * - **ReadPreference.PRIMARY_PREFERRED**, Read from primary if available, otherwise a secondary. + * - **ReadPreference.SECONDARY**, Read from secondary if available, otherwise error. + * - **ReadPreference.SECONDARY_PREFERRED**, Read from a secondary if available, otherwise read from the primary. + * - **ReadPreference.NEAREST**, All modes read from among the nearest candidates, but unlike other modes, NEAREST will include both the primary and all secondaries in the random selection. + * + * @class Represents a Read Preference. + * @param {String} the read preference type + * @param {Object} tags + * @return {ReadPreference} + */ +var ReadPreference = function(mode, tags) { + if(!(this instanceof ReadPreference)) + return new ReadPreference(mode, tags); + this._type = 'ReadPreference'; + this.mode = mode; + this.tags = tags; +} + +/** + * @ignore + */ +ReadPreference.isValid = function(_mode) { + return (_mode == ReadPreference.PRIMARY || _mode == ReadPreference.PRIMARY_PREFERRED + || _mode == ReadPreference.SECONDARY || _mode == ReadPreference.SECONDARY_PREFERRED + || _mode == ReadPreference.NEAREST); +} + +/** + * @ignore + */ +ReadPreference.prototype.isValid = function(mode) { + var _mode = typeof mode == 'string' ? mode : this.mode; + return ReadPreference.isValid(_mode); +} + +/** + * @ignore + */ +ReadPreference.prototype.toObject = function() { + var object = {mode:this.mode}; + + if(this.tags != null) { + object['tags'] = this.tags; + } + + return object; +} + +/** + * @ignore + */ +ReadPreference.PRIMARY = 'primary'; +ReadPreference.PRIMARY_PREFERRED = 'primaryPreferred'; +ReadPreference.SECONDARY = 'secondary'; +ReadPreference.SECONDARY_PREFERRED = 'secondaryPreferred'; +ReadPreference.NEAREST = 'nearest' + +/** + * @ignore + */ +exports.ReadPreference = ReadPreference; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/repl_set.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/repl_set.js new file mode 100644 index 0000000..a471e89 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/repl_set.js @@ -0,0 +1,1311 @@ +var Connection = require('./connection').Connection, + ReadPreference = require('./read_preference').ReadPreference, + DbCommand = require('../commands/db_command').DbCommand, + MongoReply = require('../responses/mongo_reply').MongoReply, + debug = require('util').debug, + inherits = require('util').inherits, + inspect = require('util').inspect, + Server = require('./server').Server, + PingStrategy = require('./strategies/ping_strategy').PingStrategy, + StatisticsStrategy = require('./strategies/statistics_strategy').StatisticsStrategy, + Base = require('./base').Base; + +const STATE_STARTING_PHASE_1 = 0; +const STATE_PRIMARY = 1; +const STATE_SECONDARY = 2; +const STATE_RECOVERING = 3; +const STATE_FATAL_ERROR = 4; +const STATE_STARTING_PHASE_2 = 5; +const STATE_UNKNOWN = 6; +const STATE_ARBITER = 7; +const STATE_DOWN = 8; +const STATE_ROLLBACK = 9; + +/** + * ReplSet constructor provides replicaset functionality + * + * Options + * - **ha** {Boolean, default:true}, turn on high availability. + * - **haInterval** {Number, default:2000}, time between each replicaset status check. + * - **reconnectWait** {Number, default:1000}, time to wait in miliseconds before attempting reconnect. + * - **retries** {Number, default:30}, number of times to attempt a replicaset reconnect. + * - **rs_name** {String}, the name of the replicaset to connect to. + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strategy** {String, default:null}, selection strategy for reads choose between (ping and statistical, default is round-robin) + * - **secondaryAcceptableLatencyMS** {Number, default:15}, sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms) + * - **connectArbiter** {Boolean, default:false}, sets if the driver should connect to arbiters or not. + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * + * @class Represents a Replicaset Configuration + * @param {Array} list of server objects participating in the replicaset. + * @param {Object} [options] additional options for the replicaset connection. + */ +var ReplSet = exports.ReplSet = function(servers, options) { + this.count = 0; + + // Set up basic + if(!(this instanceof ReplSet)) + return new ReplSet(servers, options); + + // Set up event emitter + Base.call(this); + + // Ensure no Mongos's + for(var i = 0; i < servers.length; i++) { + if(!(servers[i] instanceof Server)) throw new Error("list of servers must be of type Server"); + } + + // Just reference for simplicity + var self = this; + // Contains the master server entry + this.options = options == null ? {} : options; + this.reconnectWait = this.options["reconnectWait"] != null ? this.options["reconnectWait"] : 1000; + this.retries = this.options["retries"] != null ? this.options["retries"] : 30; + this.replicaSet = this.options["rs_name"]; + + // Are we allowing reads from secondaries ? + this.readSecondary = this.options["read_secondary"]; + this.slaveOk = true; + this.closedConnectionCount = 0; + this._used = false; + + // Connect arbiters ? + this.connectArbiter = this.options.connectArbiter == null ? false : this.options.connectArbiter; + + // Default poolSize for new server instances + this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize; + this._currentServerChoice = 0; + + // Set up ssl connections + this.ssl = this.options.ssl == null ? false : this.options.ssl; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]}; + // Internal state of server connection + this._serverState = 'disconnected'; + // Read preference + this._readPreference = null; + // Number of initalized severs + this._numberOfServersLeftToInitialize = 0; + // Do we record server stats or not + this.recordQueryStats = false; + // Update health try server + this.updateHealthServerTry = 0; + + // Get the readPreference + var readPreference = this.options['readPreference']; + + // Validate correctness of Read preferences + if(readPreference != null) { + if(readPreference != ReadPreference.PRIMARY && readPreference != ReadPreference.PRIMARY_PREFERRED + && readPreference != ReadPreference.SECONDARY && readPreference != ReadPreference.SECONDARY_PREFERRED + && readPreference != ReadPreference.NEAREST && typeof readPreference != 'object' && readPreference['_type'] != 'ReadPreference') { + throw new Error("Illegal readPreference mode specified, " + readPreference); + } + + this._readPreference = readPreference; + } else { + this._readPreference = null; + } + + // Ensure read_secondary is set correctly + if(!this.readSecondary) + this.readSecondary = this._readPreference == ReadPreference.PRIMARY + || this._readPreference == false + || this._readPreference == null ? false : true; + + // Strategy for picking a secondary + this.secondaryAcceptableLatencyMS = this.options['secondaryAcceptableLatencyMS'] == null ? 15 : this.options['secondaryAcceptableLatencyMS']; + this.strategy = this.options['strategy']; + // Make sure strategy is one of the two allowed + if(this.strategy != null && (this.strategy != 'ping' && this.strategy != 'statistical')) throw new Error("Only ping or statistical strategies allowed"); + // Let's set up our strategy object for picking secodaries + if(this.strategy == 'ping') { + // Create a new instance + this.strategyInstance = new PingStrategy(this, this.secondaryAcceptableLatencyMS); + } else if(this.strategy == 'statistical') { + // Set strategy as statistical + this.strategyInstance = new StatisticsStrategy(this); + // Add enable query information + this.enableRecordQueryStats(true); + } + + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + + // Set up logger if any set + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.debug == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + + // Ensure all the instances are of type server and auto_reconnect is false + if(!Array.isArray(servers) || servers.length == 0) { + throw Error("The parameter must be an array of servers and contain at least one server"); + } else if(Array.isArray(servers) || servers.length > 0) { + var count = 0; + servers.forEach(function(server) { + if(server instanceof Server) count = count + 1; + // Ensure no server has reconnect on + server.options.auto_reconnect = false; + }); + + if(count < servers.length) { + throw Error("All server entries must be of type Server"); + } else { + this.servers = servers; + } + } + + // var deduplicate list + var uniqueServers = {}; + // De-duplicate any servers in the seed list + for(var i = 0; i < this.servers.length; i++) { + var server = this.servers[i]; + // If server does not exist set it + if(uniqueServers[server.host + ":" + server.port] == null) { + uniqueServers[server.host + ":" + server.port] = server; + } + } + + // Let's set the deduplicated list of servers + this.servers = []; + // Add the servers + for(var key in uniqueServers) { + this.servers.push(uniqueServers[key]); + } + + // Enabled ha + this.haEnabled = this.options['ha'] == null ? true : this.options['ha']; + // How often are we checking for new servers in the replicaset + this.replicasetStatusCheckInterval = this.options['haInterval'] == null ? 1000 : this.options['haInterval']; + this._replicasetTimeoutId = null; + + // Connection timeout + this._connectTimeoutMS = this.socketOptions.connectTimeoutMS + ? this.socketOptions.connectTimeoutMS + : 1000; + + // Current list of servers to test + this.pingCandidateServers = []; + + // Last replicaset check time + this.lastReplicaSetTime = new Date().getTime(); +}; + +/** + * @ignore + */ +inherits(ReplSet, Base); + +/** + * @ignore + */ +// Allow setting the read preference at the replicaset level +ReplSet.prototype.setReadPreference = function(preference) { + // Set read preference + this._readPreference = preference; + // Ensure slaveOk is correct for secodnaries read preference and tags + if((this._readPreference == ReadPreference.SECONDARY_PREFERRED || this._readPreference == ReadPreference.SECONDARY) + || (this._readPreference != null && typeof this._readPreference == 'object')) { + this.slaveOk = true; + } +} + +/** + * @ignore + */ +ReplSet.prototype._isUsed = function() { + return this._used; +} + +/** + * @ignore + */ +ReplSet.prototype.isMongos = function() { + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isConnected = function(read) { + if(read == null || read == ReadPreference.PRIMARY || read == false) + return this.primary != null && this._state.master != null && this._state.master.isConnected(); + + if((read == ReadPreference.PRIMARY_PREFERRED || read == ReadPreference.SECONDARY_PREFERRED || read == ReadPreference.NEAREST) + && ((this.primary != null && this._state.master != null && this._state.master.isConnected()) + || (this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0))) { + return true; + } else if(read == ReadPreference.SECONDARY) { + return this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0; + } + + // No valid connection return false + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isSetMember = function() { + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isPrimary = function(config) { + return this.readSecondary && Object.keys(this._state.secondaries).length > 0 ? false : true; +} + +/** + * @ignore + */ +ReplSet.prototype.isReadPrimary = ReplSet.prototype.isPrimary; + +/** + * @ignore + */ +ReplSet.prototype.allServerInstances = function() { + var self = this; + // If no state yet return empty + if(!self._state) return []; + // Close all the servers (concatenate entire list of servers first for ease) + var allServers = self._state.master != null ? [self._state.master] : []; + + // Secondary keys + var keys = Object.keys(self._state.secondaries); + // Add all secondaries + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.secondaries[keys[i]]); + } + + // Arbiter keys + var keys = Object.keys(self._state.arbiters); + // Add all arbiters + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.arbiters[keys[i]]); + } + + // Passive keys + var keys = Object.keys(self._state.passives); + // Add all arbiters + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.passives[keys[i]]); + } + + // Return complete list of all servers + return allServers; +} + +/** + * Enables high availability pings. + * + * @ignore + */ +ReplSet.prototype._enableHA = function () { + var self = this; + return check(); + + function ping () { + if("disconnected" == self._serverState) return; + + if(Object.keys(self._state.addresses).length == 0) return; + var selectedServer = self._state.addresses[Object.keys(self._state.addresses)[self.updateHealthServerTry++]]; + if(self.updateHealthServerTry >= Object.keys(self._state.addresses).length) self.updateHealthServerTry = 0; + if(selectedServer == null) return check(); + + // If we have an active db instance + if(self.dbInstances.length > 0) { + var db = self.dbInstances[0]; + + // Create a new master connection + var _server = new Server(selectedServer.host, selectedServer.port, { + auto_reconnect: false, + returnIsMasterResults: true, + slaveOk: true, + poolSize: 1, + socketOptions: { connectTimeoutMS: self._connectTimeoutMS } + }); + + // Connect using the new _server connection to not impact the driver + // behavior on any errors we could possibly run into + _server.connect(db, function(err, result, _server) { + if(err) { + if(_server.close) _server.close(); + return check(); + } + + // Create is master command + var cmd = DbCommand.createIsMasterCommand(db); + // Execute is master command + db._executeQueryCommand(cmd, {failFast:true, connection: _server.checkoutWriter()}, function(err, res) { + // Close the connection used + _server.close(); + // If error let's set perform another check + if(err) return check(); + // Validate the replicaset + self._validateReplicaset(res, db.auths, function() { + check(); + }); + }); + }); + } + } + + function check () { + self._haTimer = setTimeout(ping, self.replicasetStatusCheckInterval); + } +} + +/** + * @ignore + */ +ReplSet.prototype._validateReplicaset = function(result, auths, cb) { + var self = this; + var res = result.documents[0]; + + // manage master node changes + if(res.primary && self._state.master && self._state.master.name != res.primary) { + // Delete master record so we can rediscover it + delete self._state.addresses[self._state.master.name]; + + // TODO existing issue? this seems to only work if + // we already have a connection to the new primary. + + // Update information on new primary + // add as master, remove from secondary + var newMaster = self._state.addresses[res.primary]; + newMaster.isMasterDoc.ismaster = true; + newMaster.isMasterDoc.secondary = false; + self._state.master = newMaster; + delete self._state.secondaries[res.primary]; + } + + // discover new hosts + var hosts = []; + + for(var i = 0; i < res.hosts.length; ++i) { + var host = res.hosts[i]; + if (host == res.me) continue; + if (!(self._state.addresses[host] || ~hosts.indexOf(host))) { + // we dont already have a connection to this host and aren't + // already planning on connecting. + hosts.push(host); + } + } + + connectTo(hosts, auths, self, cb); +} + +/** + * Create connections to all `hosts` firing `cb` after + * connections are attempted for all `hosts`. + * + * @param {Array} hosts + * @param {Array} [auths] + * @param {ReplSet} replset + * @param {Function} cb + * @ignore + */ +function connectTo (hosts, auths, replset, cb) { + var pending = hosts.length; + if (!pending) return cb(); + + for(var i = 0; i < hosts.length; ++i) { + connectToHost(hosts[i], auths, replset, handle); + } + + function handle () { + --pending; + if (0 === pending) cb(); + } +} + +/** + * Attempts connection to `host` and authenticates with optional `auth` + * for the given `replset` firing `cb` when finished. + * + * @param {String} host + * @param {Array} auths + * @param {ReplSet} replset + * @param {Function} cb + * @ignore + */ +function connectToHost (host, auths, replset, cb) { + var server = createServer(host, replset); + + var options = { + returnIsMasterResults: true, + eventReceiver: server + } + + server.connect(replset.db, options, function(err, result) { + var doc = result && result.documents && result.documents[0]; + + if (err || !doc) { + server.close(); + return cb(err, result, server); + } + + if(!(doc.ismaster || doc.secondary || doc.arbiterOnly)) { + server.close(); + return cb(null, result, server); + } + + // if host is an arbiter, disconnect if not configured for it + if(doc.arbiterOnly && !replset.connectArbiter) { + server.close(); + return cb(null, result, server); + } + + // create handler for successful connections + var handleConnect = _connectHandler(replset, null, server); + function complete () { + handleConnect(err, result); + cb(); + } + + // authenticate if necessary + if(!(Array.isArray(auths) && auths.length > 0)) { + return complete(); + } + + var pending = auths.length; + + var connections = server.allRawConnections(); + var pendingAuthConn = connections.length; + for(var x = 0; x 0) { + self.db.emit(event, err); + } + + // If it's the primary close all connections + if(self._state.master + && self._state.master.host == server.host + && self._state.master.port == server.port) { + // return self.close(); + self._state.master = null; + } + } +} + +var _connectHandler = function(self, candidateServers, instanceServer) { + return function(err, result) { + // We are disconnected stop attempting reconnect or connect + if(self._serverState == 'disconnected') return instanceServer.close(); + + // If no error handle isMaster + if(err == null && result.documents[0].hosts != null) { + // Fetch the isMaster command result + var document = result.documents[0]; + // Break out the results + var setName = document.setName; + var isMaster = document.ismaster; + var secondary = document.secondary; + var passive = document.passive; + var arbiterOnly = document.arbiterOnly; + var hosts = Array.isArray(document.hosts) ? document.hosts : []; + var arbiters = Array.isArray(document.arbiters) ? document.arbiters : []; + var passives = Array.isArray(document.passives) ? document.passives : []; + var tags = document.tags ? document.tags : {}; + var primary = document.primary; + // Find the current server name and fallback if none + var userProvidedServerString = instanceServer.host + ":" + instanceServer.port; + var me = document.me || userProvidedServerString; + + // Verify if the set name is the same otherwise shut down and return an error + if(self.replicaSet == null) { + self.replicaSet = setName; + } else if(self.replicaSet != setName) { + // Stop the set + self.close(); + // Emit a connection error + return self.emit("connectionError", + new Error("configured mongodb replicaset does not match provided replicaset [" + setName + "] != [" + self.replicaSet + "]")) + } + + // Make sure we have the right reference + var oldServer = self._state.addresses[userProvidedServerString] + if (oldServer && oldServer !== instanceServer) oldServer.close(); + delete self._state.addresses[userProvidedServerString]; + + if (self._state.addresses[me] && self._state.addresses[me] !== instanceServer) { + self._state.addresses[me].close(); + } + + self._state.addresses[me] = instanceServer; + + // Let's add the server to our list of server types + if(secondary == true && (passive == false || passive == null)) { + self._state.secondaries[me] = instanceServer; + } else if(arbiterOnly == true) { + self._state.arbiters[me] = instanceServer; + } else if(secondary == true && passive == true) { + self._state.passives[me] = instanceServer; + } else if(isMaster == true) { + self._state.master = instanceServer; + } else if(isMaster == false && primary != null && self._state.addresses[primary]) { + self._state.master = self._state.addresses[primary]; + } + + // Set the name + instanceServer.name = me; + // Add tag info + instanceServer.tags = tags; + + // Add the handlers to the instance + instanceServer.on("close", _handler("close", self)); + instanceServer.on("error", _handler("error", self)); + instanceServer.on("timeout", _handler("timeout", self)); + + // Possible hosts + var possibleHosts = Array.isArray(hosts) ? hosts.slice() : []; + possibleHosts = Array.isArray(passives) ? possibleHosts.concat(passives) : possibleHosts; + + if(self.connectArbiter == true) { + possibleHosts = Array.isArray(arbiters) ? possibleHosts.concat(arbiters) : possibleHosts; + } + + if(Array.isArray(candidateServers)) { + // Add any new candidate servers for connection + for(var j = 0; j < possibleHosts.length; j++) { + if(self._state.addresses[possibleHosts[j]] == null && possibleHosts[j] != null) { + var parts = possibleHosts[j].split(/:/); + if(parts.length == 1) { + parts = [parts[0], Connection.DEFAULT_PORT]; + } + + // New candidate server + var candidateServer = new Server(parts[0], parseInt(parts[1])); + candidateServer.name = possibleHosts[j]; + self._state.addresses[possibleHosts[j]] = candidateServer; + // Add the new server to the list of candidate servers + candidateServers.push(candidateServer); + } + } + } + } else if(err != null || self._serverState == 'disconnected'){ + delete self._state.addresses[instanceServer.host + ":" + instanceServer.port]; + // Remove it from the set + instanceServer.close(); + } + + // Attempt to connect to the next server + if(Array.isArray(candidateServers) && candidateServers.length > 0) { + var server = candidateServers.pop(); + + // Get server addresses + var addresses = self._state.addresses; + + // Default empty socket options object + var socketOptions = {}; + + // Set fast connect timeout + socketOptions['connectTimeoutMS'] = self._connectTimeoutMS; + + // If a socket option object exists clone it + if(self.socketOptions != null && typeof self.socketOptions === 'object') { + var keys = Object.keys(self.socketOptions); + for(var j = 0; j < keys.length;j++) socketOptions[keys[j]] = self.socketOptions[keys[j]]; + } + + // If ssl is specified + if(self.ssl) server.ssl = true; + + // Add host information to socket options + socketOptions['host'] = server.host; + socketOptions['port'] = server.port; + server.socketOptions = socketOptions; + server.replicasetInstance = self; + server.enableRecordQueryStats(self.recordQueryStats); + + // Set the server + if (addresses[server.host + ":" + server.port] != server) { + if (addresses[server.host + ":" + server.port]) { + // Close the connection before deleting + addresses[server.host + ":" + server.port].close(); + } + delete addresses[server.host + ":" + server.port]; + } + addresses[server.host + ":" + server.port] = server; + // Connect + server.connect(self.db, {returnIsMasterResults: true, eventReceiver:server}, _connectHandler(self, candidateServers, server)); + } else if(Array.isArray(candidateServers)) { + // If we have no primary emit error + if(self._state.master == null) { + // Stop the set + self.close(); + // Emit a connection error + return self.emit("connectionError", + new Error("no primary server found in set")) + } else{ + if (self.strategyInstance) { + self.strategyInstance.start(); + } + + self.emit("fullsetup", null, self.db, self); + self.emit("open", null, self.db, self); + } + } + } +} + +/** + * Interval state object constructor + * + * @ignore + */ +ReplSet.State = function ReplSetState () { + this.errorMessages = []; + this.secondaries = {}; + this.addresses = {}; + this.arbiters = {}; + this.passives = {}; + this.members = []; + this.errors = {}; + this.setName = null; + this.master = null; +} + +/** + * @ignore + */ +ReplSet.prototype.connect = function(parent, options, callback) { + var self = this; + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Ensure it's all closed + self.close(); + + // Set connecting status + this.db = parent; + this._serverState = 'connecting'; + this._callbackList = []; + + this._state = new ReplSet.State(); + + // Ensure parent can do a slave query if it's set + parent.slaveOk = this.slaveOk + ? this.slaveOk + : parent.slaveOk; + + // Remove any listeners + this.removeAllListeners("fullsetup"); + this.removeAllListeners("connectionError"); + + // Add primary found event handler + this.once("fullsetup", function() { + self._handleOnFullSetup(parent); + + // Callback + if(typeof callback == 'function') { + var internalCallback = callback; + callback = null; + internalCallback(null, parent, self); + } + }); + + this.once("connectionError", function(err) { + self._serverState = 'disconnected'; + // Ensure it's all closed + self.close(); + // Perform the callback + if(typeof callback == 'function') { + var internalCallback = callback; + callback = null; + internalCallback(err, parent, self); + } + }); + + // Get server addresses + var addresses = this._state.addresses; + + // De-duplicate any servers + var server, key; + for(var i = 0; i < this.servers.length; i++) { + server = this.servers[i]; + key = server.host + ":" + server.port; + if(null == addresses[key]) { + addresses[key] = server; + } + } + + // Get the list of servers that is deduplicated and start connecting + var candidateServers = []; + var keys = Object.keys(addresses); + for(var i = 0; i < keys.length; i++) { + server = addresses[keys[i]]; + server.assignReplicaSet(this); + candidateServers.push(server); + } + + // Let's connect to the first one on the list + server = candidateServers.pop(); + var opts = { + returnIsMasterResults: true, + eventReceiver: server + } + server.connect(parent, opts, _connectHandler(this, candidateServers, server)); +} + +/** + * Handles the first `fullsetup` event of this ReplSet. + * + * @param {Db} parent + * @ignore + */ +ReplSet.prototype._handleOnFullSetup = function (parent) { + this._serverState = 'connected'; + + // Emit the fullsetup and open event + parent.emit("open", null, this.db, this); + parent.emit("fullsetup", null, this.db, this); + + if(!this.haEnabled) return; + this._enableHA(); +} + +/** + * Disables high availability pings. + * + * @ignore + */ +ReplSet.prototype._disableHA = function () { + clearTimeout(this._haTimer); + this._haTimer = undefined; +} + +/** + * @ignore + */ +ReplSet.prototype.checkoutWriter = function() { + // Establish connection + var connection = this._state.master != null ? this._state.master.checkoutWriter() : null; + // Return the connection + return connection; +} + +/** + * @ignore + */ +var pickFirstConnectedSecondary = function pickFirstConnectedSecondary(self, tags) { + var keys = Object.keys(self._state.secondaries); + var connection; + + // Find first available reader if any + for(var i = 0; i < keys.length; i++) { + connection = self._state.secondaries[keys[i]].checkoutReader(); + if(connection) return connection; + } + + // If we still have a null, read from primary if it's not secondary only + if(self._readPreference == ReadPreference.SECONDARY_PREFERRED) { + connection = self._state.master.checkoutReader(); + if(connection) return connection; + } + + var preferenceName = self._readPreference == ReadPreference.SECONDARY_PREFERRED + ? 'secondary' + : self._readPreference; + + // console.log("================================================================ pickFirstConnectedSecondary :::: ") + + return new Error("No replica set member available for query with ReadPreference " + + preferenceName + " and tags " + JSON.stringify(tags)); +} + +/** + * @ignore + */ +var _pickFromTags = function(self, tags) { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Match all the servers that match the provdided tags + var keys = Object.keys(self._state.secondaries); + var candidateServers = []; + + for(var i = 0; i < keys.length; i++) { + var server = self._state.secondaries[keys[i]]; + // If we have tags match + if(server.tags != null) { + var matching = true; + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + candidateServers.push(server); + } + } + } + + // If we have a candidate server return + if(candidateServers.length > 0) { + if(this.strategyInstance) return this.strategyInstance.checkoutSecondary(tags, candidateServers); + // Set instance to return + return candidateServers[Math.floor(Math.random() * candidateServers.length)].checkoutReader(); + } + } + + // No connection found + return null; +} + +/** + * @ignore + */ +ReplSet.prototype.checkoutReader = function(readPreference, tags) { + var connection = null; + // console.log("============================ checkoutReader") + // console.dir(readPreference) + // console.log(arguments.callee.caller.toString()) + + // If we have a read preference object unpack it + if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') { + // Validate if the object is using a valid mode + if(!readPreference.isValid()) throw new Error("Illegal readPreference mode specified, " + readPreference.mode); + // Set the tag + tags = readPreference.tags; + readPreference = readPreference.mode; + } else if(typeof readPreference == 'object' && readPreference['_type'] != 'ReadPreference') { + throw new Error("read preferences must be either a string or an instance of ReadPreference"); + } + + // Set up our read Preference, allowing us to override the readPreference + var finalReadPreference = readPreference != null ? readPreference : this._readPreference; + finalReadPreference = finalReadPreference == true ? ReadPreference.SECONDARY_PREFERRED : finalReadPreference; + finalReadPreference = finalReadPreference == null ? ReadPreference.PRIMARY : finalReadPreference; + // finalReadPreference = 'primary'; + + // console.log("============================ finalReadPreference: " + finalReadPreference) + // console.dir(finalReadPreference) + + // If we are reading from a primary + if(finalReadPreference == 'primary') { + // If we provide a tags set send an error + if(typeof tags == 'object' && tags != null) { + return new Error("PRIMARY cannot be combined with tags"); + } + + // If we provide a tags set send an error + if(this._state.master == null) { + return new Error("No replica set primary available for query with ReadPreference PRIMARY"); + } + + // Checkout a writer + return this.checkoutWriter(); + } + + // If we have specified to read from a secondary server grab a random one and read + // from it, otherwise just pass the primary connection + if((this.readSecondary || finalReadPreference == ReadPreference.SECONDARY_PREFERRED || finalReadPreference == ReadPreference.SECONDARY) && Object.keys(this._state.secondaries).length > 0) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } else { + connection = _roundRobin(this, tags); + } + } else if(finalReadPreference == ReadPreference.PRIMARY_PREFERRED) { + // Check if there is a primary available and return that if possible + connection = this.checkoutWriter(); + // If no connection available checkout a secondary + if(connection == null) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } else { + connection = _roundRobin(this, tags); + } + } + } else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED && tags == null && Object.keys(this._state.secondaries).length == 0) { + // console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY_PREFERRED") + connection = this.checkoutWriter(); + // If no connection return an error + if(connection == null) { + var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference; + connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); + } + } else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + // No secondary server avilable, attemp to checkout a primary server + connection = this.checkoutWriter(); + // If no connection return an error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } + } else if(this.strategyInstance != null) { + connection = this.strategyInstance.checkoutReader(tags); + } + } else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance != null) { + connection = this.strategyInstance.checkoutSecondary(tags); + } else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance == null) { + return new Error("A strategy for calculating nearness must be enabled such as ping or statistical"); + } else if(finalReadPreference == ReadPreference.SECONDARY && Object.keys(this._state.secondaries).length == 0) { + // console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY") + if(tags != null && typeof tags == 'object') { + var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference; + connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); + } else { + connection = new Error("No replica set secondary available for query with ReadPreference SECONDARY"); + } + } else { + connection = this.checkoutWriter(); + } + + // Return the connection + return connection; +} + +/** + * Pick a secondary using round robin + * + * @ignore + */ +function _roundRobin (replset, tags) { + var keys = Object.keys(replset._state.secondaries); + var key = keys[replset._currentServerChoice++ % keys.length]; + + var conn = null != replset._state.secondaries[key] + ? replset._state.secondaries[key].checkoutReader() + : null; + + // If connection is null fallback to first available secondary + if (null == conn) { + conn = pickFirstConnectedSecondary(replset, tags); + } + + return conn; +} + +/** + * @ignore + */ +ReplSet.prototype.allRawConnections = function() { + // Neeed to build a complete list of all raw connections, start with master server + var allConnections = []; + if(this._state.master == null) return []; + // Get connection object + var allMasterConnections = this._state.master.connectionPool.getAllConnections(); + // Add all connections to list + allConnections = allConnections.concat(allMasterConnections); + // If we have read secondary let's add all secondary servers + if(Object.keys(this._state.secondaries).length > 0) { + // Get all the keys + var keys = Object.keys(this._state.secondaries); + // For each of the secondaries grab the connections + for(var i = 0; i < keys.length; i++) { + // Get connection object + var secondaryPoolConnections = this._state.secondaries[keys[i]].connectionPool.getAllConnections(); + // Add all connections to list + allConnections = allConnections.concat(secondaryPoolConnections); + } + } + + // Return all the conections + return allConnections; +} + +/** + * @ignore + */ +ReplSet.prototype.enableRecordQueryStats = function(enable) { + // Set the global enable record query stats + this.recordQueryStats = enable; + // Ensure all existing servers already have the flag set, even if the + // connections are up already or we have not connected yet + if(this._state != null && this._state.addresses != null) { + var keys = Object.keys(this._state.addresses); + // Iterate over all server instances and set the enableRecordQueryStats flag + for(var i = 0; i < keys.length; i++) { + this._state.addresses[keys[i]].enableRecordQueryStats(enable); + } + } else if(Array.isArray(this.servers)) { + for(var i = 0; i < this.servers.length; i++) { + this.servers[i].enableRecordQueryStats(enable); + } + } +} + +/** + * @ignore + */ +ReplSet.prototype.disconnect = function(callback) { + this.close(callback); +} + +/** + * @ignore + */ +ReplSet.prototype.close = function(callback) { + var self = this; + // Disconnect + this._serverState = 'disconnected'; + // Close all servers + if(this._state && this._state.addresses) { + var keys = Object.keys(this._state.addresses); + // Iterate over all server instances + for(var i = 0; i < keys.length; i++) { + this._state.addresses[keys[i]].close(); + } + } + + // If we have a strategy stop it + if(this.strategyInstance) this.strategyInstance.stop(); + + // If it's a callback + if(typeof callback == 'function') callback(null, null); +} + +/** + * Auto Reconnect property + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "autoReconnect", { enumerable: true + , get: function () { + return true; + } +}); + +/** + * Get Read Preference method + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "readPreference", { enumerable: true + , get: function () { + if(this._readPreference == null && this.readSecondary) { + return ReadPreference.SECONDARY_PREFERRED; + } else if(this._readPreference == null && !this.readSecondary) { + return ReadPreference.PRIMARY; + } else { + return this._readPreference; + } + } +}); + +/** + * Db Instances + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "dbInstances", {enumerable:true + , get: function() { + var servers = this.allServerInstances(); + return servers.length > 0 ? servers[0].dbInstances : []; + } +}) + +/** + * Just make compatible with server.js + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "host", { enumerable: true + , get: function () { + if (this.primary != null) return this.primary.host; + } +}); + +/** + * Just make compatible with server.js + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "port", { enumerable: true + , get: function () { + if (this.primary != null) return this.primary.port; + } +}); + +/** + * Get status of read + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "read", { enumerable: true + , get: function () { + return this.secondaries.length > 0 ? this.secondaries[0] : null; + } +}); + +/** + * Get list of secondaries + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "secondaries", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.secondaries); + var array = new Array(keys.length); + // Convert secondaries to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.secondaries[keys[i]]; + } + return array; + } +}); + +/** + * Get list of all secondaries including passives + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "allSecondaries", {enumerable: true + , get: function() { + return this.secondaries.concat(this.passives); + } +}); + +/** + * Get list of arbiters + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "arbiters", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.arbiters); + var array = new Array(keys.length); + // Convert arbiters to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.arbiters[keys[i]]; + } + return array; + } +}); + +/** + * Get list of passives + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "passives", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.passives); + var array = new Array(keys.length); + // Convert arbiters to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.passives[keys[i]]; + } + return array; + } +}); + +/** + * Master connection property + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "primary", { enumerable: true + , get: function () { + return this._state != null ? this._state.master : null; + } +}); + +/** + * @ignore + */ +// Backward compatibility +exports.ReplSetServers = ReplSet; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/server.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/server.js new file mode 100644 index 0000000..50635ec --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/server.js @@ -0,0 +1,860 @@ +var Connection = require('./connection').Connection, + ReadPreference = require('./read_preference').ReadPreference, + DbCommand = require('../commands/db_command').DbCommand, + MongoReply = require('../responses/mongo_reply').MongoReply, + ConnectionPool = require('./connection_pool').ConnectionPool, + EventEmitter = require('events').EventEmitter, + Base = require('./base').Base, + utils = require('../utils'), + inherits = require('util').inherits; + +/** + * Class representing a single MongoDB Server connection + * + * Options + * - **readPreference** {String, default:null}, set's the read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST) + * - **ssl** {Boolean, default:false}, use ssl connection (needs to have a mongod server with ssl support) + * - **slaveOk** {Boolean, default:false}, legacy option allowing reads from secondary, use **readPrefrence** instead. + * - **poolSize** {Number, default:5}, number of connections in the connection pool, set to 5 as default for legacy reasons. + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * - **auto_reconnect** {Boolean, default:false}, reconnect on error. + * - **disableDriverBSONSizeCheck** {Boolean, default:false}, force the server to error if the BSON message is to big + * + * @class Represents a Server connection. + * @param {String} host the server host + * @param {Number} port the server port + * @param {Object} [options] optional options for insert command + */ +function Server(host, port, options) { + // Set up Server instance + if(!(this instanceof Server)) return new Server(host, port, options); + + // Set up event emitter + Base.call(this); + + // Ensure correct values + if(port != null && typeof port == 'object') { + options = port; + port = Connection.DEFAULT_PORT; + } + + var self = this; + this.host = host; + this.port = port; + this.options = options == null ? {} : options; + this.internalConnection; + this.internalMaster = false; + this.connected = false; + this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize; + this.disableDriverBSONSizeCheck = this.options.disableDriverBSONSizeCheck != null ? this.options.disableDriverBSONSizeCheck : false; + this.ssl = this.options.ssl == null ? false : this.options.ssl; + this.slaveOk = this.options["slave_ok"] ? this.options["slave_ok"] : this.options["slaveOk"]; + this._used = false; + this.replicasetInstance = null; + + // Get the readPreference + var readPreference = this.options['readPreference']; + // If readPreference is an object get the mode string + var validateReadPreference = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference; + // Read preference setting + if(validateReadPreference != null) { + if(validateReadPreference != ReadPreference.PRIMARY && validateReadPreference != ReadPreference.SECONDARY && validateReadPreference != ReadPreference.NEAREST + && validateReadPreference != ReadPreference.SECONDARY_PREFERRED && validateReadPreference != ReadPreference.PRIMARY_PREFERRED) { + throw new Error("Illegal readPreference mode specified, " + validateReadPreference); + } + + // Set read Preference + this._readPreference = readPreference; + } else { + this._readPreference = null; + } + + // Contains the isMaster information returned from the server + this.isMasterDoc; + + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + if(this.disableDriverBSONSizeCheck) this.socketOptions.disableDriverBSONSizeCheck = this.disableDriverBSONSizeCheck; + // Set ssl up if it's defined + if(this.ssl) { + this.socketOptions.ssl = true; + } + + // Set up logger if any set + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.log == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]}; + // Internal state of server connection + this._serverState = 'disconnected'; + // this._timeout = false; + // Contains state information about server connection + this._state = {'runtimeStats': {'queryStats':new RunningStats()}}; + // Do we record server stats or not + this.recordQueryStats = false; +}; + +/** + * @ignore + */ +inherits(Server, Base); + +// +// Deprecated, USE ReadPreferences class +// +Server.READ_PRIMARY = ReadPreference.PRIMARY; +Server.READ_SECONDARY = ReadPreference.SECONDARY_PREFERRED; +Server.READ_SECONDARY_ONLY = ReadPreference.SECONDARY; + +/** + * Always ourselves + * @ignore + */ +Server.prototype.setReadPreference = function() {} + +/** + * @ignore + */ +Server.prototype.isMongos = function() { + return this.isMasterDoc != null && this.isMasterDoc['msg'] == "isdbgrid" ? true : false; +} + +/** + * @ignore + */ +Server.prototype._isUsed = function() { + return this._used; +} + +/** + * @ignore + */ +Server.prototype.close = function(callback) { + // Remove all local listeners + this.removeAllListeners(); + + if(this.connectionPool != null) { + // Remove all the listeners on the pool so it does not fire messages all over the place + this.connectionPool.removeAllEventListeners(); + // Close the connection if it's open + this.connectionPool.stop(true); + } + + // Set server status as disconnected + this._serverState = 'disconnected'; + // Peform callback if present + if(typeof callback === 'function') callback(null); +}; + +/** + * @ignore + */ +Server.prototype.isConnected = function() { + return this._serverState == 'connected'; +} + +/** + * @ignore + */ +Server.prototype.allServerInstances = function() { + return [this]; +} + +/** + * @ignore + */ +Server.prototype.isSetMember = function() { + return this.replicasetInstance != null || this.mongosInstance != null; +} + +/** + * Assigns a replica set to this `server`. + * + * @param {ReplSet} replset + * @ignore + */ +Server.prototype.assignReplicaSet = function (replset) { + this.replicasetInstance = replset; + this.inheritReplSetOptionsFrom(replset); + this.enableRecordQueryStats(replset.recordQueryStats); +} + +/** + * Takes needed options from `replset` and overwrites + * our own options. + * + * @param {ReplSet} replset + * @ignore + */ +Server.prototype.inheritReplSetOptionsFrom = function (replset) { + this.socketOptions = {}; + this.socketOptions.connectTimeoutMS = replset._connectTimeoutMS; + + if(replset.ssl) + this.socketOptions.ssl = true; + + // If a socket option object exists clone it + if(utils.isObject(replset.socketOptions)) { + var keys = Object.keys(replset.socketOptions); + for(var i = 0; i < keys.length; i++) + this.socketOptions[keys[i]] = replset.socketOptions[keys[i]]; + } +} + +/** + * Opens this server connection. + * + * @ignore + */ +Server.prototype.connect = function(dbInstance, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Currently needed to work around problems with multiple connections in a pool with ssl + // TODO fix if possible + if(this.ssl == true) { + // Set up socket options for ssl + this.socketOptions.ssl = true; + } + + // Let's connect + var server = this; + // Let's us override the main receiver of events + var eventReceiver = options.eventReceiver != null ? options.eventReceiver : this; + // Creating dbInstance + this.dbInstance = dbInstance; + // Save reference to dbInstance + this.dbInstances = [dbInstance]; + + // Force connection pool if there is one + if(server.connectionPool) server.connectionPool.stop(); + + // Set server state to connecting + this._serverState = 'connecting'; + // Ensure dbInstance can do a slave query if it's set + dbInstance.slaveOk = this.slaveOk ? this.slaveOk : dbInstance.slaveOk; + // Create connection Pool instance with the current BSON serializer + var connectionPool = new ConnectionPool(this.host, this.port, this.poolSize, dbInstance.bson, this.socketOptions); + // Set logger on pool + connectionPool.logger = this.logger; + + // Set up a new pool using default settings + server.connectionPool = connectionPool; + + // Set basic parameters passed in + var returnIsMasterResults = options.returnIsMasterResults == null ? false : options.returnIsMasterResults; + + // Create a default connect handler, overriden when using replicasets + var connectCallback = function(err, reply) { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // If something close down the connection and removed the callback before + // proxy killed connection etc, ignore the erorr as close event was isssued + if(err != null && internalCallback == null) return; + // Internal callback + if(err != null) return internalCallback(err, null); + server.master = reply.documents[0].ismaster == 1 ? true : false; + server.connectionPool.setMaxBsonSize(reply.documents[0].maxBsonObjectSize); + // Set server as connected + server.connected = true; + // Save document returned so we can query it + server.isMasterDoc = reply.documents[0]; + + // Emit open event + _emitAcrossAllDbInstances(server, eventReceiver, "open", null, returnIsMasterResults ? reply : dbInstance, null); + + // If we have it set to returnIsMasterResults + if(returnIsMasterResults) { + internalCallback(null, reply, server); + } else { + internalCallback(null, dbInstance, server); + } + }; + + // Let's us override the main connect callback + var connectHandler = options.connectHandler == null ? connectCallback : options.connectHandler; + + // Set up on connect method + connectionPool.on("poolReady", function() { + // Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks) + var db_command = DbCommand.NcreateIsMasterCommand(dbInstance, dbInstance.databaseName); + // Check out a reader from the pool + var connection = connectionPool.checkoutConnection(); + // Set server state to connEcted + server._serverState = 'connected'; + + // Register handler for messages + dbInstance._registerHandler(db_command, false, connection, connectHandler); + + // Write the command out + connection.write(db_command); + }) + + // Set up item connection + connectionPool.on("message", function(message) { + // Attempt to parse the message + try { + // Create a new mongo reply + var mongoReply = new MongoReply() + // Parse the header + mongoReply.parseHeader(message, connectionPool.bson) + // If message size is not the same as the buffer size + // something went terribly wrong somewhere + if(mongoReply.messageLength != message.length) { + // Emit the error + if(eventReceiver.listeners("error") && eventReceiver.listeners("error").length > 0) eventReceiver.emit("error", new Error("bson length is different from message length"), server); + // Remove all listeners + server.removeAllListeners(); + } else { + var startDate = new Date().getTime(); + + // Callback instance + var callbackInfo = null; + var dbInstanceObject = null; + + // Locate a callback instance and remove any additional ones + for(var i = 0; i < server.dbInstances.length; i++) { + var dbInstanceObjectTemp = server.dbInstances[i]; + var hasHandler = dbInstanceObjectTemp._hasHandler(mongoReply.responseTo.toString()); + // Assign the first one we find and remove any duplicate ones + if(hasHandler && callbackInfo == null) { + callbackInfo = dbInstanceObjectTemp._findHandler(mongoReply.responseTo.toString()); + dbInstanceObject = dbInstanceObjectTemp; + } else if(hasHandler) { + dbInstanceObjectTemp._removeHandler(mongoReply.responseTo.toString()); + } + } + + // The command executed another request, log the handler again under that request id + if(mongoReply.requestId > 0 && mongoReply.cursorId.toString() != "0" + && callbackInfo && callbackInfo.info && callbackInfo.info.exhaust) { + dbInstance._reRegisterHandler(mongoReply.requestId, callbackInfo); + } + + // Only execute callback if we have a caller + // chained is for findAndModify as it does not respect write concerns + if(callbackInfo && callbackInfo.callback && callbackInfo.info && Array.isArray(callbackInfo.info.chained)) { + // Check if callback has already been fired (missing chain command) + var chained = callbackInfo.info.chained; + var numberOfFoundCallbacks = 0; + for(var i = 0; i < chained.length; i++) { + if(dbInstanceObject._hasHandler(chained[i])) numberOfFoundCallbacks++; + } + + // If we have already fired then clean up rest of chain and move on + if(numberOfFoundCallbacks != chained.length) { + for(var i = 0; i < chained.length; i++) { + dbInstanceObject._removeHandler(chained[i]); + } + + // Just return from function + return; + } + + // Parse the body + mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { + // console.log("+++++++++++++++++++++++++++++++++++++++ recieved message") + // console.dir(message) + if(err != null) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // Remove all listeners and close the connection pool + server.removeAllListeners(); + connectionPool.stop(true); + + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + // Short cut + return; + } + + // Fetch the callback + var callbackInfo = dbInstanceObject._findHandler(mongoReply.responseTo.toString()); + // If we have an error let's execute the callback and clean up all other + // chained commands + var firstResult = mongoReply && mongoReply.documents; + + // Check for an error, if we have one let's trigger the callback and clean up + // The chained callbacks + if(firstResult[0].err != null || firstResult[0].errmsg != null) { + // Trigger the callback for the error + dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null); + } else { + var chainedIds = callbackInfo.info.chained; + + if(chainedIds.length > 0 && chainedIds[chainedIds.length - 1] == mongoReply.responseTo) { + // Cleanup all other chained calls + chainedIds.pop(); + // Remove listeners + for(var i = 0; i < chainedIds.length; i++) dbInstanceObject._removeHandler(chainedIds[i]); + // Call the handler + dbInstanceObject._callHandler(mongoReply.responseTo, callbackInfo.info.results.shift(), null); + } else{ + // Add the results to all the results + for(var i = 0; i < chainedIds.length; i++) { + var handler = dbInstanceObject._findHandler(chainedIds[i]); + // Check if we have an object, if it's the case take the current object commands and + // and add this one + if(handler.info != null) { + handler.info.results = Array.isArray(callbackInfo.info.results) ? callbackInfo.info.results : []; + handler.info.results.push(mongoReply); + } + } + } + } + }); + } else if(callbackInfo && callbackInfo.callback && callbackInfo.info) { + // Parse the body + mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { + // console.log("+++++++++++++++++++++++++++++++++++++++ recieved message") + // console.dir(message) + if(err != null) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // Remove all listeners and close the connection pool + server.removeAllListeners(); + connectionPool.stop(true); + + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + // Short cut + return; + } + + // Let's record the stats info if it's enabled + if(server.recordQueryStats == true && server._state['runtimeStats'] != null + && server._state.runtimeStats['queryStats'] instanceof RunningStats) { + // Add data point to the running statistics object + server._state.runtimeStats.queryStats.push(new Date().getTime() - callbackInfo.info.start); + } + + dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null); + }); + } + } + } catch (err) { + // Throw error in next tick + process.nextTick(function() { + throw err; + }) + } + }); + + // Handle timeout + connectionPool.on("timeout", function(err) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(err, null, server); + } else if(server.isSetMember()) { + if(server.listeners("timeout") && server.listeners("timeout").length > 0) server.emit("timeout", err, server); + } else { + if(eventReceiver.listeners("timeout") && eventReceiver.listeners("timeout").length > 0) eventReceiver.emit("timeout", err, server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(err); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "timeout", err, server, true); + } + }); + + // Handle errors + connectionPool.on("error", function(message) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error(message && message.err ? message.err : message), null, server); + } else if(server.isSetMember()) { + if(server.listeners("error") && server.listeners("error").length > 0) server.emit("error", new Error(message && message.err ? message.err : message), server); + } else { + if(eventReceiver.listeners("error") && eventReceiver.listeners("error").length > 0) eventReceiver.emit("error", new Error(message && message.err ? message.err : message), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error(message && message.err ? message.err : message)); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "error", new Error(message && message.err ? message.err : message), server, true); + } + }); + + // Handle close events + connectionPool.on("close", function() { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback == 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("close") && server.listeners("close").length > 0) server.emit("close", new Error("connection closed"), server); + } else { + if(eventReceiver.listeners("close") && eventReceiver.listeners("close").length > 0) eventReceiver.emit("close", new Error("connection closed"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "close", server, null, true); + } + }); + + // If we have a parser error we are in an unknown state, close everything and emit + // error + connectionPool.on("parseError", function(message) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + }); + + // Boot up connection poole, pass in a locator of callbacks + connectionPool.start(); +} + +/** + * @ignore + */ +var _emitAcrossAllDbInstances = function(server, filterDb, event, message, object, resetConnection) { + // Emit close event across all db instances sharing the sockets + var allServerInstances = server.allServerInstances(); + // Fetch the first server instance + var serverInstance = allServerInstances[0]; + // For all db instances signal all db instances + if(Array.isArray(serverInstance.dbInstances) && serverInstance.dbInstances.length >= 1) { + for(var i = 0; i < serverInstance.dbInstances.length; i++) { + var dbInstance = serverInstance.dbInstances[i]; + // Set the parent + if(resetConnection && typeof dbInstance.openCalled != 'undefined') + dbInstance.openCalled = false; + // Check if it's our current db instance and skip if it is + if(filterDb == null || filterDb.databaseName !== dbInstance.databaseName || filterDb.tag !== dbInstance.tag) { + // Only emit if there is a listener + if(dbInstance.listeners(event).length > 0) + dbInstance.emit(event, message, object); + } + } + } +} + +/** + * @ignore + */ +Server.prototype.allRawConnections = function() { + return this.connectionPool.getAllConnections(); +} + +/** + * Check if a writer can be provided + * @ignore + */ +var canCheckoutWriter = function(self, read) { + // We cannot write to an arbiter or secondary server + if(self.isMasterDoc['arbiterOnly'] == true) { + return new Error("Cannot write to an arbiter"); + } if(self.isMasterDoc['secondary'] == true) { + return new Error("Cannot write to a secondary"); + } else if(read == true && self._readPreference == ReadPreference.SECONDARY && self.isMasterDoc['ismaster'] == true) { + return new Error("Cannot read from primary when secondary only specified"); + } + + // Return no error + return null; +} + +/** + * @ignore + */ +Server.prototype.checkoutWriter = function(read) { + // console.log("===================== checkoutWriter :: " + read) + // console.dir(this.isMasterDoc) + if(read == true) return this.connectionPool.checkoutConnection(); + // Check if are allowed to do a checkout (if we try to use an arbiter f.ex) + var result = canCheckoutWriter(this, read); + // If the result is null check out a writer + if(result == null && this.connectionPool != null) { + return this.connectionPool.checkoutConnection(); + } else if(result == null) { + return null; + } else { + return result; + } +} + +/** + * Check if a reader can be provided + * @ignore + */ +var canCheckoutReader = function(self) { + // We cannot write to an arbiter or secondary server + if(self.isMasterDoc && self.isMasterDoc['arbiterOnly'] == true) { + return new Error("Cannot write to an arbiter"); + } else if(self._readPreference != null) { + // If the read preference is Primary and the instance is not a master return an error + if((self._readPreference == ReadPreference.PRIMARY) && self.isMasterDoc['ismaster'] != true) { + return new Error("Read preference is Server.PRIMARY and server is not master"); + } else if(self._readPreference == ReadPreference.SECONDARY && self.isMasterDoc['ismaster'] == true) { + return new Error("Cannot read from primary when secondary only specified"); + } + } + + // Return no error + return null; +} + +/** + * @ignore + */ +Server.prototype.checkoutReader = function() { + // console.log("===================== checkoutReader") + // Check if are allowed to do a checkout (if we try to use an arbiter f.ex) + var result = canCheckoutReader(this); + // If the result is null check out a writer + if(result == null && this.connectionPool != null) { + return this.connectionPool.checkoutConnection(); + } else if(result == null) { + return null; + } else { + return result; + } +} + +/** + * @ignore + */ +Server.prototype.enableRecordQueryStats = function(enable) { + this.recordQueryStats = enable; +} + +/** + * Internal statistics object used for calculating average and standard devitation on + * running queries + * @ignore + */ +var RunningStats = function() { + var self = this; + this.m_n = 0; + this.m_oldM = 0.0; + this.m_oldS = 0.0; + this.m_newM = 0.0; + this.m_newS = 0.0; + + // Define getters + Object.defineProperty(this, "numDataValues", { enumerable: true + , get: function () { return this.m_n; } + }); + + Object.defineProperty(this, "mean", { enumerable: true + , get: function () { return (this.m_n > 0) ? this.m_newM : 0.0; } + }); + + Object.defineProperty(this, "variance", { enumerable: true + , get: function () { return ((this.m_n > 1) ? this.m_newS/(this.m_n - 1) : 0.0); } + }); + + Object.defineProperty(this, "standardDeviation", { enumerable: true + , get: function () { return Math.sqrt(this.variance); } + }); + + Object.defineProperty(this, "sScore", { enumerable: true + , get: function () { + var bottom = this.mean + this.standardDeviation; + if(bottom == 0) return 0; + return ((2 * this.mean * this.standardDeviation)/(bottom)); + } + }); +} + +/** + * @ignore + */ +RunningStats.prototype.push = function(x) { + // Update the number of samples + this.m_n = this.m_n + 1; + // See Knuth TAOCP vol 2, 3rd edition, page 232 + if(this.m_n == 1) { + this.m_oldM = this.m_newM = x; + this.m_oldS = 0.0; + } else { + this.m_newM = this.m_oldM + (x - this.m_oldM) / this.m_n; + this.m_newS = this.m_oldS + (x - this.m_oldM) * (x - this.m_newM); + + // set up for next iteration + this.m_oldM = this.m_newM; + this.m_oldS = this.m_newS; + } +} + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "autoReconnect", { enumerable: true + , get: function () { + return this.options['auto_reconnect'] == null ? false : this.options['auto_reconnect']; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "connection", { enumerable: true + , get: function () { + return this.internalConnection; + } + , set: function(connection) { + this.internalConnection = connection; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "master", { enumerable: true + , get: function () { + return this.internalMaster; + } + , set: function(value) { + this.internalMaster = value; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "primary", { enumerable: true + , get: function () { + return this; + } +}); + +/** + * Getter for query Stats + * @ignore + */ +Object.defineProperty(Server.prototype, "queryStats", { enumerable: true + , get: function () { + return this._state.runtimeStats.queryStats; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "runtimeStats", { enumerable: true + , get: function () { + return this._state.runtimeStats; + } +}); + +/** + * Get Read Preference method + * @ignore + */ +Object.defineProperty(Server.prototype, "readPreference", { enumerable: true + , get: function () { + if(this._readPreference == null && this.readSecondary) { + return Server.READ_SECONDARY; + } else if(this._readPreference == null && !this.readSecondary) { + return Server.READ_PRIMARY; + } else { + return this._readPreference; + } + } +}); + +/** + * @ignore + */ +exports.Server = Server; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js new file mode 100644 index 0000000..4fefd78 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js @@ -0,0 +1,188 @@ +var Server = require("../server").Server; + +// The ping strategy uses pings each server and records the +// elapsed time for the server so it can pick a server based on lowest +// return time for the db command {ping:true} +var PingStrategy = exports.PingStrategy = function(replicaset, secondaryAcceptableLatencyMS) { + this.replicaset = replicaset; + this.secondaryAcceptableLatencyMS = secondaryAcceptableLatencyMS; + this.state = 'disconnected'; + this.pingInterval = 5000; + // Class instance + this.Db = require("../../db").Db; +} + +// Starts any needed code +PingStrategy.prototype.start = function(callback) { + // already running? + if ('connected' == this.state) return; + + this.state = 'connected'; + + // Start ping server + this._pingServer(callback); +} + +// Stops and kills any processes running +PingStrategy.prototype.stop = function(callback) { + // Stop the ping process + this.state = 'disconnected'; + + // optional callback + callback && callback(null, null); +} + +PingStrategy.prototype.checkoutSecondary = function(tags, secondaryCandidates) { + // Servers are picked based on the lowest ping time and then servers that lower than that + secondaryAcceptableLatencyMS + // Create a list of candidat servers, containing the primary if available + var candidateServers = []; + + // If we have not provided a list of candidate servers use the default setup + if(!Array.isArray(secondaryCandidates)) { + candidateServers = this.replicaset._state.master != null ? [this.replicaset._state.master] : []; + // Add all the secondaries + var keys = Object.keys(this.replicaset._state.secondaries); + for(var i = 0; i < keys.length; i++) { + candidateServers.push(this.replicaset._state.secondaries[keys[i]]) + } + } else { + candidateServers = secondaryCandidates; + } + + // Final list of eligable server + var finalCandidates = []; + + // If we have tags filter by tags + if(tags != null && typeof tags == 'object') { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Remove any that are not tagged correctly + for(var i = 0; i < candidateServers.length; i++) { + var server = candidateServers[i]; + // If we have tags match + if(server.tags != null) { + var matching = true; + + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + finalCandidates.push(server); + } + } + } + } + } else { + // Final array candidates + var finalCandidates = candidateServers; + } + + // Sort by ping time + finalCandidates.sort(function(a, b) { + return a.runtimeStats['pingMs'] > b.runtimeStats['pingMs']; + }); + + if(0 === finalCandidates.length) + return new Error("No replica set members available for query"); + + // handle undefined pingMs + var lowestPing = finalCandidates[0].runtimeStats['pingMs'] | 0; + + // determine acceptable latency + var acceptable = lowestPing + this.secondaryAcceptableLatencyMS; + + // remove any server responding slower than acceptable + var len = finalCandidates.length; + while(len--) { + if(finalCandidates[len].runtimeStats['pingMs'] > acceptable) { + finalCandidates.splice(len, 1); + } + } + + // If no candidates available return an error + if(finalCandidates.length == 0) + return new Error("No replica set members available for query"); + + // Pick a random acceptable server + return finalCandidates[Math.round(Math.random(1000000) * (finalCandidates.length - 1))].checkoutReader(); +} + +PingStrategy.prototype._pingServer = function(callback) { + var self = this; + + // Ping server function + var pingFunction = function() { + if(self.state == 'disconnected') return; + var addresses = self.replicaset._state.addresses; + + // Grab all servers + var serverKeys = Object.keys(addresses); + + // Number of server entries + var numberOfEntries = serverKeys.length; + + // We got keys + for(var i = 0; i < serverKeys.length; i++) { + + // We got a server instance + var server = addresses[serverKeys[i]]; + + // Create a new server object, avoid using internal connections as they might + // be in an illegal state + new function(serverInstance) { + var options = { poolSize: 1, timeout: 500, auto_reconnect: false }; + var server = new Server(serverInstance.host, serverInstance.port, options); + var db = new self.Db(self.replicaset.db.databaseName, server, { safe: true }); + + db.on("error", done); + + // Open the db instance + db.open(function(err, _db) { + if(err) return done(err, _db); + + // Startup time of the command + var startTime = Date.now(); + + // Execute ping on this connection + db.executeDbCommand({ping:1}, {failFast:true}, function() { + if(null != serverInstance.runtimeStats && serverInstance.isConnected()) { + serverInstance.runtimeStats['pingMs'] = Date.now() - startTime; + } + + done(null, _db); + }) + }) + + function done (err, _db) { + // Close connection + if (_db) _db.close(true); + + // Adjust the number of checks + numberOfEntries--; + + // If we are done with all results coming back trigger ping again + if(0 === numberOfEntries && 'connected' == self.state) { + setTimeout(pingFunction, self.pingInterval); + } + } + }(server); + } + } + + // Start pingFunction + setTimeout(pingFunction, 1000); + + callback && callback(null); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js new file mode 100644 index 0000000..2e87dbd --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js @@ -0,0 +1,78 @@ +// The Statistics strategy uses the measure of each end-start time for each +// query executed against the db to calculate the mean, variance and standard deviation +// and pick the server which the lowest mean and deviation +var StatisticsStrategy = exports.StatisticsStrategy = function(replicaset) { + this.replicaset = replicaset; +} + +// Starts any needed code +StatisticsStrategy.prototype.start = function(callback) { + callback && callback(null, null); +} + +StatisticsStrategy.prototype.stop = function(callback) { + callback && callback(null, null); +} + +StatisticsStrategy.prototype.checkoutSecondary = function(tags, secondaryCandidates) { + // Servers are picked based on the lowest ping time and then servers that lower than that + secondaryAcceptableLatencyMS + // Create a list of candidat servers, containing the primary if available + var candidateServers = []; + + // If we have not provided a list of candidate servers use the default setup + if(!Array.isArray(secondaryCandidates)) { + candidateServers = this.replicaset._state.master != null ? [this.replicaset._state.master] : []; + // Add all the secondaries + var keys = Object.keys(this.replicaset._state.secondaries); + for(var i = 0; i < keys.length; i++) { + candidateServers.push(this.replicaset._state.secondaries[keys[i]]) + } + } else { + candidateServers = secondaryCandidates; + } + + // Final list of eligable server + var finalCandidates = []; + + // If we have tags filter by tags + if(tags != null && typeof tags == 'object') { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Remove any that are not tagged correctly + for(var i = 0; i < candidateServers.length; i++) { + var server = candidateServers[i]; + // If we have tags match + if(server.tags != null) { + var matching = true; + + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + finalCandidates.push(server); + } + } + } + } + } else { + // Final array candidates + var finalCandidates = candidateServers; + } + + // If no candidates available return an error + if(finalCandidates.length == 0) return new Error("No replica set members available for query"); + // Pick a random server + return finalCandidates[Math.round(Math.random(1000000) * (finalCandidates.length - 1))].checkoutReader(); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/url_parser.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/url_parser.js new file mode 100644 index 0000000..9c280b7 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/connection/url_parser.js @@ -0,0 +1,223 @@ +var fs = require('fs'), + ReadPreference = require('./read_preference').ReadPreference; + +exports.parse = function(url, options) { + // Ensure we have a default options object if none set + options = options || {}; + // Variables + var connection_part = ''; + var auth_part = ''; + var query_string_part = ''; + var dbName = 'default'; + + // Must start with mongodb + if(url.indexOf("mongodb://") != 0) + throw Error("URL must be in the format mongodb://user:pass@host:port/dbname"); + // If we have a ? mark cut the query elements off + if(url.indexOf("?") != -1) { + query_string_part = url.substr(url.indexOf("?") + 1); + connection_part = url.substring("mongodb://".length, url.indexOf("?")) + } else { + connection_part = url.substring("mongodb://".length); + } + + // Check if we have auth params + if(connection_part.indexOf("@") != -1) { + auth_part = connection_part.split("@")[0]; + connection_part = connection_part.split("@")[1]; + } + + // Check if the connection string has a db + if(connection_part.indexOf(".sock") != -1) { + if(connection_part.indexOf(".sock/") != -1) { + dbName = connection_part.split(".sock/")[1]; + connection_part = connection_part.split("/", connection_part.indexOf(".sock") + ".sock".length); + } + } else if(connection_part.indexOf("/") != -1) { + dbName = connection_part.split("/")[1]; + connection_part = connection_part.split("/")[0]; + } + + // Result object + var object = {}; + + // Pick apart the authentication part of the string + var authPart = auth_part || ''; + var auth = authPart.split(':', 2); + if(options['uri_decode_auth']){ + auth[0] = decodeURIComponent(auth[0]); + if(auth[1]){ + auth[1] = decodeURIComponent(auth[1]); + } + } + + // Add auth to final object if we have 2 elements + if(auth.length == 2) object.auth = {user: auth[0], password: auth[1]}; + + // Variables used for temporary storage + var hostPart; + var urlOptions; + var servers; + var serverOptions = {socketOptions: {}}; + var dbOptions = {read_preference_tags: []}; + var replSetServersOptions = {socketOptions: {}}; + // Add server options to final object + object.server_options = serverOptions; + object.db_options = dbOptions; + object.rs_options = replSetServersOptions; + object.mongos_options = {}; + + // Let's check if we are using a domain socket + if(url.match(/\.sock/)) { + // Split out the socket part + var domainSocket = url.substring( + url.indexOf("mongodb://") + "mongodb://".length + , url.lastIndexOf(".sock") + ".sock".length); + // Clean out any auth stuff if any + if(domainSocket.indexOf("@") != -1) domainSocket = domainSocket.split("@")[1]; + servers = [{domain_socket: domainSocket}]; + } else { + // Split up the db + hostPart = connection_part; + // Parse all server results + servers = hostPart.split(',').map(function(h) { + var hostPort = h.split(':', 2); + var _host = hostPort[0] || 'localhost'; + var _port = hostPort[1] != null ? parseInt(hostPort[1], 10) : 27017; + // Check for localhost?safe=true style case + if(_host.indexOf("?") != -1) _host = _host.split(/\?/)[0]; + + // Return the mapped object + return {host: _host, port: _port}; + }); + } + + // Get the db name + object.dbName = dbName || 'default'; + // Split up all the options + urlOptions = (query_string_part || '').split(/[&;]/); + // Ugh, we have to figure out which options go to which constructor manually. + urlOptions.forEach(function(opt) { + if(!opt) return; + var splitOpt = opt.split('='), name = splitOpt[0], value = splitOpt[1]; + // Options implementations + switch(name) { + case 'slaveOk': + case 'slave_ok': + serverOptions.slave_ok = (value == 'true'); + break; + case 'maxPoolSize': + case 'poolSize': + serverOptions.poolSize = parseInt(value, 10); + replSetServersOptions.poolSize = parseInt(value, 10); + break; + case 'autoReconnect': + case 'auto_reconnect': + serverOptions.auto_reconnect = (value == 'true'); + break; + case 'minPoolSize': + throw new Error("minPoolSize not supported"); + case 'maxIdleTimeMS': + throw new Error("maxIdleTimeMS not supported"); + case 'waitQueueMultiple': + throw new Error("waitQueueMultiple not supported"); + case 'waitQueueTimeoutMS': + throw new Error("waitQueueTimeoutMS not supported"); + case 'uuidRepresentation': + throw new Error("uuidRepresentation not supported"); + case 'ssl': + if(value == 'prefer') { + serverOptions.socketOptions.ssl = value; + replSetServersOptions.socketOptions.ssl = value; + break; + } + serverOptions.socketOptions.ssl = (value == 'true'); + replSetServersOptions.socketOptions.ssl = (value == 'true'); + break; + case 'replicaSet': + case 'rs_name': + replSetServersOptions.rs_name = value; + break; + case 'reconnectWait': + replSetServersOptions.reconnectWait = parseInt(value, 10); + break; + case 'retries': + replSetServersOptions.retries = parseInt(value, 10); + break; + case 'readSecondary': + case 'read_secondary': + replSetServersOptions.read_secondary = (value == 'true'); + break; + case 'fsync': + dbOptions.fsync = (value == 'true'); + break; + case 'journal': + dbOptions.journal = (value == 'true'); + break; + case 'safe': + dbOptions.safe = (value == 'true'); + break; + case 'nativeParser': + case 'native_parser': + dbOptions.native_parser = (value == 'true'); + break; + case 'connectTimeoutMS': + serverOptions.socketOptions.connectTimeoutMS = parseInt(value, 10); + replSetServersOptions.socketOptions.connectTimeoutMS = parseInt(value, 10); + break; + case 'socketTimeoutMS': + serverOptions.socketOptions.socketTimeoutMS = parseInt(value, 10); + replSetServersOptions.socketOptions.socketTimeoutMS = parseInt(value, 10); + break; + case 'w': + dbOptions.w = parseInt(value, 10); + break; + case 'wtimeoutMS': + dbOptions.wtimeoutMS = parseInt(value, 10); + break; + case 'readPreference': + if(!ReadPreference.isValid(value)) throw new Error("readPreference must be either primary/primaryPreferred/secondary/secondaryPreferred/nearest"); + dbOptions.read_preference = value; + break; + case 'readPreferenceTags': + // Contains the tag object + var tagObject = {}; + if(value == null || value == '') { + dbOptions.read_preference_tags.push(tagObject); + break; + } + + // Split up the tags + var tags = value.split(/\,/); + for(var i = 0; i < tags.length; i++) { + var parts = tags[i].trim().split(/\:/); + tagObject[parts[0]] = parts[1]; + } + + // Set the preferences tags + dbOptions.read_preference_tags.push(tagObject); + break; + default: + break; + } + }); + + // No tags: should be null (not []) + if(dbOptions.read_preference_tags.length === 0) { + dbOptions.read_preference_tags = null; + } + + // Validate if there are an invalid write concern combinations + if((dbOptions.w == -1 || dbOptions.w == 0) && ( + dbOptions.journal == true + || dbOptions.fsync == true + || dbOptions.safe == true)) throw new Error("w set to -1 or 0 cannot be combined with safe/w/journal/fsync") + + // If no read preference set it to primary + if(!dbOptions.read_preference) dbOptions.read_preference = 'primary'; + + // Add servers to result + object.servers = servers; + // Returned parsed object + return object; +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursor.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursor.js new file mode 100644 index 0000000..f66be71 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursor.js @@ -0,0 +1,902 @@ +var QueryCommand = require('./commands/query_command').QueryCommand, + GetMoreCommand = require('./commands/get_more_command').GetMoreCommand, + KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand, + Long = require('bson').Long, + ReadPreference = require('./connection/read_preference').ReadPreference, + CursorStream = require('./cursorstream'), + utils = require('./utils'); + +/** + * Constructor for a cursor object that handles all the operations on query result + * using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly, + * but use find to acquire a cursor. + * + * Options + * - **skip** {Number} skip number of documents to skip. + * - **limit** {Number}, limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **hint** {Object}, hint force the query to use a specific index. + * - **explain** {Boolean}, explain return the explaination of the query. + * - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned. + * - **timeout** {Boolean}, timeout allow the query to timeout. + * - **tailable** {Boolean}, tailable allow the cursor to be tailable. + * - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor. + * - **batchSize** {Number}, batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database. + * - **raw** {Boolean}, raw return all query documents as raw buffers (default false). + * - **read** {Boolean}, read specify override of read from source (primary/secondary). + * - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries. + * - **returnKey** {Boolean}, returnKey only return the index key. + * - **maxScan** {Number}, maxScan limit the number of items to scan. + * - **min** {Number}, min set index bounds. + * - **max** {Number}, max set index bounds. + * - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results. + * - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler. + * - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout. + * - **dbName** {String}, dbName override the default dbName. + * - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor. + * - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets. + * - **partial** {Boolean}, partial have the sharded system return a partial result from mongos. + * + * @class Represents a Cursor. + * @param {Db} db the database object to work with. + * @param {Collection} collection the collection to query. + * @param {Object} selector the query selector. + * @param {Object} fields an object containing what fields to include or exclude from objects returned. + * @param {Object} [options] additional options for the collection. +*/ +function Cursor(db, collection, selector, fields, options) { + this.db = db; + this.collection = collection; + this.selector = selector; + this.fields = fields; + options = !options ? {} : options; + + this.skipValue = options.skip == null ? 0 : options.skip; + this.limitValue = options.limit == null ? 0 : options.limit; + this.sortValue = options.sort; + this.hint = options.hint; + this.explainValue = options.explain; + this.snapshot = options.snapshot; + this.timeout = options.timeout == null ? true : options.timeout; + this.tailable = options.tailable; + this.awaitdata = options.awaitdata; + this.numberOfRetries = options.numberOfRetries == null ? 5 : options.numberOfRetries; + this.currentNumberOfRetries = this.numberOfRetries; + this.batchSizeValue = options.batchSize == null ? 0 : options.batchSize; + this.slaveOk = options.slaveOk == null ? collection.slaveOk : options.slaveOk; + this.raw = options.raw == null ? false : options.raw; + this.read = options.read == null ? ReadPreference.PRIMARY : options.read; + this.returnKey = options.returnKey; + this.maxScan = options.maxScan; + this.min = options.min; + this.max = options.max; + this.showDiskLoc = options.showDiskLoc; + this.comment = options.comment; + this.tailableRetryInterval = options.tailableRetryInterval || 100; + this.exhaust = options.exhaust || false; + this.partial = options.partial || false; + + this.totalNumberOfRecords = 0; + this.items = []; + this.cursorId = Long.fromInt(0); + + // This name + this.dbName = options.dbName; + + // State variables for the cursor + this.state = Cursor.INIT; + // Keep track of the current query run + this.queryRun = false; + this.getMoreTimer = false; + + // If we are using a specific db execute against it + if(this.dbName != null) { + this.collectionName = this.dbName + "." + this.collection.collectionName; + } else { + this.collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collection.collectionName; + } +}; + +/** + * Resets this cursor to its initial state. All settings like the query string, + * tailable, batchSizeValue, skipValue and limits are preserved. + * + * @return {Cursor} returns itself with rewind applied. + * @api public + */ +Cursor.prototype.rewind = function() { + var self = this; + + if (self.state != Cursor.INIT) { + if (self.state != Cursor.CLOSED) { + self.close(function() {}); + } + + self.numberOfReturned = 0; + self.totalNumberOfRecords = 0; + self.items = []; + self.cursorId = Long.fromInt(0); + self.state = Cursor.INIT; + self.queryRun = false; + } + + return self; +}; + + +/** + * Returns an array of documents. The caller is responsible for making sure that there + * is enough memory to store the results. Note that the array only contain partial + * results when this cursor had been previouly accessed. In that case, + * cursor.rewind() can be used to reset the cursor. + * + * @param {Function} callback This will be called after executing this method successfully. The first parameter will contain the Error object if an error occured, or null otherwise. The second parameter will contain an array of BSON deserialized objects as a result of the query. + * @return {null} + * @api public + */ +Cursor.prototype.toArray = function(callback) { + var self = this; + + if(!callback) { + throw new Error('callback is mandatory'); + } + + if(this.tailable) { + callback(new Error("Tailable cursor cannot be converted to array"), null); + } else if(this.state != Cursor.CLOSED) { + var items = []; + + this.each(function(err, item) { + if(err != null) return callback(err, null); + + if(item != null && Array.isArray(items)) { + items.push(item); + } else { + var resultItems = items; + items = null; + self.items = []; + // Returns items + callback(err, resultItems); + } + }); + } else { + callback(new Error("Cursor is closed"), null); + } +}; + +/** + * Iterates over all the documents for this cursor. As with **{cursor.toArray}**, + * not all of the elements will be iterated if this cursor had been previouly accessed. + * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike + * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements + * at any given time if batch size is specified. Otherwise, the caller is responsible + * for making sure that the entire result can fit the memory. + * + * @param {Function} callback this will be called for while iterating every document of the query result. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the document. + * @return {null} + * @api public + */ +Cursor.prototype.each = function(callback) { + var self = this; + + if (!callback) { + throw new Error('callback is mandatory'); + } + + if(this.state != Cursor.CLOSED) { + //FIX: stack overflow (on deep callback) (cred: https://github.com/limp/node-mongodb-native/commit/27da7e4b2af02035847f262b29837a94bbbf6ce2) + process.nextTick(function(){ + var s = new Date() + // Fetch the next object until there is no more objects + self.nextObject(function(err, item) { + if(err != null) return callback(err, null); + if(item != null) { + callback(null, item); + self.each(callback); + } else { + // Close the cursor if done + self.state = Cursor.CLOSED; + callback(err, null); + } + }); + }); + } else { + callback(new Error("Cursor is closed"), null); + } +}; + +/** + * Determines how many result the query for this cursor will return + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the number of results or null if an error occured. + * @return {null} + * @api public + */ +Cursor.prototype.count = function(callback) { + this.collection.count(this.selector, callback); +}; + +/** + * Sets the sort parameter of this cursor to the given value. + * + * This method has the following method signatures: + * (keyOrList, callback) + * (keyOrList, direction, callback) + * + * @param {String|Array|Object} keyOrList This can be a string or an array. If passed as a string, the string will be the field to sort. If passed an array, each element will represent a field to be sorted and should be an array that contains the format [string, direction]. + * @param {String|Number} direction this determines how the results are sorted. "asc", "ascending" or 1 for asceding order while "desc", "desceding or -1 for descending order. Note that the strings are case insensitive. + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.sort = function(keyOrList, direction, callback) { + callback = callback || function(){}; + if(typeof direction === "function") { callback = direction; direction = null; } + + if(this.tailable) { + callback(new Error("Tailable cursor doesn't support sorting"), null); + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + callback(new Error("Cursor is closed"), null); + } else { + var order = keyOrList; + + if(direction != null) { + order = [[keyOrList, direction]]; + } + + this.sortValue = order; + callback(null, this); + } + return this; +}; + +/** + * Sets the limit parameter of this cursor to the given value. + * + * @param {Number} limit the new limit. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the limit given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.limit = function(limit, callback) { + if(this.tailable) { + if(callback) { + callback(new Error("Tailable cursor doesn't support limit"), null); + } else { + throw new Error("Tailable cursor doesn't support limit"); + } + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + if(callback) { + callback(new Error("Cursor is closed"), null); + } else { + throw new Error("Cursor is closed"); + } + } else { + if(limit != null && limit.constructor != Number) { + if(callback) { + callback(new Error("limit requires an integer"), null); + } else { + throw new Error("limit requires an integer"); + } + } else { + this.limitValue = limit; + if(callback) return callback(null, this); + } + } + + return this; +}; + +/** + * Sets the read preference for the cursor + * + * @param {String} the read preference for the cursor, one of Server.READ_PRIMARY, Server.READ_SECONDARY, Server.READ_SECONDARY_ONLY + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the read preference given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.setReadPreference = function(readPreference, tags, callback) { + if(typeof tags == 'function') callback = tags; + + var _mode = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference; + + if(this.queryRun == true || this.state == Cursor.CLOSED) { + if(callback == null) throw new Error("Cannot change read preference on executed query or closed cursor"); + callback(new Error("Cannot change read preference on executed query or closed cursor")); + } else if(_mode != null && _mode != 'primary' + && _mode != 'secondaryOnly' && _mode != 'secondary' + && _mode != 'nearest' && _mode != 'primaryPreferred' && _mode != 'secondaryPreferred') { + if(callback == null) throw new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported"); + callback(new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported")); + } else { + this.read = readPreference; + if(callback != null) callback(null, this); + } + + return this; +} + +/** + * Sets the skip parameter of this cursor to the given value. + * + * @param {Number} skip the new skip value. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the skip value given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.skip = function(skip, callback) { + callback = callback || function(){}; + + if(this.tailable) { + callback(new Error("Tailable cursor doesn't support skip"), null); + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + callback(new Error("Cursor is closed"), null); + } else { + if(skip != null && skip.constructor != Number) { + callback(new Error("skip requires an integer"), null); + } else { + this.skipValue = skip; + callback(null, this); + } + } + + return this; +}; + +/** + * Sets the batch size parameter of this cursor to the given value. + * + * @param {Number} batchSize the new batch size. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the batchSize given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.batchSize = function(batchSize, callback) { + if(this.state == Cursor.CLOSED) { + if(callback != null) { + return callback(new Error("Cursor is closed"), null); + } else { + throw new Error("Cursor is closed"); + } + } else if(batchSize != null && batchSize.constructor != Number) { + if(callback != null) { + return callback(new Error("batchSize requires an integer"), null); + } else { + throw new Error("batchSize requires an integer"); + } + } else { + this.batchSizeValue = batchSize; + if(callback != null) return callback(null, this); + } + + return this; +}; + +/** + * The limit used for the getMore command + * + * @return {Number} The number of records to request per batch. + * @ignore + * @api private + */ +var limitRequest = function(self) { + var requestedLimit = self.limitValue; + var absLimitValue = Math.abs(self.limitValue); + var absBatchValue = Math.abs(self.batchSizeValue); + + if(absLimitValue > 0) { + if (absBatchValue > 0) { + requestedLimit = Math.min(absLimitValue, absBatchValue); + } + } else { + requestedLimit = self.batchSizeValue; + } + + return requestedLimit; +}; + + +/** + * Generates a QueryCommand object using the parameters of this cursor. + * + * @return {QueryCommand} The command object + * @ignore + * @api private + */ +var generateQueryCommand = function(self) { + // Unpack the options + var queryOptions = QueryCommand.OPTS_NONE; + if(!self.timeout) { + queryOptions |= QueryCommand.OPTS_NO_CURSOR_TIMEOUT; + } + + if(self.tailable != null) { + queryOptions |= QueryCommand.OPTS_TAILABLE_CURSOR; + self.skipValue = self.limitValue = 0; + + // if awaitdata is set + if(self.awaitdata != null) { + queryOptions |= QueryCommand.OPTS_AWAIT_DATA; + } + } + + if(self.exhaust) { + queryOptions |= QueryCommand.OPTS_EXHAUST; + } + + if(self.slaveOk) { + queryOptions |= QueryCommand.OPTS_SLAVE; + } + + if(self.partial) { + queryOptions |= QueryCommand.OPTS_PARTIAL; + } + + // limitValue of -1 is a special case used by Db#eval + var numberToReturn = self.limitValue == -1 ? -1 : limitRequest(self); + + // Check if we need a special selector + if(self.sortValue != null || self.explainValue != null || self.hint != null || self.snapshot != null + || self.returnKey != null || self.maxScan != null || self.min != null || self.max != null + || self.showDiskLoc != null || self.comment != null) { + + // Build special selector + var specialSelector = {'$query':self.selector}; + if(self.sortValue != null) specialSelector['orderby'] = utils.formattedOrderClause(self.sortValue); + if(self.hint != null && self.hint.constructor == Object) specialSelector['$hint'] = self.hint; + if(self.snapshot != null) specialSelector['$snapshot'] = true; + if(self.returnKey != null) specialSelector['$returnKey'] = self.returnKey; + if(self.maxScan != null) specialSelector['$maxScan'] = self.maxScan; + if(self.min != null) specialSelector['$min'] = self.min; + if(self.max != null) specialSelector['$max'] = self.max; + if(self.showDiskLoc != null) specialSelector['$showDiskLoc'] = self.showDiskLoc; + if(self.comment != null) specialSelector['$comment'] = self.comment; + // If we have explain set only return a single document with automatic cursor close + if(self.explainValue != null) { + numberToReturn = (-1)*Math.abs(numberToReturn); + specialSelector['$explain'] = true; + } + + // Return the query + return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, specialSelector, self.fields); + } else { + return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, self.selector, self.fields); + } +}; + +/** + * @return {Object} Returns an object containing the sort value of this cursor with + * the proper formatting that can be used internally in this cursor. + * @ignore + * @api private + */ +Cursor.prototype.formattedOrderClause = function() { + return utils.formattedOrderClause(this.sortValue); +}; + +/** + * Converts the value of the sort direction into its equivalent numerical value. + * + * @param sortDirection {String|number} Range of acceptable values: + * 'ascending', 'descending', 'asc', 'desc', 1, -1 + * + * @return {number} The equivalent numerical value + * @throws Error if the given sortDirection is invalid + * @ignore + * @api private + */ +Cursor.prototype.formatSortValue = function(sortDirection) { + return utils.formatSortValue(sortDirection); +}; + +/** + * Gets the next document from the cursor. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results. + * @api public + */ +Cursor.prototype.nextObject = function(callback) { + var self = this; + + if(self.state == Cursor.INIT) { + var cmd; + try { + cmd = generateQueryCommand(self); + } catch (err) { + return callback(err, null); + } + + // Execute command + var commandHandler = function(err, result) { + self.state = Cursor.OPEN; + // console.dir(err) + if(err != null && result == null) return callback(err, null); + + if(!err && result.documents[0] && result.documents[0]['$err']) { + return self.close(function() {callback(result.documents[0]['$err'], null);}); + } + + self.queryRun = true; + self.state = Cursor.OPEN; // Adjust the state of the cursor + self.cursorId = result.cursorId; + self.totalNumberOfRecords = result.numberReturned; + + // Add the new documents to the list of items, using forloop to avoid + // new array allocations and copying + for(var i = 0; i < result.documents.length; i++) { + self.items.push(result.documents[i]); + } + + // Ignore callbacks until the cursor is dead for exhausted + if(self.exhaust && result.cursorId.toString() == "0") { + self.nextObject(callback); + } else if(self.exhaust == false || self.exhaust == null) { + self.nextObject(callback); + } + }; + + // If we have no connection set on this cursor check one out + if(self.connection == null) { + try { + self.connection = this.read == null ? self.db.serverConfig.checkoutWriter() : self.db.serverConfig.checkoutReader(this.read); + } catch(err) { + return callback(err, null); + } + } + + // Execute the command + self.db._executeQueryCommand(cmd, {exhaust: self.exhaust, raw:self.raw, read:this.read, connection:self.connection}, commandHandler); + // Set the command handler to null + commandHandler = null; + } else if(self.items.length) { + callback(null, self.items.shift()); + } else if(self.cursorId.greaterThan(Long.fromInt(0))) { + getMore(self, callback); + } else { + // Force cursor to stay open + return self.close(function() {callback(null, null);}); + } +} + +/** + * Gets more results from the database if any. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results. + * @ignore + * @api private + */ +var getMore = function(self, callback) { + var limit = 0; + + if(self.state == Cursor.GET_MORE) return callback(null, null); + + // Set get more in progress + self.state = Cursor.GET_MORE; + + // Set options + if (!self.tailable && self.limitValue > 0) { + limit = self.limitValue - self.totalNumberOfRecords; + if (limit < 1) { + self.close(function() {callback(null, null);}); + return; + } + } + + try { + var getMoreCommand = new GetMoreCommand( + self.db + , self.collectionName + , limitRequest(self) + , self.cursorId + ); + + // Set up options + var options = {read: self.read, raw: self.raw, connection:self.connection }; + + // Execute the command + self.db._executeQueryCommand(getMoreCommand, options, function(err, result) { + // Get more done + self.state = Cursor.OPEN; + + try { + if(err != null) { + return callback(err, null); + } + + var isDead = 1 === result.responseFlag && result.cursorId.isZero(); + + self.cursorId = result.cursorId; + self.totalNumberOfRecords += result.numberReturned; + + // Determine if there's more documents to fetch + if(result.numberReturned > 0) { + if (self.limitValue > 0) { + var excessResult = self.totalNumberOfRecords - self.limitValue; + + if (excessResult > 0) { + result.documents.splice(-1 * excessResult, excessResult); + } + } + + // Reset the tries for awaitdata if we are using it + self.currentNumberOfRetries = self.numberOfRetries; + // Get the documents + for(var i = 0; i < result.documents.length; i++) { + self.items.push(result.documents[i]); + } + + // result = null; + callback(null, self.items.shift()); + } else if(self.tailable && !isDead && self.awaitdata) { + // Excute the tailable cursor once more, will timeout after ~4 sec if awaitdata used + self.currentNumberOfRetries = self.currentNumberOfRetries - 1; + if(self.currentNumberOfRetries == 0) { + self.close(function() { + callback(new Error("tailable cursor timed out"), null); + }); + } else { + process.nextTick(function() { getMore(self, callback); }); + } + } else if(self.tailable && !isDead) { + self.getMoreTimer = setTimeout(function() { getMore(self, callback); }, self.tailableRetryInterval); + } else { + self.close(function() {callback(null, null); }); + } + + result = null; + } catch(err) { + callback(err, null); + } + }); + + getMoreCommand = null; + } catch(err) { + // Get more done + self.state = Cursor.OPEN; + + var handleClose = function() { + callback(err, null); + }; + + self.close(handleClose); + handleClose = null; + } +} + +/** + * Gets a detailed information about how the query is performed on this cursor and how + * long it took the database to process it. + * + * @param {Function} callback this will be called after executing this method. The first parameter will always be null while the second parameter will be an object containing the details. + * @api public + */ +Cursor.prototype.explain = function(callback) { + var limit = (-1)*Math.abs(this.limitValue); + + // * - **skip** {Number} skip number of documents to skip. + // * - **limit** {Number}, limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1. + // * - **hint** {Object}, hint force the query to use a specific index. + // * - **explain** {Boolean}, explain return the explaination of the query. + // * - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries. + // * - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned. + // * - **timeout** {Boolean}, timeout allow the query to timeout. + // * - **tailable** {Boolean}, tailable allow the cursor to be tailable. + // * - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor. + // * - **batchSize** {Number}, batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database. + // * - **raw** {Boolean}, raw return all query documents as raw buffers (default false). + // * - **read** {Boolean}, read specify override of read from source (primary/secondary). + // * - **returnKey** {Boolean}, returnKey only return the index key. + // * - **maxScan** {Number}, maxScan limit the number of items to scan. + // * - **min** {Number}, min set index bounds. + // * - **max** {Number}, max set index bounds. + // * - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results. + // * - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler. + // * - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout. + // * - **dbName** {String}, dbName override the default dbName. + // * - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor. + // * - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets. + // * - **partial** {Boolean}, partial have the sharded system return a partial result from mongos. + + // * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + +// function Cursor(db, collection, selector, fields, skip, limit +// - , sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read +// - , returnKey, maxScan, min, max, showDiskLoc, comment, awaitdata, numberOfRetries, dbName, tailableRetry + + // Create a new cursor and fetch the plan + var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, { + skip: this.skipValue + , limit:limit + , sort: this.sortValue + , hint: this.hint + , explain: true + , snapshot: this.snapshot + , timeout: this.timeout + , tailable: this.tailable + , batchSize: this.batchSizeValue + , slaveOk: this.slaveOk + , raw: this.raw + , read: this.read + , returnKey: this.returnKey + , maxScan: this.maxScan + , min: this.min + , max: this.max + , showDiskLoc: this.showDiskLoc + , comment: this.comment + , awaitdata: this.awaitdata + , numberOfRetries: this.numberOfRetries + , dbName: this.dbName + }); + + // Fetch the explaination document + cursor.nextObject(function(err, item) { + if(err != null) return callback(err, null); + // close the cursor + cursor.close(function(err, result) { + if(err != null) return callback(err, null); + callback(null, item); + }); + }); +}; + +/** + * @ignore + */ +Cursor.prototype.streamRecords = function(options) { + console.log("[WARNING] streamRecords method is deprecated, please use stream method which is much faster"); + var args = Array.prototype.slice.call(arguments, 0); + options = args.length ? args.shift() : {}; + + var + self = this, + stream = new process.EventEmitter(), + recordLimitValue = this.limitValue || 0, + emittedRecordCount = 0, + queryCommand = generateQueryCommand(self); + + // see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol + queryCommand.numberToReturn = options.fetchSize ? options.fetchSize : 500; + // Execute the query + execute(queryCommand); + + function execute(command) { + self.db._executeQueryCommand(command, {exhaust: self.exhaust, read:self.read, raw:self.raw, connection:self.connection}, function(err,result) { + if(err) { + stream.emit('error', err); + self.close(function(){}); + return; + } + + if (!self.queryRun && result) { + self.queryRun = true; + self.cursorId = result.cursorId; + self.state = Cursor.OPEN; + self.getMoreCommand = new GetMoreCommand(self.db, self.collectionName, queryCommand.numberToReturn, result.cursorId); + } + + var resflagsMap = { + CursorNotFound:1<<0, + QueryFailure:1<<1, + ShardConfigStale:1<<2, + AwaitCapable:1<<3 + }; + + if(result.documents && result.documents.length && !(result.responseFlag & resflagsMap.QueryFailure)) { + try { + result.documents.forEach(function(doc){ + if(recordLimitValue && emittedRecordCount>=recordLimitValue) { + throw("done"); + } + emittedRecordCount++; + stream.emit('data', doc); + }); + } catch(err) { + if (err != "done") { throw err; } + else { + self.close(function(){ + stream.emit('end', recordLimitValue); + }); + self.close(function(){}); + return; + } + } + // rinse & repeat + execute(self.getMoreCommand); + } else { + self.close(function(){ + stream.emit('end', recordLimitValue); + }); + } + }); + } + + return stream; +}; + +/** + * Returns a Node ReadStream interface for this cursor. + * + * @return {CursorStream} returns a stream object. + * @api public + */ +Cursor.prototype.stream = function stream () { + return new CursorStream(this); +} + +/** + * Close the cursor. + * + * @param {Function} callback this will be called after executing this method. The first parameter will always contain null while the second parameter will contain a reference to this cursor. + * @return {null} + * @api public + */ +Cursor.prototype.close = function(callback) { + var self = this + this.getMoreTimer && clearTimeout(this.getMoreTimer); + // Close the cursor if not needed + if(this.cursorId instanceof Long && this.cursorId.greaterThan(Long.fromInt(0))) { + try { + var command = new KillCursorCommand(this.db, [this.cursorId]); + // Added an empty callback to ensure we don't throw any null exceptions + this.db._executeQueryCommand(command, {read:self.read, raw:self.raw, connection:self.connection}, function() {}); + } catch(err) {} + } + + // Null out the connection + self.connection = null; + // Reset cursor id + this.cursorId = Long.fromInt(0); + // Set to closed status + this.state = Cursor.CLOSED; + + if(callback) { + callback(null, self); + self.items = []; + } + + return this; +}; + +/** + * Check if the cursor is closed or open. + * + * @return {Boolean} returns the state of the cursor. + * @api public + */ +Cursor.prototype.isClosed = function() { + return this.state == Cursor.CLOSED ? true : false; +}; + +/** + * Init state + * + * @classconstant INIT + **/ +Cursor.INIT = 0; + +/** + * Cursor open + * + * @classconstant OPEN + **/ +Cursor.OPEN = 1; + +/** + * Cursor closed + * + * @classconstant CLOSED + **/ +Cursor.CLOSED = 2; + +/** + * Cursor performing a get more + * + * @classconstant OPEN + **/ +Cursor.GET_MORE = 3; + +/** + * @ignore + * @api private + */ +exports.Cursor = Cursor; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursorstream.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursorstream.js new file mode 100644 index 0000000..ee0cd80 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/cursorstream.js @@ -0,0 +1,151 @@ +/** + * Module dependecies. + */ +var Stream = require('stream').Stream; + +/** + * CursorStream + * + * Returns a stream interface for the **cursor**. + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **error** {function(err) {}} the error event triggers if an error happens. + * - **close** {function() {}} the end event triggers when there is no more documents available. + * + * @class Represents a CursorStream. + * @param {Cursor} cursor a cursor object that the stream wraps. + * @return {Stream} + */ +function CursorStream(cursor) { + if(!(this instanceof CursorStream)) return new CursorStream(cursor); + + Stream.call(this); + + this.readable = true; + this.paused = false; + this._cursor = cursor; + this._destroyed = null; + + // give time to hook up events + var self = this; + process.nextTick(function () { + self._init(); + }); +} + +/** + * Inherit from Stream + * @ignore + * @api private + */ +CursorStream.prototype.__proto__ = Stream.prototype; + +/** + * Flag stating whether or not this stream is readable. + */ +CursorStream.prototype.readable; + +/** + * Flag stating whether or not this stream is paused. + */ +CursorStream.prototype.paused; + +/** + * Initialize the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._init = function () { + if (this._destroyed) return; + this._next(); +} + +/** + * Pull the next document from the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._next = function () { + if (this.paused || this._destroyed) return; + + var self = this; + // Get the next object + process.nextTick(function() { + self._cursor.nextObject(function (err, doc) { + self._onNextObject(err, doc); + }); + }); +} + +/** + * Handle each document as its returned from the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._onNextObject = function (err, doc) { + if(err) return this.destroy(err); + + // when doc is null we hit the end of the cursor + if(!doc && (this._cursor.state == 1 || this._cursor.state == 2)) { + this.emit('end') + return this.destroy(); + } else if(doc) { + this.emit('data', doc); + this._next(); + } +} + +/** + * Pauses the stream. + * + * @api public + */ +CursorStream.prototype.pause = function () { + this.paused = true; +} + +/** + * Resumes the stream. + * + * @api public + */ +CursorStream.prototype.resume = function () { + var self = this; + + // Don't do anything if we are not paused + if(!this.paused) return; + if(!this._cursor.state == 3) return; + + process.nextTick(function() { + self.paused = false; + // Only trigger more fetching if the cursor is open + self._next(); + }) +} + +/** + * Destroys the stream, closing the underlying + * cursor. No more events will be emitted. + * + * @api public + */ +CursorStream.prototype.destroy = function (err) { + if (this._destroyed) return; + this._destroyed = true; + this.readable = false; + + this._cursor.close(); + + if (err) { + this.emit('error', err); + } + + this.emit('close'); +} + +// TODO - maybe implement the raw option to pass binary? +//CursorStream.prototype.setEncoding = function () { +//} + +module.exports = exports = CursorStream; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/db.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/db.js new file mode 100644 index 0000000..024f65f --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/db.js @@ -0,0 +1,2212 @@ +/** + * Module dependencies. + * @ignore + */ +var QueryCommand = require('./commands/query_command').QueryCommand, + DbCommand = require('./commands/db_command').DbCommand, + MongoReply = require('./responses/mongo_reply').MongoReply, + Admin = require('./admin').Admin, + Collection = require('./collection').Collection, + Server = require('./connection/server').Server, + ReplSet = require('./connection/repl_set').ReplSet, + ReadPreference = require('./connection/read_preference').ReadPreference, + Mongos = require('./connection/mongos').Mongos, + Cursor = require('./cursor').Cursor, + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + crypto = require('crypto'), + parse = require('./connection/url_parser').parse; + +/** + * Internal class for callback storage + * @ignore + */ +var CallbackStore = function() { + // Make class an event emitter + EventEmitter.call(this); + // Add a info about call variable + this._notReplied = {}; +} + +/** + * @ignore + */ +inherits(CallbackStore, EventEmitter); + +/** + * Create a new Db instance. + * + * Options + * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **native_parser** {Boolean, default:false}, use c++ bson parser. + * - **forceServerObjectId** {Boolean, default:false}, force server to create _id fields instead of client. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **serializeFunctions** {Boolean, default:false}, serialize functions. + * - **raw** {Boolean, default:false}, peform operations using raw bson buffers. + * - **recordQueryStats** {Boolean, default:false}, record query statistics during execution. + * - **retryMiliSeconds** {Number, default:5000}, number of miliseconds between retries. + * - **numberOfRetries** {Number, default:5}, number of retries off connection. + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @class Represents a Db + * @param {String} databaseName name of the database. + * @param {Object} serverConfig server config object. + * @param {Object} [options] additional options for the collection. + */ +function Db(databaseName, serverConfig, options) { + if(!(this instanceof Db)) return new Db(databaseName, serverConfig, options); + + EventEmitter.call(this); + this.databaseName = databaseName; + this.serverConfig = serverConfig; + this.options = options == null ? {} : options; + // State to check against if the user force closed db + this._applicationClosed = false; + // Fetch the override flag if any + var overrideUsedFlag = this.options['override_used_flag'] == null ? false : this.options['override_used_flag']; + + // Verify that nobody is using this config + if(!overrideUsedFlag && this.serverConfig != null && typeof this.serverConfig == 'object' && this.serverConfig._isUsed && this.serverConfig._isUsed()) { + throw new Error("A Server or ReplSet instance cannot be shared across multiple Db instances"); + } else if(!overrideUsedFlag && typeof this.serverConfig == 'object'){ + // Set being used + this.serverConfig._used = true; + } + + // Ensure we have a valid db name + validateDatabaseName(databaseName); + + // Contains all the connections for the db + try { + this.native_parser = this.options.native_parser; + // The bson lib + var bsonLib = this.bsonLib = this.options.native_parser ? require('bson').BSONNative : require('bson').BSONPure; + // Fetch the serializer object + var BSON = bsonLib.BSON; + // Create a new instance + this.bson = new BSON([bsonLib.Long, bsonLib.ObjectID, bsonLib.Binary, bsonLib.Code, bsonLib.DBRef, bsonLib.Symbol, bsonLib.Double, bsonLib.Timestamp, bsonLib.MaxKey, bsonLib.MinKey]); + // Backward compatibility to access types + this.bson_deserializer = bsonLib; + this.bson_serializer = bsonLib; + } catch (err) { + // If we tried to instantiate the native driver + var msg = "Native bson parser not compiled, please compile " + + "or avoid using native_parser=true"; + throw Error(msg); + } + + // Internal state of the server + this._state = 'disconnected'; + + this.pkFactory = this.options.pk == null ? bsonLib.ObjectID : this.options.pk; + this.forceServerObjectId = this.options.forceServerObjectId != null ? this.options.forceServerObjectId : false; + + // Added safe + this.safe = this.options.safe == null ? false : this.options.safe; + + // If we have not specified a "safe mode" we just print a warning to the console + if(this.options.safe == null && this.options.w == null && this.options.journal == null && this.options.fsync == null) { + console.log("========================================================================================"); + console.log("= Please ensure that you set the default write concern for the database by setting ="); + console.log("= one of the options ="); + console.log("= ="); + console.log("= w: (value of > -1 or the string 'majority'), where < 1 means ="); + console.log("= no write acknowlegement ="); + console.log("= journal: true/false, wait for flush to journal before acknowlegement ="); + console.log("= fsync: true/false, wait for flush to file system before acknowlegement ="); + console.log("= ="); + console.log("= For backward compatibility safe is still supported and ="); + console.log("= allows values of [true | false | {j:true} | {w:n, wtimeout:n} | {fsync:true}] ="); + console.log("= the default value is false which means the driver receives does not ="); + console.log("= return the information of the success/error of the insert/update/remove ="); + console.log("= ="); + console.log("= ex: new Db(new Server('localhost', 27017), {safe:false}) ="); + console.log("= ="); + console.log("= http://www.mongodb.org/display/DOCS/getLastError+Command ="); + console.log("= ="); + console.log("= The default of no acknowlegement will change in the very near future ="); + console.log("= ="); + console.log("= This message will disappear when the default safe is set on the driver Db ="); + console.log("========================================================================================"); + } + + // Internal states variables + this.notReplied ={}; + this.isInitializing = true; + this.auths = []; + this.openCalled = false; + + // Command queue, keeps a list of incoming commands that need to be executed once the connection is up + this.commands = []; + + // Contains all the callbacks + this._callBackStore = new CallbackStore(); + + // Set up logger + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.log == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + // Allow slaveOk + this.slaveOk = this.options["slave_ok"] == null ? false : this.options["slave_ok"]; + + var self = this; + // Associate the logger with the server config + this.serverConfig.logger = this.logger; + this.tag = new Date().getTime(); + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[]}; + + // Controls serialization options + this.serializeFunctions = this.options.serializeFunctions != null ? this.options.serializeFunctions : false; + + // Raw mode + this.raw = this.options.raw != null ? this.options.raw : false; + + // Record query stats + this.recordQueryStats = this.options.recordQueryStats != null ? this.options.recordQueryStats : false; + + // If we have server stats let's make sure the driver objects have it enabled + if(this.recordQueryStats == true) { + this.serverConfig.enableRecordQueryStats(true); + } + + // Retry information + this.retryMiliSeconds = this.options.retryMiliSeconds != null ? this.options.retryMiliSeconds : 1000; + this.numberOfRetries = this.options.numberOfRetries != null ? this.options.numberOfRetries : 60; + + // Set default read preference if any + this.readPreference = this.options.readPreference; +}; + +/** + * @ignore + */ +function validateDatabaseName(databaseName) { + if(typeof databaseName !== 'string') throw new Error("database name must be a string"); + if(databaseName.length === 0) throw new Error("database name cannot be the empty string"); + + var invalidChars = [" ", ".", "$", "/", "\\"]; + for(var i = 0; i < invalidChars.length; i++) { + if(databaseName.indexOf(invalidChars[i]) != -1) throw new Error("database names cannot contain the character '" + invalidChars[i] + "'"); + } +} + +/** + * @ignore + */ +inherits(Db, EventEmitter); + +/** + * Initialize the database connection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the index information or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.open = function(callback) { + var self = this; + + // Check that the user has not called this twice + if(this.openCalled) { + // Close db + this.close(); + // Throw error + throw new Error("db object already connecting, open cannot be called multiple times"); + } + + // If we have a specified read preference + if(this.readPreference != null) this.serverConfig.setReadPreference(this.readPreference); + + // Set that db has been opened + this.openCalled = true; + + // Set the status of the server + self._state = 'connecting'; + // Set up connections + if(self.serverConfig instanceof Server || self.serverConfig instanceof ReplSet || self.serverConfig instanceof Mongos) { + self.serverConfig.connect(self, {firstCall: true}, function(err, result) { + if(err != null) { + // Set that db has been closed + self.openCalled = false; + // Return error from connection + return callback(err, null); + } + // Set the status of the server + self._state = 'connected'; + // If we have queued up commands execute a command to trigger replays + if(self.commands.length > 0) _execute_queued_command(self); + // Callback + return callback(null, self); + }); + } else { + return callback(Error("Server parameter must be of type Server, ReplSet or Mongos"), null); + } +}; + +// Execute any baked up commands +var _execute_queued_command = function(self) { + // Execute any backed up commands + process.nextTick(function() { + // Execute any backed up commands + while(self.commands.length > 0) { + // Fetch the command + var command = self.commands.shift(); + // Execute based on type + if(command['type'] == 'query') { + __executeQueryCommand(self, command['db_command'], command['options'], command['callback']); + } else if(command['type'] == 'insert') { + __executeInsertCommand(self, command['db_command'], command['options'], command['callback']); + } + } + }); +} + +/** + * Create a new Db instance sharing the current socket connections. + * + * @param {String} dbName the name of the database we want to use. + * @return {Db} a db instance using the new database. + * @api public + */ +Db.prototype.db = function(dbName) { + // Copy the options and add out internal override of the not shared flag + var options = {}; + for(var key in this.options) { + options[key] = this.options[key]; + } + // Add override flag + options['override_used_flag'] = true; + // Create a new db instance + var newDbInstance = new Db(dbName, this.serverConfig, options); + //copy over any auths, we may need them for reconnecting + if (this.serverConfig.db) { + newDbInstance.auths = this.serverConfig.db.auths; + } + // Add the instance to the list of approved db instances + var allServerInstances = this.serverConfig.allServerInstances(); + // Add ourselves to all server callback instances + for(var i = 0; i < allServerInstances.length; i++) { + var server = allServerInstances[i]; + server.dbInstances.push(newDbInstance); + } + // Return new db object + return newDbInstance; +} + +/** + * Close the current db connection, including all the child db instances. Emits close event if no callback is provided. + * + * @param {Boolean} [forceClose] connection can never be reused. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.close = function(forceClose, callback) { + var self = this; + // Ensure we force close all connections + this._applicationClosed = false; + + if(typeof forceClose == 'function') { + callback = forceClose; + } else if(typeof forceClose == 'boolean') { + this._applicationClosed = forceClose; + } + + // Remove all listeners and close the connection + this.serverConfig.close(function(err, result) { + // Emit the close event + if(typeof callback !== 'function') self.emit("close"); + + // Emit close event across all db instances sharing the sockets + var allServerInstances = self.serverConfig.allServerInstances(); + // Fetch the first server instance + if(Array.isArray(allServerInstances) && allServerInstances.length > 0) { + var server = allServerInstances[0]; + // For all db instances signal all db instances + if(Array.isArray(server.dbInstances) && server.dbInstances.length > 1) { + for(var i = 0; i < server.dbInstances.length; i++) { + var dbInstance = server.dbInstances[i]; + // Check if it's our current db instance and skip if it is + if(dbInstance.databaseName !== self.databaseName && dbInstance.tag !== self.tag) { + server.dbInstances[i].emit("close"); + } + } + } + } + + // Remove all listeners + self.removeAllEventListeners(); + // You can reuse the db as everything is shut down + self.openCalled = false; + // If we have a callback call it + if(callback) callback(err, result); + }); +}; + +/** + * Access the Admin database + * + * @param {Function} [callback] returns the results. + * @return {Admin} the admin db object. + * @api public + */ +Db.prototype.admin = function(callback) { + if(callback == null) return new Admin(this); + callback(null, new Admin(this)); +}; + +/** + * Returns a cursor to all the collection information. + * + * @param {String} [collectionName] the collection name we wish to retrieve the information from. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the options or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collectionsInfo = function(collectionName, callback) { + if(callback == null && typeof collectionName == 'function') { callback = collectionName; collectionName = null; } + // Create selector + var selector = {}; + // If we are limiting the access to a specific collection name + if(collectionName != null) selector.name = this.databaseName + "." + collectionName; + + // Return Cursor + // callback for backward compatibility + if(callback) { + callback(null, new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector)); + } else { + return new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector); + } +}; + +/** + * Get the list of all collection names for the specified db + * + * Options + * - **namesOnly** {String, default:false}, Return only the full collection namespace. + * + * @param {String} [collectionName] the collection name we wish to filter by. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collection names or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collectionNames = function(collectionName, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + collectionName = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Ensure no breaking behavior + if(collectionName != null && typeof collectionName == 'object') { + options = collectionName; + collectionName = null; + } + + // Let's make our own callback to reuse the existing collections info method + self.collectionsInfo(collectionName, function(err, cursor) { + if(err != null) return callback(err, null); + + cursor.toArray(function(err, documents) { + if(err != null) return callback(err, null); + + // List of result documents that have been filtered + var filtered_documents = documents.filter(function(document) { + return !(document.name.indexOf(self.databaseName) == -1 || document.name.indexOf('$') != -1); + }); + + // If we are returning only the names + if(options.namesOnly) { + filtered_documents = filtered_documents.map(function(document) { return document.name }); + } + + // Return filtered items + callback(null, filtered_documents); + }); + }); +}; + +/** + * Fetch a specific collection (containing the actual collection information) + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strict**, (Boolean, default:false) throws and error if collection already exists + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * + * @param {String} collectionName the collection name we wish to access. + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collection = function(collectionName, options, callback) { + var self = this; + if(typeof options === "function") { callback = options; options = {}; } + // Execute safe + + if(options && (options.strict)) { + self.collectionNames(collectionName, function(err, collections) { + if(err != null) return callback(err, null); + + if(collections.length == 0) { + return callback(new Error("Collection " + collectionName + " does not exist. Currently in safe mode."), null); + } else { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } + }); + } else { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + if(callback == null) { + throw err; + } else { + return callback(err, null); + } + } + + // If we have no callback return collection object + return callback == null ? collection : callback(null, collection); + } +}; + +/** + * Fetch all collections for the current db. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collections or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collections = function(callback) { + var self = this; + // Let's get the collection names + self.collectionNames(function(err, documents) { + if(err != null) return callback(err, null); + var collections = []; + documents.forEach(function(document) { + collections.push(new Collection(self, document.name.replace(self.databaseName + ".", ''), self.pkFactory)); + }); + // Return the collection objects + callback(null, collections); + }); +}; + +/** + * Evaluate javascript on the server + * + * Options + * - **nolock** {Boolean, default:false}, Tell MongoDB not to block on the evaulation of the javascript. + * + * @param {Code} code javascript to execute on server. + * @param {Object|Array} [parameters] the parameters for the call. + * @param {Object} [options] the options + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from eval or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.eval = function(code, parameters, options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + parameters = args.length ? args.shift() : parameters; + options = args.length ? args.shift() : {}; + + var finalCode = code; + var finalParameters = []; + // If not a code object translate to one + if(!(finalCode instanceof this.bsonLib.Code)) { + finalCode = new this.bsonLib.Code(finalCode); + } + + // Ensure the parameters are correct + if(parameters != null && parameters.constructor != Array && typeof parameters !== 'function') { + finalParameters = [parameters]; + } else if(parameters != null && parameters.constructor == Array && typeof parameters !== 'function') { + finalParameters = parameters; + } + + // Create execution selector + var selector = {'$eval':finalCode, 'args':finalParameters}; + // Check if the nolock parameter is passed in + if(options['nolock']) { + selector['nolock'] = options['nolock']; + } + + // Set primary read preference + options.readPreference = ReadPreference.PRIMARY; + + // Execute the eval + this.collection(DbCommand.SYSTEM_COMMAND_COLLECTION).findOne(selector, options, function(err, result) { + if(err) return callback(err); + + if(result && result.ok == 1) { + callback(null, result.retval); + } else if(result) { + callback(new Error("eval failed: " + result.errmsg), null); return; + } else { + callback(err, result); + } + }); +}; + +/** + * Dereference a dbref, against a db + * + * @param {DBRef} dbRef db reference object we wish to resolve. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dereference or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dereference = function(dbRef, callback) { + var db = this; + // If we have a db reference then let's get the db first + if(dbRef.db != null) db = this.db(dbRef.db); + // Fetch the collection and find the reference + var collection = db.collection(dbRef.namespace); + collection.findOne({'_id':dbRef.oid}, function(err, result) { + callback(err, result); + }); +}; + +/** + * Logout user from server, fire off on all connections and remove all auth info + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from logout or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.logout = function(options, callback) { + var self = this; + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Number of connections we need to logout from + var numberOfConnections = this.serverConfig.allRawConnections().length; + + // Let's generate the logout command object + var logoutCommand = DbCommand.logoutCommand(self, {logout:1}, options); + self._executeQueryCommand(logoutCommand, {onAll:true}, function(err, result) { + // Count down + numberOfConnections = numberOfConnections - 1; + // Work around the case where the number of connections are 0 + if(numberOfConnections <= 0 && typeof callback == 'function') { + var internalCallback = callback; + callback = null; + // Reset auth + self.auths = []; + // Handle any errors + if(err == null && result.documents[0].ok == 1) { + internalCallback(null, true); + } else { + err != null ? internalCallback(err, false) : internalCallback(new Error(result.documents[0].errmsg), false); + } + } + }); +} + +/** + * Authenticate a user against the server. + * + * Options + * - **authdb** {String}, The database that the credentials are for, + * different from the name of the current DB, for example admin + * @param {String} username username. + * @param {String} password password. + * @param {Object} [options] the options + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from authentication or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.authenticate = function(username, password, options, callback) { + var self = this; + + if (typeof callback === 'undefined') { + callback = options; + options = {}; + } + // the default db to authenticate against is 'this' + // if authententicate is called from a retry context, it may be another one, like admin + var authdb = options.authdb ? options.authdb : self.databaseName; + // Push the new auth if we have no previous record + + var numberOfConnections = 0; + var errorObject = null; + + if(options['connection'] != null) { + //if a connection was explicitly passed on options, then we have only one... + numberOfConnections = 1; + } else { + // Get the amount of connections in the pool to ensure we have authenticated all comments + numberOfConnections = this.serverConfig.allRawConnections().length; + options['onAll'] = true; + } + + // Execute all four + this._executeQueryCommand(DbCommand.createGetNonceCommand(self), options, function(err, result, connection) { + // Execute on all the connections + if(err == null) { + // Nonce used to make authentication request with md5 hash + var nonce = result.documents[0].nonce; + // Execute command + self._executeQueryCommand(DbCommand.createAuthenticationCommand(self, username, password, nonce, authdb), {connection:connection}, function(err, result) { + // Count down + numberOfConnections = numberOfConnections - 1; + // Ensure we save any error + if(err) { + errorObject = err; + } else if(result.documents[0].err != null || result.documents[0].errmsg != null){ + errorObject = self.wrap(result.documents[0]); + } + + // Work around the case where the number of connections are 0 + if(numberOfConnections <= 0 && typeof callback == 'function') { + var internalCallback = callback; + callback = null; + + if(errorObject == null && result.documents[0].ok == 1) { + // We authenticated correctly save the credentials + self.auths = [{'username':username, 'password':password, 'authdb': authdb}]; + // Return callback + internalCallback(errorObject, true); + } else { + internalCallback(errorObject, false); + } + } + }); + } + }); +}; + +/** + * Add a user to the database. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username username. + * @param {String} password password. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from addUser or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.addUser = function(username, password, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + // Use node md5 generator + var md5 = crypto.createHash('md5'); + // Generate keys used for authentication + md5.update(username + ":mongo:" + password); + var userPassword = md5.digest('hex'); + // Fetch a user collection + var collection = this.collection(DbCommand.SYSTEM_USER_COLLECTION); + // Check if we are inserting the first user + collection.count({}, function(err, count) { + // We got an error (f.ex not authorized) + if(err != null) return callback(err, null); + // Check if the user exists and update i + collection.find({user: username}, {dbName: options['dbName']}).toArray(function(err, documents) { + // We got an error (f.ex not authorized) + if(err != null) return callback(err, null); + // Add command keys + var commandOptions = safe; + commandOptions.dbName = options['dbName']; + commandOptions.upsert = true; + // We have a user, let's update the password or upsert if not + collection.update({user: username},{$set: {user: username, pwd: userPassword}}, commandOptions, function(err, results) { + if(count == 0 && err) { + callback(null, [{user:username, pwd:userPassword}]); + } else if(err) { + callback(err, null) + } else { + callback(null, [{user:username, pwd:userPassword}]); + } + }); + }); + }); +}; + +/** + * Remove a user from a database + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username username. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from removeUser or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.removeUser = function(username, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + + // Fetch a user collection + var collection = this.collection(DbCommand.SYSTEM_USER_COLLECTION); + collection.findOne({user: username}, {dbName: options['dbName']}, function(err, user) { + if(user != null) { + // Add command keys + var commandOptions = safe; + commandOptions.dbName = options['dbName']; + + collection.remove({user: username}, commandOptions, function(err, result) { + callback(err, true); + }); + } else { + callback(err, false); + } + }); +}; + +/** + * Creates a collection on a server pre-allocating space, need to create f.ex capped collections. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **capped** {Boolean, default:false}, create a capped collection. + * - **size** {Number}, the size of the capped collection in bytes. + * - **max** {Number}, the maximum number of documents in the capped collection. + * - **autoIndexId** {Boolean, default:true}, create an index on the _id field of the document, True by default on MongoDB 2.2 or higher off for version < 2.2. + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strict**, (Boolean, default:false) throws and error if collection already exists + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} collectionName the collection name we wish to access. + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from createCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.createCollection = function(collectionName, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : null; + var self = this; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + + // Check if we have the name + this.collectionNames(collectionName, function(err, collections) { + if(err != null) return callback(err, null); + + var found = false; + collections.forEach(function(collection) { + if(collection.name == self.databaseName + "." + collectionName) found = true; + }); + + // If the collection exists either throw an exception (if db in safe mode) or return the existing collection + if(found && options && options.strict) { + return callback(new Error("Collection " + collectionName + " already exists. Currently in safe mode."), null); + } else if(found){ + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } + + // Create a new collection and return it + self._executeQueryCommand(DbCommand.createCreateCollectionCommand(self, collectionName, options), {read:false, safe:safe}, function(err, result) { + var document = result.documents[0]; + // If we have no error let's return the collection + if(err == null && document.ok == 1) { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } else { + err != null ? callback(err, null) : callback(self.wrap(document), null); + } + }); + }); +}; + +/** + * Execute a command hash against MongoDB. This lets you acess any commands not available through the api on the server. + * + * @param {Object} selector the command hash to send to the server, ex: {ping:1}. + * @param {Function} callback this will be called after executing this method. The command always return the whole result of the command as the second parameter. + * @return {null} + * @api public + */ +Db.prototype.command = function(selector, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Set up the options + var cursor = new Cursor(this + , new Collection(this, DbCommand.SYSTEM_COMMAND_COLLECTION), selector, {}, { + limit: -1, timeout: QueryCommand.OPTS_NO_CURSOR_TIMEOUT, dbName: options['dbName'] + }); + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + // Ensure only commands who support read Prefrences are exeuted otherwise override and use Primary + if(readPreference != false) { + if(selector['group'] || selector['aggregate'] || selector['collStats'] || selector['dbStats'] + || selector['count'] || selector['distinct'] || selector['geoNear'] || selector['geoSearch'] || selector['geoWalk'] + || (selector['mapreduce'] && selector.out == 'inline')) { + // Set the read preference + cursor.setReadPreference(readPreference); + } else { + cursor.setReadPreference(ReadPreference.PRIMARY); + } + } + + // Return the next object + cursor.nextObject(callback); +}; + +/** + * Drop a collection from the database, removing it permanently. New accesses will create a new collection. + * + * @param {String} collectionName the name of the collection we wish to drop. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropCollection = function(collectionName, callback) { + var self = this; + + // Drop the collection + this._executeQueryCommand(DbCommand.createDropCollectionCommand(this, collectionName), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) return callback(null, true); + } else { + if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null); + } + }); +}; + +/** + * Rename a collection. + * + * @param {String} fromCollection the name of the current collection we wish to rename. + * @param {String} toCollection the new name of the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from renameCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.renameCollection = function(fromCollection, toCollection, callback) { + var self = this; + + // Execute the command, return the new renamed collection if successful + this._executeQueryCommand(DbCommand.createRenameCollectionCommand(this, fromCollection, toCollection), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) return callback(null, new Collection(self, toCollection, self.pkFactory)); + } else { + if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null); + } + }); +}; + +/** + * Return last error message for the given connection, note options can be combined. + * + * Options + * - **fsync** {Boolean, default:false}, option forces the database to fsync all files before returning. + * - **j** {Boolean, default:false}, awaits the journal commit before returning, > MongoDB 2.0. + * - **w** {Number}, until a write operation has been replicated to N servers. + * - **wtimeout** {Number}, number of miliseconds to wait before timing out. + * + * Connection Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Object} [connectionOptions] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from lastError or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.lastError = function(options, connectionOptions, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + connectionOptions = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createGetLastErrorCommand(options, this), connectionOptions, function(err, error) { + callback(err, error && error.documents); + }); +}; + +/** + * Legacy method calls. + * + * @ignore + * @api private + */ +Db.prototype.error = Db.prototype.lastError; +Db.prototype.lastStatus = Db.prototype.lastError; + +/** + * Return all errors up to the last time db reset_error_history was called. + * + * Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from previousErrors or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.previousErrors = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createGetPreviousErrorsCommand(this), options, function(err, error) { + callback(err, error.documents); + }); +}; + +/** + * Runs a command on the database. + * @ignore + * @api private + */ +Db.prototype.executeDbCommand = function(command_hash, options, callback) { + if(callback == null) { callback = options; options = {}; } + this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, command_hash, options), options, callback); +}; + +/** + * Runs a command on the database as admin. + * @ignore + * @api private + */ +Db.prototype.executeDbAdminCommand = function(command_hash, options, callback) { + if(callback == null) { callback = options; options = {}; } + this._executeQueryCommand(DbCommand.createAdminDbCommand(this, command_hash), options, callback); +}; + +/** + * Resets the error history of the mongo instance. + * + * Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from resetErrorHistory or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.resetErrorHistory = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createResetErrorHistoryCommand(this), options, function(err, error) { + callback(err, error.documents); + }); +}; + +/** + * Creates an index on the collection. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * + * @param {String} collectionName name of the collection to create the index on. + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from createIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.createIndex = function(collectionName, fieldOrSpec, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options = typeof callback === 'function' ? options : callback; + options = options == null ? {} : options; + + // Get the error options + var errorOptions = _getWriteConcern(this, options, callback); + // Create command + var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options); + // Default command options + var commandOptions = {}; + + // If we have error conditions set handle them + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute insert command + this._executeInsertCommand(command, commandOptions, function(err, result) { + if(err != null) return callback(err, null); + + result = result && result.documents; + if (result[0].err) { + callback(self.wrap(result[0])); + } else { + callback(null, command.documents[0].name); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute insert command + var result = this._executeInsertCommand(command, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, null); + } +}; + +/** + * Ensures that an index exists, if it does not it creates it + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} collectionName name of the collection to create the index on. + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from ensureIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.ensureIndex = function(collectionName, fieldOrSpec, options, callback) { + var self = this; + + if (typeof callback === 'undefined' && typeof options === 'function') { + callback = options; + options = {}; + } + + if (options == null) { + options = {}; + } + + // Get the error options + var errorOptions = _getWriteConcern(this, options, callback); + // Make sure we don't try to do a write concern without a callback + if(_hasWriteConcern(errorOptions) && callback == null) + throw new Error("Cannot use a writeConcern without a provided callback"); + // Create command + var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options); + var index_name = command.documents[0].name; + + // Default command options + var commandOptions = {}; + // Check if the index allready exists + this.indexInformation(collectionName, function(err, collectionInfo) { + if(err != null) return callback(err, null); + + if(!collectionInfo[index_name]) { + // If we have error conditions set handle them + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + if(typeof callback === 'function' + && commandOptions.w < 1 && !commandOptions.fsync && !commandOptions.journal) { + commandOptions.w = 1; + } + + self._executeInsertCommand(command, commandOptions, function(err, result) { + // Only callback if we have one specified + if(typeof callback === 'function') { + if(err != null) return callback(err, null); + + result = result && result.documents; + if (result[0].err) { + callback(self.wrap(result[0])); + } else { + callback(null, command.documents[0].name); + } + } + }); + } else { + // Execute insert command + var result = self._executeInsertCommand(command, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, index_name); + } + } else { + if(typeof callback === 'function') return callback(null, index_name); + } + }); +}; + +/** + * Returns the information available on allocated cursors. + * + * Options + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from cursorInfo or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.cursorInfo = function(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, {'cursorInfo':1}), options, function(err, result) { + callback(err, result.documents[0]); + }); +}; + +/** + * Drop an index on a collection. + * + * @param {String} collectionName the name of the collection where the command will drop an index. + * @param {String} indexName name of the index to drop. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropIndex = function(collectionName, indexName, callback) { + this._executeQueryCommand(DbCommand.createDropIndexCommand(this, collectionName, indexName), callback); +}; + +/** + * Reindex all indexes on the collection + * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. + * + * @param {String} collectionName the name of the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from reIndex or null if an error occured. + * @api public +**/ +Db.prototype.reIndex = function(collectionName, callback) { + this._executeQueryCommand(DbCommand.createReIndexCommand(this, collectionName), function(err, result) { + if(err != null) { + callback(err, false); + } else if(result.documents[0].errmsg == null) { + callback(null, true); + } else { + callback(new Error(result.documents[0].errmsg), false); + } + }); +}; + +/** + * Retrieves this collections index info. + * + * Options + * - **full** {Boolean, default:false}, returns the full raw index information. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {String} collectionName the name of the collection. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from indexInformation or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.indexInformation = function(collectionName, options, callback) { + if(typeof callback === 'undefined') { + if(typeof options === 'undefined') { + callback = collectionName; + collectionName = null; + } else { + callback = options; + } + options = {}; + } + + // If we specified full information + var full = options['full'] == null ? false : options['full']; + // Build selector for the indexes + var selector = collectionName != null ? {ns: (this.databaseName + "." + collectionName)} : {}; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : ReadPreference.PRIMARY; + + // Iterate through all the fields of the index + this.collection(DbCommand.SYSTEM_INDEX_COLLECTION, function(err, collection) { + // Perform the find for the collection + collection.find(selector).setReadPreference(readPreference).toArray(function(err, indexes) { + if(err != null) return callback(err, null); + // Contains all the information + var info = {}; + + // if full defined just return all the indexes directly + if(full) return callback(null, indexes); + + // Process all the indexes + for(var i = 0; i < indexes.length; i++) { + var index = indexes[i]; + // Let's unpack the object + info[index.name] = []; + for(var name in index.key) { + info[index.name].push([name, index.key[name]]); + } + } + + // Return all the indexes + callback(null, info); + }); + }); +}; + +/** + * Drop a database. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropDatabase or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropDatabase = function(callback) { + var self = this; + + this._executeQueryCommand(DbCommand.createDropDatabaseCommand(this), function(err, result) { + if (err == null && result.documents[0].ok == 1) { + callback(null, true); + } else { + if (err) { + callback(err, false); + } else { + callback(self.wrap(result.documents[0]), false); + } + } + }); +}; + +/** + * Get all the db statistics. + * + * Options + * - **scale** {Number}, divide the returned sizes by scale value. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Objects} [options] options for the stats command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from stats or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.stats = function stats(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + dbStats:this.collectionName, + } + + // Check if we have the scale value + if(options['scale'] != null) commandObject['scale'] = options['scale']; + + // Execute the command + this.command(commandObject, options, callback); +} + +/** + * Register a handler + * @ignore + * @api private + */ +Db.prototype._registerHandler = function(db_command, raw, connection, exhaust, callback) { + // If we have an array of commands, chain them + var chained = Array.isArray(db_command); + + // Check if we have exhausted + if(typeof exhaust == 'function') { + callback = exhaust; + exhaust = false; + } + + // If they are chained we need to add a special handler situation + if(chained) { + // List off chained id's + var chainedIds = []; + // Add all id's + for(var i = 0; i < db_command.length; i++) chainedIds.push(db_command[i].getRequestId().toString()); + // Register all the commands together + for(var i = 0; i < db_command.length; i++) { + var command = db_command[i]; + // Add the callback to the store + this._callBackStore.once(command.getRequestId(), callback); + // Add the information about the reply + this._callBackStore._notReplied[command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, chained:chainedIds, connection:connection, exhaust:false}; + } + } else { + // Add the callback to the list of handlers + this._callBackStore.once(db_command.getRequestId(), callback); + // Add the information about the reply + this._callBackStore._notReplied[db_command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, connection:connection, exhaust:exhaust}; + } +} + +/** + * Re-Register a handler, on the cursor id f.ex + * @ignore + * @api private + */ +Db.prototype._reRegisterHandler = function(newId, object, callback) { + // Add the callback to the list of handlers + this._callBackStore.once(newId, object.callback.listener); + // Add the information about the reply + this._callBackStore._notReplied[newId] = object.info; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._callHandler = function(id, document, err) { + // If there is a callback peform it + if(this._callBackStore.listeners(id).length >= 1) { + // Get info object + var info = this._callBackStore._notReplied[id]; + // Delete the current object + delete this._callBackStore._notReplied[id]; + // Emit to the callback of the object + this._callBackStore.emit(id, err, document, info.connection); + } +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._hasHandler = function(id) { + // If there is a callback peform it + return this._callBackStore.listeners(id).length >= 1; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._removeHandler = function(id) { + // Remove the information + if(this._callBackStore._notReplied[id] != null) delete this._callBackStore._notReplied[id]; + // Remove the callback if it's registered + this._callBackStore.removeAllListeners(id); + // Force cleanup _events, node.js seems to set it as a null value + if(this._callBackStore._events != null) delete this._callBackStore._events[id]; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._findHandler = function(id) { + var info = this._callBackStore._notReplied[id]; + // Return the callback + return {info:info, callback:(this._callBackStore.listeners(id).length >= 1) ? this._callBackStore.listeners(id)[0] : null} +} + +/** + * @ignore + */ +var __executeQueryCommand = function(self, db_command, options, callback) { + // Options unpacking + var read = options['read'] != null ? options['read'] : false; + var raw = options['raw'] != null ? options['raw'] : self.raw; + var onAll = options['onAll'] != null ? options['onAll'] : false; + var specifiedConnection = options['connection'] != null ? options['connection'] : null; + + // Correct read preference to default primary if set to false, null or primary + if(!(typeof read == 'object') && read._type == 'ReadPreference') { + read = (read == null || read == 'primary' || read == false) ? ReadPreference.PRIMARY : read; + if(!ReadPreference.isValid(read)) return callback(new Error("Illegal readPreference mode specified, " + read)); + } else if(typeof read == 'object' && read._type == 'ReadPreference') { + if(!read.isValid()) return callback(new Error("Illegal readPreference mode specified, " + read.mode)); + } + + // If we have a read preference set and we are a mongos pass the read preference on to the mongos instance, + if(self.serverConfig.isMongos() && read != null && read != false) { + db_command.setMongosReadPreference(read); + } + + // If we got a callback object + if(typeof callback === 'function' && !onAll) { + // Override connection if we passed in a specific connection + var connection = specifiedConnection != null ? specifiedConnection : null; + // connection = connection != null && connection.connected != null ? connection : null; + + if(connection instanceof Error) return callback(connection, null); + + // Fetch either a reader or writer dependent on the specified read option if no connection + // was passed in + if(connection == null) { + connection = read == null || read == 'primary' || read == false ? self.serverConfig.checkoutWriter(true) : self.serverConfig.checkoutReader(read); + } + + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error || connection['message'] != null) { + return callback(connection); + } + + // Exhaust Option + var exhaust = options.exhaust || false; + + // Register the handler in the data structure + self._registerHandler(db_command, raw, connection, exhaust, callback); + + // Write the message out and handle any errors if there are any + connection.write(db_command, function(err) { + if(err != null) { + // Call the handler with an error + self._callHandler(db_command.getRequestId(), null, err); + } + }); + } else if(typeof callback === 'function' && onAll) { + var connections = self.serverConfig.allRawConnections(); + var numberOfEntries = connections.length; + // Go through all the connections + for(var i = 0; i < connections.length; i++) { + // Fetch a connection + var connection = connections[i]; + + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error) { + return callback(connection); + } + + // Register the handler in the data structure + self._registerHandler(db_command, raw, connection, callback); + + // Write the message out + connection.write(db_command, function(err) { + // Adjust the number of entries we need to process + numberOfEntries = numberOfEntries - 1; + // Remove listener + if(err != null) { + // Clean up listener and return error + self._removeHandler(db_command.getRequestId()); + } + + // No more entries to process callback with the error + if(numberOfEntries <= 0) { + callback(err); + } + }); + + // Update the db_command request id + db_command.updateRequestId(); + } + } else { + // Fetch either a reader or writer dependent on the specified read option + var connection = read == null || read == 'primary' || read == false ? self.serverConfig.checkoutWriter(true) : self.serverConfig.checkoutReader(read); + // Override connection if needed + connection = specifiedConnection != null ? specifiedConnection : connection; + // Ensure we have a valid connection + if(connection == null || connection instanceof Error || connection['message'] != null) return null; + // Write the message out + connection.write(db_command, function(err) { + if(err != null) { + // Emit the error + self.emit("error", err); + } + }); + } +} + +/** + * @ignore + */ +var __retryCommandOnFailure = function(self, retryInMilliseconds, numberOfTimes, command, db_command, options, callback) { + if(this._state == 'connected' || this._state == 'disconnected') this._state = 'connecting'; + // Number of retries done + var numberOfRetriesDone = numberOfTimes; + // Retry function, execute once + var retryFunction = function(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback) { + _self.serverConfig.connect(_self, {}, function(err, result, _serverConfig) { + // Adjust the number of retries left + _numberOfRetriesDone = _numberOfRetriesDone - 1; + // Definitively restart + if(err != null && _numberOfRetriesDone > 0) { + _self._state = 'connecting'; + // Close the server config + _serverConfig.close(function(err) { + // Retry the connect + setTimeout(function() { + retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback); + }, _retryInMilliseconds); + }); + } else if(err != null && _numberOfRetriesDone <= 0) { + _self._state = 'disconnected'; + // Force close the current connections + _serverConfig.close(function(_err) { + // Force close the current connections + if(typeof _callback == 'function') _callback(err, null); + }); + } else if(err == null && _self.serverConfig.isConnected() == true && Array.isArray(_self.auths) && _self.auths.length > 0) { + _self._state = 'connected'; + // Get number of auths we need to execute + var numberOfAuths = _self.auths.length; + // Apply all auths + for(var i = 0; i < _self.auths.length; i++) { + _self.authenticate(_self.auths[i].username, _self.auths[i].password, {'authdb':_self.auths[i].authdb}, function(err, authenticated) { + numberOfAuths = numberOfAuths - 1; + + // If we have no more authentications to replay + if(numberOfAuths == 0) { + if(err != null || !authenticated) { + if(typeof _callback == 'function') _callback(err, null); + return; + } else { + // Execute command + command(_self, _db_command, _options, _callback); + // Execute all the commands + if(_self.commands.length > 0) _execute_queued_command(_self); + } + } + }); + } + } else if(err == null && _self.serverConfig.isConnected() == true) { + _self._state = 'connected'; + // Execute command + command(_self, _db_command, _options, _callback); + + process.nextTick(function() { + // Execute any backed up commands + while(_self.commands.length > 0) { + // Fetch the command + var command = _self.commands.shift(); + // Execute based on type + if(command['type'] == 'query') { + __executeQueryCommand(_self, command['db_command'], command['options'], command['callback']); + } else if(command['type'] == 'insert') { + __executeInsertCommand(_self, command['db_command'], command['options'], command['callback']); + } + } + }); + } else { + _self._state = 'connecting'; + // Force close the current connections + _serverConfig.close(function(err) { + // _self.serverConfig.close(function(err) { + // Retry the connect + setTimeout(function() { + retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback); + }, _retryInMilliseconds); + }); + } + }); + }; + + // Execute function first time + retryFunction(self, numberOfRetriesDone, retryInMilliseconds, numberOfTimes, command, db_command, options, callback); +} + +/** + * Execute db query command (not safe) + * @ignore + * @api private + */ +Db.prototype._executeQueryCommand = function(db_command, options, callback) { + var self = this; + + // Unpack the parameters + if (typeof callback === 'undefined') { + callback = options; + options = {}; + } + + // fast fail option used for HA, no retry + var failFast = options['failFast'] != null + ? options['failFast'] + : false; + + // Check if the user force closed the command + if(this._applicationClosed) { + var err = new Error("db closed by application"); + if('function' == typeof callback) { + return callback(err, null); + } else { + throw err; + } + } + + var config = this.serverConfig; + // If the pool is not connected, attemp to reconnect to send the message + if(this._state == 'connecting' && config.autoReconnect && !failFast) { + return process.nextTick(function() { + self.commands.push({ + type: 'query', + db_command: db_command, + options: options, + callback: callback + }); + }) + } + + if(!failFast && !config.isConnected(options.read) && config.autoReconnect + && (options.read == null + || options.read == false + || options.read == ReadPreference.PRIMARY + || config.checkoutReader() == null)) { + this._state = 'connecting'; + return __retryCommandOnFailure(this, + this.retryMiliSeconds, + this.numberOfRetries, + __executeQueryCommand, + db_command, + options, + callback); + } + + if(!config.isConnected(options.read) && !config.autoReconnect && callback) { + // Fire an error to the callback if we are not connected + // and don't reconnect. + return callback(new Error("no open connections"), null); + } + + __executeQueryCommand(self, db_command, options, function (err, result, conn) { + callback(err, result, conn); + }); + +}; + +/** + * @ignore + */ +var __executeInsertCommand = function(self, db_command, options, callback) { + // Always checkout a writer for this kind of operations + var connection = self.serverConfig.checkoutWriter(); + // Get safe mode + var safe = options['safe'] != null ? options['safe'] : false; + var raw = options['raw'] != null ? options['raw'] : self.raw; + var specifiedConnection = options['connection'] != null ? options['connection'] : null; + // Override connection if needed + connection = specifiedConnection != null ? specifiedConnection : connection; + + // Ensure we have a valid connection + if(typeof callback === 'function') { + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error) { + return callback(connection); + } + + var errorOptions = _getWriteConcern(self, options, callback); + if(errorOptions.w > 0 || errorOptions.w == 'majority' || errorOptions.j || errorOptions.journal || errorOptions.fsync) { + // db command is now an array of commands (original command + lastError) + db_command = [db_command, DbCommand.createGetLastErrorCommand(safe, self)]; + // Register the handler in the data structure + self._registerHandler(db_command[1], raw, connection, callback); + } + } + + // If we have no callback and there is no connection + if(connection == null) return null; + if(connection instanceof Error && typeof callback == 'function') return callback(connection, null); + if(connection instanceof Error) return null; + if(connection == null && typeof callback == 'function') return callback(new Error("no primary server found"), null); + + // Write the message out + connection.write(db_command, function(err) { + // Return the callback if it's not a safe operation and the callback is defined + if(typeof callback === 'function' && (safe == null || safe == false)) { + // Perform the callback + callback(err, null); + } else if(typeof callback === 'function') { + // Call the handler with an error + self._callHandler(db_command[1].getRequestId(), null, err); + } else if(typeof callback == 'function' && safe && safe.w == -1) { + // Call the handler with no error + self._callHandler(db_command[1].getRequestId(), null, null); + } else if(!safe && safe.w == -1) { + self.emit("error", err); + } + }); +} + +/** + * Execute an insert Command + * @ignore + * @api private + */ +Db.prototype._executeInsertCommand = function(db_command, options, callback) { + var self = this; + + // Unpack the parameters + if(callback == null && typeof options === 'function') { + callback = options; + options = {}; + } + + // Ensure options are not null + options = options == null ? {} : options; + + // Check if the user force closed the command + if(this._applicationClosed) { + if(typeof callback == 'function') { + return callback(new Error("db closed by application"), null); + } else { + throw new Error("db closed by application"); + } + } + + // If the pool is not connected, attemp to reconnect to send the message + if(self._state == 'connecting' && this.serverConfig.autoReconnect) { + process.nextTick(function() { + self.commands.push({type:'insert', 'db_command':db_command, 'options':options, 'callback':callback}); + }) + } else if(!this.serverConfig.isConnected() && this.serverConfig.autoReconnect) { + this._state = 'connecting'; + // Retry command + __retryCommandOnFailure(this, this.retryMiliSeconds, this.numberOfRetries, __executeInsertCommand, db_command, options, callback); + } else if(!this.serverConfig.isConnected() && !this.serverConfig.autoReconnect && callback) { + // Fire an error to the callback if we are not connected and don't do reconnect + if(callback) callback(new Error("no open connections"), null); + } else { + __executeInsertCommand(self, db_command, options, callback); + } +} + +/** + * Update command is the same + * @ignore + * @api private + */ +Db.prototype._executeUpdateCommand = Db.prototype._executeInsertCommand; +/** + * Remove command is the same + * @ignore + * @api private + */ +Db.prototype._executeRemoveCommand = Db.prototype._executeInsertCommand; + +/** + * Wrap a Mongo error document into an Error instance + * @ignore + * @api private + */ +Db.prototype.wrap = function(error) { + var msg = error.err || error.errmsg || error; + var e = new Error(msg); + e.name = 'MongoError'; + + // Get all object keys + var keys = typeof error == 'object' ? Object.keys(error) : []; + // Populate error object with properties + for(var i = 0; i < keys.length; i++) { + e[keys[i]] = error[keys[i]]; + } + + return e; +} + +/** + * Default URL + * + * @classconstant DEFAULT_URL + **/ +Db.DEFAULT_URL = 'mongodb://localhost:27017/default'; + +/** + * Connect to MongoDB using a url as documented at + * + * www.mongodb.org/display/DOCS/Connections + * + * Options + * - **uri_decode_auth** {Boolean, default:false} uri decode the user name and password for authentication + * - **db** {Object, default: null} a hash off options to set on the db object, see **Db constructor** + * - **server** {Object, default: null} a hash off options to set on the server objects, see **Server** constructor** + * - **replSet** {Object, default: null} a hash off options to set on the replSet object, see **ReplSet** constructor** + * - **mongos** {Object, default: null} a hash off options to set on the mongos object, see **Mongos** constructor** + * + * @param {String} url connection url for MongoDB. + * @param {Object} [options] optional options for insert command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the db instance or null if an error occured. + * @return {null} + * @api public + */ +Db.connect = function(url, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = typeof args[args.length - 1] == 'function' ? args.pop() : null; + options = args.length ? args.shift() : null; + options = options || {}; + var serverOptions = options.server || {}; + var mongosOptions = options.mongos || {}; + var replSetServersOptions = options.replSet || options.replSetServers || {}; + var dbOptions = options.db || {}; + + // If callback is null throw an exception + if(callback == null) throw new Error("no callback function provided"); + + // Parse the string + var object = parse(url); + // Merge in any options for db in options object + if(dbOptions) { + for(var name in dbOptions) object.db_options[name] = dbOptions[name]; + } + + // Merge in any options for server in options object + if(serverOptions) { + for(var name in serverOptions) object.server_options[name] = serverOptions[name]; + } + + // Merge in any replicaset server options + if(replSetServersOptions) { + for(var name in replSetServersOptions) object.rs_options[name] = replSetServersOptions[name]; + } + + // Merge in any replicaset server options + if(mongosOptions) { + for(var name in mongosOptions) object.mongos_options[name] = mongosOptions[name]; + } + + // We need to ensure that the list of servers are only either direct members or mongos + // they cannot be a mix of monogs and mongod's + var totalNumberOfServers = object.servers.length; + var totalNumberOfMongosServers = 0; + var totalNumberOfMongodServers = 0; + var serverConfig = null; + + // Failure modes + if(object.servers.length == 0) throw new Error("connection string must contain at least one seed host"); + + // If we have no db setting for the native parser try to set the c++ one first + object.db_options.native_parser = _setNativeParser(object.db_options); + // If no auto_reconnect is set, set it to true as default for single servers + if(typeof object.server_options.auto_reconnect != 'boolean') { + object.server_options.auto_reconnect = true; + } + + // If we have more than a server, it could be replicaset or mongos list + // need to verify that it's one or the other and fail if it's a mix + // Connect to all servers and run ismaster + for(var i = 0; i < object.servers.length; i++) { + // Set up the Server object + var _server = object.servers[i].domain_socket + ? new Server(object.servers[i].domain_socket, {socketOptions:{connectTimeoutMS:1000}, auto_reconnect:false}) + : new Server(object.servers[i].host, object.servers[i].port, {socketOptions:{connectTimeoutMS:1000}, auto_reconnect:false}); + + // Attempt connect + new Db(object.dbName, _server, {safe:false, native_parser:false}).open(function(err, db) { + // Update number of servers + totalNumberOfServers = totalNumberOfServers - 1; + // If no error do the correct checks + if(!err) { + // Close the connection + db.close(true); + var isMasterDoc = db.serverConfig.isMasterDoc; + // Check what type of server we have + if(isMasterDoc.setName) totalNumberOfMongodServers++; + if(isMasterDoc.msg && isMasterDoc.msg == "isdbgrid") totalNumberOfMongosServers++; + } + + if(totalNumberOfServers == 0) { + // If we have a mix of mongod and mongos, throw an error + if(totalNumberOfMongosServers > 0 && totalNumberOfMongodServers > 0) + return callback(new Error("cannot combine a list of replicaset seeds and mongos seeds")); + + if(totalNumberOfMongodServers == 0 && object.servers.length == 1) { + var obj = object.servers[0]; + serverConfig = obj.domain_socket ? + new Server(obj.domain_socket, object.server_options) + : new Server(obj.host, obj.port, object.server_options); + } else if(totalNumberOfMongodServers > 0) { + serverConfig = new ReplSet(object.servers.map(function(serverObj) { + return new Server(serverObj.host, serverObj.port, object.server_options); + }), object.rs_options); + } else if(totalNumberOfMongosServers > 0) { + serverConfig = new Mongos(object.servers.map(function(serverObj) { + return new Server(serverObj.host, serverObj.port, object.server_options); + }), object.mongos_options); + } + + if(serverConfig == null) return callback(new Error("Could not locate any valid servers in initial seed list")); + // Set up all options etc and connect to the database + _finishConnecting(serverConfig, object, options, callback) + } + }); + } +} + +var _setNativeParser = function(db_options) { + if(typeof db_options.native_parser == 'boolean') return db_options.native_parser; + + try { + require('bson').BSONNative.BSON; + return true; + } catch(err) { + return false; + } +} + +var _finishConnecting = function(serverConfig, object, options, callback) { + // Safe settings + var safe = {}; + // Build the safe parameter if needed + if(object.db_options.journal) safe.j = object.db_options.journal; + if(object.db_options.w) safe.w = object.db_options.w; + if(object.db_options.fsync) safe.fsync = object.db_options.fsync; + if(object.db_options.wtimeoutMS) safe.wtimeout = object.db_options.wtimeoutMS; + + // If we have a read Preference set + if(object.db_options.read_preference) { + var readPreference = new ReadPreference(object.db_options.read_preference); + // If we have the tags set up + if(object.db_options.read_preference_tags) + readPreference = new ReadPreference(object.db_options.read_preference, object.db_options.read_preference_tags); + // Add the read preference + object.db_options.readPreference = readPreference; + } + + // No safe mode if no keys + if(Object.keys(safe).length == 0) safe = false; + // Add the safe object + object.db_options.safe = safe; + // Set up the db options + var db = new Db(object.dbName, serverConfig, object.db_options); + // Don't open the connection + if(options.noOpen) return db; + + // Open the db + db.open(function(err, db){ + if(err == null && object.auth){ + db.authenticate(object.auth.user, object.auth.password, function(err, success){ + if(success){ + callback(null, db); + } else { + callback(err ? err : new Error('Could not authenticate user ' + auth[0]), db); + } + }); + } else { + callback(err, db); + } + }); +} + +/** + * State of the db connection + * @ignore + */ +Object.defineProperty(Db.prototype, "state", { enumerable: true + , get: function () { + return this.serverConfig._serverState; + } +}); + +/** + * @ignore + */ +var _hasWriteConcern = function(errorOptions) { + return errorOptions == true + || errorOptions.w > 0 + || errorOptions.w == 'majority' + || errorOptions.j == true + || errorOptions.journal == true + || errorOptions.fsync == true +} + +/** + * @ignore + */ +var _setWriteConcernHash = function(options) { + var finalOptions = {}; + if(options.w != null) finalOptions.w = options.w; + if(options.journal == true) finalOptions.j = options.journal; + if(options.j == true) finalOptions.j = options.j; + if(options.fsync == true) finalOptions.fsync = options.fsync; + if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout; + return finalOptions; +} + +/** + * @ignore + */ +var _getWriteConcern = function(self, options, callback) { + // Final options + var finalOptions = {w:1}; + // Local options verification + if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(options); + } else if(options.safe != null && typeof options.safe == 'object') { + finalOptions = _setWriteConcernHash(options.safe); + } else if(typeof options.safe == "boolean") { + finalOptions = {w: (options.safe ? 1 : 0)}; + } else if(self.options.w != null || typeof self.options.j == 'boolean' || typeof self.options.journal == 'boolean' || typeof self.options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.options); + } else if(self.safe.w != null || typeof self.safe.j == 'boolean' || typeof self.safe.journal == 'boolean' || typeof self.safe.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.safe); + } else if(typeof self.safe == "boolean") { + finalOptions = {w: (self.safe ? 1 : 0)}; + } + + // Ensure we don't have an invalid combination of write concerns + if(finalOptions.w < 1 + && (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw new Error("No acknowlegement using w < 1 cannot be combined with journal:true or fsync:true"); + + // Return the options + return finalOptions; +} + +/** + * Legacy support + * + * @ignore + * @api private + */ +exports.connect = Db.connect; +exports.Db = Db; + +/** + * Remove all listeners to the db instance. + * @ignore + * @api private + */ +Db.prototype.removeAllEventListeners = function() { + this.removeAllListeners("close"); + this.removeAllListeners("error"); + this.removeAllListeners("timeout"); + this.removeAllListeners("parseError"); + this.removeAllListeners("poolReady"); + this.removeAllListeners("message"); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/chunk.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/chunk.js new file mode 100644 index 0000000..572d144 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/chunk.js @@ -0,0 +1,213 @@ +var Binary = require('bson').Binary, + ObjectID = require('bson').ObjectID; + +/** + * Class for representing a single chunk in GridFS. + * + * @class + * + * @param file {GridStore} The {@link GridStore} object holding this chunk. + * @param mongoObject {object} The mongo object representation of this chunk. + * + * @throws Error when the type of data field for {@link mongoObject} is not + * supported. Currently supported types for data field are instances of + * {@link String}, {@link Array}, {@link Binary} and {@link Binary} + * from the bson module + * + * @see Chunk#buildMongoObject + */ +var Chunk = exports.Chunk = function(file, mongoObject) { + if(!(this instanceof Chunk)) return new Chunk(file, mongoObject); + + this.file = file; + var self = this; + var mongoObjectFinal = mongoObject == null ? {} : mongoObject; + + this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id; + this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n; + this.data = new Binary(); + + if(mongoObjectFinal.data == null) { + } else if(typeof mongoObjectFinal.data == "string") { + var buffer = new Buffer(mongoObjectFinal.data.length); + buffer.write(mongoObjectFinal.data, 'binary', 0); + this.data = new Binary(buffer); + } else if(Array.isArray(mongoObjectFinal.data)) { + var buffer = new Buffer(mongoObjectFinal.data.length); + buffer.write(mongoObjectFinal.data.join(''), 'binary', 0); + this.data = new Binary(buffer); + } else if(mongoObjectFinal.data instanceof Binary || Object.prototype.toString.call(mongoObjectFinal.data) == "[object Binary]") { + this.data = mongoObjectFinal.data; + } else if(Buffer.isBuffer(mongoObjectFinal.data)) { + } else { + throw Error("Illegal chunk format"); + } + // Update position + this.internalPosition = 0; +}; + +/** + * Writes a data to this object and advance the read/write head. + * + * @param data {string} the data to write + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + */ +Chunk.prototype.write = function(data, callback) { + this.data.write(data, this.internalPosition); + this.internalPosition = this.data.length(); + if(callback != null) return callback(null, this); + return this; +}; + +/** + * Reads data and advances the read/write head. + * + * @param length {number} The length of data to read. + * + * @return {string} The data read if the given length will not exceed the end of + * the chunk. Returns an empty String otherwise. + */ +Chunk.prototype.read = function(length) { + // Default to full read if no index defined + length = length == null || length == 0 ? this.length() : length; + + if(this.length() - this.internalPosition + 1 >= length) { + var data = this.data.read(this.internalPosition, length); + this.internalPosition = this.internalPosition + length; + return data; + } else { + return ''; + } +}; + +Chunk.prototype.readSlice = function(length) { + if ((this.length() - this.internalPosition) >= length) { + var data = null; + if (this.data.buffer != null) { //Pure BSON + data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length); + } else { //Native BSON + data = new Buffer(length); + length = this.data.readInto(data, this.internalPosition); + } + this.internalPosition = this.internalPosition + length; + return data; + } else { + return null; + } +}; + +/** + * Checks if the read/write head is at the end. + * + * @return {boolean} Whether the read/write head has reached the end of this + * chunk. + */ +Chunk.prototype.eof = function() { + return this.internalPosition == this.length() ? true : false; +}; + +/** + * Reads one character from the data of this chunk and advances the read/write + * head. + * + * @return {string} a single character data read if the the read/write head is + * not at the end of the chunk. Returns an empty String otherwise. + */ +Chunk.prototype.getc = function() { + return this.read(1); +}; + +/** + * Clears the contents of the data in this chunk and resets the read/write head + * to the initial position. + */ +Chunk.prototype.rewind = function() { + this.internalPosition = 0; + this.data = new Binary(); +}; + +/** + * Saves this chunk to the database. Also overwrites existing entries having the + * same id as this chunk. + * + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + */ +Chunk.prototype.save = function(callback) { + var self = this; + + self.file.chunkCollection(function(err, collection) { + if(err) return callback(err); + + collection.remove({'_id':self.objectId}, {safe:true}, function(err, result) { + if(err) return callback(err); + + if(self.data.length() > 0) { + self.buildMongoObject(function(mongoObject) { + collection.insert(mongoObject, {safe:true}, function(err, collection) { + callback(err, self); + }); + }); + } else { + callback(null, self); + } + }); + }); +}; + +/** + * Creates a mongoDB object representation of this chunk. + * + * @param callback {function(Object)} This will be called after executing this + * method. The object will be passed to the first parameter and will have + * the structure: + * + *

+ *        {
+ *          '_id' : , // {number} id for this chunk
+ *          'files_id' : , // {number} foreign key to the file collection
+ *          'n' : , // {number} chunk number
+ *          'data' : , // {bson#Binary} the chunk data itself
+ *        }
+ *        
+ * + * @see MongoDB GridFS Chunk Object Structure + */ +Chunk.prototype.buildMongoObject = function(callback) { + var mongoObject = {'_id': this.objectId, + 'files_id': this.file.fileId, + 'n': this.chunkNumber, + 'data': this.data}; + callback(mongoObject); +}; + +/** + * @return {number} the length of the data + */ +Chunk.prototype.length = function() { + return this.data.length(); +}; + +/** + * The position of the read/write head + * @name position + * @lends Chunk# + * @field + */ +Object.defineProperty(Chunk.prototype, "position", { enumerable: true + , get: function () { + return this.internalPosition; + } + , set: function(value) { + this.internalPosition = value; + } +}); + +/** + * The default chunk size + * @constant + */ +Chunk.DEFAULT_CHUNK_SIZE = 1024 * 256; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/grid.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/grid.js new file mode 100644 index 0000000..aa695b7 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/grid.js @@ -0,0 +1,103 @@ +var GridStore = require('./gridstore').GridStore, + ObjectID = require('bson').ObjectID; + +/** + * A class representation of a simple Grid interface. + * + * @class Represents the Grid. + * @param {Db} db A database instance to interact with. + * @param {String} [fsName] optional different root collection for GridFS. + * @return {Grid} + */ +function Grid(db, fsName) { + + if(!(this instanceof Grid)) return new Grid(db, fsName); + + this.db = db; + this.fsName = fsName == null ? GridStore.DEFAULT_ROOT_COLLECTION : fsName; +} + +/** + * Puts binary data to the grid + * + * Options + * - **_id** {Any}, unique id for this file + * - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * - **content_type** {String}, mime type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**. + * - **chunk_size** {Number}, size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**. + * - **metadata** {Object}, arbitrary data the user wants to store. + * + * @param {Buffer} data buffer with Binary Data. + * @param {Object} [options] the options for the files. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.put = function(data, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + // If root is not defined add our default one + options['root'] = options['root'] == null ? this.fsName : options['root']; + + // Return if we don't have a buffer object as data + if(!(Buffer.isBuffer(data))) return callback(new Error("Data object must be a buffer object"), null); + // Get filename if we are using it + var filename = options['filename'] || null; + // Get id if we are using it + var id = options['_id'] || null; + // Create gridstore + var gridStore = new GridStore(this.db, id, filename, "w", options); + gridStore.open(function(err, gridStore) { + if(err) return callback(err, null); + + gridStore.write(data, function(err, result) { + if(err) return callback(err, null); + + gridStore.close(function(err, result) { + if(err) return callback(err, null); + callback(null, result); + }) + }) + }) +} + +/** + * Get binary data to the grid + * + * @param {Any} id for file. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.get = function(id, callback) { + // Create gridstore + var gridStore = new GridStore(this.db, id, null, "r", {root:this.fsName}); + gridStore.open(function(err, gridStore) { + if(err) return callback(err, null); + + // Return the data + gridStore.read(function(err, data) { + return callback(err, data) + }); + }) +} + +/** + * Delete file from grid + * + * @param {Any} id for file. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.delete = function(id, callback) { + // Create gridstore + GridStore.unlink(this.db, id, {root:this.fsName}, function(err, result) { + if(err) return callback(err, false); + return callback(null, true); + }); +} + +exports.Grid = Grid; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js new file mode 100644 index 0000000..586a491 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js @@ -0,0 +1,1486 @@ +/** + * @fileOverview GridFS is a tool for MongoDB to store files to the database. + * Because of the restrictions of the object size the database can hold, a + * facility to split a file into several chunks is needed. The {@link GridStore} + * class offers a simplified api to interact with files while managing the + * chunks of split files behind the scenes. More information about GridFS can be + * found here. + */ +var Chunk = require('./chunk').Chunk, + DbCommand = require('../commands/db_command').DbCommand, + ObjectID = require('bson').ObjectID, + Buffer = require('buffer').Buffer, + fs = require('fs'), + util = require('util'), + inherits = util.inherits, + ReadStream = require('./readstream').ReadStream, + Stream = require('stream'); + +var REFERENCE_BY_FILENAME = 0, + REFERENCE_BY_ID = 1; + +/** + * A class representation of a file stored in GridFS. + * + * Modes + * - **"r"** - read only. This is the default mode. + * - **"w"** - write in truncate mode. Existing data will be overwriten. + * - **w+"** - write in edit mode. + * + * Options + * - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * - **content_type** {String}, mime type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**. + * - **chunk_size** {Number}, size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**. + * - **metadata** {Object}, arbitrary data the user wants to store. + * + * @class Represents the GridStore. + * @param {Db} db A database instance to interact with. + * @param {Any} [id] optional unique id for this file + * @param {String} [filename] optional filename for this file, no unique constrain on the field + * @param {String} mode set the mode for this file. + * @param {Object} options optional properties to specify. + * @return {GridStore} + */ +var GridStore = function GridStore(db, id, filename, mode, options) { + if(!(this instanceof GridStore)) return new GridStore(db, id, filename, mode, options); + + var self = this; + this.db = db; + + // Call stream constructor + if(typeof Stream == 'function') { + Stream.call(this); + } else { + // 0.4.X backward compatibility fix + Stream.Stream.call(this); + } + + // Handle options + if(typeof options === 'undefined') options = {}; + // Handle mode + if(typeof mode === 'undefined') { + mode = filename; + filename = undefined; + } else if(typeof mode == 'object') { + options = mode; + mode = filename; + filename = undefined; + } + + if(id instanceof ObjectID) { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } else if(typeof filename == 'undefined') { + this.referenceBy = REFERENCE_BY_FILENAME; + this.filename = id; + if (mode.indexOf('w') != null) { + this.fileId = new ObjectID(); + } + } else { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } + + /* + // Handle id + if(id instanceof ObjectID && (typeof filename == 'string' || filename == null)) { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("w") != null) { + this.referenceBy = REFERENCE_BY_FILENAME; + this.fileId = new ObjectID(); + this.filename = id; + } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("r") != null) { + this.referenceBy = REFERENCE_BY_FILENAME; + this.filename = id; + } else { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } + */ + + // Set up the rest + this.mode = mode == null ? "r" : mode; + this.options = options == null ? {} : options; + this.root = this.options['root'] == null ? exports.GridStore.DEFAULT_ROOT_COLLECTION : this.options['root']; + this.position = 0; + // Set default chunk size + this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize']; +} + +/** + * Code for the streaming capabilities of the gridstore object + * Most code from Aaron heckmanns project https://github.com/aheckmann/gridfs-stream + * Modified to work on the gridstore object itself + * @ignore + */ +if(typeof Stream == 'function') { + GridStore.prototype = { __proto__: Stream.prototype } +} else { + // Node 0.4.X compatibility code + GridStore.prototype = { __proto__: Stream.Stream.prototype } +} + +// Move pipe to _pipe +GridStore.prototype._pipe = GridStore.prototype.pipe; + +/** + * Opens the file from the database and initialize this object. Also creates a + * new one if file does not exist. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an **{Error}** object and the second parameter will be null if an error occured. Otherwise, the first parameter will be null and the second will contain the reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.open = function(callback) { + if( this.mode != "w" && this.mode != "w+" && this.mode != "r"){ + callback(new Error("Illegal mode " + this.mode), null); + return; + } + + var self = this; + + if((self.mode == "w" || self.mode == "w+") && self.db.serverConfig.primary != null) { + // Get files collection + self.collection(function(err, collection) { + if(err) return callback(err); + + // Put index on filename + collection.ensureIndex([['filename', 1]], function(err, index) { + if(err) return callback(err); + + // Get chunk collection + self.chunkCollection(function(err, chunkCollection) { + if(err) return callback(err); + + // Ensure index on chunk collection + chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) { + if(err) return callback(err); + _open(self, callback); + }); + }); + }); + }); + } else { + // Open the gridstore + _open(self, callback); + } +}; + +/** + * Hidding the _open function + * @ignore + * @api private + */ +var _open = function(self, callback) { + self.collection(function(err, collection) { + if(err!==null) { + callback(new Error("at collection: "+err), null); + return; + } + + // Create the query + var query = self.referenceBy == REFERENCE_BY_ID ? {_id:self.fileId} : {filename:self.filename}; + query = null == self.fileId && this.filename == null ? null : query; + + // Fetch the chunks + if(query != null) { + collection.find(query, function(err, cursor) { + if(err) return error(err); + + // Fetch the file + cursor.nextObject(function(err, doc) { + if(err) return error(err); + + // Check if the collection for the files exists otherwise prepare the new one + if(doc != null) { + self.fileId = doc._id; + self.filename = doc.filename; + self.contentType = doc.contentType; + self.internalChunkSize = doc.chunkSize; + self.uploadDate = doc.uploadDate; + self.aliases = doc.aliases; + self.length = doc.length; + self.metadata = doc.metadata; + self.internalMd5 = doc.md5; + } else if (self.mode != 'r') { + self.fileId = self.fileId == null ? new ObjectID() : self.fileId; + self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE; + self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize; + self.length = 0; + } else { + self.length = 0; + var txtId = self.fileId instanceof ObjectID ? self.fileId.toHexString() : self.fileId; + return error(new Error((self.referenceBy == REFERENCE_BY_ID ? txtId : self.filename) + " does not exist", self)); + } + + // Process the mode of the object + if(self.mode == "r") { + nthChunk(self, 0, function(err, chunk) { + if(err) return error(err); + self.currentChunk = chunk; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w") { + // Delete any existing chunks + deleteChunks(self, function(err, result) { + if(err) return error(err); + self.currentChunk = new Chunk(self, {'n':0}); + self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type']; + self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size']; + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w+") { + nthChunk(self, lastChunkNumber(self), function(err, chunk) { + if(err) return error(err); + // Set the current chunk + self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk; + self.currentChunk.position = self.currentChunk.data.length(); + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = self.length; + callback(null, self); + }); + } + }); + }); + } else { + // Write only mode + self.fileId = null == self.fileId ? new ObjectID() : self.fileId; + self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE; + self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize; + self.length = 0; + + self.chunkCollection(function(err, collection2) { + if(err) return error(err); + + // No file exists set up write mode + if(self.mode == "w") { + // Delete any existing chunks + deleteChunks(self, function(err, result) { + if(err) return error(err); + self.currentChunk = new Chunk(self, {'n':0}); + self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type']; + self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size']; + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w+") { + nthChunk(self, lastChunkNumber(self), function(err, chunk) { + if(err) return error(err); + // Set the current chunk + self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk; + self.currentChunk.position = self.currentChunk.data.length(); + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = self.length; + callback(null, self); + }); + } + }); + } + }); + + // only pass error to callback once + function error (err) { + if(error.err) return; + callback(error.err = err); + } +}; + +/** + * Stores a file from the file system to the GridFS database. + * + * @param {String|Buffer|FileHandle} file the file to store. + * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the the second will contain the reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.writeFile = function (file, callback) { + var self = this; + if (typeof file === 'string') { + fs.open(file, 'r', 0666, function (err, fd) { + if(err) return callback(err); + self.writeFile(fd, callback); + }); + return; + } + + self.open(function (err, self) { + if(err) return callback(err); + + fs.fstat(file, function (err, stats) { + if(err) return callback(err); + + var offset = 0; + var index = 0; + var numberOfChunksLeft = Math.min(stats.size / self.chunkSize); + + // Write a chunk + var writeChunk = function() { + fs.read(file, self.chunkSize, offset, 'binary', function(err, data, bytesRead) { + if(err) return callback(err); + + offset = offset + bytesRead; + + // Create a new chunk for the data + var chunk = new Chunk(self, {n:index++}); + chunk.write(data, function(err, chunk) { + if(err) return callback(err); + + chunk.save(function(err, result) { + if(err) return callback(err); + + self.position = self.position + data.length; + + // Point to current chunk + self.currentChunk = chunk; + + if(offset >= stats.size) { + fs.close(file); + self.close(callback); + } else { + return process.nextTick(writeChunk); + } + }); + }); + }); + } + + // Process the first write + process.nextTick(writeChunk); + }); + }); +}; + +/** + * Writes some data. This method will work properly only if initialized with mode + * "w" or "w+". + * + * @param string {string} The data to write. + * @param close {boolean=false} opt_argument Closes this file after writing if + * true. + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + * + * @ignore + * @api private + */ +var writeBuffer = function(self, buffer, close, callback) { + if(typeof close === "function") { callback = close; close = null; } + var finalClose = (close == null) ? false : close; + + if(self.mode[0] != "w") { + callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null); + } else { + if(self.currentChunk.position + buffer.length >= self.chunkSize) { + // Write out the current Chunk and then keep writing until we have less data left than a chunkSize left + // to a new chunk (recursively) + var previousChunkNumber = self.currentChunk.chunkNumber; + var leftOverDataSize = self.chunkSize - self.currentChunk.position; + var firstChunkData = buffer.slice(0, leftOverDataSize); + var leftOverData = buffer.slice(leftOverDataSize); + // A list of chunks to write out + var chunksToWrite = [self.currentChunk.write(firstChunkData)]; + // If we have more data left than the chunk size let's keep writing new chunks + while(leftOverData.length >= self.chunkSize) { + // Create a new chunk and write to it + var newChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}); + var firstChunkData = leftOverData.slice(0, self.chunkSize); + leftOverData = leftOverData.slice(self.chunkSize); + // Update chunk number + previousChunkNumber = previousChunkNumber + 1; + // Write data + newChunk.write(firstChunkData); + // Push chunk to save list + chunksToWrite.push(newChunk); + } + + // Set current chunk with remaining data + self.currentChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}); + // If we have left over data write it + if(leftOverData.length > 0) self.currentChunk.write(leftOverData); + + // Update the position for the gridstore + self.position = self.position + buffer.length; + // Total number of chunks to write + var numberOfChunksToWrite = chunksToWrite.length; + // Write out all the chunks and then return + for(var i = 0; i < chunksToWrite.length; i++) { + var chunk = chunksToWrite[i]; + chunk.save(function(err, result) { + if(err) return callback(err); + + numberOfChunksToWrite = numberOfChunksToWrite - 1; + + if(numberOfChunksToWrite <= 0) { + return callback(null, self); + } + }) + } + } else { + // Update the position for the gridstore + self.position = self.position + buffer.length; + // We have less data than the chunk size just write it and callback + self.currentChunk.write(buffer); + callback(null, self); + } + } +}; + +/** + * Creates a mongoDB object representation of this object. + * + * @param callback {function(object)} This will be called after executing this + * method. The object will be passed to the first parameter and will have + * the structure: + * + *

+ *        {
+ *          '_id' : , // {number} id for this file
+ *          'filename' : , // {string} name for this file
+ *          'contentType' : , // {string} mime type for this file
+ *          'length' : , // {number} size of this file?
+ *          'chunksize' : , // {number} chunk size used by this file
+ *          'uploadDate' : , // {Date}
+ *          'aliases' : , // {array of string}
+ *          'metadata' : , // {string}
+ *        }
+ *        
+ * + * @ignore + * @api private + */ +var buildMongoObject = function(self, callback) { + // // Keeps the final chunk number + // var chunkNumber = 0; + // var previousChunkSize = 0; + // // Get the correct chunk Number, if we have an empty chunk return the previous chunk number + // if(null != self.currentChunk && self.currentChunk.chunkNumber > 0 && self.currentChunk.position == 0) { + // chunkNumber = self.currentChunk.chunkNumber - 1; + // } else { + // chunkNumber = self.currentChunk.chunkNumber; + // previousChunkSize = self.currentChunk.position; + // } + + // // Calcuate the length + // var length = self.currentChunk != null ? (chunkNumber * self.chunkSize + previousChunkSize) : 0; + var mongoObject = { + '_id': self.fileId, + 'filename': self.filename, + 'contentType': self.contentType, + 'length': self.position ? self.position : 0, + 'chunkSize': self.chunkSize, + 'uploadDate': self.uploadDate, + 'aliases': self.aliases, + 'metadata': self.metadata + }; + + var md5Command = {filemd5:self.fileId, root:self.root}; + self.db.command(md5Command, function(err, results) { + mongoObject.md5 = results.md5; + callback(mongoObject); + }); +}; + +/** + * Saves this file to the database. This will overwrite the old entry if it + * already exists. This will work properly only if mode was initialized to + * "w" or "w+". + * + * @param {Function} callback this will be called after executing this method. Passes an **{Error}** object to the first parameter and null to the second if an error occured. Otherwise, passes null to the first and a reference to this object to the second. + * @return {null} + * @api public + */ +GridStore.prototype.close = function(callback) { + var self = this; + + if(self.mode[0] == "w") { + if(self.currentChunk != null && self.currentChunk.position > 0) { + self.currentChunk.save(function(err, chunk) { + if(err) return callback(err); + + self.collection(function(err, files) { + if(err) return callback(err); + + // Build the mongo object + if(self.uploadDate != null) { + files.remove({'_id':self.fileId}, {safe:true}, function(err, collection) { + if(err) return callback(err); + + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + }); + } else { + self.uploadDate = new Date(); + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + } + }); + }); + } else { + self.collection(function(err, files) { + if(err) return callback(err); + + self.uploadDate = new Date(); + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + }); + } + } else if(self.mode[0] == "r") { + callback(null, null); + } else { + callback(new Error("Illegal mode " + self.mode), null); + } +}; + +/** + * Gets the nth chunk of this file. + * + * @param chunkNumber {number} The nth chunk to retrieve. + * @param callback {function(*, Chunk|object)} This will be called after + * executing this method. null will be passed to the first parameter while + * a new {@link Chunk} instance will be passed to the second parameter if + * the chunk was found or an empty object {} if not. + * + * @ignore + * @api private + */ +var nthChunk = function(self, chunkNumber, callback) { + self.chunkCollection(function(err, collection) { + if(err) return callback(err); + + collection.find({'files_id':self.fileId, 'n':chunkNumber}, function(err, cursor) { + if(err) return callback(err); + + cursor.nextObject(function(err, chunk) { + if(err) return callback(err); + + var finalChunk = chunk == null ? {} : chunk; + callback(null, new Chunk(self, finalChunk)); + }); + }); + }); +}; + +/** + * + * @ignore + * @api private + */ +GridStore.prototype._nthChunk = function(chunkNumber, callback) { + nthChunk(this, chunkNumber, callback); +} + +/** + * @return {Number} The last chunk number of this file. + * + * @ignore + * @api private + */ +var lastChunkNumber = function(self) { + return Math.floor(self.length/self.chunkSize); +}; + +/** + * Retrieve this file's chunks collection. + * + * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured. + * @return {null} + * @api public + */ +GridStore.prototype.chunkCollection = function(callback) { + this.db.collection((this.root + ".chunks"), callback); +}; + +/** + * Deletes all the chunks of this file in the database. + * + * @param callback {function(*, boolean)} This will be called after this method + * executes. Passes null to the first and true to the second argument. + * + * @ignore + * @api private + */ +var deleteChunks = function(self, callback) { + if(self.fileId != null) { + self.chunkCollection(function(err, collection) { + if(err) return callback(err, false); + collection.remove({'files_id':self.fileId}, {safe:true}, function(err, result) { + if(err) return callback(err, false); + callback(null, true); + }); + }); + } else { + callback(null, true); + } +}; + +/** + * Deletes all the chunks of this file in the database. + * + * @param {Function} callback this will be called after this method executes. Passes null to the first and true to the second argument. + * @return {null} + * @api public + */ +GridStore.prototype.unlink = function(callback) { + var self = this; + deleteChunks(this, function(err) { + if(err!==null) { + err.message = "at deleteChunks: " + err.message; + return callback(err); + } + + self.collection(function(err, collection) { + if(err!==null) { + err.message = "at collection: " + err.message; + return callback(err); + } + + collection.remove({'_id':self.fileId}, {safe:true}, function(err) { + callback(err, self); + }); + }); + }); +}; + +/** + * Retrieves the file collection associated with this object. + * + * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured. + * @return {null} + * @api public + */ +GridStore.prototype.collection = function(callback) { + this.db.collection(this.root + ".files", callback); +}; + +/** + * Reads the data of this file. + * + * @param {String} [separator] the character to be recognized as the newline separator. + * @param {Function} callback This will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character. + * @return {null} + * @api public + */ +GridStore.prototype.readlines = function(separator, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + separator = args.length ? args.shift() : "\n"; + + this.read(function(err, data) { + if(err) return callback(err); + + var items = data.toString().split(separator); + items = items.length > 0 ? items.splice(0, items.length - 1) : []; + for(var i = 0; i < items.length; i++) { + items[i] = items[i] + separator; + } + + callback(null, items); + }); +}; + +/** + * Deletes all the chunks of this file in the database if mode was set to "w" or + * "w+" and resets the read/write head to the initial position. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.rewind = function(callback) { + var self = this; + + if(this.currentChunk.chunkNumber != 0) { + if(this.mode[0] == "w") { + deleteChunks(self, function(err, gridStore) { + if(err) return callback(err); + self.currentChunk = new Chunk(self, {'n': 0}); + self.position = 0; + callback(null, self); + }); + } else { + self.currentChunk(0, function(err, chunk) { + if(err) return callback(err); + self.currentChunk = chunk; + self.currentChunk.rewind(); + self.position = 0; + callback(null, self); + }); + } + } else { + self.currentChunk.rewind(); + self.position = 0; + callback(null, self); + } +}; + +/** + * Retrieves the contents of this file and advances the read/write head. Works with Buffers only. + * + * There are 3 signatures for this method: + * + * (callback) + * (length, callback) + * (length, buffer, callback) + * + * @param {Number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified. + * @param {String|Buffer} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method. + * @param {Function} callback this will be called after this method is executed. null will be passed to the first parameter and a string containing the contents of the buffer concatenated with the contents read from this file will be passed to the second. + * @return {null} + * @api public + */ +GridStore.prototype.read = function(length, buffer, callback) { + var self = this; + + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + length = args.length ? args.shift() : null; + buffer = args.length ? args.shift() : null; + + // The data is a c-terminated string and thus the length - 1 + var finalLength = length == null ? self.length - self.position : length; + var finalBuffer = buffer == null ? new Buffer(finalLength) : buffer; + // Add a index to buffer to keep track of writing position or apply current index + finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0; + + if((self.currentChunk.length() - self.currentChunk.position + finalBuffer._index) >= finalLength) { + var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index); + // Copy content to final buffer + slice.copy(finalBuffer, finalBuffer._index); + // Update internal position + self.position = finalBuffer.length; + // Check if we don't have a file at all + if(finalLength == 0 && finalBuffer.length == 0) return callback(new Error("File does not exist"), null); + // Else return data + callback(null, finalBuffer); + } else { + var slice = self.currentChunk.readSlice(self.currentChunk.length() - self.currentChunk.position); + // Copy content to final buffer + slice.copy(finalBuffer, finalBuffer._index); + // Update index position + finalBuffer._index += slice.length; + + // Load next chunk and read more + nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) { + if(err) return callback(err); + + if(chunk.length() > 0) { + self.currentChunk = chunk; + self.read(length, finalBuffer, callback); + } else { + if (finalBuffer._index > 0) { + callback(null, finalBuffer) + } else { + callback(new Error("no chunks found for file, possibly corrupt"), null); + } + } + }); + } +} + +/** + * Retrieves the position of the read/write head of this file. + * + * @param {Function} callback This gets called after this method terminates. null is passed to the first parameter and the position is passed to the second. + * @return {null} + * @api public + */ +GridStore.prototype.tell = function(callback) { + callback(null, this.position); +}; + +/** + * Moves the read/write head to a new location. + * + * There are 3 signatures for this method + * + * Seek Location Modes + * - **GridStore.IO_SEEK_SET**, **(default)** set the position from the start of the file. + * - **GridStore.IO_SEEK_CUR**, set the position from the current position in the file. + * - **GridStore.IO_SEEK_END**, set the position from the end of the file. + * + * @param {Number} [position] the position to seek to + * @param {Number} [seekLocation] seek mode. Use one of the Seek Location modes. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.seek = function(position, seekLocation, callback) { + var self = this; + + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + seekLocation = args.length ? args.shift() : null; + + var seekLocationFinal = seekLocation == null ? exports.GridStore.IO_SEEK_SET : seekLocation; + var finalPosition = position; + var targetPosition = 0; + if(seekLocationFinal == exports.GridStore.IO_SEEK_CUR) { + targetPosition = self.position + finalPosition; + } else if(seekLocationFinal == exports.GridStore.IO_SEEK_END) { + targetPosition = self.length + finalPosition; + } else { + targetPosition = finalPosition; + } + + var newChunkNumber = Math.floor(targetPosition/self.chunkSize); + if(newChunkNumber != self.currentChunk.chunkNumber) { + var seekChunk = function() { + nthChunk(self, newChunkNumber, function(err, chunk) { + self.currentChunk = chunk; + self.position = targetPosition; + self.currentChunk.position = (self.position % self.chunkSize); + callback(err, self); + }); + }; + + if(self.mode[0] == 'w') { + self.currentChunk.save(function(err) { + if(err) return callback(err); + seekChunk(); + }); + } else { + seekChunk(); + } + } else { + self.position = targetPosition; + self.currentChunk.position = (self.position % self.chunkSize); + callback(null, self); + } +}; + +/** + * Verify if the file is at EOF. + * + * @return {Boolean} true if the read/write head is at the end of this file. + * @api public + */ +GridStore.prototype.eof = function() { + return this.position == this.length ? true : false; +}; + +/** + * Retrieves a single character from this file. + * + * @param {Function} callback this gets called after this method is executed. Passes null to the first parameter and the character read to the second or null to the second if the read/write head is at the end of the file. + * @return {null} + * @api public + */ +GridStore.prototype.getc = function(callback) { + var self = this; + + if(self.eof()) { + callback(null, null); + } else if(self.currentChunk.eof()) { + nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) { + self.currentChunk = chunk; + self.position = self.position + 1; + callback(err, self.currentChunk.getc()); + }); + } else { + self.position = self.position + 1; + callback(null, self.currentChunk.getc()); + } +}; + +/** + * Writes a string to the file with a newline character appended at the end if + * the given string does not have one. + * + * @param {String} string the string to write. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.puts = function(string, callback) { + var finalString = string.match(/\n$/) == null ? string + "\n" : string; + this.write(finalString, callback); +}; + +/** + * Returns read stream based on this GridStore file + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **end** {function() {}} the end event triggers when there is no more documents available. + * - **close** {function() {}} the close event triggers when the stream is closed. + * - **error** {function(err) {}} the error event triggers if an error happens. + * + * @param {Boolean} autoclose if true current GridStore will be closed when EOF and 'close' event will be fired + * @return {null} + * @api public + */ +GridStore.prototype.stream = function(autoclose) { + return new ReadStream(autoclose, this); +}; + +/** +* The collection to be used for holding the files and chunks collection. +* +* @classconstant DEFAULT_ROOT_COLLECTION +**/ +GridStore.DEFAULT_ROOT_COLLECTION = 'fs'; + +/** +* Default file mime type +* +* @classconstant DEFAULT_CONTENT_TYPE +**/ +GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream'; + +/** +* Seek mode where the given length is absolute. +* +* @classconstant IO_SEEK_SET +**/ +GridStore.IO_SEEK_SET = 0; + +/** +* Seek mode where the given length is an offset to the current read/write head. +* +* @classconstant IO_SEEK_CUR +**/ +GridStore.IO_SEEK_CUR = 1; + +/** +* Seek mode where the given length is an offset to the end of the file. +* +* @classconstant IO_SEEK_END +**/ +GridStore.IO_SEEK_END = 2; + +/** + * Checks if a file exists in the database. + * + * @param {Db} db the database to query. + * @param {String} name the name of the file to look for. + * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * @param {Function} callback this will be called after this method executes. Passes null to the first and passes true to the second if the file exists and false otherwise. + * @return {null} + * @api public + */ +GridStore.exist = function(db, fileIdObject, rootCollection, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + rootCollection = args.length ? args.shift() : null; + + // Fetch collection + var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION; + db.collection(rootCollectionFinal + ".files", function(err, collection) { + if(err) return callback(err); + + // Build query + var query = (typeof fileIdObject == 'string' || Object.prototype.toString.call(fileIdObject) == '[object RegExp]' ) + ? {'filename':fileIdObject} + : {'_id':fileIdObject}; // Attempt to locate file + + collection.find(query, function(err, cursor) { + if(err) return callback(err); + + cursor.nextObject(function(err, item) { + if(err) return callback(err); + callback(null, item == null ? false : true); + }); + }); + }); +}; + +/** + * Gets the list of files stored in the GridFS. + * + * @param {Db} db the database to query. + * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * @param {Function} callback this will be called after this method executes. Passes null to the first and passes an array of strings containing the names of the files. + * @return {null} + * @api public + */ +GridStore.list = function(db, rootCollection, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + rootCollection = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Ensure we have correct values + if(rootCollection != null && typeof rootCollection == 'object') { + options = rootCollection; + rootCollection = null; + } + + // Check if we are returning by id not filename + var byId = options['id'] != null ? options['id'] : false; + // Fetch item + var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION; + var items = []; + db.collection((rootCollectionFinal + ".files"), function(err, collection) { + if(err) return callback(err); + + collection.find(function(err, cursor) { + if(err) return callback(err); + + cursor.each(function(err, item) { + if(item != null) { + items.push(byId ? item._id : item.filename); + } else { + callback(err, items); + } + }); + }); + }); +}; + +/** + * Reads the contents of a file. + * + * This method has the following signatures + * + * (db, name, callback) + * (db, name, length, callback) + * (db, name, length, offset, callback) + * (db, name, length, offset, options, callback) + * + * @param {Db} db the database to query. + * @param {String} name the name of the file. + * @param {Number} [length] the size of data to read. + * @param {Number} [offset] the offset from the head of the file of which to start reading from. + * @param {Object} [options] the options for the file. + * @param {Function} callback this will be called after this method executes. A string with an error message will be passed to the first parameter when the length and offset combination exceeds the length of the file while an Error object will be passed if other forms of error occured, otherwise, a string is passed. The second parameter will contain the data read if successful or null if an error occured. + * @return {null} + * @api public + */ +GridStore.read = function(db, name, length, offset, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + length = args.length ? args.shift() : null; + offset = args.length ? args.shift() : null; + options = args.length ? args.shift() : null; + + new GridStore(db, name, "r", options).open(function(err, gridStore) { + if(err) return callback(err); + // Make sure we are not reading out of bounds + if(offset && offset >= gridStore.length) return callback("offset larger than size of file", null); + if(length && length > gridStore.length) return callback("length is larger than the size of the file", null); + if(offset && length && (offset + length) > gridStore.length) return callback("offset and length is larger than the size of the file", null); + + if(offset != null) { + gridStore.seek(offset, function(err, gridStore) { + if(err) return callback(err); + gridStore.read(length, callback); + }); + } else { + gridStore.read(length, callback); + } + }); +}; + +/** + * Reads the data of this file. + * + * @param {Db} db the database to query. + * @param {String} name the name of the file. + * @param {String} [separator] the character to be recognized as the newline separator. + * @param {Object} [options] file options. + * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character. + * @return {null} + * @api public + */ +GridStore.readlines = function(db, name, separator, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + separator = args.length ? args.shift() : null; + options = args.length ? args.shift() : null; + + var finalSeperator = separator == null ? "\n" : separator; + new GridStore(db, name, "r", options).open(function(err, gridStore) { + if(err) return callback(err); + gridStore.readlines(finalSeperator, callback); + }); +}; + +/** + * Deletes the chunks and metadata information of a file from GridFS. + * + * @param {Db} db the database to interact with. + * @param {String|Array} names the name/names of the files to delete. + * @param {Object} [options] the options for the files. + * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.unlink = function(db, names, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : null; + + if(names.constructor == Array) { + var tc = 0; + for(var i = 0; i < names.length; i++) { + ++tc; + self.unlink(db, names[i], function(result) { + if(--tc == 0) { + callback(null, self); + } + }); + } + } else { + new GridStore(db, names, "w", options).open(function(err, gridStore) { + if(err) return callback(err); + deleteChunks(gridStore, function(err, result) { + if(err) return callback(err); + gridStore.collection(function(err, collection) { + if(err) return callback(err); + collection.remove({'_id':gridStore.fileId}, {safe:true}, function(err, collection) { + callback(err, self); + }); + }); + }); + }); + } +}; + +/** + * Returns the current chunksize of the file. + * + * @field chunkSize + * @type {Number} + * @getter + * @setter + * @property return number of bytes in the current chunkSize. + */ +Object.defineProperty(GridStore.prototype, "chunkSize", { enumerable: true + , get: function () { + return this.internalChunkSize; + } + , set: function(value) { + if(!(this.mode[0] == "w" && this.position == 0 && this.uploadDate == null)) { + this.internalChunkSize = this.internalChunkSize; + } else { + this.internalChunkSize = value; + } + } +}); + +/** + * The md5 checksum for this file. + * + * @field md5 + * @type {Number} + * @getter + * @setter + * @property return this files md5 checksum. + */ +Object.defineProperty(GridStore.prototype, "md5", { enumerable: true + , get: function () { + return this.internalMd5; + } +}); + +/** + * GridStore Streaming methods + * Handles the correct return of the writeable stream status + * @ignore + */ +Object.defineProperty(GridStore.prototype, "writable", { enumerable: true + , get: function () { + if(this._writeable == null) { + this._writeable = this.mode != null && this.mode.indexOf("w") != -1; + } + // Return the _writeable + return this._writeable; + } + , set: function(value) { + this._writeable = value; + } +}); + +/** + * Handles the correct return of the readable stream status + * @ignore + */ +Object.defineProperty(GridStore.prototype, "readable", { enumerable: true + , get: function () { + if(this._readable == null) { + this._readable = this.mode != null && this.mode.indexOf("r") != -1; + } + return this._readable; + } + , set: function(value) { + this._readable = value; + } +}); + +GridStore.prototype.paused; + +/** + * Handles the correct setting of encoding for the stream + * @ignore + */ +GridStore.prototype.setEncoding = fs.ReadStream.prototype.setEncoding; + +/** + * Handles the end events + * @ignore + */ +GridStore.prototype.end = function end(data) { + var self = this; + // allow queued data to write before closing + if(!this.writable) return; + this.writable = false; + + if(data) { + this._q.push(data); + } + + this.on('drain', function () { + self.close(function (err) { + if (err) return _error(self, err); + self.emit('close'); + }); + }); + + _flush(self); +} + +/** + * Handles the normal writes to gridstore + * @ignore + */ +var _writeNormal = function(self, data, close, callback) { + // If we have a buffer write it using the writeBuffer method + if(Buffer.isBuffer(data)) { + return writeBuffer(self, data, close, callback); + } else { + // Wrap the string in a buffer and write + return writeBuffer(self, new Buffer(data, 'binary'), close, callback); + } +} + +/** + * Writes some data. This method will work properly only if initialized with mode "w" or "w+". + * + * @param {String|Buffer} data the data to write. + * @param {Boolean} [close] closes this file after writing if set to true. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.write = function write(data, close, callback) { + // If it's a normal write delegate the call + if(typeof close == 'function' || typeof callback == 'function') { + return _writeNormal(this, data, close, callback); + } + + // Otherwise it's a stream write + var self = this; + if (!this.writable) { + throw new Error('GridWriteStream is not writable'); + } + + // queue data until we open. + if (!this._opened) { + // Set up a queue to save data until gridstore object is ready + this._q = []; + _openStream(self); + this._q.push(data); + return false; + } + + // Push data to queue + this._q.push(data); + _flush(this); + // Return write successful + return true; +} + +/** + * Handles the destroy part of a stream + * @ignore + */ +GridStore.prototype.destroy = function destroy() { + // close and do not emit any more events. queued data is not sent. + if(!this.writable) return; + this.readable = false; + if(this.writable) { + this.writable = false; + this._q.length = 0; + this.emit('close'); + } +} + +/** + * Handles the destroySoon part of a stream + * @ignore + */ +GridStore.prototype.destroySoon = function destroySoon() { + // as soon as write queue is drained, destroy. + // may call destroy immediately if no data is queued. + if(!this._q.length) { + return this.destroy(); + } + this._destroying = true; +} + +/** + * Handles the pipe part of the stream + * @ignore + */ +GridStore.prototype.pipe = function(destination, options) { + var self = this; + // Open the gridstore + this.open(function(err, result) { + if(err) _errorRead(self, err); + if(!self.readable) return; + // Set up the pipe + self._pipe(destination, options); + // Emit the stream is open + self.emit('open'); + // Read from the stream + _read(self); + }) +} + +/** + * Internal module methods + * @ignore + */ +var _read = function _read(self) { + if (!self.readable || self.paused || self.reading) { + return; + } + + self.reading = true; + var stream = self._stream = self.stream(); + stream.paused = self.paused; + + stream.on('data', function (data) { + if (self._decoder) { + var str = self._decoder.write(data); + if (str.length) self.emit('data', str); + } else { + self.emit('data', data); + } + }); + + stream.on('end', function (data) { + self.emit('end', data); + }); + + stream.on('error', function (data) { + _errorRead(self, data); + }); + + stream.on('close', function (data) { + self.emit('close', data); + }); + + self.pause = function () { + // native doesn't always pause. + // bypass its pause() method to hack it + self.paused = stream.paused = true; + } + + self.resume = function () { + if(!self.paused) return; + + self.paused = false; + stream.resume(); + self.readable = stream.readable; + } + + self.destroy = function () { + self.readable = false; + stream.destroy(); + } +} + +/** + * pause + * @ignore + */ +GridStore.prototype.pause = function pause () { + // Overridden when the GridStore opens. + this.paused = true; +} + +/** + * resume + * @ignore + */ +GridStore.prototype.resume = function resume () { + // Overridden when the GridStore opens. + this.paused = false; +} + +/** + * Internal module methods + * @ignore + */ +var _flush = function _flush(self, _force) { + if (!self._opened) return; + if (!_force && self._flushing) return; + self._flushing = true; + + // write the entire q to gridfs + if (!self._q.length) { + self._flushing = false; + self.emit('drain'); + + if(self._destroying) { + self.destroy(); + } + return; + } + + self.write(self._q.shift(), function (err, store) { + if (err) return _error(self, err); + self.emit('progress', store.position); + _flush(self, true); + }); +} + +var _openStream = function _openStream (self) { + if(self._opening == true) return; + self._opening = true; + + // Open the store + self.open(function (err, gridstore) { + if (err) return _error(self, err); + self._opened = true; + self.emit('open'); + _flush(self); + }); +} + +var _error = function _error(self, err) { + self.destroy(); + self.emit('error', err); +} + +var _errorRead = function _errorRead (self, err) { + self.readable = false; + self.emit('error', err); +} + +/** + * @ignore + * @api private + */ +exports.GridStore = GridStore; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/readstream.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/readstream.js new file mode 100644 index 0000000..ebb09bd --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/gridfs/readstream.js @@ -0,0 +1,188 @@ +var Stream = require('stream').Stream, + util = require('util'); + +/** + * ReadStream + * + * Returns a stream interface for the **file**. + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **end** {function() {}} the end event triggers when there is no more documents available. + * - **close** {function() {}} the close event triggers when the stream is closed. + * - **error** {function(err) {}} the error event triggers if an error happens. + * + * @class Represents a GridFS File Stream. + * @param {Boolean} autoclose automatically close file when the stream reaches the end. + * @param {GridStore} cursor a cursor object that the stream wraps. + * @return {ReadStream} + */ +function ReadStream(autoclose, gstore) { + if (!(this instanceof ReadStream)) return new ReadStream(autoclose, gstore); + Stream.call(this); + + this.autoclose = !!autoclose; + this.gstore = gstore; + + this.finalLength = gstore.length - gstore.position; + this.completedLength = 0; + this.currentChunkNumber = gstore.currentChunk.chunkNumber; + + this.paused = false; + this.readable = true; + this.pendingChunk = null; + this.executing = false; + + // Calculate the number of chunks + this.numberOfChunks = Math.ceil(gstore.length/gstore.chunkSize); + + // This seek start position inside the current chunk + this.seekStartPosition = gstore.position - (this.currentChunkNumber * gstore.chunkSize); + + var self = this; + process.nextTick(function() { + self._execute(); + }); +}; + +/** + * Inherit from Stream + * @ignore + * @api private + */ +ReadStream.prototype.__proto__ = Stream.prototype; + +/** + * Flag stating whether or not this stream is readable. + */ +ReadStream.prototype.readable; + +/** + * Flag stating whether or not this stream is paused. + */ +ReadStream.prototype.paused; + +/** + * @ignore + * @api private + */ +ReadStream.prototype._execute = function() { + if(this.paused === true || this.readable === false) { + return; + } + + var gstore = this.gstore; + var self = this; + // Set that we are executing + this.executing = true; + + var last = false; + var toRead = 0; + + if(gstore.currentChunk.chunkNumber >= (this.numberOfChunks - 1)) { + self.executing = false; + last = true; + } + + // Data setup + var data = null; + + // Read a slice (with seek set if none) + if(this.seekStartPosition > 0 && (gstore.currentChunk.length() - this.seekStartPosition) > 0) { + data = gstore.currentChunk.readSlice(gstore.currentChunk.length() - this.seekStartPosition); + this.seekStartPosition = 0; + } else { + data = gstore.currentChunk.readSlice(gstore.currentChunk.length()); + } + + // Return the data + if(data != null && gstore.currentChunk.chunkNumber == self.currentChunkNumber) { + self.currentChunkNumber = self.currentChunkNumber + 1; + self.completedLength += data.length; + self.pendingChunk = null; + self.emit("data", data); + } + + if(last === true) { + self.readable = false; + self.emit("end"); + + if(self.autoclose === true) { + if(gstore.mode[0] == "w") { + gstore.close(function(err, doc) { + if (err) { + self.emit("error", err); + return; + } + self.readable = false; + self.emit("close", doc); + }); + } else { + self.readable = false; + self.emit("close"); + } + } + } else { + gstore._nthChunk(gstore.currentChunk.chunkNumber + 1, function(err, chunk) { + if(err) { + self.readable = false; + self.emit("error", err); + self.executing = false; + return; + } + + self.pendingChunk = chunk; + if(self.paused === true) { + self.executing = false; + return; + } + + gstore.currentChunk = self.pendingChunk; + self._execute(); + }); + } +}; + +/** + * Pauses this stream, then no farther events will be fired. + * + * @ignore + * @api public + */ +ReadStream.prototype.pause = function() { + if(!this.executing) { + this.paused = true; + } +}; + +/** + * Destroys the stream, then no farther events will be fired. + * + * @ignore + * @api public + */ +ReadStream.prototype.destroy = function() { + this.readable = false; + // Emit close event + this.emit("close"); +}; + +/** + * Resumes this stream. + * + * @ignore + * @api public + */ +ReadStream.prototype.resume = function() { + if(this.paused === false || !this.readable) { + return; + } + + this.paused = false; + var self = this; + process.nextTick(function() { + self._execute(); + }); +}; + +exports.ReadStream = ReadStream; diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/index.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/index.js new file mode 100644 index 0000000..6a2b727 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/index.js @@ -0,0 +1,69 @@ +try { + exports.BSONPure = require('bson').BSONPure; + exports.BSONNative = require('bson').BSONNative; +} catch(err) { + // do nothing +} + +[ 'commands/base_command' + , 'admin' + , 'collection' + , 'connection/read_preference' + , 'connection/connection' + , 'connection/server' + , 'connection/mongos' + , 'connection/repl_set' + , 'mongo_client' + , 'cursor' + , 'db' + , 'mongo_client' + , 'gridfs/grid' + , 'gridfs/chunk' + , 'gridfs/gridstore'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + exports[i] = module[i]; + } + + // backwards compat + exports.ReplSetServers = exports.ReplSet; + + // Add BSON Classes + exports.Binary = require('bson').Binary; + exports.Code = require('bson').Code; + exports.DBRef = require('bson').DBRef; + exports.Double = require('bson').Double; + exports.Long = require('bson').Long; + exports.MinKey = require('bson').MinKey; + exports.MaxKey = require('bson').MaxKey; + exports.ObjectID = require('bson').ObjectID; + exports.Symbol = require('bson').Symbol; + exports.Timestamp = require('bson').Timestamp; + + // Add BSON Parser + exports.BSON = require('bson').BSONPure.BSON; + +}); + +// Get the Db object +var Db = require('./db').Db; +// Set up the connect function +var connect = Db.connect; +var obj = connect; +// Map all values to the exports value +for(var name in exports) { + obj[name] = exports[name]; +} + +// Add the pure and native backward compatible functions +exports.pure = exports.native = function() { + return obj; +} + +// Map all values to the exports value +for(var name in exports) { + connect[name] = exports[name]; +} + +// Set our exports to be the connect function +module.exports = connect; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/mongo_client.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/mongo_client.js new file mode 100644 index 0000000..cfc9e6f --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/mongo_client.js @@ -0,0 +1,116 @@ +var Db = require('./db').Db; + +/** + * Create a new MongoClient instance. + * + * Options + * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **native_parser** {Boolean, default:false}, use c++ bson parser. + * - **forceServerObjectId** {Boolean, default:false}, force server to create _id fields instead of client. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **serializeFunctions** {Boolean, default:false}, serialize functions. + * - **raw** {Boolean, default:false}, peform operations using raw bson buffers. + * - **recordQueryStats** {Boolean, default:false}, record query statistics during execution. + * - **retryMiliSeconds** {Number, default:5000}, number of miliseconds between retries. + * - **numberOfRetries** {Number, default:5}, number of retries off connection. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @class Represents a MongoClient + * @param {Object} serverConfig server config object. + * @param {Object} [options] additional options for the collection. + */ +function MongoClient(serverConfig, options) { + options = options == null ? {} : options; + // If no write concern is set set the default to w:1 + if(options != null && !options.journal && !options.w && !options.fsync) { + options.w = 1; + } + + // The internal db instance we are wrapping + this._db = new Db('test', serverConfig, options); +} + +/** + * Initialize the database connection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the connected mongoclient or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.prototype.open = function(callback) { + // Self reference + var self = this; + + this._db.open(function(err, db) { + if(err) return callback(err, null); + callback(null, self); + }) +} + +/** + * Close the current db connection, including all the child db instances. Emits close event if no callback is provided. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the close method or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.prototype.close = function(callback) { + this._db.close(callback); +} + +/** + * Create a new Db instance sharing the current socket connections. + * + * @param {String} dbName the name of the database we want to use. + * @return {Db} a db instance using the new database. + * @api public + */ +MongoClient.prototype.db = function(dbName) { + return this._db.db(dbName); +} + +/** + * Connect to MongoDB using a url as documented at + * + * www.mongodb.org/display/DOCS/Connections + * + * Options + * - **uri_decode_auth** {Boolean, default:false} uri decode the user name and password for authentication + * - **db** {Object, default: null} a hash off options to set on the db object, see **Db constructor** + * - **server** {Object, default: null} a hash off options to set on the server objects, see **Server** constructor** + * - **replSet** {Object, default: null} a hash off options to set on the replSet object, see **ReplSet** constructor** + * - **mongos** {Object, default: null} a hash off options to set on the mongos object, see **Mongos** constructor** + * + * @param {String} url connection url for MongoDB. + * @param {Object} [options] optional options for insert command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the initialized db object or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.connect = function(url, options, callback) { + if(typeof options == 'function') { + callback = options; + options = {}; + } + + Db.connect(url, options, function(err, db) { + if(err) return callback(err, null); + + if(db.options !== null && !db.options.safe && !db.options.journal + && !db.options.w && !db.options.fsync && typeof db.options.w != 'number' + && (db.options.safe == false && url.indexOf("safe=") == -1)) { + db.options.w = 1; + } + + // Return the db + callback(null, db); + }); +} + +exports.MongoClient = MongoClient; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js new file mode 100644 index 0000000..b129dc6 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js @@ -0,0 +1,140 @@ +var Long = require('bson').Long; + +/** + Reply message from mongo db +**/ +var MongoReply = exports.MongoReply = function() { + this.documents = []; + this.index = 0; +}; + +MongoReply.prototype.parseHeader = function(binary_reply, bson) { + // Unpack the standard header first + this.messageLength = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Fetch the request id for this reply + this.requestId = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Fetch the id of the request that triggered the response + this.responseTo = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + // Skip op-code field + this.index = this.index + 4 + 4; + // Unpack the reply message + this.responseFlag = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Unpack the cursor id (a 64 bit long integer) + var low_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + var high_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + this.cursorId = new Long(low_bits, high_bits); + // Unpack the starting from + this.startingFrom = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Unpack the number of objects returned + this.numberReturned = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; +} + +MongoReply.prototype.parseBody = function(binary_reply, bson, raw, callback) { + raw = raw == null ? false : raw; + // Just set a doc limit for deserializing + var docLimitSize = 1024*20; + + // If our message length is very long, let's switch to process.nextTick for messages + if(this.messageLength > docLimitSize) { + var batchSize = this.numberReturned; + this.documents = new Array(this.numberReturned); + + // Just walk down until we get a positive number >= 1 + for(var i = 50; i > 0; i--) { + if((this.numberReturned/i) >= 1) { + batchSize = i; + break; + } + } + + // Actual main creator of the processFunction setting internal state to control the flow + var parseFunction = function(_self, _binary_reply, _batchSize, _numberReturned) { + var object_index = 0; + // Internal loop process that will use nextTick to ensure we yield some time + var processFunction = function() { + // Adjust batchSize if we have less results left than batchsize + if((_numberReturned - object_index) < _batchSize) { + _batchSize = _numberReturned - object_index; + } + + // If raw just process the entries + if(raw) { + // Iterate over the batch + for(var i = 0; i < _batchSize; i++) { + // Are we done ? + if(object_index <= _numberReturned) { + // Read the size of the bson object + var bsonObjectSize = _binary_reply[_self.index] | _binary_reply[_self.index + 1] << 8 | _binary_reply[_self.index + 2] << 16 | _binary_reply[_self.index + 3] << 24; + // If we are storing the raw responses to pipe straight through + _self.documents[object_index] = binary_reply.slice(_self.index, _self.index + bsonObjectSize); + // Adjust binary index to point to next block of binary bson data + _self.index = _self.index + bsonObjectSize; + // Update number of docs parsed + object_index = object_index + 1; + } + } + } else { + try { + // Parse documents + _self.index = bson.deserializeStream(binary_reply, _self.index, _batchSize, _self.documents, object_index); + // Adjust index + object_index = object_index + _batchSize; + } catch (err) { + return callback(err); + } + } + + // If we hav more documents process NextTick + if(object_index < _numberReturned) { + process.nextTick(processFunction); + } else { + callback(null); + } + } + + // Return the process function + return processFunction; + }(this, binary_reply, batchSize, this.numberReturned)(); + } else { + try { + // Let's unpack all the bson documents, deserialize them and store them + for(var object_index = 0; object_index < this.numberReturned; object_index++) { + // Read the size of the bson object + var bsonObjectSize = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + // If we are storing the raw responses to pipe straight through + if(raw) { + // Deserialize the object and add to the documents array + this.documents.push(binary_reply.slice(this.index, this.index + bsonObjectSize)); + } else { + // Deserialize the object and add to the documents array + this.documents.push(bson.deserialize(binary_reply.slice(this.index, this.index + bsonObjectSize))); + } + // Adjust binary index to point to next block of binary bson data + this.index = this.index + bsonObjectSize; + } + } catch(err) { + return callback(err); + } + + // No error return + callback(null); + } +} + +MongoReply.prototype.is_error = function(){ + if(this.documents.length == 1) { + return this.documents[0].ok == 1 ? false : true; + } + return false; +}; + +MongoReply.prototype.error_message = function() { + return this.documents.length == 1 && this.documents[0].ok == 1 ? '' : this.documents[0].errmsg; +}; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/utils.js b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/utils.js new file mode 100644 index 0000000..120a584 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/lib/mongodb/utils.js @@ -0,0 +1,97 @@ +/** + * Sort functions, Normalize and prepare sort parameters + */ +var formatSortValue = exports.formatSortValue = function(sortDirection) { + var value = ("" + sortDirection).toLowerCase(); + + switch (value) { + case 'ascending': + case 'asc': + case '1': + return 1; + case 'descending': + case 'desc': + case '-1': + return -1; + default: + throw new Error("Illegal sort clause, must be of the form " + + "[['field1', '(ascending|descending)'], " + + "['field2', '(ascending|descending)']]"); + } +}; + +var formattedOrderClause = exports.formattedOrderClause = function(sortValue) { + var orderBy = {}; + + if (Array.isArray(sortValue)) { + for(var i = 0; i < sortValue.length; i++) { + if(sortValue[i].constructor == String) { + orderBy[sortValue[i]] = 1; + } else { + orderBy[sortValue[i][0]] = formatSortValue(sortValue[i][1]); + } + } + } else if(Object.prototype.toString.call(sortValue) === '[object Object]') { + orderBy = sortValue; + } else if (sortValue.constructor == String) { + orderBy[sortValue] = 1; + } else { + throw new Error("Illegal sort clause, must be of the form " + + "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"); + } + + return orderBy; +}; + +exports.encodeInt = function(value) { + var buffer = new Buffer(4); + buffer[3] = (value >> 24) & 0xff; + buffer[2] = (value >> 16) & 0xff; + buffer[1] = (value >> 8) & 0xff; + buffer[0] = value & 0xff; + return buffer; +} + +exports.encodeIntInPlace = function(value, buffer, index) { + buffer[index + 3] = (value >> 24) & 0xff; + buffer[index + 2] = (value >> 16) & 0xff; + buffer[index + 1] = (value >> 8) & 0xff; + buffer[index] = value & 0xff; +} + +exports.encodeCString = function(string) { + var buf = new Buffer(string, 'utf8'); + return [buf, new Buffer([0])]; +} + +exports.decodeUInt32 = function(array, index) { + return array[index] | array[index + 1] << 8 | array[index + 2] << 16 | array[index + 3] << 24; +} + +// Decode the int +exports.decodeUInt8 = function(array, index) { + return array[index]; +} + +/** + * Context insensitive type checks + */ + +var toString = Object.prototype.toString; + +exports.isObject = function (arg) { + return '[object Object]' == toString.call(arg) +} + +exports.isArray = function (arg) { + return Array.isArray(arg) || + 'object' == typeof arg && '[object Array]' == toString.call(arg) +} + +exports.isDate = function (arg) { + return 'object' == typeof arg && '[object Date]' == toString.call(arg) +} + +exports.isRegExp = function (arg) { + return 'object' == typeof arg && '[object RegExp]' == toString.call(arg) +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/.travis.yml b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/.travis.yml new file mode 100644 index 0000000..94740d0 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 # development version of 0.8, may be unstable \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/Makefile b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/Makefile new file mode 100644 index 0000000..2ca5592 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/Makefile @@ -0,0 +1,16 @@ +NODE = node +NPM = npm +NODEUNIT = node_modules/nodeunit/bin/nodeunit + +all: clean node_gyp + +test: clean node_gyp + npm test + +node_gyp: clean + node-gyp configure build + +clean: + node-gyp clean + +.PHONY: all diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/README.md b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/README.md new file mode 100644 index 0000000..73892e2 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/README.md @@ -0,0 +1 @@ +A JS/C++ Bson parser for node, used in the MongoDB Native driver \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js new file mode 100644 index 0000000..45a1111 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js @@ -0,0 +1,130 @@ +// var BSON = require('../../lib/mongodb').BSONNative.BSON, +// ObjectID = require('../../lib/mongodb').BSONNative.ObjectID, +// Code = require('../../lib/mongodb').BSONNative.Code, +// Long = require('../../lib/mongodb').BSONNative.Long, +// Binary = require('../../lib/mongodb').BSONNative.Binary, +// debug = require('util').debug, +// inspect = require('util').inspect, +// +// Long = require('../../lib/mongodb').Long, +// ObjectID = require('../../lib/mongodb').ObjectID, +// Binary = require('../../lib/mongodb').Binary, +// Code = require('../../lib/mongodb').Code, +// DBRef = require('../../lib/mongodb').DBRef, +// Symbol = require('../../lib/mongodb').Symbol, +// Double = require('../../lib/mongodb').Double, +// MaxKey = require('../../lib/mongodb').MaxKey, +// MinKey = require('../../lib/mongodb').MinKey, +// Timestamp = require('../../lib/mongodb').Timestamp; + + +// var BSON = require('../../lib/mongodb').BSONPure.BSON, +// ObjectID = require('../../lib/mongodb').BSONPure.ObjectID, +// Code = require('../../lib/mongodb').BSONPure.Code, +// Long = require('../../lib/mongodb').BSONPure.Long, +// Binary = require('../../lib/mongodb').BSONPure.Binary; + +var BSON = require('../lib/bson').BSONNative.BSON, + Long = require('../lib/bson').Long, + ObjectID = require('../lib/bson').ObjectID, + Binary = require('../lib/bson').Binary, + Code = require('../lib/bson').Code, + DBRef = require('../lib/bson').DBRef, + Symbol = require('../lib/bson').Symbol, + Double = require('../lib/bson').Double, + MaxKey = require('../lib/bson').MaxKey, + MinKey = require('../lib/bson').MinKey, + Timestamp = require('../lib/bson').Timestamp; + + // console.dir(require('../lib/bson')) + +var COUNT = 1000; +var COUNT = 100; + +var object = { + string: "Strings are great", + decimal: 3.14159265, + bool: true, + integer: 5, + date: new Date(), + double: new Double(1.4), + id: new ObjectID(), + min: new MinKey(), + max: new MaxKey(), + symbol: new Symbol('hello'), + long: Long.fromNumber(100), + bin: new Binary(new Buffer(100)), + + subObject: { + moreText: "Bacon ipsum dolor sit amet cow pork belly rump ribeye pastrami andouille. Tail hamburger pork belly, drumstick flank salami t-bone sirloin pork chop ribeye ham chuck pork loin shankle. Ham fatback pork swine, sirloin shankle short loin andouille shank sausage meatloaf drumstick. Pig chicken cow bresaola, pork loin jerky meatball tenderloin brisket strip steak jowl spare ribs. Biltong sirloin pork belly boudin, bacon pastrami rump chicken. Jowl rump fatback, biltong bacon t-bone turkey. Turkey pork loin boudin, tenderloin jerky beef ribs pastrami spare ribs biltong pork chop beef.", + longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly boudin shoulder ribeye pork chop brisket biltong short ribs. Salami beef pork belly, t-bone sirloin meatloaf tail jowl spare ribs. Sirloin biltong bresaola cow turkey. Biltong fatback meatball, bresaola tail shankle turkey pancetta ham ribeye flank bacon jerky pork chop. Boudin sirloin shoulder, salami swine flank jerky t-bone pork chop pork beef tongue. Bresaola ribeye jerky andouille. Ribeye ground round sausage biltong beef ribs chuck, shank hamburger chicken short ribs spare ribs tenderloin meatloaf pork loin." + }, + + subArray: [1,2,3,4,5,6,7,8,9,10], + anotherString: "another string", + code: new Code("function() {}", {i:1}) +} + +// Number of objects +var numberOfObjects = 10000; +var bson = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +console.log("---------------------- 1") +var s = new Date() +// Object serialized +for(var i = 0; i < numberOfObjects; i++) { + objectBSON = bson.serialize(object, null, true) +} +console.log("====================== " + (new Date().getTime() - s.getTime()) + " :: " + ((new Date().getTime() - s.getTime()))/numberOfObjects) + +console.log("---------------------- 2") +var s = new Date() +// Object serialized +for(var i = 0; i < numberOfObjects; i++) { + bson.deserialize(objectBSON); +} +console.log("====================== " + (new Date().getTime() - s.getTime()) + " :: " + ((new Date().getTime() - s.getTime()))/numberOfObjects) + +// // Buffer With copies of the objectBSON +// var data = new Buffer(objectBSON.length * numberOfObjects); +// var index = 0; +// +// // Copy the buffer 1000 times to create a strea m of objects +// for(var i = 0; i < numberOfObjects; i++) { +// // Copy data +// objectBSON.copy(data, index); +// // Adjust index +// index = index + objectBSON.length; +// } +// +// // console.log("-----------------------------------------------------------------------------------") +// // console.dir(objectBSON) +// +// var x, start, end, j +// var objectBSON, objectJSON +// +// // Allocate the return array (avoid concatinating everything) +// var results = new Array(numberOfObjects); +// +// console.log(COUNT + "x (objectBSON = BSON.serialize(object))") +// start = new Date +// +// // var objects = BSON.deserializeStream(data, 0, numberOfObjects); +// // console.log("----------------------------------------------------------------------------------- 0") +// // var objects = BSON.deserialize(data); +// // console.log("----------------------------------------------------------------------------------- 1") +// // console.dir(objects) +// +// for (j=COUNT; --j>=0; ) { +// var nextIndex = BSON.deserializeStream(data, 0, numberOfObjects, results, 0); +// } +// +// end = new Date +// var opsprsecond = COUNT / ((end - start)/1000); +// console.log("bson size (bytes): ", objectBSON.length); +// console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); +// console.log("MB/s = " + ((opsprsecond*objectBSON.length)/1024)); +// +// // console.dir(nextIndex) +// // console.dir(results) + + diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/binding.gyp b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/binding.gyp new file mode 100644 index 0000000..42445d3 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/binding.gyp @@ -0,0 +1,17 @@ +{ + 'targets': [ + { + 'target_name': 'bson', + 'sources': [ 'ext/bson.cc' ], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' + } + }] + ] + } + ] +} \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Makefile b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Makefile new file mode 100644 index 0000000..5c7ea72 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Makefile @@ -0,0 +1,359 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := .. +abs_srcdir := $(abspath $(srcdir)) + +# The name of the builddir. +builddir_name ?= . + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Release + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + + + +# C++ apps need to be linked with g++. +# +# Note: flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override LINK via an envrionment variable as follows: +# +# export LINK=g++ +# +# This will allow make to invoke N linker processes as specified in -jN. +LINK ?= ./gyp-mac-tool flock $(builddir)/linker.lock $(CXX) + +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crs + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crs + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds, and deletes the output file when done +# if any of the postbuilds failed. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + F=$$?;\ + if [ $$F -ne 0 ]; then\ + E=$$F;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,bson.target.mk)))),) + include bson.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = /Users/ck/.node-gyp/0.8.11/tools/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/Users/ck/coding/projects/js-bson/build/config.gypi -I/usr/local/lib/node_modules/node-gyp/addon.gypi -I/Users/ck/.node-gyp/0.8.11/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/ck/.node-gyp/0.8.11" "-Dmodule_root_dir=/Users/ck/coding/projects/js-bson" binding.gyp +Makefile: $(srcdir)/../../../.node-gyp/0.8.11/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../usr/local/lib/node_modules/node-gyp/addon.gypi + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d new file mode 100644 index 0000000..20963f4 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d @@ -0,0 +1 @@ +cmd_Release/bson.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name /usr/local/lib/bson.node -o Release/bson.node Release/obj.target/bson/ext/bson.o -undefined dynamic_lookup diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d new file mode 100644 index 0000000..f224e00 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d @@ -0,0 +1,34 @@ +cmd_Release/obj.target/bson/ext/bson.o := c++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' -I/Users/ck/.node-gyp/0.8.11/src -I/Users/ck/.node-gyp/0.8.11/deps/uv/include -I/Users/ck/.node-gyp/0.8.11/deps/v8/include -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/bson/ext/bson.o.d.raw -c -o Release/obj.target/bson/ext/bson.o ../ext/bson.cc +Release/obj.target/bson/ext/bson.o: ../ext/bson.cc \ + /Users/ck/.node-gyp/0.8.11/deps/v8/include/v8.h \ + /Users/ck/.node-gyp/0.8.11/deps/v8/include/v8stdint.h \ + /Users/ck/.node-gyp/0.8.11/src/node.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/ares.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/ares_version.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/uv-unix.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ngx-queue.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ev.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/eio.h \ + /Users/ck/.node-gyp/0.8.11/src/node_object_wrap.h \ + /Users/ck/.node-gyp/0.8.11/src/ev-emul.h \ + /Users/ck/.node-gyp/0.8.11/src/eio-emul.h \ + /Users/ck/.node-gyp/0.8.11/src/node_version.h \ + /Users/ck/.node-gyp/0.8.11/src/node_buffer.h ../ext/bson.h +../ext/bson.cc: +/Users/ck/.node-gyp/0.8.11/deps/v8/include/v8.h: +/Users/ck/.node-gyp/0.8.11/deps/v8/include/v8stdint.h: +/Users/ck/.node-gyp/0.8.11/src/node.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/ares.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/ares_version.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/uv-unix.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ngx-queue.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ev.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/eio.h: +/Users/ck/.node-gyp/0.8.11/src/node_object_wrap.h: +/Users/ck/.node-gyp/0.8.11/src/ev-emul.h: +/Users/ck/.node-gyp/0.8.11/src/eio-emul.h: +/Users/ck/.node-gyp/0.8.11/src/node_version.h: +/Users/ck/.node-gyp/0.8.11/src/node_buffer.h: +../ext/bson.h: diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/bson.node b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/bson.node new file mode 100644 index 0000000000000000000000000000000000000000..f8f077922b89902d12fe8c3db7c77f1c2d967a3e GIT binary patch literal 45292 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 literal 0 HcmV?d00001 diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/linker.lock b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/linker.lock new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o new file mode 100644 index 0000000000000000000000000000000000000000..af87b6b09bcd882705b99664be784b3b87c92441 GIT binary patch literal 256544 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

~gg$pR+~8svNUry&Zo>-?I%EVoz*7Jrm5K( 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%0AH-3MB_t0&->CkLu%tQQt)CDBAk%nPwS+_ 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ξ3F3OoyF3Y zwh*_CxRs^?ZV`LgM?2J_hxaI{Fm^@37tG~CyM(}82VS;uK9P3xFf~Q z6W7bXj5{>`XmNAIogl6k{~`&$SlsrN;BS}scg5W+?p1f{e7*FCFV^8B#T_GVp}04R zd$YLS^fyTO6mf49*PHIvFzLUM@N#k6-=)hFE3P*`FaB{!*H-ZV-SnNq+W$gf1@8ciW#b3gW_yqsXG-x`9lgm=7CNRietk82VBD)je zQv9=*Jy+nNCISb-jY!77AX-PPs$+m)wdFtl=(z&$4IjnIFQI);72ioA57) z){2O#w<6qAfgvc}`Cy&J0sRxW_n;hAC*U88sbcQNgS{MCkB3@_c@M7j8vfY}uibdK z(4_eV4|Y8-0DYG2Qn=Nc;h$YLlyy4t#g9cex$MqaR@K?6kal8vh9YPOGh7JIdw}cl zQ1x2;v)eBp4=h(H(WctO%^ZLlcRx-p2#WwGVDJ7C&scwxV3mt8z6n ziRt+WFdFHsV}NLY`qc;Gm++~WhNvFbPCejO?TUZ)LXezii6_FfhU1@I<@tEXHUy$T zp9Cl$Ra@d;G#=Ei4<1M$6RtHD|5%r)aH|#KU)Z`hJZ`SY;2%A2BU80|{skWPle0ji z>KXXQ2lZ=;2WEH~T&peq*#rjSfjm|OvhZN@cs3qbt~=pYE5pCA6FzH3_Qer>%)mOn6Lk?WOpUUNQ;}4qnzw02hVNv*c}I(CnV@9mF*S-+`Ak!0-OE(tB$s|+2eB@S ze(3u2HuVV|-N!_m>Lv_HNKZ?i^L>Mq(!WyX_^rs4vgviDX8vs`l=6cCsinHY38kC; z^Y+ik^6f#0DVL45CAn<+w$#$2hQiK-qyCx^#kCEgxtoeFPB{r}abd*CP01rSr9?2` zM^t^$kew>!$u|?a?k!$UOmMZqbqUue3{#wa(_8LVEO(hdD!J6ZI(bpg$dpol6spj` z6kwCd83-GUU)Q0|Eq8z$zC9@NhTw{%mKL+{bt8(-OkPy1i(W&KvS-c}j`X0&oK<2G_5K-wUHb&Il8UkKOdMjO(ha~@N1bdDVz zx`XJfRTokGVR?T^E=yDcNn6yosAY0lo#e}+idu)xzsSyD-pS&C9|)Cm}hbkD?i^r0R^Qp&nFSJ~5p zB3z*!ey-DY)6aD--8QOD`)$SNPpOx%rFz}uvc6H2mc*#Y@&~FzhPG6XAPau3b2d6u zVpLRlDMFLWY9yDwo;>GYRg>rZ9#Pypxvb~OnI*NXl7_&qV&{gQvm0Gtclqy_m{gYC zBZ{Mef{rEyVt&bs21O*7rbR7?C~phmb{vpdrbtvVwe)~WUtY_D&;$rI?G%cdE`C(` zKFkMpO!tU7^Jar>K(E!WEUvkc=VVL<5YphDmK>4DE~s){8BtWnUC=ENg)6IggK}0z zG6>Rw9++I(7d>!%;G9Tu>hx`( z-5|Ma^#)YRjIcyI2WaLwrF65#Dj8!Kh{nGXnVuu5H@Q-qG0r6dg0(FKR`~~iojj@I zH>^!zVKW*mR7MQdxlAa|=kB3MKmSE=bZz_Kak&^pN7YP)z0o!?5R-Y8Zbu8dL^^+t!I?0>7I4dub?#~DyJ6P{P6mQG)7o+0o_t&b#> zRxdAr)Pl;HcFI!!N?@-vnB@b2kX-iS0#|L=EgPYbNLMwrY$S3|YF>@mCu~V-Ueik0 zipS=7*~P{)LeRifsgy-U%_A`tu0Rn1UmiSh;^axG209_=Z0#MP3FE82}!>c1_y%yN%;m)$bjxfs-x&J49$QstY@T?)}Z>a%;gNq z)$7knNKzvTqrNL-BPu@Xe)3(WXPOusmSr!Yh!%RAmE3g~Ct2Za(+W6S9Et4Cz|=R8 z%Nnko^$PE*G&AMtr9Zp%$xWr`t!RfiTOiwz@v@&7=I7_xod=sCN#fpT+2foUC0z1S#zWq<<%R zPHO34*IL%5meBP}MX-2%!WKW;Rh6c!-!@s=gMs=|59SA=8)X-duJWW`G#w zPbsT`6~H#E0Jf_Yz*$pjWAlV{aEV9gY( zmjWDmS702osg%B{u}a36`jBc@wHjw?H949ncbPwH}0*va^Zku~ZBG=Jr7qm=l(X zlDz99@8f~w%c~#-M)R3L*mdU4Vpy5XO`30}5SWjJt9oto=My@sF* zp7b3(xB=5QStp2k!VpC4yEqPL#9N7|r9{scHnmNj`J&A!jcRq~zAss$Fq?E9Z4gqf zN~GIorQ(j>*JD?Foz8y)$A44V?|PdKkG|Hy_zg=j)GN6#V}fuxsq_d0DCJA9UEPln6^=1B@#dT2FF0L)ELRV#WcA)yCNaU-+eJ+@3|G4MH zUT{%khS>61cDZ`=oU?4LhSoZ%VtA-C>`iDTMqg4GJ-)cGC^|2{C|ao>M&E#2&ZAp4 zZ%OsVfN1Cl6^+Qri*DJzrPVJ7N>F*Dqw%e#==@R9Mp-F(JU-%-Ju$lF^)W5+Rn45- zEGLQzSJ_#G(OQM8WnpwC^~?%!Q{ki$xwtx$*+K0qS}URT8j%%Ei?nXhNuui*ol_W{ ziwg*&q0E>U-6=W~H#p&f1i`b>x<%W%T+!5O!lmR{(Z$eDEXW>-8$Y78wiXK=NT6G^ zQgXEERFa)SKHa>AE=5hXl#Dm1E1sK7d_NH4!ma1d#}EukLUP;Z_IbA*SY=BI-_a2kt?HM zrkYSbDWaq!8`22@yN-3~VCvFA*QEpN(m{Q0p@XhV2VIvAc3nE?x^&QW8D{m*k0xI! zm4(n%jn2ul5+;sT7uh>Ox`$+DM?>zEbW(a$GHXB{LQR5+*%OL$CW1=4Xx&b}_F)kU z)h&@gu(MO$keNhlCE;lF1+9zM9&I^sVsSxHH02~3?OZrwbar%i(+Q&ac??T8GR2mC zc|^2fp@}fh(OKwhMA53ftd!Q9WLeur7u}H0oY5Rk%?g2QSM55DJ&ai5Z;QU1j}N`& zg$#6J|L1cERVX`u+cV5A2(P+L>{Jt zv}{a+S;2TCqV@EGcGh*!MaK14ITI^Jue@%mjj)U`0%#Y`9zj_(s#CjH4_*#gX*ne! ziG(S6Z|Dw(&QlLrdTC?KE84g30HcKt98%kl9I3y8qCd;lVF((ju*1l49oo}0rQPVs z1s!7AcWLkQbts%TvIE0J<2-{__U*@5P4&E<(H=Lhk1r^i+^V%Zh@WUf2xjZTjOZTG zF@fJ~Q*(Lr<(G35Zr4KySxwcGN}>%xK-&JeC5UmpzN8r6kz)x=X0GftopwEFp2<%o zs5{El)d#Iw>oPE1aIxK=&U0ejIdcK|zy9MJX8t0k47MUl>PFP9T_fr??5LU}jIY(N z&cL<`zZpd0FX61Z^Q$DDS>3PI!HJgyct-HJBT+wtcjcT&DvVRHBNq@qT+@3+V>$G$ zoKs_A^+nZgiM$oqI{*9NZWjDFFTOl8|IL7%r-1i8@b(Ixt;=o7>7rK;^?z0Hu9k4S z-Yz_9yc`z1ODn+30^Sk9>sJ9DH8IW~q4|?i0Uk9~x(Hsg3h?#O?D<33vb4FCGhslL!|J>7oS1Aj)Qt`E<90`ODjC~UUhT;RoV`;K2l z`L6=raKW1@S^Z2qhrs_6^^3ew*RQvrN7WBsKfb8lDD=biCzgZqzg+Oz8@jbT zbY1;rF6iDT_+13w?l1C8zO4e@D#3f!i`UDUk3Ixm+`g0Q_gok}mh(^Gy&-si!>@jZ zUmiJP{%vbm)@|Qvx>Su(KZ7szQS}GjYQcNeix;9D<^sRRK~47>=|^_`J>?+%^}rwV zy~gJ&LG?4`@X%+u_XGcX!LO*@YD0eB6Fj@!3O)VV)o$IRENi#mtAjBX#FRtYBRMUB znx7We4}St`1NdWDgr%7dF;u#We5whcJ{)bRSVP)4QAw?VJX1B2XL5{d0=FrEb!-V} z4IqD5cE)#v%l;n^pj=%BU^%YY52Pf{w08=fO`PT0QUnP1Tg+#xQ_u=0-gY@0z3m)16U6r&hv2jI+Om5 z;%|`MKjq?YO@7!Wq4i{~ng{`nr|VYA(BV z?cKM$S}*_j>)-7vdGYt1J6ew`J^tlST}Di=ci{JWku!V|XZ&)*LwC0SBIe<97UXVT zmb_;BPxV*Lp7wR|z{xi)c)VJAPQkj>A3yp^;qjjL4ch$O^J(v1H23)%zi!v)qi&;~ zTXnKv-9vxdz!@tbXLX?YW;W$-Z#>@MX_F^mxf7?eF~b>=W((*}g?? z+N?{@Kh(I>17G}=SuJ-%OwYx`K4~!XysvL~viV~@kLRt4NSJW&z2l48#q{diEc(KO zcMo~+(t79hd-R^x>#jL*tbP5iAMYD~dy7N8>qjhjd*!rO=M}zD^SlEc7tLJq=!R>a zzAoau;-AWfPVv7w@}mRI@4luix5o=t^!set1-ET$ctOk3H`15)ylLa=!V9|J_Cb2{ z!8112EWEN=w-*D_k1}2%Cmm|VD}GqTsZ9PUk3CZ)Mw1* zWzXI|=#$r?7It3O>z>{dP~PabZ@M6A)bewVtSCRar(TPdoi9wS_4MmK*PM9l`NbFP z?|I_ur{cTiC-lENi`{Elb7c5Sz|KUYn zbf~?cR->hNuG_voa!jYacP2+)(I@7Q35)*u{^FC*#hv(Kz?7z6UOKDVc?VLKe30I{ z_LP_W!%Cj(HsFhs|M=sD&zd~@TULX!-|c+HO>OedyzH#5uMfH=JN5U0{%Qk$y6MwT zU+n*2i!Qg+UjDmP{rm;Hf4Ow@kK=0ny#KNV5B7{*@ZuW}&8m0c`1j=>zg2a1tvUza z-totcX-l^kY+iQzE5E+C?X?yA$1JMd_58UlyX|}Q-1cMdc(Zr>hQ}Yf_S4rtJhb+* zQMIezGX2D?6Vv((PmUVY@0{nq>wa}!WQ+5P4n*J7rc1TNYbRHmc;8Q>n>_JDi?d4~ zt~xk!ZtlbB(>^b}??CV|H}UEPkos z{!QB(U-HM!oWV^#%$Tz`Wl-bHqBEY2`?&i3hZ=nS(kpu!|M1F^smGH`N}9JGT=lH; z4s@NjblM$RZ5F>e=in2+KD2gZxBE(W-5D3(;`TG2C@Jas_Lhh{c8$8L&KXfZy|@0V zZx((1^s;BZNjqK>V>l)s8rOCQTn{J!%mmy9SPj?=*bn#>P#cp#TR>01K)`UoT)z-quIz+S-jfIk6s&%_!U5Ccd8qyw@5(*TPBD*?L! z2LZ<2{FMSTF>0jYr1 zfX#s2fL{TTXCofa6EGZ50GJ854X^^R3GgA{AmBJ43KL^Uwg?j=90)_*o0Tu%81FQn<1$+Uowf=c>s%&$k4> zBl4^Y{F2Whdw}-={0)`7Zv{^pylGi4gC|?Tlh=hOBd6-T^X|dgX@yE+t(l-{PoJZo zmoL}PTcdQ|O~Iq;i24zbi$Q#1{ ziMDFF0!BWM!aNDUU;1brR~6-3j`A%*zFSV}xGzOEeqN%V+d$4)zdYb97o0N|>%5mf zu5ngNUf&_FD^_5ihJ5ajyhecz%Xw&@#?28~`{Wt@d@NVfe{?q5VxA6rW0g)bM%t!G z(k%N($G68b%f$TOk}@pu$iS`nn#RvUbI~PQ-m<}8A9y=n_}UP>s0vy)A$%mlmq>Uo z@R@mh`IcqfEHYc%LGQh)t^mIQYaRk`ABH?VYFSSLz5*ZGuC%Ow0QlQBS(jmn^t(%> zj6X@43vShU^xAJ(XGiFEZaPi3O+Uz949c*0g=Kw=JotMuOdX~OopuMYhXnA~)x+og zC{uNWH+{sio=2V9Y_+V1Kg3?>JIi`pWSbi+%1o%dKSSG+9|=gCBK)Xvzph_D8H;A5 zSk@W9DPIl4sWp~03-B)Bg0-+K1FQl31xQ(ES$6^W`*w~t^jZK}y!Zw5i47m}3v<8+7#GlY;?Z|v_kdqt!@%k=lKupF{u$#j zBrm4A=Ieb=>9W?7wz&pnXMNiIZCSqw?^}%1vhWw$hj_gO_ki%yjKjJ2YFf*r-qvmU zIq@6T2|XgT_8HN>5Xw8I{Rq1r>3pCB&-L>4Z6)Us+Iz9QKo?c-=a z*61Fj=kNLg{k#}?@psW3`Yd4EzrfG0Fo%IB4fkW*16=qG_MU*F2e9A#7G{(O!B5O3 zQvnwqf-C?ofjs;Lxb1uF$A85B7EtGiW&I1#{}-J{UzC-+JBBi>MIHECC;Y4je%@PC z^Bh+WDR~Mt&YTAx5bK;e8u#Gsy8box!MqW9H%5Pa3T=7hH?)O|=Hrl2=3N!}wvxQ> zouzpjZqB#|eTlg|D&<<=Mn9XeAzsEa&O`kDfVqtLA1qam))thl8oV|31(yxM*3~_9 znUc^p*8})_{t_Kl8?tzN4PD3KG9G-nRpXp9MduUyihlkL@)rqj``NH{13WetvJT)c z|4EH&WU}uqSbrhB?>@{sXwM`-E?_a>SMcoyv{(D1Oo=u`KIGX1@Zs9!D#(hNpk=-L zJoKgcSeF6#yBc+^3b)lh)ETq}9AhZPv|9R1$jJ0i%zJr&^`JR*1;&yGQI?0X7DN5t zgM3tf9I^=L3@~etGSoBaU+Ck2eUQt{{gyQo@D*U>HyCFDuOQDR-&$55z-+)4z}=A3 zgMhA((_a9Ekkie8mXMR)kk5MoT~OZ@1jtKslzATD2;}B6%vr6`zXt&x0_0VRu+{;N z0nS4Iy#$aA*ar9#a1^i$txJjHyNF zKO9$QfsPqlr=VX(f{)|(q3nojj51#(?K1)Gb0*?$0zbKanhUw3?9E*UzMx-4zO2_& z3peU@Rp-5dYpbt1XxV5cZJ0Sn%WLsVmi70WmX-64Wj&3t@^Orj{Jog3<$*G0K;i$CCyYIuAj8dv8M@1$5nxxfpqLdKWVA z9@bU=#CjQXOUB1ohkl~6v=)FqWzfBLO$ASCAZ+rNm~+6B7tx-R(SCRTg)>C(uCwrb z%}QM#@|^r8ugPQbm%nJ~S7!bC0c4vo>t?BF>lO4B^8U!bAb*gfhap2lAS?WJzER^o z13vRtbBTVghH~&1kGc}*A-)n;=^62Gh~gwmrHe@n}Bw{ z1iafX?UdswTht1jwirB^utO!ao>-;nvk&ukP796WUa!4)uf{QJj2h$ha~9-;ztm}( zzh5s$Uw!~`0(cLyeFDJW7Pu_wtcTR2l_%rleDI3D=a=dDn`IsyG*iKxW3?Qld$_#KeF z*|K&4nr^}V2vC6W<4wTdfH&T;te*fG?}9eqBS2k@C&hr(fbRe;-nXm~fI}G1F8`-x zJp|YVh{t%74Y>DXqyyCb1Y;3kKHz)6g}X7{0&WNF0Q@O^?1s2gYx{W^tJnuJKf*u! zB5&3F;+oIM{R#BxD8zAJ%yn`Z>i0Z=dt|Pecc6S#C4G^MD`r3Zq|i6}uJ_yOcQxZ7 zqtjPf)(e220BxQ?Jpm5@J_Vc$+3p9p3Gh1L4?ri#TRz|kz}JArklUev+X3$Ysy&Ty z2{0M34)6n@Sh2!#&G3w0XTXSVsd^0geNb)?#f4*b8WdzET9>@1tp&KXo3^ z&jT>#REO99H~4|M_F_OcfFCeO`s}liIr8)faQSNmmom5yJnJfaioHgUC4awwI-*Z5 z+>W+JpNx?{$&0W^1n}=1gun3|*4?17eS>9nd&#l}zX7>>6Kj;W zvHp1nX?Ed8j1NEubaNpaW4{Jp4`VI$6B7Rdxk4^uWiQcsvCe~M7yMmQ`~Uy_WZZaN z=jyX{ohRI;pA!%1=R|2c_uRAzvd6aJo|wOd1%WgUc2LKfmRU=l>RQjN?@o0M$GpFk zvb3QB2Y&M)uaq_SKH{%@9kvs5&r|ahnl*HZ4&!>joF`p{v^5bu8g))xhCYe4!@LJE z-T{U_gtZptpT&=14KCyTff>5q?*o^=`)}9t#9!Z|fBb+m8ps)cHU9@>b;@a$^$yg5 z95VZ|_P6SJweMHh%c8BX1Mv&Rc>46Jv3={Ii)U*4Fi(}&v=|AKgy zWhVH{UpJJ6&pa>T@3$%X*_?shhOv$D+|%&a=T7}>)+x_T(9dRFcqw>O4S3CT^w2(x-57KE zI|eyBAMXF;SwN$Snx|$hvk`f6+;_`%JFHFm0XEC{*+Sa&)q7Y<@ZxekZ}O~?XNWCV z=yOD#S@JBA=a%L?@pjN>o3+{p_kiA?y3cU#L|?3mcyFFP!B6&g_uNuPX*uwo z>;AA@0)l`nUIt;KyaU zOlEGXHC^`wbAHctd-+~n-fz%`tW&L{&Y9phn3Gs1p5gKLF6u-a_xWA*aT>?G`w75|MdCzyE68fx%S;GU4QQR%~^94 z`dmYl<=;8SbDz&ubS~#TZ@XZn=8rj7eOu&_dn@jz_)C;^g?WeAT%J38uH$!N9l$w( z=W6CmjWL(XcL>8Ej)C zf72h;I6S8?XB535uf#ED0Np`{&*qGx588vz=8SKR&@}rkqeDm>{%UJSrJ)WXb>|5Lq}P0JWQ`Llwj7Sw9iE^gtv$=4`dK1_|)0syvC)(${x$Uf6< zn~C{q5zg{3fAP1!rmpKJQ8?3Uti$q9NCaCg0gu%{pFvz4I!+|wM_q*VF=N`(*0m18_%n6u5C46DdJM#mWn%rSqQ=#f)}*c<(=v{ABri}5 z)pyC0I)JkQ=K#nDuA$fmFOcU8#cc!^MYWm(SkyLvc7XN(4Et6LAQsRO5C`ZC=mxk9 z&>heN&=bHs65%F^-w(Gppby|m0P8dmFcg3(R`EXr?g+q0KsI13AP)esQS=JovW>7E zw$Qbe&g2ZZ5KC(wU_Rhx0Gi5TQ?U#fdX(psHyKC#p{kv(|rRzq1Jh0}uqIwUR$)IM%vio=hn2DvnO-@qWHhRJS%4Af?6?kemmS{K@xj=S&TRI6!mWo# zu08Nk!&P~;)3>Z2Iql`gIt?iM`qk}U@W{h=KahF#wQYWVu8MW^#w(t>_m7u$ zZg{BG=Z`E~9d|{>;3vOpocnj(D_%Z-?}qo@d*;}PTQ2y0{g0U`y%R zCGF4rFmCG9)g$M;*I~G!L$M{-;0q-FZ@1@;d8y^tRx2Oa zH~8%63%|d$*&W|pfAp<0?)z7@38OOO4zHj-=Y$hy)NgZNdEXihUT*)(qfM;A-`5z~ zqV=wqoAtT!tect)S=Ikaf6tBCS1qFcrmYD8OXELICewTMKv#up96lfI8`Cqw_WgbOww9Oa?3jJOEe=cnh!_@Ezc9 zz(p8uVgY_Y24Es!9$+cpDZs0MU4Z?7V}LU-STzR301^RL0dfG-0Jj1j0z3!U2G|2Q z42ZyZRu9k;&<)TJFdR?_m=CxIunO=R-~+%nfa8E#webE6-~%K9h5{x4sMmKl;7PzM z0P6L94LAy@i9xFopaY;6UFB&jGdp_5cn8BI<&7fR=!6fPR2Hz%0OGz~g|I0Ph0!0e%5g$AEnSfck+~00slH z0VRMkz;eJEz-GWF0O|+U!T{d{-~;q{K+h-L|Kwc58QkJ)IZhLkZ?c1WOw>WX>+PVH z7_Eu*DMofl*yIu&^hIww=y#0u#LA4z4=o>=$VJY}GD7XZ@ zit;FCV?>akEOZVweQaYDv<&=`n287>@}X!u=*h7faEjK+LG{QDc%$mj|&X^O4@3yWtZAm%}`S&ybP`+j71F?)CT{g z^lPyCV9yQWU{yssGw7JpD$kD5L7$}CKMoP86Nwcv&TA;D^I%0A;gow0` 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 +#include +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif + +#include + +// this and the above block must be around the v8.h header otherwise +// v8 is not happy +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __sun + #include +#endif + +#include "bson.h" + +using namespace v8; +using namespace node; + +//=========================================================================== + +void DataStream::WriteObjectId(const Handle& object, const Handle& key) +{ + uint16_t buffer[12]; + object->Get(key)->ToString()->Write(buffer, 0, 12); + for(uint32_t i = 0; i < 12; ++i) + { + *p++ = (char) buffer[i]; + } +} + +void ThrowAllocatedStringException(size_t allocationSize, const char* format, ...) +{ + va_list args; + va_start(args, format); + char* string = (char*) malloc(allocationSize); + vsprintf(string, format, args); + va_end(args); + + throw string; +} + +void DataStream::CheckKey(const Local& keyName) +{ + size_t keyLength = keyName->Utf8Length(); + if(keyLength == 0) return; + + char* keyStringBuffer = (char*) alloca(keyLength+1); + keyName->WriteUtf8(keyStringBuffer); + + if(keyStringBuffer[0] == '$') + { + ThrowAllocatedStringException(64+keyLength, "key %s must not start with '$'", keyStringBuffer); + } + + if(strchr(keyStringBuffer, '.') != NULL) + { + ThrowAllocatedStringException(64+keyLength, "key %s must not contain '.'", keyStringBuffer); + } +} + +template void BSONSerializer::SerializeDocument(const Handle& value) +{ + void* documentSize = this->BeginWriteSize(); + Local object = bson->GetSerializeObject(value); + + // Get the object property names + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + Local propertyNames = object->GetPropertyNames(); + #else + Local propertyNames = object->GetOwnPropertyNames(); + #endif + + // Length of the property + int propertyLength = propertyNames->Length(); + for(int i = 0; i < propertyLength; ++i) + { + const Local& propertyName = propertyNames->Get(i)->ToString(); + if(checkKeys) this->CheckKey(propertyName); + + const Local& propertyValue = object->Get(propertyName); + + if(serializeFunctions || !propertyValue->IsFunction()) + { + void* typeLocation = this->BeginWriteType(); + this->WriteString(propertyName); + SerializeValue(typeLocation, propertyValue); + } + } + + this->WriteByte(0); + this->CommitSize(documentSize); +} + +template void BSONSerializer::SerializeArray(const Handle& value) +{ + void* documentSize = this->BeginWriteSize(); + + Local array = Local::Cast(value->ToObject()); + uint32_t arrayLength = array->Length(); + + for(uint32_t i = 0; i < arrayLength; ++i) + { + void* typeLocation = this->BeginWriteType(); + this->WriteUInt32String(i); + SerializeValue(typeLocation, array->Get(i)); + } + + this->WriteByte(0); + this->CommitSize(documentSize); +} + +// This is templated so that we can use this function to both count the number of bytes, and to serialize those bytes. +// The template approach eliminates almost all of the inspection of values unless they're required (eg. string lengths) +// and ensures that there is always consistency between bytes counted and bytes written by design. +template void BSONSerializer::SerializeValue(void* typeLocation, const Handle& value) +{ + if(value->IsNumber()) + { + double doubleValue = value->NumberValue(); + int intValue = (int) doubleValue; + if(intValue == doubleValue) + { + this->CommitType(typeLocation, BSON_TYPE_INT); + this->WriteInt32(intValue); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_NUMBER); + this->WriteDouble(doubleValue); + } + } + else if(value->IsString()) + { + this->CommitType(typeLocation, BSON_TYPE_STRING); + this->WriteLengthPrefixedString(value->ToString()); + } + else if(value->IsBoolean()) + { + this->CommitType(typeLocation, BSON_TYPE_BOOLEAN); + this->WriteBool(value); + } + else if(value->IsArray()) + { + this->CommitType(typeLocation, BSON_TYPE_ARRAY); + SerializeArray(value); + } + else if(value->IsDate()) + { + this->CommitType(typeLocation, BSON_TYPE_DATE); + this->WriteInt64(value); + } + else if(value->IsRegExp()) + { + this->CommitType(typeLocation, BSON_TYPE_REGEXP); + const Handle& regExp = Handle::Cast(value); + + this->WriteString(regExp->GetSource()); + + int flags = regExp->GetFlags(); + if(flags & RegExp::kGlobal) this->WriteByte('s'); + if(flags & RegExp::kIgnoreCase) this->WriteByte('i'); + if(flags & RegExp::kMultiline) this->WriteByte('m'); + this->WriteByte(0); + } + else if(value->IsFunction()) + { + this->CommitType(typeLocation, BSON_TYPE_CODE); + this->WriteLengthPrefixedString(value->ToString()); + } + else if(value->IsObject()) + { + const Local& object = value->ToObject(); + if(object->Has(bson->_bsontypeString)) + { + const Local& constructorString = object->GetConstructorName(); + if(bson->longString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_LONG); + this->WriteInt32(object, bson->_longLowString); + this->WriteInt32(object, bson->_longHighString); + } + else if(bson->timestampString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_TIMESTAMP); + this->WriteInt32(object, bson->_longLowString); + this->WriteInt32(object, bson->_longHighString); + } + else if(bson->objectIDString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_OID); + this->WriteObjectId(object, bson->_objectIDidString); + } + else if(bson->binaryString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_BINARY); + + uint32_t length = object->Get(bson->_binaryPositionString)->Uint32Value(); + Local bufferObj = object->Get(bson->_binaryBufferString)->ToObject(); + + this->WriteInt32(length); + this->WriteByte(object, bson->_binarySubTypeString); // write subtype + this->WriteData(Buffer::Data(bufferObj), length); + } + else if(bson->doubleString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_NUMBER); + this->WriteDouble(object, bson->_doubleValueString); + } + else if(bson->symbolString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_SYMBOL); + this->WriteLengthPrefixedString(object->Get(bson->_symbolValueString)->ToString()); + } + else if(bson->codeString->StrictEquals(constructorString)) + { + const Local& function = object->Get(bson->_codeCodeString)->ToString(); + const Local& scope = object->Get(bson->_codeScopeString)->ToObject(); + + // For Node < 0.6.X use the GetPropertyNames + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + uint32_t propertyNameLength = scope->GetPropertyNames()->Length(); + #else + uint32_t propertyNameLength = scope->GetOwnPropertyNames()->Length(); + #endif + + if(propertyNameLength > 0) + { + this->CommitType(typeLocation, BSON_TYPE_CODE_W_SCOPE); + void* codeWidthScopeSize = this->BeginWriteSize(); + this->WriteLengthPrefixedString(function->ToString()); + SerializeDocument(scope); + this->CommitSize(codeWidthScopeSize); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_CODE); + this->WriteLengthPrefixedString(function->ToString()); + } + } + else if(bson->dbrefString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_OBJECT); + + void* dbRefSize = this->BeginWriteSize(); + + void* refType = this->BeginWriteType(); + this->WriteData("$ref", 5); + SerializeValue(refType, object->Get(bson->_dbRefNamespaceString)); + + void* idType = this->BeginWriteType(); + this->WriteData("$id", 4); + SerializeValue(idType, object->Get(bson->_dbRefOidString)); + + const Local& refDbValue = object->Get(bson->_dbRefDbString); + if(!refDbValue->IsUndefined()) + { + void* dbType = this->BeginWriteType(); + this->WriteData("$db", 4); + SerializeValue(dbType, refDbValue); + } + + this->WriteByte(0); + this->CommitSize(dbRefSize); + } + else if(bson->minKeyString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_MIN_KEY); + } + else if(bson->maxKeyString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_MAX_KEY); + } + } + else if(Buffer::HasInstance(value)) + { + this->CommitType(typeLocation, BSON_TYPE_BINARY); + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(value->ToObject()); + uint32_t length = object->length(); + #else + uint32_t length = Buffer::Length(value->ToObject()); + #endif + + this->WriteInt32(length); + this->WriteByte(0); + this->WriteData(Buffer::Data(value->ToObject()), length); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_OBJECT); + SerializeDocument(value); + } + } + else if(value->IsNull() || value->IsUndefined()) + { + this->CommitType(typeLocation, BSON_TYPE_NULL); + } +} + +// Data points to start of element list, length is length of entire document including '\0' but excluding initial size +BSONDeserializer::BSONDeserializer(BSON* aBson, char* data, size_t length) +: bson(aBson), + pStart(data), + p(data), + pEnd(data + length - 1) +{ + if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'"); +} + +BSONDeserializer::BSONDeserializer(BSONDeserializer& parentSerializer, size_t length) +: bson(parentSerializer.bson), + pStart(parentSerializer.p), + p(parentSerializer.p), + pEnd(parentSerializer.p + length - 1) +{ + parentSerializer.p += length; + if(pEnd > parentSerializer.pEnd) ThrowAllocatedStringException(64, "Child document exceeds parent's bounds"); + if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'"); +} + +Local BSONDeserializer::ReadCString() +{ + char* start = p; + while(*p++) { } + return String::New(start, (int32_t) (p-start-1) ); +} + +int32_t BSONDeserializer::ReadRegexOptions() +{ + int32_t options = 0; + for(;;) + { + switch(*p++) + { + case '\0': return options; + case 's': options |= RegExp::kGlobal; break; + case 'i': options |= RegExp::kIgnoreCase; break; + case 'm': options |= RegExp::kMultiline; break; + } + } +} + +uint32_t BSONDeserializer::ReadIntegerString() +{ + uint32_t value = 0; + while(*p) + { + if(*p < '0' || *p > '9') ThrowAllocatedStringException(64, "Invalid key for array"); + value = value * 10 + *p++ - '0'; + } + ++p; + return value; +} + +Local BSONDeserializer::ReadString() +{ + uint32_t length = ReadUInt32(); + char* start = p; + p += length; + return String::New(start, length-1); +} + +Local BSONDeserializer::ReadObjectId() +{ + uint16_t objectId[12]; + for(size_t i = 0; i < 12; ++i) + { + objectId[i] = *reinterpret_cast(p++); + } + return String::New(objectId, 12); +} + +Handle BSONDeserializer::DeserializeDocument() +{ + uint32_t length = ReadUInt32(); + if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Document is less than 5 bytes"); + + BSONDeserializer documentDeserializer(*this, length-4); + return documentDeserializer.DeserializeDocumentInternal(); +} + +Handle BSONDeserializer::DeserializeDocumentInternal() +{ + Local returnObject = Object::New(); + + while(HasMoreData()) + { + BsonType type = (BsonType) ReadByte(); + const Local& name = ReadCString(); + const Handle& value = DeserializeValue(type); + returnObject->ForceSet(name, value); + } + if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Document: Serialize consumed unexpected number of bytes"); + + // From JavaScript: + // if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']); + if(returnObject->Has(bson->_dbRefIdRefString)) + { + Local argv[] = { returnObject->Get(bson->_dbRefRefString), returnObject->Get(bson->_dbRefIdRefString), returnObject->Get(bson->_dbRefDbRefString) }; + return bson->dbrefConstructor->NewInstance(3, argv); + } + else + { + return returnObject; + } +} + +Handle BSONDeserializer::DeserializeArray() +{ + uint32_t length = ReadUInt32(); + if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Array Document is less than 5 bytes"); + + BSONDeserializer documentDeserializer(*this, length-4); + return documentDeserializer.DeserializeArrayInternal(); +} + +Handle BSONDeserializer::DeserializeArrayInternal() +{ + Local returnArray = Array::New(); + + while(HasMoreData()) + { + BsonType type = (BsonType) ReadByte(); + uint32_t index = ReadIntegerString(); + const Handle& value = DeserializeValue(type); + returnArray->Set(index, value); + } + if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Array: Serialize consumed unexpected number of bytes"); + + return returnArray; +} + +Handle BSONDeserializer::DeserializeValue(BsonType type) +{ + switch(type) + { + case BSON_TYPE_STRING: + return ReadString(); + + case BSON_TYPE_INT: + return Integer::New(ReadInt32()); + + case BSON_TYPE_NUMBER: + return Number::New(ReadDouble()); + + case BSON_TYPE_NULL: + return Null(); + + case BSON_TYPE_UNDEFINED: + return Undefined(); + + case BSON_TYPE_TIMESTAMP: + { + int32_t lowBits = ReadInt32(); + int32_t highBits = ReadInt32(); + Local argv[] = { Int32::New(lowBits), Int32::New(highBits) }; + return bson->timestampConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_BOOLEAN: + return (ReadByte() != 0) ? True() : False(); + + case BSON_TYPE_REGEXP: + { + const Local& regex = ReadCString(); + int32_t options = ReadRegexOptions(); + return RegExp::New(regex, (RegExp::Flags) options); + } + + case BSON_TYPE_CODE: + { + const Local& code = ReadString(); + const Local& scope = Object::New(); + Local argv[] = { code, scope }; + return bson->codeConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_CODE_W_SCOPE: + { + ReadUInt32(); + const Local& code = ReadString(); + const Handle& scope = DeserializeDocument(); + Local argv[] = { code, scope->ToObject() }; + return bson->codeConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_OID: + { + Local argv[] = { ReadObjectId() }; + return bson->objectIDConstructor->NewInstance(1, argv); + } + + case BSON_TYPE_BINARY: + { + uint32_t length = ReadUInt32(); + uint32_t subType = ReadByte(); + Buffer* buffer = Buffer::New(p, length); + p += length; + + Handle argv[] = { buffer->handle_, Uint32::New(subType) }; + return bson->binaryConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_LONG: + { + // Read 32 bit integers + int32_t lowBits = (int32_t) ReadInt32(); + int32_t highBits = (int32_t) ReadInt32(); + + // If value is < 2^53 and >-2^53 + if((highBits < 0x200000 || (highBits == 0x200000 && lowBits == 0)) && highBits >= -0x200000) { + // Adjust the pointer and read as 64 bit value + p -= 8; + // Read the 64 bit value + int64_t finalValue = (int64_t) ReadInt64(); + return Number::New(finalValue); + } + + Local argv[] = { Int32::New(lowBits), Int32::New(highBits) }; + return bson->longConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_DATE: + return Date::New((double) ReadInt64()); + + case BSON_TYPE_ARRAY: + return DeserializeArray(); + + case BSON_TYPE_OBJECT: + return DeserializeDocument(); + + case BSON_TYPE_SYMBOL: + { + const Local& string = ReadString(); + Local argv[] = { string }; + return bson->symbolConstructor->NewInstance(1, argv); + } + + case BSON_TYPE_MIN_KEY: + return bson->minKeyConstructor->NewInstance(); + + case BSON_TYPE_MAX_KEY: + return bson->maxKeyConstructor->NewInstance(); + + default: + ThrowAllocatedStringException(64, "Unhandled BSON Type: %d", type); + } + + return v8::Null(); +} + + +static Handle VException(const char *msg) +{ + HandleScope scope; + return ThrowException(Exception::Error(String::New(msg))); +} + +Persistent BSON::constructor_template; + +BSON::BSON() : ObjectWrap() +{ + // Setup pre-allocated comparision objects + _bsontypeString = Persistent::New(String::New("_bsontype")); + _longLowString = Persistent::New(String::New("low_")); + _longHighString = Persistent::New(String::New("high_")); + _objectIDidString = Persistent::New(String::New("id")); + _binaryPositionString = Persistent::New(String::New("position")); + _binarySubTypeString = Persistent::New(String::New("sub_type")); + _binaryBufferString = Persistent::New(String::New("buffer")); + _doubleValueString = Persistent::New(String::New("value")); + _symbolValueString = Persistent::New(String::New("value")); + _dbRefRefString = Persistent::New(String::New("$ref")); + _dbRefIdRefString = Persistent::New(String::New("$id")); + _dbRefDbRefString = Persistent::New(String::New("$db")); + _dbRefNamespaceString = Persistent::New(String::New("namespace")); + _dbRefDbString = Persistent::New(String::New("db")); + _dbRefOidString = Persistent::New(String::New("oid")); + _codeCodeString = Persistent::New(String::New("code")); + _codeScopeString = Persistent::New(String::New("scope")); + _toBSONString = Persistent::New(String::New("toBSON")); + + longString = Persistent::New(String::New("Long")); + objectIDString = Persistent::New(String::New("ObjectID")); + binaryString = Persistent::New(String::New("Binary")); + codeString = Persistent::New(String::New("Code")); + dbrefString = Persistent::New(String::New("DBRef")); + symbolString = Persistent::New(String::New("Symbol")); + doubleString = Persistent::New(String::New("Double")); + timestampString = Persistent::New(String::New("Timestamp")); + minKeyString = Persistent::New(String::New("MinKey")); + maxKeyString = Persistent::New(String::New("MaxKey")); +} + +void BSON::Initialize(v8::Handle target) +{ + // Grab the scope of the call from Node + HandleScope scope; + // Define a new function template + Local t = FunctionTemplate::New(New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("BSON")); + + // Instance methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "calculateObjectSize", CalculateObjectSize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", BSONSerialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serializeWithBufferAndIndex", SerializeWithBufferAndIndex); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserialize", BSONDeserialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserializeStream", BSONDeserializeStream); + + target->ForceSet(String::NewSymbol("BSON"), constructor_template->GetFunction()); +} + +// Create a new instance of BSON and passing it the existing context +Handle BSON::New(const Arguments &args) +{ + HandleScope scope; + + // Check that we have an array + if(args.Length() == 1 && args[0]->IsArray()) + { + // Cast the array to a local reference + Local array = Local::Cast(args[0]); + + if(array->Length() > 0) + { + // Create a bson object instance and return it + BSON *bson = new BSON(); + + uint32_t foundClassesMask = 0; + + // Iterate over all entries to save the instantiate funtions + for(uint32_t i = 0; i < array->Length(); i++) + { + // Let's get a reference to the function + Local func = Local::Cast(array->Get(i)); + Local functionName = func->GetName()->ToString(); + + // Save the functions making them persistant handles (they don't get collected) + if(functionName->StrictEquals(bson->longString)) + { + bson->longConstructor = Persistent::New(func); + foundClassesMask |= 1; + } + else if(functionName->StrictEquals(bson->objectIDString)) + { + bson->objectIDConstructor = Persistent::New(func); + foundClassesMask |= 2; + } + else if(functionName->StrictEquals(bson->binaryString)) + { + bson->binaryConstructor = Persistent::New(func); + foundClassesMask |= 4; + } + else if(functionName->StrictEquals(bson->codeString)) + { + bson->codeConstructor = Persistent::New(func); + foundClassesMask |= 8; + } + else if(functionName->StrictEquals(bson->dbrefString)) + { + bson->dbrefConstructor = Persistent::New(func); + foundClassesMask |= 0x10; + } + else if(functionName->StrictEquals(bson->symbolString)) + { + bson->symbolConstructor = Persistent::New(func); + foundClassesMask |= 0x20; + } + else if(functionName->StrictEquals(bson->doubleString)) + { + bson->doubleConstructor = Persistent::New(func); + foundClassesMask |= 0x40; + } + else if(functionName->StrictEquals(bson->timestampString)) + { + bson->timestampConstructor = Persistent::New(func); + foundClassesMask |= 0x80; + } + else if(functionName->StrictEquals(bson->minKeyString)) + { + bson->minKeyConstructor = Persistent::New(func); + foundClassesMask |= 0x100; + } + else if(functionName->StrictEquals(bson->maxKeyString)) + { + bson->maxKeyConstructor = Persistent::New(func); + foundClassesMask |= 0x200; + } + } + + // Check if we have the right number of constructors otherwise throw an error + if(foundClassesMask != 0x3ff) + { + delete bson; + return VException("Missing function constructor for either [Long/ObjectID/Binary/Code/DbRef/Symbol/Double/Timestamp/MinKey/MaxKey]"); + } + else + { + bson->Wrap(args.This()); + return args.This(); + } + } + else + { + return VException("No types passed in"); + } + } + else + { + return VException("Argument passed in must be an array of types"); + } +} + +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ + +Handle BSON::BSONDeserialize(const Arguments &args) +{ + HandleScope scope; + + // Ensure that we have an parameter + if(Buffer::HasInstance(args[0]) && args.Length() > 1) return VException("One argument required - buffer1."); + if(args[0]->IsString() && args.Length() > 1) return VException("One argument required - string1."); + // Throw an exception if the argument is not of type Buffer + if(!Buffer::HasInstance(args[0]) && !args[0]->IsString()) return VException("Argument must be a Buffer or String."); + + // Define pointer to data + Local obj = args[0]->ToObject(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // If we passed in a buffer, let's unpack it, otherwise let's unpack the string + if(Buffer::HasInstance(obj)) + { +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + char* data = buffer->data(); + size_t length = buffer->length(); +#else + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); +#endif + + // Validate that we have at least 5 bytes + if(length < 5) return VException("corrupt bson message < 5 bytes long"); + + try + { + BSONDeserializer deserializer(bson, data, length); + return deserializer.DeserializeDocument(); + } + catch(char* exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + + } + else + { + // The length of the data for this encoding + ssize_t len = DecodeBytes(args[0], BINARY); + + // Validate that we have at least 5 bytes + if(len < 5) return VException("corrupt bson message < 5 bytes long"); + + // Let's define the buffer size + char* data = (char *)malloc(len); + DecodeWrite(data, len, args[0], BINARY); + + try + { + BSONDeserializer deserializer(bson, data, len); + Handle result = deserializer.DeserializeDocument(); + free(data); + return result; + + } + catch(char* exception) + { + Handle error = VException(exception); + free(exception); + free(data); + return error; + } + } +} + +Local BSON::GetSerializeObject(const Handle& argValue) +{ + Local object = argValue->ToObject(); + if(object->Has(_toBSONString)) + { + const Local& toBSON = object->Get(_toBSONString); + if(!toBSON->IsFunction()) ThrowAllocatedStringException(64, "toBSON is not a function"); + + Local result = Local::Cast(toBSON)->Call(object, 0, NULL); + if(!result->IsObject()) ThrowAllocatedStringException(64, "toBSON function did not return an object"); + return result->ToObject(); + } + else + { + return object; + } +} + +Handle BSON::BSONSerialize(const Arguments &args) +{ + HandleScope scope; + + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 3 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean() && !args[3]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + if(args.Length() > 4) return VException("One, two, tree or four arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Calculate the total size of the document in binary form to ensure we only allocate memory once + // With serialize function + bool serializeFunctions = (args.Length() >= 4) && args[3]->BooleanValue(); + + char *serialized_object = NULL; + size_t object_size; + try + { + Local object = bson->GetSerializeObject(args[0]); + + BSONSerializer counter(bson, false, serializeFunctions); + counter.SerializeDocument(object); + object_size = counter.GetSerializeSize(); + + // Allocate the memory needed for the serialization + serialized_object = (char *)malloc(object_size); + + // Check if we have a boolean value + bool checkKeys = args.Length() >= 3 && args[1]->IsBoolean() && args[1]->BooleanValue(); + BSONSerializer data(bson, checkKeys, serializeFunctions, serialized_object); + data.SerializeDocument(object); + } + catch(char *err_msg) + { + free(serialized_object); + Handle error = VException(err_msg); + free(err_msg); + return error; + } + + // If we have 3 arguments + if(args.Length() == 3 || args.Length() == 4) + { + Buffer *buffer = Buffer::New(serialized_object, object_size); + free(serialized_object); + return scope.Close(buffer->handle_); + } + else + { + Local bin_value = Encode(serialized_object, object_size, BINARY)->ToString(); + free(serialized_object); + return bin_value; + } +} + +Handle BSON::CalculateObjectSize(const Arguments &args) +{ + HandleScope scope; + // Ensure we have a valid object + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One argument required - [object]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("Two arguments required - [object, boolean]"); + if(args.Length() > 3) return VException("One or two arguments required - [object] or [object, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + bool serializeFunctions = (args.Length() >= 2) && args[1]->BooleanValue(); + BSONSerializer countSerializer(bson, false, serializeFunctions); + countSerializer.SerializeDocument(args[0]); + + // Return the object size + return scope.Close(Uint32::New((uint32_t) countSerializer.GetSerializeSize())); +} + +Handle BSON::SerializeWithBufferAndIndex(const Arguments &args) +{ + HandleScope scope; + + //BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, ->, buffer, index) { + // Ensure we have the correct values + if(args.Length() > 5) return VException("Four or five parameters required [object, boolean, Buffer, int] or [object, boolean, Buffer, int, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32()) return VException("Four parameters required [object, boolean, Buffer, int]"); + if(args.Length() == 5 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32() && !args[4]->IsBoolean()) return VException("Four parameters required [object, boolean, Buffer, int, boolean]"); + + uint32_t index; + size_t object_size; + + try + { + BSON *bson = ObjectWrap::Unwrap(args.This()); + + Local obj = args[2]->ToObject(); + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); + + index = args[3]->Uint32Value(); + bool checkKeys = args.Length() >= 4 && args[1]->IsBoolean() && args[1]->BooleanValue(); + bool serializeFunctions = (args.Length() == 5) && args[4]->BooleanValue(); + + BSONSerializer dataSerializer(bson, checkKeys, serializeFunctions, data+index); + dataSerializer.SerializeDocument(bson->GetSerializeObject(args[0])); + object_size = dataSerializer.GetSerializeSize(); + + if(object_size + index > length) return VException("Serious error - overflowed buffer!!"); + } + catch(char *exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + + return scope.Close(Uint32::New((uint32_t) (index + object_size - 1))); +} + +Handle BSON::BSONDeserializeStream(const Arguments &args) +{ + HandleScope scope; + + // At least 3 arguments required + if(args.Length() < 5) return VException("Arguments required (Buffer(data), Number(index in data), Number(number of documents to deserialize), Array(results), Number(index in the array), Object(optional))"); + + // If the number of argumets equals 3 + if(args.Length() >= 5) + { + if(!Buffer::HasInstance(args[0])) return VException("First argument must be Buffer instance"); + if(!args[1]->IsUint32()) return VException("Second argument must be a positive index number"); + if(!args[2]->IsUint32()) return VException("Third argument must be a positive number of documents to deserialize"); + if(!args[3]->IsArray()) return VException("Fourth argument must be an array the size of documents to deserialize"); + if(!args[4]->IsUint32()) return VException("Sixth argument must be a positive index number"); + } + + // If we have 4 arguments + if(args.Length() == 6 && !args[5]->IsObject()) return VException("Fifth argument must be an object with options"); + + // Define pointer to data + Local obj = args[0]->ToObject(); + uint32_t numberOfDocuments = args[2]->Uint32Value(); + uint32_t index = args[1]->Uint32Value(); + uint32_t resultIndex = args[4]->Uint32Value(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Unpack the buffer variable +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + char* data = buffer->data(); + size_t length = buffer->length(); +#else + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); +#endif + + // Fetch the documents + Local documents = args[3]->ToObject(); + + BSONDeserializer deserializer(bson, data+index, length-index); + for(uint32_t i = 0; i < numberOfDocuments; i++) + { + try + { + documents->Set(i + resultIndex, deserializer.DeserializeDocument()); + } + catch (char* exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + } + + // Return new index of parsing + return scope.Close(Uint32::New((uint32_t) (index + deserializer.GetSerializeSize()))); +} + +// Exporting function +extern "C" void init(Handle target) +{ + HandleScope scope; + BSON::Initialize(target); +} + +NODE_MODULE(bson, BSON::Initialize); diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/bson.h b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/bson.h new file mode 100644 index 0000000..72ae8cc --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/bson.h @@ -0,0 +1,273 @@ +//=========================================================================== + +#ifndef BSON_H_ +#define BSON_H_ + +//=========================================================================== + +#define USE_MISALIGNED_MEMORY_ACCESS 1 + +#include +#include +#include + +using namespace v8; +using namespace node; + +//=========================================================================== + +enum BsonType +{ + BSON_TYPE_NUMBER = 1, + BSON_TYPE_STRING = 2, + BSON_TYPE_OBJECT = 3, + BSON_TYPE_ARRAY = 4, + BSON_TYPE_BINARY = 5, + BSON_TYPE_UNDEFINED = 6, + BSON_TYPE_OID = 7, + BSON_TYPE_BOOLEAN = 8, + BSON_TYPE_DATE = 9, + BSON_TYPE_NULL = 10, + BSON_TYPE_REGEXP = 11, + BSON_TYPE_CODE = 13, + BSON_TYPE_SYMBOL = 14, + BSON_TYPE_CODE_W_SCOPE = 15, + BSON_TYPE_INT = 16, + BSON_TYPE_TIMESTAMP = 17, + BSON_TYPE_LONG = 18, + BSON_TYPE_MAX_KEY = 0x7f, + BSON_TYPE_MIN_KEY = 0xff +}; + +//=========================================================================== + +template class BSONSerializer; + +class BSON : public ObjectWrap { +public: + BSON(); + ~BSON() {} + + static void Initialize(Handle target); + static Handle BSONDeserializeStream(const Arguments &args); + + // JS based objects + static Handle BSONSerialize(const Arguments &args); + static Handle BSONDeserialize(const Arguments &args); + + // Calculate size of function + static Handle CalculateObjectSize(const Arguments &args); + static Handle SerializeWithBufferAndIndex(const Arguments &args); + + // Constructor used for creating new BSON objects from C++ + static Persistent constructor_template; + +private: + static Handle New(const Arguments &args); + static Handle deserialize(BSON *bson, char *data, uint32_t dataLength, uint32_t startIndex, bool is_array_item); + + // BSON type instantiate functions + Persistent longConstructor; + Persistent objectIDConstructor; + Persistent binaryConstructor; + Persistent codeConstructor; + Persistent dbrefConstructor; + Persistent symbolConstructor; + Persistent doubleConstructor; + Persistent timestampConstructor; + Persistent minKeyConstructor; + Persistent maxKeyConstructor; + + // Equality Objects + Persistent longString; + Persistent objectIDString; + Persistent binaryString; + Persistent codeString; + Persistent dbrefString; + Persistent symbolString; + Persistent doubleString; + Persistent timestampString; + Persistent minKeyString; + Persistent maxKeyString; + + // Equality speed up comparison objects + Persistent _bsontypeString; + Persistent _longLowString; + Persistent _longHighString; + Persistent _objectIDidString; + Persistent _binaryPositionString; + Persistent _binarySubTypeString; + Persistent _binaryBufferString; + Persistent _doubleValueString; + Persistent _symbolValueString; + + Persistent _dbRefRefString; + Persistent _dbRefIdRefString; + Persistent _dbRefDbRefString; + Persistent _dbRefNamespaceString; + Persistent _dbRefDbString; + Persistent _dbRefOidString; + + Persistent _codeCodeString; + Persistent _codeScopeString; + Persistent _toBSONString; + + Local GetSerializeObject(const Handle& object); + + template friend class BSONSerializer; + friend class BSONDeserializer; +}; + +//=========================================================================== + +class CountStream +{ +public: + CountStream() : count(0) { } + + void WriteByte(int value) { ++count; } + void WriteByte(const Handle&, const Handle&) { ++count; } + void WriteBool(const Handle& value) { ++count; } + void WriteInt32(int32_t value) { count += 4; } + void WriteInt32(const Handle& value) { count += 4; } + void WriteInt32(const Handle& object, const Handle& key) { count += 4; } + void WriteInt64(int64_t value) { count += 8; } + void WriteInt64(const Handle& value) { count += 8; } + void WriteDouble(double value) { count += 8; } + void WriteDouble(const Handle& value) { count += 8; } + void WriteDouble(const Handle&, const Handle&) { count += 8; } + void WriteUInt32String(uint32_t name) { char buffer[32]; count += sprintf(buffer, "%u", name) + 1; } + void WriteLengthPrefixedString(const Local& value) { count += value->Utf8Length()+5; } + void WriteObjectId(const Handle& object, const Handle& key) { count += 12; } + void WriteString(const Local& value) { count += value->Utf8Length() + 1; } // This returns the number of bytes exclusive of the NULL terminator + void WriteData(const char* data, size_t length) { count += length; } + + void* BeginWriteType() { ++count; return NULL; } + void CommitType(void*, BsonType) { } + void* BeginWriteSize() { count += 4; return NULL; } + void CommitSize(void*) { } + + size_t GetSerializeSize() const { return count; } + + // Do nothing. CheckKey is implemented for DataStream + void CheckKey(const Local&) { } + +private: + size_t count; +}; + +class DataStream +{ +public: + DataStream(char* aDestinationBuffer) : destinationBuffer(aDestinationBuffer), p(aDestinationBuffer) { } + + void WriteByte(int value) { *p++ = value; } + void WriteByte(const Handle& object, const Handle& key) { *p++ = object->Get(key)->Int32Value(); } +#if USE_MISALIGNED_MEMORY_ACCESS + void WriteInt32(int32_t value) { *reinterpret_cast(p) = value; p += 4; } + void WriteInt64(int64_t value) { *reinterpret_cast(p) = value; p += 8; } + void WriteDouble(double value) { *reinterpret_cast(p) = value; p += 8; } +#else + void WriteInt32(int32_t value) { memcpy(p, &value, 4); p += 4; } + void WriteInt64(int64_t value) { memcpy(p, &value, 8); p += 8; } + void WriteDouble(double value) { memcpy(p, &value, 8); p += 8; } +#endif + void WriteBool(const Handle& value) { WriteByte(value->BooleanValue() ? 1 : 0); } + void WriteInt32(const Handle& value) { WriteInt32(value->Int32Value()); } + void WriteInt32(const Handle& object, const Handle& key) { WriteInt32(object->Get(key)); } + void WriteInt64(const Handle& value) { WriteInt64(value->IntegerValue()); } + void WriteDouble(const Handle& value) { WriteDouble(value->NumberValue()); } + void WriteDouble(const Handle& object, const Handle& key) { WriteDouble(object->Get(key)); } + void WriteUInt32String(uint32_t name) { p += sprintf(p, "%u", name) + 1; } + void WriteLengthPrefixedString(const Local& value) { WriteInt32(value->Utf8Length()+1); WriteString(value); } + void WriteObjectId(const Handle& object, const Handle& key); + void WriteString(const Local& value) { p += value->WriteUtf8(p); } // This returns the number of bytes inclusive of the NULL terminator. + void WriteData(const char* data, size_t length) { memcpy(p, data, length); p += length; } + + void* BeginWriteType() { void* returnValue = p; p++; return returnValue; } + void CommitType(void* beginPoint, BsonType value) { *reinterpret_cast(beginPoint) = value; } + void* BeginWriteSize() { void* returnValue = p; p += 4; return returnValue; } + +#if USE_MISALIGNED_MEMORY_ACCESS + void CommitSize(void* beginPoint) { *reinterpret_cast(beginPoint) = (int32_t) (p - (char*) beginPoint); } +#else + void CommitSize(void* beginPoint) { int32_t value = (int32_t) (p - (char*) beginPoint); memcpy(beginPoint, &value, 4); } +#endif + + size_t GetSerializeSize() const { return p - destinationBuffer; } + + void CheckKey(const Local& keyName); + +protected: + char *const destinationBuffer; // base, never changes + char* p; // cursor into buffer +}; + +template class BSONSerializer : public T +{ +private: + typedef T Inherited; + +public: + BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions) : Inherited(), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } + BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions, char* parentParam) : Inherited(parentParam), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } + + void SerializeDocument(const Handle& value); + void SerializeArray(const Handle& value); + void SerializeValue(void* typeLocation, const Handle& value); + +private: + bool checkKeys; + bool serializeFunctions; + BSON* bson; +}; + +//=========================================================================== + +class BSONDeserializer +{ +public: + BSONDeserializer(BSON* aBson, char* data, size_t length); + BSONDeserializer(BSONDeserializer& parentSerializer, size_t length); + + Handle DeserializeDocument(); + + bool HasMoreData() const { return p < pEnd; } + Local ReadCString(); + uint32_t ReadIntegerString(); + int32_t ReadRegexOptions(); + Local ReadString(); + Local ReadObjectId(); + + unsigned char ReadByte() { return *reinterpret_cast(p++); } +#if USE_MISALIGNED_MEMORY_ACCESS + int32_t ReadInt32() { int32_t returnValue = *reinterpret_cast(p); p += 4; return returnValue; } + uint32_t ReadUInt32() { uint32_t returnValue = *reinterpret_cast(p); p += 4; return returnValue; } + int64_t ReadInt64() { int64_t returnValue = *reinterpret_cast(p); p += 8; return returnValue; } + double ReadDouble() { double returnValue = *reinterpret_cast(p); p += 8; return returnValue; } +#else + int32_t ReadInt32() { int32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } + uint32_t ReadUInt32() { uint32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } + int64_t ReadInt64() { int64_t returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } + double ReadDouble() { double returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } +#endif + + size_t GetSerializeSize() const { return p - pStart; } + +private: + Handle DeserializeArray(); + Handle DeserializeValue(BsonType type); + Handle DeserializeDocumentInternal(); + Handle DeserializeArrayInternal(); + + BSON* bson; + char* const pStart; + char* p; + char* const pEnd; +}; + +//=========================================================================== + +#endif // BSON_H_ + +//=========================================================================== diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/index.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/index.js new file mode 100644 index 0000000..85e243c --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/index.js @@ -0,0 +1,30 @@ +var bson = null; + +// Load the precompiled win32 binary +if(process.platform == "win32" && process.arch == "x64") { + bson = require('./win32/x64/bson'); +} else if(process.platform == "win32" && process.arch == "ia32") { + bson = require('./win32/ia32/bson'); +} else { + bson = require('../build/Release/bson'); +} + +exports.BSON = bson.BSON; +exports.Long = require('../lib/bson/long').Long; +exports.ObjectID = require('../lib/bson/objectid').ObjectID; +exports.DBRef = require('../lib/bson/db_ref').DBRef; +exports.Code = require('../lib/bson/code').Code; +exports.Timestamp = require('../lib/bson/timestamp').Timestamp; +exports.Binary = require('../lib/bson/binary').Binary; +exports.Double = require('../lib/bson/double').Double; +exports.MaxKey = require('../lib/bson/max_key').MaxKey; +exports.MinKey = require('../lib/bson/min_key').MinKey; +exports.Symbol = require('../lib/bson/symbol').Symbol; + +// Just add constants tot he Native BSON parser +exports.BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; +exports.BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; +exports.BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +exports.BSON.BSON_BINARY_SUBTYPE_UUID = 3; +exports.BSON.BSON_BINARY_SUBTYPE_MD5 = 4; +exports.BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node new file mode 100644 index 0000000000000000000000000000000000000000..9ec6e970aa24a4ee9efce52ab0f176e6188292d6 GIT binary patch literal 118272 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±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Ò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 literal 0 HcmV?d00001 diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node new file mode 100644 index 0000000000000000000000000000000000000000..d074266a1fb3c8288d06daea7aea3b2a01e3d8fd GIT binary patch literal 134656 zcmeEvdwf$x`ghVaG+fFF$bljkqXex~rCKk=fFv}96G))0ps0XI6>;%`kSHiZYg%nM zW|wt!#Z_0KnKO4U{B{lthC#8NJD-tY6wNt(3v-TnRZ{`K;q zIcH{`dFGjCo_Xe(XD%mj!&;NYWHM#qPuESRO?c8jH~;?Me>#xd|Ew+jO|SLdefB1^ zfA`rl7u~+hv1IApw=KQ-PDkm@cinY&(6QiF$I{SUj@$2YcxPPWxbyB?ZoMcwyN}aA zeb%6X7vC6Kxgha(spY_eWe8ttd2hkR{QPvm)p-6a`@03#bGUWEY&>raJ-FZ`hu>Ln z13$kqo}cjZXSbIwBAh=}gOXyC>6V9knI6tsP?CUYGo9n;o6*N)nrt?iwu#vB_uy&g z_cFwa_Y9NCYQhixC7z}-gu%pf-_Jmb8=xXn;?X@UVp?~ZO((#NL9d!k;|byX4AW~= zkQ-PAP=xVsNrvey6p??c*)%*6_uo9{n@x?N>!Y}B$ z+#I~wWcuJ&00vIeV|Y%*Gv%KfR9_^(OeI%R0tn8*3ICGAYrb4n;*?CA`gXV;wS2_qvHEP5487knD^)bStKB~u-geCm{Mg5v8|6~0=xUr}DtwqLU{oWvu9_p8D2~lV8 zKZ0y%k3>tRwieg#c22+A=BI|FwoSBUXMMctexu2J{!*W4vKi4#pGR+^mRl|>QTa#( zs($#7QfiyL~V5$KOGlo@Zov~{wi{rYZg3`D1Z0f=6}18qThgg7@6bob`&ls84)q03uKkSz^LHTt2D zhF!ZN1JbGs{$!>_M6T`{PM2>bay9FL1U~SyYdE!ecA3dgvM=Hv#sL1bW z-^TiDdWUx6I%@mLKtd?hpi?0|<(X5b%NFEgNGR7m+CBnQ>r+7zoKMy-R?hVkY_2sK z6*s_8|0a?CU+dqe0H0Eyp7igPOn+DXJ0BU7`e_JL-d+FxhxVT2K)X9S+dVP>eE_UT zv{~2jClR0EO2K|K|K4ed=HJEPHigq!V)+o`WtwpV{Kj|?USUInS+^1> z=^}IRHbwQ>*pkrYEXZF)b!aGtxM%JGL^S(HJUp8&hAe6AQ>h`R*>t&?>I;_S>+1eHv#GLrHBoqC2~+qF2Ax8nGa1wm(8{JzW-#@|%e&QzrCb3U}eR`aZMN zCkUR(1kWad|KKxNT13VhXAg?(Y3*g??=AB8;rzEz{!$}9v3ry*mmqINb�z(cR3P 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 literal 0 HcmV?d00001 diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/wscript b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/wscript new file mode 100644 index 0000000..40f5317 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/ext/wscript @@ -0,0 +1,39 @@ +import Options +from os import unlink, symlink, popen +from os.path import exists + +srcdir = "." +blddir = "build" +VERSION = "0.1.0" + +def set_options(opt): + opt.tool_options("compiler_cxx") + opt.add_option( '--debug' + , action='store_true' + , default=False + , help='Build debug variant [Default: False]' + , dest='debug' + ) + +def configure(conf): + conf.check_tool("compiler_cxx") + conf.check_tool("node_addon") + conf.env.append_value('CXXFLAGS', ['-O3', '-funroll-loops']) + + # conf.env.append_value('CXXFLAGS', ['-DDEBUG', '-g', '-O0', '-Wall', '-Wextra']) + # conf.check(lib='node', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='NODE') + +def build(bld): + obj = bld.new_task_gen("cxx", "shlib", "node_addon") + obj.target = "bson" + obj.source = ["bson.cc"] + # obj.uselib = "NODE" + +def shutdown(): + # HACK to get compress.node out of build directory. + # better way to do this? + if Options.commands['clean']: + if exists('bson.node'): unlink('bson.node') + else: + if exists('build/default/bson.node') and not exists('bson.node'): + symlink('build/default/bson.node', 'bson.node') diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/install.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/install.js new file mode 100644 index 0000000..fc13b28 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/install.js @@ -0,0 +1,56 @@ +var spawn = require('child_process').spawn, + exec = require('child_process').exec; + +process.stdout.write("================================================================================\n"); +process.stdout.write("= =\n"); +process.stdout.write("= Attempting to build bson c++ extension =\n"); +process.stdout.write("= Windows: no build will be attempted as binaries are prepackaged =\n"); +process.stdout.write("= Unix: on failure the package will still install without the C++ extension =\n"); +process.stdout.write("= =\n"); +process.stdout.write("================================================================================\n"); + +// Check if we want to build the native code +var build_native = process.env['npm_package_config_mongodb_native'] != null ? process.env['npm_package_config_mongodb_native'] : 'false'; +if(process.env['npm_config_mongodb_debug']) { + console.log("== process.env['npm_package_config_mongodb_native'] :: " + process.env['npm_package_config_mongodb_native']); + console.log("== build_native :: " + build_native); +} + +build_native = process.env['npm_config_mongodb_native'] != null ? process.env['npm_config_mongodb_native'] : build_native; +if(process.env['npm_config_mongodb_debug']) { + console.log("== process.env['npm_config_mongodb_native'] :: " + process.env['npm_config_mongodb_native']); + console.log("== build_native :: " + build_native); +} + +build_native = build_native == 'true' ? true : false; +if(process.env['npm_config_mongodb_debug']) { + console.log("== build_native :: " + build_native); +} + +// If we are building the native bson extension ensure we use gmake if available +if(process.platform != "win32" && process.platform != "win64") { + // Check if we need to use gmake + exec('which gmake', function(err, stdout, stderr) { + // Set up spawn command + var make = null; + // No gmake build using make + if(err != null) { + make = spawn('make', ['node_gyp'], {cwd:process.env['PWD']}); + } else { + make = spawn('gmake', ['node_gyp'], {cwd:process.env['PWD']}); + } + + // Execute spawn + make.stdout.on('data', function(data) { + process.stdout.write(data); + }) + + make.stderr.on('data', function(data) { + process.stdout.write(data); + }) + + make.on('exit', function(code) { + process.stdout.write('child process exited with code ' + code + "\n"); + }) + }); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary.js new file mode 100644 index 0000000..cf1d69f --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary.js @@ -0,0 +1,332 @@ +/** + * Module dependencies. + */ +var Buffer = require('buffer').Buffer; // TODO just use global Buffer +var bson = require('./bson'); + +// Binary default subtype +var BSON_BINARY_SUBTYPE_DEFAULT = 0; + +/** + * @ignore + * @api private + */ +var writeStringToArray = function(data) { + // Create a buffer + var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(data.length)) : new Array(data.length); + // Write the content to the buffer + for(var i = 0; i < data.length; i++) { + buffer[i] = data.charCodeAt(i); + } + // Write the string to the buffer + return buffer; +} + +/** + * Convert Array ot Uint8Array to Binary String + * + * @ignore + * @api private + */ +var convertArraytoUtf8BinaryString = function(byteArray, startIndex, endIndex) { + var result = ""; + for(var i = startIndex; i < endIndex; i++) { + result = result + String.fromCharCode(byteArray[i]); + } + return result; +}; + +/** + * A class representation of the BSON Binary type. + * + * Sub types + * - **BSON.BSON_BINARY_SUBTYPE_DEFAULT**, default BSON type. + * - **BSON.BSON_BINARY_SUBTYPE_FUNCTION**, BSON function type. + * - **BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY**, BSON byte array type. + * - **BSON.BSON_BINARY_SUBTYPE_UUID**, BSON uuid type. + * - **BSON.BSON_BINARY_SUBTYPE_MD5**, BSON md5 type. + * - **BSON.BSON_BINARY_SUBTYPE_USER_DEFINED**, BSON user defined type. + * + * @class Represents the Binary BSON type. + * @param {Buffer} buffer a buffer object containing the binary data. + * @param {Number} [subType] the option binary type. + * @return {Grid} + */ +function Binary(buffer, subType) { + if(!(this instanceof Binary)) return new Binary(buffer, subType); + + this._bsontype = 'Binary'; + + if(buffer instanceof Number) { + this.sub_type = buffer; + this.position = 0; + } else { + this.sub_type = subType == null ? BSON_BINARY_SUBTYPE_DEFAULT : subType; + this.position = 0; + } + + if(buffer != null && !(buffer instanceof Number)) { + // Only accept Buffer, Uint8Array or Arrays + if(typeof buffer == 'string') { + // Different ways of writing the length of the string for the different types + if(typeof Buffer != 'undefined') { + this.buffer = new Buffer(buffer); + } else if(typeof Uint8Array != 'undefined' || (Object.prototype.toString.call(buffer) == '[object Array]')) { + this.buffer = writeStringToArray(buffer); + } else { + throw new Error("only String, Buffer, Uint8Array or Array accepted"); + } + } else { + this.buffer = buffer; + } + this.position = buffer.length; + } else { + if(typeof Buffer != 'undefined') { + this.buffer = new Buffer(Binary.BUFFER_SIZE); + } else if(typeof Uint8Array != 'undefined'){ + this.buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE)); + } else { + this.buffer = new Array(Binary.BUFFER_SIZE); + } + // Set position to start of buffer + this.position = 0; + } +}; + +/** + * Updates this binary with byte_value. + * + * @param {Character} byte_value a single byte we wish to write. + * @api public + */ +Binary.prototype.put = function put(byte_value) { + // If it's a string and a has more than one character throw an error + if(byte_value['length'] != null && typeof byte_value != 'number' && byte_value.length != 1) throw new Error("only accepts single character String, Uint8Array or Array"); + if(typeof byte_value != 'number' && byte_value < 0 || byte_value > 255) throw new Error("only accepts number in a valid unsigned byte range 0-255"); + + // Decode the byte value once + var decoded_byte = null; + if(typeof byte_value == 'string') { + decoded_byte = byte_value.charCodeAt(0); + } else if(byte_value['length'] != null) { + decoded_byte = byte_value[0]; + } else { + decoded_byte = byte_value; + } + + if(this.buffer.length > this.position) { + this.buffer[this.position++] = decoded_byte; + } else { + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + // Create additional overflow buffer + var buffer = new Buffer(Binary.BUFFER_SIZE + this.buffer.length); + // Combine the two buffers together + this.buffer.copy(buffer, 0, 0, this.buffer.length); + this.buffer = buffer; + this.buffer[this.position++] = decoded_byte; + } else { + var buffer = null; + // Create a new buffer (typed or normal array) + if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') { + buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE + this.buffer.length)); + } else { + buffer = new Array(Binary.BUFFER_SIZE + this.buffer.length); + } + + // We need to copy all the content to the new array + for(var i = 0; i < this.buffer.length; i++) { + buffer[i] = this.buffer[i]; + } + + // Reassign the buffer + this.buffer = buffer; + // Write the byte + this.buffer[this.position++] = decoded_byte; + } + } +}; + +/** + * Writes a buffer or string to the binary. + * + * @param {Buffer|String} string a string or buffer to be written to the Binary BSON object. + * @param {Number} offset specify the binary of where to write the content. + * @api public + */ +Binary.prototype.write = function write(string, offset) { + offset = typeof offset == 'number' ? offset : this.position; + + // If the buffer is to small let's extend the buffer + if(this.buffer.length < offset + string.length) { + var buffer = null; + // If we are in node.js + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + buffer = new Buffer(this.buffer.length + string.length); + this.buffer.copy(buffer, 0, 0, this.buffer.length); + } else if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') { + // Create a new buffer + buffer = new Uint8Array(new ArrayBuffer(this.buffer.length + string.length)) + // Copy the content + for(var i = 0; i < this.position; i++) { + buffer[i] = this.buffer[i]; + } + } + + // Assign the new buffer + this.buffer = buffer; + } + + if(typeof Buffer != 'undefined' && Buffer.isBuffer(string) && Buffer.isBuffer(this.buffer)) { + string.copy(this.buffer, offset, 0, string.length); + this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position; + // offset = string.length + } else if(typeof Buffer != 'undefined' && typeof string == 'string' && Buffer.isBuffer(this.buffer)) { + this.buffer.write(string, 'binary', offset); + this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position; + // offset = string.length; + } else if(Object.prototype.toString.call(string) == '[object Uint8Array]' + || Object.prototype.toString.call(string) == '[object Array]' && typeof string != 'string') { + for(var i = 0; i < string.length; i++) { + this.buffer[offset++] = string[i]; + } + + this.position = offset > this.position ? offset : this.position; + } else if(typeof string == 'string') { + for(var i = 0; i < string.length; i++) { + this.buffer[offset++] = string.charCodeAt(i); + } + + this.position = offset > this.position ? offset : this.position; + } +}; + +/** + * Reads **length** bytes starting at **position**. + * + * @param {Number} position read from the given position in the Binary. + * @param {Number} length the number of bytes to read. + * @return {Buffer} + * @api public + */ +Binary.prototype.read = function read(position, length) { + length = length && length > 0 + ? length + : this.position; + + // Let's return the data based on the type we have + if(this.buffer['slice']) { + return this.buffer.slice(position, position + length); + } else { + // Create a buffer to keep the result + var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(length)) : new Array(length); + for(var i = 0; i < length; i++) { + buffer[i] = this.buffer[position++]; + } + } + // Return the buffer + return buffer; +}; + +/** + * Returns the value of this binary as a string. + * + * @return {String} + * @api public + */ +Binary.prototype.value = function value(asRaw) { + asRaw = asRaw == null ? false : asRaw; + + // If it's a node.js buffer object + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + return asRaw ? this.buffer.slice(0, this.position) : this.buffer.toString('binary', 0, this.position); + } else { + if(asRaw) { + // we support the slice command use it + if(this.buffer['slice'] != null) { + return this.buffer.slice(0, this.position); + } else { + // Create a new buffer to copy content to + var newBuffer = Object.prototype.toString.call(this.buffer) == '[object Uint8Array]' ? new Uint8Array(new ArrayBuffer(this.position)) : new Array(this.position); + // Copy content + for(var i = 0; i < this.position; i++) { + newBuffer[i] = this.buffer[i]; + } + // Return the buffer + return newBuffer; + } + } else { + return convertArraytoUtf8BinaryString(this.buffer, 0, this.position); + } + } +}; + +/** + * Length. + * + * @return {Number} the length of the binary. + * @api public + */ +Binary.prototype.length = function length() { + return this.position; +}; + +/** + * @ignore + * @api private + */ +Binary.prototype.toJSON = function() { + return this.buffer != null ? this.buffer.toString('base64') : ''; +} + +/** + * @ignore + * @api private + */ +Binary.prototype.toString = function(format) { + return this.buffer != null ? this.buffer.slice(0, this.position).toString(format) : ''; +} + +Binary.BUFFER_SIZE = 256; + +/** + * Default BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_DEFAULT = 0; +/** + * Function BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_FUNCTION = 1; +/** + * Byte Array BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_BYTE_ARRAY = 2; +/** + * UUID BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_UUID = 3; +/** + * MD5 BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_MD5 = 4; +/** + * User BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_USER_DEFINED = 128; + +/** + * Expose. + */ +exports.Binary = Binary; + diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js new file mode 100644 index 0000000..d2fc811 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js @@ -0,0 +1,385 @@ +/** + * Binary Parser. + * Jonas Raoni Soares Silva + * http://jsfromhell.com/classes/binary-parser [v1.0] + */ +var chr = String.fromCharCode; + +var maxBits = []; +for (var i = 0; i < 64; i++) { + maxBits[i] = Math.pow(2, i); +} + +function BinaryParser (bigEndian, allowExceptions) { + if(!(this instanceof BinaryParser)) return new BinaryParser(bigEndian, allowExceptions); + + this.bigEndian = bigEndian; + this.allowExceptions = allowExceptions; +}; + +BinaryParser.warn = function warn (msg) { + if (this.allowExceptions) { + throw new Error(msg); + } + + return 1; +}; + +BinaryParser.decodeFloat = function decodeFloat (data, precisionBits, exponentBits) { + var b = new this.Buffer(this.bigEndian, data); + + b.checkBuffer(precisionBits + exponentBits + 1); + + var bias = maxBits[exponentBits - 1] - 1 + , signal = b.readBits(precisionBits + exponentBits, 1) + , exponent = b.readBits(precisionBits, exponentBits) + , significand = 0 + , divisor = 2 + , curByte = b.buffer.length + (-precisionBits >> 3) - 1; + + do { + for (var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 ); + } while (precisionBits -= startBit); + + return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 ); +}; + +BinaryParser.decodeInt = function decodeInt (data, bits, signed, forceBigEndian) { + var b = new this.Buffer(this.bigEndian || forceBigEndian, data) + , x = b.readBits(0, bits) + , max = maxBits[bits]; //max = Math.pow( 2, bits ); + + return signed && x >= max / 2 + ? x - max + : x; +}; + +BinaryParser.encodeFloat = function encodeFloat (data, precisionBits, exponentBits) { + var bias = maxBits[exponentBits - 1] - 1 + , minExp = -bias + 1 + , maxExp = bias + , minUnnormExp = minExp - precisionBits + , n = parseFloat(data) + , status = isNaN(n) || n == -Infinity || n == +Infinity ? n : 0 + , exp = 0 + , len = 2 * bias + 1 + precisionBits + 3 + , bin = new Array(len) + , signal = (n = status !== 0 ? 0 : n) < 0 + , intPart = Math.floor(n = Math.abs(n)) + , floatPart = n - intPart + , lastBit + , rounded + , result + , i + , j; + + for (i = len; i; bin[--i] = 0); + + for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2)); + + for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart); + + for (i = -1; ++i < len && !bin[i];); + + if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) { + if (!(rounded = bin[lastBit])) { + for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]); + } + + for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0)); + } + + for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];); + + if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) { + ++i; + } else if (exp < minExp) { + exp != bias + 1 - len && exp < minUnnormExp && this.warn("encodeFloat::float underflow"); + i = bias + 1 - (exp = minExp - 1); + } + + if (intPart || status !== 0) { + this.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status); + exp = maxExp + 1; + i = bias + 2; + + if (status == -Infinity) { + signal = 1; + } else if (isNaN(status)) { + bin[i] = 1; + } + } + + for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1); + + for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) { + n += (1 << j) * result.charAt(--i); + if (j == 7) { + r[r.length] = String.fromCharCode(n); + n = 0; + } + } + + r[r.length] = n + ? String.fromCharCode(n) + : ""; + + return (this.bigEndian ? r.reverse() : r).join(""); +}; + +BinaryParser.encodeInt = function encodeInt (data, bits, signed, forceBigEndian) { + var max = maxBits[bits]; + + if (data >= max || data < -(max / 2)) { + this.warn("encodeInt::overflow"); + data = 0; + } + + if (data < 0) { + data += max; + } + + for (var r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256)); + + for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0"); + + return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join(""); +}; + +BinaryParser.toSmall = function( data ){ return this.decodeInt( data, 8, true ); }; +BinaryParser.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); }; +BinaryParser.toByte = function( data ){ return this.decodeInt( data, 8, false ); }; +BinaryParser.fromByte = function( data ){ return this.encodeInt( data, 8, false ); }; +BinaryParser.toShort = function( data ){ return this.decodeInt( data, 16, true ); }; +BinaryParser.fromShort = function( data ){ return this.encodeInt( data, 16, true ); }; +BinaryParser.toWord = function( data ){ return this.decodeInt( data, 16, false ); }; +BinaryParser.fromWord = function( data ){ return this.encodeInt( data, 16, false ); }; +BinaryParser.toInt = function( data ){ return this.decodeInt( data, 32, true ); }; +BinaryParser.fromInt = function( data ){ return this.encodeInt( data, 32, true ); }; +BinaryParser.toLong = function( data ){ return this.decodeInt( data, 64, true ); }; +BinaryParser.fromLong = function( data ){ return this.encodeInt( data, 64, true ); }; +BinaryParser.toDWord = function( data ){ return this.decodeInt( data, 32, false ); }; +BinaryParser.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); }; +BinaryParser.toQWord = function( data ){ return this.decodeInt( data, 64, true ); }; +BinaryParser.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); }; +BinaryParser.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); }; +BinaryParser.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); }; +BinaryParser.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); }; +BinaryParser.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); }; + +// Factor out the encode so it can be shared by add_header and push_int32 +BinaryParser.encode_int32 = function encode_int32 (number, asArray) { + var a, b, c, d, unsigned; + unsigned = (number < 0) ? (number + 0x100000000) : number; + a = Math.floor(unsigned / 0xffffff); + unsigned &= 0xffffff; + b = Math.floor(unsigned / 0xffff); + unsigned &= 0xffff; + c = Math.floor(unsigned / 0xff); + unsigned &= 0xff; + d = Math.floor(unsigned); + return asArray ? [chr(a), chr(b), chr(c), chr(d)] : chr(a) + chr(b) + chr(c) + chr(d); +}; + +BinaryParser.encode_int64 = function encode_int64 (number) { + var a, b, c, d, e, f, g, h, unsigned; + unsigned = (number < 0) ? (number + 0x10000000000000000) : number; + a = Math.floor(unsigned / 0xffffffffffffff); + unsigned &= 0xffffffffffffff; + b = Math.floor(unsigned / 0xffffffffffff); + unsigned &= 0xffffffffffff; + c = Math.floor(unsigned / 0xffffffffff); + unsigned &= 0xffffffffff; + d = Math.floor(unsigned / 0xffffffff); + unsigned &= 0xffffffff; + e = Math.floor(unsigned / 0xffffff); + unsigned &= 0xffffff; + f = Math.floor(unsigned / 0xffff); + unsigned &= 0xffff; + g = Math.floor(unsigned / 0xff); + unsigned &= 0xff; + h = Math.floor(unsigned); + return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h); +}; + +/** + * UTF8 methods + */ + +// Take a raw binary string and return a utf8 string +BinaryParser.decode_utf8 = function decode_utf8 (binaryStr) { + var len = binaryStr.length + , decoded = '' + , i = 0 + , c = 0 + , c1 = 0 + , c2 = 0 + , c3; + + while (i < len) { + c = binaryStr.charCodeAt(i); + if (c < 128) { + decoded += String.fromCharCode(c); + i++; + } else if ((c > 191) && (c < 224)) { + c2 = binaryStr.charCodeAt(i+1); + decoded += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = binaryStr.charCodeAt(i+1); + c3 = binaryStr.charCodeAt(i+2); + decoded += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + + return decoded; +}; + +// Encode a cstring +BinaryParser.encode_cstring = function encode_cstring (s) { + return unescape(encodeURIComponent(s)) + BinaryParser.fromByte(0); +}; + +// Take a utf8 string and return a binary string +BinaryParser.encode_utf8 = function encode_utf8 (s) { + var a = "" + , c; + + for (var n = 0, len = s.length; n < len; n++) { + c = s.charCodeAt(n); + + if (c < 128) { + a += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + a += String.fromCharCode((c>>6) | 192) ; + a += String.fromCharCode((c&63) | 128); + } else { + a += String.fromCharCode((c>>12) | 224); + a += String.fromCharCode(((c>>6) & 63) | 128); + a += String.fromCharCode((c&63) | 128); + } + } + + return a; +}; + +BinaryParser.hprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + process.stdout.write(number + " ") + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + process.stdout.write(number + " ") + } + } + + process.stdout.write("\n\n"); +}; + +BinaryParser.ilprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(10) + : s.charCodeAt(i).toString(10); + + require('util').debug(number+' : '); + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(10) + : s.charCodeAt(i).toString(10); + require('util').debug(number+' : '+ s.charAt(i)); + } + } +}; + +BinaryParser.hlprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + require('util').debug(number+' : '); + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + require('util').debug(number+' : '+ s.charAt(i)); + } + } +}; + +/** + * BinaryParser buffer constructor. + */ +function BinaryParserBuffer (bigEndian, buffer) { + this.bigEndian = bigEndian || 0; + this.buffer = []; + this.setBuffer(buffer); +}; + +BinaryParserBuffer.prototype.setBuffer = function setBuffer (data) { + var l, i, b; + + if (data) { + i = l = data.length; + b = this.buffer = new Array(l); + for (; i; b[l - i] = data.charCodeAt(--i)); + this.bigEndian && b.reverse(); + } +}; + +BinaryParserBuffer.prototype.hasNeededBits = function hasNeededBits (neededBits) { + return this.buffer.length >= -(-neededBits >> 3); +}; + +BinaryParserBuffer.prototype.checkBuffer = function checkBuffer (neededBits) { + if (!this.hasNeededBits(neededBits)) { + throw new Error("checkBuffer::missing bytes"); + } +}; + +BinaryParserBuffer.prototype.readBits = function readBits (start, length) { + //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni) + + function shl (a, b) { + for (; b--; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1); + return a; + } + + if (start < 0 || length <= 0) { + return 0; + } + + this.checkBuffer(start + length); + + var offsetLeft + , offsetRight = start % 8 + , curByte = this.buffer.length - ( start >> 3 ) - 1 + , lastByte = this.buffer.length + ( -( start + length ) >> 3 ) + , diff = curByte - lastByte + , sum = ((this.buffer[ curByte ] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1)) + (diff && (offsetLeft = (start + length) % 8) ? (this.buffer[lastByte++] & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight : 0); + + for(; diff; sum += shl(this.buffer[lastByte++], (diff-- << 3) - offsetRight)); + + return sum; +}; + +/** + * Expose. + */ +BinaryParser.Buffer = BinaryParserBuffer; + +exports.BinaryParser = BinaryParser; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/bson.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/bson.js new file mode 100644 index 0000000..0627bf6 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/bson.js @@ -0,0 +1,1495 @@ +var Long = require('./long').Long + , Double = require('./double').Double + , Timestamp = require('./timestamp').Timestamp + , ObjectID = require('./objectid').ObjectID + , Symbol = require('./symbol').Symbol + , Code = require('./code').Code + , MinKey = require('./min_key').MinKey + , MaxKey = require('./max_key').MaxKey + , DBRef = require('./db_ref').DBRef + , Binary = require('./binary').Binary + , BinaryParser = require('./binary_parser').BinaryParser + , writeIEEE754 = require('./float_parser').writeIEEE754 + , readIEEE754 = require('./float_parser').readIEEE754 + +// To ensure that 0.4 of node works correctly +var isDate = function isDate(d) { + return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]'; +} + +/** + * Create a new BSON instance + * + * @class Represents the BSON Parser + * @return {BSON} instance of BSON Parser. + */ +function BSON () {}; + +/** + * @ignore + * @api private + */ +// BSON MAX VALUES +BSON.BSON_INT32_MAX = 0x7FFFFFFF; +BSON.BSON_INT32_MIN = -0x80000000; + +BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1; +BSON.BSON_INT64_MIN = -Math.pow(2, 63); + +// JS MAX PRECISE VALUES +BSON.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double. +BSON.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double. + +// Internal long versions +var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000); // Any integer up to 2^53 can be precisely represented by a double. +var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000); // Any integer down to -2^53 can be precisely represented by a double. + +/** + * Number BSON Type + * + * @classconstant BSON_DATA_NUMBER + **/ +BSON.BSON_DATA_NUMBER = 1; +/** + * String BSON Type + * + * @classconstant BSON_DATA_STRING + **/ +BSON.BSON_DATA_STRING = 2; +/** + * Object BSON Type + * + * @classconstant BSON_DATA_OBJECT + **/ +BSON.BSON_DATA_OBJECT = 3; +/** + * Array BSON Type + * + * @classconstant BSON_DATA_ARRAY + **/ +BSON.BSON_DATA_ARRAY = 4; +/** + * Binary BSON Type + * + * @classconstant BSON_DATA_BINARY + **/ +BSON.BSON_DATA_BINARY = 5; +/** + * ObjectID BSON Type + * + * @classconstant BSON_DATA_OID + **/ +BSON.BSON_DATA_OID = 7; +/** + * Boolean BSON Type + * + * @classconstant BSON_DATA_BOOLEAN + **/ +BSON.BSON_DATA_BOOLEAN = 8; +/** + * Date BSON Type + * + * @classconstant BSON_DATA_DATE + **/ +BSON.BSON_DATA_DATE = 9; +/** + * null BSON Type + * + * @classconstant BSON_DATA_NULL + **/ +BSON.BSON_DATA_NULL = 10; +/** + * RegExp BSON Type + * + * @classconstant BSON_DATA_REGEXP + **/ +BSON.BSON_DATA_REGEXP = 11; +/** + * Code BSON Type + * + * @classconstant BSON_DATA_CODE + **/ +BSON.BSON_DATA_CODE = 13; +/** + * Symbol BSON Type + * + * @classconstant BSON_DATA_SYMBOL + **/ +BSON.BSON_DATA_SYMBOL = 14; +/** + * Code with Scope BSON Type + * + * @classconstant BSON_DATA_CODE_W_SCOPE + **/ +BSON.BSON_DATA_CODE_W_SCOPE = 15; +/** + * 32 bit Integer BSON Type + * + * @classconstant BSON_DATA_INT + **/ +BSON.BSON_DATA_INT = 16; +/** + * Timestamp BSON Type + * + * @classconstant BSON_DATA_TIMESTAMP + **/ +BSON.BSON_DATA_TIMESTAMP = 17; +/** + * Long BSON Type + * + * @classconstant BSON_DATA_LONG + **/ +BSON.BSON_DATA_LONG = 18; +/** + * MinKey BSON Type + * + * @classconstant BSON_DATA_MIN_KEY + **/ +BSON.BSON_DATA_MIN_KEY = 0xff; +/** + * MaxKey BSON Type + * + * @classconstant BSON_DATA_MAX_KEY + **/ +BSON.BSON_DATA_MAX_KEY = 0x7f; + +/** + * Binary Default Type + * + * @classconstant BSON_BINARY_SUBTYPE_DEFAULT + **/ +BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; +/** + * Binary Function Type + * + * @classconstant BSON_BINARY_SUBTYPE_FUNCTION + **/ +BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; +/** + * Binary Byte Array Type + * + * @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY + **/ +BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +/** + * Binary UUID Type + * + * @classconstant BSON_BINARY_SUBTYPE_UUID + **/ +BSON.BSON_BINARY_SUBTYPE_UUID = 3; +/** + * Binary MD5 Type + * + * @classconstant BSON_BINARY_SUBTYPE_MD5 + **/ +BSON.BSON_BINARY_SUBTYPE_MD5 = 4; +/** + * Binary User Defined Type + * + * @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED + **/ +BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +/** + * Calculate the bson size for a passed in Javascript object. + * + * @param {Object} object the Javascript object to calculate the BSON byte size for. + * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**. + * @return {Number} returns the number of bytes the BSON object will take up. + * @api public + */ +BSON.calculateObjectSize = function calculateObjectSize(object, serializeFunctions) { + var totalLength = (4 + 1); + + if(Array.isArray(object)) { + for(var i = 0; i < object.length; i++) { + totalLength += calculateElement(i.toString(), object[i], serializeFunctions) + } + } else { + // If we have toBSON defined, override the current object + if(object.toBSON) { + object = object.toBSON(); + } + + // Calculate size + for(var key in object) { + totalLength += calculateElement(key, object[key], serializeFunctions) + } + } + + return totalLength; +} + +/** + * @ignore + * @api private + */ +function calculateElement(name, value, serializeFunctions) { + var isBuffer = typeof Buffer !== 'undefined'; + + switch(typeof value) { + case 'string': + return 1 + (!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1 + 4 + (!isBuffer ? numberOfBytes(value) : Buffer.byteLength(value, 'utf8')) + 1; + case 'number': + if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { // 32 bit + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (4 + 1); + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } + } else { // 64 bit + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } + case 'undefined': + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1); + case 'boolean': + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1 + 1); + case 'object': + if(value == null || value instanceof MinKey || value instanceof MaxKey || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1); + } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (12 + 1); + } else if(value instanceof Date || isDate(value)) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } else if(typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1 + 4 + 1) + value.length; + } else if(value instanceof Long || value instanceof Double || value instanceof Timestamp + || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Double' || value['_bsontype'] == 'Timestamp') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } else if(value instanceof Code || value['_bsontype'] == 'Code') { + // Calculate size depending on the availability of a scope + if(value.scope != null && Object.keys(value.scope).length > 0) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + 4 + (!isBuffer ? numberOfBytes(value.code.toString()) : Buffer.byteLength(value.code.toString(), 'utf8')) + 1 + BSON.calculateObjectSize(value.scope, serializeFunctions); + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + (!isBuffer ? numberOfBytes(value.code.toString()) : Buffer.byteLength(value.code.toString(), 'utf8')) + 1; + } + } else if(value instanceof Binary || value['_bsontype'] == 'Binary') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (value.position + 1 + 4 + 1); + } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + ((!isBuffer ? numberOfBytes(value.value) : Buffer.byteLength(value.value, 'utf8')) + 4 + 1 + 1); + } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') { + // Set up correct object for serialization + var ordered_values = { + '$ref': value.namespace + , '$id' : value.oid + }; + + // Add db reference if it exists + if(null != value.db) { + ordered_values['$db'] = value.db; + } + + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + BSON.calculateObjectSize(ordered_values, serializeFunctions); + } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + (!isBuffer ? numberOfBytes(value.source) : Buffer.byteLength(value.source, 'utf8')) + 1 + + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + BSON.calculateObjectSize(value, serializeFunctions) + 1; + } + case 'function': + // WTF for 0.4.X where typeof /someregexp/ === 'function' + if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]' || String.call(value) == '[object RegExp]') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + (!isBuffer ? numberOfBytes(value.source) : Buffer.byteLength(value.source, 'utf8')) + 1 + + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 + } else { + if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + 4 + (!isBuffer ? numberOfBytes(value.toString()) : Buffer.byteLength(value.toString(), 'utf8')) + 1 + BSON.calculateObjectSize(value.scope, serializeFunctions); + } else if(serializeFunctions) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + (!isBuffer ? numberOfBytes(value.toString()) : Buffer.byteLength(value.toString(), 'utf8')) + 1; + } + } + } + + return 0; +} + +/** + * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object. + * @param {Number} index the index in the buffer where we wish to start serializing into. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Number} returns the new write index in the Buffer. + * @api public + */ +BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, checkKeys, buffer, index, serializeFunctions) { + // Default setting false + serializeFunctions = serializeFunctions == null ? false : serializeFunctions; + // Write end information (length of the object) + var size = buffer.length; + // Write the size of the object + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + return serializeObject(object, checkKeys, buffer, index, serializeFunctions) - 1; +} + +/** + * @ignore + * @api private + */ +var serializeObject = function(object, checkKeys, buffer, index, serializeFunctions) { + // Process the object + if(Array.isArray(object)) { + for(var i = 0; i < object.length; i++) { + index = packElement(i.toString(), object[i], checkKeys, buffer, index, serializeFunctions); + } + } else { + // If we have toBSON defined, override the current object + if(object.toBSON) { + object = object.toBSON(); + } + + // Serialize the object + for(var key in object) { + // Check the key and throw error if it's illegal + if(checkKeys == true && (key != '$db' && key != '$ref' && key != '$id')) { + BSON.checkKey(key); + } + + // Pack the element + index = packElement(key, object[key], checkKeys, buffer, index, serializeFunctions); + } + } + + // Write zero + buffer[index++] = 0; + return index; +} + +var stringToBytes = function(str) { + var ch, st, re = []; + for (var i = 0; i < str.length; i++ ) { + ch = str.charCodeAt(i); // get char + st = []; // set up "stack" + do { + st.push( ch & 0xFF ); // push byte to stack + ch = ch >> 8; // shift value down by 1 byte + } + while ( ch ); + // add stack contents to result + // done because chars have "wrong" endianness + re = re.concat( st.reverse() ); + } + // return an array of bytes + return re; +} + +var numberOfBytes = function(str) { + var ch, st, re = 0; + for (var i = 0; i < str.length; i++ ) { + ch = str.charCodeAt(i); // get char + st = []; // set up "stack" + do { + st.push( ch & 0xFF ); // push byte to stack + ch = ch >> 8; // shift value down by 1 byte + } + while ( ch ); + // add stack contents to result + // done because chars have "wrong" endianness + re = re + st.length; + } + // return an array of bytes + return re; +} + +/** + * @ignore + * @api private + */ +var writeToTypedArray = function(buffer, string, index) { + var bytes = stringToBytes(string); + for(var i = 0; i < bytes.length; i++) { + buffer[index + i] = bytes[i]; + } + return bytes.length; +} + +/** + * @ignore + * @api private + */ +var supportsBuffer = typeof Buffer != 'undefined'; + +/** + * @ignore + * @api private + */ +var packElement = function(name, value, checkKeys, buffer, index, serializeFunctions) { + var startIndex = index; + + switch(typeof value) { + case 'string': + // Encode String type + buffer[index++] = BSON.BSON_DATA_STRING; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Calculate size + var size = supportsBuffer ? Buffer.byteLength(value) + 1 : numberOfBytes(value) + 1; + // Write the size of the string to buffer + buffer[index + 3] = (size >> 24) & 0xff; + buffer[index + 2] = (size >> 16) & 0xff; + buffer[index + 1] = (size >> 8) & 0xff; + buffer[index] = size & 0xff; + // Ajust the index + index = index + 4; + // Write the string + supportsBuffer ? buffer.write(value, index, 'utf8') : writeToTypedArray(buffer, value, index); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + // Return index + return index; + case 'number': + // We have an integer value + if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + // If the value fits in 32 bits encode as int, if it fits in a double + // encode it as a double, otherwise long + if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { + // Set int type 32 bits or less + buffer[index++] = BSON.BSON_DATA_INT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write the int value + buffer[index++] = value & 0xff; + buffer[index++] = (value >> 8) & 0xff; + buffer[index++] = (value >> 16) & 0xff; + buffer[index++] = (value >> 24) & 0xff; + } else if(value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + } else { + // Set long type + buffer[index++] = BSON.BSON_DATA_LONG; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + var longVal = Long.fromNumber(value); + var lowBits = longVal.getLowBits(); + var highBits = longVal.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + } + } else { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + } + + return index; + case 'undefined': + // Set long type + buffer[index++] = BSON.BSON_DATA_NULL; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + return index; + case 'boolean': + // Write the type + buffer[index++] = BSON.BSON_DATA_BOOLEAN; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Encode the boolean value + buffer[index++] = value ? 1 : 0; + return index; + case 'object': + if(value === null || value instanceof MinKey || value instanceof MaxKey + || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { + // Write the type of either min or max key + if(value === null) { + buffer[index++] = BSON.BSON_DATA_NULL; + } else if(value instanceof MinKey) { + buffer[index++] = BSON.BSON_DATA_MIN_KEY; + } else { + buffer[index++] = BSON.BSON_DATA_MAX_KEY; + } + + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + return index; + } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') { + // Write the type + buffer[index++] = BSON.BSON_DATA_OID; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write objectid + supportsBuffer ? buffer.write(value.id, index, 'binary') : writeToTypedArray(buffer, value.id, index); + // Ajust index + index = index + 12; + return index; + } else if(value instanceof Date || isDate(value)) { + // Write the type + buffer[index++] = BSON.BSON_DATA_DATE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the date + var dateInMilis = Long.fromNumber(value.getTime()); + var lowBits = dateInMilis.getLowBits(); + var highBits = dateInMilis.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + return index; + } else if(typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + // Write the type + buffer[index++] = BSON.BSON_DATA_BINARY; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Get size of the buffer (current write point) + var size = value.length; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the default subtype + buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT; + // Copy the content form the binary field to the buffer + value.copy(buffer, index, 0, size); + // Adjust the index + index = index + size; + return index; + } else if(value instanceof Long || value instanceof Timestamp || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Timestamp') { + // Write the type + buffer[index++] = value instanceof Long ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write the date + var lowBits = value.getLowBits(); + var highBits = value.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + return index; + } else if(value instanceof Double || value['_bsontype'] == 'Double') { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + return index; + } else if(value instanceof Code || value['_bsontype'] == 'Code') { + if(value.scope != null && Object.keys(value.scope).length > 0) { + // Write the type + buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate the scope size + var scopeSize = BSON.calculateObjectSize(value.scope, serializeFunctions); + // Function string + var functionString = value.code.toString(); + // Function Size + var codeSize = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + + // Calculate full size of the object + var totalSize = 4 + codeSize + scopeSize + 4; + + // Write the total size of the object + buffer[index++] = totalSize & 0xff; + buffer[index++] = (totalSize >> 8) & 0xff; + buffer[index++] = (totalSize >> 16) & 0xff; + buffer[index++] = (totalSize >> 24) & 0xff; + + // Write the size of the string to buffer + buffer[index++] = codeSize & 0xff; + buffer[index++] = (codeSize >> 8) & 0xff; + buffer[index++] = (codeSize >> 16) & 0xff; + buffer[index++] = (codeSize >> 24) & 0xff; + + // Write the string + supportsBuffer ? buffer.write(functionString, index, 'utf8') : writeToTypedArray(buffer, functionString, index); + // Update index + index = index + codeSize - 1; + // Write zero + buffer[index++] = 0; + // Serialize the scope object + var scopeObjectBuffer = supportsBuffer ? new Buffer(scopeSize) : new Uint8Array(new ArrayBuffer(scopeSize)); + // Execute the serialization into a seperate buffer + serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions); + + // Adjusted scope Size (removing the header) + var scopeDocSize = scopeSize; + // Write scope object size + buffer[index++] = scopeDocSize & 0xff; + buffer[index++] = (scopeDocSize >> 8) & 0xff; + buffer[index++] = (scopeDocSize >> 16) & 0xff; + buffer[index++] = (scopeDocSize >> 24) & 0xff; + + // Write the scopeObject into the buffer + supportsBuffer ? scopeObjectBuffer.copy(buffer, index, 0, scopeSize) : buffer.set(scopeObjectBuffer, index); + // Adjust index, removing the empty size of the doc (5 bytes 0000000005) + index = index + scopeDocSize - 5; + // Write trailing zero + buffer[index++] = 0; + return index + } else { + buffer[index++] = BSON.BSON_DATA_CODE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Function string + var functionString = value.code.toString(); + // Function Size + var size = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + return index; + } + } else if(value instanceof Binary || value['_bsontype'] == 'Binary') { + // Write the type + buffer[index++] = BSON.BSON_DATA_BINARY; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Extract the buffer + var data = value.value(true); + // Calculate size + var size = value.position; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the subtype to the buffer + buffer[index++] = value.sub_type; + // Write the data to the object + supportsBuffer ? data.copy(buffer, index, 0, value.position) : buffer.set(data, index); + // Ajust index + index = index + value.position; + return index; + } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') { + // Write the type + buffer[index++] = BSON.BSON_DATA_SYMBOL; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate size + var size = supportsBuffer ? Buffer.byteLength(value.value) + 1 : numberOfBytes(value.value) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(value.value, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0x00; + return index; + } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') { + // Write the type + buffer[index++] = BSON.BSON_DATA_OBJECT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Set up correct object for serialization + var ordered_values = { + '$ref': value.namespace + , '$id' : value.oid + }; + + // Add db reference if it exists + if(null != value.db) { + ordered_values['$db'] = value.db; + } + + // Message size + var size = BSON.calculateObjectSize(ordered_values, serializeFunctions); + // Serialize the object + var endIndex = BSON.serializeWithBufferAndIndex(ordered_values, checkKeys, buffer, index, serializeFunctions); + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write zero for object + buffer[endIndex++] = 0x00; + // Return the end index + return endIndex; + } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { + // Write the type + buffer[index++] = BSON.BSON_DATA_REGEXP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the regular expression string + supportsBuffer ? buffer.write(value.source, index, 'utf8') : writeToTypedArray(buffer, value.source, index); + // Adjust the index + index = index + (supportsBuffer ? Buffer.byteLength(value.source) : numberOfBytes(value.source)); + // Write zero + buffer[index++] = 0x00; + // Write the parameters + if(value.global) buffer[index++] = 0x73; // s + if(value.ignoreCase) buffer[index++] = 0x69; // i + if(value.multiline) buffer[index++] = 0x6d; // m + // Add ending zero + buffer[index++] = 0x00; + return index; + } else { + // Write the type + buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Adjust the index + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + var endIndex = serializeObject(value, checkKeys, buffer, index + 4, serializeFunctions); + // Write size + var size = endIndex - index; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + return endIndex; + } + case 'function': + // WTF for 0.4.X where typeof /someregexp/ === 'function' + if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]' || String.call(value) == '[object RegExp]') { + // Write the type + buffer[index++] = BSON.BSON_DATA_REGEXP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the regular expression string + buffer.write(value.source, index, 'utf8'); + // Adjust the index + index = index + (supportsBuffer ? Buffer.byteLength(value.source) : numberOfBytes(value.source)); + // Write zero + buffer[index++] = 0x00; + // Write the parameters + if(value.global) buffer[index++] = 0x73; // s + if(value.ignoreCase) buffer[index++] = 0x69; // i + if(value.multiline) buffer[index++] = 0x6d; // m + // Add ending zero + buffer[index++] = 0x00; + return index; + } else { + if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) { + // Write the type + buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate the scope size + var scopeSize = BSON.calculateObjectSize(value.scope, serializeFunctions); + // Function string + var functionString = value.toString(); + // Function Size + var codeSize = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + + // Calculate full size of the object + var totalSize = 4 + codeSize + scopeSize; + + // Write the total size of the object + buffer[index++] = totalSize & 0xff; + buffer[index++] = (totalSize >> 8) & 0xff; + buffer[index++] = (totalSize >> 16) & 0xff; + buffer[index++] = (totalSize >> 24) & 0xff; + + // Write the size of the string to buffer + buffer[index++] = codeSize & 0xff; + buffer[index++] = (codeSize >> 8) & 0xff; + buffer[index++] = (codeSize >> 16) & 0xff; + buffer[index++] = (codeSize >> 24) & 0xff; + + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + codeSize - 1; + // Write zero + buffer[index++] = 0; + // Serialize the scope object + var scopeObjectBuffer = new Buffer(scopeSize); + // Execute the serialization into a seperate buffer + serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions); + + // Adjusted scope Size (removing the header) + var scopeDocSize = scopeSize - 4; + // Write scope object size + buffer[index++] = scopeDocSize & 0xff; + buffer[index++] = (scopeDocSize >> 8) & 0xff; + buffer[index++] = (scopeDocSize >> 16) & 0xff; + buffer[index++] = (scopeDocSize >> 24) & 0xff; + + // Write the scopeObject into the buffer + scopeObjectBuffer.copy(buffer, index, 0, scopeSize); + + // Adjust index, removing the empty size of the doc (5 bytes 0000000005) + index = index + scopeDocSize - 5; + // Write trailing zero + buffer[index++] = 0; + return index + } else if(serializeFunctions) { + buffer[index++] = BSON.BSON_DATA_CODE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Function string + var functionString = value.toString(); + // Function Size + var size = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + return index; + } + } + } + + // If no value to serialize + return index; +} + +/** + * Serialize a Javascript object. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Buffer} returns the Buffer object containing the serialized object. + * @api public + */ +BSON.serialize = function(object, checkKeys, asBuffer, serializeFunctions) { + var buffer = null; + // Calculate the size of the object + var size = BSON.calculateObjectSize(object, serializeFunctions); + // Fetch the best available type for storing the binary data + if(buffer = typeof Buffer != 'undefined') { + buffer = new Buffer(size); + asBuffer = true; + } else if(typeof Uint8Array != 'undefined') { + buffer = new Uint8Array(new ArrayBuffer(size)); + } else { + buffer = new Array(size); + } + + // If asBuffer is false use typed arrays + BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, 0, serializeFunctions); + return buffer; +} + +/** + * Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5 + * + * @ignore + * @api private + */ +var functionCache = BSON.functionCache = {}; + +/** + * Crc state variables shared by function + * + * @ignore + * @api private + */ +var table = [0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]; + +/** + * CRC32 hash method, Fast and enough versitility for our usage + * + * @ignore + * @api private + */ +var crc32 = function(string, start, end) { + var crc = 0 + var x = 0; + var y = 0; + crc = crc ^ (-1); + + for(var i = start, iTop = end; i < iTop;i++) { + y = (crc ^ string[i]) & 0xFF; + x = table[y]; + crc = (crc >>> 8) ^ x; + } + + return crc ^ (-1); +} + +/** + * Deserialize stream data as BSON documents. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} data the buffer containing the serialized set of BSON documents. + * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start. + * @param {Number} numberOfDocuments number of documents to deserialize. + * @param {Array} documents an array where to store the deserialized documents. + * @param {Number} docStartIndex the index in the documents array from where to start inserting documents. + * @param {Object} [options] additional options used for the deserialization. + * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents. + * @api public + */ +BSON.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) { + // if(numberOfDocuments !== documents.length) throw new Error("Number of expected results back is less than the number of documents"); + options = options != null ? options : {}; + var index = startIndex; + // Loop over all documents + for(var i = 0; i < numberOfDocuments; i++) { + // Find size of the document + var size = data[index] | data[index + 1] << 8 | data[index + 2] << 16 | data[index + 3] << 24; + // Update options with index + options['index'] = index; + // Parse the document at this point + documents[docStartIndex + i] = BSON.deserialize(data, options); + // Adjust index by the document size + index = index + size; + } + + // Return object containing end index of parsing and list of documents + return index; +} + +/** + * Ensure eval is isolated. + * + * @ignore + * @api private + */ +var isolateEvalWithHash = function(functionCache, hash, functionString, object) { + // Contains the value we are going to set + var value = null; + + // Check for cache hit, eval if missing and return cached function + if(functionCache[hash] == null) { + eval("value = " + functionString); + functionCache[hash] = value; + } + // Set the object + return functionCache[hash].bind(object); +} + +/** + * Ensure eval is isolated. + * + * @ignore + * @api private + */ +var isolateEval = function(functionString) { + // Contains the value we are going to set + var value = null; + // Eval the function + eval("value = " + functionString); + return value; +} + +/** + * Convert Uint8Array to String + * + * @ignore + * @api private + */ +var convertUint8ArrayToUtf8String = function(byteArray, startIndex, endIndex) { + return BinaryParser.decode_utf8(convertArraytoUtf8BinaryString(byteArray, startIndex, endIndex)); +} + +var convertArraytoUtf8BinaryString = function(byteArray, startIndex, endIndex) { + var result = ""; + for(var i = startIndex; i < endIndex; i++) { + result = result + String.fromCharCode(byteArray[i]); + } + + return result; +}; + +/** + * Deserialize data as BSON. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} buffer the buffer containing the serialized set of BSON documents. + * @param {Object} [options] additional options used for the deserialization. + * @param {Boolean} [isArray] ignore used for recursive parsing. + * @return {Object} returns the deserialized Javascript Object. + * @api public + */ +BSON.deserialize = function(buffer, options, isArray) { + // Options + options = options == null ? {} : options; + var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions']; + var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions']; + var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32']; + + // Validate that we have at least 4 bytes of buffer + if(buffer.length < 5) throw new Error("corrupt bson message < 5 bytes long"); + + // Set up index + var index = typeof options['index'] == 'number' ? options['index'] : 0; + // Reads in a C style string + var readCStyleString = function() { + // Get the start search index + var i = index; + // Locate the end of the c string + while(buffer[i] !== 0x00) { i++ } + // Grab utf8 encoded string + var string = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, i) : convertUint8ArrayToUtf8String(buffer, index, i); + // Update index position + index = i + 1; + // Return string + return string; + } + + // Create holding object + var object = isArray ? [] : {}; + + // Read the document size + var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + + // Ensure buffer is valid size + if(size < 5 || size > buffer.length) throw new Error("corrupt bson message"); + + // While we have more left data left keep parsing + while(true) { + // Read the type + var elementType = buffer[index++]; + // If we get a zero it's the last byte, exit + if(elementType == 0) break; + // Read the name of the field + var name = readCStyleString(); + // Switch on the type + switch(elementType) { + case BSON.BSON_DATA_OID: + var string = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('binary', index, index + 12) : convertArraytoUtf8BinaryString(buffer, index, index + 12); + // Decode the oid + object[name] = new ObjectID(string); + // Update index + index = index + 12; + break; + case BSON.BSON_DATA_STRING: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Add string to object + object[name] = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_INT: + // Decode the 32bit value + object[name] = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + break; + case BSON.BSON_DATA_NUMBER: + // Decode the double value + object[name] = readIEEE754(buffer, index, 'little', 52, 8); + // Update the index + index = index + 8; + break; + case BSON.BSON_DATA_DATE: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Set date object + object[name] = new Date(new Long(lowBits, highBits).toNumber()); + break; + case BSON.BSON_DATA_BOOLEAN: + // Parse the boolean value + object[name] = buffer[index++] == 1; + break; + case BSON.BSON_DATA_NULL: + // Parse the boolean value + object[name] = null; + break; + case BSON.BSON_DATA_BINARY: + // Decode the size of the binary blob + var binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Decode the subtype + var subType = buffer[index++]; + // Decode as raw Buffer object if options specifies it + if(buffer['slice'] != null) { + object[name] = new Binary(buffer.slice(index, index + binarySize), subType); + } else { + var _buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(binarySize)) : new Array(binarySize); + for(var i = 0; i < binarySize; i++) { + _buffer[i] = buffer[index + i]; + } + // Create the binary object + object[name] = new Binary(_buffer, subType); + } + // Update the index + index = index + binarySize; + break; + case BSON.BSON_DATA_ARRAY: + options['index'] = index; + // Decode the size of the array document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Set the array to the object + object[name] = BSON.deserialize(buffer, options, true); + // Adjust the index + index = index + objectSize; + break; + case BSON.BSON_DATA_OBJECT: + options['index'] = index; + // Decode the size of the object document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Set the array to the object + object[name] = BSON.deserialize(buffer, options, false); + // Adjust the index + index = index + objectSize; + break; + case BSON.BSON_DATA_REGEXP: + // Create the regexp + var source = readCStyleString(); + var regExpOptions = readCStyleString(); + // For each option add the corresponding one for javascript + var optionsArray = new Array(regExpOptions.length); + + // Parse options + for(var i = 0; i < regExpOptions.length; i++) { + switch(regExpOptions[i]) { + case 'm': + optionsArray[i] = 'm'; + break; + case 's': + optionsArray[i] = 'g'; + break; + case 'i': + optionsArray[i] = 'i'; + break; + } + } + + object[name] = new RegExp(source, optionsArray.join('')); + break; + case BSON.BSON_DATA_LONG: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Create long object + var long = new Long(lowBits, highBits); + // Set the object + object[name] = long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG) ? long.toNumber() : long; + break; + case BSON.BSON_DATA_SYMBOL: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Add string to object + object[name] = new Symbol(buffer.toString('utf8', index, index + stringSize - 1)); + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_TIMESTAMP: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Set the object + object[name] = new Timestamp(lowBits, highBits); + break; + case BSON.BSON_DATA_MIN_KEY: + // Parse the object + object[name] = new MinKey(); + break; + case BSON.BSON_DATA_MAX_KEY: + // Parse the object + object[name] = new MaxKey(); + break; + case BSON.BSON_DATA_CODE: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Function string + var functionString = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + + // If we are evaluating the functions + if(evalFunctions) { + // Contains the value we are going to set + var value = null; + // If we have cache enabled let's look for the md5 of the function in the cache + if(cacheFunctions) { + var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; + // Got to do this to avoid V8 deoptimizing the call due to finding eval + object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); + } else { + // Set directly + object[name] = isolateEval(functionString); + } + } else { + object[name] = new Code(functionString, {}); + } + + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_CODE_W_SCOPE: + // Read the content of the field + var totalSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Javascript function + var functionString = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + // Update parse index position + index = index + stringSize; + // Parse the element + options['index'] = index; + // Decode the size of the object document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Decode the scope object + var scopeObject = BSON.deserialize(buffer, options, false); + // Adjust the index + index = index + objectSize; + + // If we are evaluating the functions + if(evalFunctions) { + // Contains the value we are going to set + var value = null; + // If we have cache enabled let's look for the md5 of the function in the cache + if(cacheFunctions) { + var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; + // Got to do this to avoid V8 deoptimizing the call due to finding eval + object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); + } else { + // Set directly + object[name] = isolateEval(functionString); + } + + // Set the scope on the object + object[name].scope = scopeObject; + } else { + object[name] = new Code(functionString, scopeObject); + } + + // Add string to object + break; + } + } + + // Check if we have a db ref object + if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']); + + // Return the final objects + return object; +} + +/** + * Check if key name is valid. + * + * @ignore + * @api private + */ +BSON.checkKey = function checkKey (key) { + if (!key.length) return; + // Check if we have a legal key for the object + if('$' == key[0]) { + throw Error("key " + key + " must not start with '$'"); + } else if (!!~key.indexOf('.')) { + throw Error("key " + key + " must not contain '.'"); + } +}; + +/** + * Deserialize data as BSON. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} buffer the buffer containing the serialized set of BSON documents. + * @param {Object} [options] additional options used for the deserialization. + * @param {Boolean} [isArray] ignore used for recursive parsing. + * @return {Object} returns the deserialized Javascript Object. + * @api public + */ +BSON.prototype.deserialize = function(data, options) { + return BSON.deserialize(data, options); +} + +/** + * Deserialize stream data as BSON documents. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} data the buffer containing the serialized set of BSON documents. + * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start. + * @param {Number} numberOfDocuments number of documents to deserialize. + * @param {Array} documents an array where to store the deserialized documents. + * @param {Number} docStartIndex the index in the documents array from where to start inserting documents. + * @param {Object} [options] additional options used for the deserialization. + * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents. + * @api public + */ +BSON.prototype.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) { + return BSON.deserializeStream(data, startIndex, numberOfDocuments, documents, docStartIndex, options); +} + +/** + * Serialize a Javascript object. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Buffer} returns the Buffer object containing the serialized object. + * @api public + */ +BSON.prototype.serialize = function(object, checkKeys, asBuffer, serializeFunctions) { + return BSON.serialize(object, checkKeys, asBuffer, serializeFunctions); +} + +/** + * Calculate the bson size for a passed in Javascript object. + * + * @param {Object} object the Javascript object to calculate the BSON byte size for. + * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**. + * @return {Number} returns the number of bytes the BSON object will take up. + * @api public + */ +BSON.prototype.calculateObjectSize = function(object, serializeFunctions) { + return BSON.calculateObjectSize(object, serializeFunctions); +} + +/** + * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object. + * @param {Number} index the index in the buffer where we wish to start serializing into. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Number} returns the new write index in the Buffer. + * @api public + */ +BSON.prototype.serializeWithBufferAndIndex = function(object, checkKeys, buffer, startIndex, serializeFunctions) { + return BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, startIndex, serializeFunctions); +} + +/** + * @ignore + * @api private + */ +exports.Code = Code; +exports.Symbol = Symbol; +exports.BSON = BSON; +exports.DBRef = DBRef; +exports.Binary = Binary; +exports.ObjectID = ObjectID; +exports.Long = Long; +exports.Timestamp = Timestamp; +exports.Double = Double; +exports.MinKey = MinKey; +exports.MaxKey = MaxKey; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/code.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/code.js new file mode 100644 index 0000000..69b56a3 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/code.js @@ -0,0 +1,25 @@ +/** + * A class representation of the BSON Code type. + * + * @class Represents the BSON Code type. + * @param {String|Function} code a string or function. + * @param {Object} [scope] an optional scope for the function. + * @return {Code} + */ +function Code(code, scope) { + if(!(this instanceof Code)) return new Code(code, scope); + + this._bsontype = 'Code'; + this.code = code; + this.scope = scope == null ? {} : scope; +}; + +/** + * @ignore + * @api private + */ +Code.prototype.toJSON = function() { + return {scope:this.scope, code:this.code}; +} + +exports.Code = Code; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js new file mode 100644 index 0000000..56b6510 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js @@ -0,0 +1,31 @@ +/** + * A class representation of the BSON DBRef type. + * + * @class Represents the BSON DBRef type. + * @param {String} namespace the collection name. + * @param {ObjectID} oid the reference ObjectID. + * @param {String} [db] optional db name, if omitted the reference is local to the current db. + * @return {DBRef} + */ +function DBRef(namespace, oid, db) { + if(!(this instanceof DBRef)) return new DBRef(namespace, oid, db); + + this._bsontype = 'DBRef'; + this.namespace = namespace; + this.oid = oid; + this.db = db; +}; + +/** + * @ignore + * @api private + */ +DBRef.prototype.toJSON = function() { + return { + '$ref':this.namespace, + '$id':this.oid, + '$db':this.db == null ? '' : this.db + }; +} + +exports.DBRef = DBRef; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/double.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/double.js new file mode 100644 index 0000000..ae51463 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/double.js @@ -0,0 +1,33 @@ +/** + * A class representation of the BSON Double type. + * + * @class Represents the BSON Double type. + * @param {Number} value the number we want to represent as a double. + * @return {Double} + */ +function Double(value) { + if(!(this instanceof Double)) return new Double(value); + + this._bsontype = 'Double'; + this.value = value; +} + +/** + * Access the number value. + * + * @return {Number} returns the wrapped double number. + * @api public + */ +Double.prototype.valueOf = function() { + return this.value; +}; + +/** + * @ignore + * @api private + */ +Double.prototype.toJSON = function() { + return this.value; +} + +exports.Double = Double; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js new file mode 100644 index 0000000..6fca392 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js @@ -0,0 +1,121 @@ +// Copyright (c) 2008, Fair Oaks Labs, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// +// Modifications to writeIEEE754 to support negative zeroes made by Brian White + +var readIEEE754 = function(buffer, offset, endian, mLen, nBytes) { + var e, m, + bBE = (endian === 'big'), + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = bBE ? 0 : (nBytes - 1), + d = bBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +var writeIEEE754 = function(buffer, value, offset, endian, mLen, nBytes) { + var e, m, c, + bBE = (endian === 'big'), + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = bBE ? (nBytes-1) : 0, + d = bBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e+eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; + +exports.readIEEE754 = readIEEE754; +exports.writeIEEE754 = writeIEEE754; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/index.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/index.js new file mode 100644 index 0000000..950fcad --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/index.js @@ -0,0 +1,74 @@ +try { + exports.BSONPure = require('./bson'); + exports.BSONNative = require('../../ext'); +} catch(err) { + // do nothing +} + +[ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + exports[i] = module[i]; + } +}); + +// Exports all the classes for the NATIVE JS BSON Parser +exports.native = function() { + var classes = {}; + // Map all the classes + [ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long' + , '../../ext' +].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + classes[i] = module[i]; + } + }); + // Return classes list + return classes; +} + +// Exports all the classes for the PURE JS BSON Parser +exports.pure = function() { + var classes = {}; + // Map all the classes + [ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long' + , '././bson'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + classes[i] = module[i]; + } + }); + // Return classes list + return classes; +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/long.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/long.js new file mode 100644 index 0000000..f8f37a6 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/long.js @@ -0,0 +1,854 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2009 Google Inc. All Rights Reserved + +/** + * Defines a Long class for representing a 64-bit two's-complement + * integer value, which faithfully simulates the behavior of a Java "Long". This + * implementation is derived from LongLib in GWT. + * + * Constructs a 64-bit two's-complement integer, given its low and high 32-bit + * values as *signed* integers. See the from* functions below for more + * convenient ways of constructing Longs. + * + * The internal representation of a Long is the two given signed, 32-bit values. + * We use 32-bit pieces because these are the size of integers on which + * Javascript performs bit-operations. For operations like addition and + * multiplication, we split each number into 16-bit pieces, which can easily be + * multiplied within Javascript's floating-point representation without overflow + * or change in sign. + * + * In the algorithms below, we frequently reduce the negative case to the + * positive case by negating the input(s) and then post-processing the result. + * Note that we must ALWAYS check specially whether those values are MIN_VALUE + * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + * a positive number, it overflows back into a negative). Not handling this + * case would often result in infinite recursion. + * + * @class Represents the BSON Long type. + * @param {Number} low the low (signed) 32 bits of the Long. + * @param {Number} high the high (signed) 32 bits of the Long. + */ +function Long(low, high) { + if(!(this instanceof Long)) return new Long(low, high); + + this._bsontype = 'Long'; + /** + * @type {number} + * @api private + */ + this.low_ = low | 0; // force into 32 signed bits. + + /** + * @type {number} + * @api private + */ + this.high_ = high | 0; // force into 32 signed bits. +}; + +/** + * Return the int value. + * + * @return {Number} the value, assuming it is a 32-bit integer. + * @api public + */ +Long.prototype.toInt = function() { + return this.low_; +}; + +/** + * Return the Number value. + * + * @return {Number} the closest floating-point representation to this value. + * @api public + */ +Long.prototype.toNumber = function() { + return this.high_ * Long.TWO_PWR_32_DBL_ + + this.getLowBitsUnsigned(); +}; + +/** + * Return the JSON value. + * + * @return {String} the JSON representation. + * @api public + */ +Long.prototype.toJSON = function() { + return this.toString(); +} + +/** + * Return the String value. + * + * @param {Number} [opt_radix] the radix in which the text should be written. + * @return {String} the textual representation of this value. + * @api public + */ +Long.prototype.toString = function(opt_radix) { + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (this.isZero()) { + return '0'; + } + + if (this.isNegative()) { + if (this.equals(Long.MIN_VALUE)) { + // We need to change the Long value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixLong = Long.fromNumber(radix); + var div = this.div(radixLong); + var rem = div.multiply(radixLong).subtract(this); + return div.toString(radix) + rem.toInt().toString(radix); + } else { + return '-' + this.negate().toString(radix); + } + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 6)); + + var rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower); + var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt(); + var digits = intval.toString(radix); + + rem = remDiv; + if (rem.isZero()) { + return digits + result; + } else { + while (digits.length < 6) { + digits = '0' + digits; + } + result = '' + digits + result; + } + } +}; + +/** + * Return the high 32-bits value. + * + * @return {Number} the high 32-bits as a signed value. + * @api public + */ +Long.prototype.getHighBits = function() { + return this.high_; +}; + +/** + * Return the low 32-bits value. + * + * @return {Number} the low 32-bits as a signed value. + * @api public + */ +Long.prototype.getLowBits = function() { + return this.low_; +}; + +/** + * Return the low unsigned 32-bits value. + * + * @return {Number} the low 32-bits as an unsigned value. + * @api public + */ +Long.prototype.getLowBitsUnsigned = function() { + return (this.low_ >= 0) ? + this.low_ : Long.TWO_PWR_32_DBL_ + this.low_; +}; + +/** + * Returns the number of bits needed to represent the absolute value of this Long. + * + * @return {Number} Returns the number of bits needed to represent the absolute value of this Long. + * @api public + */ +Long.prototype.getNumBitsAbs = function() { + if (this.isNegative()) { + if (this.equals(Long.MIN_VALUE)) { + return 64; + } else { + return this.negate().getNumBitsAbs(); + } + } else { + var val = this.high_ != 0 ? this.high_ : this.low_; + for (var bit = 31; bit > 0; bit--) { + if ((val & (1 << bit)) != 0) { + break; + } + } + return this.high_ != 0 ? bit + 33 : bit + 1; + } +}; + +/** + * Return whether this value is zero. + * + * @return {Boolean} whether this value is zero. + * @api public + */ +Long.prototype.isZero = function() { + return this.high_ == 0 && this.low_ == 0; +}; + +/** + * Return whether this value is negative. + * + * @return {Boolean} whether this value is negative. + * @api public + */ +Long.prototype.isNegative = function() { + return this.high_ < 0; +}; + +/** + * Return whether this value is odd. + * + * @return {Boolean} whether this value is odd. + * @api public + */ +Long.prototype.isOdd = function() { + return (this.low_ & 1) == 1; +}; + +/** + * Return whether this Long equals the other + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long equals the other + * @api public + */ +Long.prototype.equals = function(other) { + return (this.high_ == other.high_) && (this.low_ == other.low_); +}; + +/** + * Return whether this Long does not equal the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long does not equal the other. + * @api public + */ +Long.prototype.notEquals = function(other) { + return (this.high_ != other.high_) || (this.low_ != other.low_); +}; + +/** + * Return whether this Long is less than the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is less than the other. + * @api public + */ +Long.prototype.lessThan = function(other) { + return this.compare(other) < 0; +}; + +/** + * Return whether this Long is less than or equal to the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is less than or equal to the other. + * @api public + */ +Long.prototype.lessThanOrEqual = function(other) { + return this.compare(other) <= 0; +}; + +/** + * Return whether this Long is greater than the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is greater than the other. + * @api public + */ +Long.prototype.greaterThan = function(other) { + return this.compare(other) > 0; +}; + +/** + * Return whether this Long is greater than or equal to the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is greater than or equal to the other. + * @api public + */ +Long.prototype.greaterThanOrEqual = function(other) { + return this.compare(other) >= 0; +}; + +/** + * Compares this Long with the given one. + * + * @param {Long} other Long to compare against. + * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater. + * @api public + */ +Long.prototype.compare = function(other) { + if (this.equals(other)) { + return 0; + } + + var thisNeg = this.isNegative(); + var otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) { + return -1; + } + if (!thisNeg && otherNeg) { + return 1; + } + + // at this point, the signs are the same, so subtraction will not overflow + if (this.subtract(other).isNegative()) { + return -1; + } else { + return 1; + } +}; + +/** + * The negation of this value. + * + * @return {Long} the negation of this value. + * @api public + */ +Long.prototype.negate = function() { + if (this.equals(Long.MIN_VALUE)) { + return Long.MIN_VALUE; + } else { + return this.not().add(Long.ONE); + } +}; + +/** + * Returns the sum of this and the given Long. + * + * @param {Long} other Long to add to this one. + * @return {Long} the sum of this and the given Long. + * @api public + */ +Long.prototype.add = function(other) { + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns the difference of this and the given Long. + * + * @param {Long} other Long to subtract from this. + * @return {Long} the difference of this and the given Long. + * @api public + */ +Long.prototype.subtract = function(other) { + return this.add(other.negate()); +}; + +/** + * Returns the product of this and the given Long. + * + * @param {Long} other Long to multiply with this. + * @return {Long} the product of this and the other. + * @api public + */ +Long.prototype.multiply = function(other) { + if (this.isZero()) { + return Long.ZERO; + } else if (other.isZero()) { + return Long.ZERO; + } + + if (this.equals(Long.MIN_VALUE)) { + return other.isOdd() ? Long.MIN_VALUE : Long.ZERO; + } else if (other.equals(Long.MIN_VALUE)) { + return this.isOdd() ? Long.MIN_VALUE : Long.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().multiply(other.negate()); + } else { + return this.negate().multiply(other).negate(); + } + } else if (other.isNegative()) { + return this.multiply(other.negate()).negate(); + } + + // If both Longs are small, use float multiplication + if (this.lessThan(Long.TWO_PWR_24_) && + other.lessThan(Long.TWO_PWR_24_)) { + return Long.fromNumber(this.toNumber() * other.toNumber()); + } + + // Divide each Long into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns this Long divided by the given one. + * + * @param {Long} other Long by which to divide. + * @return {Long} this Long divided by the given one. + * @api public + */ +Long.prototype.div = function(other) { + if (other.isZero()) { + throw Error('division by zero'); + } else if (this.isZero()) { + return Long.ZERO; + } + + if (this.equals(Long.MIN_VALUE)) { + if (other.equals(Long.ONE) || + other.equals(Long.NEG_ONE)) { + return Long.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + } else if (other.equals(Long.MIN_VALUE)) { + return Long.ONE; + } else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shiftRight(1); + var approx = halfThis.div(other).shiftLeft(1); + if (approx.equals(Long.ZERO)) { + return other.isNegative() ? Long.ONE : Long.NEG_ONE; + } else { + var rem = this.subtract(other.multiply(approx)); + var result = approx.add(rem.div(other)); + return result; + } + } + } else if (other.equals(Long.MIN_VALUE)) { + return Long.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().div(other.negate()); + } else { + return this.negate().div(other).negate(); + } + } else if (other.isNegative()) { + return this.div(other.negate()).negate(); + } + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + var res = Long.ZERO; + var rem = this; + while (rem.greaterThanOrEqual(other)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2); + var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48); + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + var approxRes = Long.fromNumber(approx); + var approxRem = approxRes.multiply(other); + while (approxRem.isNegative() || approxRem.greaterThan(rem)) { + approx -= delta; + approxRes = Long.fromNumber(approx); + approxRem = approxRes.multiply(other); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) { + approxRes = Long.ONE; + } + + res = res.add(approxRes); + rem = rem.subtract(approxRem); + } + return res; +}; + +/** + * Returns this Long modulo the given one. + * + * @param {Long} other Long by which to mod. + * @return {Long} this Long modulo the given one. + * @api public + */ +Long.prototype.modulo = function(other) { + return this.subtract(this.div(other).multiply(other)); +}; + +/** + * The bitwise-NOT of this value. + * + * @return {Long} the bitwise-NOT of this value. + * @api public + */ +Long.prototype.not = function() { + return Long.fromBits(~this.low_, ~this.high_); +}; + +/** + * Returns the bitwise-AND of this Long and the given one. + * + * @param {Long} other the Long with which to AND. + * @return {Long} the bitwise-AND of this and the other. + * @api public + */ +Long.prototype.and = function(other) { + return Long.fromBits(this.low_ & other.low_, this.high_ & other.high_); +}; + +/** + * Returns the bitwise-OR of this Long and the given one. + * + * @param {Long} other the Long with which to OR. + * @return {Long} the bitwise-OR of this and the other. + * @api public + */ +Long.prototype.or = function(other) { + return Long.fromBits(this.low_ | other.low_, this.high_ | other.high_); +}; + +/** + * Returns the bitwise-XOR of this Long and the given one. + * + * @param {Long} other the Long with which to XOR. + * @return {Long} the bitwise-XOR of this and the other. + * @api public + */ +Long.prototype.xor = function(other) { + return Long.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_); +}; + +/** + * Returns this Long with bits shifted to the left by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the left by the given amount. + * @api public + */ +Long.prototype.shiftLeft = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var low = this.low_; + if (numBits < 32) { + var high = this.high_; + return Long.fromBits( + low << numBits, + (high << numBits) | (low >>> (32 - numBits))); + } else { + return Long.fromBits(0, low << (numBits - 32)); + } + } +}; + +/** + * Returns this Long with bits shifted to the right by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the right by the given amount. + * @api public + */ +Long.prototype.shiftRight = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Long.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >> numBits); + } else { + return Long.fromBits( + high >> (numBits - 32), + high >= 0 ? 0 : -1); + } + } +}; + +/** + * Returns this Long with bits shifted to the right by the given amount, with the new top bits matching the current sign bit. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the right by the given amount, with zeros placed into the new leading bits. + * @api public + */ +Long.prototype.shiftRightUnsigned = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Long.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >>> numBits); + } else if (numBits == 32) { + return Long.fromBits(high, 0); + } else { + return Long.fromBits(high >>> (numBits - 32), 0); + } + } +}; + +/** + * Returns a Long representing the given (32-bit) integer value. + * + * @param {Number} value the 32-bit integer in question. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromInt = function(value) { + if (-128 <= value && value < 128) { + var cachedObj = Long.INT_CACHE_[value]; + if (cachedObj) { + return cachedObj; + } + } + + var obj = new Long(value | 0, value < 0 ? -1 : 0); + if (-128 <= value && value < 128) { + Long.INT_CACHE_[value] = obj; + } + return obj; +}; + +/** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * + * @param {Number} value the number in question. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromNumber = function(value) { + if (isNaN(value) || !isFinite(value)) { + return Long.ZERO; + } else if (value <= -Long.TWO_PWR_63_DBL_) { + return Long.MIN_VALUE; + } else if (value + 1 >= Long.TWO_PWR_63_DBL_) { + return Long.MAX_VALUE; + } else if (value < 0) { + return Long.fromNumber(-value).negate(); + } else { + return new Long( + (value % Long.TWO_PWR_32_DBL_) | 0, + (value / Long.TWO_PWR_32_DBL_) | 0); + } +}; + +/** + * Returns a Long representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits. + * + * @param {Number} lowBits the low 32-bits. + * @param {Number} highBits the high 32-bits. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromBits = function(lowBits, highBits) { + return new Long(lowBits, highBits); +}; + +/** + * Returns a Long representation of the given string, written using the given radix. + * + * @param {String} str the textual representation of the Long. + * @param {Number} opt_radix the radix in which the text is written. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromString = function(str, opt_radix) { + if (str.length == 0) { + throw Error('number format error: empty string'); + } + + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (str.charAt(0) == '-') { + return Long.fromString(str.substring(1), radix).negate(); + } else if (str.indexOf('-') >= 0) { + throw Error('number format error: interior "-" character: ' + str); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 8)); + + var result = Long.ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i); + var value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = Long.fromNumber(Math.pow(radix, size)); + result = result.multiply(power).add(Long.fromNumber(value)); + } else { + result = result.multiply(radixToPower); + result = result.add(Long.fromNumber(value)); + } + } + return result; +}; + +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the +// from* methods on which they depend. + + +/** + * A cache of the Long representations of small integer values. + * @type {Object} + * @api private + */ +Long.INT_CACHE_ = {}; + +// NOTE: the compiler should inline these constant values below and then remove +// these variables, so there should be no runtime penalty for these. + +/** + * Number used repeated below in calculations. This must appear before the + * first call to any from* function below. + * @type {number} + * @api private + */ +Long.TWO_PWR_16_DBL_ = 1 << 16; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_24_DBL_ = 1 << 24; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_32_DBL_ = Long.TWO_PWR_16_DBL_ * Long.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_31_DBL_ = Long.TWO_PWR_32_DBL_ / 2; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_48_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_64_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_32_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_63_DBL_ = Long.TWO_PWR_64_DBL_ / 2; + +/** @type {Long} */ +Long.ZERO = Long.fromInt(0); + +/** @type {Long} */ +Long.ONE = Long.fromInt(1); + +/** @type {Long} */ +Long.NEG_ONE = Long.fromInt(-1); + +/** @type {Long} */ +Long.MAX_VALUE = + Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0); + +/** @type {Long} */ +Long.MIN_VALUE = Long.fromBits(0, 0x80000000 | 0); + +/** + * @type {Long} + * @api private + */ +Long.TWO_PWR_24_ = Long.fromInt(1 << 24); + +/** + * Expose. + */ +exports.Long = Long; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js new file mode 100644 index 0000000..0825408 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js @@ -0,0 +1,13 @@ +/** + * A class representation of the BSON MaxKey type. + * + * @class Represents the BSON MaxKey type. + * @return {MaxKey} + */ +function MaxKey() { + if(!(this instanceof MaxKey)) return new MaxKey(); + + this._bsontype = 'MaxKey'; +} + +exports.MaxKey = MaxKey; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js new file mode 100644 index 0000000..230c2e6 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js @@ -0,0 +1,13 @@ +/** + * A class representation of the BSON MinKey type. + * + * @class Represents the BSON MinKey type. + * @return {MinKey} + */ +function MinKey() { + if(!(this instanceof MinKey)) return new MinKey(); + + this._bsontype = 'MinKey'; +} + +exports.MinKey = MinKey; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js new file mode 100644 index 0000000..1ff9a83 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js @@ -0,0 +1,253 @@ +/** + * Module dependencies. + */ +var BinaryParser = require('./binary_parser').BinaryParser; + +/** + * Machine id. + * + * Create a random 3-byte value (i.e. unique for this + * process). Other drivers use a md5 of the machine id here, but + * that would mean an asyc call to gethostname, so we don't bother. + */ +var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10); + +// Regular expression that checks for hex value +var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$"); + +/** +* Create a new ObjectID instance +* +* @class Represents the BSON ObjectID type +* @param {String|Number} id Can be a 24 byte hex string, 12 byte binary string or a Number. +* @return {Object} instance of ObjectID. +*/ +var ObjectID = function ObjectID(id, _hex) { + if(!(this instanceof ObjectID)) return new ObjectID(id, _hex); + + this._bsontype = 'ObjectID'; + var __id = null; + + // Throw an error if it's not a valid setup + if(id != null && 'number' != typeof id && (id.length != 12 && id.length != 24)) + throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"); + + // Generate id based on the input + if(id == null || typeof id == 'number') { + // convert to 12 byte binary string + this.id = this.generate(id); + } else if(id != null && id.length === 12) { + // assume 12 byte string + this.id = id; + } else if(checkForHexRegExp.test(id)) { + return ObjectID.createFromHexString(id); + } else if(!checkForHexRegExp.test(id)) { + throw new Error("Value passed in is not a valid 24 character hex string"); + } + + if(ObjectID.cacheHexString) this.__id = this.toHexString(); +}; + +// Allow usage of ObjectId aswell as ObjectID +var ObjectId = ObjectID; + +/** +* Return the ObjectID id as a 24 byte hex string representation +* +* @return {String} return the 24 byte hex string representation. +* @api public +*/ +ObjectID.prototype.toHexString = function() { + if(ObjectID.cacheHexString && this.__id) return this.__id; + + var hexString = '' + , number + , value; + + for (var index = 0, len = this.id.length; index < len; index++) { + value = BinaryParser.toByte(this.id[index]); + number = value <= 15 + ? '0' + value.toString(16) + : value.toString(16); + hexString = hexString + number; + } + + if(ObjectID.cacheHexString) this.__id = hexString; + return hexString; +}; + +/** +* Update the ObjectID index used in generating new ObjectID's on the driver +* +* @return {Number} returns next index value. +* @api private +*/ +ObjectID.prototype.get_inc = function() { + return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF; +}; + +/** +* Update the ObjectID index used in generating new ObjectID's on the driver +* +* @return {Number} returns next index value. +* @api private +*/ +ObjectID.prototype.getInc = function() { + return this.get_inc(); +}; + +/** +* Generate a 12 byte id string used in ObjectID's +* +* @param {Number} [time] optional parameter allowing to pass in a second based timestamp. +* @return {String} return the 12 byte id binary string. +* @api private +*/ +ObjectID.prototype.generate = function(time) { + if ('number' == typeof time) { + var time4Bytes = BinaryParser.encodeInt(time, 32, true, true); + /* for time-based ObjectID the bytes following the time will be zeroed */ + var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false); + var pid2Bytes = BinaryParser.fromShort(typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid); + var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true); + } else { + var unixTime = parseInt(Date.now()/1000,10); + var time4Bytes = BinaryParser.encodeInt(unixTime, 32, true, true); + var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false); + var pid2Bytes = BinaryParser.fromShort(typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid); + var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true); + } + + return time4Bytes + machine3Bytes + pid2Bytes + index3Bytes; +}; + +/** +* Converts the id into a 24 byte hex string for printing +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.toString = function() { + return this.toHexString(); +}; + +/** +* Converts to a string representation of this Id. +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.inspect = ObjectID.prototype.toString; + +/** +* Converts to its JSON representation. +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.toJSON = function() { + return this.toHexString(); +}; + +/** +* Compares the equality of this ObjectID with `otherID`. +* +* @param {Object} otherID ObjectID instance to compare against. +* @return {Bool} the result of comparing two ObjectID's +* @api public +*/ +ObjectID.prototype.equals = function equals (otherID) { + var id = (otherID instanceof ObjectID || otherID.toHexString) + ? otherID.id + : ObjectID.createFromHexString(otherID).id; + + return this.id === id; +} + +/** +* Returns the generation time in seconds that this ID was generated. +* +* @return {Number} return number of seconds in the timestamp part of the 12 byte id. +* @api public +*/ +ObjectID.prototype.getTimestamp = function() { + var timestamp = new Date(); + timestamp.setTime(Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)) * 1000); + return timestamp; +} + +/** +* @ignore +* @api private +*/ +ObjectID.index = 0; + +ObjectID.createPk = function createPk () { + return new ObjectID(); +}; + +/** +* Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID. +* +* @param {Number} time an integer number representing a number of seconds. +* @return {ObjectID} return the created ObjectID +* @api public +*/ +ObjectID.createFromTime = function createFromTime (time) { + var id = BinaryParser.encodeInt(time, 32, true, true) + + BinaryParser.encodeInt(0, 64, true, true); + return new ObjectID(id); +}; + +/** +* Creates an ObjectID from a hex string representation of an ObjectID. +* +* @param {String} hexString create a ObjectID from a passed in 24 byte hexstring. +* @return {ObjectID} return the created ObjectID +* @api public +*/ +ObjectID.createFromHexString = function createFromHexString (hexString) { + // Throw an error if it's not a valid setup + if(typeof hexString === 'undefined' || hexString != null && hexString.length != 24) + throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"); + + var len = hexString.length; + + if(len > 12*2) { + throw new Error('Id cannot be longer than 12 bytes'); + } + + var result = '' + , string + , number; + + for (var index = 0; index < len; index += 2) { + string = hexString.substr(index, 2); + number = parseInt(string, 16); + result += BinaryParser.fromByte(number); + } + + return new ObjectID(result, hexString); +}; + +/** +* @ignore +*/ +Object.defineProperty(ObjectID.prototype, "generationTime", { + enumerable: true + , get: function () { + return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)); + } + , set: function (value) { + var value = BinaryParser.encodeInt(value, 32, true, true); + this.id = value + this.id.substr(4); + // delete this.__id; + this.toHexString(); + } +}); + +/** + * Expose. + */ +exports.ObjectID = ObjectID; +exports.ObjectId = ObjectID; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js new file mode 100644 index 0000000..8e2838d --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js @@ -0,0 +1,48 @@ +/** + * A class representation of the BSON Symbol type. + * + * @class Represents the BSON Symbol type. + * @param {String} value the string representing the symbol. + * @return {Symbol} + */ +function Symbol(value) { + if(!(this instanceof Symbol)) return new Symbol(value); + this._bsontype = 'Symbol'; + this.value = value; +} + +/** + * Access the wrapped string value. + * + * @return {String} returns the wrapped string. + * @api public + */ +Symbol.prototype.valueOf = function() { + return this.value; +}; + +/** + * @ignore + * @api private + */ +Symbol.prototype.toString = function() { + return this.value; +} + +/** + * @ignore + * @api private + */ +Symbol.prototype.inspect = function() { + return this.value; +} + +/** + * @ignore + * @api private + */ +Symbol.prototype.toJSON = function() { + return this.value; +} + +exports.Symbol = Symbol; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js new file mode 100644 index 0000000..c650d15 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js @@ -0,0 +1,853 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2009 Google Inc. All Rights Reserved + +/** + * Defines a Timestamp class for representing a 64-bit two's-complement + * integer value, which faithfully simulates the behavior of a Java "Timestamp". This + * implementation is derived from TimestampLib in GWT. + * + * Constructs a 64-bit two's-complement integer, given its low and high 32-bit + * values as *signed* integers. See the from* functions below for more + * convenient ways of constructing Timestamps. + * + * The internal representation of a Timestamp is the two given signed, 32-bit values. + * We use 32-bit pieces because these are the size of integers on which + * Javascript performs bit-operations. For operations like addition and + * multiplication, we split each number into 16-bit pieces, which can easily be + * multiplied within Javascript's floating-point representation without overflow + * or change in sign. + * + * In the algorithms below, we frequently reduce the negative case to the + * positive case by negating the input(s) and then post-processing the result. + * Note that we must ALWAYS check specially whether those values are MIN_VALUE + * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + * a positive number, it overflows back into a negative). Not handling this + * case would often result in infinite recursion. + * + * @class Represents the BSON Timestamp type. + * @param {Number} low the low (signed) 32 bits of the Timestamp. + * @param {Number} high the high (signed) 32 bits of the Timestamp. + */ +function Timestamp(low, high) { + if(!(this instanceof Timestamp)) return new Timestamp(low, high); + this._bsontype = 'Timestamp'; + /** + * @type {number} + * @api private + */ + this.low_ = low | 0; // force into 32 signed bits. + + /** + * @type {number} + * @api private + */ + this.high_ = high | 0; // force into 32 signed bits. +}; + +/** + * Return the int value. + * + * @return {Number} the value, assuming it is a 32-bit integer. + * @api public + */ +Timestamp.prototype.toInt = function() { + return this.low_; +}; + +/** + * Return the Number value. + * + * @return {Number} the closest floating-point representation to this value. + * @api public + */ +Timestamp.prototype.toNumber = function() { + return this.high_ * Timestamp.TWO_PWR_32_DBL_ + + this.getLowBitsUnsigned(); +}; + +/** + * Return the JSON value. + * + * @return {String} the JSON representation. + * @api public + */ +Timestamp.prototype.toJSON = function() { + return this.toString(); +} + +/** + * Return the String value. + * + * @param {Number} [opt_radix] the radix in which the text should be written. + * @return {String} the textual representation of this value. + * @api public + */ +Timestamp.prototype.toString = function(opt_radix) { + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (this.isZero()) { + return '0'; + } + + if (this.isNegative()) { + if (this.equals(Timestamp.MIN_VALUE)) { + // We need to change the Timestamp value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixTimestamp = Timestamp.fromNumber(radix); + var div = this.div(radixTimestamp); + var rem = div.multiply(radixTimestamp).subtract(this); + return div.toString(radix) + rem.toInt().toString(radix); + } else { + return '-' + this.negate().toString(radix); + } + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Timestamp.fromNumber(Math.pow(radix, 6)); + + var rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower); + var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt(); + var digits = intval.toString(radix); + + rem = remDiv; + if (rem.isZero()) { + return digits + result; + } else { + while (digits.length < 6) { + digits = '0' + digits; + } + result = '' + digits + result; + } + } +}; + +/** + * Return the high 32-bits value. + * + * @return {Number} the high 32-bits as a signed value. + * @api public + */ +Timestamp.prototype.getHighBits = function() { + return this.high_; +}; + +/** + * Return the low 32-bits value. + * + * @return {Number} the low 32-bits as a signed value. + * @api public + */ +Timestamp.prototype.getLowBits = function() { + return this.low_; +}; + +/** + * Return the low unsigned 32-bits value. + * + * @return {Number} the low 32-bits as an unsigned value. + * @api public + */ +Timestamp.prototype.getLowBitsUnsigned = function() { + return (this.low_ >= 0) ? + this.low_ : Timestamp.TWO_PWR_32_DBL_ + this.low_; +}; + +/** + * Returns the number of bits needed to represent the absolute value of this Timestamp. + * + * @return {Number} Returns the number of bits needed to represent the absolute value of this Timestamp. + * @api public + */ +Timestamp.prototype.getNumBitsAbs = function() { + if (this.isNegative()) { + if (this.equals(Timestamp.MIN_VALUE)) { + return 64; + } else { + return this.negate().getNumBitsAbs(); + } + } else { + var val = this.high_ != 0 ? this.high_ : this.low_; + for (var bit = 31; bit > 0; bit--) { + if ((val & (1 << bit)) != 0) { + break; + } + } + return this.high_ != 0 ? bit + 33 : bit + 1; + } +}; + +/** + * Return whether this value is zero. + * + * @return {Boolean} whether this value is zero. + * @api public + */ +Timestamp.prototype.isZero = function() { + return this.high_ == 0 && this.low_ == 0; +}; + +/** + * Return whether this value is negative. + * + * @return {Boolean} whether this value is negative. + * @api public + */ +Timestamp.prototype.isNegative = function() { + return this.high_ < 0; +}; + +/** + * Return whether this value is odd. + * + * @return {Boolean} whether this value is odd. + * @api public + */ +Timestamp.prototype.isOdd = function() { + return (this.low_ & 1) == 1; +}; + +/** + * Return whether this Timestamp equals the other + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp equals the other + * @api public + */ +Timestamp.prototype.equals = function(other) { + return (this.high_ == other.high_) && (this.low_ == other.low_); +}; + +/** + * Return whether this Timestamp does not equal the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp does not equal the other. + * @api public + */ +Timestamp.prototype.notEquals = function(other) { + return (this.high_ != other.high_) || (this.low_ != other.low_); +}; + +/** + * Return whether this Timestamp is less than the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is less than the other. + * @api public + */ +Timestamp.prototype.lessThan = function(other) { + return this.compare(other) < 0; +}; + +/** + * Return whether this Timestamp is less than or equal to the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is less than or equal to the other. + * @api public + */ +Timestamp.prototype.lessThanOrEqual = function(other) { + return this.compare(other) <= 0; +}; + +/** + * Return whether this Timestamp is greater than the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is greater than the other. + * @api public + */ +Timestamp.prototype.greaterThan = function(other) { + return this.compare(other) > 0; +}; + +/** + * Return whether this Timestamp is greater than or equal to the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is greater than or equal to the other. + * @api public + */ +Timestamp.prototype.greaterThanOrEqual = function(other) { + return this.compare(other) >= 0; +}; + +/** + * Compares this Timestamp with the given one. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater. + * @api public + */ +Timestamp.prototype.compare = function(other) { + if (this.equals(other)) { + return 0; + } + + var thisNeg = this.isNegative(); + var otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) { + return -1; + } + if (!thisNeg && otherNeg) { + return 1; + } + + // at this point, the signs are the same, so subtraction will not overflow + if (this.subtract(other).isNegative()) { + return -1; + } else { + return 1; + } +}; + +/** + * The negation of this value. + * + * @return {Timestamp} the negation of this value. + * @api public + */ +Timestamp.prototype.negate = function() { + if (this.equals(Timestamp.MIN_VALUE)) { + return Timestamp.MIN_VALUE; + } else { + return this.not().add(Timestamp.ONE); + } +}; + +/** + * Returns the sum of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to add to this one. + * @return {Timestamp} the sum of this and the given Timestamp. + * @api public + */ +Timestamp.prototype.add = function(other) { + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns the difference of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to subtract from this. + * @return {Timestamp} the difference of this and the given Timestamp. + * @api public + */ +Timestamp.prototype.subtract = function(other) { + return this.add(other.negate()); +}; + +/** + * Returns the product of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to multiply with this. + * @return {Timestamp} the product of this and the other. + * @api public + */ +Timestamp.prototype.multiply = function(other) { + if (this.isZero()) { + return Timestamp.ZERO; + } else if (other.isZero()) { + return Timestamp.ZERO; + } + + if (this.equals(Timestamp.MIN_VALUE)) { + return other.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO; + } else if (other.equals(Timestamp.MIN_VALUE)) { + return this.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().multiply(other.negate()); + } else { + return this.negate().multiply(other).negate(); + } + } else if (other.isNegative()) { + return this.multiply(other.negate()).negate(); + } + + // If both Timestamps are small, use float multiplication + if (this.lessThan(Timestamp.TWO_PWR_24_) && + other.lessThan(Timestamp.TWO_PWR_24_)) { + return Timestamp.fromNumber(this.toNumber() * other.toNumber()); + } + + // Divide each Timestamp into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns this Timestamp divided by the given one. + * + * @param {Timestamp} other Timestamp by which to divide. + * @return {Timestamp} this Timestamp divided by the given one. + * @api public + */ +Timestamp.prototype.div = function(other) { + if (other.isZero()) { + throw Error('division by zero'); + } else if (this.isZero()) { + return Timestamp.ZERO; + } + + if (this.equals(Timestamp.MIN_VALUE)) { + if (other.equals(Timestamp.ONE) || + other.equals(Timestamp.NEG_ONE)) { + return Timestamp.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + } else if (other.equals(Timestamp.MIN_VALUE)) { + return Timestamp.ONE; + } else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shiftRight(1); + var approx = halfThis.div(other).shiftLeft(1); + if (approx.equals(Timestamp.ZERO)) { + return other.isNegative() ? Timestamp.ONE : Timestamp.NEG_ONE; + } else { + var rem = this.subtract(other.multiply(approx)); + var result = approx.add(rem.div(other)); + return result; + } + } + } else if (other.equals(Timestamp.MIN_VALUE)) { + return Timestamp.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().div(other.negate()); + } else { + return this.negate().div(other).negate(); + } + } else if (other.isNegative()) { + return this.div(other.negate()).negate(); + } + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + var res = Timestamp.ZERO; + var rem = this; + while (rem.greaterThanOrEqual(other)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2); + var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48); + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + var approxRes = Timestamp.fromNumber(approx); + var approxRem = approxRes.multiply(other); + while (approxRem.isNegative() || approxRem.greaterThan(rem)) { + approx -= delta; + approxRes = Timestamp.fromNumber(approx); + approxRem = approxRes.multiply(other); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) { + approxRes = Timestamp.ONE; + } + + res = res.add(approxRes); + rem = rem.subtract(approxRem); + } + return res; +}; + +/** + * Returns this Timestamp modulo the given one. + * + * @param {Timestamp} other Timestamp by which to mod. + * @return {Timestamp} this Timestamp modulo the given one. + * @api public + */ +Timestamp.prototype.modulo = function(other) { + return this.subtract(this.div(other).multiply(other)); +}; + +/** + * The bitwise-NOT of this value. + * + * @return {Timestamp} the bitwise-NOT of this value. + * @api public + */ +Timestamp.prototype.not = function() { + return Timestamp.fromBits(~this.low_, ~this.high_); +}; + +/** + * Returns the bitwise-AND of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to AND. + * @return {Timestamp} the bitwise-AND of this and the other. + * @api public + */ +Timestamp.prototype.and = function(other) { + return Timestamp.fromBits(this.low_ & other.low_, this.high_ & other.high_); +}; + +/** + * Returns the bitwise-OR of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to OR. + * @return {Timestamp} the bitwise-OR of this and the other. + * @api public + */ +Timestamp.prototype.or = function(other) { + return Timestamp.fromBits(this.low_ | other.low_, this.high_ | other.high_); +}; + +/** + * Returns the bitwise-XOR of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to XOR. + * @return {Timestamp} the bitwise-XOR of this and the other. + * @api public + */ +Timestamp.prototype.xor = function(other) { + return Timestamp.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_); +}; + +/** + * Returns this Timestamp with bits shifted to the left by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the left by the given amount. + * @api public + */ +Timestamp.prototype.shiftLeft = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var low = this.low_; + if (numBits < 32) { + var high = this.high_; + return Timestamp.fromBits( + low << numBits, + (high << numBits) | (low >>> (32 - numBits))); + } else { + return Timestamp.fromBits(0, low << (numBits - 32)); + } + } +}; + +/** + * Returns this Timestamp with bits shifted to the right by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the right by the given amount. + * @api public + */ +Timestamp.prototype.shiftRight = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Timestamp.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >> numBits); + } else { + return Timestamp.fromBits( + high >> (numBits - 32), + high >= 0 ? 0 : -1); + } + } +}; + +/** + * Returns this Timestamp with bits shifted to the right by the given amount, with the new top bits matching the current sign bit. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the right by the given amount, with zeros placed into the new leading bits. + * @api public + */ +Timestamp.prototype.shiftRightUnsigned = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Timestamp.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >>> numBits); + } else if (numBits == 32) { + return Timestamp.fromBits(high, 0); + } else { + return Timestamp.fromBits(high >>> (numBits - 32), 0); + } + } +}; + +/** + * Returns a Timestamp representing the given (32-bit) integer value. + * + * @param {Number} value the 32-bit integer in question. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromInt = function(value) { + if (-128 <= value && value < 128) { + var cachedObj = Timestamp.INT_CACHE_[value]; + if (cachedObj) { + return cachedObj; + } + } + + var obj = new Timestamp(value | 0, value < 0 ? -1 : 0); + if (-128 <= value && value < 128) { + Timestamp.INT_CACHE_[value] = obj; + } + return obj; +}; + +/** + * Returns a Timestamp representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * + * @param {Number} value the number in question. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromNumber = function(value) { + if (isNaN(value) || !isFinite(value)) { + return Timestamp.ZERO; + } else if (value <= -Timestamp.TWO_PWR_63_DBL_) { + return Timestamp.MIN_VALUE; + } else if (value + 1 >= Timestamp.TWO_PWR_63_DBL_) { + return Timestamp.MAX_VALUE; + } else if (value < 0) { + return Timestamp.fromNumber(-value).negate(); + } else { + return new Timestamp( + (value % Timestamp.TWO_PWR_32_DBL_) | 0, + (value / Timestamp.TWO_PWR_32_DBL_) | 0); + } +}; + +/** + * Returns a Timestamp representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits. + * + * @param {Number} lowBits the low 32-bits. + * @param {Number} highBits the high 32-bits. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromBits = function(lowBits, highBits) { + return new Timestamp(lowBits, highBits); +}; + +/** + * Returns a Timestamp representation of the given string, written using the given radix. + * + * @param {String} str the textual representation of the Timestamp. + * @param {Number} opt_radix the radix in which the text is written. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromString = function(str, opt_radix) { + if (str.length == 0) { + throw Error('number format error: empty string'); + } + + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (str.charAt(0) == '-') { + return Timestamp.fromString(str.substring(1), radix).negate(); + } else if (str.indexOf('-') >= 0) { + throw Error('number format error: interior "-" character: ' + str); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Timestamp.fromNumber(Math.pow(radix, 8)); + + var result = Timestamp.ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i); + var value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = Timestamp.fromNumber(Math.pow(radix, size)); + result = result.multiply(power).add(Timestamp.fromNumber(value)); + } else { + result = result.multiply(radixToPower); + result = result.add(Timestamp.fromNumber(value)); + } + } + return result; +}; + +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the +// from* methods on which they depend. + + +/** + * A cache of the Timestamp representations of small integer values. + * @type {Object} + * @api private + */ +Timestamp.INT_CACHE_ = {}; + +// NOTE: the compiler should inline these constant values below and then remove +// these variables, so there should be no runtime penalty for these. + +/** + * Number used repeated below in calculations. This must appear before the + * first call to any from* function below. + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_16_DBL_ = 1 << 16; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_24_DBL_ = 1 << 24; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_32_DBL_ = Timestamp.TWO_PWR_16_DBL_ * Timestamp.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_31_DBL_ = Timestamp.TWO_PWR_32_DBL_ / 2; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_48_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_64_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_32_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_63_DBL_ = Timestamp.TWO_PWR_64_DBL_ / 2; + +/** @type {Timestamp} */ +Timestamp.ZERO = Timestamp.fromInt(0); + +/** @type {Timestamp} */ +Timestamp.ONE = Timestamp.fromInt(1); + +/** @type {Timestamp} */ +Timestamp.NEG_ONE = Timestamp.fromInt(-1); + +/** @type {Timestamp} */ +Timestamp.MAX_VALUE = + Timestamp.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0); + +/** @type {Timestamp} */ +Timestamp.MIN_VALUE = Timestamp.fromBits(0, 0x80000000 | 0); + +/** + * @type {Timestamp} + * @api private + */ +Timestamp.TWO_PWR_24_ = Timestamp.fromInt(1 << 24); + +/** + * Expose. + */ +exports.Timestamp = Timestamp; \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/package.json b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/package.json new file mode 100644 index 0000000..e4b172c --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/package.json @@ -0,0 +1,58 @@ +{ + "name": "bson", + "description": "A bson parser for node.js and the browser", + "keywords": [ + "mongodb", + "bson", + "parser" + ], + "version": "0.1.5", + "author": { + "name": "Christian Amor Kvalheim", + "email": "christkv@gmail.com" + }, + "contributors": [], + "repository": { + "type": "git", + "url": "git://github.com/mongodb/js-bson.git" + }, + "bugs": { + "email": "node-mongodb-native@googlegroups.com", + "url": "https://github.com/mongodb/js-bson/issues" + }, + "devDependencies": { + "nodeunit": "0.7.3", + "gleak": "0.2.3" + }, + "config": { + "native": false + }, + "main": "./lib/bson/index", + "directories": { + "lib": "./lib/bson" + }, + "engines": { + "node": ">=0.6.0" + }, + "scripts": { + "install": "node install.js || (exit 0)", + "test": "nodeunit ./test/node && TEST_NATIVE=TRUE nodeunit ./test/node" + }, + "licenses": [ + { + "type": "Apache License, Version 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "_id": "bson@0.1.5", + "dependencies": {}, + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.21", + "_nodeVersion": "v0.6.18", + "_defaultsLoaded": true, + "dist": { + "shasum": "f1b9a41a40fcad62fc6660b71c76bad2c093aa06" + }, + "_from": "bson@0.1.5" +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js new file mode 100644 index 0000000..84d8ffc --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js @@ -0,0 +1,260 @@ +this.bson_test = { + 'Full document serialization and deserialization': function (test) { + var motherOfAllDocuments = { + 'string': "客家话", + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary('hello world'), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSON.serialize(motherOfAllDocuments, true, true, false); + // Deserialize the object + var object = BSON.deserialize(data); + + // Asserts + test.equal(Utf8.decode(motherOfAllDocuments.string), object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + test.ok(assertArrayEqual(motherOfAllDocuments.binary.value(true), object.binary.value(true))); + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); + }, + + 'exercise all the binary object constructor methods': function (test) { + // Construct using array + var string = 'hello world'; + // String to array + var array = stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + + // Construct using number of chars + binary = new Binary(5); + test.ok(5, binary.buffer.length); + + // Construct using an Array + var binary = new Binary(stringToArray(string)); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + + // Construct using a string + var binary = new Binary(string); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + test.done(); + }, + + 'exercise the put binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + // String to array + var array = stringToArrayBuffer(string + 'a'); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(string.length + 1, binary.position); + test.ok(assertArrayEqual(array, binary.value(true))); + test.equal('hello worlda', binary.value()); + + // Exercise a binary with lots of space in the buffer + var binary = new Binary(); + test.ok(Binary.BUFFER_SIZE, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(1, binary.position); + test.ok(assertArrayEqual(['a'.charCodeAt(0)], binary.value(true))); + test.equal('a', binary.value()); + test.done(); + }, + + 'exercise the write binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + // Array + var writeArrayBuffer = new Uint8Array(new ArrayBuffer(1)); + writeArrayBuffer[0] = 'a'.charCodeAt(0); + var arrayBuffer = ['a'.charCodeAt(0)]; + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a string starting at end of buffer + binary.write('a'); + test.equal('hello worlda', binary.value()); + // Write a string starting at index 0 + binary.write('a', 0); + test.equal('aello worlda', binary.value()); + // Write a arraybuffer starting at end of buffer + binary.write(writeArrayBuffer); + test.equal('aello worldaa', binary.value()); + // Write a arraybuffer starting at position 5 + binary.write(writeArrayBuffer, 5); + test.equal('aelloaworldaa', binary.value()); + // Write a array starting at end of buffer + binary.write(arrayBuffer); + test.equal('aelloaworldaaa', binary.value()); + // Write a array starting at position 6 + binary.write(arrayBuffer, 6); + test.equal('aelloaaorldaaa', binary.value()); + test.done(); + }, + + 'exercise the read binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + var array = stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Read the first 2 bytes + var data = binary.read(0, 2); + test.ok(assertArrayEqual(stringToArrayBuffer('he'), data)); + + // Read the entire field + var data = binary.read(0); + test.ok(assertArrayEqual(stringToArrayBuffer(string), data)); + + // Read 3 bytes + var data = binary.read(6, 5); + test.ok(assertArrayEqual(stringToArrayBuffer('world'), data)); + test.done(); + }, + + 'Should correctly handle toBson function for an object': function(test) { + // Test object + var doc = { + hello: new ObjectID(), + a:1 + }; + // Add a toBson method to the object + doc.toBSON = function() { + return {b:1}; + } + + // Serialize the data + var serialized_data = BSON.serialize(doc, false, true); + var deserialized_doc = BSON.deserialize(serialized_data); + test.equal(1, deserialized_doc.b); + test.done(); + } +}; + +var assertArrayEqual = function(array1, array2) { + if(array1.length != array2.length) return false; + for(var i = 0; i < array1.length; i++) { + if(array1[i] != array2[i]) return false; + } + + return true; +} + +// String to arraybuffer +var stringToArrayBuffer = function(string) { + var dataBuffer = new Uint8Array(new ArrayBuffer(string.length)); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +// String to arraybuffer +var stringToArray = function(string) { + var dataBuffer = new Array(string.length); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +var Utf8 = { + // public method for url encoding + encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // public method for url decoding + decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + c = utftext.charCodeAt(i); + if(c < 128) { + string += String.fromCharCode(c); + i++; + } else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js new file mode 100644 index 0000000..af7fd0b --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js @@ -0,0 +1,2034 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +var JSON = {}; + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ +.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') +.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') +.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); +var assert = this.assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +/*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; + }; + + var _indexOf = function (arr, item) { + if (arr.indexOf) { + return arr.indexOf(item); + } + for (var i = 0; i < arr.length; i += 1) { + if (arr[i] === item) { + return i; + } + } + return -1; + }; + + //// 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) { + 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) { + 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(); + }; + + + 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); + } + 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}); + } + }); + }, function (err, results) { + if (err) { + return callback(err); + } + else { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var completed = []; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _forEach(listeners, function (fn) { + fn(); + }); + }; + + addListener(function () { + if (completed.length === keys.length) { + callback(null); + } + }); + + _forEach(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + if (err) { + callback(err); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + completed.push(k); + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && _indexOf(completed, x) !== -1); + }, true); + }; + if (ready()) { + task[task.length - 1](taskCallback); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + if (!tasks.length) { + return callback(); + } + callback = callback || function () {}; + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + async.parallel = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args || null); + }); + } + }, callback); + } + else { + var results = {}; + async.forEach(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args || null); + }); + } + }, callback); + } + else { + var results = {}; + async.forEachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1): null; + }; + return fn; + }; + return makeCallback(0); + }; + + async.apply = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.queue = function (worker, concurrency) { + var workers = 0; + var tasks = []; + var q = { + concurrency: concurrency, + push: function (data, callback) { + tasks.push({data: data, callback: callback}); + async.nextTick(q.process); + }, + process: function () { + if (workers < q.concurrency && tasks.length) { + var task = tasks.splice(0, 1)[0]; + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + q.process(); + }); + } + }, + length: function () { + return tasks.length; + } + }; + return q; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _forEach(args, function (x) { + console[name](x); + }); + } + } + }])); + }; + }; + async.log = _console_fn('log'); + async.dir = _console_fn('dir'); + /*async.info = _console_fn('info'); + async.warn = _console_fn('warn'); + async.error = _console_fn('error');*/ + + async.memoize = function (fn, hasher) { + var memo = {}; + hasher = hasher || function (x) { + return x; + }; + return function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else { + fn.apply(null, args.concat([function () { + memo[key] = arguments; + callback.apply(null, arguments); + }])); + } + }; + }; + +}()); +(function(exports){ +/** + * This file is based on the node.js assert module, but with some small + * changes for browser-compatibility + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + */ + + +/** + * Added for browser compatibility + */ + +var _keys = function(obj){ + if(Object.keys) return Object.keys(obj); + if (typeof obj != 'object' && typeof obj != 'function') { + throw new TypeError('-'); + } + var keys = []; + for(var k in obj){ + if(obj.hasOwnProperty(k)) keys.push(k); + } + return keys; +}; + + + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// 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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isUndefinedOrNull (value) { + return value === null || value === undefined; +} + +function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try{ + var ka = _keys(a), + kb = _keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + +//var assert = require('./assert'), //@REMOVE_LINE_FOR_BROWSER +// async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i = 0; i < this.length; i += 1) { + if (this[i].failed()) { + failures += 1; + } + } + return failures; + }; + that.passes = function () { + return that.length - that.failures(); + }; + that.duration = duration || 0; + return that; +}; + +/** + * Create a wrapper function for assert module methods. Executes a callback + * after the it's complete with an assertion object representing the result. + * + * @param {Function} callback + * @api private + */ + +var assertWrapper = function (callback) { + return function (new_method, assert_method, arity) { + return function () { + var message = arguments[arity - 1]; + var a = exports.assertion({method: new_method, message: message}); + try { + assert[assert_method].apply(null, arguments); + } + catch (e) { + a.error = e; + } + callback(a); + }; + }; +}; + +/** + * Creates the 'test' object that gets passed to every test function. + * Accepts the name of the test function as its first argument, followed by + * the start time in ms, the options object and a callback function. + * + * @param {String} name + * @param {Number} start + * @param {Object} options + * @param {Function} callback + * @api public + */ + +exports.test = function (name, start, options, callback) { + var expecting; + var a_list = []; + + var wrapAssert = assertWrapper(function (a) { + a_list.push(a); + if (options.log) { + async.nextTick(function () { + options.log(a); + }); + } + }); + + var test = { + done: function (err) { + if (expecting !== undefined && expecting !== a_list.length) { + var e = new Error( + 'Expected ' + expecting + ' assertions, ' + + a_list.length + ' ran' + ); + var a1 = exports.assertion({method: 'expect', error: e}); + a_list.push(a1); + if (options.log) { + async.nextTick(function () { + options.log(a1); + }); + } + } + if (err) { + var a2 = exports.assertion({error: err}); + a_list.push(a2); + if (options.log) { + async.nextTick(function () { + options.log(a2); + }); + } + } + var end = new Date().getTime(); + async.nextTick(function () { + var assertion_list = exports.assertionList(a_list, end - start); + options.testDone(name, assertion_list); + callback(null, a_list); + }); + }, + ok: wrapAssert('ok', 'ok', 2), + same: wrapAssert('same', 'deepEqual', 3), + equals: wrapAssert('equals', 'equal', 3), + expect: function (num) { + expecting = num; + }, + _assertion_list: a_list + }; + // add all functions from the assert module + for (var k in assert) { + if (assert.hasOwnProperty(k)) { + test[k] = wrapAssert(k, k, assert[k].length); + } + } + return test; +}; + +/** + * Ensures an options object has all callbacks, adding empty callback functions + * if any are missing. + * + * @param {Object} opt + * @return {Object} + * @api public + */ + +exports.options = function (opt) { + var optionalCallback = function (name) { + opt[name] = opt[name] || function () {}; + }; + + optionalCallback('moduleStart'); + optionalCallback('moduleDone'); + optionalCallback('testStart'); + optionalCallback('testDone'); + //optionalCallback('log'); + + // 'done' callback is not optional. + + return opt; +}; +})(types); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + +//var async = require('../deps/async'), //@REMOVE_LINE_FOR_BROWSER +// types = require('./types'); //@REMOVE_LINE_FOR_BROWSER + + +/** + * Added for browser compatibility + */ + +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; +}; + + +var _copy = function (obj) { + var nobj = {}; + var keys = _keys(obj); + for (var i = 0; i < keys.length; i += 1) { + nobj[keys[i]] = obj[keys[i]]; + } + return nobj; +}; + + +/** + * Runs a test function (fn) from a loaded module. After the test function + * calls test.done(), the callback is executed with an assertionList as its + * second argument. + * + * @param {String} name + * @param {Function} fn + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runTest = function (name, fn, opt, callback) { + var options = types.options(opt); + + options.testStart(name); + var start = new Date().getTime(); + var test = types.test(name, start, options, callback); + + try { + fn(test); + } + catch (e) { + test.done(e); + } +}; + +/** + * Takes an object containing test functions or other test suites as properties + * and runs each in series. After all tests have completed, the callback is + * called with a list of all assertions as the second argument. + * + * If a name is passed to this function it is prepended to all test and suite + * names that run within it. + * + * @param {String} name + * @param {Object} suite + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runSuite = function (name, suite, opt, callback) { + var keys = _keys(suite); + + async.concatSeries(keys, function (k, cb) { + var prop = suite[k], _name; + + _name = name ? [].concat(name, k) : [k]; + + _name.toString = function () { + // fallback for old one + return this.join(' - '); + }; + + if (typeof prop === 'function') { + var in_name = false; + for (var i = 0; i < _name.length; i += 1) { + if (_name[i] === opt.testspec) { + in_name = true; + } + } + if (!opt.testspec || in_name) { + if (opt.moduleStart) { + opt.moduleStart(); + } + exports.runTest(_name, suite[k], opt, cb); + } + else { + return cb(); + } + } + else { + exports.runSuite(_name, suite[k], opt, cb); + } + }, callback); +}; + +/** + * Run each exported test function or test suite from a loaded module. + * + * @param {String} name + * @param {Object} mod + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runModule = function (name, mod, opt, callback) { + var options = _copy(types.options(opt)); + + var _run = false; + var _moduleStart = options.moduleStart; + function run_once() { + if (!_run) { + _run = true; + _moduleStart(name); + } + } + options.moduleStart = run_once; + + var start = new Date().getTime(); + + exports.runSuite(null, mod, options, function (err, a_list) { + var end = new Date().getTime(); + var assertion_list = types.assertionList(a_list, end - start); + options.moduleDone(name, assertion_list); + callback(null, a_list); + }); +}; + +/** + * Treats an object literal as a list of modules keyed by name. Runs each + * module and finished with calling 'done'. You can think of this as a browser + * safe alternative to runFiles in the nodeunit module. + * + * @param {Object} modules + * @param {Object} opt + * @api public + */ + +// TODO: add proper unit tests for this function +exports.runModules = function (modules, opt) { + var all_assertions = []; + var options = types.options(opt); + var start = new Date().getTime(); + + async.concatSeries(_keys(modules), function (k, cb) { + exports.runModule(k, modules[k], options, cb); + }, + function (err, all_assertions) { + var end = new Date().getTime(); + options.done(types.assertionList(all_assertions, end - start)); + }); +}; + + +/** + * Wraps a test function with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Function} fn + * @api private + */ + +var wrapTest = function (setUp, tearDown, fn) { + return function (test) { + var context = {}; + if (tearDown) { + var done = test.done; + test.done = function (err) { + try { + tearDown.call(context, function (err2) { + if (err && err2) { + test._assertion_list.push( + types.assertion({error: err}) + ); + return done(err2); + } + done(err || err2); + }); + } + catch (e) { + done(e); + } + }; + } + if (setUp) { + setUp.call(context, function (err) { + if (err) { + return test.done(err); + } + fn.call(context, test); + }); + } + else { + fn.call(context, test); + } + }; +}; + + +/** + * Wraps a group of tests with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Object} group + * @api private + */ + +var wrapGroup = function (setUp, tearDown, group) { + var tests = {}; + var keys = _keys(group); + for (var i = 0; i < keys.length; i += 1) { + var k = keys[i]; + if (typeof group[k] === 'function') { + tests[k] = wrapTest(setUp, tearDown, group[k]); + } + else if (typeof group[k] === 'object') { + tests[k] = wrapGroup(setUp, tearDown, group[k]); + } + } + return tests; +}; + + +/** + * Utility for wrapping a suite of test functions with setUp and tearDown + * functions. + * + * @param {Object} suite + * @return {Object} + * @api public + */ + +exports.testCase = function (suite) { + var setUp = suite.setUp; + var tearDown = suite.tearDown; + delete suite.setUp; + delete suite.tearDown; + return wrapGroup(setUp, tearDown, suite); +}; +})(core); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + + +/** + * NOTE: this test runner is not listed in index.js because it cannot be + * used with the command-line tool, only inside the browser. + */ + + +/** + * Reporter info string + */ + +exports.info = "Browser-based test reporter"; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + + function setText(el, txt) { + if ('innerText' in el) { + el.innerText = txt; + } + else if ('textContent' in el){ + el.textContent = txt; + } + } + + function getOrCreate(tag, id) { + var el = document.getElementById(id); + if (!el) { + el = document.createElement(tag); + el.id = id; + document.body.appendChild(el); + } + return el; + }; + + var header = getOrCreate('h1', 'nodeunit-header'); + var banner = getOrCreate('h2', 'nodeunit-banner'); + var userAgent = getOrCreate('h2', 'nodeunit-userAgent'); + var tests = getOrCreate('ol', 'nodeunit-tests'); + var result = getOrCreate('p', 'nodeunit-testresult'); + + setText(userAgent, navigator.userAgent); + + nodeunit.runModules(modules, { + moduleStart: function (name) { + /*var mheading = document.createElement('h2'); + mheading.innerText = name; + results.appendChild(mheading); + module = document.createElement('ol'); + results.appendChild(module);*/ + }, + testDone: function (name, assertions) { + var test = document.createElement('li'); + var strong = document.createElement('strong'); + strong.innerHTML = name + ' (' + + '' + assertions.failures() + ', ' + + '' + assertions.passes() + ', ' + + assertions.length + + ')'; + test.className = assertions.failures() ? 'fail': 'pass'; + test.appendChild(strong); + + var aList = document.createElement('ol'); + aList.style.display = 'none'; + test.onclick = function () { + var d = aList.style.display; + aList.style.display = (d == 'none') ? 'block': 'none'; + }; + for (var i=0; i' + (a.error.stack || a.error) + ''; + li.className = 'fail'; + } + else { + li.innerHTML = a.message || a.method || 'no message'; + li.className = 'pass'; + } + aList.appendChild(li); + } + test.appendChild(aList); + tests.appendChild(test); + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + + var failures = assertions.failures(); + banner.className = failures ? 'fail': 'pass'; + + result.innerHTML = 'Tests completed in ' + duration + + ' milliseconds.
' + + assertions.passes() + ' assertions of ' + + '' + assertions.length + ' passed, ' + + assertions.failures() + ' failed.'; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite2.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite2.js new file mode 100644 index 0000000..c7288e8 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite2.js @@ -0,0 +1,13 @@ +this.suite2 = { + 'another test': function (test) { + setTimeout(function () { + // lots of assertions + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.done(); + }, 10); + } +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite3.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite3.js new file mode 100644 index 0000000..8929741 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/suite3.js @@ -0,0 +1,7 @@ +this.suite3 = { + 'test for ie6,7,8': function (test) { + test.deepEqual(["test"], ["test"]); + test.notDeepEqual(["a"], ["b"]); + test.done(); + } +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/test.html b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/test.html new file mode 100644 index 0000000..56d4d96 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/browser/test.html @@ -0,0 +1,30 @@ + + + Example tests + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js new file mode 100644 index 0000000..5304bef --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js @@ -0,0 +1,240 @@ +var mongodb = require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + utils = require('./tools/utils'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +var _Uint8Array = null; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + _Uint8Array = global.Uint8Array; + delete global['Uint8Array']; + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + global['Uint8Array'] = _Uint8Array; + callback(); +} + +// /** +// * @ignore +// */ +// exports.shouldCorrectlyDeserializeUsingTypedArray = function(test) { +// var motherOfAllDocuments = { +// 'string': '客家话', +// 'array': [1,2,3], +// 'hash': {'a':1, 'b':2}, +// 'date': new Date(), +// 'oid': new ObjectID(), +// 'binary': new Binary(new Buffer("hello")), +// 'int': 42, +// 'float': 33.3333, +// 'regexp': /regexp/, +// 'boolean': true, +// 'long': Long.fromNumber(100), +// 'where': new Code('this.a > i', {i:1}), +// 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), +// 'minkey': new MinKey(), +// 'maxkey': new MaxKey() +// } +// +// // Let's serialize it +// var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, true, false); +// // Build a typed array +// var arr = new Uint8Array(new ArrayBuffer(data.length)); +// // Iterate over all the fields and copy +// for(var i = 0; i < data.length; i++) { +// arr[i] = data[i] +// } +// +// // Deserialize the object +// var object = BSONDE.BSON.deserialize(arr); +// // Asserts +// test.equal(motherOfAllDocuments.string, object.string); +// test.deepEqual(motherOfAllDocuments.array, object.array); +// test.deepEqual(motherOfAllDocuments.date, object.date); +// test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); +// test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); +// // Assert the values of the binary +// for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { +// test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); +// } +// test.deepEqual(motherOfAllDocuments.int, object.int); +// test.deepEqual(motherOfAllDocuments.float, object.float); +// test.deepEqual(motherOfAllDocuments.regexp, object.regexp); +// test.deepEqual(motherOfAllDocuments.boolean, object.boolean); +// test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); +// test.deepEqual(motherOfAllDocuments.where, object.where); +// test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); +// test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); +// test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); +// test.deepEqual(motherOfAllDocuments.minkey, object.minkey); +// test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); +// test.done(); +// } + +/** + * @ignore + */ +exports.shouldCorrectlySerializeUsingTypedArray = function(test) { + var motherOfAllDocuments = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, false, false); + // And deserialize it again + var object = BSONSE.BSON.deserialize(data); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js new file mode 100644 index 0000000..80805a5 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js @@ -0,0 +1,482 @@ +var sys = require('util'), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + BSON = require('../../ext').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../lib/bson/bson').BSON, + BinaryParser = require('../../lib/bson/binary_parser').BinaryParser, + Long = require('../../lib/bson/long').Long, + ObjectID = require('../../lib/bson/bson').ObjectID, + Binary = require('../../lib/bson/bson').Binary, + Code = require('../../lib/bson/bson').Code, + DBRef = require('../../lib/bson/bson').DBRef, + Symbol = require('../../lib/bson/bson').Symbol, + Double = require('../../lib/bson/bson').Double, + MaxKey = require('../../lib/bson/bson').MaxKey, + MinKey = require('../../lib/bson/bson').MinKey, + Timestamp = require('../../lib/bson/bson').Timestamp, + gleak = require('../../tools/gleak'), + assert = require('assert'); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize simple edge value'] = function(test) { + // Simple serialization and deserialization of edge value + var doc = {doc:0x1ffffffffffffe}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-0x1ffffffffffffe}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly execute toJSON'] = function(test) { + var a = Long.fromNumber(10); + assert.equal(10, a); + + var a = Long.fromNumber(9223372036854775807); + assert.equal(9223372036854775807, a); + + // Simple serialization and deserialization test for a Single String value + var doc = {doc:'Serialize'}; + var simple_string_serialized = bsonC.serialize(doc, true, false); + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize nested document'] = function(test) { + // Nested doc + var doc = {a:{b:{c:1}}}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple integer serialization/deserialization test, including testing boundary conditions'] = function(test) { + var doc = {doc:-1}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:2147483648}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-2147483648}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization test for a Long value'] = function(test) { + var doc = {doc:Long.fromNumber(9223372036854775807)}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(9223372036854775807)}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:Long.fromNumber(-9223372036854775807)}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(-9223372036854775807)}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Float value'] = function(test) { + var doc = {doc:2222.3333}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-2222.3333}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a null value'] = function(test) { + var doc = {doc:null}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a boolean value'] = function(test) { + var doc = {doc:true}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a date value'] = function(test) { + var date = new Date(); + var doc = {doc:date}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a boolean value'] = function(test) { + var doc = {doc:/abcd/mi}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + + var doc = {doc:/abcd/}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a objectId value'] = function(test) { + var doc = {doc:new ObjectID()}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var doc2 = {doc:ObjectID.createFromHexString(doc.doc.toHexString())}; + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc2, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Binary value'] = function(test) { + var binary = new Binary(); + var string = 'binstring' + for(var index = 0; index < string.length; index++) { binary.put(string.charAt(index)); } + + var simple_string_serialized = bsonC.serialize({doc:binary}, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:binary}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.value(), bsonC.deserialize(simple_string_serialized).doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Code value'] = function(test) { + var code = new Code('this.a > i', {'i': 1}); + var simple_string_serialized_2 = bsonJS.serialize({doc:code}, false, true); + var simple_string_serialized = bsonC.serialize({doc:code}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc.scope, bsonC.deserialize(simple_string_serialized).doc.scope); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for an Object'] = function(test) { + var simple_string_serialized = bsonC.serialize({doc:{a:1, b:{c:2}}}, false, true); + var simple_string_serialized_2 = bsonJS.serialize({doc:{a:1, b:{c:2}}}, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + + // Simple serialization and deserialization for an Array + var simple_string_serialized = bsonC.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); + var simple_string_serialized_2 = bsonJS.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a DBRef'] = function(test) { + var oid = new ObjectID() + var oid2 = new ObjectID.createFromHexString(oid.toHexString()) + var simple_string_serialized = bsonJS.serialize({doc:new DBRef('namespace', oid2, 'integration_tests_')}, false, true); + var simple_string_serialized_2 = bsonC.serialize({doc:new DBRef('namespace', oid, 'integration_tests_')}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + // Ensure we have the same values for the dbref + var object_js = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); + var object_c = bsonC.deserialize(simple_string_serialized); + + assert.equal(object_js.doc.namespace, object_c.doc.namespace); + assert.equal(object_js.doc.oid.toHexString(), object_c.doc.oid.toHexString()); + assert.equal(object_js.doc.db, object_c.doc.db); + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly deserialize bytes array'] = function(test) { + // Serialized document + var bytes = [47,0,0,0,2,110,97,109,101,0,6,0,0,0,80,97,116,116,121,0,16,97,103,101,0,34,0,0,0,7,95,105,100,0,76,100,12,23,11,30,39,8,89,0,0,1,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + var object = bsonC.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal('Patty', object.name) + assert.equal(34, object.age) + assert.equal('4c640c170b1e270859000001', object._id.toHexString()) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize utf8'] = function(test) { + var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var simple_string_serialized2 = bsonJS.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized2) + + var object = bsonC.deserialize(simple_string_serialized); + assert.equal(doc.name, object.name) + assert.equal(doc.name1, object.name1) + assert.equal(doc.name2, object.name2) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize object with array'] = function(test) { + var doc = {b:[1, 2, 3]}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var simple_string_serialized_2 = bsonJS.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + + var object = bsonC.deserialize(simple_string_serialized); + assert.deepEqual(doc, object) + test.done(); +} + +/** + * @ignore + */ +exports['Test equality of an object ID'] = function(test) { + var object_id = new ObjectID(); + var object_id_2 = new ObjectID(); + assert.ok(object_id.equals(object_id)); + assert.ok(!(object_id.equals(object_id_2))) + test.done(); +} + +/** + * @ignore + */ +exports['Test same serialization for Object ID'] = function(test) { + var object_id = new ObjectID(); + var object_id2 = ObjectID.createFromHexString(object_id.toString()) + var simple_string_serialized = bsonJS.serialize({doc:object_id}, false, true); + var simple_string_serialized_2 = bsonC.serialize({doc:object_id2}, false, true); + + assert.equal(simple_string_serialized_2.length, simple_string_serialized.length); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + var object = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); + var object2 = bsonC.deserialize(simple_string_serialized); + assert.equal(object.doc.id, object2.doc.id) + test.done(); +} + +/** + * @ignore + */ +exports['Complex object serialization'] = function(test) { + // JS Object + var c1 = { _id: new ObjectID, comments: [], title: 'number 1' }; + var c2 = { _id: new ObjectID, comments: [], title: 'number 2' }; + var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: new ObjectID + }; + + var simple_string_serialized = bsonJS.serialize(doc, false, true); + + // C++ Object + var c1 = { _id: ObjectID.createFromHexString(c1._id.toHexString()), comments: [], title: 'number 1' }; + var c2 = { _id: ObjectID.createFromHexString(c2._id.toHexString()), comments: [], title: 'number 2' }; + var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: ObjectID.createFromHexString(doc._id.toHexString()) + }; + + var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + + for(var i = 0; i < simple_string_serialized_2.length; i++) { + // debug(i + "[" + simple_string_serialized_2[i] + "] = [" + simple_string_serialized[i] + "]") + assert.equal(simple_string_serialized_2[i], simple_string_serialized[i]); + } + + var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); + var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); + assert.equal(doc._id.id, doc1._id.id) + assert.equal(doc._id.id, doc2._id.id) + assert.equal(doc1._id.id, doc2._id.id) + + var doc = { + _id: 'testid', + key1: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 }, + key2: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 } + }; + + var simple_string_serialized = bsonJS.serialize(doc, false, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + test.done(); +} + +/** + * @ignore + */ +exports['Serialize function'] = function(test) { + var doc = { + _id: 'testid', + key1: function() {} + } + + var simple_string_serialized = bsonJS.serialize(doc, false, true, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + + // Deserialize the string + var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); + var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); + assert.equal(doc1.key1.code.toString(), doc2.key1.code.toString()) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize document with special operators'] = function(test) { + var doc = {"user_id":"4e9fc8d55883d90100000003","lc_status":{"$ne":"deleted"},"owner_rating":{"$exists":false}}; + var simple_string_serialized = bsonJS.serialize(doc, false, true, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + + // Should serialize to the same value + assert.equal(simple_string_serialized_2.toString('base64'), simple_string_serialized.toString('base64')) + var doc1 = bsonJS.deserialize(simple_string_serialized_2); + var doc2 = bsonC.deserialize(simple_string_serialized); + assert.deepEqual(doc1, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Create ObjectID from hex string'] = function(test) { + // Hex Id + var hexId = new ObjectID().toString(); + var docJS = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; + var docC = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; + var docJSBin = bsonJS.serialize(docJS, false, true, true); + var docCBin = bsonC.serialize(docC, false, true, true); + assert.equal(docCBin.toString('base64'), docJSBin.toString('base64')); + test.done(); +} + +/** + * @ignore + */ +exports['Serialize big complex document'] = function(test) { + // Complex document serialization + var doc = {"DateTime": "Tue Nov 40 2011 17:27:55 GMT+0000 (WEST)","isActive": true,"Media": {"URL": "http://videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb"},"Title": "Lisboa fecha a ganhar 0.19%","SetPosition": 60,"Type": "videos","Thumbnail": [{"URL": "http://rd3.videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb/pic/320x240","Dimensions": {"Height": 240,"Width": 320}}],"Source": {"URL": "http://videos.sapo.pt","SetID": "1288","SourceID": "http://videos.sapo.pt/tvnet/rss2","SetURL": "http://noticias.sapo.pt/videos/tv-net_1288/","ItemID": "Tc85NsjaKjj8o5aV7Ubb","Name": "SAPO Vídeos"},"Category": "Tec_ciencia","Description": "Lisboa fecha a ganhar 0.19%","GalleryID": new ObjectID("4eea2a634ce8573200000000"),"InternalRefs": {"RegisterDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","ChangeDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","Hash": 332279244514},"_id": new ObjectID("4eea2a96e52778160000003a")} + var docJSBin = bsonJS.serialize(doc, false, true, true); + var docCBin = bsonC.serialize(doc, false, true, true); + assert.equal(docCBin.toString('base64'), docJSBin.toString('base64')); + test.done(); +} + +/** + * @ignore + */ +exports['Should error out due to 24 characters but not valid hexstring for ObjectID'] = function(test) { + try { + var oid = new ObjectID("tttttttttttttttttttttttt"); + test.ok(false); + } catch(err) {} + + test.done(); +} + + +/** + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_test.js new file mode 100644 index 0000000..a2a723b --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_test.js @@ -0,0 +1,1671 @@ +var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/bson').native() : require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + ObjectId = mongoO.ObjectId, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + vm = require('vm'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly create ObjectID and do deep equals'] = function(test) { + var test_string = {hello: new ObjectID()}; + test_string.hello.toHexString(); + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly get BSON types from require'] = function(test) { + var _mongodb = require('../../lib/bson'); + test.ok(_mongodb.ObjectID === ObjectID); + test.ok(_mongodb.Binary === Binary); + test.ok(_mongodb.Long === Long); + test.ok(_mongodb.Timestamp === Timestamp); + test.ok(_mongodb.Code === Code); + test.ok(_mongodb.DBRef === DBRef); + test.ok(_mongodb.Symbol === Symbol); + test.ok(_mongodb.MinKey === MinKey); + test.ok(_mongodb.MaxKey === MaxKey); + test.ok(_mongodb.Double === Double); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object'] = function(test) { + var bytes = [95,0,0,0,2,110,115,0,42,0,0,0,105,110,116,101,103,114,97,116,105,111,110,95,116,101,115,116,115,95,46,116,101,115,116,95,105,110,100,101,120,95,105,110,102,111,114,109,97,116,105,111,110,0,8,117,110,105,113,117,101,0,0,3,107,101,121,0,12,0,0,0,16,97,0,1,0,0,0,0,2,110,97,109,101,0,4,0,0,0,97,95,49,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(new Buffer(serialized_data, 'binary')); + test.equal("a_1", object.name); + test.equal(false, object.unique); + test.equal(1, object.key.a); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object with all types'] = function(test) { + var bytes = [26,1,0,0,7,95,105,100,0,161,190,98,75,118,169,3,0,0,3,0,0,4,97,114,114,97,121,0,26,0,0,0,16,48,0,1,0,0,0,16,49,0,2,0,0,0,16,50,0,3,0,0,0,0,2,115,116,114,105,110,103,0,6,0,0,0,104,101,108,108,111,0,3,104,97,115,104,0,19,0,0,0,16,97,0,1,0,0,0,16,98,0,2,0,0,0,0,9,100,97,116,101,0,161,190,98,75,0,0,0,0,7,111,105,100,0,161,190,98,75,90,217,18,0,0,1,0,0,5,98,105,110,97,114,121,0,7,0,0,0,2,3,0,0,0,49,50,51,16,105,110,116,0,42,0,0,0,1,102,108,111,97,116,0,223,224,11,147,169,170,64,64,11,114,101,103,101,120,112,0,102,111,111,98,97,114,0,105,0,8,98,111,111,108,101,97,110,0,1,15,119,104,101,114,101,0,25,0,0,0,12,0,0,0,116,104,105,115,46,120,32,61,61,32,51,0,5,0,0,0,0,3,100,98,114,101,102,0,37,0,0,0,2,36,114,101,102,0,5,0,0,0,116,101,115,116,0,7,36,105,100,0,161,190,98,75,2,180,1,0,0,2,0,0,0,10,110,117,108,108,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(new Buffer(serialized_data, 'binary'));//, false, true); + // Perform tests + test.equal("hello", object.string); + test.deepEqual([1,2,3], object.array); + test.equal(1, object.hash.a); + test.equal(2, object.hash.b); + test.ok(object.date != null); + test.ok(object.oid != null); + test.ok(object.binary != null); + test.equal(42, object.int); + test.equal(33.3333, object.float); + test.ok(object.regexp != null); + test.equal(true, object.boolean); + test.ok(object.where != null); + test.ok(object.dbref != null); + test.ok(object[null] == null); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize String'] = function(test) { + var test_string = {hello: 'world'}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_string)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_string, false, serialized_data2, 0); + + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize Empty String'] = function(test) { + var test_string = {hello: ''}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_string)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_string, false, serialized_data2, 0); + + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_number = {doc: 5}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_number, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_number)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_number, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data2)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize null value'] = function(test) { + var test_null = {doc:null}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_null, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_null)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_null, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(null, object.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Number'] = function(test) { + var test_number = {doc: 5.5}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_number, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_number)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_number, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_int = {doc: 42}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: -5600}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: 2147483647}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: -2147483648}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Object'] = function(test) { + var doc = {doc: {age: 42, name: 'Spongebob', shoe_size: 9.5}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc.doc.age, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.age); + test.deepEqual(doc.doc.name, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.name); + test.deepEqual(doc.doc.shoe_size, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.shoe_size); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc.doc[0], deserialized.doc[0]) + test.equal(doc.doc[1], deserialized.doc[1]) + test.equal(doc.doc[2], deserialized.doc[2]) + test.equal(doc.doc[3], deserialized.doc[3]) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array with added on functions'] = function(test) { + Array.prototype.toXml = function() {}; + var doc = {doc: [1, 2, 'a', 'b']}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc.doc[0], deserialized.doc[0]) + test.equal(doc.doc[1], deserialized.doc[1]) + test.equal(doc.doc[2], deserialized.doc[2]) + test.equal(doc.doc[3], deserialized.doc[3]) + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly deserialize a nested object'] = function(test) { + var doc = {doc: {doc:1}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc.doc.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize A Boolean'] = function(test) { + var doc = {doc: true}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date'] = function(test) { + var date = new Date(); + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12); + date.setUTCFullYear(2009); + date.setUTCMonth(11 - 1); + date.setUTCHours(12); + date.setUTCMinutes(0); + date.setUTCSeconds(30); + var doc = {doc: date}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.date, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.date); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date from another VM'] = function(test) { + var script = "date1 = new Date();", + ctx = vm.createContext({ + date1 : null + }); + vm.runInContext(script, ctx, 'myfile.vm'); + + var date = ctx.date1; + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12); + date.setUTCFullYear(2009); + date.setUTCMonth(11 - 1); + date.setUTCHours(12); + date.setUTCMinutes(0); + date.setUTCSeconds(30); + var doc = {doc: date}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.date, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.date); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize nested doc'] = function(test) { + var doc = { + string: "Strings are great", + decimal: 3.14159265, + bool: true, + integer: 5, + + subObject: { + moreText: "Bacon ipsum dolor.", + longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly." + }, + + subArray: [1,2,3,4,5,6,7,8,9,10], + anotherString: "another string" + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Oid'] = function(test) { + var doc = {doc: new ObjectID()}; + var doc2 = {doc: ObjectID.createFromHexString(doc.doc.toHexString())}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly encode Empty Hash'] = function(test) { + var doc = {}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Ordered Hash'] = function(test) { + var doc = {doc: {b:1, a:2, c:3, d:4}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var decoded_hash = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc; + var keys = []; + + for(var name in decoded_hash) keys.push(name); + test.deepEqual(['b', 'a', 'c', 'd'], keys); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Regular Expression'] = function(test) { + // Serialize the regular expression + var doc = {doc: /foobar/mi}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc.doc.toString(), doc2.doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Binary object'] = function(test) { + var bin = new Binary(); + var string = 'binstring'; + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)); + } + + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a big Binary object'] = function(test) { + var data = fs.readFileSync("test/node/data/test_gs_weird_bug.png", 'binary'); + var bin = new Binary(); + bin.write(data); + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports["Should Correctly Serialize and Deserialize DBRef"] = function(test) { + var oid = new ObjectID(); + var doc = {dbref: new DBRef('namespace', oid, null)}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal("namespace", doc2.dbref.namespace); + test.deepEqual(doc2.dbref.oid.toHexString(), oid.toHexString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize partial DBRef'] = function(test) { + var id = new ObjectID(); + var doc = {'name':'something', 'user':{'$ref':'username', '$id': id}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal('something', doc2.name); + test.equal('username', doc2.user.namespace); + test.equal(id.toString(), doc2.user.oid.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize simple Int'] = function(test) { + var doc = {doc:2147483648}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, doc2.doc) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Long Integer'] = function(test) { + var doc = {doc: Long.fromNumber(9223372036854775807)}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + + doc = {doc: Long.fromNumber(-9223372036854775)}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + + doc = {doc: Long.fromNumber(-9223372036854775809)}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Deserialize Large Integers as Number not Long'] = function(test) { + function roundTrip(val) { + var doc = {doc: val}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + }; + + roundTrip(Math.pow(2,52)); + roundTrip(Math.pow(2,53) - 1); + roundTrip(Math.pow(2,53)); + roundTrip(-Math.pow(2,52)); + roundTrip(-Math.pow(2,53) + 1); + roundTrip(-Math.pow(2,53)); + roundTrip(Math.pow(2,65)); // Too big for Long. + roundTrip(-Math.pow(2,65)); + roundTrip(9223372036854775807); + roundTrip(1234567890123456800); // Bigger than 2^53, stays a double. + roundTrip(-1234567890123456800); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Long Integer and Timestamp as different types'] = function(test) { + var long = Long.fromNumber(9223372036854775807); + var timestamp = Timestamp.fromNumber(9223372036854775807); + test.ok(long instanceof Long); + test.ok(!(long instanceof Timestamp)); + test.ok(timestamp instanceof Timestamp); + test.ok(!(timestamp instanceof Long)); + + var test_int = {doc: long, doc2: timestamp}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(test_int.doc, deserialized_data.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Always put the id as the first item in a hash'] = function(test) { + var hash = {doc: {not_id:1, '_id':2}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(hash, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(hash)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(hash, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + var keys = []; + + for(var name in deserialized_data.doc) { + keys.push(name); + } + + test.deepEqual(['not_id', '_id'], keys); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a User defined Binary object'] = function(test) { + var bin = new Binary(); + bin.sub_type = BSON.BSON_BINARY_SUBTYPE_USER_DEFINED; + var string = 'binstring'; + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)); + } + + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(deserialized_data.doc.sub_type, BSON.BSON_BINARY_SUBTYPE_USER_DEFINED); + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correclty Serialize and Deserialize a Code object'] = function(test) { + var doc = {'doc': {'doc2': new Code('this.a > i', {i:1})}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc.doc2.code, deserialized_data.doc.doc2.code); + test.deepEqual(doc.doc.doc2.scope.i, deserialized_data.doc.doc2.scope.i); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly serialize and deserialize and embedded array'] = function(test) { + var doc = {'a':0, + 'b':['tmp1', 'tmp2', 'tmp3', 'tmp4', 'tmp5', 'tmp6', 'tmp7', 'tmp8', 'tmp9', 'tmp10', 'tmp11', 'tmp12', 'tmp13', 'tmp14', 'tmp15', 'tmp16'] + }; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.a, deserialized_data.a); + test.deepEqual(doc.b, deserialized_data.b); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize UTF8'] = function(test) { + // Serialize utf8 + var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize query object'] = function(test) { + var doc = { count: 'remove_with_no_callback_bug_test', query: {}, fields: null}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize empty query object'] = function(test) { + var doc = {}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize array based doc'] = function(test) { + var doc = { b: [ 1, 2, 3 ], _id: new ObjectID() }; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.b, deserialized_data.b) + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Symbol'] = function(test) { + if(Symbol != null) { + var doc = { b: [ new Symbol('test') ]}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.b, deserialized_data.b) + test.deepEqual(doc, deserialized_data); + test.ok(deserialized_data.b[0] instanceof Symbol); + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should handle Deeply nested document'] = function(test) { + var doc = {a:{b:{c:{d:2}}}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should handle complicated all typed object'] = function(test) { + // First doc + var date = new Date(); + var oid = new ObjectID(); + var string = 'binstring' + var bin = new Binary() + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + + var doc = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': date, + 'oid': oid, + 'binary': bin, + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': date.getTime(), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', oid, 'integration_tests_') + } + + // Second doc + var oid = new ObjectID.createFromHexString(oid.toHexString()); + var string = 'binstring' + var bin = new Binary() + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + + var doc2 = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': date, + 'oid': oid, + 'binary': bin, + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': date.getTime(), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', oid, 'integration_tests_') + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize Complex Nested Object'] = function(test) { + var doc = { email: 'email@email.com', + encrypted_password: 'password', + friends: [ '4db96b973d01205364000006', + '4dc77b24c5ba38be14000002' ], + location: [ 72.4930088, 23.0431957 ], + name: 'Amit Kumar', + password_salt: 'salty', + profile_fields: [], + username: 'amit', + _id: new ObjectID() } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = doc; + doc2._id = ObjectID.createFromHexString(doc2._id.toHexString()); + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly massive doc'] = function(test) { + var oid1 = new ObjectID(); + var oid2 = new ObjectID(); + + // JS doc + var doc = { dbref2: new DBRef('namespace', oid1, 'integration_tests_'), + _id: oid2 }; + + var doc2 = { dbref2: new DBRef('namespace', ObjectID.createFromHexString(oid1.toHexString()), 'integration_tests_'), + _id: new ObjectID.createFromHexString(oid2.toHexString()) }; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize regexp object'] = function(test) { + var doc = {'b':/foobaré/}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize complicated object'] = function(test) { + var doc = {a:{b:{c:[new ObjectID(), new ObjectID()]}}, d:{f:1332.3323}}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize nested object'] = function(test) { + var doc = { "_id" : { "date" : new Date(), "gid" : "6f35f74d2bea814e21000000" }, + "value" : { + "b" : { "countries" : { "--" : 386 }, "total" : 1599 }, + "bc" : { "countries" : { "--" : 3 }, "total" : 10 }, + "gp" : { "countries" : { "--" : 2 }, "total" : 13 }, + "mgc" : { "countries" : { "--" : 2 }, "total" : 14 } + } + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize nested object with even more nesting'] = function(test) { + var doc = { "_id" : { "date" : {a:1, b:2, c:new Date()}, "gid" : "6f35f74d2bea814e21000000" }, + "value" : { + "b" : { "countries" : { "--" : 386 }, "total" : 1599 }, + "bc" : { "countries" : { "--" : 3 }, "total" : 10 }, + "gp" : { "countries" : { "--" : 2 }, "total" : 13 }, + "mgc" : { "countries" : { "--" : 2 }, "total" : 14 } + } + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize empty name object'] = function(test) { + var doc = {'':'test', + 'bbbb':1}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc2[''], 'test'); + test.equal(doc2['bbbb'], 1); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly handle Forced Doubles to ensure we allocate enough space for cap collections'] = function(test) { + if(Double != null) { + var doubleValue = new Double(100); + var doc = {value:doubleValue}; + + // Serialize + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual({value:100}, doc2); + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should deserialize correctly'] = function(test) { + var doc = { + "_id" : new ObjectID("4e886e687ff7ef5e00000162"), + "str" : "foreign", + "type" : 2, + "timestamp" : ISODate("2011-10-02T14:00:08.383Z"), + "links" : [ + "http://www.reddit.com/r/worldnews/comments/kybm0/uk_home_secretary_calls_for_the_scrapping_of_the/" + ] + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly serialize and deserialize MinKey and MaxKey values'] = function(test) { + var doc = { + _id : new ObjectID("4e886e687ff7ef5e00000162"), + minKey : new MinKey(), + maxKey : new MaxKey() + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.ok(doc2.minKey instanceof MinKey); + test.ok(doc2.maxKey instanceof MaxKey); + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly serialize Double value'] = function(test) { + var doc = { + value : new Double(34343.2222) + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.ok(doc.value.valueOf(), doc2.value); + test.ok(doc.value.value, doc2.value); + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should correctly create objects'] = function(test) { + try { + var object1 = ObjectID.createFromHexString('000000000000000000000001') + var object2 = ObjectID.createFromHexString('00000000000000000000001') + test.ok(false); + } catch(err) { + test.ok(err != null); + } + + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should correctly retrieve timestamp'] = function(test) { + var testDate = new Date(); + var object1 = new ObjectID(); + test.equal(Math.floor(testDate.getTime()/1000), Math.floor(object1.getTimestamp().getTime()/1000)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly throw error on bsonparser errors'] = function(test) { + var data = new Buffer(3); + var parser = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + + // Catch to small buffer error + try { + parser.deserialize(data); + test.ok(false); + } catch(err) {} + + data = new Buffer(5); + data[0] = 0xff; + data[1] = 0xff; + // Catch illegal size + try { + parser.deserialize(data); + test.ok(false); + } catch(err) {} + + // Finish up + test.done(); +} + +/** + * A simple example showing the usage of BSON.calculateObjectSize function returning the number of BSON bytes a javascript object needs. + * + * @_class bson + * @_function BSON.calculateObjectSize + * @ignore + */ +exports['Should correctly calculate the size of a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Calculate the size of the object without serializing the function + var size = BSON.calculateObjectSize(doc, false); + test.equal(12, size); + // Calculate the size of the object serializing the function + size = BSON.calculateObjectSize(doc, true); + // Validate the correctness + test.equal(36, size); + test.done(); +} + +/** + * A simple example showing the usage of BSON.calculateObjectSize function returning the number of BSON bytes a javascript object needs. + * + * @_class bson + * @_function calculateObjectSize + * @ignore + */ +exports['Should correctly calculate the size of a given javascript object using instance method'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Calculate the size of the object without serializing the function + var size = bson.calculateObjectSize(doc, false); + test.equal(12, size); + // Calculate the size of the object serializing the function + size = bson.calculateObjectSize(doc, true); + // Validate the correctness + test.equal(36, size); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serializeWithBufferAndIndex function. + * + * @_class bson + * @_function BSON.serializeWithBufferAndIndex + * @ignore + */ +exports['Should correctly serializeWithBufferAndIndex a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Calculate the size of the document, no function serialization + var size = BSON.calculateObjectSize(doc, false); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = BSON.serializeWithBufferAndIndex(doc, true, buffer, 0, false); + // Validate the correctness + test.equal(12, size); + test.equal(11, index); + + // Serialize with functions + // Calculate the size of the document, no function serialization + var size = BSON.calculateObjectSize(doc, true); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = BSON.serializeWithBufferAndIndex(doc, true, buffer, 0, true); + // Validate the correctness + test.equal(36, size); + test.equal(35, index); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serializeWithBufferAndIndex function. + * + * @_class bson + * @_function serializeWithBufferAndIndex + * @ignore + */ +exports['Should correctly serializeWithBufferAndIndex a given javascript object using a BSON instance'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Calculate the size of the document, no function serialization + var size = bson.calculateObjectSize(doc, false); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = bson.serializeWithBufferAndIndex(doc, true, buffer, 0, false); + // Validate the correctness + test.equal(12, size); + test.equal(11, index); + + // Serialize with functions + // Calculate the size of the document, no function serialization + var size = bson.calculateObjectSize(doc, true); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = bson.serializeWithBufferAndIndex(doc, true, buffer, 0, true); + // Validate the correctness + test.equal(36, size); + test.equal(35, index); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serialize function returning serialized BSON Buffer object. + * + * @_class bson + * @_function BSON.serialize + * @ignore + */ +exports['Should correctly serialize a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Serialize the object to a buffer, checking keys and not serializing functions + var buffer = BSON.serialize(doc, true, true, false); + // Validate the correctness + test.equal(12, buffer.length); + + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(36, buffer.length); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serialize function returning serialized BSON Buffer object. + * + * @_class bson + * @_function serialize + * @ignore + */ +exports['Should correctly serialize a given javascript object using a bson instance'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and not serializing functions + var buffer = bson.serialize(doc, true, true, false); + // Validate the correctness + test.equal(12, buffer.length); + + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(36, buffer.length); + test.done(); +} + +/** + * A simple example showing the usage of BSON.deserialize function returning a deserialized Javascript function. + * + * @_class bson + * @_function BSON.deserialize + * @ignore + */ + exports['Should correctly deserialize a buffer using the BSON class level parser'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // Deserialize the object with no eval for the functions + var deserializedDoc = BSON.deserialize(buffer); + // Validate the correctness + test.equal('object', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + + // Deserialize the object with eval for the functions caching the functions + deserializedDoc = BSON.deserialize(buffer, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal('function', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + test.done(); +} + +/** + * A simple example showing the usage of BSON instance deserialize function returning a deserialized Javascript function. + * + * @_class bson + * @_function deserialize + * @ignore + */ +exports['Should correctly deserialize a buffer using the BSON instance parser'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // Deserialize the object with no eval for the functions + var deserializedDoc = bson.deserialize(buffer); + // Validate the correctness + test.equal('object', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + + // Deserialize the object with eval for the functions caching the functions + deserializedDoc = bson.deserialize(buffer, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal('function', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + test.done(); +} + +/** + * A simple example showing the usage of BSON.deserializeStream function returning deserialized Javascript objects. + * + * @_class bson + * @_function BSON.deserializeStream + * @ignore + */ +exports['Should correctly deserializeStream a buffer object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = BSON.deserializeStream(buffer, 0, 1, documents, 0); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('object', typeof documents[0].func); + + // Deserialize the object with eval for the functions caching the functions + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = BSON.deserializeStream(buffer, 0, 1, documents, 0, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('function', typeof documents[0].func); + test.done(); +} + +/** + * A simple example showing the usage of BSON instance deserializeStream function returning deserialized Javascript objects. + * + * @_class bson + * @_function deserializeStream + * @ignore + */ +exports['Should correctly deserializeStream a buffer object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = bson.deserializeStream(buffer, 0, 1, documents, 0); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('object', typeof documents[0].func); + + // Deserialize the object with eval for the functions caching the functions + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = bson.deserializeStream(buffer, 0, 1, documents, 0, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('function', typeof documents[0].func); + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should have a correct cached representation of the hexString'] = function (test) { + ObjectID.cacheHexString = true; + var a = new ObjectID; + var __id = a.__id; + test.equal(__id, a.toHexString()); + + // hexString + a = new ObjectID(__id); + test.equal(__id, a.toHexString()); + + // fromHexString + a = ObjectID.createFromHexString(__id); + test.equal(a.__id, a.toHexString()); + test.equal(__id, a.toHexString()); + + // number + var genTime = a.generationTime; + a = new ObjectID(genTime); + __id = a.__id; + test.equal(__id, a.toHexString()); + + // generationTime + delete a.__id; + a.generationTime = genTime; + test.equal(__id, a.toHexString()); + + // createFromTime + a = ObjectId.createFromTime(genTime); + __id = a.__id; + test.equal(__id, a.toHexString()); + ObjectId.cacheHexString = false; + + test.done(); +} + +/** + * @ignore + */ +// 'Should Correctly Function' = function(test) { +// var doc = {b:1, func:function() { +// this.b = 2; +// }}; +// +// var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); +// +// debug("----------------------------------------------------------------------") +// debug(inspect(serialized_data)) +// +// // var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); +// // new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); +// // assertBuffersEqual(test, serialized_data, serialized_data2, 0); +// var COUNT = 100000; +// +// // var b = null; +// // eval("b = function(x) { return x+x; }"); +// // var b = new Function("x", "return x+x;"); +// +// console.log(COUNT + "x (objectBSON = BSON.serialize(object))") +// start = new Date +// +// for (i=COUNT; --i>=0; ) { +// var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// } +// +// end = new Date +// console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec") +// +// // debug(inspect(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).functionCache)) +// // +// // var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// // // test.deepEqual(doc, doc2) +// // // +// // debug(inspect(doc2)) +// // doc2.func() +// // debug(inspect(doc2)) +// // +// // var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); +// // var doc3 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// // +// // debug("-----------------------------------------------") +// // debug(inspect(doc3)) +// +// // var key = "0" +// // for(var i = 1; i < 10000; i++) { +// // key = key + " " + i +// // } +// +// test.done(); +// +// +// // var car = { +// // model : "Volvo", +// // country : "Sweden", +// // +// // isSwedish : function() { +// // return this.country == "Sweden"; +// // } +// // } +// +// }, + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js new file mode 100644 index 0000000..cde83f8 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js @@ -0,0 +1,392 @@ +var mongodb = require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + utils = require('./tools/utils'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports.shouldCorrectlyDeserializeUsingTypedArray = function(test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + var motherOfAllDocuments = { + 'string': '客家话', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, true, false); + // Build a typed array + var arr = new Uint8Array(new ArrayBuffer(data.length)); + // Iterate over all the fields and copy + for(var i = 0; i < data.length; i++) { + arr[i] = data[i] + } + + // Deserialize the object + var object = BSONDE.BSON.deserialize(arr); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * @ignore + */ +exports.shouldCorrectlySerializeUsingTypedArray = function(test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + var motherOfAllDocuments = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, false, false); + // And deserialize it again + var object = BSONSE.BSON.deserialize(data); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * @ignore + */ +exports['exercise all the binary object constructor methods'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // String to array + var array = utils.stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + + // Construct using number of chars + binary = new Binary(5); + test.ok(5, binary.buffer.length); + + // Construct using an Array + var binary = new Binary(utils.stringToArray(string)); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + + // Construct using a string + var binary = new Binary(string); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + test.done(); +}; + +/** + * @ignore + */ +exports['exercise the put binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // String to array + var array = utils.stringToArrayBuffer(string + 'a'); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(string.length + 1, binary.position); + test.ok(utils.assertArrayEqual(array, binary.value(true))); + test.equal('hello worlda', binary.value()); + + // Exercise a binary with lots of space in the buffer + var binary = new Binary(); + test.ok(Binary.BUFFER_SIZE, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(1, binary.position); + test.ok(utils.assertArrayEqual(['a'.charCodeAt(0)], binary.value(true))); + test.equal('a', binary.value()); + test.done(); +}, + +/** + * @ignore + */ +exports['exercise the write binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // Array + var writeArrayBuffer = new Uint8Array(new ArrayBuffer(1)); + writeArrayBuffer[0] = 'a'.charCodeAt(0); + var arrayBuffer = ['a'.charCodeAt(0)]; + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a string starting at end of buffer + binary.write('a'); + test.equal('hello worlda', binary.value()); + // Write a string starting at index 0 + binary.write('a', 0); + test.equal('aello worlda', binary.value()); + // Write a arraybuffer starting at end of buffer + binary.write(writeArrayBuffer); + test.equal('aello worldaa', binary.value()); + // Write a arraybuffer starting at position 5 + binary.write(writeArrayBuffer, 5); + test.equal('aelloaworldaa', binary.value()); + // Write a array starting at end of buffer + binary.write(arrayBuffer); + test.equal('aelloaworldaaa', binary.value()); + // Write a array starting at position 6 + binary.write(arrayBuffer, 6); + test.equal('aelloaaorldaaa', binary.value()); + test.done(); +}, + +/** + * @ignore + */ +exports['exercise the read binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + var array = utils.stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Read the first 2 bytes + var data = binary.read(0, 2); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer('he'), data)); + + // Read the entire field + var data = binary.read(0); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer(string), data)); + + // Read 3 bytes + var data = binary.read(6, 5); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer('world'), data)); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png new file mode 100644 index 0000000000000000000000000000000000000000..1554dc3238dc94f4c8375b17b4b8d34a337d9c1b GIT binary patch literal 52184 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 literal 0 HcmV?d00001 diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js new file mode 100644 index 0000000..7a78347 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js @@ -0,0 +1,315 @@ +var sys = require('util'), + fs = require('fs'), + BSON = require('../../ext').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../lib/bson/bson').BSON, + BinaryParser = require('../../lib/bson/binary_parser').BinaryParser, + Long = require('../../lib/bson/long').Long, + ObjectID = require('../../lib/bson/bson').ObjectID, + Binary = require('../../lib/bson/bson').Binary, + Code = require('../../lib/bson/bson').Code, + DBRef = require('../../lib/bson/bson').DBRef, + Symbol = require('../../lib/bson/bson').Symbol, + Double = require('../../lib/bson/bson').Double, + MaxKey = require('../../lib/bson/bson').MaxKey, + MinKey = require('../../lib/bson/bson').MinKey, + Timestamp = require('../../lib/bson/bson').Timestamp, + gleak = require('../../tools/gleak'), + assert = require('assert'); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object'] = function(test) { + var bytes = [95,0,0,0,2,110,115,0,42,0,0,0,105,110,116,101,103,114,97,116,105,111,110,95,116,101,115,116,115,95,46,116,101,115,116,95,105,110,100,101,120,95,105,110,102,111,114,109,97,116,105,111,110,0,8,117,110,105,113,117,101,0,0,3,107,101,121,0,12,0,0,0,16,97,0,1,0,0,0,0,2,110,97,109,101,0,4,0,0,0,97,95,49,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = bsonC.deserialize(serialized_data); + assert.equal("a_1", object.name); + assert.equal(false, object.unique); + assert.equal(1, object.key.a); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object with all types'] = function(test) { + var bytes = [26,1,0,0,7,95,105,100,0,161,190,98,75,118,169,3,0,0,3,0,0,4,97,114,114,97,121,0,26,0,0,0,16,48,0,1,0,0,0,16,49,0,2,0,0,0,16,50,0,3,0,0,0,0,2,115,116,114,105,110,103,0,6,0,0,0,104,101,108,108,111,0,3,104,97,115,104,0,19,0,0,0,16,97,0,1,0,0,0,16,98,0,2,0,0,0,0,9,100,97,116,101,0,161,190,98,75,0,0,0,0,7,111,105,100,0,161,190,98,75,90,217,18,0,0,1,0,0,5,98,105,110,97,114,121,0,7,0,0,0,2,3,0,0,0,49,50,51,16,105,110,116,0,42,0,0,0,1,102,108,111,97,116,0,223,224,11,147,169,170,64,64,11,114,101,103,101,120,112,0,102,111,111,98,97,114,0,105,0,8,98,111,111,108,101,97,110,0,1,15,119,104,101,114,101,0,25,0,0,0,12,0,0,0,116,104,105,115,46,120,32,61,61,32,51,0,5,0,0,0,0,3,100,98,114,101,102,0,37,0,0,0,2,36,114,101,102,0,5,0,0,0,116,101,115,116,0,7,36,105,100,0,161,190,98,75,2,180,1,0,0,2,0,0,0,10,110,117,108,108,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal("hello", object.string); + assert.deepEqual([1, 2, 3], object.array); + assert.equal(1, object.hash.a); + assert.equal(2, object.hash.b); + assert.ok(object.date != null); + assert.ok(object.oid != null); + assert.ok(object.binary != null); + assert.equal(42, object.int); + assert.equal(33.3333, object.float); + assert.ok(object.regexp != null); + assert.equal(true, object.boolean); + assert.ok(object.where != null); + assert.ok(object.dbref != null); + assert.ok(object['null'] == null); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize String'] = function(test) { + var test_string = {hello: 'world'} + var serialized_data = bsonC.serialize(test_string) + assert.deepEqual(test_string, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_number = {doc: 5} + var serialized_data = bsonC.serialize(test_number) + assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize null value'] = function(test) { + var test_null = {doc:null} + var serialized_data = bsonC.serialize(test_null) + var object = bsonC.deserialize(serialized_data); + assert.deepEqual(test_null, object); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize undefined value'] = function(test) { + var test_undefined = {doc:undefined} + var serialized_data = bsonC.serialize(test_undefined) + var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal(null, object.doc) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Number'] = function(test) { + var test_number = {doc: 5.5} + var serialized_data = bsonC.serialize(test_number) + assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_int = {doc: 42} + var serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: -5600} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: 2147483647} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: -2147483648} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Object'] = function(test) { + var doc = {doc: {age: 42, name: 'Spongebob', shoe_size: 9.5}} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array with added on functions'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize A Boolean'] = function(test) { + var doc = {doc: true} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date'] = function(test) { + var date = new Date() + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12) + date.setUTCFullYear(2009) + date.setUTCMonth(11 - 1) + date.setUTCHours(12) + date.setUTCMinutes(0) + date.setUTCSeconds(30) + var doc = {doc: date} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Oid'] = function(test) { + var doc = {doc: new ObjectID()} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc.doc.toHexString(), bsonC.deserialize(serialized_data).doc.toHexString()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Buffer'] = function(test) { + var doc = {doc: new Buffer("123451234512345")} + var serialized_data = bsonC.serialize(doc) + + assert.equal("123451234512345", bsonC.deserialize(serialized_data).doc.buffer.toString('ascii')); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly encode Empty Hash'] = function(test) { + var test_code = {} + var serialized_data = bsonC.serialize(test_code) + assert.deepEqual(test_code, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Ordered Hash'] = function(test) { + var doc = {doc: {b:1, a:2, c:3, d:4}} + var serialized_data = bsonC.serialize(doc) + var decoded_hash = bsonC.deserialize(serialized_data).doc + var keys = [] + for(var name in decoded_hash) keys.push(name) + assert.deepEqual(['b', 'a', 'c', 'd'], keys) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Regular Expression'] = function(test) { + var doc = {doc: /foobar/mi} + var serialized_data = bsonC.serialize(doc) + var doc2 = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.toString(), doc2.doc.toString()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Binary object'] = function(test) { + var bin = new Binary() + var string = 'binstring' + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + var doc = {doc: bin} + var serialized_data = bsonC.serialize(doc) + var deserialized_data = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.value(), deserialized_data.doc.value()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a big Binary object'] = function(test) { + var data = fs.readFileSync("test/node/data/test_gs_weird_bug.png", 'binary'); + var bin = new Binary() + bin.write(data) + var doc = {doc: bin} + var serialized_data = bsonC.serialize(doc) + var deserialized_data = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.value(), deserialized_data.doc.value()) + test.done(); +} + +/** + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js new file mode 100644 index 0000000..e9282e5 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js @@ -0,0 +1,109 @@ +var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/bson').native() : require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser; + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should correctly handle toBson function for an object'] = function(test) { + // Test object + var doc = { + hello: new ObjectID(), + a:1 + }; + // Add a toBson method to the object + doc.toBSON = function() { + return {b:1}; + } + + // Serialize the data + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var deserialized_doc = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual({b:1}, deserialized_doc); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js new file mode 100644 index 0000000..9d7cbe7 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js @@ -0,0 +1,80 @@ +exports.assertArrayEqual = function(array1, array2) { + if(array1.length != array2.length) return false; + for(var i = 0; i < array1.length; i++) { + if(array1[i] != array2[i]) return false; + } + + return true; +} + +// String to arraybuffer +exports.stringToArrayBuffer = function(string) { + var dataBuffer = new Uint8Array(new ArrayBuffer(string.length)); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +// String to arraybuffer +exports.stringToArray = function(string) { + var dataBuffer = new Array(string.length); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +exports.Utf8 = { + // public method for url encoding + encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // public method for url decoding + decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + c = utftext.charCodeAt(i); + if(c < 128) { + string += String.fromCharCode(c); + i++; + } else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/gleak.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/gleak.js new file mode 100644 index 0000000..c707cfc --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/gleak.js @@ -0,0 +1,21 @@ + +var gleak = require('gleak')(); +gleak.ignore('AssertionError'); +gleak.ignore('testFullSpec_param_found'); +gleak.ignore('events'); +gleak.ignore('Uint8Array'); +gleak.ignore('Uint8ClampedArray'); +gleak.ignore('TAP_Global_Harness'); +gleak.ignore('setImmediate'); +gleak.ignore('clearImmediate'); + +gleak.ignore('DTRACE_NET_SERVER_CONNECTION'); +gleak.ignore('DTRACE_NET_STREAM_END'); +gleak.ignore('DTRACE_NET_SOCKET_READ'); +gleak.ignore('DTRACE_NET_SOCKET_WRITE'); +gleak.ignore('DTRACE_HTTP_SERVER_REQUEST'); +gleak.ignore('DTRACE_HTTP_SERVER_RESPONSE'); +gleak.ignore('DTRACE_HTTP_CLIENT_REQUEST'); +gleak.ignore('DTRACE_HTTP_CLIENT_RESPONSE'); + +module.exports = gleak; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE new file mode 100644 index 0000000..7c435ba --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2011 Pivotal Labs + +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. diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js new file mode 100644 index 0000000..7383401 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js @@ -0,0 +1,190 @@ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css new file mode 100644 index 0000000..6583fe7 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css @@ -0,0 +1,166 @@ +body { + font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; +} + + +.jasmine_reporter a:visited, .jasmine_reporter a { + color: #303; +} + +.jasmine_reporter a:hover, .jasmine_reporter a:active { + color: blue; +} + +.run_spec { + float:right; + padding-right: 5px; + font-size: .8em; + text-decoration: none; +} + +.jasmine_reporter { + margin: 0 5px; +} + +.banner { + color: #303; + background-color: #fef; + padding: 5px; +} + +.logo { + float: left; + font-size: 1.1em; + padding-left: 5px; +} + +.logo .version { + font-size: .6em; + padding-left: 1em; +} + +.runner.running { + background-color: yellow; +} + + +.options { + text-align: right; + font-size: .8em; +} + + + + +.suite { + border: 1px outset gray; + margin: 5px 0; + padding-left: 1em; +} + +.suite .suite { + margin: 5px; +} + +.suite.passed { + background-color: #dfd; +} + +.suite.failed { + background-color: #fdd; +} + +.spec { + margin: 5px; + padding-left: 1em; + clear: both; +} + +.spec.failed, .spec.passed, .spec.skipped { + padding-bottom: 5px; + border: 1px solid gray; +} + +.spec.failed { + background-color: #fbb; + border-color: red; +} + +.spec.passed { + background-color: #bfb; + border-color: green; +} + +.spec.skipped { + background-color: #bbb; +} + +.messages { + border-left: 1px dashed gray; + padding-left: 1em; + padding-right: 1em; +} + +.passed { + background-color: #cfc; + display: none; +} + +.failed { + background-color: #fbb; +} + +.skipped { + color: #777; + background-color: #eee; + display: none; +} + + +/*.resultMessage {*/ + /*white-space: pre;*/ +/*}*/ + +.resultMessage span.result { + display: block; + line-height: 2em; + color: black; +} + +.resultMessage .mismatch { + color: black; +} + +.stackTrace { + white-space: pre; + font-size: .8em; + margin-left: 10px; + max-height: 5em; + overflow: auto; + border: 1px inset red; + padding: 1em; + background: #eef; +} + +.finished-at { + padding-left: 1em; + font-size: .6em; +} + +.show-passed .passed, +.show-skipped .skipped { + display: block; +} + + +#jasmine_content { + position:fixed; + right: 100%; +} + +.runner { + border: 1px solid gray; + display: block; + margin: 5px 0; + padding: 2px 0 2px 10px; +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js new file mode 100644 index 0000000..c3d2dc7 --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js @@ -0,0 +1,2476 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a instanceof jasmine.Matchers.Any) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.Any) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toNotEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toNotContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.matches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.toString = function() { + return ''; +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value instanceof jasmine.Matchers.Any) { + this.emitScalar(value.toString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 1, + "build": 0, + "revision": 1315677058 +}; diff --git a/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png b/node_modules/connect-mongo/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..218f3b43713598fa5a3e78b57aceb909c33f46df GIT binary patch literal 905 zcmV;419tq0P)Px#AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_0008u zNkl3{fod28|PjmA)7fYg4w8-(2my9xtBGOs}K`n&t1VzxMO^X)M zrW+Ln1udc?q6TP)z5gAjt)P&D!M$+HJK#x<`xnD030zwD?KrxxY!2tlA zGc-58?0D7SsT)7Km=v+tNVNUk`?s@;^OxCF)y6P}_mL;~7;S<@b|MzmKq)m8l@yky zT1~ECpxZw@64!nkI34QLiUsA%i%N>-$&zGYR7WJyi9ERMyS(%kf z7A_r)X>!90&m(FwDQZ>q;+nOa*KR2+E6Fz)QwU=W1Oyo*4>_qlm|~joa|{4_A_3W8 z#FFZzRp-xMIx5a7D_Fj3&#r^TbIY@cND1d0f*^qDIs{!pw!IWGQ_%l4#ASm_D5Vet z0%ek7^)@xPihX_G0&hIc9*14ca=D!8oG}vW?H%~w^F?f_s>zU|fKrNJXJ_d6{v!t( zpEoqMws_yQws>3o?VW8Txq~#->dJG^ELW5irR!s`(_JvD^6;r+ho~eIK@ia8_lH(h zt*-p?CFC1_h2MV=?jP){uW!7WjLjCaO&c1D+tf582!XEaoB#xWAYcN5f$sLtf$koW zQs{{>)ZTq?FC6|J_%n}AWbiFK(Bo-%^-{H`*)E(ucjo-r%SYm)W5f6tN=xz=S646E fNXW#U{x?4WXWJ=0.3.0", + "markdown": "0.3.1", + "gleak": "0.2.3", + "step": "0.0.5", + "async": "0.1.22" + }, + "config": { + "native": false + }, + "main": "./lib/mongodb/index", + "homepage": "http://mongodb.github.com/node-mongodb-native/", + "directories": { + "lib": "./lib/mongodb" + }, + "engines": { + "node": ">=0.6.0" + }, + "scripts": { + "test": "make test_pure" + }, + "licenses": [ + { + "type": "Apache License, Version 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "_id": "mongodb@1.2.8", + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.21", + "_nodeVersion": "v0.6.18", + "_defaultsLoaded": true, + "dist": { + "shasum": "7a13af498de2a63018cfeab4aa9a6a2f946def46" + }, + "_from": "mongodb@1.2.x" +} diff --git a/node_modules/connect-mongo/node_modules/mongodb/upload.py b/node_modules/connect-mongo/node_modules/mongodb/upload.py new file mode 100644 index 0000000..6296fde --- /dev/null +++ b/node_modules/connect-mongo/node_modules/mongodb/upload.py @@ -0,0 +1,2347 @@ +#!/usr/bin/env python +# coding: utf-8 +# +# Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tool for uploading diffs from a version control system to the codereview app. + +Usage summary: upload.py [options] [-- diff_options] [path...] + +Diff options are passed to the diff command of the underlying system. + +Supported version control systems: + Git + Mercurial + Subversion + Perforce + CVS + +It is important for Git/Mercurial users to specify a tree/node/branch to diff +against by using the '--rev' option. +""" +# This code is derived from appcfg.py in the App Engine SDK (open source), +# and from ASPN recipe #146306. + +import ConfigParser +import cookielib +import errno +import fnmatch +import getpass +import logging +import marshal +import mimetypes +import optparse +import os +import re +import socket +import subprocess +import sys +import urllib +import urllib2 +import urlparse + +# The md5 module was deprecated in Python 2.5. +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +try: + import readline +except ImportError: + pass + +try: + import keyring +except ImportError: + keyring = None + +# The logging verbosity: +# 0: Errors only. +# 1: Status messages. +# 2: Info logs. +# 3: Debug logs. +verbosity = 1 + +# The account type used for authentication. +# This line could be changed by the review server (see handler for +# upload.py). +AUTH_ACCOUNT_TYPE = "HOSTED" + +# URL of the default review server. As for AUTH_ACCOUNT_TYPE, this line could be +# changed by the review server (see handler for upload.py). +DEFAULT_REVIEW_SERVER = "codereview.10gen.com" + +# Max size of patch or base file. +MAX_UPLOAD_SIZE = 900 * 1024 + +# Constants for version control names. Used by GuessVCSName. +VCS_GIT = "Git" +VCS_MERCURIAL = "Mercurial" +VCS_SUBVERSION = "Subversion" +VCS_PERFORCE = "Perforce" +VCS_CVS = "CVS" +VCS_UNKNOWN = "Unknown" + +VCS_ABBREVIATIONS = { + VCS_MERCURIAL.lower(): VCS_MERCURIAL, + "hg": VCS_MERCURIAL, + VCS_SUBVERSION.lower(): VCS_SUBVERSION, + "svn": VCS_SUBVERSION, + VCS_PERFORCE.lower(): VCS_PERFORCE, + "p4": VCS_PERFORCE, + VCS_GIT.lower(): VCS_GIT, + VCS_CVS.lower(): VCS_CVS, +} + +# The result of parsing Subversion's [auto-props] setting. +svn_auto_props_map = None + +def GetEmail(prompt): + """Prompts the user for their email address and returns it. + + The last used email address is saved to a file and offered up as a suggestion + to the user. If the user presses enter without typing in anything the last + used email address is used. If the user enters a new address, it is saved + for next time we prompt. + + """ + last_email_file_name = os.path.expanduser("~/.last_codereview_email_address") + last_email = "" + if os.path.exists(last_email_file_name): + try: + last_email_file = open(last_email_file_name, "r") + last_email = last_email_file.readline().strip("\n") + last_email_file.close() + prompt += " [%s]" % last_email + except IOError, e: + pass + email = raw_input(prompt + ": ").strip() + if email: + try: + last_email_file = open(last_email_file_name, "w") + last_email_file.write(email) + last_email_file.close() + except IOError, e: + pass + else: + email = last_email + return email + + +def StatusUpdate(msg): + """Print a status message to stdout. + + If 'verbosity' is greater than 0, print the message. + + Args: + msg: The string to print. + """ + if verbosity > 0: + print msg + + +def ErrorExit(msg): + """Print an error message to stderr and exit.""" + print >>sys.stderr, msg + sys.exit(1) + + +class ClientLoginError(urllib2.HTTPError): + """Raised to indicate there was an error authenticating with ClientLogin.""" + + def __init__(self, url, code, msg, headers, args): + urllib2.HTTPError.__init__(self, url, code, msg, headers, None) + self.args = args + self.reason = args["Error"] + self.info = args.get("Info", None) + + +class AbstractRpcServer(object): + """Provides a common interface for a simple RPC server.""" + + def __init__(self, host, auth_function, host_override=None, extra_headers={}, + save_cookies=False, account_type=AUTH_ACCOUNT_TYPE): + """Creates a new HttpRpcServer. + + Args: + host: The host to send requests to. + auth_function: A function that takes no arguments and returns an + (email, password) tuple when called. Will be called if authentication + is required. + host_override: The host header to send to the server (defaults to host). + extra_headers: A dict of extra headers to append to every request. + save_cookies: If True, save the authentication cookies to local disk. + If False, use an in-memory cookiejar instead. Subclasses must + implement this functionality. Defaults to False. + account_type: Account type used for authentication. Defaults to + AUTH_ACCOUNT_TYPE. + """ + self.host = host + if (not self.host.startswith("http://") and + not self.host.startswith("https://")): + self.host = "http://" + self.host + self.host_override = host_override + self.auth_function = auth_function + self.authenticated = False + self.extra_headers = extra_headers + self.save_cookies = save_cookies + self.account_type = account_type + self.opener = self._GetOpener() + if self.host_override: + logging.info("Server: %s; Host: %s", self.host, self.host_override) + else: + logging.info("Server: %s", self.host) + + def _GetOpener(self): + """Returns an OpenerDirector for making HTTP requests. + + Returns: + A urllib2.OpenerDirector object. + """ + raise NotImplementedError() + + def _CreateRequest(self, url, data=None): + """Creates a new urllib request.""" + logging.debug("Creating request for: '%s' with payload:\n%s", url, data) + req = urllib2.Request(url, data=data, headers={"Accept": "text/plain"}) + if self.host_override: + req.add_header("Host", self.host_override) + for key, value in self.extra_headers.iteritems(): + req.add_header(key, value) + return req + + def _GetAuthToken(self, email, password): + """Uses ClientLogin to authenticate the user, returning an auth token. + + Args: + email: The user's email address + password: The user's password + + Raises: + ClientLoginError: If there was an error authenticating with ClientLogin. + HTTPError: If there was some other form of HTTP error. + + Returns: + The authentication token returned by ClientLogin. + """ + account_type = self.account_type + if self.host.endswith(".google.com"): + # Needed for use inside Google. + account_type = "HOSTED" + req = self._CreateRequest( + url="https://www.google.com/accounts/ClientLogin", + data=urllib.urlencode({ + "Email": email, + "Passwd": password, + "service": "ah", + "source": "rietveld-codereview-upload", + "accountType": account_type, + }), + ) + try: + response = self.opener.open(req) + response_body = response.read() + response_dict = dict(x.split("=") + for x in response_body.split("\n") if x) + return response_dict["Auth"] + except urllib2.HTTPError, e: + if e.code == 403: + body = e.read() + response_dict = dict(x.split("=", 1) for x in body.split("\n") if x) + raise ClientLoginError(req.get_full_url(), e.code, e.msg, + e.headers, response_dict) + else: + raise + + def _GetAuthCookie(self, auth_token): + """Fetches authentication cookies for an authentication token. + + Args: + auth_token: The authentication token returned by ClientLogin. + + Raises: + HTTPError: If there was an error fetching the authentication cookies. + """ + # This is a dummy value to allow us to identify when we're successful. + continue_location = "http://localhost/" + args = {"continue": continue_location, "auth": auth_token} + req = self._CreateRequest("%s/_ah/login?%s" % + (self.host, urllib.urlencode(args))) + try: + response = self.opener.open(req) + except urllib2.HTTPError, e: + response = e + if (response.code != 302 or + response.info()["location"] != continue_location): + raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, + response.headers, response.fp) + self.authenticated = True + + def _Authenticate(self): + """Authenticates the user. + + The authentication process works as follows: + 1) We get a username and password from the user + 2) We use ClientLogin to obtain an AUTH token for the user + (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). + 3) We pass the auth token to /_ah/login on the server to obtain an + authentication cookie. If login was successful, it tries to redirect + us to the URL we provided. + + If we attempt to access the upload API without first obtaining an + authentication cookie, it returns a 401 response (or a 302) and + directs us to authenticate ourselves with ClientLogin. + """ + for i in range(3): + credentials = self.auth_function() + try: + auth_token = self._GetAuthToken(credentials[0], credentials[1]) + except ClientLoginError, e: + print >>sys.stderr, '' + if e.reason == "BadAuthentication": + if e.info == "InvalidSecondFactor": + print >>sys.stderr, ( + "Use an application-specific password instead " + "of your regular account password.\n" + "See http://www.google.com/" + "support/accounts/bin/answer.py?answer=185833") + else: + print >>sys.stderr, "Invalid username or password." + elif e.reason == "CaptchaRequired": + print >>sys.stderr, ( + "Please go to\n" + "https://www.google.com/accounts/DisplayUnlockCaptcha\n" + "and verify you are a human. Then try again.\n" + "If you are using a Google Apps account the URL is:\n" + "https://www.google.com/a/yourdomain.com/UnlockCaptcha") + elif e.reason == "NotVerified": + print >>sys.stderr, "Account not verified." + elif e.reason == "TermsNotAgreed": + print >>sys.stderr, "User has not agreed to TOS." + elif e.reason == "AccountDeleted": + print >>sys.stderr, "The user account has been deleted." + elif e.reason == "AccountDisabled": + print >>sys.stderr, "The user account has been disabled." + break + elif e.reason == "ServiceDisabled": + print >>sys.stderr, ("The user's access to the service has been " + "disabled.") + elif e.reason == "ServiceUnavailable": + print >>sys.stderr, "The service is not available; try again later." + else: + # Unknown error. + raise + print >>sys.stderr, '' + continue + self._GetAuthCookie(auth_token) + return + + def Send(self, request_path, payload=None, + content_type="application/octet-stream", + timeout=None, + extra_headers=None, + **kwargs): + """Sends an RPC and returns the response. + + Args: + request_path: The path to send the request to, eg /api/appversion/create. + payload: The body of the request, or None to send an empty request. + content_type: The Content-Type header to use. + timeout: timeout in seconds; default None i.e. no timeout. + (Note: for large requests on OS X, the timeout doesn't work right.) + extra_headers: Dict containing additional HTTP headers that should be + included in the request (string header names mapped to their values), + or None to not include any additional headers. + kwargs: Any keyword arguments are converted into query string parameters. + + Returns: + The response body, as a string. + """ + # TODO: Don't require authentication. Let the server say + # whether it is necessary. + if not self.authenticated: + self._Authenticate() + + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + tries = 0 + while True: + tries += 1 + args = dict(kwargs) + url = "%s%s" % (self.host, request_path) + if args: + url += "?" + urllib.urlencode(args) + req = self._CreateRequest(url=url, data=payload) + req.add_header("Content-Type", content_type) + if extra_headers: + for header, value in extra_headers.items(): + req.add_header(header, value) + try: + f = self.opener.open(req) + response = f.read() + f.close() + return response + except urllib2.HTTPError, e: + if tries > 3: + raise + elif e.code == 401 or e.code == 302: + self._Authenticate() + elif e.code == 301: + # Handle permanent redirect manually. + url = e.info()["location"] + url_loc = urlparse.urlparse(url) + self.host = '%s://%s' % (url_loc[0], url_loc[1]) + elif e.code >= 500: + ErrorExit(e.read()) + else: + raise + finally: + socket.setdefaulttimeout(old_timeout) + + +class HttpRpcServer(AbstractRpcServer): + """Provides a simplified RPC-style interface for HTTP requests.""" + + def _Authenticate(self): + """Save the cookie jar after authentication.""" + super(HttpRpcServer, self)._Authenticate() + if self.save_cookies: + StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) + self.cookie_jar.save() + + def _GetOpener(self): + """Returns an OpenerDirector that supports cookies and ignores redirects. + + Returns: + A urllib2.OpenerDirector object. + """ + opener = urllib2.OpenerDirector() + opener.add_handler(urllib2.ProxyHandler()) + opener.add_handler(urllib2.UnknownHandler()) + opener.add_handler(urllib2.HTTPHandler()) + opener.add_handler(urllib2.HTTPDefaultErrorHandler()) + opener.add_handler(urllib2.HTTPSHandler()) + opener.add_handler(urllib2.HTTPErrorProcessor()) + if self.save_cookies: + self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies") + self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) + if os.path.exists(self.cookie_file): + try: + self.cookie_jar.load() + self.authenticated = True + StatusUpdate("Loaded authentication cookies from %s" % + self.cookie_file) + except (cookielib.LoadError, IOError): + # Failed to load cookies - just ignore them. + pass + else: + # Create an empty cookie file with mode 600 + fd = os.open(self.cookie_file, os.O_CREAT, 0600) + os.close(fd) + # Always chmod the cookie file + os.chmod(self.cookie_file, 0600) + else: + # Don't save cookies across runs of update.py. + self.cookie_jar = cookielib.CookieJar() + opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) + return opener + + +class CondensedHelpFormatter(optparse.IndentedHelpFormatter): + """Frees more horizontal space by removing indentation from group + options and collapsing arguments between short and long, e.g. + '-o ARG, --opt=ARG' to -o --opt ARG""" + + def format_heading(self, heading): + return "%s:\n" % heading + + def format_option(self, option): + self.dedent() + res = optparse.HelpFormatter.format_option(self, option) + self.indent() + return res + + def format_option_strings(self, option): + self.set_long_opt_delimiter(" ") + optstr = optparse.HelpFormatter.format_option_strings(self, option) + optlist = optstr.split(", ") + if len(optlist) > 1: + if option.takes_value(): + # strip METAVAR from all but the last option + optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:] + optstr = " ".join(optlist) + return optstr + + +parser = optparse.OptionParser( + usage="%prog [options] [-- diff_options] [path...]", + add_help_option=False, + formatter=CondensedHelpFormatter() +) +parser.add_option("-h", "--help", action="store_true", + help="Show this help message and exit.") +parser.add_option("-y", "--assume_yes", action="store_true", + dest="assume_yes", default=False, + help="Assume that the answer to yes/no questions is 'yes'.") +# Logging +group = parser.add_option_group("Logging options") +group.add_option("-q", "--quiet", action="store_const", const=0, + dest="verbose", help="Print errors only.") +group.add_option("-v", "--verbose", action="store_const", const=2, + dest="verbose", default=1, + help="Print info level logs.") +group.add_option("--noisy", action="store_const", const=3, + dest="verbose", help="Print all logs.") +group.add_option("--print_diffs", dest="print_diffs", action="store_true", + help="Print full diffs.") +# Review server +group = parser.add_option_group("Review server options") +group.add_option("-s", "--server", action="store", dest="server", + default=DEFAULT_REVIEW_SERVER, + metavar="SERVER", + help=("The server to upload to. The format is host[:port]. " + "Defaults to '%default'.")) +group.add_option("-e", "--email", action="store", dest="email", + metavar="EMAIL", default=None, + help="The username to use. Will prompt if omitted.") +group.add_option("-H", "--host", action="store", dest="host", + metavar="HOST", default=None, + help="Overrides the Host header sent with all RPCs.") +group.add_option("--no_cookies", action="store_false", + dest="save_cookies", default=True, + help="Do not save authentication cookies to local disk.") +group.add_option("--account_type", action="store", dest="account_type", + metavar="TYPE", default=AUTH_ACCOUNT_TYPE, + choices=["GOOGLE", "HOSTED"], + help=("Override the default account type " + "(defaults to '%default', " + "valid choices are 'GOOGLE' and 'HOSTED').")) +# Issue +group = parser.add_option_group("Issue options") +group.add_option("-t", "--title", action="store", dest="title", + help="New issue subject or new patch set title") +group.add_option("-m", "--message", action="store", dest="message", + default=None, + help="New issue description or new patch set message") +group.add_option("-F", "--file", action="store", dest="file", + default=None, help="Read the message above from file.") +group.add_option("-r", "--reviewers", action="store", dest="reviewers", + metavar="REVIEWERS", default=None, + help="Add reviewers (comma separated email addresses).") +group.add_option("--cc", action="store", dest="cc", + metavar="CC", default=None, + help="Add CC (comma separated email addresses).") +group.add_option("--private", action="store_true", dest="private", + default=False, + help="Make the issue restricted to reviewers and those CCed") +# Upload options +group = parser.add_option_group("Patch options") +group.add_option("-i", "--issue", type="int", action="store", + metavar="ISSUE", default=None, + help="Issue number to which to add. Defaults to new issue.") +group.add_option("--base_url", action="store", dest="base_url", default=None, + help="Base URL path for files (listed as \"Base URL\" when " + "viewing issue). If omitted, will be guessed automatically " + "for SVN repos and left blank for others.") +group.add_option("--download_base", action="store_true", + dest="download_base", default=False, + help="Base files will be downloaded by the server " + "(side-by-side diffs may not work on files with CRs).") +group.add_option("--rev", action="store", dest="revision", + metavar="REV", default=None, + help="Base revision/branch/tree to diff against. Use " + "rev1:rev2 range to review already committed changeset.") +group.add_option("--send_mail", action="store_true", + dest="send_mail", default=False, + help="Send notification email to reviewers.") +group.add_option("-p", "--send_patch", action="store_true", + dest="send_patch", default=False, + help="Same as --send_mail, but include diff as an " + "attachment, and prepend email subject with 'PATCH:'.") +group.add_option("--vcs", action="store", dest="vcs", + metavar="VCS", default=None, + help=("Version control system (optional, usually upload.py " + "already guesses the right VCS).")) +group.add_option("--emulate_svn_auto_props", action="store_true", + dest="emulate_svn_auto_props", default=False, + help=("Emulate Subversion's auto properties feature.")) +# Perforce-specific +group = parser.add_option_group("Perforce-specific options " + "(overrides P4 environment variables)") +group.add_option("--p4_port", action="store", dest="p4_port", + metavar="P4_PORT", default=None, + help=("Perforce server and port (optional)")) +group.add_option("--p4_changelist", action="store", dest="p4_changelist", + metavar="P4_CHANGELIST", default=None, + help=("Perforce changelist id")) +group.add_option("--p4_client", action="store", dest="p4_client", + metavar="P4_CLIENT", default=None, + help=("Perforce client/workspace")) +group.add_option("--p4_user", action="store", dest="p4_user", + metavar="P4_USER", default=None, + help=("Perforce user")) + +def GetRpcServer(server, email=None, host_override=None, save_cookies=True, + account_type=AUTH_ACCOUNT_TYPE): + """Returns an instance of an AbstractRpcServer. + + Args: + server: String containing the review server URL. + email: String containing user's email address. + host_override: If not None, string containing an alternate hostname to use + in the host header. + save_cookies: Whether authentication cookies should be saved to disk. + account_type: Account type for authentication, either 'GOOGLE' + or 'HOSTED'. Defaults to AUTH_ACCOUNT_TYPE. + + Returns: + A new AbstractRpcServer, on which RPC calls can be made. + """ + + rpc_server_class = HttpRpcServer + + # If this is the dev_appserver, use fake authentication. + host = (host_override or server).lower() + if re.match(r'(http://)?localhost([:/]|$)', host): + if email is None: + email = "test@example.com" + logging.info("Using debug user %s. Override with --email" % email) + server = rpc_server_class( + server, + lambda: (email, "password"), + host_override=host_override, + extra_headers={"Cookie": + 'dev_appserver_login="%s:False"' % email}, + save_cookies=save_cookies, + account_type=account_type) + # Don't try to talk to ClientLogin. + server.authenticated = True + return server + + def GetUserCredentials(): + """Prompts the user for a username and password.""" + # Create a local alias to the email variable to avoid Python's crazy + # scoping rules. + global keyring + local_email = email + if local_email is None: + local_email = GetEmail("Email (login for uploading to %s)" % server) + password = None + if keyring: + try: + password = keyring.get_password(host, local_email) + except: + # Sadly, we have to trap all errors here as + # gnomekeyring.IOError inherits from object. :/ + print "Failed to get password from keyring" + keyring = None + if password is not None: + print "Using password from system keyring." + else: + password = getpass.getpass("Password for %s: " % local_email) + if keyring: + answer = raw_input("Store password in system keyring?(y/N) ").strip() + if answer == "y": + keyring.set_password(host, local_email, password) + return (local_email, password) + + return rpc_server_class(server, + GetUserCredentials, + host_override=host_override, + save_cookies=save_cookies) + + +def EncodeMultipartFormData(fields, files): + """Encode form fields for multipart/form-data. + + Args: + fields: A sequence of (name, value) elements for regular form fields. + files: A sequence of (name, filename, value) elements for data to be + uploaded as files. + Returns: + (content_type, body) ready for httplib.HTTP instance. + + Source: + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 + """ + BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' + CRLF = '\r\n' + lines = [] + for (key, value) in fields: + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"' % key) + lines.append('') + if isinstance(value, unicode): + value = value.encode('utf-8') + lines.append(value) + for (key, filename, value) in files: + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)) + lines.append('Content-Type: %s' % GetContentType(filename)) + lines.append('') + if isinstance(value, unicode): + value = value.encode('utf-8') + lines.append(value) + lines.append('--' + BOUNDARY + '--') + lines.append('') + body = CRLF.join(lines) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + +def GetContentType(filename): + """Helper to guess the content-type from the filename.""" + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + + +# Use a shell for subcommands on Windows to get a PATH search. +use_shell = sys.platform.startswith("win") + +def RunShellWithReturnCodeAndStderr(command, print_output=False, + universal_newlines=True, + env=os.environ): + """Executes a command and returns the output from stdout, stderr and the return code. + + Args: + command: Command to execute. + print_output: If True, the output is printed to stdout. + If False, both stdout and stderr are ignored. + universal_newlines: Use universal_newlines flag (default: True). + + Returns: + Tuple (stdout, stderr, return code) + """ + logging.info("Running %s", command) + env = env.copy() + env['LC_MESSAGES'] = 'C' + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=use_shell, universal_newlines=universal_newlines, + env=env) + if print_output: + output_array = [] + while True: + line = p.stdout.readline() + if not line: + break + print line.strip("\n") + output_array.append(line) + output = "".join(output_array) + else: + output = p.stdout.read() + p.wait() + errout = p.stderr.read() + if print_output and errout: + print >>sys.stderr, errout + p.stdout.close() + p.stderr.close() + return output, errout, p.returncode + +def RunShellWithReturnCode(command, print_output=False, + universal_newlines=True, + env=os.environ): + """Executes a command and returns the output from stdout and the return code.""" + out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output, + universal_newlines, env) + return out, retcode + +def RunShell(command, silent_ok=False, universal_newlines=True, + print_output=False, env=os.environ): + data, retcode = RunShellWithReturnCode(command, print_output, + universal_newlines, env) + if retcode: + ErrorExit("Got error status from %s:\n%s" % (command, data)) + if not silent_ok and not data: + ErrorExit("No output from %s" % command) + return data + + +class VersionControlSystem(object): + """Abstract base class providing an interface to the VCS.""" + + def __init__(self, options): + """Constructor. + + Args: + options: Command line options. + """ + self.options = options + + def GetGUID(self): + """Return string to distinguish the repository from others, for example to + query all opened review issues for it""" + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def PostProcessDiff(self, diff): + """Return the diff with any special post processing this VCS needs, e.g. + to include an svn-style "Index:".""" + return diff + + def GenerateDiff(self, args): + """Return the current diff as a string. + + Args: + args: Extra arguments to pass to the diff command. + """ + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def CheckForUnknownFiles(self): + """Show an "are you sure?" prompt if there are unknown files.""" + unknown_files = self.GetUnknownFiles() + if unknown_files: + print "The following files are not added to version control:" + for line in unknown_files: + print line + prompt = "Are you sure to continue?(y/N) " + answer = raw_input(prompt).strip() + if answer != "y": + ErrorExit("User aborted") + + def GetBaseFile(self, filename): + """Get the content of the upstream version of a file. + + Returns: + A tuple (base_content, new_content, is_binary, status) + base_content: The contents of the base file. + new_content: For text files, this is empty. For binary files, this is + the contents of the new file, since the diff output won't contain + information to reconstruct the current file. + is_binary: True iff the file is binary. + status: The status of the file. + """ + + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + + def GetBaseFiles(self, diff): + """Helper that calls GetBase file for each file in the patch. + + Returns: + A dictionary that maps from filename to GetBaseFile's tuple. Filenames + are retrieved based on lines that start with "Index:" or + "Property changes on:". + """ + files = {} + for line in diff.splitlines(True): + if line.startswith('Index:') or line.startswith('Property changes on:'): + unused, filename = line.split(':', 1) + # On Windows if a file has property changes its filename uses '\' + # instead of '/'. + filename = filename.strip().replace('\\', '/') + files[filename] = self.GetBaseFile(filename) + return files + + + def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, + files): + """Uploads the base files (and if necessary, the current ones as well).""" + + def UploadFile(filename, file_id, content, is_binary, status, is_base): + """Uploads a file to the server.""" + file_too_large = False + if is_base: + type = "base" + else: + type = "current" + if len(content) > MAX_UPLOAD_SIZE: + print ("Not uploading the %s file for %s because it's too large." % + (type, filename)) + file_too_large = True + content = "" + checksum = md5(content).hexdigest() + if options.verbose > 0 and not file_too_large: + print "Uploading %s file for %s" % (type, filename) + url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) + form_fields = [("filename", filename), + ("status", status), + ("checksum", checksum), + ("is_binary", str(is_binary)), + ("is_current", str(not is_base)), + ] + if file_too_large: + form_fields.append(("file_too_large", "1")) + if options.email: + form_fields.append(("user", options.email)) + ctype, body = EncodeMultipartFormData(form_fields, + [("data", filename, content)]) + response_body = rpc_server.Send(url, body, + content_type=ctype) + if not response_body.startswith("OK"): + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + + patches = dict() + [patches.setdefault(v, k) for k, v in patch_list] + for filename in patches.keys(): + base_content, new_content, is_binary, status = files[filename] + file_id_str = patches.get(filename) + if file_id_str.find("nobase") != -1: + base_content = None + file_id_str = file_id_str[file_id_str.rfind("_") + 1:] + file_id = int(file_id_str) + if base_content != None: + UploadFile(filename, file_id, base_content, is_binary, status, True) + if new_content != None: + UploadFile(filename, file_id, new_content, is_binary, status, False) + + def IsImage(self, filename): + """Returns true if the filename has an image extension.""" + mimetype = mimetypes.guess_type(filename)[0] + if not mimetype: + return False + return mimetype.startswith("image/") + + def IsBinaryData(self, data): + """Returns true if data contains a null byte.""" + # Derived from how Mercurial's heuristic, see + # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 + return bool(data and "\0" in data) + + +class SubversionVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Subversion.""" + + def __init__(self, options): + super(SubversionVCS, self).__init__(options) + if self.options.revision: + match = re.match(r"(\d+)(:(\d+))?", self.options.revision) + if not match: + ErrorExit("Invalid Subversion revision %s." % self.options.revision) + self.rev_start = match.group(1) + self.rev_end = match.group(3) + else: + self.rev_start = self.rev_end = None + # Cache output from "svn list -r REVNO dirname". + # Keys: dirname, Values: 2-tuple (ouput for start rev and end rev). + self.svnls_cache = {} + # Base URL is required to fetch files deleted in an older revision. + # Result is cached to not guess it over and over again in GetBaseFile(). + required = self.options.download_base or self.options.revision is not None + self.svn_base = self._GuessBase(required) + + def GetGUID(self): + return self._GetInfo("Repository UUID") + + def GuessBase(self, required): + """Wrapper for _GuessBase.""" + return self.svn_base + + def _GuessBase(self, required): + """Returns base URL for current diff. + + Args: + required: If true, exits if the url can't be guessed, otherwise None is + returned. + """ + url = self._GetInfo("URL") + if url: + scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) + guess = "" + # TODO(anatoli) - repository specific hacks should be handled by server + if netloc == "svn.python.org" and scheme == "svn+ssh": + path = "projects" + path + scheme = "http" + guess = "Python " + elif netloc.endswith(".googlecode.com"): + scheme = "http" + guess = "Google Code " + path = path + "/" + base = urlparse.urlunparse((scheme, netloc, path, params, + query, fragment)) + logging.info("Guessed %sbase = %s", guess, base) + return base + if required: + ErrorExit("Can't find URL in output from svn info") + return None + + def _GetInfo(self, key): + """Parses 'svn info' for current dir. Returns value for key or None""" + for line in RunShell(["svn", "info"]).splitlines(): + if line.startswith(key + ": "): + return line.split(":", 1)[1].strip() + + def _EscapeFilename(self, filename): + """Escapes filename for SVN commands.""" + if "@" in filename and not filename.endswith("@"): + filename = "%s@" % filename + return filename + + def GenerateDiff(self, args): + cmd = ["svn", "diff"] + if self.options.revision: + cmd += ["-r", self.options.revision] + cmd.extend(args) + data = RunShell(cmd) + count = 0 + for line in data.splitlines(): + if line.startswith("Index:") or line.startswith("Property changes on:"): + count += 1 + logging.info(line) + if not count: + ErrorExit("No valid patches found in output from svn diff") + return data + + def _CollapseKeywords(self, content, keyword_str): + """Collapses SVN keywords.""" + # svn cat translates keywords but svn diff doesn't. As a result of this + # behavior patching.PatchChunks() fails with a chunk mismatch error. + # This part was originally written by the Review Board development team + # who had the same problem (http://reviews.review-board.org/r/276/). + # Mapping of keywords to known aliases + svn_keywords = { + # Standard keywords + 'Date': ['Date', 'LastChangedDate'], + 'Revision': ['Revision', 'LastChangedRevision', 'Rev'], + 'Author': ['Author', 'LastChangedBy'], + 'HeadURL': ['HeadURL', 'URL'], + 'Id': ['Id'], + + # Aliases + 'LastChangedDate': ['LastChangedDate', 'Date'], + 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], + 'LastChangedBy': ['LastChangedBy', 'Author'], + 'URL': ['URL', 'HeadURL'], + } + + def repl(m): + if m.group(2): + return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) + return "$%s$" % m.group(1) + keywords = [keyword + for name in keyword_str.split(" ") + for keyword in svn_keywords.get(name, [])] + return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) + + def GetUnknownFiles(self): + status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) + unknown_files = [] + for line in status.split("\n"): + if line and line[0] == "?": + unknown_files.append(line) + return unknown_files + + def ReadFile(self, filename): + """Returns the contents of a file.""" + file = open(filename, 'rb') + result = "" + try: + result = file.read() + finally: + file.close() + return result + + def GetStatus(self, filename): + """Returns the status of a file.""" + if not self.options.revision: + status = RunShell(["svn", "status", "--ignore-externals", + self._EscapeFilename(filename)]) + if not status: + ErrorExit("svn status returned no output for %s" % filename) + status_lines = status.splitlines() + # If file is in a cl, the output will begin with + # "\n--- Changelist 'cl_name':\n". See + # http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt + if (len(status_lines) == 3 and + not status_lines[0] and + status_lines[1].startswith("--- Changelist")): + status = status_lines[2] + else: + status = status_lines[0] + # If we have a revision to diff against we need to run "svn list" + # for the old and the new revision and compare the results to get + # the correct status for a file. + else: + dirname, relfilename = os.path.split(filename) + if dirname not in self.svnls_cache: + cmd = ["svn", "list", "-r", self.rev_start, + self._EscapeFilename(dirname) or "."] + out, err, returncode = RunShellWithReturnCodeAndStderr(cmd) + if returncode: + # Directory might not yet exist at start revison + # svn: Unable to find repository location for 'abc' in revision nnn + if re.match('^svn: Unable to find repository location for .+ in revision \d+', err): + old_files = () + else: + ErrorExit("Failed to get status for %s:\n%s" % (filename, err)) + else: + old_files = out.splitlines() + args = ["svn", "list"] + if self.rev_end: + args += ["-r", self.rev_end] + cmd = args + [self._EscapeFilename(dirname) or "."] + out, returncode = RunShellWithReturnCode(cmd) + if returncode: + ErrorExit("Failed to run command %s" % cmd) + self.svnls_cache[dirname] = (old_files, out.splitlines()) + old_files, new_files = self.svnls_cache[dirname] + if relfilename in old_files and relfilename not in new_files: + status = "D " + elif relfilename in old_files and relfilename in new_files: + status = "M " + else: + status = "A " + return status + + def GetBaseFile(self, filename): + status = self.GetStatus(filename) + base_content = None + new_content = None + + # If a file is copied its status will be "A +", which signifies + # "addition-with-history". See "svn st" for more information. We need to + # upload the original file or else diff parsing will fail if the file was + # edited. + if status[0] == "A" and status[3] != "+": + # We'll need to upload the new content if we're adding a binary file + # since diff's output won't contain it. + mimetype = RunShell(["svn", "propget", "svn:mime-type", + self._EscapeFilename(filename)], silent_ok=True) + base_content = "" + is_binary = bool(mimetype) and not mimetype.startswith("text/") + if is_binary and self.IsImage(filename): + new_content = self.ReadFile(filename) + elif (status[0] in ("M", "D", "R") or + (status[0] == "A" and status[3] == "+") or # Copied file. + (status[0] == " " and status[1] == "M")): # Property change. + args = [] + if self.options.revision: + # filename must not be escaped. We already add an ampersand here. + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + else: + # Don't change filename, it's needed later. + url = filename + args += ["-r", "BASE"] + cmd = ["svn"] + args + ["propget", "svn:mime-type", url] + mimetype, returncode = RunShellWithReturnCode(cmd) + if returncode: + # File does not exist in the requested revision. + # Reset mimetype, it contains an error message. + mimetype = "" + else: + mimetype = mimetype.strip() + get_base = False + # this test for binary is exactly the test prescribed by the + # official SVN docs at + # http://subversion.apache.org/faq.html#binary-files + is_binary = (bool(mimetype) and + not mimetype.startswith("text/") and + mimetype not in ("image/x-xbitmap", "image/x-xpixmap")) + if status[0] == " ": + # Empty base content just to force an upload. + base_content = "" + elif is_binary: + if self.IsImage(filename): + get_base = True + if status[0] == "M": + if not self.rev_end: + new_content = self.ReadFile(filename) + else: + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_end) + new_content = RunShell(["svn", "cat", url], + universal_newlines=True, silent_ok=True) + else: + base_content = "" + else: + get_base = True + + if get_base: + if is_binary: + universal_newlines = False + else: + universal_newlines = True + if self.rev_start: + # "svn cat -r REV delete_file.txt" doesn't work. cat requires + # the full URL with "@REV" appended instead of using "-r" option. + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + base_content = RunShell(["svn", "cat", url], + universal_newlines=universal_newlines, + silent_ok=True) + else: + base_content, ret_code = RunShellWithReturnCode( + ["svn", "cat", self._EscapeFilename(filename)], + universal_newlines=universal_newlines) + if ret_code and status[0] == "R": + # It's a replaced file without local history (see issue208). + # The base file needs to be fetched from the server. + url = "%s/%s" % (self.svn_base, filename) + base_content = RunShell(["svn", "cat", url], + universal_newlines=universal_newlines, + silent_ok=True) + elif ret_code: + ErrorExit("Got error status from 'svn cat %s'" % filename) + if not is_binary: + args = [] + if self.rev_start: + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + else: + url = filename + args += ["-r", "BASE"] + cmd = ["svn"] + args + ["propget", "svn:keywords", url] + keywords, returncode = RunShellWithReturnCode(cmd) + if keywords and not returncode: + base_content = self._CollapseKeywords(base_content, keywords) + else: + StatusUpdate("svn status returned unexpected output: %s" % status) + sys.exit(1) + return base_content, new_content, is_binary, status[0:5] + + +class GitVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Git.""" + + def __init__(self, options): + super(GitVCS, self).__init__(options) + # Map of filename -> (hash before, hash after) of base file. + # Hashes for "no such file" are represented as None. + self.hashes = {} + # Map of new filename -> old filename for renames. + self.renames = {} + + def GetGUID(self): + revlist = RunShell("git rev-list --parents HEAD".split()).splitlines() + # M-A: Return the 1st root hash, there could be multiple when a + # subtree is merged. In that case, more analysis would need to + # be done to figure out which HEAD is the 'most representative'. + for r in revlist: + if ' ' not in r: + return r + + def PostProcessDiff(self, gitdiff): + """Converts the diff output to include an svn-style "Index:" line as well + as record the hashes of the files, so we can upload them along with our + diff.""" + # Special used by git to indicate "no such content". + NULL_HASH = "0"*40 + + def IsFileNew(filename): + return filename in self.hashes and self.hashes[filename][0] is None + + def AddSubversionPropertyChange(filename): + """Add svn's property change information into the patch if given file is + new file. + + We use Subversion's auto-props setting to retrieve its property. + See http://svnbook.red-bean.com/en/1.1/ch07.html#svn-ch-7-sect-1.3.2 for + Subversion's [auto-props] setting. + """ + if self.options.emulate_svn_auto_props and IsFileNew(filename): + svnprops = GetSubversionPropertyChanges(filename) + if svnprops: + svndiff.append("\n" + svnprops + "\n") + + svndiff = [] + filecount = 0 + filename = None + for line in gitdiff.splitlines(): + match = re.match(r"diff --git a/(.*) b/(.*)$", line) + if match: + # Add auto property here for previously seen file. + if filename is not None: + AddSubversionPropertyChange(filename) + filecount += 1 + # Intentionally use the "after" filename so we can show renames. + filename = match.group(2) + svndiff.append("Index: %s\n" % filename) + if match.group(1) != match.group(2): + self.renames[match.group(2)] = match.group(1) + else: + # The "index" line in a git diff looks like this (long hashes elided): + # index 82c0d44..b2cee3f 100755 + # We want to save the left hash, as that identifies the base file. + match = re.match(r"index (\w+)\.\.(\w+)", line) + if match: + before, after = (match.group(1), match.group(2)) + if before == NULL_HASH: + before = None + if after == NULL_HASH: + after = None + self.hashes[filename] = (before, after) + svndiff.append(line + "\n") + if not filecount: + ErrorExit("No valid patches found in output from git diff") + # Add auto property for the last seen file. + assert filename is not None + AddSubversionPropertyChange(filename) + return "".join(svndiff) + + def GenerateDiff(self, extra_args): + extra_args = extra_args[:] + if self.options.revision: + if ":" in self.options.revision: + extra_args = self.options.revision.split(":", 1) + extra_args + else: + extra_args = [self.options.revision] + extra_args + + # --no-ext-diff is broken in some versions of Git, so try to work around + # this by overriding the environment (but there is still a problem if the + # git config key "diff.external" is used). + env = os.environ.copy() + if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] + return RunShell( + [ "git", "diff", "--no-color", "--no-ext-diff", "--full-index", + "--ignore-submodules", "-M"] + extra_args, + env=env) + + def GetUnknownFiles(self): + status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], + silent_ok=True) + return status.splitlines() + + def GetFileContent(self, file_hash, is_binary): + """Returns the content of a file identified by its git hash.""" + data, retcode = RunShellWithReturnCode(["git", "show", file_hash], + universal_newlines=not is_binary) + if retcode: + ErrorExit("Got error status from 'git show %s'" % file_hash) + return data + + def GetBaseFile(self, filename): + hash_before, hash_after = self.hashes.get(filename, (None,None)) + base_content = None + new_content = None + status = None + + if filename in self.renames: + status = "A +" # Match svn attribute name for renames. + if filename not in self.hashes: + # If a rename doesn't change the content, we never get a hash. + base_content = RunShell( + ["git", "show", "HEAD:" + filename], silent_ok=True) + elif not hash_before: + status = "A" + base_content = "" + elif not hash_after: + status = "D" + else: + status = "M" + + is_binary = self.IsBinaryData(base_content) + is_image = self.IsImage(filename) + + # Grab the before/after content if we need it. + # We should include file contents if it's text or it's an image. + if not is_binary or is_image: + # Grab the base content if we don't have it already. + if base_content is None and hash_before: + base_content = self.GetFileContent(hash_before, is_binary) + # Only include the "after" file if it's an image; otherwise it + # it is reconstructed from the diff. + if is_image and hash_after: + new_content = self.GetFileContent(hash_after, is_binary) + + return (base_content, new_content, is_binary, status) + + +class CVSVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for CVS.""" + + def __init__(self, options): + super(CVSVCS, self).__init__(options) + + def GetGUID(self): + """For now we don't know how to get repository ID for CVS""" + return + + def GetOriginalContent_(self, filename): + RunShell(["cvs", "up", filename], silent_ok=True) + # TODO need detect file content encoding + content = open(filename).read() + return content.replace("\r\n", "\n") + + def GetBaseFile(self, filename): + base_content = None + new_content = None + status = "A" + + output, retcode = RunShellWithReturnCode(["cvs", "status", filename]) + if retcode: + ErrorExit("Got error status from 'cvs status %s'" % filename) + + if output.find("Status: Locally Modified") != -1: + status = "M" + temp_filename = "%s.tmp123" % filename + os.rename(filename, temp_filename) + base_content = self.GetOriginalContent_(filename) + os.rename(temp_filename, filename) + elif output.find("Status: Locally Added"): + status = "A" + base_content = "" + elif output.find("Status: Needs Checkout"): + status = "D" + base_content = self.GetOriginalContent_(filename) + + return (base_content, new_content, self.IsBinaryData(base_content), status) + + def GenerateDiff(self, extra_args): + cmd = ["cvs", "diff", "-u", "-N"] + if self.options.revision: + cmd += ["-r", self.options.revision] + + cmd.extend(extra_args) + data, retcode = RunShellWithReturnCode(cmd) + count = 0 + if retcode in [0, 1]: + for line in data.splitlines(): + if line.startswith("Index:"): + count += 1 + logging.info(line) + + if not count: + ErrorExit("No valid patches found in output from cvs diff") + + return data + + def GetUnknownFiles(self): + data, retcode = RunShellWithReturnCode(["cvs", "diff"]) + if retcode not in [0, 1]: + ErrorExit("Got error status from 'cvs diff':\n%s" % (data,)) + unknown_files = [] + for line in data.split("\n"): + if line and line[0] == "?": + unknown_files.append(line) + return unknown_files + +class MercurialVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Mercurial.""" + + def __init__(self, options, repo_dir): + super(MercurialVCS, self).__init__(options) + # Absolute path to repository (we can be in a subdir) + self.repo_dir = os.path.normpath(repo_dir) + # Compute the subdir + cwd = os.path.normpath(os.getcwd()) + assert cwd.startswith(self.repo_dir) + self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/") + if self.options.revision: + self.base_rev = self.options.revision + else: + self.base_rev = RunShell(["hg", "parent", "-q"]).split(':')[1].strip() + + def GetGUID(self): + # See chapter "Uniquely identifying a repository" + # http://hgbook.red-bean.com/read/customizing-the-output-of-mercurial.html + info = RunShell("hg log -r0 --template {node}".split()) + return info.strip() + + def _GetRelPath(self, filename): + """Get relative path of a file according to the current directory, + given its logical path in the repo.""" + absname = os.path.join(self.repo_dir, filename) + return os.path.relpath(absname) + + def GenerateDiff(self, extra_args): + cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args + data = RunShell(cmd, silent_ok=True) + svndiff = [] + filecount = 0 + for line in data.splitlines(): + m = re.match("diff --git a/(\S+) b/(\S+)", line) + if m: + # Modify line to make it look like as it comes from svn diff. + # With this modification no changes on the server side are required + # to make upload.py work with Mercurial repos. + # NOTE: for proper handling of moved/copied files, we have to use + # the second filename. + filename = m.group(2) + svndiff.append("Index: %s" % filename) + svndiff.append("=" * 67) + filecount += 1 + logging.info(line) + else: + svndiff.append(line) + if not filecount: + ErrorExit("No valid patches found in output from hg diff") + return "\n".join(svndiff) + "\n" + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + args = [] + status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], + silent_ok=True) + unknown_files = [] + for line in status.splitlines(): + st, fn = line.split(" ", 1) + if st == "?": + unknown_files.append(fn) + return unknown_files + + def GetBaseFile(self, filename): + # "hg status" and "hg cat" both take a path relative to the current subdir, + # but "hg diff" has given us the path relative to the repo root. + base_content = "" + new_content = None + is_binary = False + oldrelpath = relpath = self._GetRelPath(filename) + # "hg status -C" returns two lines for moved/copied files, one otherwise + out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath]) + out = out.splitlines() + # HACK: strip error message about missing file/directory if it isn't in + # the working copy + if out[0].startswith('%s: ' % relpath): + out = out[1:] + status, _ = out[0].split(' ', 1) + if len(out) > 1 and status == "A": + # Moved/copied => considered as modified, use old filename to + # retrieve base contents + oldrelpath = out[1].strip() + status = "M" + if ":" in self.base_rev: + base_rev = self.base_rev.split(":", 1)[0] + else: + base_rev = self.base_rev + if status != "A": + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], + silent_ok=True) + is_binary = self.IsBinaryData(base_content) + if status != "R": + new_content = open(relpath, "rb").read() + is_binary = is_binary or self.IsBinaryData(new_content) + if is_binary and base_content: + # Fetch again without converting newlines + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], + silent_ok=True, universal_newlines=False) + if not is_binary or not self.IsImage(relpath): + new_content = None + return base_content, new_content, is_binary, status + + +class PerforceVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Perforce.""" + + def __init__(self, options): + + def ConfirmLogin(): + # Make sure we have a valid perforce session + while True: + data, retcode = self.RunPerforceCommandWithReturnCode( + ["login", "-s"], marshal_output=True) + if not data: + ErrorExit("Error checking perforce login") + if not retcode and (not "code" in data or data["code"] != "error"): + break + print "Enter perforce password: " + self.RunPerforceCommandWithReturnCode(["login"]) + + super(PerforceVCS, self).__init__(options) + + self.p4_changelist = options.p4_changelist + if not self.p4_changelist: + ErrorExit("A changelist id is required") + if (options.revision): + ErrorExit("--rev is not supported for perforce") + + self.p4_port = options.p4_port + self.p4_client = options.p4_client + self.p4_user = options.p4_user + + ConfirmLogin() + + if not options.title: + description = self.RunPerforceCommand(["describe", self.p4_changelist], + marshal_output=True) + if description and "desc" in description: + # Rietveld doesn't support multi-line descriptions + raw_title = description["desc"].strip() + lines = raw_title.splitlines() + if len(lines): + options.title = lines[0] + + def GetGUID(self): + """For now we don't know how to get repository ID for Perforce""" + return + + def RunPerforceCommandWithReturnCode(self, extra_args, marshal_output=False, + universal_newlines=True): + args = ["p4"] + if marshal_output: + # -G makes perforce format its output as marshalled python objects + args.extend(["-G"]) + if self.p4_port: + args.extend(["-p", self.p4_port]) + if self.p4_client: + args.extend(["-c", self.p4_client]) + if self.p4_user: + args.extend(["-u", self.p4_user]) + args.extend(extra_args) + + data, retcode = RunShellWithReturnCode( + args, print_output=False, universal_newlines=universal_newlines) + if marshal_output and data: + data = marshal.loads(data) + return data, retcode + + def RunPerforceCommand(self, extra_args, marshal_output=False, + universal_newlines=True): + # This might be a good place to cache call results, since things like + # describe or fstat might get called repeatedly. + data, retcode = self.RunPerforceCommandWithReturnCode( + extra_args, marshal_output, universal_newlines) + if retcode: + ErrorExit("Got error status from %s:\n%s" % (extra_args, data)) + return data + + def GetFileProperties(self, property_key_prefix = "", command = "describe"): + description = self.RunPerforceCommand(["describe", self.p4_changelist], + marshal_output=True) + + changed_files = {} + file_index = 0 + # Try depotFile0, depotFile1, ... until we don't find a match + while True: + file_key = "depotFile%d" % file_index + if file_key in description: + filename = description[file_key] + change_type = description[property_key_prefix + str(file_index)] + changed_files[filename] = change_type + file_index += 1 + else: + break + return changed_files + + def GetChangedFiles(self): + return self.GetFileProperties("action") + + def GetUnknownFiles(self): + # Perforce doesn't detect new files, they have to be explicitly added + return [] + + def IsBaseBinary(self, filename): + base_filename = self.GetBaseFilename(filename) + return self.IsBinaryHelper(base_filename, "files") + + def IsPendingBinary(self, filename): + return self.IsBinaryHelper(filename, "describe") + + def IsBinaryHelper(self, filename, command): + file_types = self.GetFileProperties("type", command) + if not filename in file_types: + ErrorExit("Trying to check binary status of unknown file %s." % filename) + # This treats symlinks, macintosh resource files, temporary objects, and + # unicode as binary. See the Perforce docs for more details: + # http://www.perforce.com/perforce/doc.current/manuals/cmdref/o.ftypes.html + return not file_types[filename].endswith("text") + + def GetFileContent(self, filename, revision, is_binary): + file_arg = filename + if revision: + file_arg += "#" + revision + # -q suppresses the initial line that displays the filename and revision + return self.RunPerforceCommand(["print", "-q", file_arg], + universal_newlines=not is_binary) + + def GetBaseFilename(self, filename): + actionsWithDifferentBases = [ + "move/add", # p4 move + "branch", # p4 integrate (to a new file), similar to hg "add" + "add", # p4 integrate (to a new file), after modifying the new file + ] + + # We only see a different base for "add" if this is a downgraded branch + # after a file was branched (integrated), then edited. + if self.GetAction(filename) in actionsWithDifferentBases: + # -Or shows information about pending integrations/moves + fstat_result = self.RunPerforceCommand(["fstat", "-Or", filename], + marshal_output=True) + + baseFileKey = "resolveFromFile0" # I think it's safe to use only file0 + if baseFileKey in fstat_result: + return fstat_result[baseFileKey] + + return filename + + def GetBaseRevision(self, filename): + base_filename = self.GetBaseFilename(filename) + + have_result = self.RunPerforceCommand(["have", base_filename], + marshal_output=True) + if "haveRev" in have_result: + return have_result["haveRev"] + + def GetLocalFilename(self, filename): + where = self.RunPerforceCommand(["where", filename], marshal_output=True) + if "path" in where: + return where["path"] + + def GenerateDiff(self, args): + class DiffData: + def __init__(self, perforceVCS, filename, action): + self.perforceVCS = perforceVCS + self.filename = filename + self.action = action + self.base_filename = perforceVCS.GetBaseFilename(filename) + + self.file_body = None + self.base_rev = None + self.prefix = None + self.working_copy = True + self.change_summary = None + + def GenerateDiffHeader(diffData): + header = [] + header.append("Index: %s" % diffData.filename) + header.append("=" * 67) + + if diffData.base_filename != diffData.filename: + if diffData.action.startswith("move"): + verb = "rename" + else: + verb = "copy" + header.append("%s from %s" % (verb, diffData.base_filename)) + header.append("%s to %s" % (verb, diffData.filename)) + + suffix = "\t(revision %s)" % diffData.base_rev + header.append("--- " + diffData.base_filename + suffix) + if diffData.working_copy: + suffix = "\t(working copy)" + header.append("+++ " + diffData.filename + suffix) + if diffData.change_summary: + header.append(diffData.change_summary) + return header + + def GenerateMergeDiff(diffData, args): + # -du generates a unified diff, which is nearly svn format + diffData.file_body = self.RunPerforceCommand( + ["diff", "-du", diffData.filename] + args) + diffData.base_rev = self.GetBaseRevision(diffData.filename) + diffData.prefix = "" + + # We have to replace p4's file status output (the lines starting + # with +++ or ---) to match svn's diff format + lines = diffData.file_body.splitlines() + first_good_line = 0 + while (first_good_line < len(lines) and + not lines[first_good_line].startswith("@@")): + first_good_line += 1 + diffData.file_body = "\n".join(lines[first_good_line:]) + return diffData + + def GenerateAddDiff(diffData): + fstat = self.RunPerforceCommand(["fstat", diffData.filename], + marshal_output=True) + if "headRev" in fstat: + diffData.base_rev = fstat["headRev"] # Re-adding a deleted file + else: + diffData.base_rev = "0" # Brand new file + diffData.working_copy = False + rel_path = self.GetLocalFilename(diffData.filename) + diffData.file_body = open(rel_path, 'r').read() + # Replicate svn's list of changed lines + line_count = len(diffData.file_body.splitlines()) + diffData.change_summary = "@@ -0,0 +1" + if line_count > 1: + diffData.change_summary += ",%d" % line_count + diffData.change_summary += " @@" + diffData.prefix = "+" + return diffData + + def GenerateDeleteDiff(diffData): + diffData.base_rev = self.GetBaseRevision(diffData.filename) + is_base_binary = self.IsBaseBinary(diffData.filename) + # For deletes, base_filename == filename + diffData.file_body = self.GetFileContent(diffData.base_filename, + None, + is_base_binary) + # Replicate svn's list of changed lines + line_count = len(diffData.file_body.splitlines()) + diffData.change_summary = "@@ -1" + if line_count > 1: + diffData.change_summary += ",%d" % line_count + diffData.change_summary += " +0,0 @@" + diffData.prefix = "-" + return diffData + + changed_files = self.GetChangedFiles() + + svndiff = [] + filecount = 0 + for (filename, action) in changed_files.items(): + svn_status = self.PerforceActionToSvnStatus(action) + if svn_status == "SKIP": + continue + + diffData = DiffData(self, filename, action) + # Is it possible to diff a branched file? Stackoverflow says no: + # http://stackoverflow.com/questions/1771314/in-perforce-command-line-how-to-diff-a-file-reopened-for-add + if svn_status == "M": + diffData = GenerateMergeDiff(diffData, args) + elif svn_status == "A": + diffData = GenerateAddDiff(diffData) + elif svn_status == "D": + diffData = GenerateDeleteDiff(diffData) + else: + ErrorExit("Unknown file action %s (svn action %s)." % \ + (action, svn_status)) + + svndiff += GenerateDiffHeader(diffData) + + for line in diffData.file_body.splitlines(): + svndiff.append(diffData.prefix + line) + filecount += 1 + if not filecount: + ErrorExit("No valid patches found in output from p4 diff") + return "\n".join(svndiff) + "\n" + + def PerforceActionToSvnStatus(self, status): + # Mirroring the list at http://permalink.gmane.org/gmane.comp.version-control.mercurial.devel/28717 + # Is there something more official? + return { + "add" : "A", + "branch" : "A", + "delete" : "D", + "edit" : "M", # Also includes changing file types. + "integrate" : "M", + "move/add" : "M", + "move/delete": "SKIP", + "purge" : "D", # How does a file's status become "purge"? + }[status] + + def GetAction(self, filename): + changed_files = self.GetChangedFiles() + if not filename in changed_files: + ErrorExit("Trying to get base version of unknown file %s." % filename) + + return changed_files[filename] + + def GetBaseFile(self, filename): + base_filename = self.GetBaseFilename(filename) + base_content = "" + new_content = None + + status = self.PerforceActionToSvnStatus(self.GetAction(filename)) + + if status != "A": + revision = self.GetBaseRevision(base_filename) + if not revision: + ErrorExit("Couldn't find base revision for file %s" % filename) + is_base_binary = self.IsBaseBinary(base_filename) + base_content = self.GetFileContent(base_filename, + revision, + is_base_binary) + + is_binary = self.IsPendingBinary(filename) + if status != "D" and status != "SKIP": + relpath = self.GetLocalFilename(filename) + if is_binary and self.IsImage(relpath): + new_content = open(relpath, "rb").read() + + return base_content, new_content, is_binary, status + +# NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync. +def SplitPatch(data): + """Splits a patch into separate pieces for each file. + + Args: + data: A string containing the output of svn diff. + + Returns: + A list of 2-tuple (filename, text) where text is the svn diff output + pertaining to filename. + """ + patches = [] + filename = None + diff = [] + for line in data.splitlines(True): + new_filename = None + if line.startswith('Index:'): + unused, new_filename = line.split(':', 1) + new_filename = new_filename.strip() + elif line.startswith('Property changes on:'): + unused, temp_filename = line.split(':', 1) + # When a file is modified, paths use '/' between directories, however + # when a property is modified '\' is used on Windows. Make them the same + # otherwise the file shows up twice. + temp_filename = temp_filename.strip().replace('\\', '/') + if temp_filename != filename: + # File has property changes but no modifications, create a new diff. + new_filename = temp_filename + if new_filename: + if filename and diff: + patches.append((filename, ''.join(diff))) + filename = new_filename + diff = [line] + continue + if diff is not None: + diff.append(line) + if filename and diff: + patches.append((filename, ''.join(diff))) + return patches + + +def UploadSeparatePatches(issue, rpc_server, patchset, data, options): + """Uploads a separate patch for each file in the diff output. + + Returns a list of [patch_key, filename] for each file. + """ + patches = SplitPatch(data) + rv = [] + for patch in patches: + if len(patch[1]) > MAX_UPLOAD_SIZE: + print ("Not uploading the patch for " + patch[0] + + " because the file is too large.") + continue + form_fields = [("filename", patch[0])] + if not options.download_base: + form_fields.append(("content_upload", "1")) + files = [("data", "data.diff", patch[1])] + ctype, body = EncodeMultipartFormData(form_fields, files) + url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) + print "Uploading patch for " + patch[0] + response_body = rpc_server.Send(url, body, content_type=ctype) + lines = response_body.splitlines() + if not lines or lines[0] != "OK": + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + rv.append([lines[1], patch[0]]) + return rv + + +def GuessVCSName(options): + """Helper to guess the version control system. + + This examines the current directory, guesses which VersionControlSystem + we're using, and returns an string indicating which VCS is detected. + + Returns: + A pair (vcs, output). vcs is a string indicating which VCS was detected + and is one of VCS_GIT, VCS_MERCURIAL, VCS_SUBVERSION, VCS_PERFORCE, + VCS_CVS, or VCS_UNKNOWN. + Since local perforce repositories can't be easily detected, this method + will only guess VCS_PERFORCE if any perforce options have been specified. + output is a string containing any interesting output from the vcs + detection routine, or None if there is nothing interesting. + """ + for attribute, value in options.__dict__.iteritems(): + if attribute.startswith("p4") and value != None: + return (VCS_PERFORCE, None) + + def RunDetectCommand(vcs_type, command): + """Helper to detect VCS by executing command. + + Returns: + A pair (vcs, output) or None. Throws exception on error. + """ + try: + out, returncode = RunShellWithReturnCode(command) + if returncode == 0: + return (vcs_type, out.strip()) + except OSError, (errcode, message): + if errcode != errno.ENOENT: # command not found code + raise + + # Mercurial has a command to get the base directory of a repository + # Try running it, but don't die if we don't have hg installed. + # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. + res = RunDetectCommand(VCS_MERCURIAL, ["hg", "root"]) + if res != None: + return res + + # Subversion from 1.7 has a single centralized .svn folder + # ( see http://subversion.apache.org/docs/release-notes/1.7.html#wc-ng ) + # That's why we use 'svn info' instead of checking for .svn dir + res = RunDetectCommand(VCS_SUBVERSION, ["svn", "info"]) + if res != None: + return res + + # Git has a command to test if you're in a git tree. + # Try running it, but don't die if we don't have git installed. + res = RunDetectCommand(VCS_GIT, ["git", "rev-parse", + "--is-inside-work-tree"]) + if res != None: + return res + + # detect CVS repos use `cvs status && $? == 0` rules + res = RunDetectCommand(VCS_CVS, ["cvs", "status"]) + if res != None: + return res + + return (VCS_UNKNOWN, None) + + +def GuessVCS(options): + """Helper to guess the version control system. + + This verifies any user-specified VersionControlSystem (by command line + or environment variable). If the user didn't specify one, this examines + the current directory, guesses which VersionControlSystem we're using, + and returns an instance of the appropriate class. Exit with an error + if we can't figure it out. + + Returns: + A VersionControlSystem instance. Exits if the VCS can't be guessed. + """ + vcs = options.vcs + if not vcs: + vcs = os.environ.get("CODEREVIEW_VCS") + if vcs: + v = VCS_ABBREVIATIONS.get(vcs.lower()) + if v is None: + ErrorExit("Unknown version control system %r specified." % vcs) + (vcs, extra_output) = (v, None) + else: + (vcs, extra_output) = GuessVCSName(options) + + if vcs == VCS_MERCURIAL: + if extra_output is None: + extra_output = RunShell(["hg", "root"]).strip() + return MercurialVCS(options, extra_output) + elif vcs == VCS_SUBVERSION: + return SubversionVCS(options) + elif vcs == VCS_PERFORCE: + return PerforceVCS(options) + elif vcs == VCS_GIT: + return GitVCS(options) + elif vcs == VCS_CVS: + return CVSVCS(options) + + ErrorExit(("Could not guess version control system. " + "Are you in a working copy directory?")) + + +def CheckReviewer(reviewer): + """Validate a reviewer -- either a nickname or an email addres. + + Args: + reviewer: A nickname or an email address. + + Calls ErrorExit() if it is an invalid email address. + """ + if "@" not in reviewer: + return # Assume nickname + parts = reviewer.split("@") + if len(parts) > 2: + ErrorExit("Invalid email address: %r" % reviewer) + assert len(parts) == 2 + if "." not in parts[1]: + ErrorExit("Invalid email address: %r" % reviewer) + + +def LoadSubversionAutoProperties(): + """Returns the content of [auto-props] section of Subversion's config file as + a dictionary. + + Returns: + A dictionary whose key-value pair corresponds the [auto-props] section's + key-value pair. + In following cases, returns empty dictionary: + - config file doesn't exist, or + - 'enable-auto-props' is not set to 'true-like-value' in [miscellany]. + """ + if os.name == 'nt': + subversion_config = os.environ.get("APPDATA") + "\\Subversion\\config" + else: + subversion_config = os.path.expanduser("~/.subversion/config") + if not os.path.exists(subversion_config): + return {} + config = ConfigParser.ConfigParser() + config.read(subversion_config) + if (config.has_section("miscellany") and + config.has_option("miscellany", "enable-auto-props") and + config.getboolean("miscellany", "enable-auto-props") and + config.has_section("auto-props")): + props = {} + for file_pattern in config.options("auto-props"): + props[file_pattern] = ParseSubversionPropertyValues( + config.get("auto-props", file_pattern)) + return props + else: + return {} + +def ParseSubversionPropertyValues(props): + """Parse the given property value which comes from [auto-props] section and + returns a list whose element is a (svn_prop_key, svn_prop_value) pair. + + See the following doctest for example. + + >>> ParseSubversionPropertyValues('svn:eol-style=LF') + [('svn:eol-style', 'LF')] + >>> ParseSubversionPropertyValues('svn:mime-type=image/jpeg') + [('svn:mime-type', 'image/jpeg')] + >>> ParseSubversionPropertyValues('svn:eol-style=LF;svn:executable') + [('svn:eol-style', 'LF'), ('svn:executable', '*')] + """ + key_value_pairs = [] + for prop in props.split(";"): + key_value = prop.split("=") + assert len(key_value) <= 2 + if len(key_value) == 1: + # If value is not given, use '*' as a Subversion's convention. + key_value_pairs.append((key_value[0], "*")) + else: + key_value_pairs.append((key_value[0], key_value[1])) + return key_value_pairs + + +def GetSubversionPropertyChanges(filename): + """Return a Subversion's 'Property changes on ...' string, which is used in + the patch file. + + Args: + filename: filename whose property might be set by [auto-props] config. + + Returns: + A string like 'Property changes on |filename| ...' if given |filename| + matches any entries in [auto-props] section. None, otherwise. + """ + global svn_auto_props_map + if svn_auto_props_map is None: + svn_auto_props_map = LoadSubversionAutoProperties() + + all_props = [] + for file_pattern, props in svn_auto_props_map.items(): + if fnmatch.fnmatch(filename, file_pattern): + all_props.extend(props) + if all_props: + return FormatSubversionPropertyChanges(filename, all_props) + return None + + +def FormatSubversionPropertyChanges(filename, props): + """Returns Subversion's 'Property changes on ...' strings using given filename + and properties. + + Args: + filename: filename + props: A list whose element is a (svn_prop_key, svn_prop_value) pair. + + Returns: + A string which can be used in the patch file for Subversion. + + See the following doctest for example. + + >>> print FormatSubversionPropertyChanges('foo.cc', [('svn:eol-style', 'LF')]) + Property changes on: foo.cc + ___________________________________________________________________ + Added: svn:eol-style + + LF + + """ + prop_changes_lines = [ + "Property changes on: %s" % filename, + "___________________________________________________________________"] + for key, value in props: + prop_changes_lines.append("Added: " + key) + prop_changes_lines.append(" + " + value) + return "\n".join(prop_changes_lines) + "\n" + + +def RealMain(argv, data=None): + """The real main function. + + Args: + argv: Command line arguments. + data: Diff contents. If None (default) the diff is generated by + the VersionControlSystem implementation returned by GuessVCS(). + + Returns: + A 2-tuple (issue id, patchset id). + The patchset id is None if the base files are not uploaded by this + script (applies only to SVN checkouts). + """ + options, args = parser.parse_args(argv[1:]) + if options.help: + if options.verbose < 2: + # hide Perforce options + parser.epilog = "Use '--help -v' to show additional Perforce options." + parser.option_groups.remove(parser.get_option_group('--p4_port')) + parser.print_help() + sys.exit(0) + + global verbosity + verbosity = options.verbose + if verbosity >= 3: + logging.getLogger().setLevel(logging.DEBUG) + elif verbosity >= 2: + logging.getLogger().setLevel(logging.INFO) + + vcs = GuessVCS(options) + + base = options.base_url + if isinstance(vcs, SubversionVCS): + # Guessing the base field is only supported for Subversion. + # Note: Fetching base files may become deprecated in future releases. + guessed_base = vcs.GuessBase(options.download_base) + if base: + if guessed_base and base != guessed_base: + print "Using base URL \"%s\" from --base_url instead of \"%s\"" % \ + (base, guessed_base) + else: + base = guessed_base + + if not base and options.download_base: + options.download_base = True + logging.info("Enabled upload of base file") + if not options.assume_yes: + vcs.CheckForUnknownFiles() + if data is None: + data = vcs.GenerateDiff(args) + data = vcs.PostProcessDiff(data) + if options.print_diffs: + print "Rietveld diff start:*****" + print data + print "Rietveld diff end:*****" + files = vcs.GetBaseFiles(data) + if verbosity >= 1: + print "Upload server:", options.server, "(change with -s/--server)" + rpc_server = GetRpcServer(options.server, + options.email, + options.host, + options.save_cookies, + options.account_type) + form_fields = [] + + repo_guid = vcs.GetGUID() + if repo_guid: + form_fields.append(("repo_guid", repo_guid)) + if base: + b = urlparse.urlparse(base) + username, netloc = urllib.splituser(b.netloc) + if username: + logging.info("Removed username from base URL") + base = urlparse.urlunparse((b.scheme, netloc, b.path, b.params, + b.query, b.fragment)) + form_fields.append(("base", base)) + if options.issue: + form_fields.append(("issue", str(options.issue))) + if options.email: + form_fields.append(("user", options.email)) + if options.reviewers: + for reviewer in options.reviewers.split(','): + CheckReviewer(reviewer) + form_fields.append(("reviewers", options.reviewers)) + if options.cc: + for cc in options.cc.split(','): + CheckReviewer(cc) + form_fields.append(("cc", options.cc)) + + # Process --message, --title and --file. + message = options.message or "" + title = options.title or "" + if options.file: + if options.message: + ErrorExit("Can't specify both message and message file options") + file = open(options.file, 'r') + message = file.read() + file.close() + if options.issue: + prompt = "Title describing this patch set: " + else: + prompt = "New issue subject: " + title = ( + title or message.split('\n', 1)[0].strip() or raw_input(prompt).strip()) + if not title and not options.issue: + ErrorExit("A non-empty title is required for a new issue") + # For existing issues, it's fine to give a patchset an empty name. Rietveld + # doesn't accept that so use a whitespace. + title = title or " " + if len(title) > 100: + title = title[:99] + '…' + if title and not options.issue: + message = message or title + + form_fields.append(("subject", title)) + # If it's a new issue send message as description. Otherwise a new + # message is created below on upload_complete. + if message and not options.issue: + form_fields.append(("description", message)) + + # Send a hash of all the base file so the server can determine if a copy + # already exists in an earlier patchset. + base_hashes = "" + for file, info in files.iteritems(): + if not info[0] is None: + checksum = md5(info[0]).hexdigest() + if base_hashes: + base_hashes += "|" + base_hashes += checksum + ":" + file + form_fields.append(("base_hashes", base_hashes)) + if options.private: + if options.issue: + print "Warning: Private flag ignored when updating an existing issue." + else: + form_fields.append(("private", "1")) + if options.send_patch: + options.send_mail = True + if not options.download_base: + form_fields.append(("content_upload", "1")) + if len(data) > MAX_UPLOAD_SIZE: + print "Patch is large, so uploading file patches separately." + uploaded_diff_file = [] + form_fields.append(("separate_patches", "1")) + else: + uploaded_diff_file = [("data", "data.diff", data)] + ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) + response_body = rpc_server.Send("/upload", body, content_type=ctype) + patchset = None + if not options.download_base or not uploaded_diff_file: + lines = response_body.splitlines() + if len(lines) >= 2: + msg = lines[0] + patchset = lines[1].strip() + patches = [x.split(" ", 1) for x in lines[2:]] + else: + msg = response_body + else: + msg = response_body + StatusUpdate(msg) + if not response_body.startswith("Issue created.") and \ + not response_body.startswith("Issue updated."): + sys.exit(0) + issue = msg[msg.rfind("/")+1:] + + if not uploaded_diff_file: + result = UploadSeparatePatches(issue, rpc_server, patchset, data, options) + if not options.download_base: + patches = result + + if not options.download_base: + vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files) + + payload = {} # payload for final request + if options.send_mail: + payload["send_mail"] = "yes" + if options.send_patch: + payload["attach_patch"] = "yes" + if options.issue and message: + payload["message"] = message + payload = urllib.urlencode(payload) + rpc_server.Send("/" + issue + "/upload_complete/" + (patchset or ""), + payload=payload) + return issue, patchset + + +def main(): + try: + logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" + "%(lineno)s %(message)s ")) + os.environ['LC_ALL'] = 'C' + RealMain(sys.argv) + except KeyboardInterrupt: + print + StatusUpdate("Interrupted.") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/node_modules/connect-mongo/package.json b/node_modules/connect-mongo/package.json new file mode 100644 index 0000000..afb87de --- /dev/null +++ b/node_modules/connect-mongo/package.json @@ -0,0 +1,51 @@ +{ + "name": "connect-mongo", + "version": "0.3.2", + "description": "MongoDB session store for Connect", + "keywords": [ + "connect", + "mongo", + "mongodb", + "session", + "express" + ], + "author": { + "name": "Casey Banner", + "email": "kcbanner@gmail.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/kcbanner/connect-mongo.git" + }, + "bugs": { + "url": "https://github.com/kcbanner/connect-mongo/issues" + }, + "dependencies": { + "mongodb": "1.2.x" + }, + "directories": { + "lib": "./lib" + }, + "devDependencies": { + "mocha": "1.x", + "connect": ">= 1.x", + "mongoose": ">= 2.6.x" + }, + "scripts": { + "test": "make test" + }, + "main": "./lib/connect-mongo", + "engines": { + "node": "node >= 0.4.x" + }, + "_id": "connect-mongo@0.3.2", + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.21", + "_nodeVersion": "v0.6.18", + "_defaultsLoaded": true, + "dist": { + "shasum": "f77db97c25fabe4048e2de6784b7754f7ded9da9" + }, + "_from": "connect-mongo" +} diff --git a/routes/middleware/musclearray.js b/routes/middleware/musclearray.js new file mode 100644 index 0000000..4fcf679 --- /dev/null +++ b/routes/middleware/musclearray.js @@ -0,0 +1,25 @@ +var async = require('async'); + +function MuscleArray(workout) { + var dArray = []; + var sumArray = [0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + async.forEachSeries(workout.elements, function(exercise,callback){ + var i = workout.elements.indexOf(exercise); + dArray[i] = exercise.exerciseID.musclearray; + callback(); + }, + function(err){ + // + }); + + dArray.forEach(function(item,index) { + for(var i = 0; i < 6; i++) { + //var calculatedValue = dArray[index][i] * dArray[index][15] + var calculatedValue = dArray[index][i]; + sumArray[i] = calculatedValue + sumArray[i] + 0; + } + }); + return sumArray; +} + +module.exports = MuscleArray; \ No newline at end of file diff --git a/views/workouts/recent.jade b/views/workouts/recent.jade new file mode 100644 index 0000000..223ce4e --- /dev/null +++ b/views/workouts/recent.jade @@ -0,0 +1,7 @@ +h3 Recent workouts: + ul + - recentworkouts.forEach(function(workout) { + li + a.map-link(href="/workouts/" + encodeURIComponent(workout._id))= workout._id + - }); +