json-with-bigint 2.4.2 → 3.1.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
@@ -8,7 +8,7 @@ JS library that allows you to easily serialize and deserialize data with BigInt
8
8
 
9
9
  1. You need to convert some data to/from JSON and it includes BigInt values
10
10
  2. Native JSON.stringify() and JSON.parse() methods in JS can't work with BigInt
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 change your JSON or the way you want to work with your data
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
13
  ## Good things about JSON-with-BigInt
14
14
 
@@ -20,9 +20,13 @@ JSONStringify(data) // '{"bigNumber":9007199254740992}'
20
20
  JSONParse(JSONStringify(data)).bigNumber === 9007199254740992n // true
21
21
  ```
22
22
 
23
+ ✔️ No need to specify which properties in JSON include BigInt values. Library will find them itself
24
+
23
25
  ✔️ No need to change your JSON or the way you want to work with your data
24
26
 
25
- ✔️ Parses and stringifies all other values other than big numbers the same way as native JSON methods in JS do
27
+ ✔️ You don't have to memorize this library's API, you already know it. Just skip the dot, use camelCase and that's it (JSONParse(), JSONStringify())
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.
26
30
 
27
31
  ✔️ Correctly parses float numbers and negative numbers
28
32
 
@@ -30,14 +34,16 @@ JSONParse(JSONStringify(data)).bigNumber === 9007199254740992n // true
30
34
 
31
35
  ✔️ Does not contaminate your global space (unlike monkey-patching solution)
32
36
 
33
- ✔️ You don't have to memorize this library's API, you already know it. Just skip the dot, use camelCase and that's it (JSONParse(), JSONStringify())
34
-
35
37
  ✔️ Can be used in both JavaScript and TypeScript projects (.d.ts file included)
36
38
 
37
- ✔️ Size: 762 bytes (minified and gzipped)
39
+ ✔️ Can be used as both ESM and CommonJS module
40
+
41
+ ✔️ Size: 624 bytes (minified and gzipped)
38
42
 
39
43
  ✔️ No dependencies
40
44
 
45
+ ✔️ Covered by tests
46
+
41
47
  ## Getting Started
42
48
 
43
49
  This library has no default export. [Why it's a good thing](https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/)
@@ -0,0 +1,141 @@
1
+ // ------ Performance tests ------
2
+ const { performance } = require("perf_hooks");
3
+ const { JSONParse, JSONStringify } = require("../json-with-bigint.cjs");
4
+
5
+ const fs = require("fs").promises;
6
+ const https = require("https");
7
+
8
+ // JSON is located in a separate GitHub repo here (click Raw to see the URL below):
9
+ // https://github.com/Ivan-Korolenko/json-with-bigint.performance.json/blob/main/performance.json
10
+ const JSON_URL =
11
+ "https://raw.githubusercontent.com/Ivan-Korolenko/json-with-bigint.performance.json/refs/heads/main/performance.json";
12
+ const JSON_LOCAL_FILEPATH = "__tests__/performance.json";
13
+
14
+ async function fetchJSON(url, maxRetries = 3, delay = 1000) {
15
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
16
+ try {
17
+ const response = await new Promise((resolve, reject) => {
18
+ https
19
+ .get(url, (res) => {
20
+ if (res.statusCode >= 500 && res.statusCode < 600) {
21
+ reject(new Error(`Server error ${res.statusCode}: Retrying...`));
22
+ } else if (res.statusCode !== 200) {
23
+ reject(
24
+ new Error(
25
+ `Request failed with status ${res.statusCode} ${res.statusMessage}`
26
+ )
27
+ );
28
+ }
29
+
30
+ let data = "";
31
+ res.on("data", (chunk) => {
32
+ data += chunk;
33
+ });
34
+ res.on("end", () => resolve(data));
35
+ })
36
+ .on("error", reject);
37
+ });
38
+
39
+ return JSON.parse(response);
40
+ } catch (error) {
41
+ if (attempt < maxRetries) {
42
+ console.warn(`Attempt ${attempt} failed: ${error.message}`);
43
+ await new Promise((res) =>
44
+ setTimeout(res, delay * Math.pow(2, attempt - 1))
45
+ ); // Exponential backoff
46
+ } else {
47
+ console.error("Max retries reached. Fetch failed:", error);
48
+ throw error;
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ async function saveJSONToFile(filePath, data) {
55
+ try {
56
+ const jsonString = JSON.stringify(data, null, 2);
57
+ const tempPath = `${filePath}.tmp`;
58
+
59
+ await fs.writeFile(tempPath, jsonString, "utf8");
60
+ await fs.rename(tempPath, filePath); // Atomic write
61
+ console.log(`✅ JSON data saved to ${filePath}`);
62
+ } catch (error) {
63
+ console.error("Error saving JSON to file:", error);
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ async function fetchAndSaveJSON(url, filePath) {
69
+ try {
70
+ const jsonData = await fetchJSON(url);
71
+
72
+ await saveJSONToFile(filePath, jsonData);
73
+ } catch (error) {
74
+ console.error("❌ Operation failed:", error);
75
+ }
76
+ }
77
+
78
+ // If the file was downloaded earlier, use it. Otherwise, download
79
+ // After n attempts, give up and throw an error
80
+ async function readPerformanceJSON(
81
+ filePath,
82
+ encoding,
83
+ maxAttempts = 3,
84
+ attempt = 0
85
+ ) {
86
+ if (attempt === maxAttempts)
87
+ throw new Error(
88
+ `Reading performance JSON failed after ${attempt} attempts. Check download URL, file availability on that URL, and local filepath`
89
+ );
90
+
91
+ try {
92
+ const data = await fs.readFile(filePath, encoding);
93
+
94
+ return data;
95
+ } catch (error) {
96
+ if (error.code === "ENOENT") {
97
+ console.log(
98
+ `File not found. Downloading... (Attempt ${attempt + 1}/${maxAttempts})`
99
+ );
100
+ await fetchAndSaveJSON(JSON_URL, filePath);
101
+
102
+ return await readPerformanceJSON(
103
+ filePath,
104
+ encoding,
105
+ maxAttempts,
106
+ attempt + 1
107
+ );
108
+ } else {
109
+ console.error("Error reading file:", error);
110
+ throw error; // Re-throw to avoid silent failures
111
+ }
112
+ }
113
+ }
114
+
115
+ const measureExecTime = (fn) => {
116
+ const startTime = performance.now();
117
+
118
+ fn();
119
+
120
+ const endTime = performance.now();
121
+
122
+ console.log("Time: ", endTime - startTime);
123
+ };
124
+
125
+ async function main() {
126
+ const data = await readPerformanceJSON(JSON_LOCAL_FILEPATH, "utf8");
127
+
128
+ measureExecTime(() => {
129
+ console.log("___________");
130
+ console.log("Performance test. One-way");
131
+ JSONParse(data);
132
+ });
133
+
134
+ measureExecTime(() => {
135
+ console.log("___________");
136
+ console.log("Performance test. Round-trip");
137
+ JSONStringify(JSONParse(data));
138
+ });
139
+ }
140
+
141
+ main();
@@ -0,0 +1,137 @@
1
+ // ------ Unit tests ------
2
+ const assert = require("assert");
3
+ const { JSONStringify, JSONParse } = require("../json-with-bigint.cjs");
4
+
5
+ const test1JSON = `{"zero":9007199254740998,"one":-42,"two":-9007199254740998,"test":["He was\\":[-23432432432434324324324324]",111,9007199254740998,{"test2":-9007199254740998}],"test3":["He was:[-23432432432434324324324324]",111,9007199254740998,{"test2":-9007199254740998,"float":1.9007199254740998,"float2":0.1,"float3":2.9007199254740996,"int":1,"int2":3243243432432434324324324}],"float4":[1.9007199254740998,1111111111111111111111111111111111,0.1,1,54354654654654654654656546546546546]}`;
6
+ const test1Obj = {
7
+ zero: 9007199254740998n,
8
+ one: -42,
9
+ two: -9007199254740998n,
10
+ test: [
11
+ 'He was":[-23432432432434324324324324]',
12
+ 111,
13
+ 9007199254740998n,
14
+ { test2: -9007199254740998n },
15
+ ],
16
+ test3: [
17
+ "He was:[-23432432432434324324324324]",
18
+ 111,
19
+ 9007199254740998n,
20
+ {
21
+ test2: -9007199254740998n,
22
+ float: 1.9007199254740998,
23
+ float2: 0.1,
24
+ float3: 2.9007199254740996,
25
+ int: 1,
26
+ int2: 3243243432432434324324324n,
27
+ },
28
+ ],
29
+ float4: [
30
+ 1.9007199254740998,
31
+ 1111111111111111111111111111111111n,
32
+ 0.1,
33
+ 1,
34
+ 54354654654654654654656546546546546n,
35
+ ],
36
+ };
37
+
38
+ const test2JSON = `{
39
+ "test": [
40
+ {
41
+ "ID": 1035342667379599058,
42
+ "Timestamp": "2022-09-13 22:21:25",
43
+ "Contents": "broken example message",
44
+ "Contents2": "broken example 1035342667379599058 message",
45
+ "Contents3": "broken example [1035342667379599058, 1035342667379599058] message",
46
+ "Attachments": "",
47
+ "BreakingValue": 54354654654654654654656546546546546
48
+ },
49
+ 1035342667379599058,
50
+ -1.1035342667379599058,
51
+ 1035342667379599058
52
+ ],
53
+ "test2": {
54
+ "1035342667379599058": 1035342667379599058
55
+ }
56
+ }`;
57
+ // Special case, because native JSON.parse strips \n and whitespaces.
58
+ // So technically, a true round-trip operation (including all spaces, etc.) is not possible in the case of pretty JSON without writing your own full JSON.parse implementation for this particular case.
59
+ // In practice, though, it shouldn't cause any problems, because data will work fine even in that case, and if the backend expects a prettified JSON, you can just prettify it before sending it.
60
+ const test2TersedJSON = `{"test":[{"ID":1035342667379599058,"Timestamp":"2022-09-13 22:21:25","Contents":"broken example message","Contents2":"broken example 1035342667379599058 message","Contents3":"broken example [1035342667379599058, 1035342667379599058] message","Attachments":"","BreakingValue":54354654654654654654656546546546546},1035342667379599058,-1.10353426673796,1035342667379599058],"test2":{"1035342667379599058":1035342667379599058}}`;
61
+ const test2Obj = {
62
+ test: [
63
+ {
64
+ ID: 1035342667379599058n,
65
+ Timestamp: "2022-09-13 22:21:25",
66
+ Contents: "broken example message",
67
+ Contents2: "broken example 1035342667379599058 message",
68
+ Contents3:
69
+ "broken example [1035342667379599058, 1035342667379599058] message",
70
+ Attachments: "",
71
+ BreakingValue: 54354654654654654654656546546546546n,
72
+ },
73
+ 1035342667379599058n,
74
+ -1.10353426673796,
75
+ 1035342667379599058n,
76
+ ],
77
+ test2: { "1035342667379599058": 1035342667379599058n },
78
+ };
79
+
80
+ const test3JSON = `{"items":[{"message":"some text 17365091955960356025, some text"}]}`;
81
+ const test3Obj = {
82
+ items: [{ message: "some text 17365091955960356025, some text" }],
83
+ };
84
+
85
+ const test4JSON = `9007199254740998`;
86
+ const test4Obj = 9007199254740998n;
87
+
88
+ const test5JSON = `[9007199254740998,[9007199254740998],9007199254740998]`;
89
+ const test5Obj = [9007199254740998n, [9007199254740998n], 9007199254740998n];
90
+
91
+ const test6JSON = `[9007199254740998]`;
92
+ const test6Obj = [9007199254740998n];
93
+
94
+ const test7JSON = `["0a","1b","9n","9nn",90071992547409981111,"90071992547409981111.5n"]`;
95
+ const test7Obj = [
96
+ "0a",
97
+ "1b",
98
+ "9n",
99
+ "9nn",
100
+ 90071992547409981111n,
101
+ "90071992547409981111.5n",
102
+ ];
103
+
104
+ assert.deepStrictEqual(JSONParse(test1JSON), test1Obj);
105
+ console.log("1 test parsing passed");
106
+ assert.deepStrictEqual(JSONStringify(JSONParse(test1JSON)), test1JSON);
107
+ console.log("1 test round-trip passed");
108
+
109
+ assert.deepStrictEqual(JSONParse(test2JSON), test2Obj);
110
+ console.log("2 test parsing passed");
111
+ assert.deepStrictEqual(JSONStringify(JSONParse(test2JSON)), test2TersedJSON);
112
+ console.log("2 test round-trip passed");
113
+
114
+ assert.deepStrictEqual(JSONParse(test3JSON), test3Obj);
115
+ console.log("3 test parsing passed");
116
+ assert.deepStrictEqual(JSONStringify(JSONParse(test3JSON)), test3JSON);
117
+ console.log("3 test round-trip passed");
118
+
119
+ assert.deepStrictEqual(JSONParse(test4JSON), test4Obj);
120
+ console.log("4 test parsing passed");
121
+ assert.deepStrictEqual(JSONStringify(JSONParse(test4JSON)), test4JSON);
122
+ console.log("4 test round-trip passed");
123
+
124
+ assert.deepStrictEqual(JSONParse(test5JSON), test5Obj);
125
+ console.log("5 test parsing passed");
126
+ assert.deepStrictEqual(JSONStringify(JSONParse(test5JSON)), test5JSON);
127
+ console.log("5 test round-trip passed");
128
+
129
+ assert.deepStrictEqual(JSONParse(test6JSON), test6Obj);
130
+ console.log("6 test parsing passed");
131
+ assert.deepStrictEqual(JSONStringify(JSONParse(test6JSON)), test6JSON);
132
+ console.log("6 test round-trip passed");
133
+
134
+ assert.deepStrictEqual(JSONParse(test7JSON), test7Obj);
135
+ console.log("7 test parsing passed");
136
+ assert.deepStrictEqual(JSONStringify(JSONParse(test7JSON)), test7JSON);
137
+ console.log("7 test round-trip passed");
@@ -0,0 +1,84 @@
1
+ const noiseValue = /^-?\d+n+$/; // Noise - strings that match the custom format before being converted to it
2
+
3
+ /*
4
+ Function to serialize data to a JSON string.
5
+ Converts BigInt values to a custom format (strings with digits and "n" at the end) and then converts them to proper big integers in a JSON string.
6
+ */
7
+ const JSONStringify = (data, space) => {
8
+ if (!data) return JSON.stringify(data);
9
+
10
+ const bigInts = /([\[:])?"(-?\d+)n"($|[,\}\]])/g;
11
+ const noise = /([\[:])?("-?\d+n+)n("$|"[,\}\]])/g;
12
+ const convertedToCustomJSON = JSON.stringify(
13
+ data,
14
+ (_, value) => {
15
+ const isNoise =
16
+ typeof value === "string" && Boolean(value.match(noiseValue));
17
+
18
+ if (isNoise) return value.toString() + "n"; // Mark noise values with additional "n" to offset the deletion of one "n" during the processing
19
+
20
+ return typeof value === "bigint" ? value.toString() + "n" : value;
21
+ },
22
+ space
23
+ );
24
+ const processedJSON = convertedToCustomJSON.replace(bigInts, "$1$2$3"); // Delete one "n" off the end of every BigInt value
25
+ const denoisedJSON = processedJSON.replace(noise, "$1$2$3"); // Remove one "n" off the end of every noisy string
26
+
27
+ return denoisedJSON;
28
+ };
29
+
30
+ /*
31
+ Function to parse JSON.
32
+ 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.
33
+ Other types of values are not affected and parsed as native JSON.parse() would parse them.
34
+ */
35
+ const JSONParse = (json) => {
36
+ if (!json) return JSON.parse(json);
37
+
38
+ const MAX_INT = Number.MAX_SAFE_INTEGER.toString();
39
+ const MAX_DIGITS = MAX_INT.length;
40
+ const stringsOrLargeNumbers =
41
+ /"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g;
42
+ const noiseValueWithQuotes = /^"-?\d+n+"$/; // Noise - strings that match the custom format before being converted to it
43
+ const customFormat = /^-?\d+n$/;
44
+
45
+ // Find and mark big numbers with "n"
46
+ const serializedData = json.replace(
47
+ stringsOrLargeNumbers,
48
+ (text, digits, fractional, exponential) => {
49
+ const isString = text[0] === '"';
50
+ const isNoise = isString && Boolean(text.match(noiseValueWithQuotes));
51
+
52
+ if (isNoise) return text.substring(0, text.length - 1) + 'n"'; // Mark noise values with additional "n" to offset the deletion of one "n" during the processing
53
+
54
+ const isFractionalOrExponential = fractional || exponential;
55
+ const isLessThanMaxSafeInt =
56
+ digits &&
57
+ (digits.length < MAX_DIGITS ||
58
+ (digits.length === MAX_DIGITS && digits <= MAX_INT)); // With a fixed number of digits, we can correctly use lexicographical comparison to do a numeric comparison
59
+
60
+ if (isString || isFractionalOrExponential || isLessThanMaxSafeInt)
61
+ return text;
62
+
63
+ return '"' + text + 'n"';
64
+ }
65
+ );
66
+
67
+ // Convert marked big numbers to BigInt
68
+ return JSON.parse(serializedData, (_, value) => {
69
+ const isCustomFormatBigInt =
70
+ typeof value === "string" && Boolean(value.match(customFormat));
71
+
72
+ if (isCustomFormatBigInt)
73
+ return BigInt(value.substring(0, value.length - 1));
74
+
75
+ const isNoiseValue =
76
+ typeof value === "string" && Boolean(value.match(noiseValue));
77
+
78
+ if (isNoiseValue) return value.substring(0, value.length - 1); // Remove one "n" off the end of the noisy string
79
+
80
+ return value;
81
+ });
82
+ };
83
+
84
+ module.exports = { JSONStringify, JSONParse };
@@ -1,58 +1,82 @@
1
+ const noiseValue = /^-?\d+n+$/; // Noise - strings that match the custom format before being converted to it
2
+
1
3
  /*
2
- Function to serialize data to JSON string
3
- Converts BigInt values to custom format (strings with digits and "n" at the end) and then converts them to proper big integers in JSON string
4
+ Function to serialize data to a JSON string.
5
+ Converts BigInt values to a custom format (strings with digits and "n" at the end) and then converts them to proper big integers in a JSON string.
4
6
  */
5
7
  export const JSONStringify = (data, space) => {
6
8
  if (!data) return JSON.stringify(data);
7
9
 
8
- const bigInts = /([\[:])?"(-?\d+)n"([,\}\]])/g;
9
- const preliminaryJSON = JSON.stringify(
10
+ const bigInts = /([\[:])?"(-?\d+)n"($|[,\}\]])/g;
11
+ const noise = /([\[:])?("-?\d+n+)n("$|"[,\}\]])/g;
12
+ const convertedToCustomJSON = JSON.stringify(
10
13
  data,
11
- (_, value) => (typeof value === "bigint" ? value.toString() + "n" : value),
14
+ (_, value) => {
15
+ const isNoise =
16
+ typeof value === "string" && Boolean(value.match(noiseValue));
17
+
18
+ if (isNoise) return value.toString() + "n"; // Mark noise values with additional "n" to offset the deletion of one "n" during the processing
19
+
20
+ return typeof value === "bigint" ? value.toString() + "n" : value;
21
+ },
12
22
  space
13
23
  );
14
- const finalJSON = preliminaryJSON.replace(bigInts, "$1$2$3");
24
+ const processedJSON = convertedToCustomJSON.replace(bigInts, "$1$2$3"); // Delete one "n" off the end of every BigInt value
25
+ const denoisedJSON = processedJSON.replace(noise, "$1$2$3"); // Remove one "n" off the end of every noisy string
15
26
 
16
- return finalJSON;
27
+ return denoisedJSON;
17
28
  };
18
29
 
19
30
  /*
20
- Function to parse JSON
21
- If JSON has values presented in a lib's custom format (strings with digits and "n" character at the end), we just parse them to BigInt values (for backward compatibility with previous versions of the lib)
22
- If JSON has values greater than Number.MAX_SAFE_INTEGER, we convert those values to our custom format, then parse them to BigInt values.
31
+ Function to parse JSON.
32
+ 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.
23
33
  Other types of values are not affected and parsed as native JSON.parse() would parse them.
24
-
25
- Big numbers are found and marked using a Regular Expression with these conditions:
26
-
27
- 1. Before the match there is no . and one of the following is present:
28
- 1.1 ":
29
- 1.2 ":[
30
- 1.3 ":[anyNumberOf(anyCharacters)
31
- 1.4 , with no ":"anyNumberOf(anyCharacters) before it
32
- All " required in the rule are not escaped. All whitespace and newline characters outside of string values are ignored.
33
-
34
- 2. The match itself has more than 16 digits OR (16 digits and any digit of the number is greater than that of the Number.MAX_SAFE_INTEGER). And it may have a - sign at the start.
35
-
36
- 3. After the match one of the following is present:
37
- 3.1 , without anyNumberOf(anyCharacters) "} or anyNumberOf(anyCharacters) "] after it
38
- 3.2 } without " after it
39
- 3.3 ] without " after it
40
- All whitespace and newline characters outside of string values are ignored.
41
34
  */
42
35
  export const JSONParse = (json) => {
43
36
  if (!json) return JSON.parse(json);
44
37
 
45
- const numbersBiggerThanMaxInt =
46
- /(?<=[^\\]":\n*\s*[\[]?|[^\\]":\n*\s*\[.*[^\.\d*]\n*\s*|(?<![^\\]"\n*\s*:\n*\s*[^\\]".*),\n*\s*)(-?\d{17,}|-?(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))(?=,(?!.*[^\\]"(\n*\s*\}|\n*\s*\]))|\n*\s*\}[^"]?|\n*\s*\][^"])/g;
47
- const serializedData = json.replace(numbersBiggerThanMaxInt, `"$1n"`);
38
+ const MAX_INT = Number.MAX_SAFE_INTEGER.toString();
39
+ const MAX_DIGITS = MAX_INT.length;
40
+ const stringsOrLargeNumbers =
41
+ /"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g;
42
+ const noiseValueWithQuotes = /^"-?\d+n+"$/; // Noise - strings that match the custom format before being converted to it
43
+ const customFormat = /^-?\d+n$/;
44
+
45
+ // Find and mark big numbers with "n"
46
+ const serializedData = json.replace(
47
+ stringsOrLargeNumbers,
48
+ (text, digits, fractional, exponential) => {
49
+ const isString = text[0] === '"';
50
+ const isNoise = isString && Boolean(text.match(noiseValueWithQuotes));
51
+
52
+ if (isNoise) return text.substring(0, text.length - 1) + 'n"'; // Mark noise values with additional "n" to offset the deletion of one "n" during the processing
48
53
 
54
+ const isFractionalOrExponential = fractional || exponential;
55
+ const isLessThanMaxSafeInt =
56
+ digits &&
57
+ (digits.length < MAX_DIGITS ||
58
+ (digits.length === MAX_DIGITS && digits <= MAX_INT)); // With a fixed number of digits, we can correctly use lexicographical comparison to do a numeric comparison
59
+
60
+ if (isString || isFractionalOrExponential || isLessThanMaxSafeInt)
61
+ return text;
62
+
63
+ return '"' + text + 'n"';
64
+ }
65
+ );
66
+
67
+ // Convert marked big numbers to BigInt
49
68
  return JSON.parse(serializedData, (_, value) => {
50
69
  const isCustomFormatBigInt =
51
- typeof value === "string" && Boolean(value.match(/^-?\d+n$/));
70
+ typeof value === "string" && Boolean(value.match(customFormat));
52
71
 
53
72
  if (isCustomFormatBigInt)
54
73
  return BigInt(value.substring(0, value.length - 1));
55
74
 
75
+ const isNoiseValue =
76
+ typeof value === "string" && Boolean(value.match(noiseValue));
77
+
78
+ if (isNoiseValue) return value.substring(0, value.length - 1); // Remove one "n" off the end of the noisy string
79
+
56
80
  return value;
57
81
  });
58
82
  };
@@ -1 +1 @@
1
- export const JSONStringify=(n,t)=>{if(!n)return JSON.stringify(n);return JSON.stringify(n,((n,t)=>"bigint"==typeof t?t.toString()+"n":t),t).replace(/([\[:])?"(-?\d+)n"([,\}\]])/g,"$1$2$3")};export const JSONParse=n=>{if(!n)return JSON.parse(n);const t=n.replace(/(?<=[^\\]":\n*\s*[\[]?|[^\\]":\n*\s*\[.*[^\.\d*]\n*\s*|(?<![^\\]"\n*\s*:\n*\s*[^\\]".*),\n*\s*)(-?\d{17,}|-?(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))(?=,(?!.*[^\\]"(\n*\s*\}|\n*\s*\]))|\n*\s*\}[^"]?|\n*\s*\][^"])/g,'"$1n"');return JSON.parse(t,((n,t)=>"string"==typeof t&&Boolean(t.match(/^-?\d+n$/))?BigInt(t.substring(0,t.length-1)):t))};
1
+ const noiseValue=/^-?\d+n+$/;export const JSONStringify=(n,t)=>{if(!n)return JSON.stringify(n);return JSON.stringify(n,((n,t)=>"string"==typeof t&&Boolean(t.match(noiseValue))||"bigint"==typeof t?t.toString()+"n":t),t).replace(/([\[:])?"(-?\d+)n"($|[,\}\]])/g,"$1$2$3").replace(/([\[:])?("-?\d+n+)n("$|"[,\}\]])/g,"$1$2$3")};export const JSONParse=n=>{if(!n)return JSON.parse(n);const t=Number.MAX_SAFE_INTEGER.toString(),e=t.length,r=/^"-?\d+n+"$/,o=/^-?\d+n$/,i=n.replace(/"(?:\\.|[^"])*"|-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/g,((n,o,i,s)=>{const g='"'===n[0];if(g&&Boolean(n.match(r)))return n.substring(0,n.length-1)+'n"';const a=i||s,l=o&&(o.length<e||o.length===e&&o<=t);return g||a||l?n:'"'+n+'n"'}));return JSON.parse(i,((n,t)=>{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):t}))};
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "json-with-bigint",
3
- "version": "2.4.2",
3
+ "version": "3.1.0",
4
4
  "description": "JS library that allows you to easily serialize and deserialize data with BigInt values",
5
5
  "type": "module",
6
- "exports": "./json-with-bigint.js",
6
+ "exports": {
7
+ "import": "./json-with-bigint.js",
8
+ "require": "./json-with-bigint.cjs"
9
+ },
7
10
  "repository": {
8
11
  "type": "git",
9
12
  "url": "https://github.com/Ivan-Korolenko/json-with-bigint"