isopod

0.5.1 • Public • Published

Coverage Status

About

Have you ever wanted to clone an object (including all its methods and prototyped goodness) from one Javascript runtime to another?

This library will, you know, do that.

Getting started

When using it in node.js, you could:

npm install isopod

...and from your node source code do:

const isopod = require('isopod');

When using from the browser, download it, then in your html include a <script> pointing to the correct file. The script will make isopod a global variable.

For example you might run:

npm install isopod

...and then you might put this in your html (assuming the node_modules directory is statically served to the client):

<script src="/isopod/dist/isopod.min.js"></script>

...and now isopod would be a global variable available to any subsequent client scripts.

What does it include?

isopod has but two methods: serialize and deserialize.

Why would I use it?

Normally when you send some Javascript data from one program (e.g. your server) to another (e.g. your client) it loses "richness". Functions, regular expressions, circular objects, objects with non-standard prototypes—all of these things would not normally transfer over.

One way to think about why this happens is that any data that gets sent somewhere else by your Javascript program will (basically) need to get converted into a string, and is generally converted back into data (parsed) on the receiving end. Commonly the sender will JSON.stringify the data and the receiver will JSON.parse the incoming string.

This means that Javascript objects get converted into JSON strings, which are only natively equipped to represent plain objects, plain arrays, strings, numbers, booleans, and null.

isopod.serialize converts anything more complex than that into a plain object so that it can be stringified. Importantly this plain, "dehydrated", object still contains all the information necessary to reconstruct the details of the original. As such, when parsed on the other end, we should get a clone of the dehydrated object. isopod.deserialize can "rehydrate" this into a clone of the original.

Any change that occurs to the cloned object will not affect the original and vice-versa. Those objects exist in different runtimes and cannot directly affect each other. If you want automated synchronization, isopod is not that, though you could build something like that "on top" of it.

With isopod you can remotely clone:

  • Plain objects, arrays, strings, numbers, booleans, and null (these are no different than what you can do without isopod)
  • undefined, ±Infinity, and NaN
  • Functions (but not closures, nor bound functions)
  • Regular expressions
  • Errors
  • Dates
  • Symbols
  • Sets
  • Maps
  • Anything with a non-standard prototype
  • Anything with circular or duplicate references to the same object or symbol
  • Array buffers / typed arrays / data views

Note that cloning will exclude non-enumerable properties (except for the .constructor property—this in order to clone prototypes effectively). Here's a not-necessarily complete list of what you might not yet be able to clone:

  • Promises¹
  • Generators
  • Proxies

¹ The plan is never to support cloning promises—it involves cloning too much hairy state for a non-obvious use case. If you're looking to resolve any values on an object, you could do so and then pass the result to isopod.serialize. Check out bluebird's Promise.props or even promise-resolve-deep for help resolving inside an object.

How would I use it?

Install and include isopod into both environments: client and server, one iframe and another iframe, tessel and electron app—whatever two Javascript runtimes you want to clone data between.

Then for any data you'd like to "richly clone", just before sending it across call isopod.serialize on it. Just after parsing the data on the other end call isopod.deserialize on it.

For example, you might have an express route handler on your node server that looks like:

...
// an example of some data that is sufficiently complex
function Thing (names) {
  this.self = this;
  this.names = new Set(names);
}
Thing.prototype.jump = function () {
  this.state = 'jumping';
};
const thing = new Thing(['robert', 'bob', 'rob']);
...
app.get('/thing', function (req, res, next) {
  const serialized = isopod.serialize(thing);
  res.json(serialized);
});
...

And an AJAX request on your client that looks like:

...
fetch('/thing')
.then(function (response) {
  return response.json();
})
.then(function (jsonObj) {
  const deserialized = isopod.deserialize(jsonObj);
  // should now be able to do the following
  deserialized.names.has('bob'); // => true
  deserialized.jump();
  console.log(deserialized.state); // 'jumping'
  console.log(deserialized.self === deserialized); // true
});
...

For something more concrete, you can find runnable examples in the examples folder.

Similar libraries

Contributing

Pull requests / issues / comments / hate mail welcome!

If you'd like to run the tests, download this repo, open a terminal, navigate to it, then run:

npm install

...and once that's done:

npm test

Package Sidebar

Install

npm i isopod

Weekly Downloads

1

Version

0.5.1

License

MIT

Last publish

Collaborators

  • hippiccolo