├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── deserialize.js ├── index.js ├── modes │ ├── index.js │ ├── log.js │ ├── shell.js │ └── strict.js ├── reviver.js ├── serialize.js └── types.js ├── package.json └── test ├── data.json ├── deserialize.test.js ├── integration.test.js ├── logmode.test.js ├── mocha.opts ├── parse.test.js ├── regressions.test.js ├── reviver.test.js ├── serialize.test.js ├── shellmode.test.js └── stringify.test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "extends": "mongodb-js/node" 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | npm-debug.log 4 | coverage/ 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | .eslintrc 3 | .travis.yml 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: bionic 3 | language: node_js 4 | node_js: 5 | - 12.4.0 6 | install: 7 | - npm ci 8 | script: 9 | - npm run check 10 | - npm test 11 | cache: npm -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 MongoDB Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This module is no longer being maintained. Please see: https://github.com/mongodb-js/mongodb-extjson 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); 2 | 3 | -------------------------------------------------------------------------------- /lib/deserialize.js: -------------------------------------------------------------------------------- 1 | var types = require('./types'); 2 | var strict = require('./modes/strict'); 3 | var isObject = types.isObject; 4 | var async = require('async'); 5 | var raf = require('raf'); 6 | 7 | /** 8 | * Cast values from extended json objects to their BSON or JS types. 9 | * 10 | * @param {Any} data - A value which might be esjon encoded. 11 | * @return {Any} 12 | * @api private 13 | */ 14 | module.exports = function deserialize(data) { 15 | if (Array.isArray(data)) { 16 | return data.map(deserialize); 17 | } 18 | if (!isObject(data)) { 19 | return data; 20 | } 21 | 22 | var keys = Object.keys(data); 23 | if (keys.length === 0) { 24 | return data; 25 | } 26 | 27 | var caster = strict.deserialize[keys[0]]; 28 | if (!caster) { 29 | return keys.reduce(function(schema, key) { 30 | schema[key] = deserialize(data[key]); 31 | return schema; 32 | }, {}); 33 | } 34 | 35 | return caster(data); 36 | }; 37 | 38 | function deserializeAsync(data, fn) { 39 | if (Array.isArray(data) === true) { 40 | async.series(data.map(function(doc) { 41 | return function(cb) { 42 | raf(function() { 43 | deserializeAsync(doc, cb); 44 | }); 45 | }; 46 | }), fn); 47 | } else if (isObject(data) === false) { 48 | fn(null, data); 49 | } else { 50 | var keys = Object.keys(data); 51 | if (keys.length === 0) { 52 | fn(null, data); 53 | } else { 54 | var caster = strict.deserialize[keys[0]]; 55 | if (caster) { 56 | fn(null, caster.call(null, data)); 57 | } else { 58 | var res = {}; 59 | async.series(keys.map(function(key) { 60 | return function(cb) { 61 | deserializeAsync(data[key], function(err, d) { 62 | if (err) { 63 | return cb(err); 64 | } 65 | res[key] = d; 66 | cb(); 67 | }); 68 | }; 69 | }), function(err) { 70 | if (err) { 71 | return fn(err); 72 | } 73 | fn(null, res); 74 | }); 75 | } 76 | } 77 | } 78 | } 79 | 80 | module.exports.async = deserializeAsync; 81 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var deserialize = require('./deserialize'); 2 | var serialize = require('./serialize'); 3 | var shell = require('./modes/shell'); 4 | var log = require('./modes/log'); 5 | var es = require('event-stream'); 6 | var JSONStream = require('JSONStream'); 7 | var raf = require('raf'); 8 | var util = require('util'); 9 | var format = util.format; 10 | var deprecate = util.deprecate; 11 | 12 | function preprocess(text, mode) { 13 | /* eslint indent:0 */ 14 | mode = mode || 'strict'; 15 | 16 | switch (mode) { 17 | case 'strict': return text; 18 | case 'shell': return shell.toStrict(text); 19 | case 'log': return log.toStrict(text); 20 | default: 21 | throw new Error( 22 | format('unknown mode `%s`. Use `strict` (default), `shell` or `log`.', mode) 23 | ); 24 | } 25 | } 26 | 27 | /** 28 | * parses a string in strict, shell or log mode extended JSON and returns 29 | * object with BSON values. 30 | * 31 | * @param {String} text string to parse 32 | * @param {Function} reviver callback function for custom parsing, @see ./reviver.js 33 | * @param {Enum} mode one of `strict`, `shell`, `log` 34 | * @return {Object} Object with native and/or BSON values 35 | */ 36 | module.exports.parse = function(text, reviver, mode) { 37 | var normalized = preprocess(text, mode); 38 | var parsed = JSON.parse(normalized, reviver); 39 | return deserialize(parsed); 40 | }; 41 | 42 | /** 43 | * stringifies an object with native and/or BSON values back into 44 | * strict extended JSON. 45 | * 46 | * @param {Object} value Object or value to stringify 47 | * @param {Function|Array} replacer Custom replacement 48 | * @param {Number|String} space Custom spacing 49 | * @return {String} JSON representation of value 50 | * 51 | * @see http://mzl.la/1fms8sL JSON.stringify() documentation 52 | */ 53 | module.exports.stringify = function(value, replacer, space) { 54 | return JSON.stringify(serialize(value), replacer, space); 55 | }; 56 | 57 | module.exports.deserialize = deserialize; 58 | module.exports.serialize = serialize; 59 | module.exports.reviver = require('./reviver'); 60 | 61 | // Backwards compat for 1.x 62 | module.exports.deflate = deprecate(deserialize, 63 | 'mongodb-extended-json#deflate: Use deserialize(obj) instead'); 64 | 65 | module.exports.inflate = deprecate(serialize, 66 | 'mongodb-extended-json#inflate: Use serialize(obj) instead'); 67 | 68 | // JSONStream.stringify 69 | module.exports.createStringifyStream = function(op, sep, cl, indent) { 70 | indent = indent || 0; 71 | if (op === false) { 72 | op = ''; 73 | sep = '\n'; 74 | cl = ''; 75 | } else if (op === null || op === undefined) { 76 | op = '[\n'; 77 | sep = '\n,\n'; 78 | cl = '\n]\n'; 79 | } 80 | 81 | // else, what ever you like 82 | 83 | var first = true; 84 | var anyData = false; 85 | 86 | return es.through(function(data) { 87 | anyData = true; 88 | var json = module.exports.stringify(data, null, indent); 89 | if (first) { 90 | first = false; 91 | this.emit('data', op + json); 92 | } else { 93 | this.emit('data', sep + json); 94 | } 95 | }, function() { 96 | if (!anyData) { 97 | this.emit('data', op); 98 | } 99 | this.emit('data', cl); 100 | this.emit('end'); 101 | }); 102 | }; 103 | 104 | module.exports.createParseStream = function(path, map) { 105 | var parser = JSONStream.parse(path, map); 106 | var wrapper = es.through(function(data) { 107 | raf(function() { 108 | parser.write(data); 109 | }); 110 | }, function() { 111 | this.emit('end'); 112 | }); 113 | 114 | var emit = function ejsonParseEmit(data) { 115 | wrapper.emit('data', data); 116 | }; 117 | 118 | parser.on('data', function(data) { 119 | deserialize.async(data, function(_, parsed) { 120 | if (!Array.isArray(parsed)) { 121 | emit(parsed); 122 | } else { 123 | for (var i = 0; i < parsed.length; i++) { 124 | emit(parsed[i]); 125 | } 126 | } 127 | }); 128 | }) 129 | .on('error', function(err) { 130 | wrapper.emit('error', err); 131 | }) 132 | .on('end', function() { 133 | wrapper.emit('end'); 134 | }); 135 | return wrapper; 136 | }; 137 | -------------------------------------------------------------------------------- /lib/modes/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | strict: require('./strict'), 3 | log: require('./log'), 4 | shell: require('./shell') 5 | }; 6 | -------------------------------------------------------------------------------- /lib/modes/log.js: -------------------------------------------------------------------------------- 1 | var moment = require('moment'); 2 | 3 | // @todo (kangas) Remove need for eslint overrride 4 | /* eslint no-cond-assign:0 */ 5 | 6 | function toStrictQuotes(str) { 7 | return str 8 | // wrap field names in double quotes 9 | .replace(/([{,])\s*([^,{\s\'"]+)\s*:/g, '$1 "$2":'); 10 | } 11 | 12 | function toStrictSimple(str) { 13 | return str 14 | // Timestamps 15 | .replace(/Timestamp (\d+)\|(\d+)/g, '{ "$timestamp": { "t": $1, "i": $2 } }') 16 | // MinKey 17 | .replace(/MinKey/g, '{ "$minKey": 1 }') 18 | // MaxKey 19 | .replace(/MaxKey/g, '{ "$maxKey": 1 }') 20 | // ObjectIds 21 | .replace(/ObjectId\('([0-9abcdef]{24})'\)/g, '{ "$oid": "$1" }'); 22 | } 23 | 24 | function toStrictNumberLong(str) { 25 | var match = str.match(/\s\d{10,}/g); 26 | if (!match) { 27 | return str; 28 | } 29 | match.forEach(function(m) { 30 | var n = m.trim(); 31 | if (+n > 2147483647) { 32 | str = str.replace(n, '{ "$numberLong": "' + n + '" }'); 33 | } 34 | }); 35 | return str; 36 | } 37 | 38 | function toStrictRegEx(str) { 39 | var regex = /([,:]\s*)\/(.+?)\/([gims]{0,4})(\s+)/g; 40 | var match; 41 | 42 | while ((match = regex.exec(str)) !== null) { 43 | var m2 = match[2].replace(/"/g, '"'); 44 | str = str.replace(match[0], match[1] + '{ "$regex": "' + m2 + '", "$options": "' 45 | + match[3] + '" }' + match[4]); 46 | } 47 | return str; 48 | } 49 | 50 | function toStrictDate(str) { 51 | var regex = /new Date\((\d+)\)/g; 52 | var match; 53 | while ((match = regex.exec(str)) !== null) { 54 | var t = moment(parseInt(match[1], 10)); 55 | str = str.replace(match[0], '{ "$date": "' + t.toISOString() + '" }'); 56 | } 57 | return str; 58 | } 59 | 60 | function toStrictBinData(str) { 61 | var regex = /BinData\((\d+), ([0-9ABCDEF]+)\)/g; 62 | var match; 63 | while ((match = regex.exec(str)) !== null) { 64 | // convert hex to base64 65 | var hex = new Buffer(match[2], 'hex').toString('base64'); 66 | str = str.replace(match[0], '{ "$binary": "' + hex + '", "$type": "' + match[1] + '" }'); 67 | } 68 | return str; 69 | } 70 | 71 | module.exports.toStrict = function(str) { 72 | str = toStrictQuotes(str); 73 | str = toStrictSimple(str); 74 | str = toStrictNumberLong(str); 75 | str = toStrictRegEx(str); 76 | str = toStrictDate(str); 77 | str = toStrictBinData(str); 78 | return str; 79 | }; 80 | -------------------------------------------------------------------------------- /lib/modes/shell.js: -------------------------------------------------------------------------------- 1 | var format = require('util').format; 2 | 3 | // @todo (kangas) Remove need for eslint overrride 4 | /* eslint no-cond-assign:0 */ 5 | 6 | function toStrictQuotes(str) { 7 | return ( 8 | str 9 | // replace single quotes with double quotes 10 | .replace(/'/g, '"') 11 | // wrap field names in double quotes 12 | .replace(/([{,])\s*([^,{\s\'"]+)\s*:/g, '$1 "$2":') 13 | ); 14 | } 15 | 16 | function toStrictSimple(str) { 17 | return ( 18 | str 19 | // Timestamps 20 | .replace( 21 | /Timestamp\((\d+), (\d+)\)/g, 22 | '{ "$timestamp": { "t": $1, "i": $2 } }' 23 | ) 24 | 25 | // ObjectIds 26 | .replace(/ObjectId\("([0-9abcdef]{24})"\)/g, '{ "$oid": "$1" }') 27 | // NumberLong 28 | .replace(/NumberLong\("?([0-9]+)"?\)/g, '{ "$numberLong": "$1" }') 29 | // numberDecimal 30 | .replace(/NumberDecimal\("([0-9.]+)"\)/g, '{ "$numberDecimal": "$1" }') 31 | // Date also prints the wrong format, 32 | // @see https://jira.mongodb.org/browse/SERVER-19171 33 | .replace(/ISODate\("(.+?)"\)/g, '{ "$date": "$1" }') 34 | // DBRef 35 | .replace(/DBRef\("(.+?)", (.+?)\)/g, function(match, ns, id) { 36 | id = toStrictSimple(id); 37 | return '{ "$ref": "' + ns + '", "$id": ' + id + ' }'; 38 | }) 39 | // undefined, shell is buggy here too, 40 | // @see https://jira.mongodb.org/browse/SERVER-6102 41 | .replace('undefined', '{ "$undefined": true }') 42 | ); 43 | } 44 | 45 | function toStrictRegEx(str) { 46 | var regex = /([,:]\s*)\/(.+?)\/([gims]{0,4})(\s+)/g; 47 | var match; 48 | 49 | while ((match = regex.exec(str)) !== null) { 50 | var m2 = match[2].replace(/"/g, '"'); 51 | str = str.replace( 52 | match[0], 53 | format( 54 | '%s{ "$regex": "%s", "$options": "%s" }%s', 55 | match[1], 56 | m2, 57 | match[3], 58 | match[4] 59 | ) 60 | ); 61 | } 62 | return str; 63 | } 64 | 65 | function toStrictBinData(str) { 66 | var regex = /BinData\((\d+),"(.+?)"\)/g; 67 | var match; 68 | while ((match = regex.exec(str)) !== null) { 69 | var hex = parseInt(match[1], 10).toString(16); 70 | str = str.replace( 71 | match[0], 72 | '{ "$binary": "' + match[2] + '", "$type": "' + hex + '" }' 73 | ); 74 | } 75 | return str; 76 | } 77 | 78 | module.exports.toStrict = function(str) { 79 | str = toStrictQuotes(str); 80 | str = toStrictSimple(str); 81 | str = toStrictRegEx(str); 82 | str = toStrictBinData(str); 83 | return str; 84 | }; 85 | 86 | /** 87 | * Below definitions are currently not used, stringification back to shell mode 88 | * is not yet supported. We leave them here for future reference though. 89 | */ 90 | module.exports.serialize = { 91 | ObjectID: function(v) { 92 | return format('ObjectId("%s")', v.toString()); 93 | }, 94 | Timestamp: function(v) { 95 | return format('Timestamp(%d, %d)', v.low_, v.high_); 96 | }, 97 | MinKey: function(v) { 98 | return v; 99 | }, 100 | MaxKey: function(v) { 101 | return v; 102 | }, 103 | NumberLong: function(v) { 104 | return format('NumberLong(%d)', v); 105 | }, 106 | Date: function(v) { 107 | return format('ISODate("%s")', v.toISOString()); 108 | }, 109 | DBRef: function(v) { 110 | var id; 111 | 112 | if ( 113 | typeof v.oid === 'object' && 114 | v.oid !== null && 115 | (module.exports.serialize[v.oid._bsontype] || module.exports.serialize[v.oid.constructor.name]) 116 | ) { 117 | id = (module.exports.serialize[v.oid._bsontype] || module.exports.serialize[v.oid.constructor.name])(v.oid); 118 | } else if (typeof v.oid === 'string') { 119 | id = '"' + v.oid + '"'; 120 | } else { 121 | id = v.oid; 122 | } 123 | 124 | return format('DBRef("%s", %s)', v.namespace, id); 125 | }, 126 | Undefined: function() { 127 | return 'undefined'; 128 | }, 129 | RegExp: function(v) { 130 | var o = ''; 131 | 132 | if (v.global) { 133 | o += 'g'; 134 | } 135 | if (v.ignoreCase) { 136 | o += 'i'; 137 | } 138 | if (v.multiline) { 139 | o += 'm'; 140 | } 141 | return format('/%s/%s', v.source, o); 142 | }, 143 | Binary: function(v) { 144 | return format( 145 | 'BinData(%s, "%s")', 146 | v.sub_type.toString(10), 147 | v.buffer.toString('base64') 148 | ); 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /lib/modes/strict.js: -------------------------------------------------------------------------------- 1 | var bson = require('bson'); 2 | 3 | /** 4 | * Map of all extended types to serializers (stringify) and deserializers (parse). 5 | * 6 | * @api private 7 | */ 8 | module.exports = { 9 | serialize: { 10 | Code: function(v) { 11 | if (v.scope) { 12 | return { 13 | $code: v.code, 14 | $scope: v.scope 15 | }; 16 | } 17 | return { 18 | $code: v.code 19 | }; 20 | }, 21 | ObjectID: function(v) { 22 | return { 23 | $oid: v.toHexString() 24 | }; 25 | }, 26 | Binary: function(v) { 27 | return { 28 | $binary: v.buffer.toString('base64'), 29 | $type: v.sub_type.toString(16) 30 | }; 31 | }, 32 | DBRef: function(v) { 33 | var id = typeof v.oid === 'object' && v.oid !== null && 34 | (module.exports.serialize[v.oid._bsontype] || module.exports.serialize[v.oid.constructor.name]) ? 35 | (module.exports.serialize[v.oid._bsontype] || module.exports.serialize[v.oid.constructor.name])(v.oid) 36 | : v.oid; 37 | return { 38 | $ref: v.namespace, 39 | $id: id 40 | }; 41 | }, 42 | Timestamp: function(v) { 43 | return { 44 | $timestamp: { 45 | t: v.low_, 46 | i: v.high_ 47 | } 48 | }; 49 | }, 50 | Long: function(v) { 51 | return { 52 | $numberLong: v.toString() 53 | }; 54 | }, 55 | Decimal128: function(v) { 56 | return { 57 | $numberDecimal: v.toString() 58 | }; 59 | }, 60 | MaxKey: function() { 61 | return { 62 | $maxKey: 1 63 | }; 64 | }, 65 | MinKey: function() { 66 | return { 67 | $minKey: 1 68 | }; 69 | }, 70 | Date: function(v) { 71 | var timestamp = v.getTime(); 72 | if (timestamp >= 32535215999000) { 73 | return { 74 | $date: { 75 | $numberLong: '' + timestamp 76 | } 77 | }; 78 | } 79 | 80 | return { 81 | $date: v.toISOString() 82 | }; 83 | }, 84 | RegExp: function(v) { 85 | var o = ''; 86 | 87 | if (v.global) { 88 | o += 'g'; 89 | } 90 | if (v.ignoreCase) { 91 | o += 'i'; 92 | } 93 | if (v.multiline) { 94 | o += 'm'; 95 | } 96 | 97 | return { 98 | $regex: v.source, 99 | $options: o 100 | }; 101 | }, 102 | Undefined: function() { 103 | return { 104 | $undefined: true 105 | }; 106 | } 107 | }, 108 | /* eslint new-cap:0 */ 109 | deserialize: { 110 | $code: function(code) { 111 | return bson.Code(code.$code, code.$scope); 112 | }, 113 | $oid: function(data) { 114 | return bson.ObjectID(data.$oid); 115 | }, 116 | $binary: function(val) { 117 | return new bson.Binary(new Buffer(val.$binary, 'base64'), parseInt(val.$type, 16)); 118 | }, 119 | $ref: function(val) { 120 | var id = typeof val.$id === 'object' 121 | && module.exports.deserialize[Object.keys(val.$id)[0]] ? module.exports.deserialize[Object.keys(val.$id)[0]](val.$id) 122 | : val.$id; 123 | return bson.DBRef(val.$ref, id); 124 | }, 125 | $timestamp: function(val) { 126 | return bson.Timestamp(val.$timestamp.t, val.$timestamp.i); 127 | }, 128 | $numberLong: function(val) { 129 | return bson.Long.fromString(val.$numberLong); 130 | }, 131 | $numberDecimal: function(val) { 132 | return bson.Decimal128.fromString(val.$numberDecimal); 133 | }, 134 | $maxKey: function() { 135 | return bson.MaxKey(); 136 | }, 137 | $minKey: function() { 138 | return bson.MinKey(); 139 | }, 140 | $date: function(val) { 141 | var d = new Date(); 142 | var v; 143 | 144 | if (typeof val.$date === 'object') { 145 | v = val.$date.$numberLong; 146 | } else { 147 | v = val.$date; 148 | } 149 | // Kernel bug. See #2 http://git.io/AEbmFg 150 | if (isNaN(d.setTime(v))) { 151 | d = new Date(v); 152 | } 153 | return d; 154 | }, 155 | $regex: function(val) { 156 | return new RegExp(val.$regex, val.$options); 157 | }, 158 | $undefined: function() { 159 | return undefined; 160 | } 161 | } 162 | }; 163 | -------------------------------------------------------------------------------- /lib/reviver.js: -------------------------------------------------------------------------------- 1 | var types = require('./types'); 2 | var deserialize = require('./deserialize'); 3 | 4 | /** 5 | * Similar to [object_hook][object_hook] in python, ES5 JSON.parse accepts 6 | * a very not well-known [reviver][reviver] argument. This can be used to 7 | * easily plug in to existing middleware like [body-parser][body-parser] 8 | * for express: 9 | * 10 | * ```javascript 11 | * var EJSON = require('mongodb-extended-json'), 12 | * bodyParser = require('body-parser'), 13 | * app = require('express')(); 14 | * 15 | * app.post('/save', bodyParser.json({reviver: EJSON.reviver}), function(req, res){ 16 | * console.log('omg now it has extended types', req.body); 17 | * // Do stuff 18 | * }); 19 | * ``` 20 | * 21 | * [object_hook]: https://docs.python.org/2/library/json.html#json.load 22 | * [reviver]: http://mdn.io/json.parse#Example.3A_Using_the_reviver_parameter 23 | * [body-parser]: http://npm.im/body-parser 24 | * 25 | * @param {String} k - The key in the object. 26 | * @param {Any} v - Contents of `k`. 27 | * @return {Any} 28 | */ 29 | module.exports = function reviver(k, v) { 30 | if (!types.isObject(v)) { 31 | return v; 32 | } 33 | var firstKey = Object.keys(v)[0]; 34 | 35 | if (!firstKey || types.special.keys.indexOf(firstKey) === -1) { 36 | return v; 37 | } 38 | return deserialize(v); 39 | }; 40 | -------------------------------------------------------------------------------- /lib/serialize.js: -------------------------------------------------------------------------------- 1 | var strict = require('./modes/strict'); 2 | var types = require('./types'); 3 | var isObject = types.isObject; 4 | var isFunction = require('lodash.isfunction'); 5 | var transform = require('lodash.transform'); 6 | var type = types.type; 7 | 8 | /* eslint no-use-before-define:0 */ 9 | 10 | function serializeArray(arr) { 11 | return arr.map(serialize.bind(null)); 12 | } 13 | 14 | function serializeTransformer(res, val, key) { 15 | res[key] = serialize(val); 16 | return res; 17 | } 18 | 19 | function serializeObject(obj) { 20 | var value = obj; 21 | if (isFunction(obj.serialize)) { 22 | value = obj.serialize(); 23 | } 24 | return transform(value, serializeTransformer, {}); 25 | } 26 | 27 | function serializePrimitive(value) { 28 | var t = type(value); 29 | if (strict.serialize.hasOwnProperty(t) === false) { 30 | return value; 31 | } 32 | 33 | var caster = strict.serialize[t]; 34 | return caster(value); 35 | } 36 | 37 | /** 38 | * Format values with extra extended json type data. 39 | * 40 | * @param {Any} value - What to wrap as a `{$: }` object. 41 | * @return {Any} 42 | * @api private 43 | */ 44 | function serialize(value) { 45 | if (Array.isArray(value) === true) { 46 | return serializeArray(value); 47 | } 48 | if (isObject(value) === false) { 49 | return serializePrimitive(value); 50 | } 51 | return serializeObject(value); 52 | } 53 | 54 | module.exports = serialize; 55 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | var strict = require('./modes/strict'); 2 | 3 | var OBJECT_REGEX = /\[object (\w+)\]/; 4 | /** 5 | * Gets the BSON or JS type of a value. 6 | * 7 | * @param {Any} value - Value to check. 8 | * @return {String} 9 | * @api private 10 | */ 11 | module.exports.type = function type(value) { 12 | if (value && value._bsontype) { 13 | return value._bsontype; 14 | } 15 | return OBJECT_REGEX.exec(Object.prototype.toString.call(value))[1]; 16 | }; 17 | 18 | /** 19 | * @api private 20 | */ 21 | module.exports.special = { 22 | types: Object.keys(strict.serialize), 23 | keys: Object.keys(strict.deserialize) 24 | }; 25 | 26 | /** 27 | * @param {Any} value - Value to check. 28 | * @return {Boolean} 29 | * @api private 30 | */ 31 | module.exports.isSpecial = function isSpecial(value) { 32 | return module.exports.special.types.indexOf(module.exports.type(value)) > -1; 33 | }; 34 | 35 | /** 36 | * @param {Any} value - Value to check. 37 | * @return {Boolean} 38 | * @api private 39 | */ 40 | module.exports.isObject = function isObject(value) { 41 | return module.exports.type(value) === 'Object'; 42 | }; 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongodb-extended-json", 3 | "description": "MongoDB Extended JSON.", 4 | "version": "1.11.1", 5 | "author": "Lucas Hrabovsky (http://imlucas.com)", 6 | "license": "Apache-2.0", 7 | "homepage": "http://github.com/mongodb-js/extended-json", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/mongodb-js/extended-json.git" 11 | }, 12 | "keywords": [ 13 | "mongodb", 14 | "mongodb-js", 15 | "ejson", 16 | "bson", 17 | "extended-json" 18 | ], 19 | "scripts": { 20 | "test": "mocha", 21 | "check": "mongodb-js-precommit" 22 | }, 23 | "dependencies": { 24 | "JSONStream": "^1.1.1", 25 | "async": "^3.1.0", 26 | "bson": "^1.0.1", 27 | "event-stream": "^4.0.1", 28 | "lodash.isfunction": "^3.0.6", 29 | "lodash.transform": "^4.6.0", 30 | "moment": "^2.10.6", 31 | "raf": "^3.1.0" 32 | }, 33 | "devDependencies": { 34 | "debug": "^4.1.1", 35 | "eslint-config-mongodb-js": "^5.0.3", 36 | "mocha": "~7.0.0", 37 | "mongodb-js-precommit": "^2.2.0", 38 | "mongodb-schema": "^8.2.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"_id":{"$oid":"54a1e4c798c1120000bb8b0f"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419896002170},"summary":{"all_nodes":{"op_median":9538.4,"op_throughput":9538.4},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419896007183},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 3 | , 4 | {"_id":{"$oid":"54a1e42f94042000008ac2f3"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895850459},"summary":{"all_nodes":{"op_median":9394.8,"op_throughput":9394.8},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895855471},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 5 | , 6 | {"_id":{"$oid":"54a1e3e68f038b0000631fe2"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895777372},"summary":{"all_nodes":{"op_median":9127.4,"op_throughput":9127.4},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895782378},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 7 | , 8 | {"_id":{"$oid":"54a1e3bc124968000052af15"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895735862},"summary":{"all_nodes":{"op_median":14459.199999999999,"op_throughput":14459.199999999999},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895740868},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 9 | , 10 | {"_id":{"$oid":"54a1e3a8124968000052af14"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895715752},"summary":{"all_nodes":{"op_median":14723.199999999999,"op_throughput":14723.199999999999},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895720756},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 11 | , 12 | {"_id":{"$oid":"54a1e2117b93d000002ee9bb"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895308723},"summary":{"all_nodes":{"op_median":9210.6,"op_throughput":9210.6},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895313732},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 13 | , 14 | {"_id":{"$oid":"54a1e1d6360382000019c110"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895249485},"summary":{"all_nodes":{"op_median":14875.8,"op_throughput":14875.8},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895254493},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 15 | , 16 | {"_id":{"$oid":"54a1e12ea5a5bb0000561dda"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419895081882},"summary":{"all_nodes":{"op_median":14293.8,"op_throughput":14293.8},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419895086888},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 17 | , 18 | {"_id":{"$oid":"54a1e0c14f3dc50000ba1afc"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419894972766},"summary":{"all_nodes":{"op_median":9009.4,"op_throughput":9009.4},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419894977776},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 19 | , 20 | {"_id":{"$oid":"54a1df90edbfd100001943b3"},"server_git_hash":"2d679247f17dab05a492c8b6d2c250dab18e54f2","workload":"Insert.Empty","errors":[],"start_time":{"$date":1419894667657},"summary":{"all_nodes":{"op_median":12486,"op_throughput":12486},"nodes":null},"test_run_time":5,"server_stats":null,"harness":"mongo-perf","end_time":{"$date":1419894672662},"testbed":{"type":"standalone","servers":{"mongod":[{"storageengine":{"name":"mmapv1"},"hostinfo":{"os":{"type":"Darwin"},"system":{"hostname":"localhost"},"extra":{}},"serverinfo":{"ok":1,"OpenSSLVersion":"","sysInfo":"Darwin bs-osx108-2 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 29 13:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49","javascriptEngine":"V8","version":"2.8.0-rc3","gitVersion":"2d679247f17dab05a492c8b6d2c250dab18e54f2","versionArray":[2,8,0,-7],"debug":false,"compilerFlags":"-Wnon-virtual-dtor -Woverloaded-virtual -stdlib=libc++ -std=c++11 -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -pipe -Werror -O3 -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-null-conversion -mmacosx-version-min=10.7 -std=c99","maxBsonObjectSize":16777216,"bits":64,"loaderFlags":"-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.7 -stdlib=libc++","allocator":"system"}}]}},"attributes":{"multiColl":1,"writeOptions":{"writeCmdMode":"true","writeConcernW":0,"safeGLE":"false","writeConcernJ":"false"},"shard":0,"standardDeviation":0,"label":"mytest","trialTime":5,"testfiles":["testcases/simple_insert.js"],"multidb":1,"nThread":1},"test_driver":{"build_date":"","git_hash":"844bd31df5e6988d2c42835db9639c1df54c044c","version":"2.7.3-pre-"},"server_version":"2.8.0-rc3","commit":"2d679247f17dab05a492c8b6d2c250dab18e54f2","committed_at":"2014-12-18T15:37:31Z"} 21 | ] 22 | -------------------------------------------------------------------------------- /test/deserialize.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var deserialize = require('../').deserialize; 3 | var bson = require('bson'); 4 | 5 | /* eslint new-cap:0 */ 6 | describe('Deserialize', function() { 7 | var _id = bson.ObjectID(); 8 | var bin = bson.Binary(new Buffer(1)); 9 | var ref = bson.DBRef('local.startup_log', _id); 10 | var refStringId = bson.DBRef('local.startup_log', _id.toString()); 11 | var code = bson.Code('return true'); 12 | var codeWithScope = bson.Code('return true', {}); 13 | 14 | it('converts `{$numberLong: }` to `bson.Long`', function() { 15 | assert( 16 | deserialize({ 17 | $numberLong: '10' 18 | }).equals(bson.Long.fromString('10')) 19 | ); 20 | }); 21 | 22 | it('converts `{$numberLong: }` to `bson.Long` (between 2^32 and 2^53)', function() { 23 | assert( 24 | deserialize({ 25 | $numberLong: '4294967297' 26 | }).equals(bson.Long.fromString('4294967297')) 27 | ); 28 | }); 29 | 30 | it('converts `{$numberLong: }` to `bson.Long` (greater than 2^53)', function() { 31 | assert.ok(deserialize({ $numberLong: '1234567903' }) instanceof bson.Long); 32 | assert( 33 | deserialize({ 34 | $numberLong: '18014398509481984' 35 | }).equals(bson.Long.fromString('18014398509481984')) 36 | ); 37 | }); 38 | 39 | it('converts `{$numberDecimal: }` to `bson.Decimal128`', function() { 40 | assert.ok( 41 | deserialize({ $numberDecimal: '1234.567' }) instanceof bson.Decimal128 42 | ); 43 | assert.equal( 44 | deserialize({ $numberDecimal: '1234.567' }).toString(), 45 | bson.Decimal128.fromString('1234.567').toString() 46 | ); 47 | }); 48 | 49 | it('converts `{$oid: <_id>}` to `bson.ObjectId`', function() { 50 | assert.deepEqual( 51 | deserialize({ 52 | $oid: _id.toString() 53 | }), 54 | _id 55 | ); 56 | }); 57 | 58 | it('converts `{$binary: }` to `bson.Binary`', function() { 59 | assert.equal( 60 | deserialize({ 61 | $binary: bin.buffer.toString('base64') 62 | }).toString('base64'), 63 | bin.buffer.toString('base64') 64 | ); 65 | }); 66 | 67 | it('converts `{$code: }` to `bson.Code`', function() { 68 | assert.equal( 69 | deserialize({ 70 | $code: code.code 71 | }).code, 72 | code.code 73 | ); 74 | }); 75 | 76 | it('converts `{$code: , $scope: }` to `bson.Code`', function() { 77 | assert.equal( 78 | deserialize({ 79 | $code: codeWithScope.code, 80 | $scope: codeWithScope.scope 81 | }).scope, 82 | codeWithScope.scope 83 | ); 84 | }); 85 | 86 | it('converts `{$ref: , $id: }` to `bson.DBRef`', function() { 87 | assert.deepEqual( 88 | deserialize({ 89 | $ref: 'local.startup_log', 90 | $id: _id.toString() 91 | }).toString(), 92 | refStringId.toString() 93 | ); 94 | }); 95 | 96 | it('converts `{$ref: , $id: }` to `bson.DBRef`', function() { 97 | assert.deepEqual( 98 | deserialize({ 99 | $ref: 'local.startup_log', 100 | $id: { 101 | $oid: _id.toString() 102 | } 103 | }).toString(), 104 | ref.toString() 105 | ); 106 | }); 107 | 108 | it('converts `{$timestamp: {$t: , $i: }` to `bson.Timestamp`', function() { 109 | assert.deepEqual( 110 | deserialize({ 111 | $timestamp: { 112 | $t: 0, 113 | $i: 0 114 | } 115 | }), 116 | bson.Timestamp() 117 | ); 118 | }); 119 | 120 | it('converts `{$minKey: 1}` to `bson.MinKey`', function() { 121 | assert.deepEqual( 122 | deserialize({ 123 | $minKey: 1 124 | }), 125 | bson.MinKey() 126 | ); 127 | }); 128 | 129 | it('converts `{$maxKey: 1}` to `bson.MaxKey`', function() { 130 | assert.deepEqual( 131 | deserialize({ 132 | $maxKey: 1 133 | }), 134 | bson.MaxKey() 135 | ); 136 | }); 137 | 138 | it('converts `{$date: }` to `Date`', function() { 139 | var d = new Date(); 140 | assert.deepEqual( 141 | deserialize({ 142 | $date: d.getTime() 143 | }), 144 | d 145 | ); 146 | }); 147 | 148 | it('converts `{$date: }` to `Date`', function() { 149 | var d = new Date(); 150 | assert.deepEqual( 151 | deserialize({ 152 | $date: d.toISOString() 153 | }), 154 | d 155 | ); 156 | }); 157 | 158 | it('converts `{$date: {$numberLong: }}` to `Date`', function() { 159 | var d = new Date(); 160 | assert.deepEqual( 161 | deserialize({ 162 | $date: { 163 | $numberLong: '' + d.getTime() 164 | } 165 | }), 166 | d 167 | ); 168 | }); 169 | 170 | it('converts `{$regex: , $options: }` to `RegExp`', function() { 171 | assert.deepEqual( 172 | deserialize({ 173 | $regex: 'mongodb.com$', 174 | $options: 'g' 175 | }).toString(), 176 | '/mongodb.com$/g' 177 | ); 178 | }); 179 | 180 | it('converts `{$undefined: true}` to `undefined`', function() { 181 | assert.deepEqual( 182 | deserialize({ 183 | $undefined: true 184 | }), 185 | undefined 186 | ); 187 | }); 188 | 189 | it('DOCS-3879: converts `{$date: }` to a proper date', function() { 190 | assert.equal( 191 | deserialize({ 192 | $date: '2014-08-25T17:49:42.288-0400' 193 | }).toUTCString(), 194 | 'Mon, 25 Aug 2014 21:49:42 GMT' 195 | ); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /test/integration.test.js: -------------------------------------------------------------------------------- 1 | var getSchema = require('mongodb-schema'); 2 | var assert = require('assert'); 3 | var BSON = require('bson'); 4 | var EJSON = require('../'); 5 | 6 | /* eslint new-cap: 0, quote-props: 0 */ 7 | describe('Important integrations we dont want to break', function() { 8 | describe('mongodb-schema', function() { 9 | var schema; 10 | var docs = [ 11 | { 12 | _id: BSON.ObjectID('55e6484748f15136d28b6e76') 13 | }, 14 | { 15 | _id: BSON.ObjectID('55f0a1ca62510c0b042b59e8'), 16 | arr1: [1, 2, 3], 17 | arr2: [true, false], 18 | arr3: [{ 19 | c: 1, 20 | d: 2 21 | }, { 22 | c: 1, 23 | e: 3 24 | }, { 25 | e: 3 26 | }], 27 | doc: { 28 | a: 1, 29 | b: 2 30 | }, 31 | x: 1 32 | }, 33 | { 34 | _id: BSON.ObjectID('55f0a1ca62510c0b042b59e9'), 35 | arr1: [1, 2, 3], 36 | arr2: [true, false], 37 | arr3: [{ 38 | c: 1, 39 | d: 2 40 | }, { 41 | c: 1, 42 | e: 3 43 | }, { 44 | e: 3 45 | }], 46 | doc: { 47 | a: 1, 48 | b: 2 49 | }, 50 | x: 1 51 | }, 52 | { 53 | _id: BSON.ObjectID('55f0a1ca62510c0b042b59ea'), 54 | arr1: [1, 2, 3], 55 | arr2: [true, false], 56 | arr3: [{ 57 | c: 1, 58 | d: 2 59 | }, { 60 | c: 1, 61 | e: 3 62 | }, { 63 | e: 3 64 | }], 65 | doc: { 66 | a: 1, 67 | b: 2 68 | }, 69 | x: 1 70 | } 71 | ]; 72 | 73 | before(function(done) { 74 | getSchema(docs, function(err, result) { 75 | assert.equal(err, null); 76 | schema = result; 77 | done(); 78 | }); 79 | }); 80 | it('should work with regular JSON.stringify', function() { 81 | assert.ok(JSON.stringify(schema)); 82 | }); 83 | it('should work with EJSON.stringify', function() { 84 | assert.doesNotThrow(function() { 85 | EJSON.stringify(schema); 86 | }, /maximum call stack size exceeded/i); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/logmode.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var logToStrict = require('../lib/modes/log.js').toStrict; 3 | var parse = require('../').parse; 4 | var bson = require('bson'); 5 | 6 | describe('Parse from log mode', function() { 7 | /* eslint new-cap:0 */ 8 | it('should work on a simple example with ObjectId', function() { 9 | assert.deepEqual(parse('{"_id": ObjectId(\'53c2b570c15c457669f481f7\') }', null, 'log'), { 10 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 11 | }); 12 | }); 13 | 14 | it('should work without quotes around field names', function() { 15 | assert.deepEqual(parse('{_id: ObjectId(\'53c2b570c15c457669f481f7\') }', null, 'log'), { 16 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 17 | }); 18 | }); 19 | }); 20 | 21 | describe('Log mode -> Strict mode', function() { 22 | /* eslint new-cap:0 */ 23 | it('should replace ObjectId', function() { 24 | var s = logToStrict('{ oid: ObjectId(\'87654f73c737a19e1d112233\') }'); 25 | assert.equal(s, '{ "oid": { "$oid": "87654f73c737a19e1d112233" } }'); 26 | }); 27 | 28 | it('should replace Date', function() { 29 | var s = logToStrict('{ date: new Date(1388534400000) }'); 30 | assert.equal(s, '{ "date": { "$date": "2014-01-01T00:00:00.000Z" } }'); 31 | }); 32 | 33 | it('should replace multiple dates in one line', function() { 34 | var s = logToStrict('{ start: new Date(1388534400000), end: new Date(1388534406000) }'); 35 | assert.equal(s, '{ "start": { "$date": "2014-01-01T00:00:00.000Z" }, "end":' 36 | + ' { "$date": "2014-01-01T00:00:06.000Z" } }'); 37 | }); 38 | 39 | it('should replace Timestamp', function() { 40 | var s = logToStrict('{ ts: Timestamp 0|0 }'); 41 | assert.equal(s, '{ "ts": { "$timestamp": { "t": 0, "i": 0 } } }'); 42 | }); 43 | 44 | it('should replace RegExp', function() { 45 | var s = logToStrict('{ regex: /foo/gi }'); 46 | assert.equal(s, '{ "regex": { "$regex": "foo", "$options": "gi" } }'); 47 | }); 48 | 49 | it('should escape double quotes in RegExp', function() { 50 | var s = logToStrict('{ regex: /foo"bar/ }'); 51 | assert.equal(s, '{ "regex": { "$regex": "foo"bar", "$options": "" } }'); 52 | }); 53 | 54 | it('should not confuse URLs with RegExp', function() { 55 | var s = logToStrict('{ url: "https://www.google.com/accounts/cd/id?abc=123" }'); 56 | assert.equal(s, '{ "url": "https://www.google.com/accounts/cd/id?abc=123" }'); 57 | }); 58 | 59 | it('should handle RegExp with embedded URLs', function() { 60 | var s = logToStrict('{ url: /(?:^|\W)href="http:\/\/www\.google\.com\/indexes' 61 | + '\/12345678\/0987654321a\/"/i }'); 62 | assert.equal(s, '{ "url": { "$regex": "(?:^|\W)href="http:\/\/www\.google\.com\/indexes' 63 | + '\/12345678\/0987654321a\/"", "$options": "i" } }'); 64 | }); 65 | 66 | it('should not confuse string paths with RegExp', function() { 67 | var s = logToStrict('{ path: "/local/mis" }'); 68 | assert.equal(s, '{ "path": "/local/mis" }'); 69 | }); 70 | 71 | it('should replace MaxKey', function() { 72 | var s = logToStrict('{ val: MaxKey }'); 73 | assert.equal(s, '{ "val": { "$maxKey": 1 } }'); 74 | }); 75 | 76 | it('should replace MinKey', function() { 77 | var s = logToStrict('{ val: MinKey }'); 78 | assert.equal(s, '{ "val": { "$minKey": 1 } }'); 79 | }); 80 | 81 | it('should replace BinData and convert from hex to base64', function() { 82 | var s = logToStrict('{ bin: BinData(0, 48656C6C6F20576F726C64) }'); 83 | assert.equal(s, '{ "bin": { "$binary": "SGVsbG8gV29ybGQ=", "$type": "0" } }'); 84 | }); 85 | 86 | it('should replace NumberLong', function() { 87 | var s = logToStrict('{ long: 9223372036854775807 }'); 88 | assert.equal(s, '{ "long": { "$numberLong": "9223372036854775807" } }'); 89 | }); 90 | 91 | it('should replace multiple NumberLong in one line', function() { 92 | var s = logToStrict('{ long: { $in: [ 9223372036854775807, 9223372036854775806 ] } }'); 93 | assert.equal(s, '{ "long": { "$in": [ { "$numberLong": "9223372036854775807" }, ' 94 | + '{ "$numberLong": "9223372036854775806" } ] } }'); 95 | }); 96 | 97 | it('should leave short numbers alone', function() { 98 | var s = logToStrict('{ short: 1234567 }'); 99 | assert.equal(s, '{ "short": 1234567 }'); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | -R spec 2 | -------------------------------------------------------------------------------- /test/parse.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var parse = require('../').parse; 3 | var bson = require('bson'); 4 | 5 | /* eslint new-cap:0 */ 6 | describe('Parse', function() { 7 | it('should work', function() { 8 | assert.deepEqual(parse('{"_id":{"$oid":"53c2b570c15c457669f481f7"}}'), { 9 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 10 | }); 11 | }); 12 | 13 | it.skip('should throw an error when using an unknown mode', function() { 14 | assert.throws(function() { 15 | parse('{"a": 1}', 'invalid_mode'); 16 | }, Error); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/regressions.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var EJSON = require('../'); 3 | var isFunction = require('lodash.isfunction'); 4 | var bson = require('bson'); 5 | 6 | /* eslint new-cap:0 */ 7 | describe('Regressions', function() { 8 | /** 9 | * NOTE (imlucas) sorry I missed this but 10 | * http://github.com/mongodb-js/extended-json/pulls/24 11 | * should have been a version bump to 2.x. 12 | */ 13 | describe('TypeError: EJSON.inflate is not a function', function() { 14 | var doc = { 15 | _id: bson.ObjectID('5671d0902515a6bc614d5a79') 16 | }; 17 | 18 | var serialized = { 19 | _id: { 20 | $oid: '5671d0902515a6bc614d5a79' 21 | } 22 | }; 23 | 24 | it('should have an inflate alias function for serialize', function() { 25 | assert(isFunction(EJSON.inflate)); 26 | assert.deepEqual(EJSON.inflate(doc), EJSON.serialize(doc)); 27 | }); 28 | 29 | it('should have a deflate alias function for deserialize', function() { 30 | assert(isFunction(EJSON.deflate)); 31 | assert.deepEqual(EJSON.deflate(serialized), EJSON.deserialize(serialized)); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/reviver.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var EJSON = require('../'); 3 | var bson = require('bson'); 4 | 5 | /* eslint new-cap:0 */ 6 | describe('Reviver', function() { 7 | var _id = bson.ObjectID(); 8 | var userId = bson.ObjectID(); 9 | var bin = bson.Binary(new Buffer(1)); 10 | 11 | var text; 12 | var data; 13 | 14 | before(function() { 15 | text = EJSON.stringify({ 16 | _id: _id, 17 | download_count: bson.Long.fromNumber(10), 18 | tarball: bin, 19 | maintainer: bson.DBRef('npm.user', userId), 20 | versions: [ 21 | { 22 | _id: bson.ObjectID(), 23 | tag: 'v0.0.2', 24 | created_on: new Date() 25 | }, 26 | { 27 | _id: bson.ObjectID(), 28 | tag: 'v0.0.3', 29 | created_on: new Date() 30 | } 31 | ] 32 | }); 33 | data = JSON.parse(text, EJSON.reviver); 34 | }); 35 | 36 | it('should revive `{$numberLong: }` to `bson.Long`', function() { 37 | assert(data.download_count.equals(bson.Long.fromNumber(10))); 38 | }); 39 | 40 | it('should revive `{$oid: <_id>}` to `bson.ObjectId`', function() { 41 | assert.deepEqual(data._id, _id); 42 | }); 43 | 44 | it('should revive `{$binary: }` to `bson.Binary`', function() { 45 | assert.equal(data.tarball.buffer.toString('base64'), 46 | bin.buffer.toString('base64')); 47 | }); 48 | 49 | it('should revive `{$ref: , $id: }` to `bson.DBRef`', function() { 50 | assert.deepEqual(data.maintainer.toString(), 51 | bson.DBRef('npm.user', userId).toString()); 52 | }); 53 | 54 | it('should revive embedded documents', function() { 55 | assert.equal(data.versions.length, 2); 56 | data.versions.map(function(doc) { 57 | assert.equal(doc._id._bsontype, 'ObjectID'); 58 | assert(doc.created_on.getTime); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/serialize.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var serialize = require('../').serialize; 3 | var bson = require('bson'); 4 | 5 | /* eslint new-cap:0 */ 6 | describe('Serialize', function() { 7 | var _id = bson.ObjectID(); 8 | var bin = bson.Binary(new Buffer(1)); 9 | var ref = bson.DBRef('local.startup_log', _id); 10 | var refStringId = bson.DBRef('local.startup_log', _id.toString()); 11 | var code = bson.Code('return true'); 12 | var codeWithScope = bson.Code('return true', {}); 13 | 14 | it('is a passthrough for primitive types', function() { 15 | assert.equal(serialize('bson'), 'bson'); 16 | assert.deepEqual(serialize([]), []); 17 | assert.deepEqual(serialize(1), 1); 18 | assert.deepEqual(serialize(false), false); 19 | assert.deepEqual(serialize({ 20 | a: 1 21 | }), { 22 | a: 1 23 | }); 24 | assert.deepEqual(serialize(Infinity), 'Infinity'); 25 | assert.deepEqual(serialize(-Infinity), '-Infinity'); 26 | }); 27 | 28 | it('converts `bson.Long` to `{$numberLong: }`', function() { 29 | assert.deepEqual(serialize(bson.Long.fromString('10')), { 30 | $numberLong: '10' 31 | }); 32 | }); 33 | 34 | it('converts `bson.Long` to `{$numberLong: }` (between 2^32 and 2^53)', function() { 35 | assert.deepEqual(serialize(bson.Long.fromString('4294967297')), { 36 | $numberLong: '4294967297' 37 | }); 38 | }); 39 | 40 | it('converts `bson.Long` to `{$numberLong: }` (greater than 2^53)', function() { 41 | assert.deepEqual(serialize(bson.Long.fromString('18014398509481984')), { 42 | $numberLong: '18014398509481984' 43 | }); 44 | }); 45 | 46 | it('converts `bson.Decimal128` to `{$numberDecimal: }`', function() { 47 | assert.deepEqual(serialize(bson.Decimal128.fromString('1234.567')), { 48 | $numberDecimal: '1234.567' 49 | }); 50 | }); 51 | 52 | it('converts `bson.ObjectId` to `{$oid: <_id>}`', function() { 53 | assert.deepEqual(serialize(_id), { 54 | $oid: _id.toString() 55 | }); 56 | }); 57 | 58 | it('converts `bson.Binary` to `{$binary: }`', function() { 59 | assert.deepEqual(serialize(bin), { 60 | $binary: bin.buffer.toString('base64'), 61 | $type: '0' 62 | }); 63 | }); 64 | 65 | it('converts `bson.Code` with no scope to `{$code: }`', function() { 66 | assert.deepEqual(serialize(code), { 67 | $code: code.code 68 | }); 69 | }); 70 | 71 | it('converts `bson.Code` with scope to `{$code: , $scope: }`', function() { 72 | assert.deepEqual(serialize(codeWithScope), { 73 | $code: codeWithScope.code, 74 | $scope: codeWithScope.scope 75 | }); 76 | }); 77 | 78 | it('converts `bson.DBRef` to `{$ref: , $id: }`', function() { 79 | assert.deepEqual(serialize(refStringId), { 80 | $ref: 'local.startup_log', 81 | $id: _id.toString() 82 | }); 83 | }); 84 | 85 | it('converts `bson.DBRef` to `{$ref: , $id: }`', function() { 86 | assert.deepEqual(serialize(ref), { 87 | $ref: 'local.startup_log', 88 | $id: { 89 | $oid: _id.toString() 90 | } 91 | }); 92 | }); 93 | 94 | it('converts `bson.Timestamp` to `{$timestamp: {t: , i: }`', function() { 95 | assert.deepEqual(serialize(bson.Timestamp()), { 96 | $timestamp: { 97 | t: 0, 98 | i: 0 99 | } 100 | }); 101 | }); 102 | 103 | it('converts `bson.MinKey` to `{$minKey: 1}`', function() { 104 | assert.deepEqual(serialize(bson.MinKey()), { 105 | $minKey: 1 106 | }); 107 | }); 108 | 109 | it('converts `bson.MaxKey` to `{$maxKey: 1}`', function() { 110 | assert.deepEqual(serialize(bson.MaxKey()), { 111 | $maxKey: 1 112 | }); 113 | }); 114 | 115 | it('converts `Date` to `{$date: }`', function() { 116 | var d = new Date(32535215998999); 117 | assert.deepEqual(serialize(d), { 118 | $date: d.toISOString() 119 | }); 120 | }); 121 | 122 | it('converts `Date` to `{$date: {$numberLong: }}`', function() { 123 | var d = new Date(32535215999000); 124 | assert.deepEqual(serialize(d), { 125 | $date: { 126 | $numberLong: '' + d.getTime() 127 | } 128 | }); 129 | }); 130 | 131 | it('converts `RegExp` to `{$regex: , $options: }`', function() { 132 | assert.deepEqual(serialize(/mongodb.com$/g), 133 | { 134 | $regex: 'mongodb.com$', 135 | $options: 'g' 136 | }); 137 | }); 138 | 139 | it('converts `undefined` to `{$undefined: true}`', function() { 140 | assert.deepEqual(serialize(undefined), { 141 | $undefined: true 142 | }); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /test/shellmode.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var shellToStrict = require('../lib/modes/shell.js').toStrict; 3 | var parse = require('../').parse; 4 | var bson = require('bson'); 5 | 6 | 7 | /* eslint new-cap:0 */ 8 | describe('Parse from shell mode', function() { 9 | it('should work on a simple example with ObjectId', function() { 10 | assert.deepEqual(parse('{"_id": ObjectId("53c2b570c15c457669f481f7") }', null, 'shell'), { 11 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 12 | }); 13 | }); 14 | 15 | it('should work without field name quotes', function() { 16 | assert.deepEqual(parse('{_id: ObjectId("53c2b570c15c457669f481f7") }', null, 'shell'), { 17 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 18 | }); 19 | }); 20 | 21 | it('should work with single instead of double quotes', function() { 22 | assert.deepEqual(parse('{\'_id\': ObjectId(\'53c2b570c15c457669f481f7\') }', null, 'shell'), { 23 | _id: bson.ObjectID('53c2b570c15c457669f481f7') 24 | }); 25 | }); 26 | }); 27 | 28 | describe('Shell mode -> Strict mode', function() { 29 | it('should replace ObjectId', function() { 30 | var s = shellToStrict('{ "_id": ObjectId("87654f73c737a19e1d112233") }'); 31 | assert.equal(s, '{ "_id": { "$oid": "87654f73c737a19e1d112233" } }'); 32 | }); 33 | 34 | it('should replace Date', function() { 35 | var s = shellToStrict('{ "created_at": ISODate("2014-01-01T00:00:00.000Z") }'); 36 | assert.equal(s, '{ "created_at": { "$date": "2014-01-01T00:00:00.000Z" } }'); 37 | }); 38 | 39 | it('should replace multiple dates in one line', function() { 40 | var s = shellToStrict('{ "start": ISODate("2014-01-01T00:00:00.000Z"), "end": ' 41 | + 'ISODate("2015-01-01T00:00:00.000Z") }'); 42 | assert.equal(s, '{ "start": { "$date": "2014-01-01T00:00:00.000Z" }, "end": ' 43 | + '{ "$date": "2015-01-01T00:00:00.000Z" } }'); 44 | }); 45 | 46 | it('should replace Timestamp', function() { 47 | var s = shellToStrict('{ "ts": Timestamp(100, 15) }'); 48 | assert.equal(s, '{ "ts": { "$timestamp": { "t": 100, "i": 15 } } }'); 49 | }); 50 | 51 | it('should replace RegExp', function() { 52 | var s = shellToStrict('{ "regex": /foo/gi }'); 53 | assert.equal(s, '{ "regex": { "$regex": "foo", "$options": "gi" } }'); 54 | }); 55 | 56 | it('should escape double quotes in RegExp', function() { 57 | var s = shellToStrict('{ "regex": /foo"bar/ }'); 58 | assert.equal(s, '{ "regex": { "$regex": "foo"bar", "$options": "" } }'); 59 | }); 60 | 61 | it('should not confuse URLs with RegExp', function() { 62 | var s = shellToStrict('{ url: "https://www.google.com/accounts/cd/id?abc=123" }'); 63 | assert.equal(s, '{ "url": "https://www.google.com/accounts/cd/id?abc=123" }'); 64 | }); 65 | 66 | it('should handle RegExp with embedded URLs', function() { 67 | var s = shellToStrict('{ "url": /(?:^|\W)href="http:\/\/www\.google\.com\/index' 68 | + 'es\/12345678\/0987654321a\/"/i }'); 69 | assert.equal(s, '{ "url": { "$regex": "(?:^|\W)href="http:\/\/www\.google\.com\/index' 70 | + 'es\/12345678\/0987654321a\/"", "$options": "i" } }'); 71 | }); 72 | 73 | it('should not confuse string paths with RegExp', function() { 74 | var s = shellToStrict('{ "path": "/local/mis" }'); 75 | assert.equal(s, '{ "path": "/local/mis" }'); 76 | }); 77 | 78 | it('should replace BinData', function() { 79 | var s = shellToStrict('{ "bin": BinData(0,"SGVsbG8gV29ybGQ=") }'); 80 | assert.equal(s, '{ "bin": { "$binary": "SGVsbG8gV29ybGQ=", "$type": "0" } }'); 81 | }); 82 | 83 | it('should replace NumberLong without quotes', function() { 84 | var s = shellToStrict('{ "long": NumberLong(9223372036854775807) }'); 85 | assert.equal(s, '{ "long": { "$numberLong": "9223372036854775807" } }'); 86 | }); 87 | 88 | it('should replace NumberLong with quotes', function() { 89 | var s = shellToStrict('{ "long": NumberLong("9223372036854775807") }'); 90 | assert.equal(s, '{ "long": { "$numberLong": "9223372036854775807" } }'); 91 | }); 92 | 93 | it('should replace NumberDecimal', function() { 94 | var s = shellToStrict('{ "decimal": NumberDecimal("123.456") }'); 95 | assert.equal(s, '{ "decimal": { "$numberDecimal": "123.456" } }'); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/stringify.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var stringify = require('../').stringify; 3 | var parse = require('../').parse; 4 | var createStringifyStream = require('../').createStringifyStream; 5 | var createParseStream = require('../').createParseStream; 6 | var bson = require('bson'); 7 | var fs = require('fs'); 8 | var os = require('os'); 9 | 10 | var util = require('util'); 11 | var Readable = require('stream').Readable; 12 | 13 | function DocsStream(docs) { 14 | Readable.call(this, { 15 | objectMode: true 16 | }); 17 | this.docs = docs; 18 | } 19 | util.inherits(DocsStream, Readable); 20 | 21 | DocsStream.prototype._read = function() { 22 | this.docs.forEach(function(doc) { 23 | this.push(doc); 24 | }.bind(this)); 25 | this.push(null); 26 | }; 27 | 28 | /* eslint new-cap:0 */ 29 | describe('Stringify', function() { 30 | it('should work', function() { 31 | var doc = { 32 | _id: bson.ObjectID() 33 | }; 34 | assert.equal(stringify(doc), '{"_id":{"$oid":"' + doc._id.toString() + '"}}'); 35 | }); 36 | describe('streaming', function() { 37 | var docs = [ 38 | { 39 | _id: bson.ObjectID() 40 | } 41 | ]; 42 | var dest = os.tmpdir() + '/ejson-streams-result.json'; 43 | after(function(done) { 44 | fs.unlink(dest, function() { 45 | done(); 46 | }); 47 | }); 48 | it('should support stringify', function(done) { 49 | new DocsStream(docs) 50 | .pipe(createStringifyStream()) 51 | .pipe(fs.createWriteStream(dest)) 52 | .on('finish', function() { 53 | fs.readFile(dest, function(err, buf) { 54 | if (err) { 55 | return done(err); 56 | } 57 | 58 | var data = parse(buf); 59 | assert.deepEqual(data, docs); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | 65 | it('should support parse', function(done) { 66 | fs.createReadStream(dest) 67 | .pipe(createParseStream('*')) 68 | .on('end', function() { 69 | done(); 70 | }); 71 | }); 72 | }); 73 | }); 74 | --------------------------------------------------------------------------------