@brillout/json-serializer 0.5.1
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/LICENSE.md +9 -0
- package/dist/cjs/parse.d.ts +2 -0
- package/dist/cjs/parse.js +30 -0
- package/dist/cjs/stringify.d.ts +7 -0
- package/dist/cjs/stringify.js +54 -0
- package/dist/cjs/types.d.ts +8 -0
- package/dist/cjs/types.js +83 -0
- package/dist/cjs/utils/isCallable.d.ts +1 -0
- package/dist/cjs/utils/isCallable.js +7 -0
- package/dist/cjs/utils/isObject.d.ts +3 -0
- package/dist/cjs/utils/isObject.js +13 -0
- package/dist/cjs/utils/isReactElement.d.ts +1 -0
- package/dist/cjs/utils/isReactElement.js +9 -0
- package/dist/esm/parse.d.ts +2 -0
- package/dist/esm/parse.js +27 -0
- package/dist/esm/stringify.d.ts +7 -0
- package/dist/esm/stringify.js +51 -0
- package/dist/esm/types.d.ts +8 -0
- package/dist/esm/types.js +80 -0
- package/dist/esm/utils/isCallable.d.ts +1 -0
- package/dist/esm/utils/isCallable.js +3 -0
- package/dist/esm/utils/isObject.d.ts +3 -0
- package/dist/esm/utils/isObject.js +10 -0
- package/dist/esm/utils/isReactElement.d.ts +1 -0
- package/dist/esm/utils/isReactElement.js +5 -0
- package/index.js +1 -0
- package/package.json +49 -0
- package/parse.d.ts +2 -0
- package/parse.js +8 -0
- package/readme.md +325 -0
- package/readme.template.md +77 -0
- package/stringify.d.ts +2 -0
- package/stringify.js +8 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-present Romuald Brillout
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parse = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
function parse(str) {
|
|
6
|
+
// We don't use the reviver option in `JSON.parse(str, reviver)` because it doesn't support `undefined` values
|
|
7
|
+
const value = JSON.parse(str);
|
|
8
|
+
return modifier(value);
|
|
9
|
+
}
|
|
10
|
+
exports.parse = parse;
|
|
11
|
+
function modifier(value) {
|
|
12
|
+
if (typeof value === 'string') {
|
|
13
|
+
return reviver(value);
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === 'object' && value !== null) {
|
|
16
|
+
Object.entries(value).forEach(([key, val]) => {
|
|
17
|
+
;
|
|
18
|
+
value[key] = modifier(val);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function reviver(value) {
|
|
24
|
+
for (const { match, deserialize } of types_1.types) {
|
|
25
|
+
if (match(value)) {
|
|
26
|
+
return deserialize(value, parse);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stringify = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
const isReactElement_1 = require("./utils/isReactElement");
|
|
6
|
+
const isCallable_1 = require("./utils/isCallable");
|
|
7
|
+
const isObject_1 = require("./utils/isObject");
|
|
8
|
+
function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
|
|
9
|
+
const path = [];
|
|
10
|
+
// The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
|
|
11
|
+
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
|
|
12
|
+
// - This means we have total of 3 possible errors while serializing:
|
|
13
|
+
// - Cyclic references
|
|
14
|
+
// - Functions
|
|
15
|
+
// - React elements
|
|
16
|
+
const serializer = (val) => JSON.stringify(val, replacer, space);
|
|
17
|
+
return serializer(value);
|
|
18
|
+
function replacer(key, value) {
|
|
19
|
+
if (key !== '') {
|
|
20
|
+
path.push(key);
|
|
21
|
+
}
|
|
22
|
+
if (forbidReactElements && (0, isReactElement_1.isReactElement)(value)) {
|
|
23
|
+
throw new Error(genErrMsg('React element'));
|
|
24
|
+
}
|
|
25
|
+
if ((0, isCallable_1.isCallable)(value)) {
|
|
26
|
+
const functionName = value.name;
|
|
27
|
+
throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
|
|
28
|
+
}
|
|
29
|
+
const valueOriginal = this[key];
|
|
30
|
+
for (const { is, serialize } of types_1.types.slice().reverse()) {
|
|
31
|
+
if (is(valueOriginal)) {
|
|
32
|
+
//@ts-ignore
|
|
33
|
+
return serialize(valueOriginal, serializer);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (sortObjectKeys && (0, isObject_1.isObject)(value)) {
|
|
37
|
+
const copy = {};
|
|
38
|
+
Object.keys(value)
|
|
39
|
+
.sort()
|
|
40
|
+
.forEach((key) => {
|
|
41
|
+
copy[key] = value[key];
|
|
42
|
+
});
|
|
43
|
+
value = copy;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
function genErrMsg(valueType, valName) {
|
|
48
|
+
const name = valName ? ' `' + valName + '`' : '';
|
|
49
|
+
const location = path.length === 0 ? '' : ` ${name ? 'at ' : ''}\`${valueName}[${path.map((p) => `'${p}'`).join('][')}]\``;
|
|
50
|
+
const fallback = name === '' && location === '' ? ` ${valueName}` : '';
|
|
51
|
+
return `Cannot serialize${name}${location}${fallback} because it is a ${valueType} (https://github.com/brillout/json-s)`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.stringify = stringify;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { types };
|
|
2
|
+
declare const types: readonly [Type<undefined, unknown>, Type<number, unknown>, Type<number, unknown>, Type<number, unknown>, Type<Date, any>, Type<BigInt, any>, Type<RegExp, any>, Type<Map<any, any>, any[]>, Type<Set<unknown>, unknown[]>, Type<string, any>];
|
|
3
|
+
declare type Type<T, IntermediateType> = {
|
|
4
|
+
is: (val: unknown) => asserts val is T;
|
|
5
|
+
match: (str: string) => boolean;
|
|
6
|
+
serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
|
|
7
|
+
deserialize: (str: string, deserializer: (str: string) => IntermediateType) => T;
|
|
8
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.types = void 0;
|
|
4
|
+
const types = [
|
|
5
|
+
ts({
|
|
6
|
+
is: (val) => val === undefined,
|
|
7
|
+
match: (str) => str === '!undefined',
|
|
8
|
+
serialize: () => '!undefined',
|
|
9
|
+
deserialize: () => undefined
|
|
10
|
+
}),
|
|
11
|
+
ts({
|
|
12
|
+
is: (val) => val === Infinity,
|
|
13
|
+
match: (str) => str === '!Infinity',
|
|
14
|
+
serialize: () => '!Infinity',
|
|
15
|
+
deserialize: () => Infinity
|
|
16
|
+
}),
|
|
17
|
+
ts({
|
|
18
|
+
is: (val) => val === -Infinity,
|
|
19
|
+
match: (str) => str === '!-Infinity',
|
|
20
|
+
serialize: () => '!-Infinity',
|
|
21
|
+
deserialize: () => -Infinity
|
|
22
|
+
}),
|
|
23
|
+
ts({
|
|
24
|
+
is: (val) => typeof val === 'number' && isNaN(val),
|
|
25
|
+
match: (str) => str === '!NaN',
|
|
26
|
+
serialize: () => '!NaN',
|
|
27
|
+
deserialize: () => NaN
|
|
28
|
+
}),
|
|
29
|
+
ts({
|
|
30
|
+
is: (val) => val instanceof Date,
|
|
31
|
+
match: (str) => str.startsWith('!Date:'),
|
|
32
|
+
serialize: (val) => '!Date:' + val.toISOString(),
|
|
33
|
+
deserialize: (str) => new Date(str.slice('!Date:'.length))
|
|
34
|
+
}),
|
|
35
|
+
ts({
|
|
36
|
+
is: (val) => typeof val === 'bigint',
|
|
37
|
+
match: (str) => str.startsWith('!BigInt:'),
|
|
38
|
+
serialize: (val) => '!BigInt:' + val.toString(),
|
|
39
|
+
deserialize: (str) => {
|
|
40
|
+
if (typeof BigInt === 'undefined') {
|
|
41
|
+
throw new Error('Your JavaScript environement does not support BigInt. Consider adding a polyfill.');
|
|
42
|
+
}
|
|
43
|
+
return BigInt(str.slice('!BigInt:'.length));
|
|
44
|
+
}
|
|
45
|
+
}),
|
|
46
|
+
ts({
|
|
47
|
+
is: (val) => val instanceof RegExp,
|
|
48
|
+
match: (str) => str.startsWith('!RegExp:'),
|
|
49
|
+
serialize: (val) => '!RegExp:' + val.toString(),
|
|
50
|
+
deserialize: (str) => {
|
|
51
|
+
str = str.slice('!RegExp:'.length);
|
|
52
|
+
// const args: string[] = str.match(/\/(.*?)\/([gimy])?$/)!
|
|
53
|
+
const args = str.match(/\/(.*)\/(.*)?/);
|
|
54
|
+
const pattern = args[1];
|
|
55
|
+
const flags = args[2];
|
|
56
|
+
return new RegExp(pattern, flags);
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
ts({
|
|
60
|
+
is: (val) => val instanceof Map,
|
|
61
|
+
match: (str) => str.startsWith('!Map:'),
|
|
62
|
+
serialize: (val, serializer) => '!Map:' + serializer(Array.from(val.entries())),
|
|
63
|
+
deserialize: (str, deserializer) => new Map(deserializer(str.slice('!Map:'.length)))
|
|
64
|
+
}),
|
|
65
|
+
ts({
|
|
66
|
+
is: (val) => val instanceof Set,
|
|
67
|
+
match: (str) => str.startsWith('!Set:'),
|
|
68
|
+
serialize: (val, serializer) => '!Set:' + serializer(Array.from(val.values())),
|
|
69
|
+
deserialize: (str, deserializer) => new Set(deserializer(str.slice('!Set:'.length)))
|
|
70
|
+
}),
|
|
71
|
+
// Avoid collisions with the special strings defined above
|
|
72
|
+
ts({
|
|
73
|
+
is: (val) => typeof val === 'string' && val.startsWith('!'),
|
|
74
|
+
match: (str) => str.startsWith('!'),
|
|
75
|
+
serialize: (val) => '!' + val,
|
|
76
|
+
deserialize: (str) => str.slice(1)
|
|
77
|
+
})
|
|
78
|
+
];
|
|
79
|
+
exports.types = types;
|
|
80
|
+
// Type check
|
|
81
|
+
function ts(t) {
|
|
82
|
+
return t;
|
|
83
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isCallable<T extends (...args: unknown[]) => unknown>(thing: T | unknown): thing is T;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isObject = void 0;
|
|
4
|
+
function isObject(value) {
|
|
5
|
+
if (typeof value !== 'object' || value === null) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
exports.isObject = isObject;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isReactElement(value: unknown): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isReactElement = void 0;
|
|
4
|
+
function isReactElement(value) {
|
|
5
|
+
return (typeof value === 'object' &&
|
|
6
|
+
value !== null &&
|
|
7
|
+
String(value['$$typeof']) === 'Symbol(react.element)');
|
|
8
|
+
}
|
|
9
|
+
exports.isReactElement = isReactElement;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { parse };
|
|
2
|
+
import { types } from './types';
|
|
3
|
+
function parse(str) {
|
|
4
|
+
// We don't use the reviver option in `JSON.parse(str, reviver)` because it doesn't support `undefined` values
|
|
5
|
+
const value = JSON.parse(str);
|
|
6
|
+
return modifier(value);
|
|
7
|
+
}
|
|
8
|
+
function modifier(value) {
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
return reviver(value);
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === 'object' && value !== null) {
|
|
13
|
+
Object.entries(value).forEach(([key, val]) => {
|
|
14
|
+
;
|
|
15
|
+
value[key] = modifier(val);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function reviver(value) {
|
|
21
|
+
for (const { match, deserialize } of types) {
|
|
22
|
+
if (match(value)) {
|
|
23
|
+
return deserialize(value, parse);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export { stringify };
|
|
2
|
+
import { types } from './types';
|
|
3
|
+
import { isReactElement } from './utils/isReactElement';
|
|
4
|
+
import { isCallable } from './utils/isCallable';
|
|
5
|
+
import { isObject } from './utils/isObject';
|
|
6
|
+
function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
|
|
7
|
+
const path = [];
|
|
8
|
+
// The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
|
|
9
|
+
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
|
|
10
|
+
// - This means we have total of 3 possible errors while serializing:
|
|
11
|
+
// - Cyclic references
|
|
12
|
+
// - Functions
|
|
13
|
+
// - React elements
|
|
14
|
+
const serializer = (val) => JSON.stringify(val, replacer, space);
|
|
15
|
+
return serializer(value);
|
|
16
|
+
function replacer(key, value) {
|
|
17
|
+
if (key !== '') {
|
|
18
|
+
path.push(key);
|
|
19
|
+
}
|
|
20
|
+
if (forbidReactElements && isReactElement(value)) {
|
|
21
|
+
throw new Error(genErrMsg('React element'));
|
|
22
|
+
}
|
|
23
|
+
if (isCallable(value)) {
|
|
24
|
+
const functionName = value.name;
|
|
25
|
+
throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
|
|
26
|
+
}
|
|
27
|
+
const valueOriginal = this[key];
|
|
28
|
+
for (const { is, serialize } of types.slice().reverse()) {
|
|
29
|
+
if (is(valueOriginal)) {
|
|
30
|
+
//@ts-ignore
|
|
31
|
+
return serialize(valueOriginal, serializer);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (sortObjectKeys && isObject(value)) {
|
|
35
|
+
const copy = {};
|
|
36
|
+
Object.keys(value)
|
|
37
|
+
.sort()
|
|
38
|
+
.forEach((key) => {
|
|
39
|
+
copy[key] = value[key];
|
|
40
|
+
});
|
|
41
|
+
value = copy;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function genErrMsg(valueType, valName) {
|
|
46
|
+
const name = valName ? ' `' + valName + '`' : '';
|
|
47
|
+
const location = path.length === 0 ? '' : ` ${name ? 'at ' : ''}\`${valueName}[${path.map((p) => `'${p}'`).join('][')}]\``;
|
|
48
|
+
const fallback = name === '' && location === '' ? ` ${valueName}` : '';
|
|
49
|
+
return `Cannot serialize${name}${location}${fallback} because it is a ${valueType} (https://github.com/brillout/json-s)`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { types };
|
|
2
|
+
declare const types: readonly [Type<undefined, unknown>, Type<number, unknown>, Type<number, unknown>, Type<number, unknown>, Type<Date, any>, Type<BigInt, any>, Type<RegExp, any>, Type<Map<any, any>, any[]>, Type<Set<unknown>, unknown[]>, Type<string, any>];
|
|
3
|
+
declare type Type<T, IntermediateType> = {
|
|
4
|
+
is: (val: unknown) => asserts val is T;
|
|
5
|
+
match: (str: string) => boolean;
|
|
6
|
+
serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
|
|
7
|
+
deserialize: (str: string, deserializer: (str: string) => IntermediateType) => T;
|
|
8
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export { types };
|
|
2
|
+
const types = [
|
|
3
|
+
ts({
|
|
4
|
+
is: (val) => val === undefined,
|
|
5
|
+
match: (str) => str === '!undefined',
|
|
6
|
+
serialize: () => '!undefined',
|
|
7
|
+
deserialize: () => undefined
|
|
8
|
+
}),
|
|
9
|
+
ts({
|
|
10
|
+
is: (val) => val === Infinity,
|
|
11
|
+
match: (str) => str === '!Infinity',
|
|
12
|
+
serialize: () => '!Infinity',
|
|
13
|
+
deserialize: () => Infinity
|
|
14
|
+
}),
|
|
15
|
+
ts({
|
|
16
|
+
is: (val) => val === -Infinity,
|
|
17
|
+
match: (str) => str === '!-Infinity',
|
|
18
|
+
serialize: () => '!-Infinity',
|
|
19
|
+
deserialize: () => -Infinity
|
|
20
|
+
}),
|
|
21
|
+
ts({
|
|
22
|
+
is: (val) => typeof val === 'number' && isNaN(val),
|
|
23
|
+
match: (str) => str === '!NaN',
|
|
24
|
+
serialize: () => '!NaN',
|
|
25
|
+
deserialize: () => NaN
|
|
26
|
+
}),
|
|
27
|
+
ts({
|
|
28
|
+
is: (val) => val instanceof Date,
|
|
29
|
+
match: (str) => str.startsWith('!Date:'),
|
|
30
|
+
serialize: (val) => '!Date:' + val.toISOString(),
|
|
31
|
+
deserialize: (str) => new Date(str.slice('!Date:'.length))
|
|
32
|
+
}),
|
|
33
|
+
ts({
|
|
34
|
+
is: (val) => typeof val === 'bigint',
|
|
35
|
+
match: (str) => str.startsWith('!BigInt:'),
|
|
36
|
+
serialize: (val) => '!BigInt:' + val.toString(),
|
|
37
|
+
deserialize: (str) => {
|
|
38
|
+
if (typeof BigInt === 'undefined') {
|
|
39
|
+
throw new Error('Your JavaScript environement does not support BigInt. Consider adding a polyfill.');
|
|
40
|
+
}
|
|
41
|
+
return BigInt(str.slice('!BigInt:'.length));
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
44
|
+
ts({
|
|
45
|
+
is: (val) => val instanceof RegExp,
|
|
46
|
+
match: (str) => str.startsWith('!RegExp:'),
|
|
47
|
+
serialize: (val) => '!RegExp:' + val.toString(),
|
|
48
|
+
deserialize: (str) => {
|
|
49
|
+
str = str.slice('!RegExp:'.length);
|
|
50
|
+
// const args: string[] = str.match(/\/(.*?)\/([gimy])?$/)!
|
|
51
|
+
const args = str.match(/\/(.*)\/(.*)?/);
|
|
52
|
+
const pattern = args[1];
|
|
53
|
+
const flags = args[2];
|
|
54
|
+
return new RegExp(pattern, flags);
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
ts({
|
|
58
|
+
is: (val) => val instanceof Map,
|
|
59
|
+
match: (str) => str.startsWith('!Map:'),
|
|
60
|
+
serialize: (val, serializer) => '!Map:' + serializer(Array.from(val.entries())),
|
|
61
|
+
deserialize: (str, deserializer) => new Map(deserializer(str.slice('!Map:'.length)))
|
|
62
|
+
}),
|
|
63
|
+
ts({
|
|
64
|
+
is: (val) => val instanceof Set,
|
|
65
|
+
match: (str) => str.startsWith('!Set:'),
|
|
66
|
+
serialize: (val, serializer) => '!Set:' + serializer(Array.from(val.values())),
|
|
67
|
+
deserialize: (str, deserializer) => new Set(deserializer(str.slice('!Set:'.length)))
|
|
68
|
+
}),
|
|
69
|
+
// Avoid collisions with the special strings defined above
|
|
70
|
+
ts({
|
|
71
|
+
is: (val) => typeof val === 'string' && val.startsWith('!'),
|
|
72
|
+
match: (str) => str.startsWith('!'),
|
|
73
|
+
serialize: (val) => '!' + val,
|
|
74
|
+
deserialize: (str) => str.slice(1)
|
|
75
|
+
})
|
|
76
|
+
];
|
|
77
|
+
// Type check
|
|
78
|
+
function ts(t) {
|
|
79
|
+
return t;
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isCallable<T extends (...args: unknown[]) => unknown>(thing: T | unknown): thing is T;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isReactElement(value: unknown): boolean;
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
throw new Error("Module `@brillout/json-serializer` doesn't exist anymore: load `@brillout/json-serializer/parse` and `@brillout/json-serializer/stringify` instead.")
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brillout/json-serializer",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Same as JSON but with added support for `Date`, `undefined`, `Map`, `Set`, and more.",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./parse": {
|
|
9
|
+
"require": "./dist/cjs/parse.js",
|
|
10
|
+
"import": "./dist/esm/parse.js"
|
|
11
|
+
},
|
|
12
|
+
"./stringify": {
|
|
13
|
+
"require": "./dist/cjs/stringify.js",
|
|
14
|
+
"import": "./dist/esm/stringify.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "pnpm run tsc:watch:cjs",
|
|
19
|
+
"build": "pnpm run clean && pnpm run tsc:esm && pnpm run tsc:cjs",
|
|
20
|
+
"test": "self-import && node test/",
|
|
21
|
+
"docs": "mdocs",
|
|
22
|
+
"tsc:esm": "tsc",
|
|
23
|
+
"tsc:cjs": "tsc --project ./tsconfig.cjs.json",
|
|
24
|
+
"tsc:watch:esm": "tsc --incremental --watch",
|
|
25
|
+
"tsc:watch:cjs": "tsc --incremental --watch --project ./tsconfig.cjs.json",
|
|
26
|
+
"clean": "rm -rf dist/",
|
|
27
|
+
"reset": "git clean -Xdf && pnpm install && pnpm build",
|
|
28
|
+
"release": "release-me patch"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@brillout/mdocs": "^0.1.30",
|
|
32
|
+
"@brillout/release-me": "^0.0.5",
|
|
33
|
+
"@types/node": "17.0.13",
|
|
34
|
+
"lodash.isequal": "^4.5.0",
|
|
35
|
+
"react": "^17.0.2",
|
|
36
|
+
"self-import": "^1.0.1",
|
|
37
|
+
"typescript": "^4.8.2"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist/",
|
|
41
|
+
"*.d.ts",
|
|
42
|
+
"*.js"
|
|
43
|
+
],
|
|
44
|
+
"repository": "github:brillout/json-serializer",
|
|
45
|
+
"packageManager": "pnpm@7.9.5",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/parse.d.ts
ADDED
package/parse.js
ADDED
package/readme.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
<!---
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
WARNING, READ THIS.
|
|
9
|
+
This is a computed file. Do not edit.
|
|
10
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
WARNING, READ THIS.
|
|
24
|
+
This is a computed file. Do not edit.
|
|
25
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
WARNING, READ THIS.
|
|
39
|
+
This is a computed file. Do not edit.
|
|
40
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
WARNING, READ THIS.
|
|
54
|
+
This is a computed file. Do not edit.
|
|
55
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
WARNING, READ THIS.
|
|
69
|
+
This is a computed file. Do not edit.
|
|
70
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
-->
|
|
78
|
+
# `@brillout/json-serializer`
|
|
79
|
+
|
|
80
|
+
Same as JSON but with added support for:
|
|
81
|
+
- `Date`
|
|
82
|
+
- `undefined`
|
|
83
|
+
- `Set`
|
|
84
|
+
- `Map`
|
|
85
|
+
- `BigInt`
|
|
86
|
+
- `RegExp`
|
|
87
|
+
- `NaN`
|
|
88
|
+
- `Infinity`
|
|
89
|
+
|
|
90
|
+
JSON is a good serializer for JavaScript values but
|
|
91
|
+
is lacking some JavaScript types such as `Date`:
|
|
92
|
+
|
|
93
|
+
~~~js
|
|
94
|
+
const assert = require('assert')
|
|
95
|
+
|
|
96
|
+
let obj = {
|
|
97
|
+
time: new Date('2042-01-01')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// JSON converts dates to strings
|
|
101
|
+
assert(obj.time.constructor === Date)
|
|
102
|
+
obj = JSON.parse(JSON.stringify(obj))
|
|
103
|
+
assert(obj.time.constructor === String)
|
|
104
|
+
assert(obj.time === '2042-01-01T00:00:00.000Z')
|
|
105
|
+
~~~
|
|
106
|
+
|
|
107
|
+
Whereas with `@brillout/json-serializer`:
|
|
108
|
+
|
|
109
|
+
~~~js
|
|
110
|
+
const assert = require('assert')
|
|
111
|
+
const { parse } = require('@brillout/json-serializer/parse')
|
|
112
|
+
const { stringify } = require('@brillout/json-serializer/stringify')
|
|
113
|
+
|
|
114
|
+
let obj = {
|
|
115
|
+
time: new Date('2042-01-01')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// `@brillout/json-serializer` preserves Date
|
|
119
|
+
assert(obj.time.constructor === Date)
|
|
120
|
+
obj = parse(stringify(obj))
|
|
121
|
+
assert(obj.time.constructor === Date)
|
|
122
|
+
assert(obj.time.getTime() === new Date('2042-01-01').getTime())
|
|
123
|
+
~~~
|
|
124
|
+
|
|
125
|
+
<br/>
|
|
126
|
+
|
|
127
|
+
#### Contents
|
|
128
|
+
|
|
129
|
+
- [Usage](#usage)
|
|
130
|
+
- [Full Example](#full-example)
|
|
131
|
+
- [How it Works](#how-it-works)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
<br/>
|
|
135
|
+
|
|
136
|
+
### Usage
|
|
137
|
+
|
|
138
|
+
~~~js
|
|
139
|
+
// npm install @brillout/json-serializer
|
|
140
|
+
const { parse } = require('@brillout/json-serializer/parse')
|
|
141
|
+
const { stringify } = require('@brillout/json-serializer/stringify')
|
|
142
|
+
|
|
143
|
+
const obj = {
|
|
144
|
+
hello: 'from the future',
|
|
145
|
+
time: new Date('2042-01-01')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Serialize with @brillout/json-serializer
|
|
149
|
+
const obj_serialized = stringify(obj)
|
|
150
|
+
|
|
151
|
+
// Deserialize a @brillout/json-serializer string
|
|
152
|
+
const obj_deserialized = parse(obj_serialized)
|
|
153
|
+
~~~
|
|
154
|
+
|
|
155
|
+
<br/>
|
|
156
|
+
|
|
157
|
+
### Full Example
|
|
158
|
+
|
|
159
|
+
Example exposing all differences between JSON and `@brillout/json-serializer`.
|
|
160
|
+
|
|
161
|
+
~~~js
|
|
162
|
+
// /examples/json-serializer.js
|
|
163
|
+
|
|
164
|
+
const assert = require('assert')
|
|
165
|
+
|
|
166
|
+
const { parse } = require('@brillout/json-serializer/parse')
|
|
167
|
+
const { stringify } = require('@brillout/json-serializer/stringify')
|
|
168
|
+
|
|
169
|
+
const obj = {
|
|
170
|
+
date: new Date(),
|
|
171
|
+
undefined: undefined,
|
|
172
|
+
NaN: NaN,
|
|
173
|
+
Infinity: Infinity,
|
|
174
|
+
regexp: /^\d+$/g
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// All of `obj` can be serialized with @brillout/json-serializer
|
|
178
|
+
const obj2 = parse(stringify(obj))
|
|
179
|
+
assert(obj2.date.getTime() === obj.date.getTime())
|
|
180
|
+
assert(obj2.undefined === undefined && 'undefined' in obj2)
|
|
181
|
+
assert(isNaN(obj2.NaN))
|
|
182
|
+
assert(obj2.Infinity === Infinity)
|
|
183
|
+
assert(obj2.regexp.toString() === obj.regexp.toString())
|
|
184
|
+
|
|
185
|
+
// JSON cannot serialize any of `obj`
|
|
186
|
+
const obj3 = JSON.parse(JSON.stringify(obj))
|
|
187
|
+
// JSON converts dates to strings
|
|
188
|
+
assert(obj3.constructor !== Date)
|
|
189
|
+
// JSON removes properties with a value of `undefined`
|
|
190
|
+
assert(!('undefined' in obj3))
|
|
191
|
+
// JSON converts `NaN` to `null`
|
|
192
|
+
assert(obj3.NaN === null)
|
|
193
|
+
// JSON converts `Infinity` to `null`
|
|
194
|
+
assert(obj3.Infinity === null)
|
|
195
|
+
// JSON converts RegExp to an empty object
|
|
196
|
+
assert(obj3.regexp.constructor === Object && Object.keys(obj3.regexp).length === 0)
|
|
197
|
+
~~~
|
|
198
|
+
|
|
199
|
+
To run the example:
|
|
200
|
+
|
|
201
|
+
~~~shell
|
|
202
|
+
$ git clone git@github.com:brillout/json-serializer
|
|
203
|
+
$ cd json-serializer
|
|
204
|
+
$ npm install
|
|
205
|
+
$ npm run self-link
|
|
206
|
+
$ node ./examples/json-serializer.js
|
|
207
|
+
~~~
|
|
208
|
+
|
|
209
|
+
The `npm run self-link` is required to be able to self `require('@brillout/json-serializer')`.
|
|
210
|
+
|
|
211
|
+
<br/>
|
|
212
|
+
|
|
213
|
+
### How it Works
|
|
214
|
+
|
|
215
|
+
Let's see how `@brillout/json-serializer` serializes an object:
|
|
216
|
+
|
|
217
|
+
~~~js
|
|
218
|
+
// /examples/inspect.js
|
|
219
|
+
|
|
220
|
+
const { stringify } = require('@brillout/json-serializer/stringify')
|
|
221
|
+
|
|
222
|
+
const obj = {
|
|
223
|
+
date: new Date(),
|
|
224
|
+
undefined: undefined,
|
|
225
|
+
collision: '!undefined',
|
|
226
|
+
NaN: NaN,
|
|
227
|
+
Infinity: Infinity,
|
|
228
|
+
regexp: /^\d+$/g
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(stringify(obj, undefined, 2))
|
|
232
|
+
// Prints:
|
|
233
|
+
/*
|
|
234
|
+
{
|
|
235
|
+
"date": "!Date:2021-01-12T22:15:56.319Z",
|
|
236
|
+
"undefined": "!undefined",
|
|
237
|
+
"collision": "!!undefined"
|
|
238
|
+
"NaN": "!NaN",
|
|
239
|
+
"Infinity": "!Infinity",
|
|
240
|
+
"regexp": "!RegExp:/^\\d+$/g"
|
|
241
|
+
}
|
|
242
|
+
*/
|
|
243
|
+
~~~
|
|
244
|
+
|
|
245
|
+
`@brillout/json-serializer` is based on JSON while using prefixed strings for unsupported types.
|
|
246
|
+
|
|
247
|
+
`@brillout/json-serializer` uses the native `JSON.parse` and `JSON.stringify` functions while modifying the serialization of unsupported types.
|
|
248
|
+
|
|
249
|
+
<!---
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
WARNING, READ THIS.
|
|
257
|
+
This is a computed file. Do not edit.
|
|
258
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
WARNING, READ THIS.
|
|
272
|
+
This is a computed file. Do not edit.
|
|
273
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
WARNING, READ THIS.
|
|
287
|
+
This is a computed file. Do not edit.
|
|
288
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
WARNING, READ THIS.
|
|
302
|
+
This is a computed file. Do not edit.
|
|
303
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
WARNING, READ THIS.
|
|
317
|
+
This is a computed file. Do not edit.
|
|
318
|
+
Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
-->
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# `@brillout/json-serializer`
|
|
2
|
+
|
|
3
|
+
Same as JSON but with added support for:
|
|
4
|
+
- `Date`
|
|
5
|
+
- `undefined`
|
|
6
|
+
- `Set`
|
|
7
|
+
- `Map`
|
|
8
|
+
- `BigInt`
|
|
9
|
+
- `RegExp`
|
|
10
|
+
- `NaN`
|
|
11
|
+
- `Infinity`
|
|
12
|
+
|
|
13
|
+
JSON is a good serializer for JavaScript values but
|
|
14
|
+
is lacking some JavaScript types such as `Date`:
|
|
15
|
+
|
|
16
|
+
~~~js
|
|
17
|
+
!INLINE /examples/date_json.js --hide-source-path
|
|
18
|
+
~~~
|
|
19
|
+
|
|
20
|
+
Whereas with `@brillout/json-serializer`:
|
|
21
|
+
|
|
22
|
+
~~~js
|
|
23
|
+
!INLINE /examples/date_json-serializer.js --hide-source-path
|
|
24
|
+
~~~
|
|
25
|
+
|
|
26
|
+
<br/>
|
|
27
|
+
|
|
28
|
+
#### Contents
|
|
29
|
+
|
|
30
|
+
- [Usage](#usage)
|
|
31
|
+
- [Full Example](#full-example)
|
|
32
|
+
- [How it Works](#how-it-works)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
<br/>
|
|
36
|
+
|
|
37
|
+
### Usage
|
|
38
|
+
|
|
39
|
+
~~~js
|
|
40
|
+
!INLINE /examples/simple.js --hide-source-path
|
|
41
|
+
~~~
|
|
42
|
+
|
|
43
|
+
<br/>
|
|
44
|
+
|
|
45
|
+
### Full Example
|
|
46
|
+
|
|
47
|
+
Example exposing all differences between JSON and `@brillout/json-serializer`.
|
|
48
|
+
|
|
49
|
+
~~~js
|
|
50
|
+
!INLINE /examples/json-serializer.js
|
|
51
|
+
~~~
|
|
52
|
+
|
|
53
|
+
To run the example:
|
|
54
|
+
|
|
55
|
+
~~~shell
|
|
56
|
+
$ git clone git@github.com:brillout/json-serializer
|
|
57
|
+
$ cd json-serializer
|
|
58
|
+
$ npm install
|
|
59
|
+
$ npm run self-link
|
|
60
|
+
$ node ./examples/json-serializer.js
|
|
61
|
+
~~~
|
|
62
|
+
|
|
63
|
+
The `npm run self-link` is required to be able to self `require('@brillout/json-serializer')`.
|
|
64
|
+
|
|
65
|
+
<br/>
|
|
66
|
+
|
|
67
|
+
### How it Works
|
|
68
|
+
|
|
69
|
+
Let's see how `@brillout/json-serializer` serializes an object:
|
|
70
|
+
|
|
71
|
+
~~~js
|
|
72
|
+
!INLINE /examples/inspect.js
|
|
73
|
+
~~~
|
|
74
|
+
|
|
75
|
+
`@brillout/json-serializer` is based on JSON while using prefixed strings for unsupported types.
|
|
76
|
+
|
|
77
|
+
`@brillout/json-serializer` uses the native `JSON.parse` and `JSON.stringify` functions while modifying the serialization of unsupported types.
|
package/stringify.d.ts
ADDED