soyuka-fclone-f96efdd/000077500000000000000000000000001304140544200150575ustar00rootroot00000000000000soyuka-fclone-f96efdd/.gitignore000066400000000000000000000000431304140544200170440ustar00rootroot00000000000000node_modules bower_components dist soyuka-fclone-f96efdd/.npmignore000066400000000000000000000001431304140544200170540ustar00rootroot00000000000000demo !dist/dpicker.js !dist/dpicker.min.js test.js gulpfile.js screen.png bower_components bump.sh soyuka-fclone-f96efdd/.travis.yml000066400000000000000000000003461304140544200171730ustar00rootroot00000000000000language: node_js branches: only: - master node_js: - "0.10" - "4" - "6" install: - npm install - npm run build matrix: fast_finish: true allow_failures: - node_js: 0.10 # container-base sudo: false soyuka-fclone-f96efdd/LICENSE000066400000000000000000000021451304140544200160660ustar00rootroot00000000000000Clone javascript object without circular values Copyright © 2016 Antoine Bluchet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. soyuka-fclone-f96efdd/README.md000066400000000000000000000027121304140544200163400ustar00rootroot00000000000000# FClone Clone objects by dropping circular references [![Build Status](https://travis-ci.org/soyuka/fclone.svg?branch=master)](https://travis-ci.org/soyuka/fclone) This module clones a Javascript object in safe mode (eg: drops circular values) recursively. Circular values are replaced with a string: `'[Circular]'`. Ideas from [tracker1/safe-clone-deep](https://github.com/tracker1/safe-clone-deep). I improved the workflow a bit by: - refactoring the code (complete rewrite) - fixing node 6+ - micro optimizations - use of `Array.isArray` and `Buffer.isBuffer` Node 0.10 compatible, distributed files are translated to es2015. ## Installation ```bash npm install fclone # or bower install fclone ``` ## Usage ```javascript const fclone = require('fclone'); let a = {c: 'hello'}; a.b = a; let o = fclone(a); console.log(o); // outputs: { c: 'hello', b: '[Circular]' } //JSON.stringify is now safe console.log(JSON.stringify(o)); ``` ## Benchmarks Some benchs: ``` fclone x 17,081 ops/sec ±0.71% (79 runs sampled) fclone + json.stringify x 9,433 ops/sec ±0.91% (81 runs sampled) util.inspect (outputs a string) x 2,498 ops/sec ±0.77% (90 runs sampled) jsan x 5,379 ops/sec ±0.82% (91 runs sampled) circularjson x 4,719 ops/sec ±1.16% (91 runs sampled) deepcopy x 5,478 ops/sec ±0.77% (86 runs sampled) json-stringify-safe x 5,828 ops/sec ±1.30% (84 runs sampled) clone x 8,713 ops/sec ±0.68% (88 runs sampled) Fastest is util.format (outputs a string) ``` soyuka-fclone-f96efdd/bench/000077500000000000000000000000001304140544200161365ustar00rootroot00000000000000soyuka-fclone-f96efdd/bench/index.js000066400000000000000000000025561304140544200176130ustar00rootroot00000000000000'use strict' const Benchmark = require('benchmark') const suite = new Benchmark.Suite function Complex() { this.bar = 'foo' this.foo = new Array(100).fill(0).map(e => Math.random()) } Complex.prototype.method = function() {} const a = {b: 'hello', c: {foo: 'bar', bar: 'foo', error: new Error('Test')}, complex: new Complex()} a.a = a a.c.complex = a.complex a.env = process.env const fclone = require('../dist/fclone.js') const clone = require('clone') const deepcopy = require('deepcopy') const jsonstringifysafe = require('json-stringify-safe') const jsan = require('jsan') const circularjson = require('circular-json-es6') const util = require('util') suite .add('fclone', function() { let b = fclone(a) }) .add('fclone + json.stringify', function() { let b = JSON.stringify(fclone(a)) }) .add('util.inspect (outputs a string)', function() { let b = util.inspect(a) }) .add('jsan', function() { let b = jsan.stringify(a) }) .add('circularjson', function() { let b = circularjson.stringify(a) }) .add('deepcopy', function() { let b = deepcopy(a) }) .add('json-stringify-safe', function() { let b = jsonstringifysafe(a) }) .add('clone', function() { let b = clone(a) }) .on('cycle', function(event) { console.log(String(event.target)) }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')) }) .run({ 'async': true }) soyuka-fclone-f96efdd/bench/looparr.js000066400000000000000000000012021304140544200201450ustar00rootroot00000000000000'use strict' const Benchmark = require('benchmark') const suite = new Benchmark.Suite let arr = new Array(100).fill(Math.random() * 100) suite .add('for', function() { let l = arr.length for (let i = 0; i < l; i++) { let o = arr[i] } }) .add('while --', function() { let l = arr.length while(l--) { let o = arr[l] } }) .add('while ++', function() { let l = arr.length let i = -1 while(l > ++i) { let o = arr[i] } }) .on('cycle', function(event) { console.log(String(event.target)) }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')) }) .run({ 'async': true }) soyuka-fclone-f96efdd/bench/loopobj.js000066400000000000000000000011771304140544200201460ustar00rootroot00000000000000'use strict' const Benchmark = require('benchmark') const suite = new Benchmark.Suite let obj = process.env suite .add('for in', function() { for(let i in obj) { let o = obj[i] } }) .add('while --', function() { let keys = Object.keys(obj) let l = keys.length while(l--) { let o = obj[keys[l]] } }) .add('while shift', function() { let keys = Object.keys(obj) let k while(k = keys.shift()) { let o = obj[k] } }) .on('cycle', function(event) { console.log(String(event.target)) }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')) }) .run({ 'async': true }) soyuka-fclone-f96efdd/bench/package.json000066400000000000000000000006351304140544200204300ustar00rootroot00000000000000{ "name": "bench", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "benchmark": "^2.1.0", "clone": "^1.0.2", "circular-json-es6": "^2.0.0", "deepcopy": "^0.6.3", "jsan": "^3.1.2", "json-stringify-safe": "^5.0.1" } } soyuka-fclone-f96efdd/bower.json000066400000000000000000000010371304140544200170710ustar00rootroot00000000000000{ "name": "fclone", "description": "Fastest JSON cloning module that handles circular references", "main": "dist/fclone.js", "authors": [ "soyuka" ], "license": "MIT", "keywords": [ "clone", "deep", "circular", "json", "stringify", "fast" ], "homepage": "https://github.com/soyuka/fclone", "ignore": [ "**/.*", "node_modules", "bower_components", "test.js", "!dist/dpicker.js", "!dist/dpicker.min.js", "screen.png", "gulpfile.js", "demo", "bump.sh" ] } soyuka-fclone-f96efdd/bump.sh000066400000000000000000000005761304140544200163660ustar00rootroot00000000000000#!/bin/bash [[ '' == $1 ]] && echo "Please provide patch, minor, major argument" && exit 1 gulp cp fclone.d.ts dist/ newver=$(npm --no-git-tag-version version $1) git add -f dist package.json git commit -m $newver git tag $newver npm publish git reset --hard HEAD~1 newver=$(npm --no-git-tag-version version $1) git add package.json git commit -m $newver git push --tags git push soyuka-fclone-f96efdd/fclone.d.ts000066400000000000000000000001141304140544200171130ustar00rootroot00000000000000declare module 'fclone' { export default function fclone(obj: T): T; } soyuka-fclone-f96efdd/gulpfile.js000066400000000000000000000017351304140544200172320ustar00rootroot00000000000000'use strict' var gulp = require('gulp') var wrap = require('gulp-wrap') var rename = require('gulp-rename') var babel = require('gulp-babel') var uglify = require('gulp-uglify') var umd = [ "(function (root, factory) {", " if (typeof define === 'function' && define.amd) {", " // AMD", " define('fclone', [], factory);", " } else if (typeof module === 'object' && module.exports) {", " //node", " module.exports = factory();", " } else {", " // Browser globals (root is window)", " root.fclone = factory();", " }", "}(this, function () {", " <%= contents %>", " return fclone", "}));"].join(require('os').EOL); gulp.task('default', function() { return gulp.src(['src/fclone.js']) .pipe(babel({presets: ['es2015']})) .pipe(wrap(umd)) .pipe(gulp.dest('dist')) .pipe(uglify()) .pipe(rename('fclone.min.js')) .pipe(gulp.dest('dist')) }) gulp.task('watch', ['default'], function() { gulp.watch('src/*.js', ['default']) }) soyuka-fclone-f96efdd/package.json000066400000000000000000000013471304140544200173520ustar00rootroot00000000000000{ "name": "fclone", "version": "1.0.11", "description": "Clone objects by dropping circular references", "main": "dist/fclone", "scripts": { "test": "./node_modules/.bin/_mocha", "build": "./node_modules/.bin/gulp" }, "repository": { "type": "git", "url": "git+https://github.com/soyuka/fclone.git" }, "keywords": [ "clone", "deep", "circular", "json", "stringify", "fast" ], "author": "soyuka ", "license": "MIT", "devDependencies": { "babel-preset-es2015": "^6.9.0", "chai": "^3.5.0", "gulp": "^3.9.1", "gulp-babel": "^6.1.2", "gulp-rename": "^1.2.2", "gulp-uglify": "^1.5.4", "gulp-wrap": "^0.13.0", "mocha": "^2.5.3" } } soyuka-fclone-f96efdd/src/000077500000000000000000000000001304140544200156465ustar00rootroot00000000000000soyuka-fclone-f96efdd/src/fclone.js000066400000000000000000000026461304140544200174620ustar00rootroot00000000000000'use strict'; // see if it looks and smells like an iterable object, and do accept length === 0 function isArrayLike(item) { if (Array.isArray(item)) return true; const len = item && item.length; return typeof len === 'number' && (len === 0 || (len - 1) in item) && typeof item.indexOf === 'function'; } function fclone(obj, refs) { if (!obj || "object" !== typeof obj) return obj; if (obj instanceof Date) { return new Date(obj); } if (typeof Buffer !== 'undefined' && Buffer.isBuffer(obj)) { return new Buffer(obj); } // typed array Int32Array etc. if (typeof obj.subarray === 'function' && /[A-Z][A-Za-z\d]+Array/.test(Object.prototype.toString.call(obj))) { return obj.subarray(0); } if (!refs) { refs = []; } if (isArrayLike(obj)) { refs[refs.length] = obj; let l = obj.length; let i = -1; let copy = []; while (l > ++i) { copy[i] = ~refs.indexOf(obj[i]) ? '[Circular]' : fclone(obj[i], refs); } refs.length && refs.length--; return copy; } refs[refs.length] = obj; let copy = {}; if (obj instanceof Error) { copy.name = obj.name; copy.message = obj.message; copy.stack = obj.stack; } let keys = Object.keys(obj); let l = keys.length; while(l--) { let k = keys[l]; copy[k] = ~refs.indexOf(obj[k]) ? '[Circular]' : fclone(obj[k], refs); } refs.length && refs.length--; return copy; } fclone.default = fclone soyuka-fclone-f96efdd/test/000077500000000000000000000000001304140544200160365ustar00rootroot00000000000000soyuka-fclone-f96efdd/test/test.js000066400000000000000000000062721304140544200173620ustar00rootroot00000000000000'use strict' var expect = require('chai').expect var clone = require('../dist/fclone.js') describe('fclone', function(){ var input, output beforeEach(function(){ var a = {} a.a = a a.b = {} a.b.a = a a.b.b = a.b a.c = {} a.c.b = a.b a.c.c = a.c a.x = 1 a.b.x = 2 a.c.x = 3 a.d = [0,a,1,a.b,2,a.c,3] input = a }) describe('will clone', function(){ it('a string', function() { var i = '' var o = clone(i) expect(o).to.equal(i) }) it('an object', function() { var t = {foo: 'bar', bar: 'foo'} var o = clone(t) delete t.foo expect(t.foo).to.be.undefined expect(o.foo).to.equal('bar') }) it('a Date', function(){ var a = new Date() var b = clone(a) expect(a).to.deep.equal(b) expect(a).to.not.equal(b) }) it('a Buffer', function(){ var a = new Buffer('this is a test') var b = clone(a) expect(a.toString()).to.equal(b.toString()) expect(a).to.not.equal(b) }) it('an Error\'s properties', function(){ var a = new Error("this is a test") var b = clone(a) expect(a).to.not.equal(b) expect(b).to.have.property('name', a.name) expect(b).to.have.property('message', a.message) expect(b).to.have.property('stack', a.stack) }) it('an inherited property', function(){ function Base(){ this.base = true } function Child(){ this.child = true } Child.prototype = new Base() var z = clone(new Child()) expect(z).to.have.property('child',true) expect(z).not.to.have.property('base') }) it('an Uint8Array', function() { var t = new Uint8Array(3) ;[0,1,2].map(function(e) { t[e] = 0 }) var o = clone(t) expect(o).to.be.an.instanceof(Uint8Array) expect(o).to.have.length.of(3) }) it('an array-like object', function() { var t = {length: 3, 0: 'test', 1: 'test', 2: 'test'} var o = clone(t) expect(o).to.deep.equal(t) }) it('a uint8array like', function() { var t = {subarray: function() { return 'fail'}} var o = clone(t) expect(o).not.to.equal('fail') expect(o.subarray()).to.equal('fail') }) }) describe('will not clone circular data', function(){ beforeEach(function(){ output = clone(input) }) it('base object', function(){ expect(output).to.have.property('a','[Circular]') expect(output).to.have.property('b') expect(output).to.have.property('x',1) expect(output).to.have.property('c') }) it('nested property', function(){ expect(output.b).to.exist expect(output.b).to.have.property('a','[Circular]') expect(output.b).to.have.property('b','[Circular]') expect(output.b).to.have.property('x',2) }) it('secondary nested property', function(){ expect(output.c).to.exist expect(output.c).to.not.have.property('a') expect(output.c).to.have.property('b') expect(output.c).to.have.property('c','[Circular]') expect(output.c.b).to.deep.equal({a: '[Circular]', b: '[Circular]', x: 2}) expect(output.c).to.have.property('x',3) }) }) })