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

smykowski

v1.3.4

Published

*I deal with the goddamn objects so JSON doesn’t have to!! I have object skills!!*

Downloads

924

Readme

Smykowski

I deal with the goddamn objects so JSON doesn’t have to!! I have object skills!!

The Story

So what you do is you take the JavaScript objects and you pass them down to the JSON?

That, that's right.

Well, then I gotta ask, then why can't the objects go directly to JSON, huh?

Well, uh, uh, uh, because, uh, JSON is not good at dealing with some objects.

You convert the JS objects for JSON?

Well, no, my, my plugins do that, or, or your plugins do.

Ah. Then you must then convert the JSON back to objects.

Well...no. Yeah, I mean, sometimes.

Well, what would you say… you do here?

Well, look, I already told you. I deal with the goddamn objects so JSON doesn’t have to!! I have object skills!! I am good at dealing with objects!!! Can't you understand that?!? WHAT THE HELL IS WRONG WITH YOU PEOPLE?!!!!!!!

Goals

Create a pluggable tool to encode and decode JavaScript objects to and from data structures compatible with the JSON. Non-goals include parsing/stringifying to/from non-JSON strings.

Current plugin features (see Supplied plugins below)

  • cyclical and repeated references
  • undefined, ±Infinity, NaN, -0
  • regexp, dates, buffers, maps and sets
  • deterministic (sorted) hash of objects
  • toJSON and fromJSON
  • registered classes

Install

npm i smykowski

Usage

import {  Smykowski, defaultEncoders, defaultDecoders } from 'smykowski';

const asjon = new Smykowski()
  .use(defaultEncoders)
  .use(defaultDecoders);

const obj = {
  _id: '5aa882d3638a0f580d92c677',
  index: 0,
  name: {
    first: 'Valenzuela',
    last: 'Valenzuela'
  },
  registered: new Date(2014, 0, 1),
  symbol: Symbol('banana'),
  range: [
    -Infinity, 0, 1, 2, 3, 4, 5, 6, 7, 8, Infinity
  ],
  friends: [
    {
      id: -0,
      name: 'Benton Chase'
    },
    {
      id: 1,
      name: 'Mccarthy Morgan'
    },
    {
      id: NaN,
      name: 'Kaufman Price'
    }
  ]
};

const stringified = asjon.stringify(obj, null, 2);
console.log(stringified);

yields:

{
  "_id": "5aa882d3638a0f580d92c677",
  "index": 0,
  "name": {
    "first": "Valenzuela",
    "last": "Valenzuela"
  },
  "registered": {
    "$date": "2014-01-01T07:00:00.000Z"
  },
  "symbol": {
    "$symbol": "banana"
  },
  "range": [
    {
      "$numberDecimal": "-Infinity"
    },
    0,
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    {
      "$numberDecimal": "Infinity"
    }
  ],
  "friends": [
    {
      "id": {
        "$numberDecimal": "-0"
      },
      "name": "Benton Chase"
    },
    {
      "id": 1,
      "name": "Mccarthy Morgan"
    },
    {
      "id": {
        "$numberDecimal": "NaN"
      },
      "name": "Kaufman Price"
    }
  ]
}

then

const parsed = asjon.parse(stringified);
console.log(parsed);

returns the a new object "equal" to the first.

Plugins

In Smykowski we have a concept of encoders, decoders, and plugins. Encoders and decoders are functions that take no arguments and return a "replacer" or "reviver" function with the signature (value: any, path: Array<string | number>). For encoders the "replacer" function should return the encoded JS object that will continue to be processed by additional plugins. For decoders the "reviver" function returns the decoded JS object. The path value may be user to write more advanced encoders/decoders.

For example, a very simple encoder that replaces all values that are not arrays with "foo":

const foo = () => {
  const FOO = 'foo';
  return value => {
    return Array.isArray(value) ? v : FOO;
  };
};

A more complex examples is an encoder that replaces duplicate values with JSON pointers:

export const jsonPointer = () => {
  const repeated = new WeakMap();
  return (v, path: Path) => {
    if (v !== null && typeof v === 'object') {
      if (repeated.has(v)) {
        return { $ref: '#' + jsonpointer.compile(repeated.get(v)) };
      }
      repeated.set(v, path);
    }
    return v;
  };
};

The order of the plugins does matter. In this case jsonPointer should come last:

const asjon = new Smykowski()
  .addEncoder(foo)
  .addEncoder(jsonPointer);

const arr = [1, 2, 3];
asjon.stringify([arr, arr]);

yields:

[["foo","foo","foo"],{"$ref":"foo"}]

Decoders work similarly using the addDecoder method. A plug-in is a set of encoders and/or decoders like so:

function myPlugin(_: Smykowski) {
  return _
    .addEncoder(recurseArrays)
    .addEncoder(foo)
    .addDecoder(myDecoder);
}

const asjon = new Smykowski()
  .use(myPlugin);

Note: use(fn) is sugar for fn(asjon).

Supplied plugins

defaultEncoders

  • encodeJSONPointer: Replaces cycles and repeated objects with JSON Pointers.
  • encodeBuffers: Encodes buffers n the form of { $binary: '...' } where the string is the Base64 encoded value.
  • encodeMap: Encodes Maps returning the result in the form of { $map: [[...]] }
  • encodeSet: Encodes plain Sets returning the result in the form of { $set: [...] }
  • encodeSpecialNumbers: Returns special numeric values (-0, NaN and +/-Infinity) as a strict MongoDB Extended JSON numberDecimal ({ $numberDecimal: "..." }).
  • encodeUndefined: Returns undefined values as a strict MongoDB Extended JSON undefined ({ $undefined: true }).
  • encodeRegexps: Returns regular expression values as a strict MongoDB Extended JSON Regular Expressions ({ "$regex": "...", "$options": "..." }).
  • encodeDates: Returns dates as a strict MongoDB Extended JSON Regular Date ({ "$date": "..." }).
  • encodeSymbols: Returns symbols in the form of { $symbol: "..." }
  • toJSON: Returns the result of the toJSON method for objects whose toJSON property is a function
  • stableObject: Sorts object properties by key (get a consistent hash from objects)

defaultDecoders

  • decodeSpecialNumbers: Decodes special numeric values.
  • decodeUndefined: Decodes undefined.
  • decodeRegexps: Decodes Regexp values.
  • decodeDates: Decodes Dates.
  • decodeSymbols: Decodes Symbols.
  • decodeMap: Decodes Maps.
  • decodeSet: Decodes Sets.
  • decodeBuffers: Decodes Buffer.
  • decodeJSONPointers: Decodes JSON pointers.

classSerializer

This plugin registers classes for encoding/decoding using typed hints. For example:

class Person {
  constructor(public first: string, public last: string) {

  }

  getFullname() {
    return this.first + ' ' + this.last;
  }
}

class Employee extends Person {
  constructor(public first: string, public last: string, public id: string) {
    super(first, last);

  }
}

const ajson = new Smykowski()
  .use(classSerializer, { Person, Employee })
  .use(defaultEncoders)
  .use(defaultDecoders);

const str = ajson.stringify([new Person('John', 'Doe'), new Employee('Jane', 'Doe', 'A123')]);
/*
  [
    {
      "@@Person": {
        "first": "John",
        "last": "Doe"
      }
    },
    {
      "@@Employee": {
        "first": "Jane",
        "id": "A123",
        "last": "Doe"
    }
  ]
*/

const [ john, jane ] = ajson.parse(str) as [Person, Employee];

john instanceof Person; // true
jane instanceof Employee; // true
jane.getFullname(); // "Jane Doe"

Alternatives

License

This project is licensed under the MIT License - see the LICENSE file for details