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

js-freeze-dry

v1.1.5

Published

Round trip conversion of complex cyclic object graphs to strings with support for classes, maps and sets

Downloads

63

Readme

js-freeze-dry

Round trip conversion of complex cyclic object graphs to strings with support for classes, maps and sets.

Installation

yarn add js-freeze-dry

or

npm install js-freeze-dry

It has no dependencies and is written in Typescript

Usage

Consists of two functions, serialize and deserialize which convert object graphs to and from strings.

serialize converts an object graph to JSON, discovering any objects discovered in the process and noting their constructor in the JSON. deserialize does the opposite and re-instantiates the object graph. It can cover cases where the same object instance is referenced in multiple places and cyclic patterns.

Example

Here is an example structure that includes multiple references to the same object

class Box {
    x = 0;
    y = 0;
    name = "";
    constructor(x : number, y : number) {
        this.x = x;
        this.y = y;
    }
}
serializable({Box});


class Arrow {
    from;
    to;
    constructor(from : Box, to : Box, name: string) {
        this.from = from;
        this.to = to;
        this.name = name;
    }
}
serializable({Arrow})

class Drawing {
    boxes : Array<Box> = [];
    arrows : Array<Arrow> = [];
}
serializable({Drawing});

Assume it is initialized like this:

    const drawing = new Drawing()
    const box1 = new Box(20, 40, "One");
    const box2 = new Box(70, 70, "Two");
    const arrow1 = new Arrow(box1, box2)
    drawing.boxes.push(box1, box2)
    drawing.arrows.push(arrow1);

To serialize it:

const json = serialize(drawing);

And to deserialize it:

const newDrawing = deserialize(json);

Note that to deserialize you need to call serializable to provide a hash of className with a value of the class itself. Note that serializable({Drawing}) is short for serializable({'Drawing' : Drawing})). This is so that deserialize can find the name of the class reliably even if class names are mangled by code compression.

js-freeze-dry is designed for data oriented classes though through the use of helpers it may be used with members that are not natively serializable.

Restrictions

There are some constraints on the structure: You can serialize anything that JSON.stringify / JSON.parse support plus:

  • Dates
  • Sets
  • Maps
  • Classes - deserialize will instantiate the class with an empty constructor and then copy over the properties. Therefore, the class must be instantiable with an empty constructor.

deserialize cannot reconstitute objects containing functions unless they are part of classes. Also objects that contain internal objects (e.g. DOM element references, XMLHTTPRequest, Promises) of course will not be reconstituted properly.

Helpers

Sometimes you want to control the process of serialization or deserialization on a class by class basis. Examples include:

  • Encrypting data
  • Removing sensitive data
  • Classes that have unserializable content
  • Classes that expect parameters in their constructors.

To create a "serializer":

serializer({Box: (box : Box) => ({...box, name: encrypt(box.name)}) });

The serializer function is passed the data and it may return a copy of the data. Generally you would not want to mutuate the data itself as it would impact the instance being serialized.

To create a "deserializer":

deserializer({Box: (box : Box) => new Box(box.x, box.y, decrypt(box.name)) });

The deserializer will be passed the data and is to return a new instance of the class. Note that i can use Object.assign to transfer the properties if needed as this is what the default deserialization does.

Reference

Serialize

function serialize(rootObj : any, 
                   classes? : {[index: string] : any}, 
                   serializers? : {[index: string] : (obj: any, type? : any) => any}, type? : any) 

Serializes an object returning a string that can be deserialized.

  • classes is an optional parameter to define the same values as would be passed to serializable
  • serializers is an optional parameter to define the same values as would be passed to serializer
  • type is an optional type that is passed as the 2nd argument to the serializer.

It returns a JSON string. While the string can be parsed with JSON.parse it will parse into an internal format that has types and id's and so really is only useful for processing by deserialize. See the restrictions above on the data that can be processed in this fashion.

Deserialize

deserialize(json : string, 
            classes? : {[index: string] : any},
            deserializers? : {[index: string] : (obj: any, type? : any) => any})
  • classes is an optional parameter to define the same values as would be passed to serializable
  • deserializers is an optional parameter to define the same values as would be passed to deserializer
  • type is an optional type that is passed as the 2nd argument to the deserializer.

Returns an instance of an object.

Serializer

function serializer (classHelpers? : {[index: string] : (obj: any)=>any}) 

Establishes one or more serializer functions identified by a key that represents the name of the class. The function will be passed an object to be serialized and optionally a type passed to serialize. The function is expected to return the data to be serialized. It should not mutate the data but rather make a copy if the data must be changed prior to serialization. For example:

serializer({Box: (box : Box) => ({...box, name: encrypt(box.name)}) });

Deserializer

function deserializer(classHelpers?: {[index: string] : (obj: any, type? : any) => any})

Establishes one or more deserializer functions identified by a key that represents the name of the class. The function will be passed the data of an object to be deserialized and optionally a type passed to serialize. The function is expected to return an instance of the object. For example:

deserializer({Box: (box : Box) => new Box(box.x, box.y, decrypt(box.name)) });