├── .editorconfig ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── benchmark ├── .jshintrc ├── README.md ├── benchmark.js ├── benchmarks │ ├── circular-dedupe │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ ├── circular │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ ├── dedupe │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ ├── deserialize-circular-dedupe │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── refify.js │ │ ├── warp10-parse.js │ │ └── warp10.js │ ├── deserialize-simple-large │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── parse-native.js │ │ ├── refify.js │ │ ├── warp10-parse.js │ │ └── warp10.js │ ├── simple-large-b │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── json3.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── stringify-native.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ ├── simple-large │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── json3.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── stringify-native.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ ├── simple-small │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── json3.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── stringify-native.js │ │ ├── warp10-stringify.js │ │ └── warp10.js │ └── test-a │ │ ├── circular-json.js │ │ ├── data.js │ │ ├── lave.js │ │ ├── refify.js │ │ ├── warp10-stringify.js │ │ └── warp10.js └── third-party │ └── json3.js ├── finalize.js ├── package.json ├── parse.js ├── serialize.js ├── src ├── finalize.js ├── index.js ├── parse.js ├── serialize.js ├── stringify.js └── stringifyPrepare.js ├── stringify.js ├── stringifyPrepare.js └── test ├── .gitignore ├── .jshintrc ├── test.js ├── tests ├── additive-duplicate │ └── test.js ├── additive-special-chars │ └── test.js ├── additive │ └── test.js ├── array-root │ └── test.js ├── array │ └── test.js ├── circular-ancestor-descendent │ └── test.js ├── circular-array │ └── test.js ├── circular-no-var │ └── test.js ├── circular-parent-child │ └── test.js ├── circular │ └── test.js ├── date-root │ └── test.js ├── date │ └── test.js ├── dedupe-array │ └── test.js ├── dedupe │ └── test.js ├── example-circular-dedupe │ └── test.js ├── example-circular │ └── test.js ├── example-dedupe │ └── test.js ├── example-simple-types │ └── test.js ├── example-simple-var │ └── test.js ├── example-simple │ └── test.js ├── finalize-multiple-times │ └── test.js ├── finalize-o-null │ └── test.js ├── newline-chars │ └── test.js ├── null-root │ └── test.js ├── parse-bad-type │ └── test.js ├── primitive-root-no-var │ └── test.js ├── primitive-root-var │ └── test.js ├── simple-no-var │ └── test.js ├── simple-var │ └── test.js ├── stringifyPrepare-null │ └── test.js ├── stringifyPrepare-object-null-prototype-nested │ └── test.js ├── stringifyPrepare-object-null-prototype │ └── test.js ├── submodules │ └── test.js ├── toJSON-to-primitive-nested │ └── test.js ├── toJSON-to-primitive-root │ └── test.js ├── toJSON │ └── test.js ├── undefined-root │ └── test.js ├── xss-stringify │ └── test.js └── xss │ └── test.js └── util ├── autotest.js ├── browser-load.js ├── safeStringify.js └── stringify.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | # Matches multiple files with brace expansion notation 10 | # Set default charset 11 | [*.{js,json,marko,yml}] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /work 2 | /build 3 | /.idea/ 4 | /npm-debug.log 5 | /node_modules 6 | /*.sublime-workspace 7 | *.orig 8 | .DS_Store 9 | coverage 10 | yarn.lock 11 | ~* 12 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node" : true, 3 | "esnext": true, 4 | "boss" : false, 5 | "curly": false, 6 | "debug": false, 7 | "devel": false, 8 | "eqeqeq": true, 9 | "evil": true, 10 | "forin": false, 11 | "immed": true, 12 | "laxbreak": false, 13 | "newcap": true, 14 | "noarg": true, 15 | "noempty": false, 16 | "nonew": true, 17 | "nomen": false, 18 | "onevar": false, 19 | "plusplus": false, 20 | "regexp": false, 21 | "undef": true, 22 | "sub": false, 23 | "white": false, 24 | "eqeqeq": false, 25 | "latedef": "func", 26 | "unused": "vars", 27 | "strict": false, 28 | "eqnull": true 29 | } 30 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /work 2 | /build 3 | /.idea/ 4 | /npm-debug.log 5 | /node_modules 6 | /*.sublime-workspace 7 | *.orig 8 | .DS_Store 9 | coverage 10 | ~* 11 | /benchmark/ 12 | /test/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | node_js: 3 | - "4" 4 | - "5" 5 | - "6" 6 | language: node_js 7 | script: "npm run test-coverage" 8 | after_success: "npm run coveralls" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Patrick Steele-Idem (psteeleidem.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | warp10 2 | ====== 3 | 4 | Transport complex JavaScript objects from the server to the web browser at lightning fast speeds. Circular dependencies are correctly handled and de-duping is done automatically. Deserialization is done completely via generated JavaScript code for optimal performance (no library code is required to deserialize an object). 5 | 6 | [![Build Status](https://travis-ci.org/patrick-steele-idem/warp10.svg?branch=master)](https://travis-ci.org/patrick-steele-idem/warp10) 7 | [![Coverage Status](https://coveralls.io/repos/github/patrick-steele-idem/warp10/badge.svg?branch=master)](https://coveralls.io/github/patrick-steele-idem/warp10?branch=master) 8 | [![NPM](https://img.shields.io/npm/v/warp10.svg)](https://www.npmjs.com/package/warp10) 9 | 10 | # Features 11 | 12 | - Circular references are correctly serialized and deserialized 13 | - Duplicate objects/arrays found during serialization are only serialized once 14 | - `Date` values are correctly serialized and deserialized 15 | - Output is the code for a JavaScript expression that, when evaluated, will return the deserialized object 16 | - Extremely fast serialization and deserialization 17 | - [100% test coverage](https://coveralls.io/github/patrick-steele-idem/warp10?branch=master) 18 | - [Benchmarks](#is-it-fast) 19 | 20 | # Installation 21 | 22 | ```bash 23 | npm install warp10 --save 24 | ``` 25 | 26 | # Usage 27 | 28 | With `warp10` you can choose to serialize an object to either a JSON string or JavaScript deserialization code. Generating JavaScript deserialization code is typically faster than producing JSON code and the JavaScript deserialization code will typically allow the object to parse much more quickly. In addition, no library code is needed to parse an object when outputting JavaScript deserialization code since only the JavaScript code needs to be evaluated by the JavaScript runtime. 29 | 30 | ## Outputting JavaScript code 31 | 32 | ```javascript 33 | var warp10 = require('warp10'); 34 | 35 | var deserializationCode = warp10.serialize(object[, options]); // Returns a String 36 | ``` 37 | 38 | For example: 39 | 40 | ```javascript 41 | warp10.serialize({ 42 | hello: 'world' 43 | }, 44 | { 45 | var: 'foo' 46 | }); 47 | ``` 48 | 49 | This will produce code similar to the following: 50 | 51 | ```javascript 52 | window.foo = { 53 | "hello": "world" 54 | } 55 | ``` 56 | 57 | Supported options: 58 | 59 | - `safe` - If `true` then the ending `` tags will be escaped. (optional, default: `true`) 60 | - `var` - A global variable name to assign the output expression to. If not specified then no global variable will be created (optional, default: `undefined`) 61 | - `additive` - If `true` then objects will be merged into the existing object referenced by the global variable (i.e. the `var` option) (optional, default: `false`) 62 | 63 | Evaluating the output deserialization code as a JavaScript expression will produce a clone of the original object graph. 64 | 65 | You could transport the object graph to the browser by placing the code in a ` 72 | ``` 73 | 74 | You can also `eval` the `deserializationCode`: 75 | 76 | ```javascript 77 | console.log('DESERIALIZED:', eval(deserializationCode)); 78 | ``` 79 | 80 | ## JSON stringify/parse 81 | 82 | If outputting JavaScript code is not an option or not desired then you can use the `stringify` and `parse` methods provided by `warp10`. 83 | 84 | ```javascript 85 | var warp10 = require('warp10'); 86 | 87 | var json = warp10.stringify(object[, options]); // Returns a String 88 | ``` 89 | 90 | Supported options: 91 | 92 | - `safe` - If `true` then the ending `` tags will be escaped. (optional, default: `false`) 93 | 94 | The JSON can then be parsed using code similar to the following: 95 | 96 | ```javascript 97 | var parse = require('warp10/parse'); 98 | 99 | var object = parse(json); 100 | ``` 101 | 102 | ## JSON stringifyPrepare/finalize 103 | 104 | The `stringifyPrepare` function can be used to produce a JavaScript object that is safe to serialize using the native `JSON.stringify` method. The `finalize` method should be called on the parsed object to produce the final object with duplicate objects and circular dependencies intact. 105 | 106 | _On the server:_ 107 | 108 | ```javascript 109 | var warp10 = require('warp10').stringifyPrepare; 110 | var object = stringifyPrepare(object); // Returns an Object 111 | var json = JSON.stringify(object); 112 | ``` 113 | 114 | _In the browser:_ 115 | 116 | ```javascript 117 | var finalize = require('warp10/finalize'); 118 | var clone = finalize(JSON.parse(json)); 119 | ``` 120 | 121 | # Examples 122 | 123 | ## Serialize examples 124 | 125 | ### Simple 126 | 127 | ```javascript 128 | warp10.serialize({ name: 'Frank' }); 129 | ``` 130 | 131 | Output (formatted for readability): 132 | 133 | ```javascript 134 | ({ 135 | "name": "Frank" 136 | }) 137 | ``` 138 | 139 | ### Simple types 140 | 141 | ```javascript 142 | warp10.serialize({ 143 | object: { 144 | foo: 'bar' 145 | }, 146 | array: ['a', 'b', 'c'], 147 | boolean: true, 148 | string: 'Hello World', 149 | number: 123, 150 | date: new Date(1776, 6, 4) 151 | }); 152 | ``` 153 | 154 | Output (formatted for readability): 155 | 156 | ```javascript 157 | (function() { 158 | var $ = { 159 | "object": { 160 | "foo": "bar" 161 | }, 162 | "array": ["a", "b", "c"], 163 | "boolean": true, 164 | "string": "Hello World", 165 | "number": 123 166 | } 167 | $.date = new Date(-6106039200000) 168 | return $ 169 | }()) 170 | ``` 171 | 172 | ### Global variable 173 | 174 | ```javascript 175 | warp10.serialize({ name: 'Frank' }, { var: 'person' }); 176 | ``` 177 | 178 | Output (formatted for readability): 179 | 180 | ```javascript 181 | window.person = { 182 | "name": "Frank" 183 | } 184 | ``` 185 | 186 | ### Global variable with additive 187 | 188 | ```javascript 189 | var deserializationCodeA = warp10.serialize({ 190 | foo: 'foo', 191 | bar: 'bar' 192 | }, 193 | { 194 | var: 'myStore', 195 | additive: true 196 | }); 197 | 198 | var deserializationCodeB = warp10.serialize({ 199 | baz: 'baz' 200 | }, 201 | { 202 | var: 'myStore', 203 | additive: true 204 | }); 205 | ``` 206 | 207 | Output (formatted for readability): 208 | 209 | ```javascript 210 | // deserializationCodeA 211 | (function() { 212 | var t = window.myStore || (window.myStore = {}) 213 | var $ = { 214 | "foo": "foo", 215 | "bar": "bar" 216 | } 217 | t.foo = $.foo 218 | t.bar = $.bar 219 | }()); 220 | 221 | // deserializationCodeB 222 | (function() { 223 | var t = window.myStore || (window.myStore = {}) 224 | var $ = { 225 | "baz": "baz" 226 | } 227 | t.baz = $.baz 228 | }()) 229 | ``` 230 | 231 | Final value of the `window.myStore` global: 232 | 233 | ```javascript 234 | { 235 | foo: 'foo', 236 | bar: 'bar', 237 | baz: 'baz' 238 | } 239 | ``` 240 | 241 | ### Circular dependency 242 | 243 | ```javascript 244 | var parent = { 245 | name: 'parent' 246 | }; 247 | 248 | var child = { 249 | parent: parent 250 | }; 251 | 252 | parent.child = child; 253 | 254 | warp10.serialize(parent); 255 | ``` 256 | 257 | Output (formatted for readability): 258 | 259 | ```javascript 260 | (function() { 261 | var $ = { 262 | "name": "parent", 263 | "child": {} 264 | } 265 | $.child.parent = $ 266 | return $ 267 | }()) 268 | ``` 269 | 270 | ### De-duping 271 | 272 | ```javascript 273 | var child = { 274 | name: 'Henry' 275 | }; 276 | 277 | var mother = { 278 | name: 'Jane', 279 | child: child 280 | }; 281 | 282 | var father = { 283 | name: 'Frank', 284 | child: child 285 | }; 286 | 287 | warp10.serialize({ 288 | mother: mother, 289 | father: father 290 | }); 291 | ``` 292 | 293 | Output (formatted for readability): 294 | 295 | ```javascript 296 | (function() { 297 | var $ = { 298 | "mother": { 299 | "name": "Jane", 300 | "child": { 301 | "name": "Henry" 302 | } 303 | }, 304 | "father": { 305 | "name": "Frank" 306 | } 307 | } 308 | $.father.child = $.mother.child 309 | return $ 310 | }()) 311 | ``` 312 | 313 | ### Circular dependency plus de-duping 314 | 315 | ```javascript 316 | var warp10 = require('warp10'); 317 | 318 | var mother = { 319 | name: 'Jane', 320 | age: 30 321 | }; 322 | 323 | var father = { 324 | name: 'Frank', 325 | age: 32 326 | }; 327 | 328 | var child1 = { 329 | name: 'Sue', 330 | age: 5, 331 | mother: mother, // circular 332 | father: father // circular 333 | }; 334 | 335 | var child2 = { 336 | name: 'Henry', 337 | age: 10, 338 | mother: mother, // circular 339 | father: father // circular 340 | }; 341 | 342 | mother.children = [child1, child2]; 343 | father.children = [child1 /* duplicate */, child2 /* duplicate */]; 344 | 345 | warp10.serialize({ 346 | mother: mother, 347 | father: father 348 | }); 349 | ``` 350 | 351 | The value of `deserializationCode` will be similar to the following (formatted for readability): 352 | 353 | ```javascript 354 | (function() { 355 | var $ = { 356 | "mother": { 357 | "name": "Jane", 358 | "age": 30, 359 | "children": [{ 360 | "name": "Sue", 361 | "age": 5, 362 | "father": { 363 | "name": "Frank", 364 | "age": 32, 365 | "children": [null, { 366 | "name": "Henry", 367 | "age": 10 368 | }] 369 | } 370 | }, null] 371 | } 372 | } 373 | $.mother.children[0].mother = $.mother 374 | $.mother.children[0].father.children[0] = $.mother.children[0] 375 | $.mother.children[0].father.children[1].mother = $.mother 376 | $.mother.children[0].father.children[1].father = $.mother.children[0].father 377 | $.mother.children[1] = $.mother.children[0].father.children[1] 378 | $.father = $.mother.children[0].father 379 | return $ 380 | }()) 381 | ``` 382 | 383 | ## Stringify examples 384 | 385 | ### Simple 386 | 387 | ```javascript 388 | warp10.stringify({ name: 'Frank' }); 389 | ``` 390 | 391 | Output (formatted for readability): 392 | 393 | ```javascript 394 | { 395 | "object": { 396 | "name": "Frank" 397 | } 398 | } 399 | ``` 400 | 401 | ### Circular dependency 402 | 403 | ```javascript 404 | var parent = { 405 | name: 'parent' 406 | }; 407 | 408 | var child = { 409 | parent: parent 410 | }; 411 | 412 | parent.child = child; 413 | 414 | warp10.serialize(parent); 415 | ``` 416 | 417 | Output (formatted for readability): 418 | 419 | ```javascript 420 | { 421 | "object": { 422 | "mother": { 423 | "name": "Jane", 424 | "age": 30, 425 | "children": [{ 426 | "name": "Sue", 427 | "age": 5, 428 | "father": { 429 | "name": "Frank", 430 | "age": 32 431 | } 432 | }, { 433 | "name": "Henry", 434 | "age": 10 435 | }] 436 | } 437 | }, 438 | "assignments": [{ 439 | "l": ["mother", "children", 0, "mother"], 440 | "r": ["mother"] 441 | }, { 442 | "l": ["mother", "children", 0, "father", "children"], 443 | "r": ["mother", "children"] 444 | }, { 445 | "l": ["mother", "children", 1, "mother"], 446 | "r": ["mother"] 447 | }, { 448 | "l": ["mother", "children", 1, "father"], 449 | "r": ["mother", "children", 0, "father"] 450 | }, { 451 | "l": ["father"], 452 | "r": ["mother", "children", 0, "father"] 453 | }] 454 | } 455 | ``` 456 | 457 | # Is it fast? 458 | 459 | Yes, this library is optimized for both fast serialization and deserialization. This library was built on top of the native `JSON.stringify` method for optimal performance. This library includes [benchmarks](./benchmarks) that you can run locally: 460 | 461 | ``` 462 | cd warp10/ 463 | npm run benchmark 464 | ``` 465 | 466 | Below is the output for one run of the benchmarks: 467 | 468 | ```text 469 | circular 470 | 284,357 op/s » circular-json 471 | 521 op/s » lave 472 | 654,493 op/s » refify 473 | 519,239 op/s » warp10-stringify 474 | 820,863 op/s » warp10 475 | 476 | circular-dedupe 477 | 49,070 op/s » circular-json 478 | 505 op/s » lave 479 | 46,396 op/s » refify 480 | 113,071 op/s » warp10-stringify 481 | 177,930 op/s » warp10 482 | 483 | dedupe 484 | 104,117 op/s » circular-json 485 | 558 op/s » lave 486 | 334,314 op/s » refify 487 | 343,625 op/s » warp10-stringify 488 | 478,872 op/s » warp10 489 | 490 | deserialize-circular-dedupe 491 | 32,124 op/s » circular-json 492 | 25,247 op/s » refify 493 | 82,770 op/s » warp10-parse 494 | 1,052,371 op/s » warp10 495 | 496 | deserialize-simple-large 497 | 2,551 op/s » circular-json 498 | 24,051 op/s » parse-native 499 | 1,918 op/s » refify 500 | 24,497 op/s » warp10-parse 501 | 149,809 op/s » warp10 502 | 503 | simple-large 504 | 3,150 op/s » circular-json 505 | 2,076 op/s » json3 506 | 283 op/s » lave 507 | 2,504 op/s » refify 508 | 31,057 op/s » stringify-native 509 | 11,174 op/s » warp10-stringify 510 | 11,161 op/s » warp10 511 | 512 | simple-large-b 513 | 124 op/s » circular-json 514 | 117 op/s » json3 515 | 26 op/s » lave 516 | 156 op/s » refify 517 | 2,263 op/s » stringify-native 518 | 1,505 op/s » warp10-stringify 519 | 1,461 op/s » warp10 520 | 521 | simple-small 522 | 164,080 op/s » circular-json 523 | 105,782 op/s » json3 524 | 635 op/s » lave 525 | 259,742 op/s » refify 526 | 1,121,181 op/s » stringify-native 527 | 555,886 op/s » warp10-stringify 528 | 686,304 op/s » warp10 529 | 530 | test-a 531 | 190,557 op/s » circular-json 532 | 554 op/s » lave 533 | 90,753 op/s » refify 534 | 219,099 op/s » warp10-stringify 535 | 381,360 op/s » warp10 536 | ``` 537 | 538 | Test setup: 539 | 540 | - Node.js v6.3.1 541 | - OSX 10.11.5 542 | - 2.8 GHz Intel Core i7 543 | - 16 GB 1600 MHz DDR3 544 | 545 | # How does it work? 546 | 547 | Internally, this library utilizes the native [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method to serialize an object to JSON. However, before calling `JSON.stringify`, the object is pruned by removing duplicate objects. If an already serialized object is encountered then the current property is skipped and the skipped property is tracked so that it can be fixed up later using generated JavaScript code. 548 | 549 | `warp10` detects circular dependencies and duplicate objects by marking each object with a non-enumerable [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) property using code similar to the following: 550 | 551 | ```javascript 552 | var markerKey = Symbol(); 553 | var marker = {} 554 | obj[markerKey] = marker; 555 | ``` 556 | 557 | This special property is largely private and only discoverable at runtime via `Object.getOwnPropertySymbols` or proxies. 558 | 559 | # Why? 560 | 561 | This library can be used to transport a complex JavaScript graph from one JavaScript runtime to another JavaScript runtime. This library was originally created to support serializing potentially complex UI component state down to the browser for the [Marko Widgets](https://github.com/marko-js/marko-widgets) UI components library. This allows the web browser to pickup exactly where the server left off when utilizing server-side rendering of a web page. Marko Widgets is optimized for speed and it is important to minimize the CPU usage of both the server and the web browser to reduce page load times (accompanied by a reduced payload size through de-duping of data). 562 | 563 | # Similar projects 564 | 565 | - [circular-json](https://github.com/WebReflection/circular-json) 566 | - [JSON-js](https://github.com/douglascrockford/JSON-js) 567 | - [jsonr](https://github.com/graniteds/jsonr) 568 | - [refify](https://github.com/grncdr/refify) 569 | - [serialize-javascript](https://github.com/yahoo/serialize-javascript) 570 | 571 | # Maintainers 572 | 573 | * [Patrick Steele-Idem](https://github.com/patrick-steele-idem) (Twitter: [@psteeleidem](http://twitter.com/psteeleidem)) 574 | 575 | # Contributing 576 | 577 | Pull Requests welcome. Please submit Github issues for any feature enhancements, bugs or documentation problems. Please make sure all tests pass: 578 | 579 | ``` 580 | npm test 581 | ``` 582 | 583 | # License 584 | 585 | MIT 586 | -------------------------------------------------------------------------------- /benchmark/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "suite", 4 | "bench" 5 | ], 6 | "node" : true, 7 | "es5" : false, 8 | "esnext": true, 9 | "browser" : true, 10 | "boss" : false, 11 | "curly": false, 12 | "debug": false, 13 | "devel": false, 14 | "eqeqeq": true, 15 | "evil": true, 16 | "forin": false, 17 | "immed": true, 18 | "laxbreak": false, 19 | "newcap": true, 20 | "noarg": true, 21 | "noempty": false, 22 | "nonew": true, 23 | "nomen": false, 24 | "onevar": false, 25 | "plusplus": false, 26 | "regexp": false, 27 | "undef": true, 28 | "sub": false, 29 | "white": false, 30 | "eqeqeq": false, 31 | "latedef": true, 32 | "unused": "vars", 33 | "eqnull": true 34 | } 35 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | Benchmarks 2 | ========== 3 | 4 | Each bench is provided as a directory under the [./benchmarks/](./benchmarks/) directory. Each JavaScript file within a benchmark directory is loaded and executed automatically by the benchmark runner. -------------------------------------------------------------------------------- /benchmark/benchmark.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | 4 | var benchmarksDir = path.join(__dirname, 'benchmarks'); 5 | 6 | fs.readdirSync(benchmarksDir).forEach(function(suiteName) { 7 | var dir = path.join(benchmarksDir, suiteName); 8 | if (!fs.statSync(dir).isDirectory()) { 9 | return; 10 | } 11 | 12 | suite(suiteName, function() { 13 | fs.readdirSync(dir).forEach(function(benchFile) { 14 | if (!benchFile.endsWith('.js') || benchFile === 'data.js') { 15 | return; 16 | } 17 | 18 | var dataFile = path.join(dir, 'data.js'); 19 | if (!fs.existsSync(dataFile)) { 20 | return; 21 | } 22 | 23 | var data = require(dataFile); 24 | var benchName = benchFile.slice(0, -3); 25 | 26 | benchFile = path.join(dir, benchFile); 27 | 28 | 29 | var benchFunc = require(benchFile); 30 | bench(benchName, function() { 31 | benchFunc(data); 32 | }); 33 | }); 34 | }); 35 | }); -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/data.js: -------------------------------------------------------------------------------- 1 | var mother = { 2 | name: 'Jane', 3 | age: 30 4 | } 5 | 6 | var father = { 7 | name: 'Frank', 8 | age: 32 9 | } 10 | 11 | var child1 = { 12 | name: 'Sue', 13 | age: 5, 14 | mother: mother, // circular 15 | father: father // circular 16 | }; 17 | 18 | var child2 = { 19 | name: 'Henry', 20 | age: 10, 21 | mother: mother, // circular 22 | father: father // circular 23 | }; 24 | 25 | mother.children = [child1, child2]; 26 | father.children = [child1 /* duplicate */, child2 /* duplicate */]; 27 | 28 | module.exports = { 29 | mother: mother, 30 | father: father 31 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular-dedupe/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/data.js: -------------------------------------------------------------------------------- 1 | var parent = { 2 | name: 'parent' 3 | }; 4 | 5 | var child = { 6 | parent: parent 7 | }; 8 | 9 | parent.child = child; 10 | 11 | module.exports = parent; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/circular/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/data.js: -------------------------------------------------------------------------------- 1 | var child = { 2 | name: 'Henry' 3 | }; 4 | 5 | var mother = { 6 | name: 'Jane', 7 | child: child 8 | }; 9 | 10 | var father = { 11 | name: 'Frank', 12 | child: child 13 | }; 14 | 15 | module.exports = { 16 | mother: mother, 17 | father: father 18 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/dedupe/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-circular-dedupe/circular-json.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var CircularJSON = require('circular-json'); 3 | 4 | var json = CircularJSON.stringify(data); 5 | 6 | module.exports = function(data, helpers) { 7 | return CircularJSON.parse(json); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-circular-dedupe/data.js: -------------------------------------------------------------------------------- 1 | var mother = { 2 | name: 'Jane', 3 | age: 30 4 | } 5 | 6 | var father = { 7 | name: 'Frank', 8 | age: 32 9 | } 10 | 11 | var child1 = { 12 | name: 'Sue', 13 | age: 5, 14 | mother: mother, // circular 15 | father: father // circular 16 | }; 17 | 18 | var child2 = { 19 | name: 'Henry', 20 | age: 10, 21 | mother: mother, // circular 22 | father: father // circular 23 | }; 24 | 25 | mother.children = [child1, child2]; 26 | father.children = [child1 /* duplicate */, child2 /* duplicate */]; 27 | 28 | module.exports = { 29 | mother: mother, 30 | father: father 31 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-circular-dedupe/refify.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var refify = require('refify'); 3 | 4 | var json = refify.stringify(data); 5 | 6 | module.exports = function(data, helpers) { 7 | return refify.parse(json); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-circular-dedupe/warp10-parse.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var warp10 = require('../../../'); 3 | 4 | var options = { safe: false }; 5 | 6 | var json = warp10.stringify(data, options); 7 | 8 | module.exports = function(data, helpers) { 9 | return warp10.parse(json); 10 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-circular-dedupe/warp10.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var warp10 = require('../../../'); 3 | 4 | var options = { safe: false }; 5 | 6 | var deserializationCode = warp10.serialize(data, options); 7 | 8 | module.exports = function(data, helpers) { 9 | return eval(deserializationCode); 10 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/circular-json.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var CircularJSON = require('circular-json'); 3 | 4 | var json = CircularJSON.stringify(data); 5 | 6 | module.exports = function(data, helpers) { 7 | return CircularJSON.parse(json); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/data.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "_id": "57923a918e0803771dd95e99", 4 | "index": 0, 5 | "guid": "7c861ac4-cd23-4fbd-8195-905ca912ab62", 6 | "isActive": false, 7 | "balance": "$3,831.70", 8 | "picture": "http://placehold.it/32x32", 9 | "age": 38, 10 | "eyeColor": "brown", 11 | "name": { 12 | "first": "Michele", 13 | "last": "Vinson" 14 | }, 15 | "company": "MEDALERT", 16 | "email": "michele.vinson@medalert.io", 17 | "phone": "+1 (842) 480-3620", 18 | "address": "779 Gold Street, Woodburn, Kansas, 9693", 19 | "about": "Excepteur nisi sint tempor labore. Sunt do veniam Lorem eiusmod ullamco Lorem ut quis sunt qui esse labore. Anim ex non occaecat excepteur veniam mollit irure culpa mollit. Non ex nisi cillum consectetur sunt duis duis ipsum. Sint aliquip deserunt veniam nulla irure. Ad consequat enim aliqua ullamco ut ullamco.", 20 | "registered": "Sunday, July 17, 2016 9:46 PM", 21 | "latitude": "-16.247476", 22 | "longitude": "-88.01798", 23 | "tags": [ 24 | "dolor", 25 | "laborum", 26 | "deserunt", 27 | "enim", 28 | "laborum" 29 | ], 30 | "range": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 6, 38 | 7, 39 | 8, 40 | 9 41 | ], 42 | "friends": [ 43 | { 44 | "id": 0, 45 | "name": "Marks Hale" 46 | }, 47 | { 48 | "id": 1, 49 | "name": "Noreen Atkins" 50 | }, 51 | { 52 | "id": 2, 53 | "name": "Karen George" 54 | } 55 | ], 56 | "greeting": "Hello, Michele! You have 10 unread messages.", 57 | "favoriteFruit": "strawberry" 58 | }, 59 | { 60 | "_id": "57923a91696bb9374a396b6a", 61 | "index": 1, 62 | "guid": "6110ae0e-bc6e-4df1-a60a-52876f98fde6", 63 | "isActive": true, 64 | "balance": "$1,967.19", 65 | "picture": "http://placehold.it/32x32", 66 | "age": 25, 67 | "eyeColor": "brown", 68 | "name": { 69 | "first": "Mcdaniel", 70 | "last": "Chaney" 71 | }, 72 | "company": "GREEKER", 73 | "email": "mcdaniel.chaney@greeker.co.uk", 74 | "phone": "+1 (988) 436-3981", 75 | "address": "789 Essex Street, Harborton, Massachusetts, 3262", 76 | "about": "Lorem dolor officia et officia. Ullamco nulla amet consequat culpa occaecat id esse. Do ea ex excepteur eu occaecat Lorem incididunt commodo ex ullamco anim anim adipisicing. Cupidatat duis ex sit Lorem incididunt velit eu sit enim est commodo consectetur. Pariatur laborum aliqua in duis proident in do aliquip reprehenderit quis sint elit elit. Voluptate cupidatat enim do occaecat ex sunt dolore. Culpa sint adipisicing non cillum ea in sit.", 77 | "registered": "Monday, October 12, 2015 8:38 AM", 78 | "latitude": "-32.986009", 79 | "longitude": "-172.995706", 80 | "tags": [ 81 | "sint", 82 | "officia", 83 | "ipsum", 84 | "eiusmod", 85 | "mollit" 86 | ], 87 | "range": [ 88 | 0, 89 | 1, 90 | 2, 91 | 3, 92 | 4, 93 | 5, 94 | 6, 95 | 7, 96 | 8, 97 | 9 98 | ], 99 | "friends": [ 100 | { 101 | "id": 0, 102 | "name": "Perry Lynn" 103 | }, 104 | { 105 | "id": 1, 106 | "name": "May Clemons" 107 | }, 108 | { 109 | "id": 2, 110 | "name": "Melissa Klein" 111 | } 112 | ], 113 | "greeting": "Hello, Mcdaniel! You have 7 unread messages.", 114 | "favoriteFruit": "strawberry" 115 | }, 116 | { 117 | "_id": "57923a91934371c1f957c876", 118 | "index": 2, 119 | "guid": "c2bf9efa-54ff-4903-9a49-2f51158cb333", 120 | "isActive": true, 121 | "balance": "$3,182.19", 122 | "picture": "http://placehold.it/32x32", 123 | "age": 34, 124 | "eyeColor": "green", 125 | "name": { 126 | "first": "Dudley", 127 | "last": "Castaneda" 128 | }, 129 | "company": "TERRAGO", 130 | "email": "dudley.castaneda@terrago.biz", 131 | "phone": "+1 (981) 542-3083", 132 | "address": "881 Tiffany Place, Harold, Idaho, 9685", 133 | "about": "Qui ad sint est sit occaecat pariatur velit sit ullamco sint est incididunt anim reprehenderit. Deserunt enim qui amet ad ex adipisicing esse velit dolor id laboris sunt consequat. Officia aute anim eiusmod tempor anim Lorem culpa aute. Eu ad et in pariatur et mollit duis eu duis ad mollit exercitation eiusmod cupidatat.", 134 | "registered": "Tuesday, June 2, 2015 12:19 PM", 135 | "latitude": "20.296917", 136 | "longitude": "138.238473", 137 | "tags": [ 138 | "esse", 139 | "sit", 140 | "velit", 141 | "anim", 142 | "eiusmod" 143 | ], 144 | "range": [ 145 | 0, 146 | 1, 147 | 2, 148 | 3, 149 | 4, 150 | 5, 151 | 6, 152 | 7, 153 | 8, 154 | 9 155 | ], 156 | "friends": [ 157 | { 158 | "id": 0, 159 | "name": "Kent Black" 160 | }, 161 | { 162 | "id": 1, 163 | "name": "Angeline Donaldson" 164 | }, 165 | { 166 | "id": 2, 167 | "name": "Clements Larsen" 168 | } 169 | ], 170 | "greeting": "Hello, Dudley! You have 10 unread messages.", 171 | "favoriteFruit": "apple" 172 | }, 173 | { 174 | "_id": "57923a9112bfbe3d7589e2f8", 175 | "index": 3, 176 | "guid": "b5616830-c170-41d3-96df-ad46d25067f5", 177 | "isActive": false, 178 | "balance": "$1,499.05", 179 | "picture": "http://placehold.it/32x32", 180 | "age": 32, 181 | "eyeColor": "blue", 182 | "name": { 183 | "first": "Mcneil", 184 | "last": "Ellis" 185 | }, 186 | "company": "PREMIANT", 187 | "email": "mcneil.ellis@premiant.me", 188 | "phone": "+1 (951) 427-2177", 189 | "address": "458 Madoc Avenue, Wescosville, Tennessee, 288", 190 | "about": "Cillum ullamco id tempor duis commodo anim Lorem. Magna aute commodo voluptate tempor ea officia minim minim officia. Nulla esse ut adipisicing adipisicing mollit laborum nulla laborum esse labore sunt irure incididunt. Nulla sunt ex culpa nostrud sunt magna.", 191 | "registered": "Monday, September 1, 2014 8:24 AM", 192 | "latitude": "47.852095", 193 | "longitude": "-92.942884", 194 | "tags": [ 195 | "tempor", 196 | "quis", 197 | "adipisicing", 198 | "eu", 199 | "ex" 200 | ], 201 | "range": [ 202 | 0, 203 | 1, 204 | 2, 205 | 3, 206 | 4, 207 | 5, 208 | 6, 209 | 7, 210 | 8, 211 | 9 212 | ], 213 | "friends": [ 214 | { 215 | "id": 0, 216 | "name": "Ruiz Golden" 217 | }, 218 | { 219 | "id": 1, 220 | "name": "Cantu Bradford" 221 | }, 222 | { 223 | "id": 2, 224 | "name": "Kaitlin Marshall" 225 | } 226 | ], 227 | "greeting": "Hello, Mcneil! You have 8 unread messages.", 228 | "favoriteFruit": "strawberry" 229 | }, 230 | { 231 | "_id": "57923a91b6f6df9fc46950cf", 232 | "index": 4, 233 | "guid": "6087c264-4b43-4a79-938c-b6e0828efaab", 234 | "isActive": true, 235 | "balance": "$1,802.66", 236 | "picture": "http://placehold.it/32x32", 237 | "age": 23, 238 | "eyeColor": "green", 239 | "name": { 240 | "first": "Shields", 241 | "last": "Santiago" 242 | }, 243 | "company": "PAPRIKUT", 244 | "email": "shields.santiago@paprikut.net", 245 | "phone": "+1 (883) 568-3460", 246 | "address": "959 Meserole Street, Elfrida, Wisconsin, 3154", 247 | "about": "Ullamco et elit eu nostrud fugiat do. Ut minim anim anim magna fugiat. Ut amet ipsum dolore pariatur duis. Mollit minim sunt irure proident cillum.", 248 | "registered": "Saturday, April 4, 2015 5:10 AM", 249 | "latitude": "62.135082", 250 | "longitude": "-111.592041", 251 | "tags": [ 252 | "sint", 253 | "commodo", 254 | "ullamco", 255 | "veniam", 256 | "dolor" 257 | ], 258 | "range": [ 259 | 0, 260 | 1, 261 | 2, 262 | 3, 263 | 4, 264 | 5, 265 | 6, 266 | 7, 267 | 8, 268 | 9 269 | ], 270 | "friends": [ 271 | { 272 | "id": 0, 273 | "name": "Leah Rice" 274 | }, 275 | { 276 | "id": 1, 277 | "name": "Ruth Merrill" 278 | }, 279 | { 280 | "id": 2, 281 | "name": "Ava Jefferson" 282 | } 283 | ], 284 | "greeting": "Hello, Shields! You have 7 unread messages.", 285 | "favoriteFruit": "apple" 286 | }, 287 | { 288 | "_id": "57923a9114fb2fd691b1cf18", 289 | "index": 5, 290 | "guid": "a6b5ff16-a1a8-45e0-a05a-8d9bf68e01eb", 291 | "isActive": false, 292 | "balance": "$1,310.36", 293 | "picture": "http://placehold.it/32x32", 294 | "age": 29, 295 | "eyeColor": "brown", 296 | "name": { 297 | "first": "Horn", 298 | "last": "Hendrix" 299 | }, 300 | "company": "VETRON", 301 | "email": "horn.hendrix@vetron.us", 302 | "phone": "+1 (888) 457-2512", 303 | "address": "163 Irving Avenue, Longoria, North Carolina, 8637", 304 | "about": "Et tempor culpa laboris id et cupidatat. Duis enim officia cupidatat in Lorem proident nulla id. Ex ea proident officia mollit eiusmod nisi magna. Nostrud nulla adipisicing do deserunt est aliqua do et dolor.", 305 | "registered": "Thursday, March 17, 2016 11:05 AM", 306 | "latitude": "41.629224", 307 | "longitude": "34.254476", 308 | "tags": [ 309 | "non", 310 | "nostrud", 311 | "amet", 312 | "ut", 313 | "esse" 314 | ], 315 | "range": [ 316 | 0, 317 | 1, 318 | 2, 319 | 3, 320 | 4, 321 | 5, 322 | 6, 323 | 7, 324 | 8, 325 | 9 326 | ], 327 | "friends": [ 328 | { 329 | "id": 0, 330 | "name": "Elizabeth Harrison" 331 | }, 332 | { 333 | "id": 1, 334 | "name": "Kenya Landry" 335 | }, 336 | { 337 | "id": 2, 338 | "name": "Marsha Dean" 339 | } 340 | ], 341 | "greeting": "Hello, Horn! You have 5 unread messages.", 342 | "favoriteFruit": "banana" 343 | }, 344 | { 345 | "_id": "57923a91a93b95b4382a17b3", 346 | "index": 6, 347 | "guid": "f22415dd-6ea1-4003-9e9a-75a6b33a33d9", 348 | "isActive": true, 349 | "balance": "$2,396.03", 350 | "picture": "http://placehold.it/32x32", 351 | "age": 25, 352 | "eyeColor": "green", 353 | "name": { 354 | "first": "Eliza", 355 | "last": "Tyler" 356 | }, 357 | "company": "SPHERIX", 358 | "email": "eliza.tyler@spherix.name", 359 | "phone": "+1 (876) 482-3238", 360 | "address": "981 Corbin Place, Bowden, Illinois, 2479", 361 | "about": "Voluptate laboris labore consequat consequat dolore est ullamco aliquip id ad magna consequat. Pariatur occaecat laboris ut cillum qui sit pariatur minim non consequat. Anim non laborum dolor reprehenderit reprehenderit. Et laborum cillum mollit id minim. Deserunt commodo anim do eu veniam irure Lorem ea. Amet aliqua eiusmod proident deserunt labore nisi adipisicing velit tempor labore.", 362 | "registered": "Sunday, January 31, 2016 10:20 PM", 363 | "latitude": "36.091364", 364 | "longitude": "-9.842138", 365 | "tags": [ 366 | "ea", 367 | "in", 368 | "fugiat", 369 | "in", 370 | "laborum" 371 | ], 372 | "range": [ 373 | 0, 374 | 1, 375 | 2, 376 | 3, 377 | 4, 378 | 5, 379 | 6, 380 | 7, 381 | 8, 382 | 9 383 | ], 384 | "friends": [ 385 | { 386 | "id": 0, 387 | "name": "Gabriela Mitchell" 388 | }, 389 | { 390 | "id": 1, 391 | "name": "Duncan Rivas" 392 | }, 393 | { 394 | "id": 2, 395 | "name": "Dunlap Leblanc" 396 | } 397 | ], 398 | "greeting": "Hello, Eliza! You have 5 unread messages.", 399 | "favoriteFruit": "strawberry" 400 | }, 401 | { 402 | "_id": "57923a911d6d4ba431c15432", 403 | "index": 7, 404 | "guid": "d2882ba2-47b4-489c-bd3e-80d829d627b6", 405 | "isActive": false, 406 | "balance": "$3,935.57", 407 | "picture": "http://placehold.it/32x32", 408 | "age": 30, 409 | "eyeColor": "brown", 410 | "name": { 411 | "first": "Melendez", 412 | "last": "Buchanan" 413 | }, 414 | "company": "DIGIAL", 415 | "email": "melendez.buchanan@digial.biz", 416 | "phone": "+1 (871) 477-2868", 417 | "address": "931 Schenectady Avenue, Faxon, Northern Mariana Islands, 8772", 418 | "about": "Nostrud pariatur magna ullamco est consequat. Ut laborum proident nisi do nulla velit culpa. Officia ipsum elit dolore pariatur nostrud eiusmod deserunt enim adipisicing adipisicing. Qui dolor elit pariatur sunt anim aliqua. Laboris do sint culpa proident mollit id fugiat eu minim ullamco.", 419 | "registered": "Wednesday, April 20, 2016 1:46 PM", 420 | "latitude": "36.526109", 421 | "longitude": "18.225863", 422 | "tags": [ 423 | "do", 424 | "eu", 425 | "exercitation", 426 | "eu", 427 | "minim" 428 | ], 429 | "range": [ 430 | 0, 431 | 1, 432 | 2, 433 | 3, 434 | 4, 435 | 5, 436 | 6, 437 | 7, 438 | 8, 439 | 9 440 | ], 441 | "friends": [ 442 | { 443 | "id": 0, 444 | "name": "Ray Johnston" 445 | }, 446 | { 447 | "id": 1, 448 | "name": "Gilmore Cantu" 449 | }, 450 | { 451 | "id": 2, 452 | "name": "Gale Castro" 453 | } 454 | ], 455 | "greeting": "Hello, Melendez! You have 8 unread messages.", 456 | "favoriteFruit": "strawberry" 457 | } 458 | ] -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/parse-native.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var json = JSON.stringify(data); 3 | 4 | module.exports = function(data, helpers) { 5 | return JSON.parse(json); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/refify.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var refify = require('refify'); 3 | 4 | var json = refify.stringify(data); 5 | 6 | module.exports = function(data, helpers) { 7 | return refify.parse(json); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/warp10-parse.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var warp10 = require('../../../'); 3 | 4 | var options = { safe: false }; 5 | 6 | var json = warp10.stringify(data, options); 7 | 8 | module.exports = function(data, helpers) { 9 | return warp10.parse(json); 10 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/deserialize-simple-large/warp10.js: -------------------------------------------------------------------------------- 1 | var data = require('./data'); 2 | var warp10 = require('../../../'); 3 | 4 | var options = { safe: false }; 5 | 6 | var deserializationCode = warp10.serialize(data, options); 7 | 8 | module.exports = function(data, helpers) { 9 | return eval(deserializationCode); 10 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/json3.js: -------------------------------------------------------------------------------- 1 | var json3 = require('../../third-party/json3'); 2 | 3 | module.exports = function(data, helpers) { 4 | return json3.stringify(data); 5 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/stringify-native.js: -------------------------------------------------------------------------------- 1 | module.exports = function(data, helpers) { 2 | return JSON.stringify(data); 3 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large-b/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/data.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "_id": "57923a918e0803771dd95e99", 4 | "index": 0, 5 | "guid": "7c861ac4-cd23-4fbd-8195-905ca912ab62", 6 | "isActive": false, 7 | "balance": "$3,831.70", 8 | "picture": "http://placehold.it/32x32", 9 | "age": 38, 10 | "eyeColor": "brown", 11 | "name": { 12 | "first": "Michele", 13 | "last": "Vinson" 14 | }, 15 | "company": "MEDALERT", 16 | "email": "michele.vinson@medalert.io", 17 | "phone": "+1 (842) 480-3620", 18 | "address": "779 Gold Street, Woodburn, Kansas, 9693", 19 | "about": "Excepteur nisi sint tempor labore. Sunt do veniam Lorem eiusmod ullamco Lorem ut quis sunt qui esse labore. Anim ex non occaecat excepteur veniam mollit irure culpa mollit. Non ex nisi cillum consectetur sunt duis duis ipsum. Sint aliquip deserunt veniam nulla irure. Ad consequat enim aliqua ullamco ut ullamco.", 20 | "registered": "Sunday, July 17, 2016 9:46 PM", 21 | "latitude": "-16.247476", 22 | "longitude": "-88.01798", 23 | "tags": [ 24 | "dolor", 25 | "laborum", 26 | "deserunt", 27 | "enim", 28 | "laborum" 29 | ], 30 | "range": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 6, 38 | 7, 39 | 8, 40 | 9 41 | ], 42 | "friends": [ 43 | { 44 | "id": 0, 45 | "name": "Marks Hale" 46 | }, 47 | { 48 | "id": 1, 49 | "name": "Noreen Atkins" 50 | }, 51 | { 52 | "id": 2, 53 | "name": "Karen George" 54 | } 55 | ], 56 | "greeting": "Hello, Michele! You have 10 unread messages.", 57 | "favoriteFruit": "strawberry" 58 | }, 59 | { 60 | "_id": "57923a91696bb9374a396b6a", 61 | "index": 1, 62 | "guid": "6110ae0e-bc6e-4df1-a60a-52876f98fde6", 63 | "isActive": true, 64 | "balance": "$1,967.19", 65 | "picture": "http://placehold.it/32x32", 66 | "age": 25, 67 | "eyeColor": "brown", 68 | "name": { 69 | "first": "Mcdaniel", 70 | "last": "Chaney" 71 | }, 72 | "company": "GREEKER", 73 | "email": "mcdaniel.chaney@greeker.co.uk", 74 | "phone": "+1 (988) 436-3981", 75 | "address": "789 Essex Street, Harborton, Massachusetts, 3262", 76 | "about": "Lorem dolor officia et officia. Ullamco nulla amet consequat culpa occaecat id esse. Do ea ex excepteur eu occaecat Lorem incididunt commodo ex ullamco anim anim adipisicing. Cupidatat duis ex sit Lorem incididunt velit eu sit enim est commodo consectetur. Pariatur laborum aliqua in duis proident in do aliquip reprehenderit quis sint elit elit. Voluptate cupidatat enim do occaecat ex sunt dolore. Culpa sint adipisicing non cillum ea in sit.", 77 | "registered": "Monday, October 12, 2015 8:38 AM", 78 | "latitude": "-32.986009", 79 | "longitude": "-172.995706", 80 | "tags": [ 81 | "sint", 82 | "officia", 83 | "ipsum", 84 | "eiusmod", 85 | "mollit" 86 | ], 87 | "range": [ 88 | 0, 89 | 1, 90 | 2, 91 | 3, 92 | 4, 93 | 5, 94 | 6, 95 | 7, 96 | 8, 97 | 9 98 | ], 99 | "friends": [ 100 | { 101 | "id": 0, 102 | "name": "Perry Lynn" 103 | }, 104 | { 105 | "id": 1, 106 | "name": "May Clemons" 107 | }, 108 | { 109 | "id": 2, 110 | "name": "Melissa Klein" 111 | } 112 | ], 113 | "greeting": "Hello, Mcdaniel! You have 7 unread messages.", 114 | "favoriteFruit": "strawberry" 115 | }, 116 | { 117 | "_id": "57923a91934371c1f957c876", 118 | "index": 2, 119 | "guid": "c2bf9efa-54ff-4903-9a49-2f51158cb333", 120 | "isActive": true, 121 | "balance": "$3,182.19", 122 | "picture": "http://placehold.it/32x32", 123 | "age": 34, 124 | "eyeColor": "green", 125 | "name": { 126 | "first": "Dudley", 127 | "last": "Castaneda" 128 | }, 129 | "company": "TERRAGO", 130 | "email": "dudley.castaneda@terrago.biz", 131 | "phone": "+1 (981) 542-3083", 132 | "address": "881 Tiffany Place, Harold, Idaho, 9685", 133 | "about": "Qui ad sint est sit occaecat pariatur velit sit ullamco sint est incididunt anim reprehenderit. Deserunt enim qui amet ad ex adipisicing esse velit dolor id laboris sunt consequat. Officia aute anim eiusmod tempor anim Lorem culpa aute. Eu ad et in pariatur et mollit duis eu duis ad mollit exercitation eiusmod cupidatat.", 134 | "registered": "Tuesday, June 2, 2015 12:19 PM", 135 | "latitude": "20.296917", 136 | "longitude": "138.238473", 137 | "tags": [ 138 | "esse", 139 | "sit", 140 | "velit", 141 | "anim", 142 | "eiusmod" 143 | ], 144 | "range": [ 145 | 0, 146 | 1, 147 | 2, 148 | 3, 149 | 4, 150 | 5, 151 | 6, 152 | 7, 153 | 8, 154 | 9 155 | ], 156 | "friends": [ 157 | { 158 | "id": 0, 159 | "name": "Kent Black" 160 | }, 161 | { 162 | "id": 1, 163 | "name": "Angeline Donaldson" 164 | }, 165 | { 166 | "id": 2, 167 | "name": "Clements Larsen" 168 | } 169 | ], 170 | "greeting": "Hello, Dudley! You have 10 unread messages.", 171 | "favoriteFruit": "apple" 172 | }, 173 | { 174 | "_id": "57923a9112bfbe3d7589e2f8", 175 | "index": 3, 176 | "guid": "b5616830-c170-41d3-96df-ad46d25067f5", 177 | "isActive": false, 178 | "balance": "$1,499.05", 179 | "picture": "http://placehold.it/32x32", 180 | "age": 32, 181 | "eyeColor": "blue", 182 | "name": { 183 | "first": "Mcneil", 184 | "last": "Ellis" 185 | }, 186 | "company": "PREMIANT", 187 | "email": "mcneil.ellis@premiant.me", 188 | "phone": "+1 (951) 427-2177", 189 | "address": "458 Madoc Avenue, Wescosville, Tennessee, 288", 190 | "about": "Cillum ullamco id tempor duis commodo anim Lorem. Magna aute commodo voluptate tempor ea officia minim minim officia. Nulla esse ut adipisicing adipisicing mollit laborum nulla laborum esse labore sunt irure incididunt. Nulla sunt ex culpa nostrud sunt magna.", 191 | "registered": "Monday, September 1, 2014 8:24 AM", 192 | "latitude": "47.852095", 193 | "longitude": "-92.942884", 194 | "tags": [ 195 | "tempor", 196 | "quis", 197 | "adipisicing", 198 | "eu", 199 | "ex" 200 | ], 201 | "range": [ 202 | 0, 203 | 1, 204 | 2, 205 | 3, 206 | 4, 207 | 5, 208 | 6, 209 | 7, 210 | 8, 211 | 9 212 | ], 213 | "friends": [ 214 | { 215 | "id": 0, 216 | "name": "Ruiz Golden" 217 | }, 218 | { 219 | "id": 1, 220 | "name": "Cantu Bradford" 221 | }, 222 | { 223 | "id": 2, 224 | "name": "Kaitlin Marshall" 225 | } 226 | ], 227 | "greeting": "Hello, Mcneil! You have 8 unread messages.", 228 | "favoriteFruit": "strawberry" 229 | }, 230 | { 231 | "_id": "57923a91b6f6df9fc46950cf", 232 | "index": 4, 233 | "guid": "6087c264-4b43-4a79-938c-b6e0828efaab", 234 | "isActive": true, 235 | "balance": "$1,802.66", 236 | "picture": "http://placehold.it/32x32", 237 | "age": 23, 238 | "eyeColor": "green", 239 | "name": { 240 | "first": "Shields", 241 | "last": "Santiago" 242 | }, 243 | "company": "PAPRIKUT", 244 | "email": "shields.santiago@paprikut.net", 245 | "phone": "+1 (883) 568-3460", 246 | "address": "959 Meserole Street, Elfrida, Wisconsin, 3154", 247 | "about": "Ullamco et elit eu nostrud fugiat do. Ut minim anim anim magna fugiat. Ut amet ipsum dolore pariatur duis. Mollit minim sunt irure proident cillum.", 248 | "registered": "Saturday, April 4, 2015 5:10 AM", 249 | "latitude": "62.135082", 250 | "longitude": "-111.592041", 251 | "tags": [ 252 | "sint", 253 | "commodo", 254 | "ullamco", 255 | "veniam", 256 | "dolor" 257 | ], 258 | "range": [ 259 | 0, 260 | 1, 261 | 2, 262 | 3, 263 | 4, 264 | 5, 265 | 6, 266 | 7, 267 | 8, 268 | 9 269 | ], 270 | "friends": [ 271 | { 272 | "id": 0, 273 | "name": "Leah Rice" 274 | }, 275 | { 276 | "id": 1, 277 | "name": "Ruth Merrill" 278 | }, 279 | { 280 | "id": 2, 281 | "name": "Ava Jefferson" 282 | } 283 | ], 284 | "greeting": "Hello, Shields! You have 7 unread messages.", 285 | "favoriteFruit": "apple" 286 | }, 287 | { 288 | "_id": "57923a9114fb2fd691b1cf18", 289 | "index": 5, 290 | "guid": "a6b5ff16-a1a8-45e0-a05a-8d9bf68e01eb", 291 | "isActive": false, 292 | "balance": "$1,310.36", 293 | "picture": "http://placehold.it/32x32", 294 | "age": 29, 295 | "eyeColor": "brown", 296 | "name": { 297 | "first": "Horn", 298 | "last": "Hendrix" 299 | }, 300 | "company": "VETRON", 301 | "email": "horn.hendrix@vetron.us", 302 | "phone": "+1 (888) 457-2512", 303 | "address": "163 Irving Avenue, Longoria, North Carolina, 8637", 304 | "about": "Et tempor culpa laboris id et cupidatat. Duis enim officia cupidatat in Lorem proident nulla id. Ex ea proident officia mollit eiusmod nisi magna. Nostrud nulla adipisicing do deserunt est aliqua do et dolor.", 305 | "registered": "Thursday, March 17, 2016 11:05 AM", 306 | "latitude": "41.629224", 307 | "longitude": "34.254476", 308 | "tags": [ 309 | "non", 310 | "nostrud", 311 | "amet", 312 | "ut", 313 | "esse" 314 | ], 315 | "range": [ 316 | 0, 317 | 1, 318 | 2, 319 | 3, 320 | 4, 321 | 5, 322 | 6, 323 | 7, 324 | 8, 325 | 9 326 | ], 327 | "friends": [ 328 | { 329 | "id": 0, 330 | "name": "Elizabeth Harrison" 331 | }, 332 | { 333 | "id": 1, 334 | "name": "Kenya Landry" 335 | }, 336 | { 337 | "id": 2, 338 | "name": "Marsha Dean" 339 | } 340 | ], 341 | "greeting": "Hello, Horn! You have 5 unread messages.", 342 | "favoriteFruit": "banana" 343 | }, 344 | { 345 | "_id": "57923a91a93b95b4382a17b3", 346 | "index": 6, 347 | "guid": "f22415dd-6ea1-4003-9e9a-75a6b33a33d9", 348 | "isActive": true, 349 | "balance": "$2,396.03", 350 | "picture": "http://placehold.it/32x32", 351 | "age": 25, 352 | "eyeColor": "green", 353 | "name": { 354 | "first": "Eliza", 355 | "last": "Tyler" 356 | }, 357 | "company": "SPHERIX", 358 | "email": "eliza.tyler@spherix.name", 359 | "phone": "+1 (876) 482-3238", 360 | "address": "981 Corbin Place, Bowden, Illinois, 2479", 361 | "about": "Voluptate laboris labore consequat consequat dolore est ullamco aliquip id ad magna consequat. Pariatur occaecat laboris ut cillum qui sit pariatur minim non consequat. Anim non laborum dolor reprehenderit reprehenderit. Et laborum cillum mollit id minim. Deserunt commodo anim do eu veniam irure Lorem ea. Amet aliqua eiusmod proident deserunt labore nisi adipisicing velit tempor labore.", 362 | "registered": "Sunday, January 31, 2016 10:20 PM", 363 | "latitude": "36.091364", 364 | "longitude": "-9.842138", 365 | "tags": [ 366 | "ea", 367 | "in", 368 | "fugiat", 369 | "in", 370 | "laborum" 371 | ], 372 | "range": [ 373 | 0, 374 | 1, 375 | 2, 376 | 3, 377 | 4, 378 | 5, 379 | 6, 380 | 7, 381 | 8, 382 | 9 383 | ], 384 | "friends": [ 385 | { 386 | "id": 0, 387 | "name": "Gabriela Mitchell" 388 | }, 389 | { 390 | "id": 1, 391 | "name": "Duncan Rivas" 392 | }, 393 | { 394 | "id": 2, 395 | "name": "Dunlap Leblanc" 396 | } 397 | ], 398 | "greeting": "Hello, Eliza! You have 5 unread messages.", 399 | "favoriteFruit": "strawberry" 400 | }, 401 | { 402 | "_id": "57923a911d6d4ba431c15432", 403 | "index": 7, 404 | "guid": "d2882ba2-47b4-489c-bd3e-80d829d627b6", 405 | "isActive": false, 406 | "balance": "$3,935.57", 407 | "picture": "http://placehold.it/32x32", 408 | "age": 30, 409 | "eyeColor": "brown", 410 | "name": { 411 | "first": "Melendez", 412 | "last": "Buchanan" 413 | }, 414 | "company": "DIGIAL", 415 | "email": "melendez.buchanan@digial.biz", 416 | "phone": "+1 (871) 477-2868", 417 | "address": "931 Schenectady Avenue, Faxon, Northern Mariana Islands, 8772", 418 | "about": "Nostrud pariatur magna ullamco est consequat. Ut laborum proident nisi do nulla velit culpa. Officia ipsum elit dolore pariatur nostrud eiusmod deserunt enim adipisicing adipisicing. Qui dolor elit pariatur sunt anim aliqua. Laboris do sint culpa proident mollit id fugiat eu minim ullamco.", 419 | "registered": "Wednesday, April 20, 2016 1:46 PM", 420 | "latitude": "36.526109", 421 | "longitude": "18.225863", 422 | "tags": [ 423 | "do", 424 | "eu", 425 | "exercitation", 426 | "eu", 427 | "minim" 428 | ], 429 | "range": [ 430 | 0, 431 | 1, 432 | 2, 433 | 3, 434 | 4, 435 | 5, 436 | 6, 437 | 7, 438 | 8, 439 | 9 440 | ], 441 | "friends": [ 442 | { 443 | "id": 0, 444 | "name": "Ray Johnston" 445 | }, 446 | { 447 | "id": 1, 448 | "name": "Gilmore Cantu" 449 | }, 450 | { 451 | "id": 2, 452 | "name": "Gale Castro" 453 | } 454 | ], 455 | "greeting": "Hello, Melendez! You have 8 unread messages.", 456 | "favoriteFruit": "strawberry" 457 | } 458 | ] -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/json3.js: -------------------------------------------------------------------------------- 1 | var json3 = require('../../third-party/json3'); 2 | 3 | module.exports = function(data, helpers) { 4 | return json3.stringify(data); 5 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/stringify-native.js: -------------------------------------------------------------------------------- 1 | module.exports = function(data, helpers) { 2 | return JSON.stringify(data); 3 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-large/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/data.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | "id": 1, 3 | "first_name": "Jimmy", 4 | "last_name": "Hansen", 5 | "email": "jhansen0@skyrock.com", 6 | "gender": "Male", 7 | "ip_address": "166.6.70.130" 8 | }]; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/json3.js: -------------------------------------------------------------------------------- 1 | var json3 = require('../../third-party/json3'); 2 | 3 | module.exports = function(data, helpers) { 4 | return json3.stringify(data); 5 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/stringify-native.js: -------------------------------------------------------------------------------- 1 | module.exports = function(data, helpers) { 2 | return JSON.stringify(data); 3 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/simple-small/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/circular-json.js: -------------------------------------------------------------------------------- 1 | 2 | var CircularJSON = require('circular-json'); 3 | 4 | module.exports = function(data, helpers) { 5 | return CircularJSON.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/data.js: -------------------------------------------------------------------------------- 1 | var object = {}; 2 | object.arr = [ 3 | object, object 4 | ]; 5 | object.arr.push(object.arr); 6 | object.obj = object; 7 | 8 | module.exports = [object]; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/lave.js: -------------------------------------------------------------------------------- 1 | var generate = require('escodegen').generate; 2 | var lave = require('lave'); 3 | 4 | var options = {generate}; 5 | 6 | module.exports = function(data, helpers) { 7 | return lave(data, options); 8 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/refify.js: -------------------------------------------------------------------------------- 1 | 2 | var refify = require('refify'); 3 | 4 | module.exports = function(data, helpers) { 5 | return refify.stringify(data); 6 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/warp10-stringify.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.stringify(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/benchmarks/test-a/warp10.js: -------------------------------------------------------------------------------- 1 | var warp10 = require('../../../'); 2 | 3 | var options = { safe: false }; 4 | 5 | module.exports = function(data, helpers) { 6 | return warp10.serialize(data, options); 7 | }; -------------------------------------------------------------------------------- /benchmark/third-party/json3.js: -------------------------------------------------------------------------------- 1 | /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ 2 | ;(function () { 3 | // Detect the `define` function exposed by asynchronous module loaders. The 4 | // strict `define` check is necessary for compatibility with `r.js`. 5 | var isLoader = typeof define === "function" && define.amd; 6 | 7 | // A set of types used to distinguish objects from primitives. 8 | var objectTypes = { 9 | "function": true, 10 | "object": true 11 | }; 12 | 13 | // Detect the `exports` object exposed by CommonJS implementations. 14 | var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; 15 | 16 | // Use the `global` object exposed by Node (including Browserify via 17 | // `insert-module-globals`), Narwhal, and Ringo as the default context, 18 | // and the `window` object in browsers. Rhino exports a `global` function 19 | // instead. 20 | var root = objectTypes[typeof window] && window || this, 21 | freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global; 22 | 23 | if (freeGlobal && (freeGlobal["global"] === freeGlobal || freeGlobal["window"] === freeGlobal || freeGlobal["self"] === freeGlobal)) { 24 | root = freeGlobal; 25 | } 26 | 27 | // Public: Initializes JSON 3 using the given `context` object, attaching the 28 | // `stringify` and `parse` functions to the specified `exports` object. 29 | function runInContext(context, exports) { 30 | context || (context = root["Object"]()); 31 | exports || (exports = root["Object"]()); 32 | 33 | // Native constructor aliases. 34 | var Number = context["Number"] || root["Number"], 35 | String = context["String"] || root["String"], 36 | Object = context["Object"] || root["Object"], 37 | Date = context["Date"] || root["Date"], 38 | SyntaxError = context["SyntaxError"] || root["SyntaxError"], 39 | TypeError = context["TypeError"] || root["TypeError"], 40 | Math = context["Math"] || root["Math"], 41 | nativeJSON = context["JSON"] || root["JSON"]; 42 | 43 | // Convenience aliases. 44 | var objectProto = Object.prototype, 45 | getClass = objectProto.toString, 46 | isProperty, forEach, undef; 47 | 48 | // Test the `Date#getUTC*` methods. Based on work by @Yaffle. 49 | var isExtended = new Date(-3509827334573292); 50 | try { 51 | // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical 52 | // results for certain dates in Opera >= 10.53. 53 | isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && 54 | // Safari < 2.0.2 stores the internal millisecond time value correctly, 55 | // but clips the values returned by the date methods to the range of 56 | // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). 57 | isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; 58 | } catch (exception) {} 59 | 60 | // Internal: Determines whether the native `JSON.stringify` and `parse` 61 | // implementations are spec-compliant. Based on work by Ken Snyder. 62 | function has(name) { 63 | return false; 64 | } 65 | 66 | if (!has("json")) { 67 | // Common `[[Class]]` name aliases. 68 | var functionClass = "[object Function]", 69 | dateClass = "[object Date]", 70 | numberClass = "[object Number]", 71 | stringClass = "[object String]", 72 | arrayClass = "[object Array]", 73 | booleanClass = "[object Boolean]"; 74 | 75 | // Detect incomplete support for accessing string characters by index. 76 | var charIndexBuggy = has("bug-string-char-index"); 77 | 78 | // Define additional utility methods if the `Date` methods are buggy. 79 | if (!isExtended) { 80 | var floor = Math.floor; 81 | // A mapping between the months of the year and the number of days between 82 | // January 1st and the first of the respective month. 83 | var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; 84 | // Internal: Calculates the number of days between the Unix epoch and the 85 | // first day of the given month. 86 | var getDay = function (year, month) { 87 | return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); 88 | }; 89 | } 90 | 91 | // Internal: Determines if a property is a direct property of the given 92 | // object. Delegates to the native `Object#hasOwnProperty` method. 93 | if (!(isProperty = objectProto.hasOwnProperty)) { 94 | isProperty = function (property) { 95 | var members = {}, constructor; 96 | if ((members.__proto__ = null, members.__proto__ = { 97 | // The *proto* property cannot be set multiple times in recent 98 | // versions of Firefox and SeaMonkey. 99 | "toString": 1 100 | }, members).toString != getClass) { 101 | // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but 102 | // supports the mutable *proto* property. 103 | isProperty = function (property) { 104 | // Capture and break the object's prototype chain (see section 8.6.2 105 | // of the ES 5.1 spec). The parenthesized expression prevents an 106 | // unsafe transformation by the Closure Compiler. 107 | var original = this.__proto__, result = property in (this.__proto__ = null, this); 108 | // Restore the original prototype chain. 109 | this.__proto__ = original; 110 | return result; 111 | }; 112 | } else { 113 | // Capture a reference to the top-level `Object` constructor. 114 | constructor = members.constructor; 115 | // Use the `constructor` property to simulate `Object#hasOwnProperty` in 116 | // other environments. 117 | isProperty = function (property) { 118 | var parent = (this.constructor || constructor).prototype; 119 | return property in this && !(property in parent && this[property] === parent[property]); 120 | }; 121 | } 122 | members = null; 123 | return isProperty.call(this, property); 124 | }; 125 | } 126 | 127 | // Internal: Normalizes the `for...in` iteration algorithm across 128 | // environments. Each enumerated key is yielded to a `callback` function. 129 | forEach = function (object, callback) { 130 | var size = 0, Properties, members, property; 131 | 132 | // Tests for bugs in the current environment's `for...in` algorithm. The 133 | // `valueOf` property inherits the non-enumerable flag from 134 | // `Object.prototype` in older versions of IE, Netscape, and Mozilla. 135 | (Properties = function () { 136 | this.valueOf = 0; 137 | }).prototype.valueOf = 0; 138 | 139 | // Iterate over a new instance of the `Properties` class. 140 | members = new Properties(); 141 | for (property in members) { 142 | // Ignore all properties inherited from `Object.prototype`. 143 | if (isProperty.call(members, property)) { 144 | size++; 145 | } 146 | } 147 | Properties = members = null; 148 | 149 | // Normalize the iteration algorithm. 150 | if (!size) { 151 | // A list of non-enumerable properties inherited from `Object.prototype`. 152 | members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; 153 | // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable 154 | // properties. 155 | forEach = function (object, callback) { 156 | var isFunction = getClass.call(object) == functionClass, property, length; 157 | var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty; 158 | for (property in object) { 159 | // Gecko <= 1.0 enumerates the `prototype` property of functions under 160 | // certain conditions; IE does not. 161 | if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { 162 | callback(property); 163 | } 164 | } 165 | // Manually invoke the callback for each non-enumerable property. 166 | for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); 167 | }; 168 | } else if (size == 2) { 169 | // Safari <= 2.0.4 enumerates shadowed properties twice. 170 | forEach = function (object, callback) { 171 | // Create a set of iterated properties. 172 | var members = {}, isFunction = getClass.call(object) == functionClass, property; 173 | for (property in object) { 174 | // Store each property name to prevent double enumeration. The 175 | // `prototype` property of functions is not enumerated due to cross- 176 | // environment inconsistencies. 177 | if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { 178 | callback(property); 179 | } 180 | } 181 | }; 182 | } else { 183 | // No bugs detected; use the standard `for...in` algorithm. 184 | forEach = function (object, callback) { 185 | var isFunction = getClass.call(object) == functionClass, property, isConstructor; 186 | for (property in object) { 187 | if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { 188 | callback(property); 189 | } 190 | } 191 | // Manually invoke the callback for the `constructor` property due to 192 | // cross-environment inconsistencies. 193 | if (isConstructor || isProperty.call(object, (property = "constructor"))) { 194 | callback(property); 195 | } 196 | }; 197 | } 198 | return forEach(object, callback); 199 | }; 200 | 201 | // Public: Serializes a JavaScript `value` as a JSON string. The optional 202 | // `filter` argument may specify either a function that alters how object and 203 | // array members are serialized, or an array of strings and numbers that 204 | // indicates which properties should be serialized. The optional `width` 205 | // argument may be either a string or number that specifies the indentation 206 | // level of the output. 207 | if (!has("json-stringify")) { 208 | // Internal: A map of control characters and their escaped equivalents. 209 | var Escapes = { 210 | 92: "\\\\", 211 | 34: '\\"', 212 | 8: "\\b", 213 | 12: "\\f", 214 | 10: "\\n", 215 | 13: "\\r", 216 | 9: "\\t" 217 | }; 218 | 219 | // Internal: Converts `value` into a zero-padded string such that its 220 | // length is at least equal to `width`. The `width` must be <= 6. 221 | var leadingZeroes = "000000"; 222 | var toPaddedString = function (width, value) { 223 | // The `|| 0` expression is necessary to work around a bug in 224 | // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. 225 | return (leadingZeroes + (value || 0)).slice(-width); 226 | }; 227 | 228 | // Internal: Double-quotes a string `value`, replacing all ASCII control 229 | // characters (characters with code unit values between 0 and 31) with 230 | // their escaped equivalents. This is an implementation of the 231 | // `Quote(value)` operation defined in ES 5.1 section 15.12.3. 232 | var unicodePrefix = "\\u00"; 233 | var quote = function (value) { 234 | var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10; 235 | var symbols = useCharIndex && (charIndexBuggy ? value.split("") : value); 236 | for (; index < length; index++) { 237 | var charCode = value.charCodeAt(index); 238 | // If the character is a control character, append its Unicode or 239 | // shorthand escape sequence; otherwise, append the character as-is. 240 | switch (charCode) { 241 | case 8: case 9: case 10: case 12: case 13: case 34: case 92: 242 | result += Escapes[charCode]; 243 | break; 244 | default: 245 | if (charCode < 32) { 246 | result += unicodePrefix + toPaddedString(2, charCode.toString(16)); 247 | break; 248 | } 249 | result += useCharIndex ? symbols[index] : value.charAt(index); 250 | } 251 | } 252 | return result + '"'; 253 | }; 254 | 255 | // Internal: Recursively serializes an object. Implements the 256 | // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. 257 | var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { 258 | var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; 259 | try { 260 | // Necessary for host object support. 261 | value = object[property]; 262 | } catch (exception) {} 263 | if (typeof value == "object" && value) { 264 | className = getClass.call(value); 265 | if (className == dateClass && !isProperty.call(value, "toJSON")) { 266 | if (value > -1 / 0 && value < 1 / 0) { 267 | // Dates are serialized according to the `Date#toJSON` method 268 | // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 269 | // for the ISO 8601 date time string format. 270 | if (getDay) { 271 | // Manually compute the year, month, date, hours, minutes, 272 | // seconds, and milliseconds if the `getUTC*` methods are 273 | // buggy. Adapted from @Yaffle's `date-shim` project. 274 | date = floor(value / 864e5); 275 | for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); 276 | for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); 277 | date = 1 + date - getDay(year, month); 278 | // The `time` value specifies the time within the day (see ES 279 | // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used 280 | // to compute `A modulo B`, as the `%` operator does not 281 | // correspond to the `modulo` operation for negative numbers. 282 | time = (value % 864e5 + 864e5) % 864e5; 283 | // The hours, minutes, seconds, and milliseconds are obtained by 284 | // decomposing the time within the day. See section 15.9.1.10. 285 | hours = floor(time / 36e5) % 24; 286 | minutes = floor(time / 6e4) % 60; 287 | seconds = floor(time / 1e3) % 60; 288 | milliseconds = time % 1e3; 289 | } else { 290 | year = value.getUTCFullYear(); 291 | month = value.getUTCMonth(); 292 | date = value.getUTCDate(); 293 | hours = value.getUTCHours(); 294 | minutes = value.getUTCMinutes(); 295 | seconds = value.getUTCSeconds(); 296 | milliseconds = value.getUTCMilliseconds(); 297 | } 298 | // Serialize extended years correctly. 299 | value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + 300 | "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + 301 | // Months, dates, hours, minutes, and seconds should have two 302 | // digits; milliseconds should have three. 303 | "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + 304 | // Milliseconds are optional in ES 5.0, but required in 5.1. 305 | "." + toPaddedString(3, milliseconds) + "Z"; 306 | } else { 307 | value = null; 308 | } 309 | } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { 310 | // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the 311 | // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 312 | // ignores all `toJSON` methods on these objects unless they are 313 | // defined directly on an instance. 314 | value = value.toJSON(property); 315 | } 316 | } 317 | if (callback) { 318 | // If a replacement function was provided, call it to obtain the value 319 | // for serialization. 320 | value = callback.call(object, property, value); 321 | } 322 | if (value === null) { 323 | return "null"; 324 | } 325 | className = getClass.call(value); 326 | if (className == booleanClass) { 327 | // Booleans are represented literally. 328 | return "" + value; 329 | } else if (className == numberClass) { 330 | // JSON numbers must be finite. `Infinity` and `NaN` are serialized as 331 | // `"null"`. 332 | return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; 333 | } else if (className == stringClass) { 334 | // Strings are double-quoted and escaped. 335 | return quote("" + value); 336 | } 337 | // Recursively serialize objects and arrays. 338 | if (typeof value == "object") { 339 | // Check for cyclic structures. This is a linear search; performance 340 | // is inversely proportional to the number of unique nested objects. 341 | for (length = stack.length; length--;) { 342 | if (stack[length] === value) { 343 | // Cyclic structures cannot be serialized by `JSON.stringify`. 344 | throw TypeError(); 345 | } 346 | } 347 | // Add the object to the stack of traversed objects. 348 | stack.push(value); 349 | results = []; 350 | // Save the current indentation level and indent one additional level. 351 | prefix = indentation; 352 | indentation += whitespace; 353 | if (className == arrayClass) { 354 | // Recursively serialize array elements. 355 | for (index = 0, length = value.length; index < length; index++) { 356 | element = serialize(index, value, callback, properties, whitespace, indentation, stack); 357 | results.push(element === undef ? "null" : element); 358 | } 359 | result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; 360 | } else { 361 | // Recursively serialize object members. Members are selected from 362 | // either a user-specified list of property names, or the object 363 | // itself. 364 | forEach(properties || value, function (property) { 365 | var element = serialize(property, value, callback, properties, whitespace, indentation, stack); 366 | if (element !== undef) { 367 | // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} 368 | // is not the empty string, let `member` {quote(property) + ":"} 369 | // be the concatenation of `member` and the `space` character." 370 | // The "`space` character" refers to the literal space 371 | // character, not the `space` {width} argument provided to 372 | // `JSON.stringify`. 373 | results.push(quote(property) + ":" + (whitespace ? " " : "") + element); 374 | } 375 | }); 376 | result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; 377 | } 378 | // Remove the object from the traversed object stack. 379 | stack.pop(); 380 | return result; 381 | } 382 | }; 383 | 384 | // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. 385 | exports.stringify = function (source, filter, width) { 386 | var whitespace, callback, properties, className; 387 | if (objectTypes[typeof filter] && filter) { 388 | if ((className = getClass.call(filter)) == functionClass) { 389 | callback = filter; 390 | } else if (className == arrayClass) { 391 | // Convert the property names array into a makeshift set. 392 | properties = {}; 393 | for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); 394 | } 395 | } 396 | if (width) { 397 | if ((className = getClass.call(width)) == numberClass) { 398 | // Convert the `width` to an integer and create a string containing 399 | // `width` number of space characters. 400 | if ((width -= width % 1) > 0) { 401 | for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); 402 | } 403 | } else if (className == stringClass) { 404 | whitespace = width.length <= 10 ? width : width.slice(0, 10); 405 | } 406 | } 407 | // Opera <= 7.54u2 discards the values associated with empty string keys 408 | // (`""`) only if they are used directly within an object member list 409 | // (e.g., `!("" in { "": 1})`). 410 | return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); 411 | }; 412 | } 413 | 414 | // Public: Parses a JSON source string. 415 | if (!has("json-parse")) { 416 | var fromCharCode = String.fromCharCode; 417 | 418 | // Internal: A map of escaped control characters and their unescaped 419 | // equivalents. 420 | var Unescapes = { 421 | 92: "\\", 422 | 34: '"', 423 | 47: "/", 424 | 98: "\b", 425 | 116: "\t", 426 | 110: "\n", 427 | 102: "\f", 428 | 114: "\r" 429 | }; 430 | 431 | // Internal: Stores the parser state. 432 | var Index, Source; 433 | 434 | // Internal: Resets the parser state and throws a `SyntaxError`. 435 | var abort = function () { 436 | Index = Source = null; 437 | throw SyntaxError(); 438 | }; 439 | 440 | // Internal: Returns the next token, or `"$"` if the parser has reached 441 | // the end of the source string. A token may be a string, number, `null` 442 | // literal, or Boolean literal. 443 | var lex = function () { 444 | var source = Source, length = source.length, value, begin, position, isSigned, charCode; 445 | while (Index < length) { 446 | charCode = source.charCodeAt(Index); 447 | switch (charCode) { 448 | case 9: case 10: case 13: case 32: 449 | // Skip whitespace tokens, including tabs, carriage returns, line 450 | // feeds, and space characters. 451 | Index++; 452 | break; 453 | case 123: case 125: case 91: case 93: case 58: case 44: 454 | // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at 455 | // the current position. 456 | value = charIndexBuggy ? source.charAt(Index) : source[Index]; 457 | Index++; 458 | return value; 459 | case 34: 460 | // `"` delimits a JSON string; advance to the next character and 461 | // begin parsing the string. String tokens are prefixed with the 462 | // sentinel `@` character to distinguish them from punctuators and 463 | // end-of-string tokens. 464 | for (value = "@", Index++; Index < length;) { 465 | charCode = source.charCodeAt(Index); 466 | if (charCode < 32) { 467 | // Unescaped ASCII control characters (those with a code unit 468 | // less than the space character) are not permitted. 469 | abort(); 470 | } else if (charCode == 92) { 471 | // A reverse solidus (`\`) marks the beginning of an escaped 472 | // control character (including `"`, `\`, and `/`) or Unicode 473 | // escape sequence. 474 | charCode = source.charCodeAt(++Index); 475 | switch (charCode) { 476 | case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: 477 | // Revive escaped control characters. 478 | value += Unescapes[charCode]; 479 | Index++; 480 | break; 481 | case 117: 482 | // `\u` marks the beginning of a Unicode escape sequence. 483 | // Advance to the first character and validate the 484 | // four-digit code point. 485 | begin = ++Index; 486 | for (position = Index + 4; Index < position; Index++) { 487 | charCode = source.charCodeAt(Index); 488 | // A valid sequence comprises four hexdigits (case- 489 | // insensitive) that form a single hexadecimal value. 490 | if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { 491 | // Invalid Unicode escape sequence. 492 | abort(); 493 | } 494 | } 495 | // Revive the escaped character. 496 | value += fromCharCode("0x" + source.slice(begin, Index)); 497 | break; 498 | default: 499 | // Invalid escape sequence. 500 | abort(); 501 | } 502 | } else { 503 | if (charCode == 34) { 504 | // An unescaped double-quote character marks the end of the 505 | // string. 506 | break; 507 | } 508 | charCode = source.charCodeAt(Index); 509 | begin = Index; 510 | // Optimize for the common case where a string is valid. 511 | while (charCode >= 32 && charCode != 92 && charCode != 34) { 512 | charCode = source.charCodeAt(++Index); 513 | } 514 | // Append the string as-is. 515 | value += source.slice(begin, Index); 516 | } 517 | } 518 | if (source.charCodeAt(Index) == 34) { 519 | // Advance to the next character and return the revived string. 520 | Index++; 521 | return value; 522 | } 523 | // Unterminated string. 524 | abort(); 525 | default: 526 | // Parse numbers and literals. 527 | begin = Index; 528 | // Advance past the negative sign, if one is specified. 529 | if (charCode == 45) { 530 | isSigned = true; 531 | charCode = source.charCodeAt(++Index); 532 | } 533 | // Parse an integer or floating-point value. 534 | if (charCode >= 48 && charCode <= 57) { 535 | // Leading zeroes are interpreted as octal literals. 536 | if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { 537 | // Illegal octal literal. 538 | abort(); 539 | } 540 | isSigned = false; 541 | // Parse the integer component. 542 | for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); 543 | // Floats cannot contain a leading decimal point; however, this 544 | // case is already accounted for by the parser. 545 | if (source.charCodeAt(Index) == 46) { 546 | position = ++Index; 547 | // Parse the decimal component. 548 | for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); 549 | if (position == Index) { 550 | // Illegal trailing decimal. 551 | abort(); 552 | } 553 | Index = position; 554 | } 555 | // Parse exponents. The `e` denoting the exponent is 556 | // case-insensitive. 557 | charCode = source.charCodeAt(Index); 558 | if (charCode == 101 || charCode == 69) { 559 | charCode = source.charCodeAt(++Index); 560 | // Skip past the sign following the exponent, if one is 561 | // specified. 562 | if (charCode == 43 || charCode == 45) { 563 | Index++; 564 | } 565 | // Parse the exponential component. 566 | for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); 567 | if (position == Index) { 568 | // Illegal empty exponent. 569 | abort(); 570 | } 571 | Index = position; 572 | } 573 | // Coerce the parsed value to a JavaScript number. 574 | return +source.slice(begin, Index); 575 | } 576 | // A negative sign may only precede numbers. 577 | if (isSigned) { 578 | abort(); 579 | } 580 | // `true`, `false`, and `null` literals. 581 | if (source.slice(Index, Index + 4) == "true") { 582 | Index += 4; 583 | return true; 584 | } else if (source.slice(Index, Index + 5) == "false") { 585 | Index += 5; 586 | return false; 587 | } else if (source.slice(Index, Index + 4) == "null") { 588 | Index += 4; 589 | return null; 590 | } 591 | // Unrecognized token. 592 | abort(); 593 | } 594 | } 595 | // Return the sentinel `$` character if the parser has reached the end 596 | // of the source string. 597 | return "$"; 598 | }; 599 | 600 | // Internal: Parses a JSON `value` token. 601 | var get = function (value) { 602 | var results, hasMembers; 603 | if (value == "$") { 604 | // Unexpected end of input. 605 | abort(); 606 | } 607 | if (typeof value == "string") { 608 | if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { 609 | // Remove the sentinel `@` character. 610 | return value.slice(1); 611 | } 612 | // Parse object and array literals. 613 | if (value == "[") { 614 | // Parses a JSON array, returning a new JavaScript array. 615 | results = []; 616 | for (;; hasMembers || (hasMembers = true)) { 617 | value = lex(); 618 | // A closing square bracket marks the end of the array literal. 619 | if (value == "]") { 620 | break; 621 | } 622 | // If the array literal contains elements, the current token 623 | // should be a comma separating the previous element from the 624 | // next. 625 | if (hasMembers) { 626 | if (value == ",") { 627 | value = lex(); 628 | if (value == "]") { 629 | // Unexpected trailing `,` in array literal. 630 | abort(); 631 | } 632 | } else { 633 | // A `,` must separate each array element. 634 | abort(); 635 | } 636 | } 637 | // Elisions and leading commas are not permitted. 638 | if (value == ",") { 639 | abort(); 640 | } 641 | results.push(get(value)); 642 | } 643 | return results; 644 | } else if (value == "{") { 645 | // Parses a JSON object, returning a new JavaScript object. 646 | results = {}; 647 | for (;; hasMembers || (hasMembers = true)) { 648 | value = lex(); 649 | // A closing curly brace marks the end of the object literal. 650 | if (value == "}") { 651 | break; 652 | } 653 | // If the object literal contains members, the current token 654 | // should be a comma separator. 655 | if (hasMembers) { 656 | if (value == ",") { 657 | value = lex(); 658 | if (value == "}") { 659 | // Unexpected trailing `,` in object literal. 660 | abort(); 661 | } 662 | } else { 663 | // A `,` must separate each object member. 664 | abort(); 665 | } 666 | } 667 | // Leading commas are not permitted, object property names must be 668 | // double-quoted strings, and a `:` must separate each property 669 | // name and value. 670 | if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { 671 | abort(); 672 | } 673 | results[value.slice(1)] = get(lex()); 674 | } 675 | return results; 676 | } 677 | // Unexpected token encountered. 678 | abort(); 679 | } 680 | return value; 681 | }; 682 | 683 | // Internal: Updates a traversed object member. 684 | var update = function (source, property, callback) { 685 | var element = walk(source, property, callback); 686 | if (element === undef) { 687 | delete source[property]; 688 | } else { 689 | source[property] = element; 690 | } 691 | }; 692 | 693 | // Internal: Recursively traverses a parsed JSON object, invoking the 694 | // `callback` function for each value. This is an implementation of the 695 | // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. 696 | var walk = function (source, property, callback) { 697 | var value = source[property], length; 698 | if (typeof value == "object" && value) { 699 | // `forEach` can't be used to traverse an array in Opera <= 8.54 700 | // because its `Object#hasOwnProperty` implementation returns `false` 701 | // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). 702 | if (getClass.call(value) == arrayClass) { 703 | for (length = value.length; length--;) { 704 | update(value, length, callback); 705 | } 706 | } else { 707 | forEach(value, function (property) { 708 | update(value, property, callback); 709 | }); 710 | } 711 | } 712 | return callback.call(source, property, value); 713 | }; 714 | 715 | // Public: `JSON.parse`. See ES 5.1 section 15.12.2. 716 | exports.parse = function (source, callback) { 717 | var result, value; 718 | Index = 0; 719 | Source = "" + source; 720 | result = get(lex()); 721 | // If a JSON string contains multiple tokens, it is invalid. 722 | if (lex() != "$") { 723 | abort(); 724 | } 725 | // Reset the parser state. 726 | Index = Source = null; 727 | return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; 728 | }; 729 | } 730 | } 731 | 732 | exports["runInContext"] = runInContext; 733 | return exports; 734 | } 735 | 736 | if (freeExports && !isLoader) { 737 | // Export for CommonJS environments. 738 | runInContext(root, freeExports); 739 | } else { 740 | // Export for web browsers and JavaScript engines. 741 | var nativeJSON = root.JSON, 742 | previousJSON = root["JSON3"], 743 | isRestored = false; 744 | 745 | var JSON3 = runInContext(root, (root["JSON3"] = { 746 | // Public: Restores the original value of the global `JSON` object and 747 | // returns a reference to the `JSON3` object. 748 | "noConflict": function () { 749 | if (!isRestored) { 750 | isRestored = true; 751 | root.JSON = nativeJSON; 752 | root["JSON3"] = previousJSON; 753 | nativeJSON = previousJSON = null; 754 | } 755 | return JSON3; 756 | } 757 | })); 758 | 759 | root.JSON = { 760 | "parse": JSON3.parse, 761 | "stringify": JSON3.stringify 762 | }; 763 | } 764 | 765 | // Export for asynchronous module loaders. 766 | if (isLoader) { 767 | define(function () { 768 | return JSON3; 769 | }); 770 | } 771 | }).call(this); 772 | -------------------------------------------------------------------------------- /finalize.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/finalize'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "warp10", 3 | "version": "1.3.6", 4 | "description": "Transport complex JavaScript objects from the server to the web browser at lightning fast speeds", 5 | "main": "src/index.js", 6 | "keywords": [ 7 | "JSON", 8 | "circular", 9 | "reference", 10 | "recursive", 11 | "recursion", 12 | "parse", 13 | "stringify", 14 | "eval" 15 | ], 16 | "scripts": { 17 | "test": "mocha test/ && npm run jshint", 18 | "benchmark": "matcha benchmark/benchmark.js", 19 | "test-coverage": "istanbul cover _mocha -- --ui bdd --reporter spec ./test/ && npm run jshint", 20 | "jshint": "jshint src/", 21 | "coveralls": "cat ./coverage/lcov.info | coveralls" 22 | }, 23 | "author": "Patrick Steele-Idem ", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "chai": "^3.5.0", 27 | "circular-json": "^0.3.0", 28 | "coveralls": "^2.11.11", 29 | "escodegen": "^1.8.0", 30 | "istanbul": "^0.4.4", 31 | "js-beautify": "^1.6.3", 32 | "jshint": "^2.9.2", 33 | "lave": "^1.1.10", 34 | "matcha": "^0.7.0", 35 | "mocha": "^2.5.3", 36 | "refify": "^1.0.0" 37 | }, 38 | "directories": { 39 | "test": "test" 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/patrick-steele-idem/warp10.git" 44 | }, 45 | "bugs": { 46 | "url": "https://github.com/patrick-steele-idem/warp10/issues" 47 | }, 48 | "homepage": "https://github.com/patrick-steele-idem/warp10#readme" 49 | } 50 | -------------------------------------------------------------------------------- /parse.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/parse'); -------------------------------------------------------------------------------- /serialize.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/serialize'); -------------------------------------------------------------------------------- /src/finalize.js: -------------------------------------------------------------------------------- 1 | var isArray = Array.isArray; 2 | 3 | function resolve(object, path, len) { 4 | var current = object; 5 | for (var i=0; ialert("Hello World")' 5 | }; 6 | 7 | var json = helpers.warp10.stringify(frank, { safe: true }); 8 | expect(json).to.not.contain(''); 9 | 10 | json = helpers.warp10.stringify(frank, { safe: false }); 11 | expect(json).to.contain(''); 12 | }; -------------------------------------------------------------------------------- /test/tests/xss/test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | module.exports = function(helpers) { 3 | var frank = { 4 | evil: '' 5 | }; 6 | 7 | var deserializationCode = helpers.warp10.serialize(frank); 8 | expect(deserializationCode).to.not.contain(''); 9 | 10 | deserializationCode = helpers.warp10.serialize(frank, { safe: false }); 11 | expect(deserializationCode).to.contain(''); 12 | }; -------------------------------------------------------------------------------- /test/util/autotest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var enabledTest = process.env.TEST; 5 | var path = require('path'); 6 | 7 | exports.scanDir = function(autoTestDir, run, options) { 8 | describe('autotest', function() { 9 | fs.readdirSync(autoTestDir) 10 | .forEach(function(name) { 11 | if (name.charAt(0) === '.') { 12 | return; 13 | } 14 | 15 | var itFunc = it; 16 | 17 | if (enabledTest && name === enabledTest) { 18 | itFunc = it.only; 19 | } 20 | 21 | var dir = path.join(autoTestDir, name); 22 | 23 | itFunc(`[${name}] `, function(done) { 24 | run({ 25 | dir: dir 26 | }, done); 27 | }); 28 | }); 29 | }); 30 | }; -------------------------------------------------------------------------------- /test/util/browser-load.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const vm = require('vm'); 4 | 5 | module.exports = function(code) { 6 | var sandbox = { 7 | }; 8 | 9 | sandbox.window = sandbox; 10 | 11 | var context = vm.createContext(sandbox); 12 | 13 | var script = new vm.Script(code, { 14 | filename: 'warp10', 15 | displayErrors: true 16 | }); 17 | 18 | script.runInContext(context); 19 | 20 | return sandbox; 21 | }; -------------------------------------------------------------------------------- /test/util/safeStringify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var COMMA = ','; 4 | var NULL = 'null'; 5 | 6 | function stringify(o) { 7 | var buffer = ''; 8 | 9 | var symbol = Symbol('warp10_test'); 10 | 11 | 12 | function append(str) { 13 | buffer += str; 14 | } 15 | 16 | var nextId = 0; 17 | 18 | function assignId(obj) { 19 | let value = nextId++; 20 | value = value.toString(); 21 | while (value.length < 3) { 22 | value = '0' + value; 23 | } 24 | value = 'id_' + value; 25 | 26 | obj[symbol] = value; 27 | 28 | return value; 29 | } 30 | 31 | 32 | function getId(obj) { 33 | return obj[symbol]; 34 | } 35 | 36 | function serialize(o, indent) { 37 | if (o == null) { 38 | append(NULL); 39 | return; 40 | } 41 | 42 | var id; 43 | 44 | if (o != null && typeof o === 'object') { 45 | 46 | if (o.constructor === Date) { 47 | append('[[Date:' + o + ']]'); 48 | return; 49 | } 50 | 51 | id = getId(o); 52 | if (id == null) { 53 | id = assignId(o); 54 | } else { 55 | append('"[[' + id + ']]"'); 56 | return; 57 | } 58 | } 59 | 60 | var constr = o.constructor; 61 | 62 | if (o === true || o === false || constr === Boolean) { 63 | append(o.toString()); 64 | } else if (Array.isArray(o)) { 65 | 66 | append('['); 67 | append('/* ' + id + '*/'); 68 | append('\n'); 69 | 70 | let len = o.length; 71 | for (let i=0; i { 105 | var value = o[key]; 106 | return value != null; 107 | }); 108 | 109 | keys.sort(); 110 | 111 | let len = keys.length; 112 | 113 | keys.forEach((k, i) => { 114 | var v = o[k]; 115 | append(indent + ' '); 116 | append(JSON.stringify(k)); 117 | append(":"); 118 | serialize(v, indent + ' '); 119 | 120 | if (i < len-1) { 121 | append(COMMA); 122 | } 123 | 124 | append('\n'); 125 | }); 126 | 127 | 128 | append(indent); 129 | append('}'); 130 | 131 | break; 132 | default: 133 | append(NULL); 134 | } 135 | } 136 | } 137 | 138 | serialize(o, ''); 139 | 140 | return buffer.toString(); 141 | } 142 | 143 | module.exports = stringify; -------------------------------------------------------------------------------- /test/util/stringify.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrick-steele-idem/warp10/1bd307b2bdd36bee54dcf8f9dee76778db361a87/test/util/stringify.js --------------------------------------------------------------------------------