Import Upstream version 6.0.0
This commit is contained in:
commit
924f2cd5f6
|
@ -0,0 +1,8 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "13:00"
|
||||||
|
open-pull-requests-limit: 10
|
|
@ -0,0 +1,22 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [12.x, 14.x, 16.x]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
|
@ -0,0 +1,3 @@
|
||||||
|
.nyc_output
|
||||||
|
coverage
|
||||||
|
node_modules
|
|
@ -0,0 +1,8 @@
|
||||||
|
artifacts/
|
||||||
|
coverage/
|
||||||
|
test/
|
||||||
|
.gitignore
|
||||||
|
.npmignore
|
||||||
|
.nyc_output
|
||||||
|
.vscode
|
||||||
|
.github
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright 2014 Yahoo! Inc.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the Yahoo! Inc. nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,142 @@
|
||||||
|
Serialize JavaScript
|
||||||
|
====================
|
||||||
|
|
||||||
|
Serialize JavaScript to a _superset_ of JSON that includes regular expressions, dates and functions.
|
||||||
|
|
||||||
|
[![npm Version][npm-badge]][npm]
|
||||||
|
[![Dependency Status][david-badge]][david]
|
||||||
|
![Test](https://github.com/yahoo/serialize-javascript/workflows/Test/badge.svg)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The code in this package began its life as an internal module to [express-state][]. To expand its usefulness, it now lives as `serialize-javascript` — an independent package on npm.
|
||||||
|
|
||||||
|
You're probably wondering: **What about `JSON.stringify()`!?** We've found that sometimes we need to serialize JavaScript **functions**, **regexps**, **dates**, **sets** or **maps**. A great example is a web app that uses client-side URL routing where the route definitions are regexps that need to be shared from the server to the client. But this module is also great for communicating between node processes.
|
||||||
|
|
||||||
|
The string returned from this package's single export function is literal JavaScript which can be saved to a `.js` file, or be embedded into an HTML document by making the content of a `<script>` element.
|
||||||
|
|
||||||
|
> **HTML characters and JavaScript line terminators are escaped automatically.**
|
||||||
|
|
||||||
|
Please note that serialization for ES6 Sets & Maps requires support for `Array.from` (not available in IE or Node < 0.12), or an `Array.from` polyfill.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install using npm:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ npm install serialize-javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
var serialize = require('serialize-javascript');
|
||||||
|
|
||||||
|
serialize({
|
||||||
|
str : 'string',
|
||||||
|
num : 0,
|
||||||
|
obj : {foo: 'foo'},
|
||||||
|
arr : [1, 2, 3],
|
||||||
|
bool : true,
|
||||||
|
nil : null,
|
||||||
|
undef: undefined,
|
||||||
|
inf : Infinity,
|
||||||
|
date : new Date("Thu, 28 Apr 2016 22:02:17 GMT"),
|
||||||
|
map : new Map([['hello', 'world']]),
|
||||||
|
set : new Set([123, 456]),
|
||||||
|
fn : function echo(arg) { return arg; },
|
||||||
|
re : /([^\s]+)/g,
|
||||||
|
big : BigInt(10),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The above will produce the following string output:
|
||||||
|
|
||||||
|
```js
|
||||||
|
'{"str":"string","num":0,"obj":{"foo":"foo"},"arr":[1,2,3],"bool":true,"nil":null,"undef":undefined,"inf":Infinity,"date":new Date("2016-04-28T22:02:17.000Z"),"map":new Map([["hello","world"]]),"set":new Set([123,456]),"fn":function echo(arg) { return arg; },"re":new RegExp("([^\\\\s]+)", "g"),"big":BigInt("10")}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: to produced a beautified string, you can pass an optional second argument to `serialize()` to define the number of spaces to be used for the indentation.
|
||||||
|
|
||||||
|
### Automatic Escaping of HTML Characters
|
||||||
|
|
||||||
|
A primary feature of this package is to serialize code to a string of literal JavaScript which can be embedded in an HTML document by adding it as the contents of the `<script>` element. In order to make this safe, HTML characters and JavaScript line terminators are escaped automatically.
|
||||||
|
|
||||||
|
```js
|
||||||
|
serialize({
|
||||||
|
haxorXSS: '</script>'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The above will produce the following string, HTML-escaped output which is safe to put into an HTML document as it will not cause the inline script element to terminate:
|
||||||
|
|
||||||
|
```js
|
||||||
|
'{"haxorXSS":"\\u003C\\u002Fscript\\u003E"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
> You can pass an optional `unsafe` argument to `serialize()` for straight serialization.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
The `serialize()` function accepts an `options` object as its second argument. All options are being defaulted to `undefined`:
|
||||||
|
|
||||||
|
#### `options.space`
|
||||||
|
|
||||||
|
This option is the same as the `space` argument that can be passed to [`JSON.stringify`][JSON.stringify]. It can be used to add whitespace and indentation to the serialized output to make it more readable.
|
||||||
|
|
||||||
|
```js
|
||||||
|
serialize(obj, {space: 2});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `options.isJSON`
|
||||||
|
|
||||||
|
This option is a signal to `serialize()` that the object being serialized does not contain any function or regexps values. This enables a hot-path that allows serialization to be over 3x faster. If you're serializing a lot of data, and know its pure JSON, then you can enable this option for a speed-up.
|
||||||
|
|
||||||
|
**Note:** That when using this option, the output will still be escaped to protect against XSS.
|
||||||
|
|
||||||
|
```js
|
||||||
|
serialize(obj, {isJSON: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `options.unsafe`
|
||||||
|
|
||||||
|
This option is to signal `serialize()` that we want to do a straight conversion, without the XSS protection. This options needs to be explicitly set to `true`. HTML characters and JavaScript line terminators will not be escaped. You will have to roll your own.
|
||||||
|
|
||||||
|
```js
|
||||||
|
serialize(obj, {unsafe: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `options.ignoreFunction`
|
||||||
|
|
||||||
|
This option is to signal `serialize()` that we do not want serialize JavaScript function.
|
||||||
|
Just treat function like `JSON.stringify` do, but other features will work as expected.
|
||||||
|
|
||||||
|
```js
|
||||||
|
serialize(obj, {ignoreFunction: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deserializing
|
||||||
|
|
||||||
|
For some use cases you might also need to deserialize the string. This is explicitly not part of this module. However, you can easily write it yourself:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function deserialize(serializedJavascript){
|
||||||
|
return eval('(' + serializedJavascript + ')');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Don't forget the parentheses around the serialized javascript, as the opening bracket `{` will be considered to be the start of a body.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This software is free to use under the Yahoo! Inc. BSD license.
|
||||||
|
See the [LICENSE file][LICENSE] for license text and copyright information.
|
||||||
|
|
||||||
|
|
||||||
|
[npm]: https://www.npmjs.org/package/serialize-javascript
|
||||||
|
[npm-badge]: https://img.shields.io/npm/v/serialize-javascript.svg?style=flat-square
|
||||||
|
[david]: https://david-dm.org/yahoo/serialize-javascript
|
||||||
|
[david-badge]: https://img.shields.io/david/yahoo/serialize-javascript.svg?style=flat-square
|
||||||
|
[express-state]: https://github.com/yahoo/express-state
|
||||||
|
[JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
||||||
|
[LICENSE]: https://github.com/yahoo/serialize-javascript/blob/main/LICENSE
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
|
||||||
|
Copyrights licensed under the New BSD License.
|
||||||
|
See the accompanying LICENSE file for terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var randomBytes = require('randombytes');
|
||||||
|
|
||||||
|
// Generate an internal UID to make the regexp pattern harder to guess.
|
||||||
|
var UID_LENGTH = 16;
|
||||||
|
var UID = generateUID();
|
||||||
|
var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\d+)__@"', 'g');
|
||||||
|
|
||||||
|
var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
|
||||||
|
var IS_PURE_FUNCTION = /function.*?\(/;
|
||||||
|
var IS_ARROW_FUNCTION = /.*?=>.*?/;
|
||||||
|
var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g;
|
||||||
|
|
||||||
|
var RESERVED_SYMBOLS = ['*', 'async'];
|
||||||
|
|
||||||
|
// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their
|
||||||
|
// Unicode char counterparts which are safe to use in JavaScript strings.
|
||||||
|
var ESCAPED_CHARS = {
|
||||||
|
'<' : '\\u003C',
|
||||||
|
'>' : '\\u003E',
|
||||||
|
'/' : '\\u002F',
|
||||||
|
'\u2028': '\\u2028',
|
||||||
|
'\u2029': '\\u2029'
|
||||||
|
};
|
||||||
|
|
||||||
|
function escapeUnsafeChars(unsafeChar) {
|
||||||
|
return ESCAPED_CHARS[unsafeChar];
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateUID() {
|
||||||
|
var bytes = randomBytes(UID_LENGTH);
|
||||||
|
var result = '';
|
||||||
|
for(var i=0; i<UID_LENGTH; ++i) {
|
||||||
|
result += bytes[i].toString(16);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFunctions(obj){
|
||||||
|
var functionKeys = [];
|
||||||
|
for (var key in obj) {
|
||||||
|
if (typeof obj[key] === "function") {
|
||||||
|
functionKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < functionKeys.length; i++) {
|
||||||
|
delete obj[functionKeys[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function serialize(obj, options) {
|
||||||
|
options || (options = {});
|
||||||
|
|
||||||
|
// Backwards-compatibility for `space` as the second argument.
|
||||||
|
if (typeof options === 'number' || typeof options === 'string') {
|
||||||
|
options = {space: options};
|
||||||
|
}
|
||||||
|
|
||||||
|
var functions = [];
|
||||||
|
var regexps = [];
|
||||||
|
var dates = [];
|
||||||
|
var maps = [];
|
||||||
|
var sets = [];
|
||||||
|
var arrays = [];
|
||||||
|
var undefs = [];
|
||||||
|
var infinities= [];
|
||||||
|
var bigInts = [];
|
||||||
|
var urls = [];
|
||||||
|
|
||||||
|
// Returns placeholders for functions and regexps (identified by index)
|
||||||
|
// which are later replaced by their string representation.
|
||||||
|
function replacer(key, value) {
|
||||||
|
|
||||||
|
// For nested function
|
||||||
|
if(options.ignoreFunction){
|
||||||
|
deleteFunctions(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value && value !== undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is an object w/ a toJSON method, toJSON is called before
|
||||||
|
// the replacer runs, so we use this[key] to get the non-toJSONed value.
|
||||||
|
var origValue = this[key];
|
||||||
|
var type = typeof origValue;
|
||||||
|
|
||||||
|
if (type === 'object') {
|
||||||
|
if(origValue instanceof RegExp) {
|
||||||
|
return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origValue instanceof Date) {
|
||||||
|
return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origValue instanceof Map) {
|
||||||
|
return '@__M-' + UID + '-' + (maps.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origValue instanceof Set) {
|
||||||
|
return '@__S-' + UID + '-' + (sets.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origValue instanceof Array) {
|
||||||
|
var isSparse = origValue.filter(function(){return true}).length !== origValue.length;
|
||||||
|
if (isSparse) {
|
||||||
|
return '@__A-' + UID + '-' + (arrays.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origValue instanceof URL) {
|
||||||
|
return '@__L-' + UID + '-' + (urls.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'function') {
|
||||||
|
return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'undefined') {
|
||||||
|
return '@__U-' + UID + '-' + (undefs.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'number' && !isNaN(origValue) && !isFinite(origValue)) {
|
||||||
|
return '@__I-' + UID + '-' + (infinities.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'bigint') {
|
||||||
|
return '@__B-' + UID + '-' + (bigInts.push(origValue) - 1) + '__@';
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function serializeFunc(fn) {
|
||||||
|
var serializedFn = fn.toString();
|
||||||
|
if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
|
||||||
|
throw new TypeError('Serializing native function: ' + fn.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pure functions, example: {key: function() {}}
|
||||||
|
if(IS_PURE_FUNCTION.test(serializedFn)) {
|
||||||
|
return serializedFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow functions, example: arg1 => arg1+5
|
||||||
|
if(IS_ARROW_FUNCTION.test(serializedFn)) {
|
||||||
|
return serializedFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
var argsStartsAt = serializedFn.indexOf('(');
|
||||||
|
var def = serializedFn.substr(0, argsStartsAt)
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.filter(function(val) { return val.length > 0 });
|
||||||
|
|
||||||
|
var nonReservedSymbols = def.filter(function(val) {
|
||||||
|
return RESERVED_SYMBOLS.indexOf(val) === -1
|
||||||
|
});
|
||||||
|
|
||||||
|
// enhanced literal objects, example: {key() {}}
|
||||||
|
if(nonReservedSymbols.length > 0) {
|
||||||
|
return (def.indexOf('async') > -1 ? 'async ' : '') + 'function'
|
||||||
|
+ (def.join('').indexOf('*') > -1 ? '*' : '')
|
||||||
|
+ serializedFn.substr(argsStartsAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow functions
|
||||||
|
return serializedFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the parameter is function
|
||||||
|
if (options.ignoreFunction && typeof obj === "function") {
|
||||||
|
obj = undefined;
|
||||||
|
}
|
||||||
|
// Protects against `JSON.stringify()` returning `undefined`, by serializing
|
||||||
|
// to the literal string: "undefined".
|
||||||
|
if (obj === undefined) {
|
||||||
|
return String(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
var str;
|
||||||
|
|
||||||
|
// Creates a JSON string representation of the value.
|
||||||
|
// NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.
|
||||||
|
if (options.isJSON && !options.space) {
|
||||||
|
str = JSON.stringify(obj);
|
||||||
|
} else {
|
||||||
|
str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protects against `JSON.stringify()` returning `undefined`, by serializing
|
||||||
|
// to the literal string: "undefined".
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return String(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace unsafe HTML and invalid JavaScript line terminator chars with
|
||||||
|
// their safe Unicode char counterpart. This _must_ happen before the
|
||||||
|
// regexps and functions are serialized and added back to the string.
|
||||||
|
if (options.unsafe !== true) {
|
||||||
|
str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functions.length === 0 && regexps.length === 0 && dates.length === 0 && maps.length === 0 && sets.length === 0 && arrays.length === 0 && undefs.length === 0 && infinities.length === 0 && bigInts.length === 0 && urls.length === 0) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replaces all occurrences of function, regexp, date, map and set placeholders in the
|
||||||
|
// JSON string with their string representations. If the original value can
|
||||||
|
// not be found, then `undefined` is used.
|
||||||
|
return str.replace(PLACE_HOLDER_REGEXP, function (match, backSlash, type, valueIndex) {
|
||||||
|
// The placeholder may not be preceded by a backslash. This is to prevent
|
||||||
|
// replacing things like `"a\"@__R-<UID>-0__@"` and thus outputting
|
||||||
|
// invalid JS.
|
||||||
|
if (backSlash) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'D') {
|
||||||
|
return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'R') {
|
||||||
|
return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + regexps[valueIndex].flags + "\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'M') {
|
||||||
|
return "new Map(" + serialize(Array.from(maps[valueIndex].entries()), options) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'S') {
|
||||||
|
return "new Set(" + serialize(Array.from(sets[valueIndex].values()), options) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'A') {
|
||||||
|
return "Array.prototype.slice.call(" + serialize(Object.assign({ length: arrays[valueIndex].length }, arrays[valueIndex]), options) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'U') {
|
||||||
|
return 'undefined'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'I') {
|
||||||
|
return infinities[valueIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'B') {
|
||||||
|
return "BigInt(\"" + bigInts[valueIndex] + "\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'L') {
|
||||||
|
return "new URL(\"" + urls[valueIndex].toString() + "\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
var fn = functions[valueIndex];
|
||||||
|
|
||||||
|
return serializeFunc(fn);
|
||||||
|
});
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "serialize-javascript",
|
||||||
|
"version": "6.0.0",
|
||||||
|
"description": "Serialize JavaScript to a superset of JSON that includes regular expressions and functions.",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"benchmark": "node -v && node test/benchmark/serialize.js",
|
||||||
|
"test": "nyc --reporter=lcov mocha test/unit"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/yahoo/serialize-javascript.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"serialize",
|
||||||
|
"serialization",
|
||||||
|
"javascript",
|
||||||
|
"js",
|
||||||
|
"json"
|
||||||
|
],
|
||||||
|
"author": "Eric Ferraiuolo <edf@ericf.me>",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/yahoo/serialize-javascript/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/yahoo/serialize-javascript",
|
||||||
|
"devDependencies": {
|
||||||
|
"benchmark": "^2.1.4",
|
||||||
|
"chai": "^4.1.0",
|
||||||
|
"mocha": "^9.0.0",
|
||||||
|
"nyc": "^15.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Benchmark = require('benchmark');
|
||||||
|
var serialize = require('../../');
|
||||||
|
|
||||||
|
var suiteConfig = {
|
||||||
|
onStart: function (e) {
|
||||||
|
console.log(e.currentTarget.name + ':');
|
||||||
|
},
|
||||||
|
|
||||||
|
onCycle: function (e) {
|
||||||
|
console.log(String(e.target));
|
||||||
|
},
|
||||||
|
|
||||||
|
onComplete: function () {
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- simpleOjb ----------------------------------------------------------------
|
||||||
|
|
||||||
|
var simpleObj = {
|
||||||
|
foo: 'foo',
|
||||||
|
bar: false,
|
||||||
|
num: 100,
|
||||||
|
arr: [1, 2, 3, 4],
|
||||||
|
obj: {baz: 'baz'}
|
||||||
|
};
|
||||||
|
|
||||||
|
new Benchmark.Suite('simpleObj', suiteConfig)
|
||||||
|
.add('JSON.stringify( simpleObj )', function () {
|
||||||
|
JSON.stringify(simpleObj);
|
||||||
|
})
|
||||||
|
.add('JSON.stringify( simpleObj ) with replacer', function () {
|
||||||
|
JSON.stringify(simpleObj, function (key, value) {
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.add('serialize( simpleObj, {isJSON: true} )', function () {
|
||||||
|
serialize(simpleObj, {isJSON: true});
|
||||||
|
})
|
||||||
|
.add('serialize( simpleObj, {unsafe: true} )', function () {
|
||||||
|
serialize(simpleObj, {unsafe: true});
|
||||||
|
})
|
||||||
|
.add('serialize( simpleObj, {unsafe: true, isJSON: true} )', function () {
|
||||||
|
serialize(simpleObj, {unsafe: true, isJSON: true});
|
||||||
|
})
|
||||||
|
.add('serialize( simpleObj )', function () {
|
||||||
|
serialize(simpleObj);
|
||||||
|
})
|
||||||
|
.run();
|
|
@ -0,0 +1,575 @@
|
||||||
|
/* global describe, it, beforeEach */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// temporarily monkeypatch `crypto.randomBytes` so we'll have a
|
||||||
|
// predictable UID for our tests
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var oldRandom = crypto.randomBytes;
|
||||||
|
crypto.randomBytes = function(len, cb) {
|
||||||
|
var buf = Buffer.alloc(len);
|
||||||
|
buf.fill(0x00);
|
||||||
|
if (cb)
|
||||||
|
cb(null, buf);
|
||||||
|
return buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
var serialize = require('../../'),
|
||||||
|
expect = require('chai').expect;
|
||||||
|
|
||||||
|
crypto.randomBytes = oldRandom;
|
||||||
|
|
||||||
|
describe('serialize( obj )', function () {
|
||||||
|
it('should be a function', function () {
|
||||||
|
expect(serialize).to.be.a('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('undefined', function () {
|
||||||
|
it('should serialize `undefined` to a string', function () {
|
||||||
|
expect(serialize()).to.be.a('string').equal('undefined');
|
||||||
|
expect(serialize(undefined)).to.be.a('string').equal('undefined');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize "undefined" to `undefined`', function () {
|
||||||
|
expect(eval(serialize())).to.equal(undefined);
|
||||||
|
expect(eval(serialize(undefined))).to.equal(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('null', function () {
|
||||||
|
it('should serialize `null` to a string', function () {
|
||||||
|
expect(serialize(null)).to.be.a('string').equal('null');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize "null" to `null`', function () {
|
||||||
|
expect(eval(serialize(null))).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('JSON', function () {
|
||||||
|
var data;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
data = {
|
||||||
|
str : 'string',
|
||||||
|
num : 0,
|
||||||
|
obj : {foo: 'foo'},
|
||||||
|
arr : [1, 2, 3],
|
||||||
|
bool: true,
|
||||||
|
nil : null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize JSON to a JSON string', function () {
|
||||||
|
expect(serialize(data)).to.equal(JSON.stringify(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a JSON string to a JSON object', function () {
|
||||||
|
expect(JSON.parse(serialize(data))).to.deep.equal(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize weird whitespace characters correctly', function () {
|
||||||
|
var ws = String.fromCharCode(8232);
|
||||||
|
expect(eval(serialize(ws))).to.equal(ws);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize undefined correctly', function () {
|
||||||
|
var obj;
|
||||||
|
var str = '{"undef":undefined,"nest":{"undef":undefined}}';
|
||||||
|
eval('obj = ' + str);
|
||||||
|
expect(serialize(obj)).to.equal(str);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('functions', function () {
|
||||||
|
it('should serialize annonymous functions', function () {
|
||||||
|
var fn = function () {};
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('function () {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize annonymous functions', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function () {}));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize named functions', function () {
|
||||||
|
function fn() {}
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('function fn() {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize named functions', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function fn() {}));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn.name).to.equal('fn');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize functions with arguments', function () {
|
||||||
|
function fn(arg1, arg2) {}
|
||||||
|
expect(serialize(fn)).to.equal('function fn(arg1, arg2) {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize functions with arguments', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function (arg1, arg2) {}));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize functions with bodies', function () {
|
||||||
|
function fn() { return true; }
|
||||||
|
expect(serialize(fn)).to.equal('function fn() { return true; }');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize functions with bodies', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function () { return true; }));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn()).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a TypeError when serializing native built-ins', function () {
|
||||||
|
var err;
|
||||||
|
expect(Number.toString()).to.equal('function Number() { [native code] }');
|
||||||
|
try { serialize(Number); } catch (e) { err = e; }
|
||||||
|
expect(err).to.be.an.instanceOf(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize enhanced literal objects', function () {
|
||||||
|
var obj = {
|
||||||
|
foo() { return true; },
|
||||||
|
*bar() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(serialize(obj)).to.equal('{"foo":function() { return true; },"bar":function*() { return true; }}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize enhanced literal objects', function () {
|
||||||
|
var obj;
|
||||||
|
eval('obj = ' + serialize({ hello() { return true; } }));
|
||||||
|
|
||||||
|
expect(obj.hello()).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize functions that contain dates', function () {
|
||||||
|
function fn(arg1) {return new Date('2016-04-28T22:02:17.156Z')};
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('function fn(arg1) {return new Date(\'2016-04-28T22:02:17.156Z\')}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize functions that contain dates', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function () { return new Date('2016-04-28T22:02:17.156Z') }));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn().getTime()).to.equal(new Date('2016-04-28T22:02:17.156Z').getTime());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize functions that return other functions', function () {
|
||||||
|
function fn() {return function(arg1) {return arg1 + 5}};
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('function fn() {return function(arg1) {return arg1 + 5}}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize functions that return other functions', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(function () { return function(arg1) {return arg1 + 5} }));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn()(7)).to.equal(12);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('arrow-functions', function () {
|
||||||
|
it('should serialize arrow functions', function () {
|
||||||
|
var fn = () => {};
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('() => {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(() => true));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn()).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize arrow functions with one argument', function () {
|
||||||
|
var fn = arg1 => {}
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('arg1 => {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions with one argument', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(arg1 => {}));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize arrow functions with multiple arguments', function () {
|
||||||
|
var fn = (arg1, arg2) => {}
|
||||||
|
expect(serialize(fn)).to.equal('(arg1, arg2) => {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions with multiple arguments', function () {
|
||||||
|
var fn; eval('fn = ' + serialize( (arg1, arg2) => {}));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize arrow functions with bodies', function () {
|
||||||
|
var fn = () => { return true; }
|
||||||
|
expect(serialize(fn)).to.equal('() => { return true; }');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions with bodies', function () {
|
||||||
|
var fn; eval('fn = ' + serialize( () => { return true; }));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn()).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize enhanced literal objects', function () {
|
||||||
|
var obj = {
|
||||||
|
foo: () => { return true; },
|
||||||
|
bar: arg1 => { return true; },
|
||||||
|
baz: (arg1, arg2) => { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(serialize(obj)).to.equal('{"foo":() => { return true; },"bar":arg1 => { return true; },"baz":(arg1, arg2) => { return true; }}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize enhanced literal objects', function () {
|
||||||
|
var obj;
|
||||||
|
eval('obj = ' + serialize({ foo: () => { return true; },
|
||||||
|
foo: () => { return true; },
|
||||||
|
bar: arg1 => { return true; },
|
||||||
|
baz: (arg1, arg2) => { return true; }
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(obj.foo()).to.equal(true);
|
||||||
|
expect(obj.bar('arg1')).to.equal(true);
|
||||||
|
expect(obj.baz('arg1', 'arg1')).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize arrow functions with added properties', function () {
|
||||||
|
var fn = () => {};
|
||||||
|
fn.property1 = 'a string'
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('() => {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions with added properties', function () {
|
||||||
|
var fn; eval('fn = ' + serialize( () => { this.property1 = 'a string'; return 5 }));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn()).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize arrow functions that return other functions', function () {
|
||||||
|
var fn = arg1 => { return arg2 => arg1 + arg2 };
|
||||||
|
expect(serialize(fn)).to.be.a('string').equal('arg1 => { return arg2 => arg1 + arg2 }');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize arrow functions that return other functions', function () {
|
||||||
|
var fn; eval('fn = ' + serialize(arg1 => { return arg2 => arg1 + arg2 } ));
|
||||||
|
expect(fn).to.be.a('function');
|
||||||
|
expect(fn(2)(3)).to.equal(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('regexps', function () {
|
||||||
|
it('should serialize constructed regexps', function () {
|
||||||
|
var re = new RegExp('asdf');
|
||||||
|
expect(serialize(re)).to.be.a('string').equal('new RegExp("asdf", "")');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize constructed regexps', function () {
|
||||||
|
var re = eval(serialize(new RegExp('asdf')));
|
||||||
|
expect(re).to.be.a('RegExp');
|
||||||
|
expect(re.source).to.equal('asdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize literal regexps', function () {
|
||||||
|
var re = /asdf/;
|
||||||
|
expect(serialize(re)).to.be.a('string').equal('new RegExp("asdf", "")');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize literal regexps', function () {
|
||||||
|
var re = eval(serialize(/asdf/));
|
||||||
|
expect(re).to.be.a('RegExp');
|
||||||
|
expect(re.source).to.equal('asdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize regexps with flags', function () {
|
||||||
|
var re = /^asdf$/gi;
|
||||||
|
expect(serialize(re)).to.equal('new RegExp("^asdf$", "gi")');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize regexps with flags', function () {
|
||||||
|
var re = eval(serialize(/^asdf$/gi));
|
||||||
|
expect(re).to.be.a('RegExp');
|
||||||
|
expect(re.global).to.equal(true);
|
||||||
|
expect(re.ignoreCase).to.equal(true);
|
||||||
|
expect(re.multiline).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize regexps with escaped chars', function () {
|
||||||
|
expect(serialize(/\..*/)).to.equal('new RegExp("\\\\..*", "")');
|
||||||
|
expect(serialize(new RegExp('\\..*'))).to.equal('new RegExp("\\\\..*", "")');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize regexps with escaped chars', function () {
|
||||||
|
var re = eval(serialize(/\..*/));
|
||||||
|
expect(re).to.be.a('RegExp');
|
||||||
|
expect(re.source).to.equal('\\..*');
|
||||||
|
re = eval(serialize(new RegExp('\\..*')));
|
||||||
|
expect(re).to.be.a('RegExp');
|
||||||
|
expect(re.source).to.equal('\\..*');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize dangerous regexps', function () {
|
||||||
|
var re = /[<\/script><script>alert('xss')\/\/]/
|
||||||
|
expect(serialize(re)).to.be.a('string').equal('new RegExp("[\\u003C\\\\\\u002Fscript\\u003E\\u003Cscript\\u003Ealert(\'xss\')\\\\\\u002F\\\\\\u002F]", "")');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dates', function () {
|
||||||
|
it('should serialize dates', function () {
|
||||||
|
var d = new Date('2016-04-28T22:02:17.156Z');
|
||||||
|
expect(serialize(d)).to.be.a('string').equal('new Date("2016-04-28T22:02:17.156Z")');
|
||||||
|
expect(serialize({t: [d]})).to.be.a('string').equal('{"t":[new Date("2016-04-28T22:02:17.156Z")]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a date', function () {
|
||||||
|
var d = eval(serialize(new Date('2016-04-28T22:02:17.156Z')));
|
||||||
|
expect(d).to.be.a('Date');
|
||||||
|
expect(d.toISOString()).to.equal('2016-04-28T22:02:17.156Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a string that is not a valid date', function () {
|
||||||
|
var d = eval(serialize('2016-04-28T25:02:17.156Z'));
|
||||||
|
expect(d).to.be.a('string');
|
||||||
|
expect(d).to.equal('2016-04-28T25:02:17.156Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize dates within objects', function () {
|
||||||
|
var d = {foo: new Date('2016-04-28T22:02:17.156Z')};
|
||||||
|
expect(serialize(d)).to.be.a('string').equal('{"foo":new Date("2016-04-28T22:02:17.156Z")}');
|
||||||
|
expect(serialize({t: [d]})).to.be.a('string').equal('{"t":[{"foo":new Date("2016-04-28T22:02:17.156Z")}]}');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('maps', function () {
|
||||||
|
it('should serialize maps', function () {
|
||||||
|
var regexKey = /.*/;
|
||||||
|
var m = new Map([
|
||||||
|
['a', 123],
|
||||||
|
[regexKey, 456],
|
||||||
|
[Infinity, 789]
|
||||||
|
]);
|
||||||
|
expect(serialize(m)).to.be.a('string').equal('new Map([["a",123],[new RegExp(".*", ""),456],[Infinity,789]])');
|
||||||
|
expect(serialize({t: [m]})).to.be.a('string').equal('{"t":[new Map([["a",123],[new RegExp(".*", ""),456],[Infinity,789]])]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a map', function () {
|
||||||
|
var m = eval(serialize(new Map([
|
||||||
|
['a', 123],
|
||||||
|
[null, 456],
|
||||||
|
[Infinity, 789]
|
||||||
|
])));
|
||||||
|
expect(m).to.be.a('Map');
|
||||||
|
expect(m.get(null)).to.equal(456);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sets', function () {
|
||||||
|
it('should serialize sets', function () {
|
||||||
|
var regex = /.*/;
|
||||||
|
var m = new Set([
|
||||||
|
'a',
|
||||||
|
123,
|
||||||
|
regex,
|
||||||
|
Infinity
|
||||||
|
]);
|
||||||
|
expect(serialize(m)).to.be.a('string').equal('new Set(["a",123,new RegExp(".*", ""),Infinity])');
|
||||||
|
expect(serialize({t: [m]})).to.be.a('string').equal('{"t":[new Set(["a",123,new RegExp(".*", ""),Infinity])]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a set', function () {
|
||||||
|
var m = eval(serialize(new Set([
|
||||||
|
'a',
|
||||||
|
123,
|
||||||
|
null,
|
||||||
|
Infinity
|
||||||
|
])));
|
||||||
|
expect(m).to.be.a('Set');
|
||||||
|
expect(m.has(null)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sparse arrays', function () {
|
||||||
|
it('should serialize sparse arrays', function () {
|
||||||
|
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
delete a[0];
|
||||||
|
a.length = 3;
|
||||||
|
a[5] = "wat"
|
||||||
|
expect(serialize(a)).to.be.a('string').equal('Array.prototype.slice.call({"1":2,"2":3,"5":"wat","length":6})');
|
||||||
|
expect(serialize({t: [a]})).to.be.a('string').equal('{"t":[Array.prototype.slice.call({"1":2,"2":3,"5":"wat","length":6})]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize a sparse array', function () {
|
||||||
|
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
delete a[0];
|
||||||
|
a.length = 3;
|
||||||
|
a[5] = "wat"
|
||||||
|
var b = eval(serialize(a));
|
||||||
|
expect(b).to.be.a('Array').deep.equal([ , 2, 3, , , 'wat' ]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Infinity', function () {
|
||||||
|
it('should serialize Infinity', function () {
|
||||||
|
expect(serialize(Infinity)).to.equal('Infinity');
|
||||||
|
expect(serialize({t: [Infinity]})).to.be.a('string').equal('{"t":[Infinity]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize Infinity', function () {
|
||||||
|
var d = eval(serialize(Infinity));
|
||||||
|
expect(d).to.equal(Infinity);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should serialize -Infinity', function () {
|
||||||
|
expect(serialize(-Infinity)).to.equal('-Infinity');
|
||||||
|
expect(serialize({t: [-Infinity]})).to.be.a('string').equal('{"t":[-Infinity]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize -Infinity', function () {
|
||||||
|
var d = eval(serialize(-Infinity));
|
||||||
|
expect(d).to.equal(-Infinity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BigInt', function () {
|
||||||
|
it('should serialize BigInt', function () {
|
||||||
|
var b = BigInt(9999);
|
||||||
|
expect(serialize(b)).to.equal('BigInt("9999")');
|
||||||
|
expect(serialize({t: [b]})).to.be.a('string').equal('{"t":[BigInt("9999")]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize BigInt', function () {
|
||||||
|
var d = eval(serialize(BigInt(9999)));
|
||||||
|
expect(d).to.be.a('BigInt');
|
||||||
|
expect(d.toString()).to.equal('9999');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for invalid bigint', function () {
|
||||||
|
expect(() => serialize(BigInt('abc'))).to.throw(Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('URL', function () {
|
||||||
|
it('should serialize URL', function () {
|
||||||
|
var u = new URL('https://x.com/')
|
||||||
|
expect(serialize(u)).to.equal('new URL("https://x.com/")');
|
||||||
|
expect(serialize({t: [u]})).to.be.a('string').equal('{"t":[new URL("https://x.com/")]}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deserialize URL', function () {
|
||||||
|
var d = eval(serialize(new URL('https://x.com/')));
|
||||||
|
expect(d).to.be.a('URL');
|
||||||
|
expect(d.toString()).to.equal('https://x.com/');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('XSS', function () {
|
||||||
|
it('should encode unsafe HTML chars to Unicode', function () {
|
||||||
|
expect(serialize('</script>')).to.equal('"\\u003C\\u002Fscript\\u003E"');
|
||||||
|
expect(JSON.parse(serialize('</script>'))).to.equal('</script>');
|
||||||
|
expect(eval(serialize('</script>'))).to.equal('</script>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('options', function () {
|
||||||
|
it('should accept options as the second argument', function () {
|
||||||
|
expect(serialize('foo', {})).to.equal('"foo"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept a `space` option', function () {
|
||||||
|
expect(serialize([1], {space: 0})).to.equal('[1]');
|
||||||
|
expect(serialize([1], {space: ''})).to.equal('[1]');
|
||||||
|
expect(serialize([1], {space: undefined})).to.equal('[1]');
|
||||||
|
expect(serialize([1], {space: null})).to.equal('[1]');
|
||||||
|
expect(serialize([1], {space: false})).to.equal('[1]');
|
||||||
|
|
||||||
|
expect(serialize([1], {space: 1})).to.equal('[\n 1\n]');
|
||||||
|
expect(serialize([1], {space: ' '})).to.equal('[\n 1\n]');
|
||||||
|
expect(serialize([1], {space: 2})).to.equal('[\n 1\n]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept a `isJSON` option', function () {
|
||||||
|
expect(serialize('foo', {isJSON: true})).to.equal('"foo"');
|
||||||
|
expect(serialize('foo', {isJSON: false})).to.equal('"foo"');
|
||||||
|
|
||||||
|
function fn() { return true; }
|
||||||
|
|
||||||
|
expect(serialize(fn)).to.equal('function fn() { return true; }');
|
||||||
|
expect(serialize(fn, {isJSON: false})).to.equal('function fn() { return true; }');
|
||||||
|
|
||||||
|
expect(serialize(fn, {isJSON: true})).to.equal('undefined');
|
||||||
|
expect(serialize([1], {isJSON: true, space: 2})).to.equal('[\n 1\n]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should accept a `unsafe` option', function () {
|
||||||
|
expect(serialize('foo', {unsafe: true})).to.equal('"foo"');
|
||||||
|
expect(serialize('foo', {unsafe: false})).to.equal('"foo"');
|
||||||
|
|
||||||
|
function fn() { return true; }
|
||||||
|
|
||||||
|
expect(serialize(fn)).to.equal('function fn() { return true; }');
|
||||||
|
expect(serialize(fn, {unsafe: false})).to.equal('function fn() { return true; }');
|
||||||
|
expect(serialize(fn, {unsafe: undefined})).to.equal('function fn() { return true; }');
|
||||||
|
expect(serialize(fn, {unsafe: "true"})).to.equal('function fn() { return true; }');
|
||||||
|
|
||||||
|
expect(serialize(fn, {unsafe: true})).to.equal('function fn() { return true; }');
|
||||||
|
expect(serialize(["1"], {unsafe: false, space: 2})).to.equal('[\n "1"\n]');
|
||||||
|
expect(serialize(["1"], {unsafe: true, space: 2})).to.equal('[\n "1"\n]');
|
||||||
|
expect(serialize(["<"], {space: 2})).to.equal('[\n "\\u003C"\n]');
|
||||||
|
expect(serialize(["<"], {unsafe: true, space: 2})).to.equal('[\n "<"\n]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should accept a `ignoreFunction` option", function() {
|
||||||
|
function fn() { return true; }
|
||||||
|
var obj = {
|
||||||
|
fn: fn,
|
||||||
|
fn_arrow: () => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var obj2 = {
|
||||||
|
num: 123,
|
||||||
|
str: 'str',
|
||||||
|
fn: fn
|
||||||
|
}
|
||||||
|
// case 1. Pass function to serialize
|
||||||
|
expect(serialize(fn, { ignoreFunction: true })).to.equal('undefined');
|
||||||
|
// case 2. Pass function(arrow) in object to serialze
|
||||||
|
expect(serialize(obj, { ignoreFunction: true })).to.equal('{}');
|
||||||
|
// case 3. Other features should work
|
||||||
|
expect(serialize(obj2, { ignoreFunction: true })).to.equal(
|
||||||
|
'{"num":123,"str":"str"}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('backwards-compatability', function () {
|
||||||
|
it('should accept `space` as the second argument', function () {
|
||||||
|
expect(serialize([1], 0)).to.equal('[1]');
|
||||||
|
expect(serialize([1], '')).to.equal('[1]');
|
||||||
|
expect(serialize([1], undefined)).to.equal('[1]');
|
||||||
|
expect(serialize([1], null)).to.equal('[1]');
|
||||||
|
expect(serialize([1], false)).to.equal('[1]');
|
||||||
|
|
||||||
|
expect(serialize([1], 1)).to.equal('[\n 1\n]');
|
||||||
|
expect(serialize([1], ' ')).to.equal('[\n 1\n]');
|
||||||
|
expect(serialize([1], 2)).to.equal('[\n 1\n]');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('placeholders', function() {
|
||||||
|
it('should not be replaced within string literals', function () {
|
||||||
|
// Since we made the UID deterministic this should always be the placeholder
|
||||||
|
var fakePlaceholder = '"@__R-0000000000000000-0__@';
|
||||||
|
var serialized = serialize({bar: /1/i, foo: fakePlaceholder}, {uid: 'foo'});
|
||||||
|
var obj = eval('(' + serialized + ')');
|
||||||
|
expect(obj).to.be.a('Object');
|
||||||
|
expect(obj.foo).to.be.a('String');
|
||||||
|
expect(obj.foo).to.equal(fakePlaceholder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue