npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

warp10

v2.1.0

Published

Transport complex JavaScript objects from the server to the web browser at lightning fast speeds

Downloads

20,989

Readme

warp10

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).

Build Status Coverage Status NPM

Features

  • Circular references are correctly serialized and deserialized
  • Duplicate objects/arrays found during serialization are only serialized once
  • Date values are correctly serialized and deserialized
  • Output is the code for a JavaScript expression that, when evaluated, will return the deserialized object
  • Extremely fast serialization and deserialization
  • 100% test coverage
  • Benchmarks

Installation

npm install warp10 --save

Usage

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.

Outputting JavaScript code

var warp10 = require('warp10');

var deserializationCode = warp10.serialize(object[, options]); // Returns a String

For example:

warp10.serialize({
        hello: 'world'
    },
    {
        var: 'foo'
    });

This will produce code similar to the following:

window.foo = {
  "hello": "world"
}

Supported options:

  • safe - If true then the ending </script> tags will be escaped. (optional, default: true)
  • var - A global variable name to assign the output expression to. If not specified then no global variable will be created (optional, default: undefined)
  • 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)

Evaluating the output deserialization code as a JavaScript expression will produce a clone of the original object graph.

You could transport the object graph to the browser by placing the code in a <script> tag as shown below:

<script>
var deserializedObject = <%= deserializationCode %>;
console.log('DESERIALIZED:', deserializedObject);
</script>

You can also eval the deserializationCode:

console.log('DESERIALIZED:', eval(deserializationCode));

JSON stringify/parse

If outputting JavaScript code is not an option or not desired then you can use the stringify and parse methods provided by warp10.

var warp10 = require('warp10');

var json = warp10.stringify(object[, options]); // Returns a String

Supported options:

  • safe - If true then the ending </script> tags will be escaped. (optional, default: false)

The JSON can then be parsed using code similar to the following:

var parse = require('warp10/parse');

var object = parse(json);

JSON stringifyPrepare/finalize

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.

On the server:

var warp10 = require('warp10').stringifyPrepare;
var object = stringifyPrepare(object); // Returns an Object
var json = JSON.stringify(object);

In the browser:

var finalize = require('warp10/finalize');
var clone = finalize(JSON.parse(json));

Examples

Serialize examples

Simple

warp10.serialize({ name: 'Frank' });

Output (formatted for readability):

({
  "name": "Frank"
})

Simple types

warp10.serialize({
    object: {
        foo: 'bar'
    },
    array: ['a', 'b', 'c'],
    boolean: true,
    string: 'Hello World',
    number: 123,
    date: new Date(1776, 6, 4)
});

Output (formatted for readability):

(function() {
  var $ = {
    "object": {
      "foo": "bar"
    },
    "array": ["a", "b", "c"],
    "boolean": true,
    "string": "Hello World",
    "number": 123
  }
  $.date = new Date(-6106039200000)
  return $
}())

Global variable

warp10.serialize({ name: 'Frank' }, { var: 'person' });

Output (formatted for readability):

window.person = {
  "name": "Frank"
}

Global variable with additive

var deserializationCodeA = warp10.serialize({
        foo: 'foo',
        bar: 'bar'
    },
    {
        var: 'myStore',
        additive: true
    });

var deserializationCodeB = warp10.serialize({
        baz: 'baz'
    },
    {
        var: 'myStore',
        additive: true
    });

Output (formatted for readability):

// deserializationCodeA
(function() {
    var t = window.myStore || (window.myStore = {})
    var $ = {
        "foo": "foo",
        "bar": "bar"
    }
    t.foo = $.foo
    t.bar = $.bar
}());

// deserializationCodeB
(function() {
    var t = window.myStore || (window.myStore = {})
    var $ = {
        "baz": "baz"
    }
    t.baz = $.baz
}())

Final value of the window.myStore global:

{
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
}

Circular dependency

var parent = {
    name: 'parent'
};

var child = {
    parent: parent
};

parent.child = child;

warp10.serialize(parent);

Output (formatted for readability):

(function() {
  var $ = {
    "name": "parent",
    "child": {}
  }
  $.child.parent = $
  return $
}())

De-duping

var child = {
    name: 'Henry'
};

var mother = {
    name: 'Jane',
    child: child
};

var father = {
    name: 'Frank',
    child: child
};

warp10.serialize({
    mother: mother,
    father: father
});

Output (formatted for readability):

(function() {
  var $ = {
    "mother": {
      "name": "Jane",
      "child": {
        "name": "Henry"
      }
    },
    "father": {
      "name": "Frank"
    }
  }
  $.father.child = $.mother.child
  return $
}())

Circular dependency plus de-duping

var warp10 = require('warp10');

var mother = {
    name: 'Jane',
    age: 30
};

var father = {
    name: 'Frank',
    age: 32
};

var child1 = {
    name: 'Sue',
    age: 5,
    mother: mother, // circular
    father: father // circular
};

var child2 = {
    name: 'Henry',
    age: 10,
    mother: mother, // circular
    father: father // circular
};

mother.children = [child1, child2];
father.children = [child1 /* duplicate */, child2 /* duplicate */];

warp10.serialize({
    mother: mother,
    father: father
});

The value of deserializationCode will be similar to the following (formatted for readability):

(function() {
  var $ = {
    "mother": {
      "name": "Jane",
      "age": 30,
      "children": [{
        "name": "Sue",
        "age": 5,
        "father": {
          "name": "Frank",
          "age": 32,
          "children": [null, {
            "name": "Henry",
            "age": 10
          }]
        }
      }, null]
    }
  }
  $.mother.children[0].mother = $.mother
  $.mother.children[0].father.children[0] = $.mother.children[0]
  $.mother.children[0].father.children[1].mother = $.mother
  $.mother.children[0].father.children[1].father = $.mother.children[0].father
  $.mother.children[1] = $.mother.children[0].father.children[1]
  $.father = $.mother.children[0].father
  return $
}())

Stringify examples

Simple

warp10.stringify({ name: 'Frank' });

Output (formatted for readability):

{
  "object": {
    "name": "Frank"
  }
}

Circular dependency

var parent = {
    name: 'parent'
};

var child = {
    parent: parent
};

parent.child = child;

warp10.serialize(parent);

Output (formatted for readability):

{
  "object": {
    "mother": {
      "name": "Jane",
      "age": 30,
      "children": [{
        "name": "Sue",
        "age": 5,
        "father": {
          "name": "Frank",
          "age": 32
        }
      }, {
        "name": "Henry",
        "age": 10
      }]
    }
  },
  "assignments": [{
    "l": ["mother", "children", 0, "mother"],
    "r": ["mother"]
  }, {
    "l": ["mother", "children", 0, "father", "children"],
    "r": ["mother", "children"]
  }, {
    "l": ["mother", "children", 1, "mother"],
    "r": ["mother"]
  }, {
    "l": ["mother", "children", 1, "father"],
    "r": ["mother", "children", 0, "father"]
  }, {
    "l": ["father"],
    "r": ["mother", "children", 0, "father"]
  }]
}

Is it fast?

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 that you can run locally:

cd warp10/
npm run benchmark

Below is the output for one run of the benchmarks:

                      circular
         284,357 op/s » circular-json
             521 op/s » lave
         654,493 op/s » refify
         519,239 op/s » warp10-stringify
         820,863 op/s » warp10

                      circular-dedupe
          49,070 op/s » circular-json
             505 op/s » lave
          46,396 op/s » refify
         113,071 op/s » warp10-stringify
         177,930 op/s » warp10

                      dedupe
         104,117 op/s » circular-json
             558 op/s » lave
         334,314 op/s » refify
         343,625 op/s » warp10-stringify
         478,872 op/s » warp10

                      deserialize-circular-dedupe
          32,124 op/s » circular-json
          25,247 op/s » refify
          82,770 op/s » warp10-parse
       1,052,371 op/s » warp10

                      deserialize-simple-large
           2,551 op/s » circular-json
          24,051 op/s » parse-native
           1,918 op/s » refify
          24,497 op/s » warp10-parse
         149,809 op/s » warp10

                      simple-large
           3,150 op/s » circular-json
           2,076 op/s » json3
             283 op/s » lave
           2,504 op/s » refify
          31,057 op/s » stringify-native
          11,174 op/s » warp10-stringify
          11,161 op/s » warp10

                      simple-large-b
             124 op/s » circular-json
             117 op/s » json3
              26 op/s » lave
             156 op/s » refify
           2,263 op/s » stringify-native
           1,505 op/s » warp10-stringify
           1,461 op/s » warp10

                      simple-small
         164,080 op/s » circular-json
         105,782 op/s » json3
             635 op/s » lave
         259,742 op/s » refify
       1,121,181 op/s » stringify-native
         555,886 op/s » warp10-stringify
         686,304 op/s » warp10

                      test-a
         190,557 op/s » circular-json
             554 op/s » lave
          90,753 op/s » refify
         219,099 op/s » warp10-stringify
         381,360 op/s » warp10

Test setup:

  • Node.js v6.3.1
  • OSX 10.11.5
  • 2.8 GHz Intel Core i7
  • 16 GB 1600 MHz DDR3

How does it work?

Internally, this library utilizes the native 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.

warp10 detects circular dependencies and duplicate objects by marking each object with a non-enumerable Symbol property using code similar to the following:

var markerKey = Symbol();
var marker = {}
obj[markerKey] = marker;

This special property is largely private and only discoverable at runtime via Object.getOwnPropertySymbols or proxies.

Why?

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 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).

Similar projects

Maintainers

Contributing

Pull Requests welcome. Please submit Github issues for any feature enhancements, bugs or documentation problems. Please make sure all tests pass:

npm test

License

MIT