json-with-bigint 3.3.4 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  JS library that allows you to easily serialize and deserialize data with BigInt values
4
4
 
5
- ## Why would I need JSON-with-BigInt?
5
+ ## Why would I need json-with-bigint?
6
6
 
7
7
  3 reasons:
8
8
 
@@ -10,7 +10,7 @@ JS library that allows you to easily serialize and deserialize data with BigInt
10
10
  2. Native JSON.stringify() and JSON.parse() methods in JS can't work with BigInt
11
11
  3. Other libraries and pieces of code that you'll find either can't solve this problem while supporting consistent round-trip operations (meaning, you will not get the same BigInt values if you serialize and then deserialize them) or requires you to specify which properties in JSON include BigInt values, or to change your JSON or the way you want to work with your data
12
12
 
13
- ## Good things about JSON-with-BigInt
13
+ ## json-with-bigint advantages
14
14
 
15
15
  ✔️ Supports consistent round-trip operations with JSON
16
16
 
@@ -24,9 +24,9 @@ JSONParse(JSONStringify(data)).bigNumber === 9007199254740992n // true
24
24
 
25
25
  ✔️ No need to change your JSON or the way you want to work with your data
26
26
 
27
- ✔️ You don't have to memorize this library's API, you already know it. Just skip the dot, and that's it (JSONParse(), JSONStringify())
27
+ ✔️ You don't have to memorize this library's API, you already know it. Just skip the dot, and that's it (`JSONParse()`, `JSONStringify()`)
28
28
 
29
- ✔️ Parses and stringifies all other values other than big numbers the same way as native JSON methods in JS do. You can just replace every `JSON.parse` and `JSON.stringify` call in your project with functions from this library and it'll work.
29
+ ✔️ Parsed big number values are just regular BigInt. Parses and stringifies all other values other than big numbers the same way as native JSON methods in JS do. Signatures match too. You can just replace every `JSON.parse()` and `JSON.strinfigy()` in your project with `JSONParse()` and `JSONStringify()`, and it will work
30
30
 
31
31
  ✔️ Correctly parses float numbers and negative numbers
32
32
 
@@ -40,11 +40,15 @@ JSONParse(JSONStringify(data)).bigNumber === 9007199254740992n // true
40
40
 
41
41
  ✔️ Can be used as both ESM and CommonJS module
42
42
 
43
- ✔️ Size: 776 bytes (minified and gzipped)
43
+ ✔️ No transpilers needed. Runs even in ES5 environments
44
44
 
45
- ✔️ No dependencies
45
+ ✔️ Actively supported
46
46
 
47
- ✔️ Covered by tests
47
+ ✔️ Size: 988 bytes (minified and gzipped)
48
+
49
+ ✔️ No dependencies. Even the dev ones
50
+
51
+ ✔️ Extensively covered by tests
48
52
 
49
53
  ## Getting Started
50
54
 
@@ -1,4 +1,6 @@
1
1
  // ------ Performance tests ------
2
+ // To test both JSONParseV2() and JSONParse(), switch Node.js version (latest vs something old, like 12.3.0)
3
+
2
4
  const { performance } = require("perf_hooks");
3
5
  const { JSONParse, JSONStringify } = require("../json-with-bigint.cjs");
4
6
 
@@ -1,4 +1,6 @@
1
1
  // ------ Unit tests ------
2
+ // To test both JSONParseV2() and JSONParse(), switch Node.js version (latest vs something old, like 12.3.0)
3
+
2
4
  const assert = require("assert");
3
5
  const { JSONStringify, JSONParse } = require("../json-with-bigint.cjs");
4
6
 
@@ -51,6 +51,45 @@ const JSONStringify = (value, replacer, space) => {
51
51
  return denoisedJSON;
52
52
  };
53
53
 
54
+ /*
55
+ Function to check if the JSON.parse's context.source feature is supported.
56
+ */
57
+ const isContextSourceSupported = () => {
58
+ let supported = false;
59
+
60
+ JSON.parse('{"test": 1}', (key, value, context) => {
61
+ if (key === "test" && context && context.source === "1") {
62
+ supported = true;
63
+ }
64
+ return value;
65
+ });
66
+
67
+ return supported;
68
+ };
69
+
70
+ /*
71
+ Faster (2x) and simpler function to parse JSON.
72
+ Based on JSON.parse's context.source feature, which is not universally available now.
73
+ Does not support the legacy custom format, used in the first version of this library.
74
+ */
75
+ const JSONParseV2 = (text, reviver) => {
76
+ const intRegex = /^-?\d+$/;
77
+
78
+ return JSON.parse(text, (key, value, context) => {
79
+ const isBigNumber =
80
+ typeof value === "number" &&
81
+ (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER);
82
+ const isInt = intRegex.test(context.source);
83
+ const isBigInt = isBigNumber && isInt;
84
+
85
+ if (isBigInt) return BigInt(context.source);
86
+
87
+ if (typeof reviver !== "function") return value;
88
+
89
+ return reviver(key, value, context);
90
+ });
91
+ };
92
+
54
93
  /*
55
94
  Function to parse JSON.
56
95
  If JSON has number values greater than Number.MAX_SAFE_INTEGER, we convert those values to a custom format, then parse them to BigInt values.
@@ -59,6 +98,8 @@ const JSONStringify = (value, replacer, space) => {
59
98
  const JSONParse = (text, reviver) => {
60
99
  if (!text) return originalParse(text, reviver);
61
100
 
101
+ if (isContextSourceSupported()) return JSONParseV2(text, reviver); // Shortcut to a faster (2x) and simpler version
102
+
62
103
  const MAX_INT = Number.MAX_SAFE_INTEGER.toString();
63
104
  const MAX_DIGITS = MAX_INT.length;
64
105
  const stringsOrLargeNumbers =
@@ -1,28 +1,3 @@
1
- type JsonObject = {
2
- [x: string]: Json;
3
- };
1
+ export const JSONStringify: typeof JSON.stringify;
4
2
 
5
- type JsonArray = Json[];
6
-
7
- export type Json =
8
- | null
9
- | undefined
10
- | string
11
- | number
12
- | bigint
13
- | boolean
14
- | JsonObject
15
- | {}
16
- | JsonArray;
17
-
18
- export function JSONStringify(
19
- data: Exclude<Json, undefined>,
20
- space?: string | number
21
- ): string;
22
-
23
- export function JSONStringify(
24
- data: undefined,
25
- space?: string | number
26
- ): undefined;
27
-
28
- export function JSONParse<T extends Json = Json>(serializedData: string): T;
3
+ export const JSONParse: typeof JSON.parse;
@@ -1,28 +1,3 @@
1
- type JsonObject = {
2
- [x: string]: Json;
3
- };
1
+ export const JSONStringify: typeof JSON.stringify;
4
2
 
5
- type JsonArray = Json[];
6
-
7
- export type Json =
8
- | null
9
- | undefined
10
- | string
11
- | number
12
- | bigint
13
- | boolean
14
- | JsonObject
15
- | {}
16
- | JsonArray;
17
-
18
- export function JSONStringify(
19
- data: Exclude<Json, undefined>,
20
- space?: string | number
21
- ): string;
22
-
23
- export function JSONStringify(
24
- data: undefined,
25
- space?: string | number
26
- ): undefined;
27
-
28
- export function JSONParse<T extends Json = Json>(serializedData: string): T;
3
+ export const JSONParse: typeof JSON.parse;
@@ -51,6 +51,45 @@ export const JSONStringify = (value, replacer, space) => {
51
51
  return denoisedJSON;
52
52
  };
53
53
 
54
+ /*
55
+ Function to check if the JSON.parse's context.source feature is supported.
56
+ */
57
+ const isContextSourceSupported = () => {
58
+ let supported = false;
59
+
60
+ JSON.parse('{"test": 1}', (key, value, context) => {
61
+ if (key === "test" && context && context.source === "1") {
62
+ supported = true;
63
+ }
64
+ return value;
65
+ });
66
+
67
+ return supported;
68
+ };
69
+
70
+ /*
71
+ Faster (2x) and simpler function to parse JSON.
72
+ Based on JSON.parse's context.source feature, which is not universally available now.
73
+ Does not support the legacy custom format, used in the first version of this library.
74
+ */
75
+ const JSONParseV2 = (text, reviver) => {
76
+ const intRegex = /^-?\d+$/;
77
+
78
+ return JSON.parse(text, (key, value, context) => {
79
+ const isBigNumber =
80
+ typeof value === "number" &&
81
+ (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER);
82
+ const isInt = intRegex.test(context.source);
83
+ const isBigInt = isBigNumber && isInt;
84
+
85
+ if (isBigInt) return BigInt(context.source);
86
+
87
+ if (typeof reviver !== "function") return value;
88
+
89
+ return reviver(key, value, context);
90
+ });
91
+ };
92
+
54
93
  /*
55
94
  Function to parse JSON.
56
95
  If JSON has number values greater than Number.MAX_SAFE_INTEGER, we convert those values to a custom format, then parse them to BigInt values.
@@ -59,6 +98,8 @@ export const JSONStringify = (value, replacer, space) => {
59
98
  export const JSONParse = (text, reviver) => {
60
99
  if (!text) return originalParse(text, reviver);
61
100
 
101
+ if (isContextSourceSupported()) return JSONParseV2(text, reviver); // Shortcut to a faster (2x) and simpler version
102
+
62
103
  const MAX_INT = Number.MAX_SAFE_INTEGER.toString();
63
104
  const MAX_DIGITS = MAX_INT.length;
64
105
  const stringsOrLargeNumbers =
@@ -1 +1 @@
1
- const noiseValue=/^-?\d+n+$/,originalStringify=JSON.stringify,originalParse=JSON.parse;export const JSONStringify=(n,r,t)=>{if("rawJSON"in JSON)return originalStringify(n,((n,t)=>"bigint"==typeof t?JSON.rawJSON(t.toString()):"function"==typeof r?r(n,t):(Array.isArray(r)&&r.includes(n),t)),t);if(!n)return originalStringify(n,r,t);const i=originalStringify(n,((n,t)=>"string"==typeof t&&Boolean(t.match(noiseValue))||"bigint"==typeof t?t.toString()+"n":"function"==typeof r?r(n,t):(Array.isArray(r)&&r.includes(n),t)),t);return i.replace(/([\[:])?"(-?\d+)n"($|([\\n]|\s)*(\s|[\\n])*[,\}\]])/g,"$1$2$3").replace(/([\[:])?("-?\d+n+)n("$|"([\\n]|\s)*(\s|[\\n])*[,\}\]])/g,"$1$2$3")};export const JSONParse=(n,r)=>{if(!n)return originalParse(n,r);const t=Number.MAX_SAFE_INTEGER.toString(),i=t.length,e=/^"-?\d+n+"$/,o=/^-?\d+n$/,g=n.replace(/"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g,((n,r,o,g)=>{const s='"'===n[0];if(s&&Boolean(n.match(e)))return n.substring(0,n.length-1)+'n"';const a=o||g,l=r&&(r.length<i||r.length===i&&r<=t);return s||a||l?n:'"'+n+'n"'}));return originalParse(g,((n,t,i)=>{if("string"==typeof t&&Boolean(t.match(o)))return BigInt(t.substring(0,t.length-1));return"string"==typeof t&&Boolean(t.match(noiseValue))?t.substring(0,t.length-1):"function"!=typeof r?t:r(n,t,i)}))};
1
+ const noiseValue=/^-?\d+n+$/,originalStringify=JSON.stringify,originalParse=JSON.parse;export const JSONStringify=(n,t,r)=>{if("rawJSON"in JSON)return originalStringify(n,((n,r)=>"bigint"==typeof r?JSON.rawJSON(r.toString()):"function"==typeof t?t(n,r):(Array.isArray(t)&&t.includes(n),r)),r);if(!n)return originalStringify(n,t,r);const e=originalStringify(n,((n,r)=>"string"==typeof r&&Boolean(r.match(noiseValue))||"bigint"==typeof r?r.toString()+"n":"function"==typeof t?t(n,r):(Array.isArray(t)&&t.includes(n),r)),r);return e.replace(/([\[:])?"(-?\d+)n"($|([\\n]|\s)*(\s|[\\n])*[,\}\]])/g,"$1$2$3").replace(/([\[:])?("-?\d+n+)n("$|"([\\n]|\s)*(\s|[\\n])*[,\}\]])/g,"$1$2$3")};const isContextSourceSupported=()=>{let n=!1;return JSON.parse('{"test": 1}',((t,r,e)=>("test"===t&&e&&"1"===e.source&&(n=!0),r))),n},JSONParseV2=(n,t)=>{const r=/^-?\d+$/;return JSON.parse(n,((n,e,i)=>{const o="number"==typeof e&&(e>Number.MAX_SAFE_INTEGER||e<Number.MIN_SAFE_INTEGER),s=r.test(i.source);return o&&s?BigInt(i.source):"function"!=typeof t?e:t(n,e,i)}))};export const JSONParse=(n,t)=>{if(!n)return originalParse(n,t);if(isContextSourceSupported())return JSONParseV2(n,t);const r=Number.MAX_SAFE_INTEGER.toString(),e=r.length,i=/^"-?\d+n+"$/,o=/^-?\d+n$/,s=n.replace(/"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g,((n,t,o,s)=>{const a='"'===n[0];if(a&&Boolean(n.match(i)))return n.substring(0,n.length-1)+'n"';const u=o||s,g=t&&(t.length<e||t.length===e&&t<=r);return a||u||g?n:'"'+n+'n"'}));return originalParse(s,((n,r,e)=>{if("string"==typeof r&&Boolean(r.match(o)))return BigInt(r.substring(0,r.length-1));return"string"==typeof r&&Boolean(r.match(noiseValue))?r.substring(0,r.length-1):"function"!=typeof t?r:t(n,r,e)}))};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-with-bigint",
3
- "version": "3.3.4",
3
+ "version": "3.5.0",
4
4
  "description": "JS library that allows you to easily serialize and deserialize data with BigInt values",
5
5
  "type": "module",
6
6
  "exports": {