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

kaiser

v0.0.4

Published

Serialization of arbitrary objects

Downloads

55,943

Readme

kaiser

Serialization library that allows arbitrary objects and classes to be serialized or deserialized in a safe way.

kaiser can serialize standard JavaScript data, objects, arrays and dates out of the box.

However, kaiser is not magical. Any non-native object type has to be registered with a type id (ideally a uuid), a serializer and a deserializer.

  • General: kaiser can be configured to serialize and deserialize any data.

  • Powerful: object identity is preserved by deserialization. Circular references can be handled.

  • Safe(ish): the deserializer can be configured to only deserialize objects from a whitelist. This is safe as long as the serializers in the whitelist are safe.

  • Low registration overhead: packages can register custom serializers for their classes and functions using kaiser/reg, which is about 300 bytes minified.

  • Other uses: kaiser can be used to make shallow or deep copies of objects.

The kaiser package in and of itself is fairly large for what it does, mostly because it is written in Earl Grey, which comes with some overhead. I plan to address that eventually.

Usage

Simple serialization/deserialization

var kaiser = require("kaiser");
var str = kaiser.serialize({a: 1, b: new Date()});
var obj = kaiser.deserialize(str);

whitelists

You can also instantiate a Serializer object with a whitelist, which means only a limited list of approved objects and classes can be serialized or deserialized. The resulting serializer, when used on unsanitized data, will be as safe as the most unsafe object in the whitelist, so be careful:

var kaiser = require("kaiser");
var s = kaiser.Serializer([Date, Person]);

var str1 = ser.serialize({a: new Date(), b: new Person()}); // OK!
var obj2 = ser.deserialize(str1);                           // OK!

var str2 = ser.serialize({a: 1, b: new Animal()});          // ERROR!

var str3 = kaiser.serialize({a: 1, b: new Animal()});       // OK (generic serializer)
var obj3 = ser.deserialize(str3);                           // ERROR!

Registering functionality

To register with kaiser you need to assign a uuid to your class or function. A uuid is a unique symbol which can identify your functionality in any application that imports it and will not clash with any other package. There are a few ways to do it, and kaiser can help you.

First you must import kaiser, for that you have two options:

// Option A: Import the full package
kaiser = require("kaiser");

// Option B: Import only the registering functions
kaiser = require("kaiser/reg");

The difference between the two is that option B only defines a few stubs to let you register your classes for serialization and is about 300 bytes minified, so it is super cheap if you only want to provide the functionality to people who need it. Then when they import the full kaiser package, the serializers will get registered for real.

Now, suppose you have the following definition:

function Vehicle(brand) {
    this.brand = brand;
}
Vehicle.prototype.start = function () {
    console.log("vroom vroom!");
}

Use package info

This will assign Vehicle the uuid npm:my-package/Vehicle:

kaiser.register(Vehicle.prototype, {
    package: {name: "my-package", "version": "1.2.3"}
});

This will assign Vehicle the uuid npm:my-package@1/Vehicle:

kaiser.register(Vehicle.prototype, {
    package: {name: "my-package", "version": "1.2.3"},
    useVersion: "major"
});

Of course, if you have a package.json file in the same directory, you should simply do this:

kaiser.register(Vehicle.prototype, {
    package: require("./package.json"),
    useVersion: "minor"
});

To register more than one class, use kaiser.registerAll:

kaiser.registerAll([Vehicle.prototype, Animal.prototype], {
    package: require("./package.json"),
    useVersion: "minor"
});

uuid

Install the uuid command on your system and run it. It will give you a (presumably) unique hexadecimal identifier that you can paste in your code:

$ uuid
bfd249d8-302a-11e5-8044-278351ad39e9

Then you must set the typeId field:

kaiser.register(Vehicle.prototype, {
    typeId: "bfd249d8-302a-11e5-8044-278351ad39e9"
});

kaiser.registerAll can be used with just one typeId. What it will do is that it will generate ids like bfd249d8-302a-11e5-8044-278351ad39e9/Vehicle and so on.

uuid interface

In order to generate a uuid, kaiser follows these steps:

  • If the configuration object contains:
  • typeId and variant: typeId + JSON.stringify(variant)
  • typeId and nameVariant === true: typeId + object.name
  • typeId and nameVariant: typeId + nameVariant
  • typeId: typeId
  • package and useVersion: package@version/object.name

Note that registerAll and registerSingletons automatically set nameVariant in order to differentiate the entries. It is thus important that they all have names.

Registering functions and singletons

By default, kaiser understands that what is being registered is a prototype, and that objects that directly inherit from the prototype are those that we wish to serialize.

On the other hand, you may want to serialize functions, or objects as-is. To that purpose you may use kaiser.registerFunction or kaiser.registerSingleton (they are the same thing). Both functions have a plural equivalent that lets you register more than one thing at once.

function hello(name) {
    return "hello, " + name
}
function bye(name) {
    return "bye, " + name
}

kaiser.registerSingletons([hello, bye], {
    package: require("./package.json"),
    useVersion: "patch"
});

The above will registers ids "npm:[email protected]/hello" and "npm:[email protected]/bye".

Custom serialization

By default kaiser.serialize will pack all the fields in an object and kaiser.deserialize will instantiate an object with the right prototype and write the fields back.

But you can change this. Here is a simple example (of a flawed serialization mechanism):

kaiser.register(Person.prototype, {
    typeId: "bfd249d8-302a-11e5-8044-278351ad39e9",
    serialize: function (person) {
        return person.name + "/" + person.age;
    },
    deserialize: function (person) {
        fields = person.split("/");
        return new Person(fields[0], parseInt(fields[1]));
    }
});

The serialization and deserialization interface is as follows:

serialize(object) must return either some primitive type like a String or Number, or a plain object or array in which the fields are not serialized (in other words, do not call kaiser.serialize in that function). kaiser will serialize these fields for you so that you can focus on the logic.

deserialize(form) must rebuild the object previously serialized exactly. It will receive the same output serialize produced, with already deserialized fields (do not call kaiser.deserialize in that function).

In addition, the following two methods must be implemented to support circular references for your type:

create() must return an instance of the object.

fill(target, form) must fill the return value of create so that target becomes the deserialized form.

To put it simply, var r = create(); fill(r, x) must be equivalent to var r = deserialize(x).