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

@ungap/raw-json

v0.4.1

Published

JSON.rawJSON and isRawJSON ponyfill

Downloads

488

Readme

raw-json

Coverage Status

A JSON.rawJSON and JSON.isRawJSON ponyfill (meaning: no global patch applied).

import * as JSON from '@ungap/raw-json';

JSON.stringify({
  a: JSON.rawJSON('12345678901234567890'),
  b: JSON.rawJSON(12345678901234567890n),
  c: JSON.rawJSON('null'),
  d: JSON.rawJSON(null),
  e: JSON.rawJSON(123),
  f: JSON.rawJSON('"hello raw JSON"'),
  g: JSON.rawJSON(true),
  h: JSON.rawJSON(false)
});

JSON.parse(
  JSON.stringify(
    JSON.rawJSON('12345678901234567890')
  ),
  // this is an extra utility to have BigInts back
  JSON.reviver
);

// 12345678901234567890n

About RawJSON

Practically born to solve the fact serialized BigInts are broken in current JS world, but not necessarily in other programming languages such as Python, this ponyfill allows its consumers to safely store, or retrieve, also BigInt primitives as valid values.

const myBigInt = 12345678901234567890n;

// ⚠️ Uncaught TypeError: Do not know how to serialize a BigInt
JSON.stringify({ myBigInt });

// parsing too big numbers is still allowed by JSON specs
JSON.parse('{"myBigInt":12345678901234567890}');
// ⚠️ but the result is lost or incorrect
{ myBigInt: 12345678901234567000 }
//                           ^^^

Enter JSON.rawJSON, an utility that accepts only primitives, except Symbols, and returns a very special, frozen, null prototyped object that contains a rawJSON public field.

import * as JSON from 'https://esm.run/@ungap/raw-json';

const myBigInt = 12345678901234567890n;

JSON.isRawJSON(myBigInt);   // false

const asRawJSON = JSON.rawJSON(myBigInt);
// { rawJSON: '12345678901234567890' }

JSON.isRawJSON(asRawJSON);  // true

JSON.stringify({ myBigInt: asRawJSON });
// {"myBigInt":12345678901234567890}

At this point the JSON string {"myBigInt":12345678901234567890} can be posted or parsed back, but to retrieve a BigInt back we need to use a reviver function.

Differently from the pre-rawJSON era, a reviver would receive a key and a value as arguments, but not the recently introduced context. Such context has a source value, which points at the original string representation of such value, only if the value is a primitive JSON value, meaning it's not present when such value is an object or an array.

JSON.parse(
  '{"myBigInt":12345678901234567890}',
  (key, value, context) => {
    if (typeof value === 'number') {
      console.log({ value, source: context.source });
      // { value: 12345678901234567000, source: '12345678901234567890' }
      return BigInt(context.source);
    }
    return value;
  }
);

{ myBigInt: 12345678901234567890n }

To simplify the repeated reviver dance, this module also offers a non standard JSON.reviver helper that will recognize BigInt and return these when found in the source, keeping all other numbers the same.

What about core-js or performance?

This module goal is to temporarily enable rawJSON until it gets native support and it moves the minimum amount of code to do so to keep it simple, size-friendly, yet still performant.

If you are interested in code-size and performance you can test yourself in Safari or Firefox.

On average, this module is ~1.5X faster on simple cases and 3X up to 5X faster on more complex (nested) cases.

Compared to native Chrome/ium implementation, this module is nearly as fast as that one too, specially with complex values (same test page, this time with Chrome/ium).

Known limitations VS core-js

If you seppuku arrays or objects while parsing as opposite of operating on these after their values have been resolved there are chances the result might be not desired.

I am thinking if I should fix or care about these kind of edge-cases / footgun but if I need to end up writing a whole JSON parser then this module can RIP and you'd be better off with the core-js variant.

Use native JSON.parse if you need to perform seppuku on parsed objects values while these are being parsed, use this ponyfill alternative for every other case.